feat(beispiel): die Leiste ueber die eigene Adresse der App ausliefern
Marcs BentoPDF-Image (ghcr.io/alam00000/bentopdf-simple) schickt eine strenge CSP — mit dem Image von Docker Hub, an dem ich zuerst geprueft hatte, war das nicht zu sehen. Statt fremde Kopfzeilen umzuschreiben, reicht der Einsetzer jetzt auch /honeybar/ und die zwei Abfragen der Leiste unter seiner eigenen Adresse durch. Damit ist fuer den Browser alles derselbe Ursprung: CSP unangetastet, kein CORS, kein COEP, und der Schluessel bleibt auf dem Server. Durchgereicht wird nur /api/apps und /api/weather, nur lesend. Ein pauschales /api/ haette die ganze Schnittstelle unter die Adresse der App gehaengt — beim Ausprobieren hat meine eigene Schreibprobe prompt die App-Liste geleert. Das Binden auf 127.0.0.1 ist raus: Es passt nur, wenn der Proxy auf demselben Wirt im Host-Netz laeuft, und nicht fuer Marcs Aufbau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d1cd20586f
commit
8f294e15db
4 files changed
+107
-54
No files matched your search
@@ -231,9 +231,30 @@ aus. Ohne das erscheint dort keine Leiste, und in der Konsole steht nur
|
|||||||
`ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep`.
|
`ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep`.
|
||||||
Das COEP der App bleibt dabei unangetastet — ihre eigenen Fähigkeiten also auch.
|
Das COEP der App bleibt dabei unangetastet — ihre eigenen Fähigkeiten also auch.
|
||||||
|
|
||||||
|
**Zwei Bauarten.** Die zweite ist die bessere, sobald die App eine eigene CSP
|
||||||
|
schickt — und das tun die meisten:
|
||||||
|
|
||||||
|
| | Skript von HoneyOS | Skript über die eigene Adresse |
|
||||||
|
|---|---|---|
|
||||||
|
| CSP der App | muss ersetzt werden | **unangetastet** (`'self'` genügt) |
|
||||||
|
| CORS | Ursprung muss eingetragen sein | entfällt |
|
||||||
|
| COEP der App | braucht `Cross-Origin-Resource-Policy` | entfällt |
|
||||||
|
| `HONEYOS_APPS_KEY` | steht im Quelltext der Seite | **bleibt auf dem Server** |
|
||||||
|
| Aufwand | eine `sub_filter`-Zeile | drei `location`-Blöcke |
|
||||||
|
|
||||||
|
Bei der zweiten reicht der Einsetzer auch `/honeybar/` und die zwei Abfragen der
|
||||||
|
Leiste durch, unter *seiner* Adresse. Für den Browser ist damit alles derselbe
|
||||||
|
Ursprung, und die drei Hürden fallen auf einmal weg. Durchgereicht werden dabei
|
||||||
|
**nur** `/api/apps` und `/api/weather`, und nur lesend (`limit_except GET`) —
|
||||||
|
ein pauschales `/api/` hinge sonst die ganze HoneyOS-Schnittstelle unter die
|
||||||
|
Adresse der App.
|
||||||
|
|
||||||
**Fertige Beispiele:**
|
**Fertige Beispiele:**
|
||||||
|
|
||||||
- [`beispiele/bentopdf/`](beispiele/bentopdf/) — ein Dienst, kürzestmöglich.
|
- [`beispiele/bentopdf/`](beispiele/bentopdf/) — ein Dienst, alles über die
|
||||||
|
eigene Adresse. Geprüft mit `ghcr.io/alam00000/bentopdf-simple`, das eine
|
||||||
|
strenge CSP und COEP mitbringt: Leiste, Apps und Wetter erscheinen, ohne dass
|
||||||
|
an dessen Kopfzeilen etwas geändert wird.
|
||||||
- [`beispiele/mehrere-apps/`](beispiele/mehrere-apps/) — **HoneyOS und ein
|
- [`beispiele/mehrere-apps/`](beispiele/mehrere-apps/) — **HoneyOS und ein
|
||||||
Einsetzer für alle Dienste in einem Stapel.** Am Host erkennt nginx, welcher
|
Einsetzer für alle Dienste in einem Stapel.** Am Host erkennt nginx, welcher
|
||||||
Dienst gemeint ist; eine neue App sind zwei Zeilen in den Tabellen oben in
|
Dienst gemeint ist; eine neue App sind zwei Zeilen in den Tabellen oben in
|
||||||
|
|||||||
@@ -1,35 +1,102 @@
|
|||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# BentoPDF mit HoneyOS-Leiste
|
# BentoPDF mit HoneyOS-Leiste — alles über den eigenen Ursprung.
|
||||||
#
|
#
|
||||||
# Aufbau: Pangolin → leiste (nginx) → bentopdf
|
# Pangolin → leiste → bentopdf
|
||||||
|
# ↘ honeyos (nur /honeybar/ und zwei Abfragen)
|
||||||
#
|
#
|
||||||
# Der nginx tut nichts weiter, als beim Durchreichen einen <script>-Tag vor
|
# Der Kniff: Die Leiste wird nicht von honeyos.… geladen, sondern über die
|
||||||
# </head> zu setzen. BentoPDF selbst bleibt unangetastet und weiss nichts davon.
|
# Adresse der App selbst durchgereicht. Für den Browser ist sie damit eigener
|
||||||
|
# Ursprung — und alle drei Hürden fallen auf einmal weg:
|
||||||
|
#
|
||||||
|
# · die CSP der App (BentoPDF erlaubt 'self', mehr braucht es nicht)
|
||||||
|
# · CORS (keine fremde Anfrage mehr)
|
||||||
|
# · COEP (kein fremdes Skript mehr)
|
||||||
|
#
|
||||||
|
# Ausserdem bleibt HONEYOS_APPS_KEY auf dem Server: Der nginx setzt ihn ein,
|
||||||
|
# im Quelltext der Seite steht er nicht.
|
||||||
#
|
#
|
||||||
# docker compose up -d
|
# docker compose up -d
|
||||||
# → in Pangolin die Ressource auf leiste:80 zeigen lassen (nicht mehr auf
|
# → in Pangolin die Ressource auf leiste:80 zeigen lassen,
|
||||||
# bentopdf), und in HoneyOS BentoPDF als App eintragen — diese Liste ist
|
# und in HoneyOS BentoPDF als App eintragen (id: bentopdf, URL: die
|
||||||
# zugleich die CORS-Freigabe.
|
# öffentliche Adresse, exakt so, wie du sie im Browser aufrufst).
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
services:
|
services:
|
||||||
bentopdf:
|
bentopdf:
|
||||||
image: bentopdf/bentopdf:latest
|
image: ghcr.io/alam00000/bentopdf-simple:latest
|
||||||
container_name: bentopdf
|
container_name: bentopdf
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# Kein ports: mehr. Erreichbar ist nur noch der Einsetzer davor.
|
# Kein ports: nötig — erreichbar ist nur noch der Einsetzer davor.
|
||||||
networks: [intern]
|
# (Zum direkten Nachsehen ohne Leiste ruhig eins lassen.)
|
||||||
|
networks: [honeybar]
|
||||||
|
|
||||||
leiste:
|
leiste:
|
||||||
image: nginx:alpine
|
image: nginx:alpine
|
||||||
container_name: bentopdf-leiste
|
container_name: bentopdf-leiste
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on: [bentopdf]
|
depends_on: [bentopdf]
|
||||||
volumes:
|
|
||||||
- ./leiste.conf:/etc/nginx/conf.d/default.conf:ro
|
|
||||||
ports:
|
ports:
|
||||||
# Nur an localhost binden: davor steht Pangolin, nicht das offene Netz.
|
# Auf welcher Adresse Pangolin die App abholt. 127.0.0.1 nur, wenn
|
||||||
- "127.0.0.1:8185:80"
|
# Pangolin auf demselben Wirt im Host-Netz läuft — hängt es in einem
|
||||||
networks: [intern]
|
# eigenen Behälter oder auf einer anderen Maschine, kommt es dort nicht
|
||||||
|
# hin. Am saubersten: kein ports:, und Pangolin (bzw. sein Newt) tritt
|
||||||
|
# dem Netz `honeybar` bei und spricht `leiste:80` direkt an.
|
||||||
|
- "${LEISTE_PORT:-8185}:80"
|
||||||
|
configs:
|
||||||
|
- source: leiste
|
||||||
|
target: /etc/nginx/conf.d/default.conf
|
||||||
|
networks: [honeybar]
|
||||||
|
|
||||||
|
configs:
|
||||||
|
leiste:
|
||||||
|
# `$$` ist hier ein einfaches `$` für nginx. Ein einzelnes `$` setzt
|
||||||
|
# dagegen Compose ein — genau das ist bei ${HONEYOS_APPS_KEY} gewollt:
|
||||||
|
# der Schlüssel kommt aus der .env und landet nur in der Konfiguration
|
||||||
|
# des nginx, nie im Quelltext der Seite. (nginx selbst löst keine
|
||||||
|
# Umgebungsvariablen auf; stünde dort $${…}, ginge die Zeichenkette
|
||||||
|
# wörtlich als Schlüssel hinaus.)
|
||||||
|
content: |
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
resolver 127.0.0.11 valid=30s ipv6=off;
|
||||||
|
|
||||||
|
# 1. Die Leiste selbst, durchgereicht über diesen Ursprung.
|
||||||
|
location /honeybar/ {
|
||||||
|
proxy_pass http://honeyos:8080/honeybar/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Ihre zwei Abfragen — einzeln benannt und nur lesend. Ein
|
||||||
|
# durchgereichtes /api/ hinge die ganze HoneyOS-Schnittstelle
|
||||||
|
# unter die Adresse dieser App.
|
||||||
|
location = /honeyos-api/apps {
|
||||||
|
limit_except GET { deny all; }
|
||||||
|
proxy_pass http://honeyos:8080/api/apps;
|
||||||
|
proxy_set_header X-HoneyOS-Key "${HONEYOS_APPS_KEY}";
|
||||||
|
}
|
||||||
|
location = /honeyos-api/weather {
|
||||||
|
limit_except GET { deny all; }
|
||||||
|
proxy_pass http://honeyos:8080/api/weather;
|
||||||
|
proxy_set_header X-HoneyOS-Key "${HONEYOS_APPS_KEY}";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Die App selbst, mit eingesetzter Zeile.
|
||||||
|
location / {
|
||||||
|
proxy_pass http://bentopdf:8080;
|
||||||
|
proxy_set_header Host $$host;
|
||||||
|
proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $$scheme;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
# Unkomprimiert anfordern — in einem gzip-Strom ist nichts zu ersetzen.
|
||||||
|
proxy_set_header Accept-Encoding "";
|
||||||
|
|
||||||
|
# sub_filter_types braucht es nicht: text/html ist voreingestellt,
|
||||||
|
# und doppelt genannt warnt nginx beim Start.
|
||||||
|
sub_filter_once on;
|
||||||
|
sub_filter '</head>'
|
||||||
|
'<script defer src="/honeybar/honeybar.js" data-app="bentopdf" data-apps="/honeyos-api/apps" data-home="https://honeyos.duckdns.org/"></script></head>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
intern:
|
honeybar:
|
||||||
|
external: true
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Setzt die HoneyOS-Leiste in eine fremde App ein, die man nicht ändern kann.
|
|
||||||
#
|
|
||||||
# Angepasst werden müssen genau zwei Dinge: die Adresse von HoneyOS und
|
|
||||||
# data-app (der Name muss der id der App in HoneyOS entsprechen, sonst weiss
|
|
||||||
# die Leiste nicht, wo man gerade ist).
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://bentopdf:8080;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
|
|
||||||
# Unkomprimiert anfordern — in einem gzip-Strom ist nichts zu ersetzen.
|
|
||||||
# Das betrifft nur die Strecke Einsetzer→App; nach draussen komprimiert
|
|
||||||
# Pangolin weiterhin.
|
|
||||||
proxy_set_header Accept-Encoding "";
|
|
||||||
|
|
||||||
# Nur HTML anfassen. Bilder, JSON und Downloads gehen unberührt durch.
|
|
||||||
sub_filter_types text/html;
|
|
||||||
sub_filter_once on;
|
|
||||||
sub_filter '</head>'
|
|
||||||
'<script defer src="https://honeyos.duckdns.org/honeybar/honeybar.js" data-app="bentopdf"></script></head>';
|
|
||||||
|
|
||||||
# Falls die App eine eigene CSP schickt (BentoPDF tut es nicht), muss
|
|
||||||
# sie ersetzt werden — zwei CSP-Kopfzeilen heissen nicht „mehr erlaubt",
|
|
||||||
# sondern „beide müssen zustimmen". Dann diese zwei Zeilen einschalten:
|
|
||||||
#
|
|
||||||
# proxy_hide_header Content-Security-Policy;
|
|
||||||
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://honeyos.duckdns.org; connect-src 'self' https://honeyos.duckdns.org; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:" always;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -62,7 +62,6 @@ server {
|
|||||||
proxy_set_header Accept-Encoding "";
|
proxy_set_header Accept-Encoding "";
|
||||||
|
|
||||||
# Nur HTML anfassen; alles andere geht unberührt durch.
|
# Nur HTML anfassen; alles andere geht unberührt durch.
|
||||||
sub_filter_types text/html;
|
|
||||||
sub_filter_once on;
|
sub_filter_once on;
|
||||||
sub_filter '</head>'
|
sub_filter '</head>'
|
||||||
'<script defer src="https://honeyos.duckdns.org/honeybar/honeybar.js" data-app="$appid"></script></head>';
|
'<script defer src="https://honeyos.duckdns.org/honeybar/honeybar.js" data-app="$appid"></script></head>';
|
||||||
|
|||||||
Reference in new issue
Block a user