expositions n etait resolu nulle part — ni par le pare-feu d hote, qui ne filtre pas l egress, ni par le devis de frontiere. Il n avait jamais eu besoin de l etre : chez un locataire le trafic edge vers amont ne traverse pas le boitier. Au site il le traverse. Une declaration peut dormir des mois avant que la premiere fabric ne la reveille. Une regle par amont, avec son port. Jamais une regle large : ouvrir l edge sur tout le site annulerait ce qu une zone de publication separee cherche a obtenir. La meme faute qu hier, avalee de la meme facon : U au lieu de underlay_mod, et un except Exception large a transforme le NameError en note plausible. Resserre a OSError/ValueError — une faute de frappe doit faire du bruit. Le schema de proxy_pass suit maintenant le port, et l amont TLS est VERIFIE contre l AC interne : relayer en TLS sans verifier ne fait que deplacer la confiance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
89 lines
4.3 KiB
Django/Jinja
89 lines
4.3 KiB
Django/Jinja
# Vhosts derives des applications (champ 'expose' de instance/plan/applications.yml,
|
|
# edge depuis instance/plan/domaines.yml) — gere par Set-OPS (role serveur_nginx).
|
|
# Ne pas editer a la main.
|
|
#
|
|
# Un FQDN expose produit un vhost si l'hote de l'application est resolvable :
|
|
# amont = http://<IP interne de l'hote de l'application>:<port>. Une exposition
|
|
# non resolvable (application sans hote actif, ou sans port) est listee mais
|
|
# NON publiee (binding declare, pas encore cablable).
|
|
{% if serveur_nginx_expositions | default([]) | selectattr('websocket', 'defined') | selectattr('websocket') | list | length > 0 %}
|
|
# Une exposition au moins est WebSocket-aware (ex. Collabora) : map d'upgrade.
|
|
map $http_upgrade $connection_upgrade { default upgrade; "" close; }
|
|
{% endif %}
|
|
{% for expo in serveur_nginx_expositions | default([]) %}
|
|
{% set ns = namespace(amont='') %}
|
|
{#- LE SCHEMA DE L'AMONT SUIT LE PORT DE L'AMONT (2026-09-15).
|
|
`http://` etait ecrit en dur. Un service qui termine DEJA le TLS — la forge, sur son
|
|
443 — recevait donc du clair sur un port qui attend une poignee de main, et rendait
|
|
`400 Bad Request`. Du TLS parfait a l'entree, une erreur de protocole a la sortie.
|
|
On ne devine pas : un amont declare sur un port de TLS se parle en TLS. La liste est
|
|
reglable (`serveur_nginx_ports_tls`) parce qu'un service peut servir du TLS ailleurs
|
|
que sur 443. -#}
|
|
{% if expo.hote in hostvars and hostvars[expo.hote].ansible_host is defined and expo.port %}
|
|
{% set _schema = 'https://' if (expo.port | int) in (serveur_nginx_ports_tls | default([443, 8443])) else 'http://' %}
|
|
{% set ns.amont = _schema ~ hostvars[expo.hote].ansible_host ~ ':' ~ expo.port %}
|
|
{% endif %}
|
|
{% if ns.amont %}
|
|
|
|
# {{ expo.fqdn }} -> application {{ expo.application }} sur {{ expo.hote }} ({{ ns.amont }})
|
|
{% if serveur_nginx_redirection_http | bool %}
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name {{ expo.fqdn }};
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
{% endif %}
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
http2 on;
|
|
server_name {{ expo.fqdn }};
|
|
|
|
ssl_certificate {{ serveur_nginx_certificat }};
|
|
ssl_certificate_key {{ serveur_nginx_cle }};
|
|
|
|
location / {
|
|
proxy_pass {{ ns.amont }};
|
|
{% if ns.amont.startswith('https://') %}
|
|
# ON VERIFIE L'AMONT, PAS SEULEMENT LE VISITEUR. Un edge qui relaie en TLS sans
|
|
# verifier ne fait que DEPLACER la confiance : le visiteur croit l'edge, l'edge
|
|
# ne croit personne. L'AC interne est la meme des deux cotes.
|
|
proxy_ssl_verify on;
|
|
proxy_ssl_trusted_certificate {{ serveur_nginx_ca_interne }};
|
|
proxy_ssl_name {{ expo.fqdn }};
|
|
proxy_ssl_server_name on;
|
|
{% endif %}
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
# Tampons d'en-tetes de reponse. Le defaut de nginx (4 Ko) ne suffit PAS
|
|
# derriere une authentification OIDC : le cookie de session porte le jeton
|
|
# d'identite, decoupe en plusieurs `Set-Cookie`, et nginx repond alors 502
|
|
# « upstream sent too big header » — juste apres une authentification
|
|
# REUSSIE, ce qui rend le symptome trompeur. Constate le 2026-08-08 sur
|
|
# icinga.chezlepro.internal, ou oauth2-proxy journalisait `AuthSuccess`
|
|
# pendant que le navigateur recevait une erreur de passerelle.
|
|
#
|
|
# Pose pour TOUTES les expositions : c'est une propriete du proxy, pas du
|
|
# service, et le prochain service place derriere un IdP rencontrerait le
|
|
# meme mur.
|
|
proxy_buffer_size {{ serveur_nginx_proxy_buffer_size }};
|
|
proxy_buffers {{ serveur_nginx_proxy_buffers }};
|
|
proxy_busy_buffers_size {{ serveur_nginx_proxy_busy_buffers_size }};
|
|
{% if expo.websocket | default(false) %}
|
|
# Collabora & co : editeur en direct via WebSocket (upgrade + longue tenue).
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade;
|
|
proxy_read_timeout 3600s;
|
|
{% endif %}
|
|
}
|
|
}
|
|
{% else %}
|
|
|
|
# (non publie) {{ expo.fqdn }} -> application {{ expo.application }} : amont non resolvable (hote sans IP active ou sans port).
|
|
{% endif %}
|
|
{% endfor %}
|