Omgekeerde proxy — architectuurnotitie¶
Machinevertaling van het Franse origineel.
De volledige route¶
flowchart TB
subgraph internet["Internet"]
client["Client<br/>navigateur"]
end
subgraph ovh["OVH"]
dns["Zone DNS savethemall.eu<br/>A → <IP publique>"]
end
subgraph maison["LAN <sous-reseau LAN>"]
velop["Routeur Velop<br/>NAT 443 → nas:8080"]
subgraph nas["NAS Synology · le NAS"]
nascaddy["nas-caddy — EDGE WAN<br/>TLS explicite, certs copiés"]
statique["Sites statiques locaux<br/>weber · chroniques · rgpd"]
end
subgraph vmd["vm-docker · vm-docker"]
caddy["Caddy<br/>routage interne + CrowdSec"]
authentik["Authentik<br/>outpost embarqué"]
app["Conteneur applicatif<br/>infrastructure_network"]
end
end
client -->|"résout le nom"| dns
client -->|"HTTPS 443"| velop
velop --> nascaddy
nascaddy -->|"local"| statique
nascaddy -->|"import to_vmdocker<br/>header_up Host"| caddy
caddy -->|"forward_auth"| authentik
authentik -.->|"302 vers auth.savethemall.eu<br/>si pas de session"| client
caddy -->|"reverse_proxy nom:port"| app
Waarom twee lagen¶
Eén zou voldoende zijn. Er zijn er twee omdat beide machines niet hetzelfde kunnen doen:
- De NAS is de enige die toegang heeft tot het WAN. De NAT van de router wijst naar deze. Hij bevat dus de statische websites die moeten blijven werken, zelfs als vm-docker uitvalt.
- vm-docker is de enige die blokkades kan uitvoeren. Het NAS draait op een DSM-kernel zonder
xt_set, zonder boom/lib/modules, ensudovereist een wachtwoord: er zijn geen L3-blokkades mogelijk. De CrowdSec-oplossing draait dus op vm-docker, met 403 applicaties.
De twee gevolgen die uren kosten¶
Een L3-filter op vm-docker blokkeert niets
Alle WAN-verzoeken komen op vm-docker binnen met nas als bron.
Een regel per client-IP is fundamenteel ondoordacht.
Wat wel werkt: trusted_proxies static le NAS/32 in de globale instellingen van Caddy, waardoor het de X-Forwarded-For van nas-caddy leest. De modules die de client-IP gebruiken (waaronder CrowdSec) zien dan de echte.
De statische bestanden die via het NAS worden aangeboden, gaan niet via CrowdSec
Ze gaan nooit door Caddy van vm-docker. De L7-oplossing beschermt ze niet. Dit is een bewuste beperking, geen vergissing.
Authenticatie¶
De code die gedeeld is, in het Caddyfile van vm-docker:
(authentik) {
forward_auth authentik-server:9000 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-authentik-username X-authentik-groups X-authentik-email X-authentik-name
}
}
En de overeenkomstige oplossing voor statische bestanden die lokaal via het NAS worden aangeboden:
(authentik_edge) {
forward_auth vm-docker:9092 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-authentik-username X-authentik-groups X-authentik-email X-authentik-name
}
}
Wat forward_auth doet, per verzoek¶
sequenceDiagram
autonumber
participant N as Navigateur
participant C as Caddy
participant A as Authentik
participant S as Service
N->>C: GET /une-page
C->>A: sous-requête /outpost.goauthentik.io/auth/caddy
alt pas de session
A-->>C: 401
C-->>N: 302 vers auth.savethemall.eu
N->>A: connexion
A-->>N: 302 vers /outpost.goauthentik.io/callback
Note over N,C: c'est CETTE route qui doit exister<br/>dans un `handle` frère, sinon le callback<br/>tombe dans le reverse_proxy de l'app
N->>C: GET /outpost.goauthentik.io/callback
C->>A: relais vers authentik-server:9000
A-->>N: cookie de session, puis 302 vers /une-page
N->>C: GET /une-page (avec le cookie)
end
C->>A: sous-requête d'autorisation
A-->>C: 200 + en-têtes X-authentik-*
C->>S: reverse_proxy, en-têtes recopiés
S-->>N: la page
De service ziet nooit het wachtwoord: het ontvangt een verzoek dat al is geautoriseerd, met de identiteit in de headers. Dit is ook zijn beperking: hij kan niet weten of een sessie is beëindigd.
forward_auth gaat voor handle¶
L'ordre d'exécution des directives Caddy n'est pas l'ordre du fichier.
forward_auth est ordonné avant handle et reverse_proxy : il attrape la
requête en premier, quoi qu'on écrive après lui — dans le même bloc comme dans un
snippet importé.
flowchart TB
req["Requête<br/>/outpost.goauthentik.io/callback"]
subgraph fichier["Ce que le fichier donne à lire"]
f1["1. handle /outpost…/*"]
f2["2. forward_auth"]
f3["3. reverse_proxy app"]
end
subgraph reel["L'ordre réellement appliqué par Caddy"]
r1["1. forward_auth"]
r2["2. handle"]
r3["3. reverse_proxy"]
end
req --> fichier
fichier -.->|"n'est PAS<br/>l'ordre d'exécution"| reel
r1 --> perdu["forward_auth intercepte<br/>→ 302, boucle de connexion"]
classDef bad stroke-width:3px,stroke-dasharray: 5 5
class perdu bad
Conséquence : la route de l'outpost ne peut pas être posée une fois pour toutes
dans le snippet (authentik). Les deux écritures suivantes ont été essayées, elles
passent caddy validate, se rechargent sans erreur, et ne changent rien :
(authentik) {
handle /outpost.goauthentik.io/* { reverse_proxy authentik-server:9000 } # sans effet
reverse_proxy /outpost.goauthentik.io/* authentik-server:9000 # sans effet
forward_auth authentik-server:9000 { ... }
}
Le seul endroit où elle prend est le site, avec l'application elle aussi dans un
handle : deux handle frères sont des routes mutuellement exclusives, et la plus
spécifique gagne avant que forward_auth n'entre en jeu.
site.savethemall.eu {
import tls_cert site.savethemall.eu
handle /outpost.goauthentik.io/* {
reverse_proxy authentik-server:9000
}
handle {
import authentik
reverse_proxy monservice:8080
}
}
La sonde qui distingue les deux formes en une requête, sans compte ni session :
curl -s -o /dev/null -w '%{http_code}\n' https://<site>/outpost.goauthentik.io/zzz
# 404 → la route outpost existe, c'est Authentik qui répond
# 302 → pas de route ; forward_auth a intercepté
Ce n'est pas une panne pour les sites qui ne l'ont pas
Van de 39 sites die authentik gebruiken, zijn er 4 die dit handle gebruiken. De andere 35 werken. Het handle is een versterking conform de Authenticik-documentatie, en geen vereiste voor het functioneren. Het generaliseren van de vorm vereist het herschrijven van 35 blokken één voor één — een klus die veel tijd kost, waarbij de sonde als criterium dient, en niet sed.
OIDC eerst, forward_auth daarna, nooit beide¶
Voor een nieuw service, eerst integrations.goauthentik.io/documentation/<service> onderzoeken. Als de applicatie OIDC ondersteunt, kan deze worden aangesloten.
forward_auth kent de applicatie-identiteit niet: geen account, geen rol, geen eigen afmelden — alleen een muur. Het blijft een goede optie voor situaties waarin er geen sprake is van gebruikers, zoals een statische website.
Het gebruik van beide tegelijk is een valkuil
De proxy vangt /api/auth/callback/* op voordat de applicatie → een lus van verbinding, met drie componenten die allemaal goed lijken.
Sluit de lokale verbinding (DISABLE_PASSWORD_AUTH) na de eerste succesvolle OIDC-verbinding, nooit eerder.
Certificaten¶
- Uitgegeven door acme.sh via DNS-01 bij OVH, één certificaat per domein, in
/opt/docker/acme/certs/<fqdn>/. - Expliciet geladen:
auto_https offin beide Caddy-instanties, en een macro(tls_cert)per site. - Herladen na vernieuwing via de Caddy-beheer API
(
POST http://127.0.0.1:2019/load), publiée sur la boucle locale uniquement. - Le NAS n'émet rien : les certificats lui sont copiés par un
rsyncquotidien depuis vm-docker.
Een detail dat veel impact heeft: een upstream-server geïdentificeerd via zijn IP-adres¶
Caddy stuurt vervolgens Host: nas:443. De meeste applicaties maken hierbij een absolute redirect, en de client gaat naar een intern adres. Het is altijd belangrijk om toe te voegen:
Dernière vérification de cette page : 2026-09-08