Let's Encrypt : Certificats TLS automatises avec DNS-01 et TSIG

par

dans

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-key dans 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.

Liens

Articles connexes


Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.