aboutsummaryrefslogtreecommitdiff
path: root/deploy
diff options
context:
space:
mode:
Diffstat (limited to 'deploy')
-rw-r--r--deploy/nginx-aendggner.conf101
-rwxr-xr-xdeploy/webpaket.sh73
2 files changed, 140 insertions, 34 deletions
diff --git a/deploy/nginx-aendggner.conf b/deploy/nginx-aendggner.conf
index 46eacb5..78677b5 100644
--- a/deploy/nginx-aendggner.conf
+++ b/deploy/nginx-aendggner.conf
@@ -1,47 +1,80 @@
-# Vorlage für die Auslieferung der ÄndGgner-Browserfassung. Es gibt keinen
-# Anwendungsprozess mehr: Der Server liefert nur statische Dateien aus
-# (Inhalt von target/web/), gerechnet wird im Browser.
+# Vorlage für die Auslieferung der ÄndGgner-Browserfassung unter
+# https://matthias.benkard.de/aendggner/.
#
-# Vor dem Einsatz anpassen: server_name, ssl_certificate(_key) (z. B. via certbot), root.
+# Es gibt keinen Anwendungsprozess: Der Server liefert nur statische Dateien aus
+# (Inhalt von target/web/), gerechnet wird im Browser. Weil der Dienst unter einem
+# Unterpfad einer bestehenden Domain läuft, ist dies kein eigener server-Block,
+# sondern ein Schnipsel zum Einfügen in den vorhandenen — Zertifikat, HTTP→HTTPS
+# und Domainname bleiben, wie sie sind:
+#
+# server { ... include /etc/nginx/snippets/aendggner.conf; ... }
+#
+# Ausgerollt wird nach /var/www/aendggner (siehe README.adoc, Abschnitt „Ausrollen“).
-server {
- listen 80;
- listen [::]:80;
- server_name aendggner.example.org;
- return 301 https://$host$request_uri;
+# Ohne diesen Schritt liefe /aendggner ins Leere, und alle relativen Verweise der
+# Seite gingen von / statt von /aendggner/ aus.
+location = /aendggner {
+ return 301 /aendggner/;
}
-server {
- listen 443 ssl http2;
- listen [::]:443 ssl http2;
- server_name aendggner.example.org;
-
- ssl_certificate /etc/letsencrypt/live/aendggner.example.org/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/aendggner.example.org/privkey.pem;
-
- # Inhalt von target/web/ — ohne aendggner.js.wat, das nur ein Zwischenschritt
- # des Übersetzers ist.
- root /var/www/aendggner;
+location /aendggner/ {
+ # Bewusst „root /var/www“ (mit dem Verzeichnis /var/www/aendggner) statt
+ # „alias /var/www/aendggner/“: In einer Präfix-Location löst try_files $uri
+ # gegen den root auf, nicht gegen den alias, und liefert dann 404 für alles.
+ root /var/www;
index index.html;
- # Ohne diesen Typ lehnt der Browser die Streaming-Instanziierung des Moduls ab.
+ try_files $uri $uri/ =404;
+
+ # Ohne den ersten Typ lehnt der Browser die Streaming-Instanziierung des Moduls
+ # ab. Ein types-Block in einer location ersetzt die Zuordnung vollständig, statt
+ # sie zu ergänzen — deshalb stehen hier alle vorkommenden Endungen.
types {
application/wasm wasm;
+ text/html html;
+ text/css css;
+ text/javascript js;
+ image/svg+xml svg;
+ text/plain txt;
+ application/gzip gz;
}
+ default_type application/octet-stream;
- gzip on;
- gzip_types application/wasm application/javascript text/css text/html;
- gzip_min_length 1024;
+ # Das Wasm-Modul ist rund 24 MB groß. deploy/webpaket.sh legt beim Bauen .gz
+ # und .br daneben; ausgeliefert wird die fertige Datei, statt sie je Abruf neu
+ # zu packen. gzip_static bringen die üblichen Distributionspakete mit;
+ # brotli_static setzt das Modul ngx_brotli voraus — fehlt es, die Zeile
+ # streichen, gzip_static genügt.
+ gzip_static on;
+ brotli_static on;
- # Das Wasm-Modul ist einige Megabyte groß und ändert sich nur mit einer neuen
- # Fassung; vorkomprimierte .gz/.br daneben zu legen lohnt sich (ngx_brotli bzw.
- # gzip_static).
- location ~ \.(wasm|js|css)$ {
- expires 7d;
- add_header Cache-Control "public";
- }
+ # Kein „expires 7d“: Die Dateinamen tragen keine Fassungskennung. Ein Browser
+ # mit altem app.js und neuem .wasm (oder umgekehrt) bekäme eine Mischfassung,
+ # die es nie gegeben hat. Stattdessen Revalidierung bei jedem Abruf; für das
+ # unveränderte Modul kostet das ein 304 ohne Rumpf.
+ add_header Cache-Control "no-cache" always;
- location / {
- try_files $uri $uri/ =404;
- }
+ # add_header in einer location verdrängt sämtliche vom server-Block geerbten
+ # Header. Alles, was hier gelten soll, muss deshalb hier stehen — auch ein
+ # dort gesetztes HSTS.
+ # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
+
+ # 'wasm-unsafe-eval' ist für die Instanziierung des Moduls nötig; echtes eval
+ # oder new Function kommt in der erzeugten aendggner.js nicht vor.
+ #
+ # style-src braucht 'unsafe-inline': Die fertige Synopse wird als blob:-Dokument
+ # geöffnet, und ein blob:-Dokument erbt die CSP des Dokuments, das es erzeugt
+ # hat. Der HtmlRenderer bettet sein Stylesheet als <style> ein — ohne die
+ # Freigabe erschiene die Synopse unformatiert. Skripte bleiben streng.
+ add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Referrer-Policy "no-referrer" always;
+ add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), usb=()" always;
}
+
+# Eine robots.txt unter /aendggner/ wäre wirkungslos: Maßgeblich ist allein die im
+# Wurzelverzeichnis der Domain. Wer die Seite von der Indizierung ausnehmen will,
+# trage dort ein:
+#
+# User-agent: *
+# Disallow: /aendggner/
diff --git a/deploy/webpaket.sh b/deploy/webpaket.sh
new file mode 100755
index 0000000..2003134
--- /dev/null
+++ b/deploy/webpaket.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# Macht aus dem Übersetzerergebnis ein auslieferbares Verzeichnis. Läuft in der Phase
+# „package“ des Profils -Pwasm, unmittelbar nach native-image.
+#
+# 1. Der Textzwischenschritt aendggner.js.wat (mehrere hundert Megabyte) wird entfernt.
+# 2. Der Quelltext der gebauten Fassung wird als Tarball beigelegt — AGPLv3 §13 verlangt
+# beim Netzwerkbetrieb einen Quellcode-Zugang für die Nutzer:innen, und der Footer der
+# Startseite verweist darauf.
+# 3. Die großen Dateien werden vorkomprimiert, damit nginx sie mit gzip_static bzw.
+# brotli_static ausliefern kann, statt 24 MB je Abruf neu zu packen.
+#
+# Aufruf: deploy/webpaket.sh <ausgabeverzeichnis> (Standard: target/web)
+
+set -eu
+
+ziel="${1:-target/web}"
+quelle="$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)"
+
+if [ ! -d "$ziel" ]; then
+ echo "webpaket: $ziel gibt es nicht — erst -Pwasm package laufen lassen." >&2
+ exit 1
+fi
+
+# 1. Zwischenschritt fort.
+rm -f "$ziel/aendggner.js.wat"
+
+# 2. Quelltext der gebauten Fassung.
+#
+# Ein veränderter Arbeitsbaum bricht den Bau ab: Der angebotene Quelltext muss der
+# ausgelieferten Fassung entsprechen, sonst ist die Auflage aus §13 verfehlt. Wer nur
+# ausprobieren will, setzt QUELLTEXT_UNGEPRUEFT=1.
+if ! git -C "$quelle" rev-parse --git-dir >/dev/null 2>&1; then
+ echo "webpaket: $quelle ist kein Git-Arbeitsbaum; ohne ihn lässt sich der Quelltext" >&2
+ echo " der gebauten Fassung nicht beilegen (AGPLv3 §13)." >&2
+ exit 1
+fi
+
+if [ -n "$(git -C "$quelle" status --porcelain)" ] && [ "${QUELLTEXT_UNGEPRUEFT:-}" != 1 ]; then
+ echo "webpaket: Der Arbeitsbaum trägt uneingecheckte Änderungen. Der beigelegte Quelltext" >&2
+ echo " wäre dann nicht der gebaute. Erst einchecken — oder für einen Probelauf" >&2
+ echo " QUELLTEXT_UNGEPRUEFT=1 setzen (dann nicht ausliefern)." >&2
+ exit 1
+fi
+
+fassung="$(git -C "$quelle" rev-parse HEAD)"
+git -C "$quelle" archive --format=tar.gz --prefix=aendggner/ HEAD \
+ -o "$(CDPATH='' cd -- "$ziel" && pwd)/aendggner-quelltext.tar.gz"
+
+{
+ echo "ÄndGgner — Quelltext der ausgelieferten Fassung"
+ echo
+ echo "Commit: $fassung"
+ echo "Gebaut: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
+ echo
+ echo "Vollständig in aendggner-quelltext.tar.gz; Bauanleitung darin in README.adoc."
+ echo "Fortlaufend: https://gerrit.benkard.de/plugins/gitiles/aendggner"
+} > "$ziel/quelltext-fassung.txt"
+
+# 3. Vorkompression. Der Tarball ist bereits gepackt und bleibt außen vor.
+for datei in "$ziel"/*.wasm "$ziel"/*.js "$ziel"/*.css "$ziel"/*.html "$ziel"/*.svg \
+ "$ziel"/*.txt; do
+ [ -f "$datei" ] || continue
+ gzip -9 -f -k -- "$datei"
+ if command -v brotli >/dev/null 2>&1; then
+ brotli -f -- "$datei"
+ fi
+done
+
+if ! command -v brotli >/dev/null 2>&1; then
+ echo "webpaket: brotli nicht gefunden — nur .gz angelegt." >&2
+fi
+
+echo "webpaket: $ziel ist auslieferbar (Fassung $(git -C "$quelle" rev-parse --short HEAD))."