Génération contrib : vhosts nginx h3 edge + appsettings (jq) + QUIC logiciel Kestrel
Vhosts nginx (contrib/template.vhost + Makefile, envsubst allowlist) :
HTTP/3 edge per contrib/quic.md — listen 443 quic reuseport (Org only),
http2/http3 on, Alt-Svc, ssl_protocols TLSv1.3. Cibles generate-nginx /
generate-nginx-prod -> generated/nginx/ (gitignoré). Allowlist préserve
les variables nginx intrinsèques ($host, $scheme, ...).
Appsettings (contrib/template.appsettings-{org,api,blogs}.json + Makefile) :
génération via jq (et non envsubst) car le mot de passe SMTP contient un qui cassait le JSON. Substitue $DOMAIN (DOMAIN_ROOT) partout et injecte
$SMTP_PASSWORD depuis env (échappement JSON, secret non exposé en cmd).
Cible generate-appsettings -> generated/appsettings/.
QUIC logiciel : Kestrel:Endpoints:Https:Protocols = Http1AndHttp2AndHttp3
sur les 3 templates appsettings (capacité, non joignable derrière nginx).
Fix bonus : générateur systemd HTTP_PORT"$($*_PORT)" (était
"$*_$(HTTP_PORT)" -> port vide localhost:Org_).
Runbook contrib/README.nginx.md. Prérequis host : nginx http_v3_module,
UDP 443, libmsquic (Kestrel h3).
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
3fe7b8b46c
commit
5553f2f290
6 changed files with 132 additions and 83 deletions
|
|
@ -49,8 +49,9 @@ generate-service-units: generated/$(APP_INSTALL_PREFIX)yavscOrg.service generate
|
||||||
# make generate-nginx -> generated/nginx/preprod-yavsc{Org,Blogs,Api}
|
# make generate-nginx -> generated/nginx/preprod-yavsc{Org,Blogs,Api}
|
||||||
# make generate-nginx APP_INSTALL_ENV=production APP_INSTALL_PREFIX= -> prod (yavsc/api/blogs.pschneider.fr)
|
# make generate-nginx APP_INSTALL_ENV=production APP_INSTALL_PREFIX= -> prod (yavsc/api/blogs.pschneider.fr)
|
||||||
#
|
#
|
||||||
# HTTP/3 (QUIC) termine au edge nginx ; Kestrel reste en h1/h2 backend.
|
# HTTP/3 (QUIC) termine au edge nginx (plan contrib/quic.md). Requiert nginx
|
||||||
# Les vhosts générés vont dans generated/nginx/ (gitignoré, comme generated/*.service).
|
# avec http_v3_module + UDP 443 ouvert. reuseport sur Org uniquement
|
||||||
|
# (QUIC_OPTS). Les vhosts générés vont dans generated/nginx/ (gitignoré).
|
||||||
NGINX_TEMPLATE := template.vhost
|
NGINX_TEMPLATE := template.vhost
|
||||||
NGINX_OUT_DIR := generated/nginx
|
NGINX_OUT_DIR := generated/nginx
|
||||||
NGINX_HOSTS := Org Blogs Api
|
NGINX_HOSTS := Org Blogs Api
|
||||||
|
|
@ -83,6 +84,32 @@ generate-nginx-prod:
|
||||||
$(NGINX_OUT_DIR):
|
$(NGINX_OUT_DIR):
|
||||||
@mkdir -p $@
|
@mkdir -p $@
|
||||||
|
|
||||||
|
# --- appsettings (générés depuis template.appsettings-*.json) ---
|
||||||
|
# make generate-appsettings -> generated/appsettings/appsettings-{org,api,blogs}.json
|
||||||
|
#
|
||||||
|
# Substitue $DOMAIN (racine du domaine, cf. DOMAIN_ROOT) dans toutes les
|
||||||
|
# chaînes, et $SMTP_PASSWORD (secret du .env) dans Smtp.Password.
|
||||||
|
# On utilise jq (et non envsubst) car envsubst n'échappe pas le JSON : un
|
||||||
|
# secret contenant \ ou " casserait le JSON (le mot de passe SMTP actuel
|
||||||
|
# contient un \). jq lit le secret depuis l'environnement (export) ->
|
||||||
|
# échappement JSON correct, aucun shell-quoting ni exposition en ligne de cmd.
|
||||||
|
# Les templates actuels sont issus de la preprod (préfixe preprod-, ports
|
||||||
|
# 88-93) ; pour la prod il faudra un template prod (ports 82-87, sans préfixe).
|
||||||
|
export ASPNETCORE_Smtp__Password
|
||||||
|
APPSETTINGS_OUT_DIR := generated/appsettings
|
||||||
|
APPSETTINGS_HOSTS := org api blogs
|
||||||
|
|
||||||
|
$(APPSETTINGS_OUT_DIR)/appsettings-%.json: $(APPSETTINGS_OUT_DIR) template.appsettings-%.json .$(APP_INSTALL_ENV).env
|
||||||
|
@jq --arg domain "$(DOMAIN_ROOT)" \
|
||||||
|
'walk(if type=="string" then gsub("\\$$DOMAIN"; $$domain) else . end) | if has("Smtp") then .Smtp.Password = env["ASPNETCORE_Smtp__Password"] else . end' \
|
||||||
|
template.appsettings-$*.json > $@
|
||||||
|
@echo Created appsettings: $@
|
||||||
|
|
||||||
|
generate-appsettings: $(addprefix $(APPSETTINGS_OUT_DIR)/appsettings-,$(addsuffix .json,$(APPSETTINGS_HOSTS)))
|
||||||
|
|
||||||
|
$(APPSETTINGS_OUT_DIR):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
install-service-%: /etc/systemd/system/$(APP_INSTALL_PREFIX)yavsc%.service
|
install-service-%: /etc/systemd/system/$(APP_INSTALL_PREFIX)yavsc%.service
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable $(APP_INSTALL_PREFIX)yavsc$*
|
sudo systemctl enable $(APP_INSTALL_PREFIX)yavsc$*
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,50 @@
|
||||||
Les vhosts nginx sont **générés** à l'image des unités de service systemd
|
Les vhosts nginx sont **générés** à l'image des unités de service systemd
|
||||||
(`template.service` + `envsubst` → `generated/*.service`) : un template
|
(`template.service` + `envsubst` → `generated/*.service`) : un template
|
||||||
unique `contrib/template.vhost` est substitué par `envsubst` vers
|
unique `contrib/template.vhost` est substitué par `envsubst` vers
|
||||||
`contrib/generated/nginx/`, qui est **gitignoré** (comme `generated/`).
|
`contrib/generated/nginx/`, qui est **gitignoré**.
|
||||||
|
|
||||||
HTTP/3 (QUIC) **termine au edge nginx** sur 443/udp. Kestrel reste en
|
## Rôle de nginx : edge HTTP/3 (plan `contrib/quic.md`)
|
||||||
h1/h2 sur son port backend ; nginx lui parle en HTTP/1.1
|
|
||||||
(`proxy_http_version 1.1`).
|
nginx termine HTTP/3 (QUIC) au edge sur 443/udp, en coexistence avec
|
||||||
|
h1/h2 sur 443/tcp. Kestrel reste backend h1/h2 (`proxy_http_version 1.1`).
|
||||||
|
Le « QUIC logiciel » Kestrel (`Protocols: Http1AndHttp2AndHttp3` dans les
|
||||||
|
`template.appsettings-*.json`) est une capacité complémentaire — non
|
||||||
|
joignable par les clients derrière nginx (nginx OSS = upstream h1.1
|
||||||
|
uniquement), mais disponible si Kestrel est un jour exposé en direct.
|
||||||
|
|
||||||
|
Directives générées (par vhost) :
|
||||||
|
```nginx
|
||||||
|
listen 443 ssl; listen [::]:443 ssl; http2 on;
|
||||||
|
listen 443 quic $QUIC_OPTS; listen [::]:443 quic $QUIC_OPTS; http3 on;
|
||||||
|
add_header Alt-Svc 'h3=":443"; ma=86400' always;
|
||||||
|
ssl_protocols TLSv1.3;
|
||||||
|
```
|
||||||
|
`$QUIC_OPTS = reuseport` sur **Org uniquement**, vide sur Api/Blogs (règle
|
||||||
|
`contrib/quic.md` : `reuseport` une seule fois par addr:port, tous vhosts
|
||||||
|
confondus).
|
||||||
|
|
||||||
|
## Prérequis côté host
|
||||||
|
|
||||||
|
1. **nginx avec `http_v3_module`** (≥ 1.25 ; la directive `http3 on;`
|
||||||
|
exige ≥ 1.27.4 — sur une version 1.25–1.27.3, retirer `http3 on;`,
|
||||||
|
`listen ... quic` suffit à activer h3) :
|
||||||
|
```bash
|
||||||
|
nginx -V 2>&1 | grep -o 'http_v3_module' # doit être non vide
|
||||||
|
```
|
||||||
|
Debian 13 « trixie » fournit `nginx` avec `http_v3_module`. Sinon,
|
||||||
|
backports ou `nginx-full`.
|
||||||
|
2. **UDP 443 ouvert** :
|
||||||
|
```bash
|
||||||
|
sudo ufw allow 443/udp
|
||||||
|
sudo ufw status | grep '443/udp' # → ALLOW
|
||||||
|
```
|
||||||
|
3. **IPv6** : le template émet `listen [::]:443 ...` (et `[::]:80`). Si le
|
||||||
|
host n'a pas d'IPv6, retirer/commenter ces lignes (sinon `nginx -t`
|
||||||
|
échoue sur le bind IPv6).
|
||||||
|
4. Certbot / certs Let's Encrypt déjà en place (chemins
|
||||||
|
`/etc/letsencrypt/live/<domain>/{fullchain,privkey}.pem` référencés).
|
||||||
|
5. (Optionnel, pour le QUIC logiciel Kestrel) `apt install libmsquic` —
|
||||||
|
sinon Kestrel désactive h3 silencieusement (fallback h1/h2 gracieux).
|
||||||
|
|
||||||
## Génération
|
## Génération
|
||||||
|
|
||||||
|
|
@ -23,56 +62,28 @@ make generate-nginx-prod
|
||||||
make generate-nginx APP_INSTALL_ENV=production APP_INSTALL_PREFIX=
|
make generate-nginx APP_INSTALL_ENV=production APP_INSTALL_PREFIX=
|
||||||
```
|
```
|
||||||
|
|
||||||
Les ports backend et `HTTP_HOST` proviennent de `.preprod.env` /
|
Ports backend et `HTTP_HOST` depuis `.preprod.env` / `.production.env`
|
||||||
`.production.env` (ex. prod Org=83, Api=87, Blogs=85 ; preprod 89/93/91).
|
(prod Org=83, Api=87, Blogs=85 ; preprod 89/93/91). Schéma backend
|
||||||
Le schéma backend est `https` en prod (Kestrel écoute HTTPS, cert
|
`https` en prod, `http` en preprod.
|
||||||
Let's Encrypt) et `http` en preprod (Kestrel écoute HTTP simple).
|
|
||||||
|
|
||||||
| Hôte | Sous-domaine | `reuseport` | Backend prod | Backend preprod |
|
| Hôte | Sous-domaine | `reuseport` | Backend prod | Backend preprod |
|
||||||
|-------|--------------|-------------|--------------|-----------------|
|
|-------|--------------|-------------|-------------------------|------------------------|
|
||||||
| Org | yavsc | **oui** | `https://localhost:83` | `http://localhost:89` |
|
| Org | yavsc | **oui** | `https://localhost:83` | `http://localhost:89` |
|
||||||
| Api | api | non | `https://localhost:87` | `http://localhost:93` |
|
| Api | api | non | `https://localhost:87` | `http://localhost:93` |
|
||||||
| Blogs | blogs | non | `https://localhost:85` | `http://localhost:91` |
|
| Blogs | blogs | non | `https://localhost:85` | `http://localhost:91` |
|
||||||
|
|
||||||
Domaines : `<prefix><subdomain>.pschneider.fr`
|
Domaines : `<prefix><subdomain>.pschneider.fr`
|
||||||
(prod : `yavsc.pschneider.fr` ; preprod : `preprod-yavsc.pschneider.fr`).
|
(prod `yavsc.pschneider.fr` ; preprod `preprod-yavsc.pschneider.fr`).
|
||||||
|
|
||||||
## Déploiement sur le host
|
## Déploiement sur le host
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# depuis le host, après avoir copié les fichiers générés :
|
|
||||||
sudo cp generated/nginx/yavscOrg /etc/nginx/sites-available/yavscOrg
|
sudo cp generated/nginx/yavscOrg /etc/nginx/sites-available/yavscOrg
|
||||||
sudo ln -sf /etc/nginx/sites-available/yavscOrg /etc/nginx/sites-enabled/yavscOrg
|
sudo ln -sf /etc/nginx/sites-available/yavscOrg /etc/nginx/sites-enabled/yavscOrg
|
||||||
# (idem pour yavscApi, yavscBlogs)
|
# (idem yavscApi, yavscBlogs)
|
||||||
sudo nginx -t && sudo systemctl reload nginx
|
sudo nginx -t && sudo systemctl reload nginx
|
||||||
```
|
```
|
||||||
|
|
||||||
## Prérequis côté host
|
|
||||||
|
|
||||||
1. **nginx ≥ 1.25** compilé avec `http_v3_module` :
|
|
||||||
```bash
|
|
||||||
nginx -V 2>&1 | grep -o 'http_v3_module' # doit être non vide
|
|
||||||
```
|
|
||||||
Debian 13 « trixie » fournit `nginx` avec `http_v3_module`. Sinon,
|
|
||||||
`nginx-full` ou les backports.
|
|
||||||
2. **UDP 443 ouvert** :
|
|
||||||
```bash
|
|
||||||
sudo ufw allow 443/udp
|
|
||||||
sudo ufw status | grep '443/udp' # → ALLOW
|
|
||||||
```
|
|
||||||
3. Certbot / certs Let's Encrypt déjà en place (réutilisés tels quels —
|
|
||||||
les chemins `/etc/letsencrypt/live/<domain>/{fullchain,privkey}.pm`
|
|
||||||
sont référencés par le template).
|
|
||||||
|
|
||||||
## Règle : un seul `reuseport` sur 443/quic
|
|
||||||
|
|
||||||
`listen 443 quic reuseport;` ne peut figurer **qu'une seule fois** par
|
|
||||||
couple adresse:port, pour tous les vhosts confondus. Le template le
|
|
||||||
pose sur **Org** seulement (`QUIC_OPTS=reuseport` pour Org, vide pour
|
|
||||||
Api/Blogs — règle `$(if $(filter Org,$*),reuseport,)` dans le
|
|
||||||
`Makefile`). Si vous réordonnez les vhosts, gardez `reuseport` sur un
|
|
||||||
seul.
|
|
||||||
|
|
||||||
## Vérification (sur le host)
|
## Vérification (sur le host)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -85,46 +96,39 @@ sudo ufw status | grep '443/udp'
|
||||||
# 3. En-tête Alt-Svc annoncé
|
# 3. En-tête Alt-Svc annoncé
|
||||||
curl -sI https://yavsc.pschneider.fr/ | grep -i alt-svc # → h3=":443"
|
curl -sI https://yavsc.pschneider.fr/ | grep -i alt-svc # → h3=":443"
|
||||||
|
|
||||||
# 4. Requête HTTP/3 effective (curl doit être compilé avec HTTP/3)
|
# 4. Requête HTTP/3 effective (curl compilé avec HTTP/3)
|
||||||
curl -v --http3 https://yavsc.pschneider.fr/ 2>&1 | grep -i 'HTTP/3'
|
curl -v --http3 https://yavsc.pschneider.fr/ 2>&1 | grep -i 'HTTP/3'
|
||||||
|
|
||||||
# 5. Fallback h2 et h1 toujours fonctionnels
|
# 5. Fallback h2 et h1 toujours fonctionnels
|
||||||
curl --http2 https://yavsc.pschneider.fr/ -o /dev/null -w '%{http_code}\n'
|
curl --http2 https://yavsc.pschneider.fr/ -o /dev/null -w '%{http_code}\n'
|
||||||
curl --http1.1 https://yavsc.pschneider.fr/ -o /dev/null -w '%{http_code}\n'
|
curl --http1.1 https://yavsc.pschneider.fr/ -o /dev/null -w '%{http_code}\n'
|
||||||
```
|
```
|
||||||
|
Répéter 3-5 pour `api.pschneider.fr` et `blogs.pschneider.fr` (prod) et
|
||||||
Répéter 3-5 pour `api.pschneider.fr` et `blogs.pschneider.fr` (prod),
|
les trois domaines preprod. Sans curl HTTP/3, un navigateur : DevTools →
|
||||||
et pour les trois domaines preprod. Sans curl HTTP/3, utiliser un
|
Network → colonne « Protocole » → `h3` après mise à niveau.
|
||||||
navigateur : DevTools → Network → colonne « Protocole » affiche `h3`
|
|
||||||
après la première mise à niveau.
|
|
||||||
|
|
||||||
## Comment ça marche (template + envsubst)
|
## Comment ça marche (template + envsubst)
|
||||||
|
|
||||||
`contrib/template.vhost` contient des placeholders `$DOMAIN`,
|
`contrib/template.vhost` contient les placeholders `$DOMAIN`,
|
||||||
`$BACKEND_SCHEME`, `$HTTP_HOST`, `$BACKEND_PORT`, `$QUIC_OPTS`. La
|
`$BACKEND_SCHEME`, `$HTTP_HOST`, `$BACKEND_PORT`, `$QUIC_OPTS`. La règle
|
||||||
règle `generated/nginx/$(APP_INSTALL_PREFIX)yavsc%` du `Makefile` les
|
`generated/nginx/$(APP_INSTALL_PREFIX)yavsc%` du `Makefile` les substitue
|
||||||
substitue via `envsubst` avec une **allowlist** :
|
via `envsubst` avec une **allowlist** :
|
||||||
|
|
||||||
```makefile
|
```makefile
|
||||||
envsubst '$DOMAIN $BACKEND_SCHEME $HTTP_HOST $BACKEND_PORT $QUIC_OPTS'
|
envsubst '$DOMAIN $BACKEND_SCHEME $HTTP_HOST $BACKEND_PORT $QUIC_OPTS'
|
||||||
```
|
```
|
||||||
|
|
||||||
Cette allowlist est **critique** : elle limite la substitution aux 5
|
Cette allowlist limite la substitution aux 5 variables citées, de sorte
|
||||||
variables citées, de sorte que les variables nginx intrinsèques
|
que les variables nginx intrinsèques (`$host`, `$scheme`, `$remote_addr`,
|
||||||
(`$host`, `$scheme`, `$remote_addr`, `$proxy_add_x_forwarded_for`,
|
`$proxy_add_x_forwarded_for`, `$request_uri`) survivent intactes. Sans
|
||||||
`$request_uri`) survivent intactes dans le vhost généré. Sans
|
|
||||||
allowlist, `envsubst` détruirait ces variables nginx.
|
allowlist, `envsubst` détruirait ces variables nginx.
|
||||||
|
|
||||||
## Hors périmètre
|
## Hors périmètre
|
||||||
|
|
||||||
- **Kestrel inchangé** : ni `Protocols: Http1AndHttp2AndHttp3`, ni
|
- `docker-compose` / `Dockerfile` : non touchés (déploiement bare-metal
|
||||||
`libmsquic`. Si l'on veut plus tard que Kestrel parle aussi h3
|
systemd + nginx).
|
||||||
(accès direct sans nginx / topo L4), ajouter
|
- Backend HTTP/2 vers Kestrel : nginx open-source ne supporte que
|
||||||
`Kestrel:Endpoints:Https:Protocols = "Http1AndHttp2AndHttp3"` dans
|
HTTP/1.1 en upstream ; on garde `proxy_http_version 1.1`.
|
||||||
les `appsettings-{app}*.json` + `apt install libmsquic` sur le host
|
- `ssl_protocols TLSv1.3` (suivant `contrib/quic.md`) désactive TLS 1.2
|
||||||
(Kestrel désactive h3 silencieusement si libmsquic absent — fallback
|
côté TCP aussi. Pour conserver la compat TLS 1.2 sur le fallback TCP,
|
||||||
gracieux).
|
relâcher en `ssl_protocols TLSv1.2 TLSv1.3;` (QUIC reste en 1.3).
|
||||||
- **docker-compose / Dockerfile** : non touchés (déploiement réel
|
|
||||||
bare-metal systemd + nginx).
|
|
||||||
- **Backend HTTP/2** : nginx open-source ne supporte que HTTP/1.1 en
|
|
||||||
upstream ; on garde `proxy_http_version 1.1`.
|
|
||||||
|
|
@ -25,7 +25,8 @@
|
||||||
"Certificate": {
|
"Certificate": {
|
||||||
"Path": "/etc/letsencrypt/live/preprod-api.$DOMAIN/fullchain.pem",
|
"Path": "/etc/letsencrypt/live/preprod-api.$DOMAIN/fullchain.pem",
|
||||||
"KeyPath": "/etc/letsencrypt/live/preprod-api.$DOMAIN/privkey.pem"
|
"KeyPath": "/etc/letsencrypt/live/preprod-api.$DOMAIN/privkey.pem"
|
||||||
}
|
},
|
||||||
|
"Protocols": "Http1AndHttp2AndHttp3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@
|
||||||
"Certificate": {
|
"Certificate": {
|
||||||
"Path": "/etc/letsencrypt/live/preprod-blogs.$DOMAIN/fullchain.pem",
|
"Path": "/etc/letsencrypt/live/preprod-blogs.$DOMAIN/fullchain.pem",
|
||||||
"KeyPath": "/etc/letsencrypt/live/preprod-blogs.$DOMAIN/privkey.pem"
|
"KeyPath": "/etc/letsencrypt/live/preprod-blogs.$DOMAIN/privkey.pem"
|
||||||
}
|
},
|
||||||
|
"Protocols": "Http1AndHttp2AndHttp3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,8 @@
|
||||||
"Certificate": {
|
"Certificate": {
|
||||||
"Path": "/etc/letsencrypt/live/preprod-yavsc.$DOMAIN/fullchain.pem",
|
"Path": "/etc/letsencrypt/live/preprod-yavsc.$DOMAIN/fullchain.pem",
|
||||||
"KeyPath": "/etc/letsencrypt/live/preprod-yavsc.$DOMAIN/privkey.pem"
|
"KeyPath": "/etc/letsencrypt/live/preprod-yavsc.$DOMAIN/privkey.pem"
|
||||||
}
|
},
|
||||||
|
"Protocols": "Http1AndHttp2AndHttp3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,49 @@
|
||||||
# Généré depuis contrib/template.vhost par `make generate-nginx` — NE PAS ÉDITER.
|
# Généré depuis contrib/template.vhost par `make generate-nginx` — NE PAS ÉDITER.
|
||||||
# Vhost $DOMAIN — backend $BACKEND_SCHEME://$HTTP_HOST:$BACKEND_PORT
|
# Vhost $DOMAIN — backend $BACKEND_SCHEME://$HTTP_HOST:$BACKEND_PORT
|
||||||
# HTTP/3 (QUIC) termine au edge nginx ; Kestrel reste en h1/h2 backend.
|
#
|
||||||
|
# HTTP/3 (QUIC) termine au edge nginx (plan contrib/quic.md). Requiert un
|
||||||
|
# nginx compilé avec http_v3_module (≥ 1.25 ; `http3 on` ≥ 1.27.4) + UDP 443
|
||||||
|
# ouvert. Kestrel reste backend h1/h2 (mais configure aussi h3 côté logiciel,
|
||||||
|
# cf. template.appsettings-*.json — capacité, non joignable derrière nginx).
|
||||||
#
|
#
|
||||||
# Variables substituées par envsubst (allowlist) :
|
# Variables substituées par envsubst (allowlist) :
|
||||||
# DOMAIN BACKEND_SCHEME HTTP_HOST BACKEND_PORT QUIC_OPTS
|
# DOMAIN BACKEND_SCHEME HTTP_HOST BACKEND_PORT QUIC_OPTS
|
||||||
|
# $QUIC_OPTS = "reuseport" sur un seul vhost (Org), vide sur les autres
|
||||||
|
# (règle : reuseport une seule fois par addr:port, cf. contrib/quic.md).
|
||||||
# Les variables nginx intrinsèques ($host, $scheme, $remote_addr, …) sont
|
# Les variables nginx intrinsèques ($host, $scheme, $remote_addr, …) sont
|
||||||
# préservées (hors allowlist).
|
# préservées (hors allowlist).
|
||||||
|
|
||||||
# Port 80 : redirection vers HTTPS. (Certbot/nginx-plugin gère les
|
# Port 80 : redirection vers HTTPS. (Certbot/nginx-plugin gère les challenges
|
||||||
# challenges ACME ; ne pas servir l'app en HTTP clair.)
|
# ACME ; ne pas servir l'app en HTTP clair.)
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
server_name $DOMAIN;
|
server_name $DOMAIN;
|
||||||
return 301 https://$host$request_uri;
|
return 301 https://$host$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
|
# 1. TCP : HTTPS classique + HTTP/2
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
listen 443 quic $QUIC_OPTS; # HTTP/3 (UDP 443) — reuseport sur un seul vhost
|
listen [::]:443 ssl;
|
||||||
http2 on; # HTTP/2 sur TCP
|
http2 on;
|
||||||
|
|
||||||
|
# 2. UDP : HTTP/3 (QUIC). reuseport sur UN seul vhost (Org via QUIC_OPTS).
|
||||||
|
listen 443 quic $QUIC_OPTS;
|
||||||
|
listen [::]:443 quic $QUIC_OPTS;
|
||||||
|
http3 on;
|
||||||
|
|
||||||
server_name $DOMAIN;
|
server_name $DOMAIN;
|
||||||
|
|
||||||
# Annonce HTTP/3 au client sur le même port 443/udp. `always` pour
|
# 3. Certificats Let's Encrypt (Certbot).
|
||||||
# émettre l'en-tête même sur les réponses d'erreur. Le navigateur
|
|
||||||
# fait d'abord h1/h2 puis upgrade en h3.
|
|
||||||
add_header Alt-Svc 'h3=":443"; ma=86400' always;
|
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; # managed by Certbot
|
ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; # managed by Certbot
|
||||||
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; # managed by Certbot
|
ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; # managed by Certbot
|
||||||
ssl_protocols TLSv1.2 TLSv1.3; # QUIC exige TLS 1.3 ; TLS 1.2 gardé pour le fallback TCP
|
# QUIC exige TLS 1.3. (TLS 1.2 conservable pour le fallback TCP :
|
||||||
|
# `ssl_protocols TLSv1.2 TLSv1.3;` — QUIC reste en 1.3.)
|
||||||
|
ssl_protocols TLSv1.3;
|
||||||
|
|
||||||
|
# 4. Annonce h3 au navigateur sur 443/udp. `always` = émis même en erreur.
|
||||||
|
add_header Alt-Svc 'h3=":443"; ma=86400' always;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass $BACKEND_SCHEME://$HTTP_HOST:$BACKEND_PORT;
|
proxy_pass $BACKEND_SCHEME://$HTTP_HOST:$BACKEND_PORT;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue