# DNS FAQ > SdV's FAQ for DNS users. ## Gestion des domaines DNS chez SdV ### Vue d'ensemble SdV met à disposition des API sécurisées pour gérer de manière autonome la configuration DNS de vos domaines. Cette approche permet une intégration native dans vos workflows DevOps et vos pipelines CI/CD. Deux modes de gestion coexistent : * **Mode traditionnel** : demandes via l'[outil de requêtes](https://requete.sdv.fr/) avec accompagnement de l'équipe Système * **Mode autonome** : utilisation des API SdV pour une gestion programmatique des enregistrements DNS L'utilisation des API nécessite l'obtention de clés d'API. Contactez l'équipe Système pour en faire la demande. ### Cas d'usage #### Certificats SSL et challenge DNS Lors de la génération de certificats SSL via Let's Encrypt ou tout autre CA supportant le protocole ACME, le **challenge DNS** est l'un des modes de validation de propriété du domaine. ##### Principe du challenge DNS Le CA génère un token unique et demande la création d'un enregistrement DNS de type `TXT` sur un sous-domaine spécifique : ``` _acme-challenge.example.com. 300 IN TXT "token_generated_by_ca" ``` Une fois l'enregistrement détecté, le CA valide la propriété du domaine et émet le certificat. ##### Avantages du challenge DNS * Permet la génération de certificats wildcard (`*.example.com`) * Fonctionne sans exposition HTTP publique (pas besoin d'ouvrir le port 80) * Idéal pour les environnements internes ou les serveurs sans accès Internet direct ##### Intégration avec CertBot SdV fournit un plugin CertBot compatible avec l'API de challenge DNS : Repository : [certbot\_challenge\_dns\_sdv](https://gitlab.sdv.fr/sdv_public/certbot_challenge_dns_sdv) ```bash [Terminal] certbot certonly \ --authenticator dns-sdv \ --dns-sdv-credentials ~/.secrets/certbot/sdv.ini \ --dns-sdv-propagation-seconds 60 \ -d example.com \ -d *.example.com ``` Fichier de credentials `~/.secrets/certbot/sdv.ini` : ```ini showLineNumbers [sdv.ini] dns_sdv_api_url = https://api-challenge-dns.sdv.fr dns_sdv_api_key = your_api_key_here ``` Consultez le [guide détaillé Let's Encrypt](/guides/letsencrypt-certbot-challenge-dns) pour un workflow complet. #### Infrastructure as Code (IaC) La gestion DNS s'inscrit naturellement dans une approche Infrastructure as Code. SdV supporte deux outils majeurs : * **Terraform** : via le provider PowerDNS * **Ansible** : via les modules de gestion DNS (documentation en cours) ##### Exemple Terraform ::::steps ##### Étape 1: Configuration du provider ```hcl showLineNumbers [main.tf] terraform { required_providers { powerdns = { source = "pan-net/powerdns" version = "~> 1.5.0" } } } provider "powerdns" { api_key = var.powerdns_api_key server_url = "https://powerdns-endpoint-dns.sdv.fr" } ``` ##### Étape 2: Exemple de gestion d'enregistrements :::code-group ```hcl showLineNumbers [dns.tf] resource "powerdns_record" "app_web" { zone = "example.com." name = "app.example.com." type = "A" ttl = 300 records = ["192.0.2.10"] } resource "powerdns_record" "app_cname" { zone = "example.com." name = "www.example.com." type = "CNAME" ttl = 3600 records = ["app.example.com."] } ``` ```hcl showLineNumbers [variables.tf] variable "powerdns_api_key" { description = "Clé API PowerDNS SdV" type = string sensitive = true } ``` ::: :::: Référez-vous au [guide Terraform](/guides/terraform) pour des exemples avancés et des patterns de modules. #### Édition directe de fichier de zone Pour une gestion ponctuelle ou exploratoire, SdV permet l'usage d'une interface Web via **PowerDNS Admin**. :::warning L'édition manuelle via GUI n'est pas recommandée en production. Privilégiez Terraform ou l'API pour garantir traçabilité et reproductibilité. ::: Cas d'usage recommandés : * Modifications mineures ponctuelles * Consultation de l'état actuel des zones * Dépannage rapide * Environnements de développement ou test Limitations : * Non adapté à la gestion à grande échelle * Pas de traçabilité Git * Risque d'erreurs manuelles Consultez le [guide PowerDNS Admin](/guides/powerdns-admin-gui) pour la configuration et l'utilisation. ### Architecture des API SdV SdV expose deux API distinctes selon les cas d'usage : | API | Endpoint | Périmètre | Cas d'usage | | ----------------- | -------------------------------------- | ------------------------------------- | ---------------------------------- | | API Challenge DNS | `https://api-challenge-dns.sdv.fr` | Enregistrements `TXT` ACME uniquement | CertBot, cert-manager (Kubernetes) | | API PowerDNS | `https://powerdns-endpoint-dns.sdv.fr` | Gestion complète des zones | Terraform, Ansible, scripts custom | #### API Challenge DNS Caractéristiques : * Restriction fonctionnelle : création/suppression d'enregistrements `TXT` pour validation ACME uniquement * Sécurité renforcée : scope limité pour minimiser les risques en cas de compromission * Intégration native : plugin CertBot, cert-manager Kubernetes Pour Kubernetes, référez-vous à la [documentation cert-manager](https://kubernetes-faq.sdv.fr/guides/cert-manager/challenge-dns) de la Kubernetes FAQ. ##### Exemple de création d'enregistrement `TXT` avec `curl` ```bash [Terminal] curl -X POST https://api-challenge-dns.sdv.fr/v1/challenge \ -H "X-API-Key: your_api_key_here" \ -H "Content-Type: application/json" \ -d '{ "domain": "_acme-challenge.example.com", "value": "validation_token_here" }' ``` Réponse attendue (201 Created) : ```json { "status": "created", "domain": "_acme-challenge.example.com", "value": "validation_token_here", "ttl": 60 } ``` #### API PowerDNS Caractéristiques : * Fonctionnalités complètes : gestion de tous types d'enregistrements (`A`, `AAAA`, `CNAME`, `MX`, `TXT`, `SRV`, `NS`, etc.) * Compatibilité : respecte le standard API PowerDNS, compatible avec l'écosystème Open Source * Documentation : [PowerDNS HTTP API](https://doc.powerdns.com/authoritative/http-api/) Exemple de récupération de zone : ```bash [Terminal] curl -X GET https://powerdns-endpoint-dns.sdv.fr/api/v1/servers/localhost/zones/example.com. \ -H "X-API-Key: your_api_key_here" ``` Réponse attendue (200 OK) : ```json { "id": "example.com.", "name": "example.com.", "type": "Zone", "kind": "Native", "serial": 2026022601, "notified_serial": 2026022601, "rrsets": [ { "name": "example.com.", "type": "A", "ttl": 3600, "records": [ {"content": "192.0.2.1", "disabled": false} ] } ] } ``` ### Gestion des clés d'API #### Codes de retour API Les API SdV suivent les conventions HTTP standard : | Code | Signification | Action | | ----- | ---------------------------- | ----------------------------------- | | `200` | Succès (GET) | Traitement normal | | `201` | Ressource créée (POST) | Traitement normal | | `204` | Suppression réussie (DELETE) | Traitement normal | | `400` | Requête invalide | Vérifier le format JSON/paramètres | | `401` | Non authentifié | Vérifier la clé API | | `403` | Non autorisé | Vérifier les permissions/IP source | | `404` | Ressource introuvable | Vérifier le domaine/zone | | `429` | Rate limit dépassé | Ralentir les requêtes (max 100/min) | | `500` | Erreur serveur | Contacter le support SdV | #### Principes de sécurité Une clé d'API SdV repose sur trois piliers de sécurité : 1. Périmètre de domaines : chaque clé donne accès à un ensemble défini de domaines 2. Filtrage par IP source : seules les adresses IP autorisées peuvent utiliser la clé 3. Révocabilité : possibilité de révoquer une clé compromise sans impact sur les autres #### Demande de clé d'API Procédure : 1. Ouvrir un ticket via l'[outil de requêtes SdV](https://requete.sdv.fr/) 2. Préciser les informations suivantes : * Liste exhaustive des domaines concernés * Adresses IP sources autorisées (IP publiques fixes) * Cas d'usage prévu (CertBot, Terraform, autre) * Environnement cible (production, preprod, dev) Délai de traitement : sous 48h ouvrées #### Stockage sécurisé :::warning Les clés d'API sont des secrets hautement sensibles. Une clé compromise permet la modification complète des zones DNS associées, avec des impacts potentiels majeurs (détournement de trafic, usurpation d'identité, interruption de service). ::: Bonnes pratiques de stockage : | Contexte | Solution recommandée | Exemple | | ------------------------ | -------------------------------------------- | --------------------------------------------------- | | Poste de travail local | Vault chiffré (1Password, Bitwarden, pass) | `pass insert sdv/dns-api-prod` | | Pipelines CI/CD | Variables secrètes chiffrées | GitLab CI Variables (masked + protected) | | Infrastructure Terraform | Backend state chiffré + variables sensibles | Terraform Cloud, S3 + KMS | | Cluster Kubernetes | Secret Kubernetes avec RBAC strict | `kubectl create secret generic dns-api-key` | | Scripts shell | Variables d'environnement, jamais hardcodées | `export POWERDNS_API_KEY=$(cat ~/.secrets/dns.key)` | À éviter absolument : * Commit de clés dans Git (même en repo privé) * Stockage en clair dans des fichiers de configuration * Partage par email ou messagerie instantanée * Logs applicatifs contenant la clé en clair #### Stratégie de découpage des clés Appliquez le principe du moindre privilège : chaque clé doit avoir le scope minimal nécessaire. ##### Comparaison des approches | Approche | Scope | Impact compromission | Recommandation | | --------------------- | ---------------------------------- | ---------------------------------------- | ------------------- | | Clé unique globale | Tous domaines, tous environnements | Très élevé : accès total | À éviter absolument | | Clé par environnement | Tous domaines prod/preprod | Élevé : compromission d'un environnement | Non recommandé | | Clé par domaine/env | Domaine spécifique + environnement | Faible : limité à un périmètre | **Recommandé** | | Clé par cas d'usage | API Challenge DNS uniquement | Très faible : scope minimal | **Optimal** | ##### Exemples concrets :::code-group ```yaml [E-Commerce] # Séparation par domaine et environnement cle_boutique_prod: domaines: [shop.example.com, www.shop.example.com] environnement: production usage: Terraform production cle_boutique_staging: domaines: [shop-staging.example.com] environnement: staging usage: Terraform staging cle_boutique_acme: domaines: [shop.example.com, *.shop.example.com] api: Challenge DNS uniquement usage: Certificats Let's Encrypt ``` ```yaml [Microservices] # Séparation par service cle_api_gateway: domaines: [api.example.com] environnement: production usage: API Gateway infrastructure cle_auth_service: domaines: [auth.example.com, sso.example.com] environnement: production usage: Service d'authentification cle_monitoring: domaines: [grafana.example.com, prometheus.example.com] environnement: production usage: Outils de monitoring cle_wildcard_acme: domaines: [*.example.com] api: Challenge DNS uniquement usage: Renouvellement certificats wildcard ``` ```yaml [Multi-Client] # Agence gérant plusieurs clients cle_client_a_prod: domaines: [client-a.com, www.client-a.com] environnement: production usage: Client A infrastructure cle_client_b_prod: domaines: [client-b.fr, www.client-b.fr] environnement: production usage: Client B infrastructure # Isolation complète entre clients ``` ::: ##### Avantages du découpage | Avantage | Description | | ------------------- | ----------------------------------------------------------- | | 🛡️ Sécurité | Impact limité en cas de fuite (blast radius réduit) | | 📊 Traçabilité | Logs API par clé = identification précise des changements | | 🔄 Rotation | Renouvellement d'une clé sans impacter les autres contextes | | ⚖️ Conformité | Respect des exigences ISO 27001, PCI-DSS, etc. | | 👥 Gestion d'équipe | Attribution de clés par équipe/projet sans accès transverse | ### Types d'enregistrements supportés L'API PowerDNS supporte l'ensemble des types d'enregistrements DNS standards : | Type | Description | Exemple | TTL recommandé | | ------- | ------------------------------------- | ------------------------------------------------------ | -------------- | | `A` | Adresse IPv4 | `example.com. → 192.0.2.1` | 300-3600 | | `AAAA` | Adresse IPv6 | `example.com. → 2001:db8::1` | 300-3600 | | `CNAME` | Alias canonique | `www.example.com. → example.com.` | 3600 | | `MX` | Serveur mail | `example.com. → 10 mail.example.com.` | 3600-86400 | | `TXT` | Texte libre | `example.com. → "v=spf1 include:_spf.google.com ~all"` | 3600 | | `SRV` | Service locator | `_http._tcp.example.com. → 10 5 80 web.example.com.` | 3600 | | `NS` | Serveur de noms | `sub.example.com. → ns1.provider.com.` | 86400 | | `CAA` | Certification Authority Authorization | `example.com. → 0 issue "letsencrypt.org"` | 86400 | | `PTR` | Reverse DNS | `1.2.0.192.in-addr.arpa. → example.com.` | 86400 | #### Recommandations TTL Le TTL (Time To Live) représente la durée de mise en cache de l'enregistrement par les résolveurs DNS. | TTL | Durée | Cas d'usage | Remarque | | ---------- | -------- | ----------------------------------------- | ------------------------------------- | | 60-300 | 1-5 min | Bascule de service, maintenance planifiée | Convergence rapide, charge DNS élevée | | 300-3600 | 5-60 min | Enregistrements applicatifs courants | Bon équilibre | | 3600-86400 | 1-24h | Enregistrements stables (MX, NS) | Réduit la charge DNS | | 86400+ | 24h+ | Infrastructure critique (NS racine) | Attention aux délais de propagation | :::tip Anticipez les modifications en abaissant le TTL 24-48h avant une opération critique. ::: ### Validation et vérification Après modification DNS, il est impératif de valider la propagation des changements. #### Commandes de diagnostic ##### `dig` (recommandé pour diagnostic avancé) ```bash [Terminal] # Interrogation simple dig example.com A # Interrogation d'un serveur DNS spécifique dig @8.8.8.8 example.com A # Trace complète de résolution dig +trace example.com # Affichage court (uniquement la réponse) dig +short example.com A # Vérification TTL dig example.com A | grep -E "^example.com" # Enregistrements multiples dig example.com ANY ``` ##### `nslookup` (disponible partout, moins verbeux) ```bash [Terminal] # Interrogation simple nslookup example.com # Serveur DNS spécifique nslookup example.com 8.8.8.8 # Type d'enregistrement spécifique nslookup -type=MX example.com ``` ##### `host` (output minimaliste) ```bash [Terminal] # Résolution simple host example.com # Type spécifique host -t AAAA example.com ``` #### Vérification de propagation Les modifications DNS ne sont pas instantanées. Le délai de propagation dépend : * Du TTL de l'ancien enregistrement * Du cache des résolveurs intermédiaires * De la configuration des serveurs DNS autoritatifs ##### Checklist post-modification 1. Vérifier sur les serveurs DNS SdV autoritatifs 2. Vérifier sur un résolveur public (Google `8.8.8.8`, Cloudflare `1.1.1.1`) 3. Vérifier depuis l'application cliente 4. Attendre au minimum 2× le TTL avant validation complète ##### Exemple de script de vérification ```bash showLineNumbers [check-dns.sh] #!/bin/bash set -euo pipefail # Configuration DOMAIN="${1:-example.com}" EXPECTED_IP="${2:-192.0.2.10}" DNS_SERVERS=("8.8.8.8" "1.1.1.1" "208.67.222.222") if [ $# -eq 0 ]; then echo "Usage: $0 DOMAIN EXPECTED_IP" echo "Exemple: $0 example.com 192.0.2.10" exit 1 fi echo "Vérification DNS pour $DOMAIN (IP attendue: $EXPECTED_IP)" echo "-----------------------------------------------------------" for dns in "${DNS_SERVERS[@]}"; do result=$(timeout 5 dig @"$dns" +short "$DOMAIN" A | head -n1 || echo "TIMEOUT") if [ "$result" = "$EXPECTED_IP" ]; then echo "✓ $dns : OK ($result)" else echo "✗ $dns : KO (got '$result', expected '$EXPECTED_IP')" fi done ``` ### Bonnes pratiques #### Gestion des zones * Versionner vos configurations Terraform dans Git avec revue de code systématique * Tester les modifications en preprod avant application en production * Documenter les enregistrements critiques : raison d'être, date de création, équipe responsable * Utiliser des modules Terraform pour standardiser la création d'enregistrements récurrents #### Sécurité * Rotation des clés API : au minimum annuelle, immédiatement en cas de suspicion de compromission * Audit des accès : consulter régulièrement les logs d'utilisation des API * Principe du moindre privilège : une clé par contexte/application * Filtrage IP strict : autoriser uniquement les IP nécessaires (NAT gateway, runners CI/CD) #### Opérations * Planifier les changements DNS critiques : horaires de faible trafic, communication préalable * Abaisser le TTL 24-48h avant un changement d'infrastructure majeur * Conserver un rollback plan : garder les anciennes valeurs documentées * Monitorer la résolution DNS : alertes sur échec de résolution depuis différents points du réseau #### CI/CD * Terraform plan automatique sur les Merge Requests pour visibilité des changements * Validation des zones : terraform validate, tflint pour détection d'erreurs de syntaxe * Approbation manuelle pour les modifications production * Notification post-déploiement : Slack, email, ticketing ### Points d'attention #### Impacts opérationnels * Modification d'un enregistrement `A`/`AAAA` : peut causer une coupure temporaire (durée = TTL + cache applicatif) * Suppression d'enregistrement : vérifier l'absence de dépendances (`CNAME` pointant vers la cible, certificats SSL) * Modification de `MX` : risque de perte d'emails pendant la transition (utiliser des priorités progressives) * Changement de `NS` : opération critique, propagation globale de 24-72h #### Erreurs fréquentes * FQDN mal formé : toujours terminer par un point (`example.com.` et non `example.com`) * CNAME sur le domaine apex : interdit par la RFC (utiliser ALIAS ou `A`/`AAAA`) * TTL trop élevé : complique les modifications d'urgence * TTL trop faible : charge excessive sur les serveurs DNS * Enregistrements orphelins : nettoyer régulièrement les enregistrements obsolètes #### Dépendances applicatives * Certificats SSL : penser à renouveler après changement de domaine/`CNAME` * CDN : reconfiguration nécessaire en cas de changement de `CNAME` * Load Balancers : vérifier la cohérence DNS/configuration LB * Services tiers (SendGrid, Mailchimp, etc.) : nécessitent parfois des enregistrements DNS spécifiques ### Notes d'exploitation #### Démarrage d'un nouveau projet 1. Demander la création d'une clé API dédiée (ticket SdV) 2. Stocker la clé de manière sécurisée (vault d'équipe) 3. Initialiser le repository Terraform avec backend distant 4. Créer les enregistrements DNS de base (`A`, `AAAA`, `MX` si applicable) 5. Configurer les enregistrements de validation (SPF, DKIM, DMARC pour les emails) 6. Mettre en place le monitoring DNS (uptimerobot, pingdom, ou équivalent) #### Incident DNS : procédure de remédiation 1. Identification : utiliser `dig`, `nslookup` pour diagnostiquer 2. Rollback d'urgence : * Via Terraform : `git revert` + `terraform apply` * Via API directe : restauration manuelle des valeurs précédentes * Via GUI PowerDNS Admin pour un fix immédiat 3. Communication : informer les équipes impactées du statut et ETA 4. Post-mortem : documenter la cause racine et les actions correctives #### Changement d'infrastructure majeur Exemple : migration d'un service vers un nouveau datacenter. ##### Préparation (J-2 ou J-7) ```bash [Terminal] # Abaisser le TTL des enregistrements concernés terraform apply -var="app_dns_ttl=60" ``` ##### Bascule (J-Day) ```bash [Terminal] # Mise à jour des enregistrements avec nouvelle IP terraform apply -var="app_ip=new_datacenter_ip" ``` ##### Post-bascule (J+1) ```bash [Terminal] # Rétablir un TTL standard terraform apply -var="app_dns_ttl=3600" ``` #### Contacts et escalade * Support SdV : [https://requete.sdv.fr/](https://requete.sdv.fr/) * Documentation API PowerDNS : [https://doc.powerdns.com/authoritative/http-api/](https://doc.powerdns.com/authoritative/http-api/) En cas d'incident DNS critique impactant la production, privilégier le contact direct (téléphone/escalade) plutôt que le ticket. ## Guide Lego et Challenge DNS ### Introduction Lego est un client Let's Encrypt et une bibliothèque ACME écrite en Go. Il supporte nativement de nombreux providers DNS, dont PowerDNS, ce qui permet de générer des certificats SSL/TLS via le challenge DNS-01. Documentation officielle : [https://go-acme.github.io/lego/dns/](https://go-acme.github.io/lego/dns/) #### Avantages de Lego * Écrit en Go : binaire statique unique, pas de dépendances runtime * Support natif de nombreux providers DNS * Performance élevée * Idéal pour l'automatisation et l'intégration dans des pipelines CI/CD * Compatible avec Docker et Kubernetes #### Provider PowerDNS En utilisant le provider DNS `pdns` (PowerDNS) de Lego avec l'endpoint SdV `https://powerdns-endpoint-dns.sdv.fr/`, vous pouvez générer facilement vos certificats SSL. :::warning Contrairement au plugin CertBot SdV qui utilise un endpoint dédié et restreint en fonctionnalités, la clé d'API PowerDNS permet d'effectuer tous types de modifications sur la ou les zones DNS associées. Utilisez cette clé avec précaution. ::: ### Installation #### Téléchargement du binaire :::code-group ```shell [Linux AMD64] # Télécharger la dernière version wget https://github.com/go-acme/lego/releases/download/v4.14.2/lego_v4.14.2_linux_amd64.tar.gz # Extraire l'archive tar -xzf lego_v4.14.2_linux_amd64.tar.gz # Déplacer le binaire dans le PATH sudo mv lego /usr/local/bin/ # Vérifier l'installation lego --version ``` ```shell [Linux ARM64] wget https://github.com/go-acme/lego/releases/download/v4.14.2/lego_v4.14.2_linux_arm64.tar.gz tar -xzf lego_v4.14.2_linux_arm64.tar.gz sudo mv lego /usr/local/bin/ lego --version ``` ```shell [macOS] # Via Homebrew brew install lego # Ou téléchargement manuel wget https://github.com/go-acme/lego/releases/download/v4.14.2/lego_v4.14.2_darwin_amd64.tar.gz tar -xzf lego_v4.14.2_darwin_amd64.tar.gz sudo mv lego /usr/local/bin/ ``` ::: #### Installation via Docker ```shell [Terminal] docker pull goacme/lego:latest ``` #### Vérification de l'installation ```shell [Terminal] lego --version # lego version 4.14.2 darwin/amd64 ``` ### Configuration #### Variables d'environnement Lego utilise des variables d'environnement pour la configuration du provider DNS PowerDNS. ##### Variables requises | Variable | Description | Exemple | | -------------- | ---------------------- | --------------------------------------- | | `PDNS_API_URL` | URL de l'API PowerDNS | `https://powerdns-endpoint-dns.sdv.fr/` | | `PDNS_API_KEY` | Clé d'API PowerDNS SdV | `XXXXXX...` | ##### Variables optionnelles | Variable | Description | Défaut | | -------------------------- | ----------------------------------------- | ------ | | `PDNS_TTL` | TTL des records DNS créés | 120 | | `PDNS_PROPAGATION_TIMEOUT` | Temps max d'attente de propagation | 2m0s | | `PDNS_POLLING_INTERVAL` | Intervalle de vérification de propagation | 2s | | `PDNS_HTTP_TIMEOUT` | Timeout HTTP vers l'API | 30s | #### Configuration des variables ```shell [Terminal] export PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/ export PDNS_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ``` Pour une configuration persistante, ajoutez ces variables dans votre fichier de profil : ```shell [Terminal] # Ajouter au ~/.bashrc ou ~/.zshrc echo 'export PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/' >> ~/.bashrc echo 'export PDNS_API_KEY=XXXXXX...' >> ~/.bashrc source ~/.bashrc ``` :::warning Sécurité : Ne jamais committer la clé API dans un repository Git. Utilisez un gestionnaire de secrets (Vault, AWS Secrets Manager, etc.) en production. ::: #### Obtention de la clé API Pour obtenir votre clé API PowerDNS, contactez SdV via [l'outil de ticket](https://requete.sdv.fr/) en fournissant : * Liste des adresses IP sources autorisées * Liste des domaines concernés * Environnement (production, préproduction, développement) * Contexte d'utilisation ### Génération de certificats #### Certificat simple Génération d'un certificat pour un seul domaine : ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ --accept-tos \ run ``` ##### Options principales | Option | Description | | -------------- | --------------------------------------------------- | | `--email` | Email de contact pour notifications Let's Encrypt | | `--dns` | Provider DNS à utiliser (pdns pour PowerDNS) | | `--domains` | Domaine(s) pour le certificat | | `--accept-tos` | Accepter les conditions d'utilisation Let's Encrypt | | `run` | Commande pour générer un nouveau certificat | #### Certificat multi-domaines (SAN) ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ --domains www.monsite.fr \ --domains api.monsite.fr \ --accept-tos \ run ``` #### Certificat wildcard ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains '*.monsite.fr' \ --domains monsite.fr \ --accept-tos \ run ``` :::info Un certificat wildcard `*.monsite.fr` ne couvre pas le domaine racine `monsite.fr`. Il est recommandé d'inclure les deux dans le certificat. ::: #### Test en environnement staging Avant de générer un certificat production, testez avec l'environnement staging de Let's Encrypt : ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains test.monsite.fr \ --accept-tos \ --server https://acme-staging-v02.api.letsencrypt.org/directory \ run ``` #### Exemple d'exécution réussie ```shell [Terminal] export PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/ export PDNS_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX lego --email admin@monsite.fr --dns pdns --domains keycloak.monsite.fr --accept-tos run 2022/12/12 18:32:41 [INFO] [keycloak.monsite.fr] acme: Obtaining bundled SAN certificate 2022/12/12 18:32:41 [INFO] [keycloak.monsite.fr] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/186475346237 2022/12/12 18:32:41 [INFO] [keycloak.monsite.fr] acme: Could not find solver for: tls-alpn-01 2022/12/12 18:32:41 [INFO] [keycloak.monsite.fr] acme: Could not find solver for: http-01 2022/12/12 18:32:41 [INFO] [keycloak.monsite.fr] acme: use dns-01 solver 2022/12/12 18:32:41 [INFO] [keycloak.monsite.fr] acme: Preparing to solve DNS-01 2022/12/12 18:32:50 [INFO] [keycloak.monsite.fr] acme: Trying to solve DNS-01 2022/12/12 18:32:50 [INFO] [keycloak.monsite.fr] acme: Checking DNS record propagation using [208.67.222.222:53 208.67.220.220:53] 2022/12/12 18:32:52 [INFO] Wait for propagation [timeout: 2m0s, interval: 2s] 2022/12/12 18:32:52 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:32:54 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:32:56 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:32:58 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:33:00 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:33:02 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:33:04 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:33:06 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:33:08 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:33:10 [INFO] [keycloak.monsite.fr] acme: Waiting for DNS record propagation. 2022/12/12 18:33:18 [INFO] [keycloak.monsite.fr] The server validated our request 2022/12/12 18:33:18 [INFO] [keycloak.monsite.fr] acme: Cleaning DNS-01 challenge 2022/12/12 18:33:26 [INFO] [keycloak.monsite.fr] acme: Validations succeeded; requesting certificates 2022/12/12 18:33:27 [INFO] [keycloak.monsite.fr] Server responded with a certificate. ``` #### Emplacement des certificats Par défaut, Lego stocke les certificats dans le répertoire `.lego` du répertoire courant : ```shell [Terminal] .lego/ ├── accounts/ │ └── acme-v02.api.letsencrypt.org/ │ └── admin@monsite.fr/ │ ├── account.json │ └── keys/ ├── certificates/ │ ├── monsite.fr.crt # Certificat │ ├── monsite.fr.key # Clé privée │ ├── monsite.fr.issuer.crt # Certificat intermédiaire │ └── monsite.fr.json # Métadonnées ``` ##### Personnaliser le chemin ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ --path /etc/lego \ --accept-tos \ run ``` ### Renouvellement automatique #### Renouvellement manuel Lego gère automatiquement le renouvellement si le certificat expire dans moins de 30 jours : ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ renew ``` ##### Options de renouvellement | Option | Description | | -------------- | ------------------------------------------------------------- | | `--days` | Nombre de jours avant expiration pour renouveler (défaut: 30) | | `--reuse-key` | Réutiliser la clé privée existante | | `--renew-hook` | Script à exécuter après renouvellement | #### Renouvellement avec hook ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ --renew-hook "/usr/local/bin/reload-services.sh" \ renew ``` Exemple de script de hook `/usr/local/bin/reload-services.sh` : ```bash showLineNumbers [reload-services.sh] #!/bin/bash # Script exécuté après renouvellement réussi DOMAIN="$LEGO_CERT_DOMAIN" CERT_PATH="$LEGO_CERT_PATH" KEY_PATH="$LEGO_CERT_KEY_PATH" # Copier les certificats vers le serveur web cp "$CERT_PATH" /etc/nginx/ssl/ cp "$KEY_PATH" /etc/nginx/ssl/ # Recharger Nginx systemctl reload nginx logger "Certificat $DOMAIN renouvelé et Nginx rechargé" ``` Rendre le script exécutable : ```shell [Terminal] chmod +x /usr/local/bin/reload-services.sh ``` #### Automatisation via cron Créez un cron job pour renouveler automatiquement les certificats : ```shell [Terminal] # Éditer la crontab crontab -e # Ajouter la ligne suivante (exécution tous les jours à 3h du matin) 0 3 * * * PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/ PDNS_API_KEY=XXXX /usr/local/bin/lego --email admin@monsite.fr --dns pdns --domains monsite.fr --renew-hook /usr/local/bin/reload-services.sh renew >> /var/log/lego-renew.log 2>&1 ``` #### Automatisation via systemd timer ##### Créer le service Fichier `/etc/systemd/system/lego-renew.service` : ```ini showLineNumbers [lego-renew.service] [Unit] Description=Renew Let''s Encrypt certificates using Lego After=network-online.target [Service] Type=oneshot Environment="PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/" Environment="PDNS_API_KEY=XXXXXX" ExecStart=/usr/local/bin/lego --email admin@monsite.fr --dns pdns --domains monsite.fr --renew-hook /usr/local/bin/reload-services.sh renew StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target ``` ##### Créer le timer Fichier `/etc/systemd/system/lego-renew.timer` : ```ini showLineNumbers [lego-renew.timer] [Unit] Description=Daily renewal of Let''s Encrypt certificates [Timer] OnCalendar=daily Persistent=true [Install] WantedBy=timers.target ``` ##### Activer le timer ```shell [Terminal] # Recharger systemd systemctl daemon-reload # Activer le timer systemctl enable lego-renew.timer # Démarrer le timer systemctl start lego-renew.timer # Vérifier le statut systemctl status lego-renew.timer systemctl list-timers lego-renew.timer ``` ### Utilisation avec Docker #### Génération de certificat ```shell [Terminal] docker run --rm \ -v $(pwd)/.lego:/root/.lego \ -e PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/ \ -e PDNS_API_KEY=XXXXXX \ goacme/lego:latest \ --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ --accept-tos \ run ``` #### Renouvellement avec Docker ```shell [Terminal] docker run --rm \ -v $(pwd)/.lego:/root/.lego \ -e PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/ \ -e PDNS_API_KEY=XXXXXX \ goacme/lego:latest \ --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ renew ``` #### Docker Compose Fichier `docker-compose.yml` : ```yaml showLineNumbers [docker-compose.yml] services: lego: image: goacme/lego:latest volumes: - ./lego:/root/.lego environment: - PDNS_API_URL=https://powerdns-endpoint-dns.sdv.fr/ - PDNS_API_KEY=${PDNS_API_KEY} command: > --email admin@monsite.fr --dns pdns --domains monsite.fr --accept-tos run ``` Exécution : ```shell [Terminal] # Définir la clé API export PDNS_API_KEY=XXXXXX # Générer le certificat docker-compose run --rm lego # Renouveler docker-compose run --rm lego \ --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ renew ``` ### Intégration Kubernetes #### ConfigMap pour la configuration ```yaml showLineNumbers [configmap.yaml] apiVersion: v1 kind: ConfigMap metadata: name: lego-config namespace: cert-manager data: email: admin@monsite.fr pdns-api-url: https://powerdns-endpoint-dns.sdv.fr/ ``` #### Secret pour la clé API ```shell [Terminal] kubectl create secret generic lego-pdns-secret \ --from-literal=api-key=XXXXXX \ -n cert-manager ``` #### CronJob pour renouvellement ```yaml showLineNumbers [cronjob.yaml] apiVersion: batch/v1 kind: CronJob metadata: name: lego-renew namespace: cert-manager spec: schedule: "0 3 * * *" jobTemplate: spec: template: spec: containers: - name: lego image: goacme/lego:latest args: - --email - admin@monsite.fr - --dns - pdns - --domains - monsite.fr - --path - /lego - renew env: - name: PDNS_API_URL valueFrom: configMapKeyRef: name: lego-config key: pdns-api-url - name: PDNS_API_KEY valueFrom: secretKeyRef: name: lego-pdns-secret key: api-key volumeMounts: - name: lego-data mountPath: /lego volumes: - name: lego-data persistentVolumeClaim: claimName: lego-pvc restartPolicy: OnFailure ``` ### Troubleshooting #### Erreurs courantes ##### Erreur d'authentification API ``` error: failed to create challenge: 401 Unauthorized ``` Vérifications : ```shell [Terminal] # Vérifier les variables d'environnement echo $PDNS_API_URL echo $PDNS_API_KEY # Tester l'accès à l'API curl -H "X-API-Key: $PDNS_API_KEY" "$PDNS_API_URL/api/v1/servers" ``` ##### Timeout de propagation DNS ``` error: challenge validation failed: DNS problem: query timed out ``` Augmenter le timeout de propagation : ```shell [Terminal] export PDNS_PROPAGATION_TIMEOUT=5m export PDNS_POLLING_INTERVAL=5s lego --email admin@monsite.fr --dns pdns --domains monsite.fr run ``` ##### Certificat déjà existant ``` error: Certificate already exists for domain ``` Forcer le renouvellement : ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ renew --days 90 ``` #### Mode debug Activer le mode verbose pour diagnostic : ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ --accept-tos \ run \ --dns.resolvers 8.8.8.8:53 \ --http.timeout 60 ``` #### Vérification DNS manuelle ```shell [Terminal] # Vérifier la présence du record TXT dig _acme-challenge.monsite.fr TXT +short # Interroger les serveurs DNS SdV directement dig @ns1.sdv.fr _acme-challenge.monsite.fr TXT +short ``` ### Bonnes pratiques #### Sécurité ##### Protection de la clé API * Ne jamais committer la clé API dans un repository Git * Utiliser des variables d'environnement ou un gestionnaire de secrets * Limiter les permissions de la clé aux domaines strictement nécessaires * Renouveler régulièrement la clé API ##### Stockage des certificats * Protéger les clés privées avec des permissions restrictives (chmod 600) * Stocker les certificats dans un emplacement sécurisé * Sauvegarder régulièrement les certificats et clés ```shell [Terminal] # Sécuriser les permissions chmod 600 .lego/certificates/*.key chmod 644 .lego/certificates/*.crt ``` #### Opérationnel ##### Tests en staging * Toujours tester avec l'environnement staging avant la production * Valider le processus de renouvellement en staging ##### Monitoring * Surveiller les dates d'expiration des certificats * Alerter en cas d'échec de renouvellement * Logger toutes les opérations ##### Automatisation * Préférer le renouvellement automatique * Utiliser des hooks pour déployer automatiquement les certificats * Intégrer dans votre CI/CD #### Architecture ##### Gestion multi-environnements | Environnement | Domaine | Clé API | Serveur | | ------------- | ---------------------------- | ----------------- | --------------- | | Production | `monsite.fr`, `*.monsite.fr` | `prod_api_key` | prod-cert-01 | | Préproduction | `preprod.monsite.fr` | `preprod_api_key` | preprod-cert-01 | | Développement | `dev.monsite.fr` | `dev_api_key` | dev-cert-01 | ##### Haute disponibilité * Centraliser le stockage des certificats (NFS, S3, etc.) * Synchroniser les certificats entre serveurs * Prévoir un mécanisme de fallback ### Points d'attention #### Différences avec CertBot * Lego utilise l'API PowerDNS complète (toutes permissions) * CertBot SdV utilise une API restreinte (uniquement records `_acme-challenge`) * Lego est plus adapté à l'automatisation et aux conteneurs * CertBot est plus adapté aux installations traditionnelles sur serveur #### Permissions PowerDNS La clé API PowerDNS utilisée par Lego permet de : * Créer, modifier et supprimer des records DNS * Gérer les zones DNS complètes * Modifier la configuration des zones Implications : * Une clé compromise peut impacter l'ensemble de vos zones DNS * Utilisez des clés dédiées par environnement * Limitez les IP sources autorisées * Auditez régulièrement l'utilisation des clés #### Rate limiting Let's Encrypt Les mêmes limites que CertBot s'appliquent : | Type de limite | Valeur | Période | | ---------------------------------- | ------ | ------- | | Certificats par domaine enregistré | 50 | 7 jours | | Noms par certificat | 100 | - | | Échecs de validation | 5 | 1 heure | Recommandation : Utiliser l'environnement staging pour les tests. ### Commandes utiles #### Gestion des certificats ```shell [Terminal] # Lister les certificats ls -lh .lego/certificates/ # Afficher les détails d'un certificat openssl x509 -in .lego/certificates/monsite.fr.crt -text -noout # Vérifier la date d'expiration openssl x509 -in .lego/certificates/monsite.fr.crt -noout -enddate # Vérifier la chaîne de certification openssl verify -CAfile .lego/certificates/monsite.fr.issuer.crt .lego/certificates/monsite.fr.crt ``` #### Test SSL en production ```shell [Terminal] # Tester le certificat SSL openssl s_client -connect monsite.fr:443 -servername monsite.fr # Vérifier l'émetteur echo | openssl s_client -connect monsite.fr:443 -servername monsite.fr 2>/dev/null | openssl x509 -noout -issuer # Tester avec curl curl -vI https://monsite.fr ``` #### Révocation de certificat ```shell [Terminal] lego --email admin@monsite.fr \ --dns pdns \ --domains monsite.fr \ revoke ``` ### Contact et support En cas de problème ou pour améliorer cette documentation, contactez : * Support SdV : [Outil de ticket](https://requete.sdv.fr/) * Documentation Lego : [https://go-acme.github.io/lego/](https://go-acme.github.io/lego/) * Repository Lego : [https://github.com/go-acme/lego](https://github.com/go-acme/lego) ## Guide Let's Encrypt et Challenge DNS ### Introduction Ce guide décrit la mise en œuvre de certificats SSL/TLS Let's Encrypt via le protocole ACME DNS-01 challenge dans le contexte d'une infrastructure DNS gérée par SdV. #### Types de certificats supportés Vous pouvez générer trois types de certificats selon vos besoins : | Type | Syntaxe | Usage | | -------------------- | ------------------------------------ | ------------------------------------------------------ | | Simple | `-d monsite.fr` | Un seul domaine ou sous-domaine | | Wildcard | `-d *.monsite.fr` | Tous les sous-domaines d'un domaine (nécessite DNS-01) | | Multi-domaines (SAN) | `-d monsite.fr -d aliasdemonsite.fr` | Plusieurs domaines dans un même certificat | :::info Les certificats wildcard nécessitent obligatoirement le challenge DNS-01. Le challenge HTTP-01 ne peut pas valider des domaines wildcard. ::: #### Architecture et sécurité ##### API dédiée au challenge ACME Afin d'augmenter le niveau de sécurité de vos domaines, SdV met à disposition une API spécifique et restreinte pour les challenges Let's Encrypt. Endpoint API : `https://certmanager-endpoint-dns.sdv.fr/dnsapi` Caractéristiques de sécurité : * **Principe de moindre privilège :** Les clés d'API utilisées pour ACME DNS-01 sont différentes des clés de gestion complète du domaine * **Permissions limitées :** Cette API ne permet que de créer et supprimer des enregistrements DNS de type `TXT` préfixés par `_acme-challenge` * **Isolation :** Aucune autre opération DNS (ajout A, CNAME, MX, suppression de zone, etc.) n'est possible avec ces clés ##### Fonctionnement du challenge DNS-01 Le challenge DNS-01 fonctionne en quatre étapes : 1. **Demande de certificat :** CertBot envoie une demande à Let's Encrypt via le protocole ACME 2. **Challenge :** Let's Encrypt génère un token aléatoire et demande de créer un enregistrement DNS `TXT` sous `_acme-challenge.monsite.fr` contenant une valeur calculée 3. **Validation :** Let's Encrypt interroge les serveurs DNS autoritatifs pour vérifier la présence de cet enregistrement 4. **Émission :** Si le challenge réussit, Let's Encrypt émet le certificat ``` ┌─────────────┐ ┌──────────────────┐ │ CertBot │───── ACME ──────►│ Let's Encrypt │ │ (serveur) │ │ (CA Server) │ └──────┬──────┘ └────────┬─────────┘ │ │ │ 1. Hook authenticator │ 2. DNS Query │ (créer TXT) │ _acme-challenge ▼ ▼ ┌─────────────────────────────────────────────────────┐ │ API SdV DNS (certmanager-endpoint-dns) │ │ POST /dnsapi/v1/records │ │ { │ │ "type": "TXT", │ │ "name": "_acme-challenge.monsite.fr", │ │ "value": "base64_token_from_letsencrypt" │ │ } │ └─────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────┐ │ Serveurs DNS autoritatifs SdV │ │ _acme-challenge.monsite.fr. 60 IN TXT "token..." │ └─────────────────────────────────────────────────────┘ ``` ### Prérequis #### Composants nécessaires Vous aurez besoin des éléments suivants : * CertBot installé sur le serveur où le certificat sera généré * Scripts SdV : Contenu du repository [https://gitlab.sdv.fr/sdv\_public/certbot\_challenge\_dns\_sdv](https://gitlab.sdv.fr/sdv_public/certbot_challenge_dns_sdv) * Clé API SdV : Permettant la gestion du challenge ACME DNS-01 sur vos domaines * Permissions root : Les manipulations nécessitent les privilèges administrateur * Connectivité réseau : Accès sortant vers l'API SdV et Let's Encrypt #### Dépendances système | Composant | Dépendances système | | ----------- | ------------------------- | | CertBot | Python 3.6+, pip, openssl | | Scripts SdV | bash, curl, jq | | API SdV | Accès HTTPS sortant (443) | ### Installation #### Installation de CertBot :::code-group ```shell [Debian/Ubuntu] apt-get update && apt-get install -y certbot ``` ```shell [RHEL/CentOS/Rocky] dnf install -y certbot ``` ```shell [Alpine] apk add certbot ``` ```shell [pip] pip3 install certbot ``` ::: #### Installation des scripts SdV Après avoir installé CertBot, téléchargez la dernière release des scripts SdV : [télécharger](https://gitlab.sdv.fr/sdv_public/certbot_challenge_dns_sdv/-/releases). Les scripts doivent être installés à l'emplacement `/etc/certbot-sdv-scripts` : ```shell [Terminal] # Créer le répertoire mkdir -p /etc/certbot-sdv-scripts # Télécharger et extraire la release wget https://gitlab.sdv.fr/sdv_public/certbot_challenge_dns_sdv/-/archive/v1.1.0/certbot_challenge_dns_sdv-v1.1.0.tar.bz2 tar xvjf certbot_challenge_dns_sdv-v1.1.0.tar.bz2 -C /etc/certbot-sdv-scripts --strip-components=1 # Nettoyer l'archive rm -f certbot_challenge_dns_sdv-v1.1.0.tar.bz2 ``` Vous devriez alors avoir les fichiers suivants : ```shell [Terminal] find /etc/certbot-sdv-scripts/ # /etc/certbot-sdv-scripts/ # /etc/certbot-sdv-scripts/sdv.ini # /etc/certbot-sdv-scripts/debug.sh # /etc/certbot-sdv-scripts/command.sample # /etc/certbot-sdv-scripts/authenticator.sh # /etc/certbot-sdv-scripts/check_env.sh # /etc/certbot-sdv-scripts/cleanup.sh ``` #### Sécurisation des scripts Pour votre sécurité, les scripts doivent appartenir à `root:root` et n'être lisibles/exécutables que par `root` : ```shell [Terminal] # Définir les permissions appropriées chown -R root:root /etc/certbot-sdv-scripts/ chmod 700 /etc/certbot-sdv-scripts/ chmod 600 /etc/certbot-sdv-scripts/sdv.ini chmod 700 /etc/certbot-sdv-scripts/*.sh # Vérification ls -la /etc/certbot-sdv-scripts/ # drwx------ 2 root root 4096 ... . # -rw------- 1 root root 256 ... sdv.ini # -rwx------ 1 root root 1024 ... authenticator.sh # -rwx------ 1 root root 512 ... cleanup.sh ``` :::warning Sécurité critique : Le fichier `sdv.ini` contient votre clé API. Des permissions trop permissives exposeraient votre domaine à des manipulations DNS non autorisées. ::: ### Configuration #### Fichier de configuration SdV Éditez le fichier `/etc/certbot-sdv-scripts/sdv.ini` avec les paramètres suivants : ```ini showLineNumbers [sdv.ini] # Clé d'API fournie par SdV (obligatoire) API_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # Email de contact pour notifications Let's Encrypt (obligatoire) EMAIL="admin@monsite.fr" # Endpoint de l'API SdV (obligatoire) SdV_ENDPOINT="https://certmanager-endpoint-dns.sdv.fr/dnsapi" # Fichier de logs pour le debug (optionnel) CERTBOT_SdV_LOGS="/var/log/certbot-sdv-scripts.log" ``` ##### Paramètres de configuration | Paramètre | Description | Obligatoire | | ------------------ | ----------------------------------- | -------------------------------------------- | | `API_KEY` | Clé d'authentification API SdV | Oui | | `EMAIL` | Contact pour expirations et alertes | Oui | | `SdV_ENDPOINT` | URL de l'API DNS SdV | Oui | | `CERTBOT_SdV_LOGS` | Chemin du fichier de logs | Non (défaut: `/tmp/certbot-sdv-scripts.log`) | #### Obtention de la clé API ##### Informations requises Pour obtenir votre clé API, contactez SdV via [l'outil de ticket](https://requete.sdv.fr/) en fournissant : * Liste des adresses IP sources autorisées à appeler l'API (serveurs CertBot) * Liste des domaines concernés par la génération de certificats * Environnement : production, préproduction, développement * Contexte d'utilisation : CI/CD, génération manuelle, renouvellement automatisé ##### Modèle de périmètre | Serveur | IP | Domaines gérés | Usage | | -------------- | ---------- | ----------------------------- | ------------- | | prod-web-01 | 192.0.2.10 | `*.monsite.fr`, `monsite.com` | Production | | preprod-web-01 | 192.0.2.20 | `*.preprod.monsite.fr` | Préproduction | ##### Stratégies de clés API * Clé globale (par défaut) : Accès à tous les domaines de votre portefeuille SdV * Clés par domaine : Une clé par domaine ou groupe de domaines (recommandé pour isolation) * Clés par environnement : Séparation prod/preprod/dev pour limiter l'impact d'une fuite :::info Filtrage IP : Chaque clé API est liée à une liste d'adresses IP autorisées. Toute tentative d'utilisation depuis une IP non déclarée sera rejetée. ::: ### Génération de certificats #### Test en environnement staging Avant de générer un certificat production, testez avec l'environnement staging de Let's Encrypt pour éviter de déclencher les [rate limits](https://letsencrypt.org/docs/rate-limits/) : ```shell [Terminal] certbot certonly \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /etc/certbot-sdv-scripts/authenticator.sh \ --manual-cleanup-hook /etc/certbot-sdv-scripts/cleanup.sh \ --staging \ -d test.monsite.fr ``` ##### Options de la commande | Option | Description | | ---------------------------- | ------------------------------------------------------------ | | `certonly` | Ne génère que le certificat (pas d'installation automatique) | | `--manual` | Mode manuel avec hooks personnalisés | | `--preferred-challenges=dns` | Force le challenge DNS-01 | | `--manual-auth-hook` | Script exécuté pour créer le record TXT | | `--manual-cleanup-hook` | Script exécuté pour supprimer le record TXT | | `--staging` | Utilise l'environnement de test Let's Encrypt | | `-d` | Spécifie le(s) domaine(s) | ##### Vérification du certificat généré ```shell [Terminal] # Lister les certificats certbot certificates # Détails du certificat openssl x509 -in /etc/letsencrypt/live/test.monsite.fr/cert.pem -text -noout # Vérifier l'émetteur (doit contenir "Fake LE" en staging) openssl x509 -in /etc/letsencrypt/live/test.monsite.fr/cert.pem -issuer -noout # issuer=CN = (STAGING) Fake LE Intermediate X1 ``` #### Génération en production ##### Certificat simple Une fois les tests validés en staging, générez le certificat production : ```shell [Terminal] certbot certonly \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /etc/certbot-sdv-scripts/authenticator.sh \ --manual-cleanup-hook /etc/certbot-sdv-scripts/cleanup.sh \ -d monsite.fr \ -d www.monsite.fr ``` ##### Certificat wildcard ```shell [Terminal] certbot certonly \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /etc/certbot-sdv-scripts/authenticator.sh \ --manual-cleanup-hook /etc/certbot-sdv-scripts/cleanup.sh \ -d '*.monsite.fr' \ -d monsite.fr ``` :::warning Certificats wildcard : Utilisez des guillemets autour de `*.monsite.fr` pour éviter l'expansion du shell. Il est recommandé d'inclure également le domaine racine (`monsite.fr`) dans le certificat. ::: ##### Certificat multi-domaines (SAN) ```shell [Terminal] certbot certonly \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /etc/certbot-sdv-scripts/authenticator.sh \ --manual-cleanup-hook /etc/certbot-sdv-scripts/cleanup.sh \ -d monsite.fr \ -d www.monsite.fr \ -d shop.monsite.fr \ -d api.monsite.fr ``` #### Emplacement des certificats Les certificats générés sont stockés dans `/etc/letsencrypt/live/{domaine}/` : ```shell [Terminal] /etc/letsencrypt/live/monsite.fr/ ├── README # Instructions d'utilisation ├── cert.pem # Certificat du domaine uniquement ├── chain.pem # Chaîne de certification intermédiaire ├── fullchain.pem # cert.pem + chain.pem (utilisé pour serveurs web) └── privkey.pem # Clé privée (à protéger impérativement) ``` ##### Configuration serveur web Configuration Nginx : ```nginx ssl_certificate /etc/letsencrypt/live/monsite.fr/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/monsite.fr/privkey.pem; ``` Configuration Apache : ```apache SSLCertificateFile /etc/letsencrypt/live/monsite.fr/cert.pem SSLCertificateKeyFile /etc/letsencrypt/live/monsite.fr/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/monsite.fr/chain.pem ``` ### Renouvellement automatique #### Configuration du renouvellement Les certificats Let's Encrypt ont une validité de 90 jours. CertBot peut gérer le renouvellement automatique. ##### Test de renouvellement ```shell [Terminal] certbot renew --dry-run ``` ##### Renouvellement forcé ```shell [Terminal] certbot renew --cert-name monsite.fr --force-renewal ``` #### Automatisation via cron Créez un cron job pour renouveler automatiquement les certificats : ```shell [Terminal] # Éditer la crontab root crontab -e # Ajouter la ligne suivante (exécution tous les jours à 3h du matin) 0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx" ``` ##### Options de post-processing Recharger le service web après renouvellement : ```shell [Terminal] certbot renew --post-hook "systemctl reload nginx" ``` Exécuter un script personnalisé : ```shell [Terminal] certbot renew --deploy-hook /usr/local/bin/deploy-cert.sh ``` ##### Exemple de script de déploiement Contenu du fichier `/usr/local/bin/deploy-cert.sh` : ```bash showLineNumbers [deploy-cert.sh] #!/bin/bash # Script exécuté après chaque renouvellement réussi RENEWED_DOMAINS="${RENEWED_DOMAINS}" RENEWED_LINEAGE="${RENEWED_LINEAGE}" # Recharger les services concernés if [[ $RENEWED_DOMAINS == *"monsite.fr"* ]]; then systemctl reload nginx logger "Certificat monsite.fr renouvelé et Nginx rechargé" fi # Copier le certificat vers d'autres serveurs (optionnel) # scp -r $RENEWED_LINEAGE/ user@backend-server:/etc/letsencrypt/live/ ``` #### Surveillance du renouvellement ##### Vérification de l'expiration ```shell [Terminal] # Lister les certificats avec dates d'expiration certbot certificates # Extraire la date d'expiration d'un certificat openssl x509 -in /etc/letsencrypt/live/monsite.fr/cert.pem -noout -enddate # notAfter=May 27 12:00:00 2026 GMT # Vérifier combien de jours restants openssl x509 -in /etc/letsencrypt/live/monsite.fr/cert.pem -noout -checkend 2592000 # Certificate will not expire (si > 30 jours) ``` ##### Monitoring via logs ```shell [Terminal] # Logs CertBot tail -f /var/log/letsencrypt/letsencrypt.log # Logs hooks SdV tail -f /var/log/certbot-sdv-scripts.log ``` ### Troubleshooting #### Diagnostic des erreurs courantes ##### Erreur d'authentification API ``` Error: API authentication failed (401 Unauthorized) ``` Causes possibles : * Clé API incorrecte dans `/etc/certbot-sdv-scripts/sdv.ini` * Adresse IP source non autorisée (filtrage IP SdV) * Clé API expirée ou révoquée Vérifications : ```shell [Terminal] # Tester l'accès API manuellement curl -H "Authorization: Bearer YOUR_API_KEY" \ https://certmanager-endpoint-dns.sdv.fr/dnsapi/v1/domains # Vérifier l'IP publique du serveur curl https://ifconfig.me ``` ##### Échec validation DNS ``` Timeout during connect (likely firewall problem) Challenge validation failed ``` Causes possibles : * Record TXT non propagé à temps * Problème de propagation DNS * TTL trop élevé sur la zone Vérifications : ```shell [Terminal] # Vérifier la présence du record TXT dig _acme-challenge.monsite.fr TXT +short # Interroger les serveurs DNS autoritatifs directement dig @ns1.sdv.fr _acme-challenge.monsite.fr TXT +short # Vérifier la propagation DNS mondiale # https://dnschecker.org/#TXT/_acme-challenge.monsite.fr ``` ##### Rate limiting Let's Encrypt ``` Error: too many certificates already issued for: monsite.fr ``` Limites Let's Encrypt : | Type de limite | Valeur | Période | | ---------------------------------- | -------- | -------- | | Certificats par domaine enregistré | 50 | 7 jours | | Noms par certificat | 100 | - | | Renouvellements | Illimité | - | | Échecs de validation | 5 | 1 heure | | Comptes par IP | 10 | 3 heures | Solution : Utiliser l'environnement staging pour les tests, ou attendre la fin de la période. ##### Permissions insuffisantes ``` Permission denied: '/etc/certbot-sdv-scripts/sdv.ini' ``` Solution : ```shell [Terminal] # Vérifier et corriger les permissions ls -la /etc/certbot-sdv-scripts/ chown root:root /etc/certbot-sdv-scripts/sdv.ini chmod 600 /etc/certbot-sdv-scripts/sdv.ini ``` #### Mode debug Activer le mode debug pour analyser les problèmes : ```shell [Terminal] # Verbose CertBot certbot certonly -vv \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /etc/certbot-sdv-scripts/authenticator.sh \ --manual-cleanup-hook /etc/certbot-sdv-scripts/cleanup.sh \ --staging \ -d test.monsite.fr # Debug des scripts SdV bash -x /etc/certbot-sdv-scripts/authenticator.sh # Logs détaillés tail -f /var/log/letsencrypt/letsencrypt.log tail -f /var/log/certbot-sdv-scripts.log ``` #### Script de validation d'environnement Utilisez le script fourni pour valider la configuration : ```shell [Terminal] /etc/certbot-sdv-scripts/check_env.sh ``` Ce script vérifie : * Présence de CertBot * Permissions des fichiers * Validité du fichier `sdv.ini` * Connectivité vers l'API SdV * Présence des dépendances (curl, jq, openssl) ### Bonnes pratiques #### Sécurité ##### Protection de la clé API * Ne jamais committer `sdv.ini` dans un repository Git * Utiliser des permissions restrictives (`chmod 600`) * Stocker les clés dans un gestionnaire de secrets en production (Vault, AWS Secrets Manager, etc.) ##### Isolation des clés * Utiliser des clés API différentes par environnement (prod/preprod/dev) * Créer des clés spécifiques par domaine si possible * Renouveler régulièrement les clés API ##### Principe de moindre privilège * L'API SdV pour ACME est déjà restreinte aux records TXT `_acme-challenge` * Ne pas réutiliser ces clés pour d'autres usages * Limiter les IP sources au strict nécessaire #### Opérationnel ##### Tests systématiques * Toujours tester avec `--staging` avant la production * Valider le renouvellement automatique en staging ##### Monitoring * Surveiller les dates d'expiration des certificats * Alerter si renouvellement échoue (logs, métriques) * Intégrer dans votre système de monitoring (Prometheus, Nagios, etc.) ##### Documentation * Maintenir un inventaire des certificats générés * Documenter la stratégie de renouvellement * Tracer les changements de configuration ##### Automatisation * Préférer le renouvellement automatique via cron * Utiliser `--deploy-hook` pour recharger les services * Intégrer dans votre CI/CD si possible #### Architecture ##### Répartition des certificats * Certificat wildcard pour simplifier la gestion de nombreux sous-domaines * Certificats spécifiques pour domaines critiques (séparation des clés privées) * Certificat multi-domaines pour des services liés ##### Haute disponibilité * Synchroniser les certificats entre serveurs (rsync, Ansible, etc.) * Utiliser un stockage centralisé pour les certificats (NFS, S3) * Prévoir un mécanisme de basculement en cas d'échec de renouvellement ##### Gestion multi-environnements | Environnement | Domaine | Clé API | Serveur CertBot | | ------------- | -------------------------------------------- | ----------------- | ------------------ | | Production | `monsite.fr`, `*.monsite.fr` | `prod_api_key` | prod-certbot-01 | | Préproduction | `preprod.monsite.fr`, `*.preprod.monsite.fr` | `preprod_api_key` | preprod-certbot-01 | | Développement | `dev.monsite.fr` | `dev_api_key` | dev-certbot-01 | ### Points d'attention #### Délais de propagation DNS * Les records TXT créés par l'API SdV peuvent nécessiter quelques secondes de propagation * Les scripts SdV intègrent un délai d'attente pour garantir la propagation * En cas d'échec répété, augmenter le délai dans le script `authenticator.sh` #### TTL et cache DNS * Un TTL trop élevé sur la zone peut ralentir la validation * Les résolveurs DNS peuvent cacher temporairement l'absence du record TXT * Recommandation : TTL de 60-300 secondes pour les records `_acme-challenge` #### Renouvellement et indisponibilité * Le renouvellement ne provoque aucune interruption de service si bien configuré * Utiliser `--post-hook` ou `--deploy-hook` pour recharger les services (pas de restart) * Tester le renouvellement en dehors des heures de production #### Certificats wildcard et domaine racine * Un certificat `*.monsite.fr` ne couvre pas `monsite.fr` * Toujours inclure le domaine racine : `-d '*.monsite.fr' -d monsite.fr` * Alternative : générer deux certificats séparés #### Dépendances externes * Let's Encrypt : Dépendance critique externe (SLA \~99.9%) * API SdV : Point de défaillance unique pour validation DNS * DNS autoritatifs SdV : Doivent être joignables par Let's Encrypt Mitigation : * Renouveler les certificats bien avant expiration (30 jours recommandé) * Conserver des certificats de secours valides * Mettre en place des alertes d'expiration ### Notes d'exploitation #### Checklist de mise en production * [ ] CertBot installé et testé * [ ] Scripts SdV installés dans `/etc/certbot-sdv-scripts` * [ ] Permissions des scripts vérifiées (`root:root`, `600/700`) * [ ] Fichier `sdv.ini` configuré avec clé API valide * [ ] Test de génération en staging réussi * [ ] Génération du certificat production validée * [ ] Configuration serveur web mise à jour (Nginx/Apache) * [ ] Renouvellement automatique configuré (cron) * [ ] Test du renouvellement avec `--dry-run` réussi * [ ] Monitoring de l'expiration mis en place * [ ] Documentation mise à jour (wiki, runbook) #### Procédures opérationnelles ##### Renouvellement manuel En cas d'échec du renouvellement automatique : ```shell [Terminal] # 1. Vérifier l'état des certificats certbot certificates # 2. Identifier le certificat expirant openssl x509 -in /etc/letsencrypt/live/monsite.fr/cert.pem -noout -dates # 3. Tenter un renouvellement en mode debug certbot renew -vv --cert-name monsite.fr # 4. En cas d'échec persistant, régénérer certbot certonly \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /etc/certbot-sdv-scripts/authenticator.sh \ --manual-cleanup-hook /etc/certbot-sdv-scripts/cleanup.sh \ --force-renewal \ -d monsite.fr # 5. Recharger le serveur web systemctl reload nginx ``` ##### Révocation de certificat Si une clé privée est compromise, révoquer immédiatement le certificat : ```shell [Terminal] # Révoquer le certificat certbot revoke --cert-path /etc/letsencrypt/live/monsite.fr/cert.pem # Régénérer un nouveau certificat certbot certonly \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /etc/certbot-sdv-scripts/authenticator.sh \ --manual-cleanup-hook /etc/certbot-sdv-scripts/cleanup.sh \ -d monsite.fr # Recharger le serveur web systemctl reload nginx ``` #### Commandes utiles ##### Gestion des certificats ```shell [Terminal] # Lister tous les certificats gérés certbot certificates # Détails d'un certificat spécifique certbot certificates --cert-name monsite.fr # Supprimer un certificat (sans révoquer) certbot delete --cert-name monsite.fr # Tester le renouvellement de tous les certificats certbot renew --dry-run # Renouveler uniquement les certificats expirant dans 30 jours certbot renew # Renouveler en forçant même si non expiré certbot renew --force-renewal ``` ##### Vérification CertBot ```shell [Terminal] # Vérifier la configuration CertBot certbot --version certbot plugins ``` ##### Debug SSL en production ```shell [Terminal] # Debugger un certificat SSL en production openssl s_client -connect monsite.fr:443 -servername monsite.fr # Extraire l'émetteur du certificat en production echo | openssl s_client -connect monsite.fr:443 -servername monsite.fr 2>/dev/null | openssl x509 -noout -issuer ``` #### Contact et support En cas de problème ou pour améliorer cette documentation, contactez : * Support SdV : [Outil de ticket](https://requete.sdv.fr/) * Issues scripts SdV : [https://gitlab.sdv.fr/sdv\_public/certbot\_challenge\_dns\_sdv/-/issues](https://gitlab.sdv.fr/sdv_public/certbot_challenge_dns_sdv/-/issues) * Documentation Let's Encrypt : [https://letsencrypt.org/docs/](https://letsencrypt.org/docs/) ## PowerDNS Admin GUI [PowerDNS Admin](https://www.poweradmin.org/) est un projet Open-Source vous permettant de disposer d'une interface web d'administration des APIs PowerDNS. Dans l'exemple qui va suivre, nous vous proposons d'utiliser l'image Docker suivante : [https://hub.docker.com/r/ngoduykhanh/powerdns-admin](https://hub.docker.com/r/ngoduykhanh/powerdns-admin). Cette denière est maintenue au moment où nous écrivons ce guide. ```shell docker run -d \ -e SECRET_KEY='a-very-secret-key' \ -v pda-data:/data \ -p 9191:80 \ ngoduykhanh/powerdns-admin:v0.3.0 ``` :::danger **12/01/2023 : l'image `ngoduykhanh/powerdns-admin:latest` ( DIGEST: `9898a7cf37d2` ) ne fonctionne pas comme attendu : son utilisation va "casser" votre zone DNS : NE PAS UTILISER CETTE VERSION** ::: :::warning Nous insistons fortement sur les risques à manipuler vos zones DNS via des logiciels comme PowerDNS Admin : les conséquences d'un bug ou d'un comportement non désiré peuvent être importantes ! Si vous utilisez notre API et qu'un incident se produit, nous vous invitons à nous contacter au plus vite via notre outil de ticket : [https://requete.sdv.fr](https://requete.sdv.fr) afin de restaurer votre zone. ::: L'application permettant de définir des profils utilisateurs, un volume de stockage est nécessaire pour sauvegarder votre configuration. La `SECRET_KEY` ci-dessus n'est pas la clé d'API fournie par SdV, mais une clé secrète, permettant de chiffrer vos données dans le volume `pda-data`. Une fois le container démarré, il suffit de se rendre sur l'URL associée. Lors du 1er démarrage, il va vous falloir procéder à 2 opérations : * Créer un 1er compte utilisateur `admin`. * Configurer PowerDNS Admin pour utiliser le Endpoint SdV, et la clé d'API que nous vous aurons fournie.\ Le Endpoint à paramétrer est : `https://powerdns-endpoint-dns.sdv.fr/`. Une fois ces deux étapes réalisées, vous devriez avoir accès à l'interface web de PowerDNS Admin. Vous trouverez ci-dessous quelques captures d'écran de l'application. La clé d'API SdV utilisée dans cet exemple ne donne accès qu'à la gestion de deux domaines. :::tip SdV fournit un endpoint compatible avec l'API native de PowerDNS, mais pour des raisons de sécurité, toutes les fonctionnalités n'ont pas été implémentées.\ En conséquence, il est possible que certaines actions via l'interface PowerDNS Admin ne soient pas possibles.\ Dans le cadre d'actions simples sur vos enregistrements DNS, l'interface fonctionne parfaitement. ::: ![](/guides/pdns/login.png) ![](/guides/pdns/dashboard.png) ![](/guides/pdns/config.png) ![](/guides/pdns/zone.png) ## Terraform et Gestion DNS ### Introduction SdV propose une API compatible PowerDNS pour la gestion programmatique de vos zones DNS. Cette API permet d'adopter l'Infrastructure as Code (IaC) pour automatiser la création, modification et suppression des enregistrements DNS. Documentation API PowerDNS : [https://doc.powerdns.com/authoritative/http-routingtable.html](https://doc.powerdns.com/authoritative/http-routingtable.html) #### Architecture de l'API L'API PowerDNS n'étant pas multi-tenant nativement, SdV a mis en place un middleware qui apporte les fonctionnalités suivantes : * Gestion multi-tenant avec isolation par clé API * Accès à l'ensemble de vos domaines via une unique clé d'API (si souhaité) * Création de clés API avec portée limitée à un ou plusieurs domaines spécifiques * Filtrage IP spécifique à chaque clé * Restrictions de sécurité sur certaines fonctionnalités de l'API PowerDNS Endpoint API : `https://powerdns-endpoint-dns.sdv.fr/` #### Avantages de Terraform * Infrastructure as Code : versioning, revue de code, reproductibilité * Plan avant exécution : visualisation des changements avant application * État partagé : collaboration en équipe * Idempotence : application sûre et répétée * Intégration CI/CD : automatisation complète ### Prérequis #### Installation de Terraform :::code-group ```shell [Linux] # Télécharger Terraform wget https://releases.hashicorp.com/terraform/1.14.6/terraform_1.14.6_linux_amd64.zip # Extraire et installer unzip terraform_1.14.6_linux_amd64.zip sudo mv terraform /usr/local/bin/ # Vérifier l'installation terraform --version ``` ```shell [macOS] # Via Homebrew brew tap hashicorp/tap brew install hashicorp/tap/terraform # Vérifier l'installation terraform --version ``` ```shell [Windows] # Via Chocolatey choco install terraform # Vérifier l'installation terraform --version ``` ::: #### Obtention de la clé API Pour obtenir votre clé API PowerDNS, contactez SdV : * Via [l'outil de ticket](https://requete.sdv.fr/) * Ou contactez votre commercial Informations à fournir : * Liste des domaines concernés * Portée souhaitée (tous les domaines ou domaines spécifiques) * Adresses IP sources autorisées * Environnement (production, préproduction, développement) ### Configuration #### Structure du projet Organisation recommandée pour un projet Terraform DNS : ``` terraform-dns/ ├── main.tf # Ressources principales ├── provider.tf # Configuration du provider ├── variables.tf # Variables d'entrée ├── outputs.tf # Outputs ├── terraform.tfvars # Valeurs des variables (ne pas versionner) ├── versions.tf # Contraintes de version └── environments/ ├── prod/ │ └── terraform.tfvars ├── preprod/ │ └── terraform.tfvars └── dev/ └── terraform.tfvars ``` #### Configuration du provider ##### Déclaration du provider ```hcl showLineNumbers [versions.tf] terraform { required_version = ">= 1.0.0" required_providers { powerdns = { source = "pan-net/powerdns" version = "~> 1.5.0" } } } ``` ##### Configuration de base ```hcl showLineNumbers [provider.tf] provider "powerdns" { api_key = var.powerdns_api_key server_url = "https://powerdns-endpoint-dns.sdv.fr/" } ``` :::warning Sécurité : Ne jamais versionner la clé API en clair dans les fichiers Terraform. Utilisez des variables d'environnement ou un gestionnaire de secrets. ::: ##### Variables ```hcl showLineNumbers [variables.tf] variable "powerdns_api_key" { description = "API key for PowerDNS" type = string sensitive = true } variable "zone" { description = "DNS zone name" type = string } variable "environment" { description = "Environment name (prod, preprod, dev)" type = string default = "prod" } ``` ##### Fichier de valeurs Fichier `terraform.tfvars` (à ne pas versionner) : ```hcl showLineNumbers [terraform.tfvars] powerdns_api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" zone = "monsite.fr." environment = "prod" ``` Ajoutez `terraform.tfvars` dans votre `.gitignore` : ```shell [Terminal] echo "terraform.tfvars" >> .gitignore echo "*.tfvars" >> .gitignore echo ".terraform/" >> .gitignore echo "terraform.tfstate*" >> .gitignore ``` #### Méthodes de configuration sécurisées ##### Via variables d'environnement ```shell [Terminal] export TF_VAR_powerdns_api_key="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" terraform plan ``` ##### Via fichier de variables externe ```shell [Terminal] terraform plan -var-file="secrets.tfvars" ``` ##### Via gestionnaire de secrets Avec HashiCorp Vault : ```hcl showLineNumbers [secrets-vault.tf] data "vault_generic_secret" "powerdns" { path = "secret/powerdns" } provider "powerdns" { api_key = data.vault_generic_secret.powerdns.data["api_key"] server_url = "https://powerdns-endpoint-dns.sdv.fr/" } ``` Avec AWS Secrets Manager : ```hcl showLineNumbers [secrets-secretmanager.tf] data "aws_secretsmanager_secret_version" "powerdns" { secret_id = "powerdns/api_key" } provider "powerdns" { api_key = jsondecode(data.aws_secretsmanager_secret_version.powerdns.secret_string)["api_key"] server_url = "https://powerdns-endpoint-dns.sdv.fr/" } ``` ##### Via git-crypt Pour les projets nécessitant le versioning des secrets chiffrés : ```shell [Terminal] # Installer git-crypt apt-get install git-crypt # Debian/Ubuntu brew install git-crypt # macOS # Initialiser git-crypt cd terraform-dns/ git-crypt init # Ajouter les fichiers à chiffrer dans .gitattributes echo "terraform.tfvars filter=git-crypt diff=git-crypt" >> .gitattributes echo "*.tfvars filter=git-crypt diff=git-crypt" >> .gitattributes # Ajouter une clé GPG (chaque collaborateur) git-crypt add-gpg-user user@example.com # Les fichiers .tfvars seront automatiquement chiffrés dans Git ``` ### Initialisation #### Première utilisation ```shell [Terminal] # Initialiser le projet Terraform terraform init # Télécharge les providers # Initialise le backend # Crée le répertoire .terraform/ ``` Sortie attendue : ```shell [Terminal] Initializing the backend... Initializing provider plugins... - Finding pan-net/powerdns versions matching "~> 1.5.0"... - Installing pan-net/powerdns v1.5.0... - Installed pan-net/powerdns v1.5.0 (signed by a HashiCorp partner) Terraform has been successfully initialized! ``` #### Mise à jour des providers ```shell [Terminal] terraform init -upgrade ``` ### Gestion des enregistrements DNS #### Record type `A` ##### Enregistrement simple ```hcl showLineNumbers [main.tf] resource "powerdns_record" "www" { zone = var.zone name = "www.${var.zone}" type = "A" ttl = 300 records = ["192.0.2.10"] } ``` ##### Enregistrement avec plusieurs IPs ```hcl showLineNumbers [main.tf] resource "powerdns_record" "app" { zone = var.zone name = "app.${var.zone}" type = "A" ttl = 300 records = [ "192.0.2.10", "192.0.2.11", "192.0.2.12" ] } ``` #### Record type `AAAA` (IPv6) ```hcl showLineNumbers [main.tf] resource "powerdns_record" "www_ipv6" { zone = var.zone name = "www.${var.zone}" type = "AAAA" ttl = 300 records = ["2001:db8::1"] } ``` #### Record type `CNAME` ```hcl showLineNumbers [main.tf] resource "powerdns_record" "blog" { zone = var.zone name = "blog.${var.zone}" type = "CNAME" ttl = 300 records = ["www.${var.zone}"] } ``` :::info Un record `CNAME` ne peut pas coexister avec d'autres types de records pour le même nom. Le `CNAME` doit pointer vers un FQDN se terminant par un point. ::: #### Record type `MX` ```hcl showLineNumbers [main.tf] resource "powerdns_record" "mail" { zone = var.zone name = var.zone type = "MX" ttl = 300 records = [ "10 mail1.${var.zone}", "20 mail2.${var.zone}" ] } ``` #### Record type `TXT` ```hcl showLineNumbers [main.tf] resource "powerdns_record" "spf" { zone = var.zone name = var.zone type = "TXT" ttl = 300 records = [ "\"v=spf1 include:_spf.google.com ~all\"" ] } resource "powerdns_record" "dkim" { zone = var.zone name = "default._domainkey.${var.zone}" type = "TXT" ttl = 300 records = [ "\"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...\"" ] } ``` #### Record type `SRV` ```hcl showLineNumbers [main.tf] resource "powerdns_record" "xmpp" { zone = var.zone name = "_xmpp-client._tcp.${var.zone}" type = "SRV" ttl = 300 records = [ "5 0 5222 xmpp.${var.zone}" ] } ``` #### Record type `CAA` ```hcl showLineNumbers [main.tf] resource "powerdns_record" "caa" { zone = var.zone name = var.zone type = "CAA" ttl = 300 records = [ "0 issue \"letsencrypt.org\"", "0 issuewild \"letsencrypt.org\"", "0 iodef \"mailto:security@monsite.fr\"" ] } ``` #### Record type `NS` (sous-délégation) ```hcl showLineNumbers [main.tf] resource "powerdns_record" "subdomain_ns" { zone = var.zone name = "sub.${var.zone}" type = "NS" ttl = 3600 records = [ "ns1.provider.com.", "ns2.provider.com." ] } ``` ### Workflow Terraform #### Plan Visualiser les changements avant application : ```shell [Terminal] terraform plan ``` Exemple de sortie : ```shell [Terminal] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # powerdns_record.www will be created + resource "powerdns_record" "www" { + id = (known after apply) + name = "www.monsite.fr." + records = [ + "192.0.2.10", ] + ttl = 300 + type = "A" + zone = "monsite.fr." } Plan: 1 to add, 0 to change, 0 to destroy. ``` ##### Sauvegarder un plan ```shell [Terminal] terraform plan -out=tfplan terraform apply tfplan ``` #### Apply Appliquer les changements : ```shell [Terminal] terraform apply ``` Terraform affiche le plan et demande confirmation : ```shell [Terminal] Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes ``` ##### Application automatique (CI/CD) ```shell [Terminal] terraform apply -auto-approve ``` #### Destroy Supprimer toutes les ressources gérées : ```shell [Terminal] terraform destroy ``` ##### Supprimer une ressource spécifique ```shell [Terminal] terraform destroy -target=powerdns_record.www ``` #### Vérification DNS post-application ```shell [Terminal] # Vérifier l'enregistrement A dig A www.monsite.fr +short # Vérifier l'enregistrement MX dig MX monsite.fr +short # Vérifier l'enregistrement TXT dig TXT monsite.fr +short ``` ### Exemples complets #### Infrastructure web standard ```hcl showLineNumbers [main.tf] # Record A pour le domaine racine resource "powerdns_record" "root" { zone = var.zone name = var.zone type = "A" ttl = 300 records = ["192.0.2.10"] } # Record A pour www resource "powerdns_record" "www" { zone = var.zone name = "www.${var.zone}" type = "A" ttl = 300 records = ["192.0.2.10"] } # Records MX pour les emails resource "powerdns_record" "mail" { zone = var.zone name = var.zone type = "MX" ttl = 300 records = [ "10 mail.${var.zone}", ] } # Record A pour le serveur mail resource "powerdns_record" "mail_server" { zone = var.zone name = "mail.${var.zone}" type = "A" ttl = 300 records = ["192.0.2.20"] } # SPF resource "powerdns_record" "spf" { zone = var.zone name = var.zone type = "TXT" ttl = 300 records = [ "\"v=spf1 mx a:mail.${var.zone} ~all\"" ] } # DMARC resource "powerdns_record" "dmarc" { zone = var.zone name = "_dmarc.${var.zone}" type = "TXT" ttl = 300 records = [ "\"v=DMARC1; p=quarantine; rua=mailto:dmarc@${var.zone}\"" ] } ``` #### Infrastructure microservices ```hcl showLineNumbers [main.tf] # API principale resource "powerdns_record" "api" { zone = var.zone name = "api.${var.zone}" type = "A" ttl = 60 records = ["192.0.2.30"] } # API version 2 resource "powerdns_record" "api_v2" { zone = var.zone name = "api-v2.${var.zone}" type = "A" ttl = 60 records = ["192.0.2.31"] } # Load balancer avec plusieurs IPs resource "powerdns_record" "lb" { zone = var.zone name = "lb.${var.zone}" type = "A" ttl = 30 records = [ "192.0.2.40", "192.0.2.41", "192.0.2.42" ] } # Services backend resource "powerdns_record" "auth_service" { zone = var.zone name = "auth.${var.zone}" type = "A" ttl = 300 records = ["192.0.2.50"] } resource "powerdns_record" "data_service" { zone = var.zone name = "data.${var.zone}" type = "A" ttl = 300 records = ["192.0.2.51"] } ``` #### Utilisation de boucles (`for_each`) ```hcl showLineNumbers [main.tf] variable "subdomains" { description = "Map of subdomains to IP addresses" type = map(object({ ip = string ttl = number })) default = { "www" = { ip = "192.0.2.10" ttl = 300 } "api" = { ip = "192.0.2.20" ttl = 60 } "admin" = { ip = "192.0.2.30" ttl = 300 } } } resource "powerdns_record" "subdomains" { for_each = var.subdomains zone = var.zone name = "${each.key}.${var.zone}" type = "A" ttl = each.value.ttl records = [each.value.ip] } ``` #### Utilisation de modules Fichier `modules/dns-record/main.tf` : ```hcl showLineNumbers [main.tf] variable "zone" { type = string } variable "name" { type = string } variable "type" { type = string } variable "ttl" { type = number default = 300 } variable "records" { type = list(string) } resource "powerdns_record" "this" { zone = var.zone name = var.name type = var.type ttl = var.ttl records = var.records } output "fqdn" { value = powerdns_record.this.name } ``` Utilisation du module : ```hcl module "www" { source = "./modules/dns-record" zone = var.zone name = "www.${var.zone}" type = "A" ttl = 300 records = ["192.0.2.10"] } module "api" { source = "./modules/dns-record" zone = var.zone name = "api.${var.zone}" type = "A" ttl = 60 records = ["192.0.2.20"] } ``` ### Gestion de l'état (State) #### Backend local Par défaut, Terraform stocke l'état localement dans `terraform.tfstate`. :::warning Ne jamais versionner `terraform.tfstate` dans Git. Ajoutez-le au `.gitignore`. ::: #### Backend distant ##### S3 Backend (AWS) ```hcl showLineNumbers [main.tf] terraform { backend "s3" { bucket = "terraform-state-monsite" key = "dns/terraform.tfstate" region = "eu-west-1" dynamodb_table = "terraform-locks" encrypt = true } } ``` ##### GCS Backend (Google Cloud) ```hcl showLineNumbers [main.tf] terraform { backend "gcs" { bucket = "terraform-state-monsite" prefix = "dns" } } ``` ##### Terraform Cloud ```hcl showLineNumbers [main.tf] terraform { cloud { organization = "mon-organisation" workspaces { name = "dns-production" } } } ``` #### Manipulation de l'état ##### Lister les ressources ```shell [Terminal] terraform state list ``` ##### Afficher une ressource ```shell [Terminal] terraform state show powerdns_record.www ``` ##### Supprimer une ressource de l'état ```shell [Terminal] terraform state rm powerdns_record.www ``` ##### Importer une ressource existante ```shell [Terminal] terraform import powerdns_record.www monsite.fr.:::A:::www.monsite.fr. ``` ### Intégration CI/CD #### GitLab CI Fichier `.gitlab-ci.yml` : ```yaml showLineNumbers [.gitlab-ci.yml] variables: TF_ROOT: ${CI_PROJECT_DIR} TF_STATE_NAME: dns stages: - validate - plan - apply before_script: - cd ${TF_ROOT} - export TF_VAR_powerdns_api_key=${POWERDNS_API_KEY} validate: stage: validate image: hashicorp/terraform:latest script: - terraform init -backend=false - terraform validate - terraform fmt -check only: - merge_requests - main plan: stage: plan image: hashicorp/terraform:latest script: - terraform init - terraform plan -out=tfplan artifacts: paths: - ${TF_ROOT}/tfplan expire_in: 1 week only: - merge_requests - main apply: stage: apply image: hashicorp/terraform:latest script: - terraform init - terraform apply -auto-approve tfplan dependencies: - plan only: - main when: manual ``` #### GitHub Actions Fichier `.github/workflows/terraform.yml` : ```yaml showLineNumbers [terraform.yml] name: Terraform DNS on: push: branches: - main pull_request: branches: - main env: TF_VAR_powerdns_api_key: ${{ secrets.POWERDNS_API_KEY }} jobs: terraform: name: Terraform runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Terraform uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.14.6 - name: Terraform Init run: terraform init - name: Terraform Format run: terraform fmt -check - name: Terraform Validate run: terraform validate - name: Terraform Plan run: terraform plan -no-color continue-on-error: true - name: Terraform Apply if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: terraform apply -auto-approve ``` #### Jenkins Pipeline Fichier `Jenkinsfile` : ```groovy showLineNumbers [Jenkinsfile] pipeline { agent any environment { TF_VAR_powerdns_api_key = credentials('powerdns-api-key') } stages { stage('Checkout') { steps { checkout scm } } stage('Terraform Init') { steps { sh 'terraform init' } } stage('Terraform Validate') { steps { sh 'terraform validate' } } stage('Terraform Plan') { steps { sh 'terraform plan -out=tfplan' } } stage('Terraform Apply') { when { branch 'main' } steps { input message: 'Apply Terraform changes?', ok: 'Apply' sh 'terraform apply tfplan' } } } post { always { cleanWs() } } } ``` ### Bonnes pratiques #### Organisation du code ##### Séparation des environnements ``` terraform-dns/ ├── modules/ │ └── dns-zone/ ├── environments/ │ ├── prod/ │ │ ├── main.tf │ │ ├── variables.tf │ │ └── terraform.tfvars │ ├── preprod/ │ │ ├── main.tf │ │ ├── variables.tf │ │ └── terraform.tfvars │ └── dev/ │ ├── main.tf │ ├── variables.tf │ └── terraform.tfvars ``` ##### Utilisation de workspaces ```shell [Terminal] # Créer un workspace terraform workspace new prod # Lister les workspaces terraform workspace list # Sélectionner un workspace terraform workspace select prod # Afficher le workspace actuel terraform workspace show ``` #### Sécurité ##### Protection des secrets * Ne jamais versionner les clés API en clair * Utiliser des variables sensibles (`sensitive = true`) * Chiffrer l'état Terraform (backend S3 avec encryption) * Utiliser git-crypt pour les fichiers sensibles versionnés * Privilégier les gestionnaires de secrets (Vault, AWS Secrets Manager) ##### Gestion des accès * Clés API avec portée limitée (domaines spécifiques) * Filtrage IP sur les clés API * Rotation régulière des clés API * Audit des modifications via logs #### Validation ##### Pre-commit hooks Fichier `.pre-commit-config.yaml` : ```yaml showLineNumbers [.pre-commit-config.yaml] repos: - repo: https://github.com/antonbabenko/pre-commit-terraform rev: v1.83.5 hooks: - id: terraform_fmt - id: terraform_validate - id: terraform_docs ``` Installation : ```shell [Terminal] pip install pre-commit pre-commit install ``` ##### Linting ```shell [Terminal] # TFLint tflint --init tflint # Checkov (sécurité) checkov -d . ``` #### Documentation ##### Génération automatique ```shell [Terminal] # Installer terraform-docs brew install terraform-docs # macOS # ou wget https://github.com/terraform-docs/terraform-docs/releases/download/v0.16.0/terraform-docs-v0.16.0-linux-amd64.tar.gz # Générer la documentation terraform-docs markdown table . > README.md ``` ##### Commentaires dans le code ```hcl showLineNumbers [main.tf] # Record A pour le load balancer principal # TTL court pour permettre un basculement rapide resource "powerdns_record" "lb" { zone = var.zone name = "lb.${var.zone}" type = "A" ttl = 30 # 30 secondes pour basculement rapide records = [ "192.0.2.40", "192.0.2.41" ] } ``` #### Tests ##### Validation syntaxique ```shell [Terminal] terraform validate ``` ##### Tests avec Terratest Fichier `test/dns_test.go` : ```go showLineNumbers [dns_test.go] package test import ( "testing" "github.com/gruntwork-io/terratest/modules/terraform" "github.com/stretchr/testify/assert" ) func TestDNSRecords(t *testing.T) { terraformOptions := &terraform.Options{ TerraformDir: "../", Vars: map[string]interface{}{ "zone": "test.example.com.", }, } defer terraform.Destroy(t, terraformOptions) terraform.InitAndApply(t, terraformOptions) // Vérifications output := terraform.Output(t, terraformOptions, "www_fqdn") assert.Equal(t, "www.test.example.com.", output) } ``` ### Points d'attention #### TTL et propagation * TTL court (30-60s) : basculement rapide, charge DNS élevée * TTL moyen (300-3600s) : bon compromis pour la plupart des usages * TTL long (86400s) : records stables, propagation lente des changements Recommandations par type : * Load balancers : 30-60s * Serveurs web : 300-600s * Serveurs mail : 3600s * NS records : 86400s #### Limitations de l'API Certaines fonctionnalités de l'API PowerDNS sont filtrées par le middleware SdV pour des raisons de sécurité : * Création/suppression de zones : non disponible * Modification des paramètres de zone : restreint * Opérations de maintenance : non disponible Pour ces opérations, contactez le support SdV. #### Performance * Utilisez des modules pour éviter la duplication * Limitez le nombre de ressources par fichier (\< 100) * Utilisez `-parallelism` pour contrôler le parallélisme * Backend distant pour les projets d'équipe ```shell [Terminal] # Limiter le parallélisme terraform apply -parallelism=10 ``` #### Impacts opérationnels * Plan avant chaque apply en production * Testez dans un environnement de préproduction * Sauvegardes de l'état Terraform * Monitoring des changements DNS * Documentation des modifications ### Troubleshooting #### Erreurs courantes ##### Erreur d'authentification ``` Error: API authentication failed ``` Vérifications : ```shell [Terminal] # Vérifier la variable echo $TF_VAR_powerdns_api_key # Tester l'API manuellement curl -H "X-API-Key: $TF_VAR_powerdns_api_key" \ https://powerdns-endpoint-dns.sdv.fr/api/v1/servers ``` ##### Record déjà existant ``` Error: record already exists ``` Solution : Importer la ressource existante ```shell [Terminal] terraform import powerdns_record.www monsite.fr.:::A:::www.monsite.fr. ``` ##### État verrouillé ``` Error: state locked ``` Solution : Déverrouiller l'état (avec précaution) ```shell [Terminal] terraform force-unlock LOCK_ID ``` #### Debug ##### Mode verbose ```shell [Terminal] TF_LOG=DEBUG terraform apply ``` ##### Niveaux de log ```shell [Terminal] # TRACE (le plus verbeux) TF_LOG=TRACE terraform apply # DEBUG TF_LOG=DEBUG terraform apply # INFO TF_LOG=INFO terraform apply # WARN TF_LOG=WARN terraform apply # ERROR TF_LOG=ERROR terraform apply ``` ##### Logs dans un fichier ```shell [Terminal] TF_LOG=DEBUG TF_LOG_PATH=terraform.log terraform apply ``` ### Commandes utiles #### Gestion basique ```shell [Terminal] # Initialiser terraform init # Formater le code terraform fmt -recursive # Valider la syntaxe terraform validate # Planifier les changements terraform plan # Appliquer les changements terraform apply # Détruire les ressources terraform destroy ``` #### État et ressources ```shell [Terminal] # Lister les ressources terraform state list # Afficher une ressource terraform state show powerdns_record.www # Rafraîchir l'état terraform refresh # Importer une ressource terraform import powerdns_record.www monsite.fr.:::A:::www.monsite.fr. # Supprimer de l'état (sans détruire) terraform state rm powerdns_record.www # Déplacer une ressource terraform state mv powerdns_record.www powerdns_record.www_new ``` #### Outputs ```shell [Terminal] # Afficher tous les outputs terraform output # Afficher un output spécifique terraform output www_ip # Format JSON terraform output -json ``` #### Workspaces ```shell [Terminal] # Créer un workspace terraform workspace new prod # Lister terraform workspace list # Sélectionner terraform workspace select prod # Supprimer terraform workspace delete dev ``` ### Contact et support En cas de problème ou pour améliorer cette documentation, contactez : * Support SdV : [Outil de ticket](https://requete.sdv.fr/) * Documentation Terraform : [https://www.terraform.io/docs](https://www.terraform.io/docs) * Provider PowerDNS : [https://registry.terraform.io/providers/pan-net/powerdns/latest/docs](https://registry.terraform.io/providers/pan-net/powerdns/latest/docs)