fix: Ingress auf Traefik umstellen — die nginx-Annotationen waren wirkungslos
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m6s
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m6s
Der Zielcluster nutzt Traefik, nicht ingress-nginx. Saemtliche Annotationen der Form nginx.ingress.kubernetes.io/* ignoriert Traefik ersatzlos und still: sie erzeugen keinen Fehler, wirken aber auch nicht. Das betraf das Upload-Limit (proxy-body-size) ebenso wie die Zeitlimits, und ingressClassName zeigte auf "nginx". Damit aendert sich auch die Diagnose des fehlgeschlagenen Video-Uploads. Belegt aus Traefiks Quelltext (pkg/config/static/static_config.go, SetDefaults) und der Referenzdokumentation: - readTimeout des EntryPoints: Default 60s, beschrieben als "maximum duration for reading the entire request, including the body". Er umfasst also die gesamte Uebertragung des Uploads. Bei 20-100 MB ueber eine normale Upload-Leitung ist das schnell ueberschritten — 80 MB bei 10 Mbit/s sind rund 64 Sekunden. Laeuft er ab, kappt Traefik die Verbindung und antwortet mit einer eigenen HTML-Fehlerseite. Das ist die wahrscheinlichste Quelle des "Unexpected token '<'" im Browser. - responseHeaderTimeout: von SetDefaults NICHT gesetzt, bleibt 0, und "If zero, no timeout exists". Die Dauer des ffmpeg-Transcodings ist bei Traefik also NICHT begrenzt — anders als zuvor angenommen. - Body-Groesse: Traefik erzwingt keine Obergrenze (begrenzt ist nur maxHeaderBytes mit 1 MB fuer die Header). Ein Gegenstueck zu nginx' proxy-body-size wird nicht gebraucht. readTimeout gehoert zur statischen EntryPoint-Konfiguration und laesst sich nicht per Ingress-Annotation setzen. Das Manifest dokumentiert deshalb ausfuehrlich, welche Einstellung in der Traefik-Installation noetig ist (Helm: ports.websecure.transport.respondingTimeouts.readTimeout: 600s). Weiter angepasst: ingressClassName auf "traefik", HTTPS-Umleitung ueber eine Traefik-Middleware statt der nginx-Annotation, und der Hinweis zu X-Forwarded-For nennt jetzt Traefiks forwardedHeaders.trustedIPs — davon haengt ab, ob lib/rate-limit.ts dem Header trauen darf. Die Kommentare in lib/uploads.ts und lib/csrf.ts verwiesen auf die entfernten nginx-Einstellungen und sind entsprechend berichtigt.
This commit is contained in:
1 parent
72d36f4716
commit
e8f0bd4f55
3 files changed
+98
-47
No files matched your search
+83
-41
@@ -1,54 +1,83 @@
|
|||||||
# Setzt einen bereits im Cluster laufenden Ingress-Controller voraus (z.B.
|
# Ingress fuer Traefik (der im Zielcluster eingesetzte Ingress-Controller),
|
||||||
# ingress-nginx) sowie optional cert-manager fuer automatisches TLS. Host
|
# optional zusammen mit cert-manager fuer automatisches TLS. Host unten
|
||||||
# unten anpassen (muss zu NEXT_PUBLIC_SITE_URL in 01-configmap.yaml passen).
|
# anpassen (muss zu NEXT_PUBLIC_SITE_URL in 01-configmap.yaml passen).
|
||||||
#
|
#
|
||||||
# X-Forwarded-For: ingress-nginx setzt den Header standardmaessig korrekt
|
# ============================================================================
|
||||||
# (verwirft einen vom Client mitgeschickten Wert), lib/rate-limit.ts vertraut
|
# WICHTIG fuer die Admin-Uploads: eine Einstellung fehlt hier zwangslaeufig
|
||||||
# genau darauf — bei einem ANDEREN Ingress-Controller/zusaetzlichen Proxy
|
# ============================================================================
|
||||||
# davor unbedingt pruefen, dass er sich genauso verhaelt, siehe Kommentar in
|
# Der Video-Upload (bis 100 MB, MAX_UPLOAD_VIDEO_BYTES in lib/uploads.ts)
|
||||||
# lib/rate-limit.ts.
|
# scheitert mit Traefiks Standardeinstellung, sobald die Uebertragung laenger
|
||||||
|
# als 60 Sekunden dauert. Das ist bei 20-100 MB ueber eine normale
|
||||||
|
# Upload-Leitung schnell erreicht: 80 MB bei 10 Mbit/s sind rund 64 Sekunden.
|
||||||
|
#
|
||||||
|
# Verantwortlich ist "readTimeout" des EntryPoints. Traefik beschreibt ihn als
|
||||||
|
# "maximum duration for reading the entire request, including the body" — er
|
||||||
|
# umfasst also die gesamte Uebertragung des Uploads, nicht nur die Header.
|
||||||
|
# Der Standardwert ist 60s. Laeuft er ab, kappt Traefik die Verbindung und
|
||||||
|
# antwortet mit einer eigenen HTML-Fehlerseite; im Browser erschien davon nur
|
||||||
|
# "Unexpected token '<', "<!DOCTYPE "... is not valid JSON", weil die Antwort
|
||||||
|
# kein JSON mehr war (lib/upload-client.ts meldet diesen Fall inzwischen mit
|
||||||
|
# dem HTTP-Status statt als Parse-Fehler).
|
||||||
|
#
|
||||||
|
# Dieser Wert laesst sich NICHT per Ingress-Annotation setzen — er gehoert zur
|
||||||
|
# statischen Konfiguration des EntryPoints und damit in die Traefik-
|
||||||
|
# Installation selbst. Mit dem offiziellen Helm-Chart:
|
||||||
|
#
|
||||||
|
# ports:
|
||||||
|
# websecure:
|
||||||
|
# transport:
|
||||||
|
# respondingTimeouts:
|
||||||
|
# readTimeout: 600s
|
||||||
|
#
|
||||||
|
# 600s deckt auch eine langsame Leitung ab. Ein hoher Wert ist hier
|
||||||
|
# unbedenklich, weil er nur greift, solange tatsaechlich Daten fliessen.
|
||||||
|
#
|
||||||
|
# Zwei Dinge, die bei Traefik ausdruecklich KEIN Problem sind:
|
||||||
|
#
|
||||||
|
# - Body-Groesse: Traefik erzwingt keine Obergrenze fuer den Request-Body
|
||||||
|
# (begrenzt ist nur maxHeaderBytes mit 1 MB, und das betrifft die Header).
|
||||||
|
# Eine Entsprechung zu nginx' "proxy-body-size" wird hier also nicht
|
||||||
|
# gebraucht. Nur falls eine "buffering"-Middleware im Einsatz ist, deren
|
||||||
|
# maxRequestBodyBytes pruefen.
|
||||||
|
# - Verarbeitungsdauer: "responseHeaderTimeout" (die Wartezeit auf die Antwort
|
||||||
|
# des Backends, also das ffmpeg-Transcoding) ist per Default 0 = kein Limit.
|
||||||
|
# Das Umrechnen darf demnach so lange dauern wie noetig; die Anwendung
|
||||||
|
# begrenzt es selbst auf 5 Minuten (TRANSCODE_TIMEOUT_MS in
|
||||||
|
# lib/video-transcode.ts).
|
||||||
|
#
|
||||||
|
# Sitzt zusaetzlich ein CDN davor (z.B. Cloudflare), gilt dessen eigenes
|
||||||
|
# Origin-Zeitlimit unabhaengig hiervon weiter — bei Cloudflare 100 Sekunden
|
||||||
|
# (Fehler 524), ebenfalls als HTML-Seite.
|
||||||
|
#
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# X-Forwarded-For: lib/rate-limit.ts vertraut darauf, dass der Proxy diesen
|
||||||
|
# Header selbst setzt und einen vom Client mitgeschickten Wert verwirft —
|
||||||
|
# sonst liessen sich saemtliche Rate-Limits (Login, 2FA, Checkout) umgehen.
|
||||||
|
# Bei Traefik haengt das an "forwardedHeaders.trustedIPs" des EntryPoints:
|
||||||
|
# nur fuer dort aufgefuehrte Absender werden eingehende X-Forwarded-*-Header
|
||||||
|
# uebernommen, von allen anderen werden sie ersetzt. Vor dem Produktivbetrieb
|
||||||
|
# pruefen, dass diese Liste nicht unbeabsichtigt weit gefasst ist.
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: wabenhain
|
name: wabenhain
|
||||||
namespace: wabenhain
|
namespace: wabenhain
|
||||||
annotations:
|
annotations:
|
||||||
# Nur relevant, falls cert-manager installiert ist — sonst diese beiden
|
# Nur relevant, falls cert-manager installiert ist — sonst diese Zeile
|
||||||
# Zeilen und den "tls:"-Block unten entfernen und Host per HTTP betreiben
|
# und den "tls:"-Block unten entfernen und den Host per HTTP betreiben
|
||||||
# oder TLS auf andere Weise terminieren.
|
# oder TLS auf andere Weise terminieren.
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
# Erzwingt HTTPS ueber die Middleware unten. Legt die Traefik-Installation
|
||||||
# ingress-nginx laesst ohne diese Annotation standardmaessig nur 1 MB pro
|
# die Umleitung bereits EntryPoint-weit an (Helm-Chart:
|
||||||
# Request durch — weit unter dem Bild- (25 MB, lib/uploads.ts
|
# ports.web.redirectTo.port=websecure), sind diese Zeile UND die
|
||||||
# MAX_UPLOAD_BYTES) und erst recht dem Video-Limit (100 MB, lib/uploads.ts
|
# Middleware am Ende der Datei ueberfluessig. Der Namespace-Praefix im
|
||||||
# MAX_UPLOAD_VIDEO_BYTES) der Admin-Uploads. Ohne diese Zeile bricht
|
# Wert ist Pflicht — Traefik loest den Namen sonst nicht auf.
|
||||||
# nginx jeden Upload jenseits von 1 MB vor der App ab (Verbindungsabbruch
|
traefik.ingress.kubernetes.io/router.middlewares: wabenhain-redirect-to-https@kubernetescrd
|
||||||
# ohne verwertbare Fehlermeldung im Browser) — 110m deckt das groessere
|
|
||||||
# der beiden Limits (100 MB) plus etwas Spielraum fuer den
|
|
||||||
# multipart/form-data-Overhead ab.
|
|
||||||
nginx.ingress.kubernetes.io/proxy-body-size: "110m"
|
|
||||||
# Ohne diese drei Annotationen wartet ingress-nginx nur 60 Sekunden je
|
|
||||||
# Phase und liefert danach eine eigene HTML-Fehlerseite (504) aus. Der
|
|
||||||
# Video-Upload braucht regelmaessig laenger: erst muessen bis zu 100 MB
|
|
||||||
# ueber die Leitung, danach rechnet ffmpeg das Video fuers Web um (siehe
|
|
||||||
# lib/video-transcode.ts). Im Browser kam davon nur an, dass die Antwort
|
|
||||||
# kein JSON war ("Unexpected token '<', "<!DOCTYPE "... is not valid
|
|
||||||
# JSON") — die Ursache war daraus nicht ablesbar.
|
|
||||||
#
|
|
||||||
# 600s deckt den langsamsten realistischen Fall ab und liegt bewusst
|
|
||||||
# oberhalb des harten ffmpeg-Zeitlimits von 5 Minuten
|
|
||||||
# (TRANSCODE_TIMEOUT_MS in lib/video-transcode.ts): so bricht im Zweifel
|
|
||||||
# die Anwendung selbst mit einer verwertbaren JSON-Meldung ab, statt dass
|
|
||||||
# nginx die Verbindung vorher kappt.
|
|
||||||
#
|
|
||||||
# Achtung: sitzt zusaetzlich ein CDN davor (z.B. Cloudflare), gilt dessen
|
|
||||||
# eigenes Origin-Zeitlimit unabhaengig hiervon weiter — bei Cloudflare
|
|
||||||
# sind das 100 Sekunden (Fehler 524), ebenfalls als HTML-Seite.
|
|
||||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
|
||||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
|
||||||
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
|
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
# Muss zur IngressClass der Traefik-Installation passen. Das offizielle
|
||||||
|
# Helm-Chart legt sie als "traefik" an; mit "kubectl get ingressclass"
|
||||||
|
# pruefen, falls der Ingress ohne Wirkung bleibt.
|
||||||
|
ingressClassName: traefik
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
- shop.example.com
|
- shop.example.com
|
||||||
@@ -64,3 +93,16 @@ spec:
|
|||||||
name: wabenhain-app
|
name: wabenhain-app
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
|
---
|
||||||
|
# Leitet HTTP dauerhaft auf HTTPS um; wird von der Annotation oben
|
||||||
|
# referenziert. Entfaellt samt Annotation, wenn die Umleitung bereits
|
||||||
|
# EntryPoint-weit konfiguriert ist.
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: redirect-to-https
|
||||||
|
namespace: wabenhain
|
||||||
|
spec:
|
||||||
|
redirectScheme:
|
||||||
|
scheme: https
|
||||||
|
permanent: true
|
||||||
+6
-3
@@ -5,9 +5,12 @@ import { getBaseUrl } from "@/lib/url";
|
|||||||
*
|
*
|
||||||
* Reihenfolge nach Verlaesslichkeit:
|
* Reihenfolge nach Verlaesslichkeit:
|
||||||
* 1. X-Forwarded-Host — der oeffentliche Host, den der eigene Reverse-Proxy
|
* 1. X-Forwarded-Host — der oeffentliche Host, den der eigene Reverse-Proxy
|
||||||
* (Caddy/nginx, siehe Caddyfile bzw. k8s/06-ingress.yaml) weiterreicht.
|
* (Caddy bzw. Traefik, siehe Caddyfile bzw. k8s/06-ingress.yaml)
|
||||||
* Caddy verwirft einen vom Client mitgeschickten X-Forwarded-Host per
|
* weiterreicht. Caddy verwirft einen vom Client mitgeschickten
|
||||||
* Default, der Wert stammt hier also immer vom Proxy selbst.
|
* X-Forwarded-Host per Default; bei Traefik entscheidet darueber
|
||||||
|
* "forwardedHeaders.trustedIPs" des EntryPoints — nur fuer dort
|
||||||
|
* aufgefuehrte Absender werden eingehende X-Forwarded-*-Header
|
||||||
|
* uebernommen, von allen anderen ersetzt.
|
||||||
* 2. Host — der Host, den der Browser tatsaechlich adressiert hat. Das ist
|
* 2. Host — der Host, den der Browser tatsaechlich adressiert hat. Das ist
|
||||||
* die massgebliche Groesse fuer eine Same-Origin-Pruefung und genau der
|
* die massgebliche Groesse fuer eine Same-Origin-Pruefung und genau der
|
||||||
* Vergleich, den Next.js fuer Server Actions intern selbst anstellt.
|
* Vergleich, den Next.js fuer Server Actions intern selbst anstellt.
|
||||||
|
|||||||
+9
-3
@@ -67,9 +67,15 @@ export const MAX_INPUT_PIXELS = 100_000_000;
|
|||||||
* groesser sind. Diese Grenze gilt fuer die ROHDATEI vor dem Transcoding
|
* groesser sind. Diese Grenze gilt fuer die ROHDATEI vor dem Transcoding
|
||||||
* (siehe lib/video-transcode.ts transcodeVideoForWeb, aufgerufen in
|
* (siehe lib/video-transcode.ts transcodeVideoForWeb, aufgerufen in
|
||||||
* app/api/admin/upload-video/route.ts) — das tatsaechlich gespeicherte,
|
* app/api/admin/upload-video/route.ts) — das tatsaechlich gespeicherte,
|
||||||
* web-optimierte Ergebnis ist danach in aller Regel deutlich kleiner. Muss
|
* web-optimierte Ergebnis ist danach in aller Regel deutlich kleiner.
|
||||||
* zum "proxy-body-size" in k8s/06-ingress.yaml passen (dort grosszuegiger
|
*
|
||||||
* bemessen).
|
* Der vorgeschaltete Proxy muss eine Datei dieser Groesse durchlassen. Bei
|
||||||
|
* Traefik (dem hier eingesetzten Ingress-Controller) gibt es keine Grenze
|
||||||
|
* fuer die Body-Groesse, wohl aber eine fuer die Uebertragungsdauer:
|
||||||
|
* "readTimeout" des EntryPoints umfasst das Lesen des gesamten Requests
|
||||||
|
* inklusive Body und steht per Default auf 60s — zu wenig, um 100 MB ueber
|
||||||
|
* eine normale Upload-Leitung zu schicken. Siehe die ausfuehrliche Notiz in
|
||||||
|
* k8s/06-ingress.yaml.
|
||||||
*/
|
*/
|
||||||
export const MAX_UPLOAD_VIDEO_BYTES = 100 * 1024 * 1024;
|
export const MAX_UPLOAD_VIDEO_BYTES = 100 * 1024 * 1024;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user