fix(deploy): passe la prod sur prod.enervision-g3.dynv6.net
dynv6 sert mal un TXT _acme-challenge à la racine de la zone : l'API ne le liste ni ne le supprime, et un seul de ses trois serveurs le renvoie. Le défi DNS-01 de la prod échouait donc à chaque essai, alors que rec. et dev. passaient. La prod rejoint ses voisines en sous-domaine, ce qui aligne aussi les trois noms sur les environnements. - provision-host.sh : hôte prod.$DOMAINE, enregistrement A prod publié. - Makefile : --dnssleep 90, le temps que les trois serveurs de dynv6 servent le TXT avant la validation multi-réseaux de Let's Encrypt. - deploy.yml, ADR 0018, 10-infra.md, infra/README.md, Terraform.
This commit is contained in:
@@ -28,7 +28,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
environment:
|
||||
name: ${{ github.ref_name == 'main' && 'prod' || github.ref_name == 'dev' && 'rec' || 'dev' }}
|
||||
url: ${{ github.ref_name == 'main' && 'https://enervision-g3.dynv6.net' || github.ref_name == 'dev' && 'https://rec.enervision-g3.dynv6.net' || 'https://dev.enervision-g3.dynv6.net' }}
|
||||
url: ${{ github.ref_name == 'main' && 'https://prod.enervision-g3.dynv6.net' || github.ref_name == 'dev' && 'https://rec.enervision-g3.dynv6.net' || 'https://dev.enervision-g3.dynv6.net' }}
|
||||
env:
|
||||
ENVIRONNEMENT: ${{ github.ref_name == 'main' && 'prod' || github.ref_name == 'dev' && 'rec' || 'dev' }}
|
||||
PORT_HTTPS: ${{ github.ref_name == 'main' && '10443' || github.ref_name == 'dev' && '8443' || '9443' }}
|
||||
|
||||
@@ -244,14 +244,14 @@ DNS01_JETON_FICHIER ?= $(abspath $(CURDIR)/../dns.token)
|
||||
acme-sh = docker run --rm --user "$$(id -u):$$(id -g)" -e $(DNS01_JETON_VAR) -e AUTO_UPGRADE=0 \
|
||||
-v "$(CURDIR)/infra/proxy/acme:/acme.sh" -v "$(CURDIR)/infra/proxy/tls:/tls" $(ACME_SH)
|
||||
|
||||
# acme.sh sort en 2 quand le certificat n'est pas encore à renouveler, et recopie le jeton dans
|
||||
# acme/account.conf : d'où le chmod, qui le soustrait aux autres comptes de la machine.
|
||||
# acme.sh sort en 2 quand le certificat n'est pas à renouveler, et recopie le jeton dans
|
||||
# acme/account.conf, d'où le chmod. `--dnssleep` : Let's Encrypt valide depuis plusieurs réseaux.
|
||||
tls-dns01: ## Certificat Let's Encrypt par DNS-01, renouvelé seulement à échéance. Jeton : ../dns.token
|
||||
@case "$(PUBLIC_HOST)" in *.local | localhost) echo "PUBLIC_HOST=$(PUBLIC_HOST) n'est pas un nom public"; exit 1 ;; esac
|
||||
@test -r "$(DNS01_JETON_FICHIER)" || { echo "Jeton DNS illisible : $(DNS01_JETON_FICHIER)"; exit 1; }
|
||||
@mkdir -p infra/proxy/acme && chmod 700 infra/proxy/acme
|
||||
@$(DNS01_JETON_VAR)="$$(tr -d '[:space:]' < "$(DNS01_JETON_FICHIER)")"; export $(DNS01_JETON_VAR); \
|
||||
$(acme-sh) --issue --server letsencrypt --dns $(DNS01_API) -d "$(PUBLIC_HOST)"; \
|
||||
$(acme-sh) --issue --server letsencrypt --dns $(DNS01_API) --dnssleep 90 -d "$(PUBLIC_HOST)"; \
|
||||
code=$$?; chmod -R go-rwx infra/proxy/acme; [ $$code -eq 0 ] || [ $$code -eq 2 ] || exit $$code
|
||||
@$(acme-sh) --install-cert --ecc -d "$(PUBLIC_HOST)" \
|
||||
--fullchain-file /tls/fullchain.pem --key-file /tls/privkey.pem
|
||||
|
||||
@@ -20,16 +20,19 @@ et API, depuis les postes comme depuis la VM.
|
||||
|
||||
## Décision
|
||||
|
||||
**Des noms publics qui visent l'IP privée.** `enervision-g3.dynv6.net`, zone gratuite de dynv6,
|
||||
porte la prod, et deux enregistrements A portent `rec.` et `dev.`. `provision-host.sh` les publie
|
||||
par l'API dynv6 : le DNS est décrit par le code comme le reste. Tout
|
||||
**Des noms publics qui visent l'IP privée.** Dans `enervision-g3.dynv6.net`, zone gratuite de
|
||||
dynv6, trois enregistrements A portent `prod.`, `rec.` et `dev.`. `provision-host.sh` les publie
|
||||
par l'API dynv6 : le DNS est décrit par le code comme le reste. La prod n'est pas à la racine de
|
||||
la zone : dynv6 y sert mal un TXT `_acme-challenge`, que l'API ne liste ni ne supprime et qu'un
|
||||
seul de ses trois serveurs renvoie (constaté le 23/09), si bien que son défi DNS-01 échoue. Tout
|
||||
poste du réseau de l'école les résout sans configuration ; hors de ce réseau, l'IP ne mène
|
||||
nulle part.
|
||||
|
||||
**Des certificats Let's Encrypt par défi DNS-01.** Le défi passe par l'API dynv6, qui pose
|
||||
l'enregistrement TXT : Let's Encrypt n'a jamais à joindre la VM. `make tls-dns01` (acme.sh
|
||||
épinglé) le joue dans chaque stack ; il ne renouvelle qu'à échéance, d'où son rejeu à chaque
|
||||
déploiement et chaque nuit par cron. Un certificat par environnement plutôt qu'un joker : chaque
|
||||
déploiement et chaque nuit par cron. `--dnssleep 90` laisse aux trois serveurs de dynv6 le temps
|
||||
de servir le TXT avant que Let's Encrypt ne le cherche depuis plusieurs réseaux. Un certificat par environnement plutôt qu'un joker : chaque
|
||||
stack garde le sien, et la clé de la prod n'est pas lisible depuis le clone de dev.
|
||||
|
||||
**Un frontal SNI sur 443, le seul composant exposé.** `infra/front`, un nginx sur le réseau de
|
||||
|
||||
@@ -248,7 +248,7 @@ les secrets et les certificats, et ne démarre rien.
|
||||
|---|---|---|---|
|
||||
| Branche, environnement GitHub | toute branche lancée à la main, `dev` | `dev`, `rec` | `main`, `prod` |
|
||||
| Dossier, projet Compose | `/srv/enervision/dev`, `enervision-dev` | `/srv/enervision/rec`, `enervision-rec` | `/srv/enervision/prod`, `enervision-prod` |
|
||||
| URL | `https://dev.enervision-g3.dynv6.net` | `https://rec.enervision-g3.dynv6.net` | `https://enervision-g3.dynv6.net` |
|
||||
| URL | `https://dev.enervision-g3.dynv6.net` | `https://rec.enervision-g3.dynv6.net` | `https://prod.enervision-g3.dynv6.net` |
|
||||
| Proxy HTTP, HTTPS, PROXY protocol, sur `127.0.0.1` | `8083`, `9443`, `9444` | `8081`, `8443`, `8444` | `10080`, `10443`, `10444` |
|
||||
| PostgreSQL, Mailpit, Airflow, sur `127.0.0.1` | `5435`, `8027`, `8084` | `5434`, `8026`, `8082` | `5433`, `8025`, `8080` |
|
||||
| Supervision (profil `monitoring`) | à la demande, `make monitoring-up` | à la demande, `make monitoring-up` | active, `COMPOSE_PROFILES=monitoring` |
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ sur `dev` et sur `main`, et a chaque lancement manuel d'une autre branche pour `
|
||||
|
||||
Noms et certificats (ADR 0018) : avant l'apply, la zone `domaine` doit exister chez dynv6 et son
|
||||
jeton se trouver dans `<racine>/dns.token` (600, proprietaire). L'apply fait alors pointer la
|
||||
zone, `rec` et `dev` vers la machine, obtient un certificat Let's Encrypt par environnement et planifie leur
|
||||
zone, `prod`, `rec` et `dev` vers la machine, obtient un certificat Let's Encrypt par environnement et planifie leur
|
||||
renouvellement ; sans jeton, chaque environnement garde un certificat auto-signe. Le frontal SNI (`infra/front`)
|
||||
se demarre une fois depuis le dossier de la prod, `make front-up`.
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ variable "depot_url" {
|
||||
|
||||
variable "domaine" {
|
||||
type = string
|
||||
description = "Zone dynv6 de la prod, rec. et dev. en sous-domaines (ADR 0018). Son jeton doit se trouver dans <racine>/dns.token sur la machine : provision-host.sh y fait pointer la zone et ses deux sous-domaines vers la machine."
|
||||
description = "Zone dynv6 des trois environnements, prod., rec. et dev. en sous-domaines (ADR 0018). Son jeton doit se trouver dans <racine>/dns.token sur la machine : provision-host.sh y fait pointer la zone et ses trois sous-domaines vers la machine."
|
||||
default = "enervision-g3.dynv6.net"
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ preparer() {
|
||||
# jeton passe par l'environnement du seul processus Python, jamais par `argv`.
|
||||
publier_dns() {
|
||||
[[ -r "$JETON_DNS" ]] || { echo "pas de jeton $JETON_DNS : ni DNS ni Let's Encrypt"; return 0; }
|
||||
DNS_TOKEN="$(tr -d '[:space:]' < "$JETON_DNS")" python3 - "$DOMAINE" "$ADRESSE" rec dev <<'PY' \
|
||||
DNS_TOKEN="$(tr -d '[:space:]' < "$JETON_DNS")" python3 - "$DOMAINE" "$ADRESSE" prod rec dev <<'PY' \
|
||||
|| echo "DNS : dynv6 refuse la mise à jour de $DOMAINE, enregistrements inchangés" >&2
|
||||
import json, os, sys, time, urllib.request
|
||||
|
||||
@@ -198,10 +198,10 @@ verifier_outils
|
||||
mkdir -p "$RACINE"
|
||||
publier_dns
|
||||
|
||||
# Supervision active en prod seulement (ADR 0016). Le frontal (infra/front) publie 80 et 443 et
|
||||
# relaie vers les ports `front` ; les stacks ne publient plus rien hors de la boucle locale.
|
||||
# Supervision active en prod seulement (ADR 0016). La prod vit sur `prod.` et non à la racine :
|
||||
# dynv6 ne sert pas de façon fiable un TXT `_acme-challenge` à la racine de la zone (ADR 0018).
|
||||
# env branche hôte https http front pg mailpit airflow profils grafana prometheus alertmanager
|
||||
preparer prod main "$DOMAINE" 127.0.0.1:10443 127.0.0.1:10080 127.0.0.1:10444 5433 8025 8080 monitoring 3001 9090 9093
|
||||
preparer prod main "prod.$DOMAINE" 127.0.0.1:10443 127.0.0.1:10080 127.0.0.1:10444 5433 8025 8080 monitoring 3001 9090 9093
|
||||
preparer rec dev "rec.$DOMAINE" 127.0.0.1:8443 127.0.0.1:8081 127.0.0.1:8444 5434 8026 8082 "" 3002 9091 9094
|
||||
preparer dev dev "dev.$DOMAINE" 127.0.0.1:9443 127.0.0.1:8083 127.0.0.1:9444 5435 8027 8084 "" 3003 9092 9095
|
||||
|
||||
|
||||
Reference in New Issue
Block a user