Role dans l architecture
Les certificats TLS Let's Encrypt securisent tous les services exposes du homelab. Le renouvellement est entierement automatise via le challenge DNS-01 : certbot prouve la propriete du domaine en creant un enregistrement TXT dans la zone DNS, valide par les serveurs Let's Encrypt.
Flux de renouvellement
sequenceDiagram
actor Admin
participant Certbot as Certbot LXC
participant BIND9 as BIND9 VyOS
participant LE as Lets Encrypt
participant HAP as HAProxy VyOS
Admin->>Certbot: certbot certonly --dns
activate Certbot
Certbot->>Certbot: Execute auth hook
Certbot->>BIND9: nsupdate TSIG HMAC-SHA512
Note over BIND9: Cree _acme-challenge TXT
Certbot->>LE: Demande validation
activate LE
LE->>BIND9: DNS query TXT _acme-challenge
BIND9-->>LE: Reponse token
LE-->>Certbot: Certificat delivre
deactivate LE
Certbot->>Certbot: Execute cleanup hook
Certbot->>BIND9: nsupdate supprime TXT
Certbot->>HAP: SCP fichier PEM
Note over HAP: Certificat deploye
deactivate Certbot
Pourquoi DNS-01 et pas HTTP-01
Le challenge HTTP-01 necessite que le serveur web soit directement accessible depuis Internet sur le port 80. Dans notre architecture multi-couche HAProxy, Traefik, WAF, backend, il faudrait configurer chaque couche pour laisser passer les requetes ACME.
Le DNS-01 contourne ce probleme : seul le serveur DNS doit etre accessible depuis Internet, ce qui est deja le cas via la regle DNAT WAN.
Architecture des composants
flowchart TB
subgraph certbot_lxc["Certbot - Container LXC"]
certbot["certbot CLI"]
auth["auth hook nsupdate"]
cleanup["cleanup hook nsupdate"]
sync["certbot_sync_vyos.sh"]
cron["Crontab renouvellement"]
end
subgraph vyos["VyOS - Routeur"]
bind9["BIND9 hardened<br/>vue internet<br/>update-policy TSIG"]
haproxy["HAProxy<br/>crt-list SNI"]
end
subgraph internet["Internet"]
le["Lets Encrypt CA"]
end
cron --> certbot
certbot --> auth
auth -->|"nsupdate signe TSIG"| bind9
certbot --> le
le -->|"DNS TXT query"| bind9
certbot --> cleanup
cleanup -->|"nsupdate supprime TXT"| bind9
certbot --> sync
sync -->|"SCP fichier .pem"| haproxy
Configuration BIND9 pour ACME
La zone publique dans la vue internet a un update-policy restreint aux enregistrements ACME :
zone "jbsky.fr" {
type master;
file "/etc/bind/db.jbsky.fr";
update-policy {
grant tsig-key name _acme-challenge.jbsky.fr. TXT;
grant tsig-key name _acme-challenge.www.jbsky.fr. TXT;
grant tsig-key name _acme-challenge.mail.jbsky.fr. TXT;
grant tsig-key name _acme-challenge.home.jbsky.fr. TXT;
};
};
Les vues per-subnet utilisent la directive !key tsig-key dans leur match-clients pour exclure les requetes signees TSIG. Ainsi, les nsupdate de certbot tombent toujours dans la vue internet qui a l update-policy.
Pipeline CI : rotation de la cle TSIG
flowchart LR
subgraph ci["Pipeline GitLab CI"]
gen["generate_tsig<br/>Cle aleatoire 64 octets"]
val["validate_bind9<br/>checkconf + zones"]
dep["deploy_bind9<br/>Rsync zones VyOS"]
inj["deploy_certbot<br/>Injecte cle TSIG"]
end
gen --> val --> dep --> inj
inj --> bind9_target["BIND9 VyOS"]
inj --> certbot_target["Certbot LXC"]
style gen fill:#4a9,color:#fff
style inj fill:#e74,color:#fff
La cle TSIG est ephemere : generee a chaque execution du pipeline, jamais stockee dans le depot git. Un simple push sur master regenere automatiquement la cle.
Certificats geres
| Certificat | Domaines | Renouvellement | Fichier HAProxy |
|---|---|---|---|
| www-jbsky | jbsky.fr, www.jbsky.fr, mail.jbsky.fr | 1er et 15 du mois | WWW.pem |
| home-jbsky | home.jbsky.fr | 8 et 22 du mois | HOME.pem |
Workflow ajout d un nouveau domaine
L ordre est strict : un PEM manquant dans la crt-list empeche HAProxy de demarrer et impacte TOUS les sites.
flowchart TB
A["DNS : A record + grant ACME"] --> B["Certbot : ajouter dans defaults Ansible"]
B --> C["Deployer certbot via Ansible"]
C --> D["Generer le certificat manuellement"]
D --> E{"PEM existe sur VyOS ?"}
E -->|"Non"| D
E -->|"Oui"| F["HAProxy : ajouter ACL + crt-list"]
F --> G["Deployer HAProxy"]
style E fill:#f90,color:#fff
style F fill:#4a9,color:#fff
Points notables
- Vue split-horizon et TSIG : sans l exclusion
!key tsig-keydans les vues per-subnet, un nsupdate depuis le reseau interne tombe dans la mauvaise vue et retourne REFUSED. - Journal .jnl : les mises a jour dynamiques creent un fichier journal a cote de la zone. Le rsync de bind-deploy.yaml ne doit PAS exclure les .jnl sinon desync au prochain nsupdate.
- Rotation TSIG : chaque deploy CI genere une nouvelle cle. En cas de compromission, un simple push sur master regenere automatiquement la cle.
- HAProxy crt-list : HAProxy refuse categoriquement de demarrer si un PEM reference dans la crt-list n existe pas. C est pourquoi le certificat doit etre genere et verifie AVANT de modifier la configuration HAProxy.
Laisser un commentaire