Hashicorp Vault et Synology

Gerer les certificats sur NAS Synology avec Vault

Publié le 2025-06-28 • Dernière modification le 2025-07-21
30 min de lecture

HashiCorp Vault sur Synology :
Fini les certificats pourris ! 🔐

Ou comment j'ai arrêté de pleurer devant mes certificats auto-signés

🤦‍♂️ Générer des certificats, quelle galère !

C'est toujours chiant de générer des certificats. Tu te dis “je le ferai plus tard” pour aller vite… et puis, au moment où tu veux brancher un service, il en a absolument besoin. Là, c'est la galère : ça ne fait pas très pro, et tu perds un temps fou à te remettre dedans.

Ou alors cette magnifique erreur qui fait toujours plaisir en gros dans ton navigateur :

NET::ERR_CERT_AUTHORITY_INVALID

Votre connexion n'est pas privée. Des individus malveillants tentent peut-être de dérober vos informations...

Yeah, génial ! Sans compter que parfois, c'est un service tout frais qui te sort cette erreur et lui, il n'arrive carrément pas à s'en servir ! 😅

Et ne parlons pas de ces moments où tu configures ton homelab avec des certificats auto-signés “juste pour tester”, et que finalement une bonne moitié de tes services traînent encore en HTTP ou avec des certificats foireux.

BREEEEEFFFFFF....

"Il n'y a que deux types de développeurs : ceux qui ont déjà eu des problèmes de certificats, et ceux qui mentent."

Un sage développeur, probablement

💡 La solution : Vault + Synology = ❤️

Alors voilà l'idée géniale (bon, pas de moi, merci HashiCorp) : utiliser HashiCorp Vault comme une PKI (Public Key Infrastructure) pour générer automatiquement nos certificats SSL.

Mais attention, on ne va pas juste installer Vault et l'oublier dans un coin. Non non non. On va faire ça proprement avec :

Consul

Notre backend de stockage. Parce que Vault tout seul, c'est comme un frigo vide : ça sert à rien.

Stockage distribué, haute disponibilité, tout ça...
Vault

Le chef d'orchestre. Il génère les certificats, gère les secrets, et fait tout ça de manière sécurisée.

PKI, secrets management, API REST...
Vault-Agent

Le petit robot qui fait le sale boulot : récupère les certificats et les déploie automatiquement.

Templating, déploiement, redémarrage des services...
Mais pourquoi sur Synology ?

Parce que ton NAS Synology tourne H24, qu'il a Docker, et qu'il est parfait pour héberger ce genre de services d'infrastructure ! Et puis soyons honnêtes, on a tous un Synology qui tourne dans un coin, autant qu'il serve à quelque chose d'utile. 😏 Oui, oui Netflix tu m'as vu 🙃

🏗️ Architecture de notre petit chef-d'œuvre

Bon, avant de se lancer tête baissée dans les commandes, comprenons un peu ce qu'on va construire :

Phase 1 : "Houston, we have a problem"

Au début, notre Vault backé par Consul sera accessible en HTTP (oui, je sais, c'est ironique pour un truc qui gère la sécurité). Mais c'est temporaire, le temps qu'on configure tout.

Phase 2 : "The magic happens"

Une fois configuré, Vault-Agent va :

  • Se connecter à Vault avec ses identifiants en HTTP.
    Mais lui il sera sur le réseau Docker donc il a le droit !
  • Générer un nouveau certificat SSL
  • Le déployer dans le bon répertoire de Synology
  • Redémarrer Nginx pour prendre en compte le nouveau certificat
  • Répéter tout ça automatiquement avant expiration
Phase 3 : "Profit!"

Résultat : on accède à Vault en HTTPS depuis l'exterieur avec un certificat valide, et tous nos autres services peuvent bénéficier du même système !

Le truc cool

Une fois que c'est en place, tu peux générer des certificats pour tous tes services internes : le NAS, Portainer, ton API maison, etc. Un seul endroit pour gérer tous tes certificats !

Autre point positif : Vault est générique, tu n'es même pas obligé de lui faire générer des certificats : tu peux aussi lui en fournir comme... ceux d'un Let's Encrypt avec un Cerbot pour exposer ton service sur Internet par example 😉
Mais cela sera l'object d'un autre article notament sur Kubernetes avec Cert-manager pour celles et ceux qui conaissent déjà.

📁 Structure du projet (ou "comment s'organiser comme un chef")

Treve de bavardage, passons à la pratique

On commence par créer notre petite architecture. Parce que oui, l'organisation c'est important (même si on fait tous semblant de s'en foutre) :

Le point de départ sur mon Synology où je vais travailler :

  • sur la partition RAID1 que j'ai crée : volume1
  • dans un dossier partagé : docker qui me sert bah à tous mes Dockers hein logique ! En plus il backupé sur AWS toutes les nuits donc les configs ne vont pas s'envoler 😉.
  • dans un dossier spécifique : secret afin de ne pas mélanger les choux et les carottes.
Arborescence du projet
📁 /volume1/docker/secret/
├── 🐳 swarm_consul_vault.yml          # Orchestration pour Consul & Vault
├── 🐳 swarm_vault_agent.yml           # Orchestration pour Vault-Agent
├── 📁 consul/
│   ├── 📁 config/
│   │   ├── 📄 server.hcl           # Config Consul
│   │   └── 📄 start-consul.sh      # Script de démarrage
│   └── 📁 data/                    # Données Consul (généré auto)
├── 📁 vault/
│   ├── 📁 config/
│   │   └── 📄 vault.hcl            # Config Vault
└── 📁 vault-agent/
    ├── 📁 config/
    │   └── 📄 agent.hcl            # Config de l'agent
    │   └── 📄 restart_service.sh   # Script de redémarrage
    └── 📁 templates/     
        └── 📄 cert.tpl            # Template de certificat

Chaque dossier a son rôle, chaque fichier a sa mission. C'est beau, c'est organisé, ça donne envie de pleurer de bonheur ! 🥲

🚀 Phase 1 : On met le bazaar en place

Bon, maintenant qu'on a compris le principe, on va passer aux choses sérieuses. Prépare-toi un bon café, on va faire du Docker Swarm !

Oui je sais tu ne l'avais pas vu venir celle-là hein ☺️!

La vérité c'est que l'interface Docker de Synology est bien trop limité. Deplus sur le long terme si tu as envie d'avoir plusieurs Synology, tu pouras étandre le Swarm dessus (et donc notament le vault-agent automatiquement) pas mal hein !

Attention, moment critique !

Avant de commencer, assure-toi d'avoir accès SSH à ton Synology et que Docker est installé ET configuré. Si tu ne sais pas comment faire, Google est ton ami (ou ChatGPT, on ne juge pas).

Étape 1 : Initialisation du swarm
Ma jolie baleine

Première étape : on active notre Docker en mode Swarm. Pourquoi ? Parce que ça nous permet d'utiliser les secrets Docker (bien plus sécurisé que des variables d'environnement).

Je ferai probablement un article sur Docker Swarm. Je trouve cette techno vraiment sous-estimée alors qu'elle peut rendre de sacrés services (comme ici), surtout face au monstre Kubernetes qui demande énormement de configuration. Mais bon, restons concentrés, on n'est pas là pour parler des Godzilla du cloud !

# On active le mode swarm sur l'interface réseau principale (chez moi c'est la premiere eth0)
docker swarm init --advertise-addr eth0

Bon, là il n'y a pas grand-chose à dire : Docker est déjà installé via le gestionnaire de paquets de Synology, et Swarm est nativement intégré à Docker Engine depuis près de dix ans.

Pensez simplement à enregistrer le token, au cas où tu souhaiterais ajouter d'autres Synology au cluster Swarm à l'avenir.

Dans l'absolu, nous allons utiliser très peu de fonctionnalités de Swarm : déploiement, réplication, exposition de services et gestion des secrets.

Étape 2 : Déploiement de la stack

Bon, maintenant qu'on a fait le tour de l'architecture, il est temps de rentrer dans le vif du sujet ! On va éplucher ensemble chaque fichier de cette arborescence que je t'ai montrée plus haut.

Alors prépare-toi un bon café ☕ (ou un thé si tu es de ce bord-là), lance ton éditeur de code favori - et s'il te plaît, pas Notepad, on a notre dignité ! - et c'est parti pour une plongée dans ces fichiers YAML, HCL et Bash qui vont métamorphoser ton petit NAS en véritable bunker numérique.

Stack 1 — Consul + Vault :

Cette stack déploie les services consul et vault avec leurs volumes, réseaux et configurations spécifiques. Elle utilise une syntaxe compatible Docker Compose, que Docker Swarm comprend et interprète sans souci.

Cette première stack prépare le backend de stockage de Vault avec Consul, et déploie Vault en mode serveur.

Important : Toujours déployer cette stack en premier. Le Vault-Agent (stack 2) dépendra de ce serveur et ne fonctionnera pas sans.
Explications des options clés :
  • cap_add: IPC_LOCK : Permet à Vault de verrouiller sa mémoire et éviter que des secrets ne soient échangés sur le disque.
  • volumes : Persistence des informations, chargement des configurations et synchronisation de l'heure avec l'hôte via /etc/localtime pour éviter les problèmes de logs et certificats.
  • secrets: Les clés de chiffrement sont injectées de façon sécurisée via les secrets Swarm.
  • networks: secret : Un réseau overlay Swarm dédié pour isoler la communication entre services.
  • healthcheck : Teste périodiquement la disponibilité des services (ex: API Consul, endpoint santé Vault).
  • deploy.restart_policy : Paramètres pour gérer automatiquement la résilience en cas d'erreurs.

Voici le fichier swarm_consul_vault.yml complet avec tous les commentaires qui expliquent chaque section en détail :

version: "3.8"

services:
  consul:
    image: hashicorp/consul:1.17
    ports:
      # Port web UI et API Consul
      - "8500:8500"
    volumes:
      # Configuration de Consul en lecture seule
      - /volume1/docker/secret/consul/config:/consul/config
      # Données persistantes pour Consul
      - /volume1/docker/secret/consul/data:/consul/data
      # Synchronisation de l'heure avec l'hôte pour cohérence temporelle
      - /etc/localtime:/etc/localtime:ro
    environment:
      # Informations sur le cluster Consul
      CONSUL_DATACENTER: Low-layer
      CONSUL_DOMAIN: consul
      CONSUL_NODE_NAME: synology-01-consul
      # Clé de chiffrement réseau, injectée via secret
      CONSUL_ENCRYPT_KEY_FILE: /run/secrets/consul-encrypt-key
      # Identifiants utilisateur/groupe pour permissions d'accès
      PUID: 1027
      PGID: 100
    command: sh /consul/config/start_consul.sh
    networks:
      - secret
    secrets:
      # Secret Consul encrypt key externe
      - consul-encrypt-key
    healthcheck:
      # Test pour vérifier que Consul est joignable
      test: ["CMD", "consul", "members", "-http-addr=http://localhost:8500"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 30s
        max_attempts: 20
        window: 600s

  vault:
    image: hashicorp/vault:1.15
    cap_add:
      # Verrouillage mémoire pour éviter le swap des secrets sur disque
      - IPC_LOCK
    ports:
      # Port de l'API Vault et UI accessible via 8200
      - "8200:8200"
    volumes:
      # Configuration Vault
      - /volume1/docker/secret/vault/config:/vault/config
      # Synchronisation de l'heure avec l'hôte pour éviter dérives
      - /etc/localtime:/etc/localtime:ro
    environment:
      PUID: 1027
      PGID: 100
    command: vault server -config=/vault/config/vault.hcl
    networks:
      - secret
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 30s
        max_attempts: 20
        window: 600s
    healthcheck:
      # Vérifie la santé de Vault, accepte aussi état non initialisé ou scellé
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8200/v1/sys/health?uninitcode=200&sealedcode=200"]
      interval: 30s
      timeout: 10s
      retries: 10
      start_period: 120s

# Secrets à créer à la main avant déploiement
secrets:
  consul-encrypt-key:
    external: true

# Réseau chiffré Swarm externe déjà créé (overlay)
networks:
  secret:
    name: secret
    external: true

Stack 2 — Vault Agent :

Cette stack déploie le vault_agent, un client Vault qui s'exécute sur tous les nœuds (mode global). Il s'authentifie via un AppRole sécurisé (avec role_id et secret_id) et récupère les secrets, en particulier les certificats, qu'il écrit directement dans les emplacements système des NAS Synology.

Pourquoi deux stacks ?
  • Première stack : déploie le backend consul + vault, nécessaire pour initialiser et faire tourner Vault.
  • Deuxième stack : déploie l'agent Vault qui consomme les secrets, il ne peut être lancé qu'après que Vault est initialisé et déverrouillé.
Note de sécurité : Le Vault Agent requiert des droits élevés (root), des capacités spécifiques (SYS_CHROOT, IPC_LOCK) et accès en lecture sur le système hôte. Cela permet notamment d'appliquer les certificats et de redémarrer les services (nginx notamment) sur le NAS.
Explications des options clés :
  • user: root pour avoir tous les droits nécessaires.
  • cap_add: IPC_LOCK pour verrouiller la mémoire et protéger les secrets.
  • cap_add: SYS_CHROOT pour utiliser chroot et accéder au système hôte, notamment pour redémarrer des services.
  • volumes importants :
    • Les configs agent et templates de certificats.
    • L'emplacement des certificats Synology (avec RW).
    • L'accès au système hôte en lecture pour contrôle et actions.
  • secrets : injection des role_id et secret_id via secrets Docker Swarm.
  • deploy.mode: global pour garantir la présence d'un agent par nœud.

Voici le fichier swarm_vault_agent.yml complet avec commentaires pour bien comprendre chaque partie :

version: "3.8"

services:
  vault_agent:
    image: hashicorp/vault:1.15
    user: root
    cap_add:
      # Protection mémoire pour éviter le swap des secrets
      - IPC_LOCK
      # Permet d'utiliser chroot pour accéder au système hôte (ex: pour redémarrer nginx)
      - SYS_CHROOT 
    volumes:
      # Configuration de l'agent Vault
      - /volume1/docker/secret/vault_agent/config:/vault/config
      # Templates des certificats TLS à générer
      - /volume1/docker/secret/vault_agent/templates:/vault/templates
      # Emplacement des certificats Synology, accès lecture/écriture
      - /usr/syno/etc/certificate/_archive:/certs:rw
      # Synchronisation horaire avec l'hôte
      - /etc/localtime:/etc/localtime:ro
      # Accès en lecture au système hôte pour contrôle et redémarrage
      - /:/host:ro
    secrets:
      # Credentials AppRole Vault injectés via secrets Swarm
      - vault-role-id
      - vault-secret-id
    command: sh -c "vault agent -config=/vault/config/agent.hcl"
    networks:
      - secret
    deploy:
      # Lancement d'un agent sur chaque nœud du Swarm
      mode: global
      restart_policy:
        condition: any
        delay: 30s
        max_attempts: 20
        window: 600s

# Secrets AppRole à créer manuellement avant déploiement
secrets:
  vault-role-id:
    external: true
  vault-secret-id:
    external: true

# Réseau chiffré overlay Swarm partagé entre services
networks:
  secret:
    name: secret
    external: true

Le server.hcl : Quand la simplicité rencontre l'efficacité

Ce fichier server.hcl est probablement le plus court de notre stack, mais ne t'y trompes pas ce petit fichier optimise silencieusement les performances de Consul.

Le multiplicateur Raft :

Consul utilise l'algorithme Raft pour maintenir la cohérence des données dans le cluster. Ce multiplicateur est un facteur d'échelle qui affecte directement les timeouts de HeartbeatTimeout, ElectionTimeout et LeaderLeaseTimeout.

raft_multiplier = 1 Configuration haute performance recommandée pour la production. HashiCorp recommande de configurer ce paramètre à 1 en production pour permettre aux serveurs Consul de détecter rapidement une défaillance du leader et de terminer les élections de leader beaucoup plus rapidement.

Pourquoi raft_multiplier = 1 ?
  • Performance optimale : Détection rapide des défaillances et élections de leader plus rapides
  • Réseau local : Convient parfaitement aux environnements avec faible latence réseau
  • Production ready : Configuration recommandée par HashiCorp
  • Timeouts optimisés : HeartbeatTimeout=1000ms, ElectionTimeout=1000ms, LeaderLeaseTimeout=500ms
⚠️ Attention :

La valeur par défaut de Consul est 5 (pour des instances t2.micro AWS), mais cela rend la détection de défaillance et les élections beaucoup plus lentes. Dans notre environnement NAS local, 1 est le choix optimal !

📖 Documentation officielle

Pour plus de détails sur les paramètres de performance Consul, consultez la documentation HashiCorp sur les exigences serveur.

En résumé, ce fichier fait exactement ce qu'il faut : il configure Consul pour qu'il fonctionne avec les performances optimales recommandées par HashiCorp. Contrairement aux idées reçues, la valeur 1 n'est pas la "configuration par défaut" mais bien la configuration haute performance pour la production !

# Configuration de performance pour Consul
performance {
  # Multiplicateur Raft pour ajuster les timeouts
  # Valeur 1 = configuration pour la production
  raft_multiplier = 1
}

Le start_consul.sh : Quand Docker rencontre l'auto-configuration

Le script shell start_consul.sh fait le travail ingrat que personne ne veut faire : récupérer automatiquement l'IP du conteneur et démarrer Consul avec les bons paramètres. Parce que personne n'a envie de faire du docker inspect à chaque redémarrage !

🔍 Détection automatique de l'IP

IP=$(getent hosts $HOSTNAME | awk '{ print $1 }') - Cette ligne magique récupère l'IP du conteneur dans le réseau Docker. Plus besoin de deviner ou de hardcoder une IP !

🚀 Paramètres de démarrage Consul :
  • -server Mode serveur (pas agent)
  • -ui Active l'interface web
  • -client="0.0.0.0" Écoute sur toutes les interfaces
  • -bind="$IP" IP d'écoute pour le cluster
  • -advertise="$IP" IP annoncée aux autres nœuds
⚠️ Configuration cluster mono-nœud :

-bootstrap-expect=1 indique à Consul qu'il n'y aura qu'un seul serveur dans le cluster. Parfait pour notre NAS, mais attention à ne pas utiliser cette configuration pour un vrai cluster distribué !

📁 Répertoires et logs :
  • -data-dir="/consul/data" - Stockage persistant des données
  • -config-dir="/consul/config" - Répertoire de configuration
  • -log-level="INFO" - Niveau de logs équilibré
  • -disable-host-node-id=true - Désactive l'ID basé sur l'hôte (recommandé pour Docker)

Le exec final remplace le processus shell par Consul, permettant une gestion propre des signaux Docker (PID 1). C'est la différence entre un script amateur et un script professionnel !

💡 Pourquoi ce script ?

Docker attribue des IPs dynamiques aux conteneurs. Ce script évite les configurations statiques fragiles et s'adapte automatiquement à l'environnement Docker Swarm, même si l'IP change.

#!/bin/sh

# Retrieves the IP on the 'secret' network (adapt the interface name if needed)
IP=$(getent hosts $HOSTNAME | awk '{ print $1 }')

echo "Starting Consul with IP: $IP"

# Read encryption key in secret Docker Swarm
if [ -f /run/secrets/consul-encrypt-key ]; then
    ENCRYPT_KEY=$(cat $CONSUL_ENCRYPT_KEY_FILE)
    echo "Encrypt key load from secret"
else
    echo "ERROR: Secret consul-encrypt-key not found!"
    exit 1
fi


exec consul agent \
  -server \
  -ui \
  -client="0.0.0.0" \
  -bind="$IP" \
  -advertise="$IP" \
  -bootstrap-expect=1 \
  -data-dir="/consul/data" \
  -config-dir="/consul/config" \
  -log-level="INFO" \
  -encrypt="$ENCRYPT_KEY" \
  -datacenter="$CONSUL_DATACENTER" \
  -domain="$CONSUL_DOMAIN" \
  -node="$CONSUL_NODE_NAME" \
  -disable-host-node-id=true

Le vault.hcl : Configuration du coffre-fort numérique

Ce fichier vault.hcl est le cerveau de notre Vault. Contrairement au server.hcl minimaliste, celui-ci contient tous les paramètres essentiels pour faire tourner notre coffre-fort en mode production.

Interface Web et Backend de stockage

ui = true Active l'interface web de Vault, parce que personne n'a envie de tout faire en ligne de commande (même si c'est possible).

Le stockage Consul : Mariage parfait

storage "consul" Vault utilise Consul comme backend de stockage. C'est du mariage arrangé qui fonctionne parfaitement : Consul gère la persistance, Vault gère la sécurité. Pour une solution encore plus simple, il est tout à fait possible de se passer complètement de Consul et de configurer Vault pour qu'il stocke ses données directement sur l'hôte, via un backend comme file.

Paramètres de stockage Consul :
  • address = "consul:8500" Adresse du serveur Consul (nom Docker)
  • path = "vault/" Chemin de stockage dans le KV store Consul
  • scheme = "http" Protocole HTTP (réseau interne sécurisé)
  • session_ttl = "15s" Durée de vie des sessions Consul
  • lock_wait_time = "15s" Temps d'attente pour les verrous
Listener TCP : L'oreille de Vault

listener "tcp" Configuration du listener HTTP sur le port 8200 avec TLS désactivé car géré par le reverse proxy Synology. Le chiffrement ne sera pas strictement de bout en bout, puisque c'est le reverse proxy qui termine la connexion TLS. Cependant, comme ce proxy est hébergé sur le même hôte que Vault, le trafic non chiffré entre le proxy et Vault ne transite que sur l'interface localhost.

🔐 Note de sécurité :

Ce compromis est généralement acceptable dans un environnement contrôlé, tant que l'hôte est correctement sécurisé. Toutefois, pour une configuration de production à haute sensibilité, il est recommandé d'activer TLS directement dans Vault afin d'assurer un chiffrement complet, même en local.

🧰 Caisse à Outils

Pour plus de détails sur le fonctionnement du mTLS consultez l'article ici.

Configuration X-Forwarded-For :

Les paramètres x_forwarded_for_* permettent à Vault de récupérer les vraies adresses IP des clients qui passent par le reverse proxy. Essentiel pour l'audit et la sécurité !

Configuration des baux (leases) :
  • default_lease_ttl = "168h" 7 jours par défaut pour les secrets
  • max_lease_ttl = "720h" 30 jours maximum, parce qu'il faut bien une limite
  • api_addr = "https://vault.internal" URL publique via le reverse proxy
Logs et monitoring :

log_level = "INFO" et log_format = "standard" Configuration optimisée pour Synology avec logs lisibles et niveau d'information approprié.

🔧 Détail technique :

disable_mlock = false Active le verrouillage mémoire (mlock) pour éviter que des secrets ne se retrouvent dans le swap. C'est la bonne pratique recommandée par HashiCorp pour une instance sérieuse de Vault. Par défaut, les containers Docker ne peuvent pas utiliser mlock(), mais ici on a ajouté la capacité IPC_LOCK (merci cap_add), ce qui permet à Vault de protéger ses secrets correctement, même en container. Bref, si tu veux dormir tranquille, tu laisses ça à false (oui, c'est contre-intuitif, mais c'est bien false qui signifie : "je n'empêche pas Vault d'utiliser mlock()"). 🛡️

disable_clustering = true Désactive le clustering Vault car nous n'avons qu'un seul nœud. Simplifie la configuration.

# Active l'interface web de Vault
ui = true

# Configuration du backend de stockage Consul
storage "consul" {
  # Adresse du serveur Consul
  address = "consul:8500"
  # Chemin de stockage dans Consul
  path    = "vault/"
  # Protocole (HTTP car réseau interne)
  scheme  = "http"
  
  # Configuration de session Consul
  # Durée de vie des sessions
  session_ttl = "15s"
  # Temps d'attente pour les verrous
  lock_wait_time = "15s"
}

# Listener HTTP - TLS géré par le reverse proxy Synology
listener "tcp" {
  address     = "0.0.0.0:8200"
  # TLS désactivé (géré par le reverse proxy)
  tls_disable = 1
  
  # Configuration pour les en-têtes X-Forwarded-For
  x_forwarded_for_authorized_addrs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
  x_forwarded_for_hop_skips = 0
  x_forwarded_for_reject_not_authorized = false
  x_forwarded_for_reject_not_present = false
}

# Configuration pour le reverse proxy HTTPS
# URL publique via reverse proxy
api_addr = "https://vault.internal"
disable_clustering = true

# Activer mlock pour éviter le swap sur l'hôte
disable_mlock = false

# Configuration des logs
log_level = "INFO"
# Format lisible pour Synology
log_format = "standard"

# Configuration des baux (leases)
# 7 jours par défaut
default_lease_ttl = "168h"
# 30 jours maximum
max_lease_ttl = "720h"

Le agent.hcl : Le chef d'orchestre de l'automatisation

Le agent.hcl configure Vault-Agent, le composant qui transforme notre infrastructure en véritable usine à certificats automatisée. C'est lui qui se connecte à Vault, s'authentifie tout seul comme un grand, et génère nos certificats SSL sans intervention humaine.

Configuration de connexion à Vault

La section vault définit comment l'agent se connecte au serveur Vault avec une stratégie de retry robuste (5 tentatives) pour gérer les redémarrages et les problèmes réseau temporaires. Chaque tentative est plus longue que la précédente afin de laisser le temps au Vault de démarrer.

Authentification automatique :

L'agent utilise la méthode AppRole pour s'authentifier automatiquement auprès de Vault.

Pourquoi AppRole ?
  • Sécurité : Les paires sont révocables et assignables à des politiques de gestion plus fines.
  • Automatisation : Pas besoin d'intervention ou d'interaction humaine. C'est fait pour les scripts, les jobs ou les agents !
  • Rotation : Les secret_id peuvent être renouvelés automatiquement.

remove_secret_id_file_after_reading = false

Cette option importante permet de conserver le secret_id en mémoire afin d'éviter des réauthentifications constantes. Dans notre environnement Docker, cela apporte plus de stabilité. Pas d'inquiétude côté sécurité : notre agent ne l'utilise que pour récupérer un token, et c'est ce token qui servira ensuite à toutes les opérations sur les certificats.
Template magic :

La section template est le cœur de l'automatisation. Elle surveille le template /vault/templates/cert.tpl et génère automatiquement les certificats.

Important à savoir
  • writeToFile écrit systématiquement les fichiers à chaque rendu du template, même si le contenu n'a pas changé.
  • Le fichier destination est quant à lui comparé avant écriture : Vault Agent ne le réécrit que si son contenu diffère du précédent.
  • Le script command (ex : redémarrage d'un service) est déclenché uniquement si le fichier destination est mis à jour.
  • C'est pourquoi il est recommandé de regrouper dans un seul fichier destination tous les éléments critiques (certificat, clé, CA concaténés), pour éviter des redémarrages inutiles.
Le workflow automatique
  • Surveillance L'agent surveille en permanence la validité du certificat (en interne).
  • Génération Quand le certificat approche de l'expiration, le template est exécuté (à 90% du TTL ou lors d'un redémarrage de l'agent).
  • Déploiement Le script restart_service.sh est automatiquement exécuté uniquement si le fichier destination a changé.
    Cela évite de redémarrer le service inutilement si seuls les fichiers générés par writeToFile ont été écrits mais sans modification réelle du certificat.
Pro tip

Il est tout à fait possible de définir plusieurs sections template dans le fichier de configuration de Vault-Agent. Chacune peut cibler un certificat différent, un chemin spécifique dans Vault, et avoir son propre script de démarrage (command) associé.

Cela te permet de générer autant de certificats que nécessaire, chacun avec des paramètres distincts (chemins, rôles, SAN, TTL, etc.). C'est particulièrement utile si tu dois équiper plusieurs services, applications ou interfaces réseau avec des certificats différents, tout en gardant une configuration centralisée et cohérente.

Astuce supplémentaire
Si tu utilises plusieurs templates avec chacun leur propre destination et command, veille à bien gérer l'ordre des redémarrages pour éviter des interruptions partielles ou en double.

🔧 Détail technique :

disable_mlock = false Active le verrouillage mémoire (mlock) pour éviter que des secrets ne se retrouvent dans le swap. C'est la bonne pratique recommandée par HashiCorp pour une instance sérieuse de Vault. Par défaut, les containers Docker ne peuvent pas utiliser mlock(), mais ici on a ajouté la capacité IPC_LOCK (merci cap_add), ce qui permet à Vault de protéger ses secrets correctement, même en container. Bref, si tu veux dormir tranquille, tu laisses ça à false (oui, c'est contre-intuitif, mais c'est bien false qui signifie : "je n'empêche pas Vault d'utiliser mlock()"). 🛡️

En résumé, ce fichier transforme Vault-Agent en véritable assistant personnel qui gère tout le cycle de vie des certificats SSL : authentification, génération, déploiement et redémarrage des services. Plus besoin de se lever à 3h du matin parce qu'un certificat a expiré !

# Configuration de connexion à Vault
vault {
  address = "http://vault:8200"
  retry {
    # Nombre de tentatives de reconnexion
    num_retries = 5
  }
}

# Authentification automatique via AppRole
auto_auth {
  method "approle" {
    mount_path = "auth/approle"
    config = {
      # Fichiers contenant les identifiants AppRole
      role_id_file_path   = "/run/secrets/vault_role_id"
      secret_id_file_path = "/run/secrets/vault_secret_id"
      # Garde le secret_id pour éviter les re-authentifications
      remove_secret_id_file_after_reading = false
    }
  }
}

# Activer mlock pour éviter le swap sur l'hôte
disable_mlock = true

# Template pour générer les certificats SSL
template {
  # Template source
  source      = "/vault/templates/cert.tpl"
  # Fichier de sortie
  destination = "/certs/myCert/all-certs"
  # Script post-génération
  command     = "/bin/sh /vault/config/restart_service.sh"
}

Le restart_service.sh : Quand Docker rencontre Synology

Le script restart_service.sh fait le lien entre notre conteneur Vault Agent et le système hôte Synology. Il permet de redémarrer nginx (ou d'autres services) automatiquement après la mise à jour des certificats, évitant ainsi toute intervention manuelle.

Le défi technique :

Comment redémarrer un service système depuis un conteneur Docker ? La solution repose sur :

  • chroot pour basculer la racine du système de fichiers vers le système hôte
  • Un volume Docker /:/host:ro monté en lecture seule, exposant le système Synology à l'intérieur du conteneur

chroot /host « maquille » le conteneur en système hôte, lui permettant d'exécuter les commandes système du Synology.

La commande synosystemctl restart nginx est spécifique au DSM de Synology : c'est l'équivalent de systemctl sur Linux classique.

Pourquoi cette approche ?
  • Sécurité : Le volume est monté en lecture seule (:ro) pour limiter les risques
  • Isolation : Le conteneur n'a accès qu'à ce dont il a besoin
  • Automatisation : Le redémarrage se fait sans intervention humaine
  • Simplicité : Pas besoin de services additionnels ou de mécanismes complexes de communication inter-processus
⚠️ Prérequis importants :

Pour que chroot fonctionne correctement, le conteneur Vault Agent doit être lancé avec l'utilisateur root et la capability SYS_CHROOT activée.
Vérifie aussi que le montage du volume vers /host est correctement configuré.

🔧 Alternatives possibles :

Redémarrage manuel
  • Nécessite une intervention humaine
  • Peu adapté à l'automatisation
Fichier de signal sur volume partagé
  • Un fichier flag écrit par le conteneur, surveillé par un cron ou un service hôte
  • Complexité et risques d'incohérence liés à la synchronisation
Communication via socket Unix ou API
  • Implique un processus écouteur sur l'hôte
  • Complexité accrue de mise en place

En résumé, ce script court et simple est une solution pragmatique et efficace pour relier Vault Agent et la gestion des services système sous Synology.

#!/bin/bash

# Script de redémarrage automatique des services après mise à jour des certificats
# Utilisé par Vault Agent via la directive `command` dans le template

set -euo pipefail

NGINX_RESTART_CMD="chroot /host /usr/syno/bin/synosystemctl restart nginx"

echo "Certificates deployed"

echo "Restarting nginx..."
if $NGINX_RESTART_CMD; then
   echo "Nginx restarted successfully"
else
   echo "Error while restarting nginx"
   exit 1
fi

echo "Deployment completed successfully"

Le cert.tpl : La magie des templates Vault-Agent

Ce template est le cœur de notre système de génération automatique de certificats. Il utilise la syntaxe des templates Vault-Agent pour générer des certificats SSL/TLS et les déployer automatiquement dans l'architecture Synology.

Anatomie du template :

Le template utilise la fonction pkiCert de Vault-Agent pour générer un certificat via l'API PKI de Vault (que l'on va créer en Phase 3), puis déploie les différents composants dans les fichiers requis par Synology. Tu peux écrire tous les certificats que tu souhaites et même faire plusieurs templates du moment qu'ils sont déclarés dans le agent.hcl.

Paramètres de génération du certificat :

  • pki/issue/internal-server : Chemin vers le rôle PKI configuré dans Vault.
  • common_name=nas.internal : Nom principal du certificat (CN).
  • alt_names=consul.internal,vault.internal : Noms alternatifs (SAN).
  • ttl=2160h : Durée de vie du certificat (90 jours).

{{ .Cert }}{{ .CA }}{{ .Key }}

Cette ligne concatène la clé privée, le certificat et la chaîne d'autorité dans un seul fichier nommé all-certs, utilisé par Vault Agent pour suivre l'état du certificat.

Supprimer ce fichier sur l'hôte ne force pas automatiquement un renouvellement immédiat. Toutefois, cette suppression peut être détectée par Vault Agent si celui-ci est redémarré ou si son cache local est effacé, ce qui déclenche alors la régénération des certificats. Cette manipulation est utile si tu souhaites appliquer des modifications comme ajouter des SAN, changer la durée de vie (TTL) ou modifier d'autres paramètres du certificat.

Déploiement automatique des fichiers :

Le template utilise la fonction writeToFile pour créer automatiquement les fichiers nécessaires avec les bonnes permissions dans l'archive des certificats Synology.

Structure des fichiers générés :
  • privkey.pem : Clé privée (permissions 0600, propriétaire root : format attendu par Synology)
  • cert.pem : Certificat seul (permissions 0644, propriétaire root : format attendu par Synology)
  • fullchain.pem : Certificat + CA (permissions 0644, propriétaire root : format attendu par Synology)
  • all-certs : Fichier principal contenant tout (format attendu par Vault)
🔄 Processus automatique :

À chaque exécution du template (renouvellement du certificat), Vault-Agent génère un nouveau certificat, l'écrit dans les fichiers appropriés, puis déclenche le script restart_service.sh pour redémarrer nginx et appliquer les nouveaux certificats.

Ce template illustre parfaitement la puissance de Vault-Agent : une seule configuration qui gère génération, déploiement et rechargement automatique des certificats. Plus besoin de se soucier des renouvellements manuels !

{{- with pkiCert "pki/issue/internal-server" "common_name=nas.internal" "alt_names=consul.internal,vault.internal" "ttl=2160h" -}}
{{ .Cert }}{{ .CA }}{{ .Key }}
{{- .Key | writeToFile "/certs/myCert/privkey.pem" "root" "root" "0600" -}}
{{- .Cert | writeToFile "/certs/myCert/cert.pem" "root" "root" "0644" -}}
{{- .Cert | writeToFile "/certs/myCert/fullchain.pem" "root" "root" "0644" -}}
{{- .CA | writeToFile "/certs/myCert/fullchain.pem" "root" "root" "0644" "append" -}}
{{- end -}}

Maintenant, on va déployer notre première stack, celle qui contient Consul et Vault. En effet, on travaille avec deux fichiers docker-compose.yml distincts, donc deux stacks séparées :

  • swarm_consul_vault.yml pour Consul et Vault
  • swarm_vault_agent.yml pour le Vault-agent
Pour le moment, on ne va lancer que la première stack, car le Vault-agent bloquerait sur l'authentification sans identifiants.

# Avant de déployer, il va nous falloir un reseau
docker network create --driver overlay --attachable --scope swarm secret

# On va également avoir besoin du hash d'une clef de chiffrement symétrique de 32 bytes pour Consul 
KEY=$(openssl rand -base64 32); echo "Generated key: $KEY"; echo "$KEY" | docker secret create consul_encrypt_key -

# Vérification des secrets
docker secret ls

# Déploiement de la première stack (Consul + Vault)
docker stack deploy -c /volume1/docker/secret/swarm_consul_vault.yml secret

# Vérification que tout est bien déployé
docker stack services secret
Pro tip :

Les secrets Docker sont chiffrés au repos et ne sont accessibles qu'aux services autorisés. C'est bien mieux que des fichiers .env qui traînent partout !

Note

Je n'ai malheureusement pas encore trouvé comment chiffrer le flux réseau entre les composants dans le docker de Synology. Ajouter cette option dans la création du réseau semble bloquer le déploiement des conteneurs — probablement à cause d'un droit manquant au niveau du noyau, ou un autre mécanisme de sécurité interne.

Bref, ça ne fonctionne pas pour le moment.

Donc, si tu as besoin d'augmenter la sécurité, je te conseille de configurer un mTLS (mutual TLS) pour chiffrer la communication entre tes conteneurs, soit via un proxy type Traefik, soit directement en configurant des certificats dans tes services. Mais ça dépasse le cadre de cet article.

Attention, à la clef !

La clé de Consul (aka la clé gossip) n'est pas critique à mort, puisqu'elle ne sert qu'à chiffrer les communications entre les nœuds Consul. Pas touche aux données elles-mêmes, donc pas de panique côté secrets ou KV.

Théoriquement, tu peux la remplacer si tu l'as perdu — mais bon, c'est la galère : chaque nœud doit connaître la nouvelle clé, il faut gérer une période où nœuds supportent l'ancienne et la nouvelle (mode double clé, ambiance “team building”), et une mauvaise manip peut carrément éclater ton cluster en plusieurs morceaux. Bref, tu m'as compris...

Moralité : garde-la bien précieusement dans un coin. Je me suis même cassé le dos à t'écrire une commande qui te l'affiche, alors profites-en bien !

Si tout va bien, tu devrais voir 2 services dans cette première stack : consul et vault. Souvient toi, le Vault-agent, lui n'est pas lancé pour le moment car il a besoin de Vault configuré pour s'authentifier.

Cette gestion en deux stacks séparées n'est pas très pratique, mais Swarm ne permet pas de sélectionner les services à déployer dans une stack unique. C'est la solution la plus propre pour avancer sereinement : d'abord déployer ce qui fonctionne, puis, une fois les identifiants prêts, déployer la deuxième stack pour le Vault-agent.

Étape 3 : Initialisation de Vault

Là, c'est le moment crucial. Vault est démarré mais pas encore initialisé. On va créer les clés de chiffrement et le token root :

# Initialisation de Vault (à faire une seule fois !)
docker exec -it $(docker ps -qf "name=secret_vault.1") vault operator init

ULTRA IMPORTANT !

Cette commande va afficher 5 clés de déchiffrement et 1 token root. SAUVEGARDE TOUT ÇA DANS UN ENDROIT SÛR ! Si tu perds ces clés, tu peux dire adieu à toutes tes données Vault. Sérieusement, pas de blague. Ce n'est pas la même chose que la clef de Consul là.

Moi je les mets dans mon gestionnaire de mots de passe, certains les impriment et les mettent dans un coffre. À toi de voir, mais NE LES PERDS PAS !

Étape 4 : Vérification Consul et Vault

Avant d'aller plus loin, il est temps de vérifier que Consul et Vault ont démarré correctement et communiquent l'un avec l'autre, sinon la suite ne sert à rien vu qu'on va s'appuyer dessus. Comme dit le proverbe du dev : "mieux vaut vérifier deux fois que débugger toute la nuit" 😅

On va donc faire un petit check-up de nos services, histoire de s'assurer que tout ce beau monde se parle correctement avant de passer aux choses sérieuses.

Pour commencer, vérifions que Consul dans son coin est OK. Il suffit d'aller sur http://__NAS_IP__:8500 (oui, encore une interface web, je sais, on dirait que tout passe par le browser maintenant...)

consul doit afficher status ready
Pro tip :

Si ça mouline trop longtemps, c'est soit que le service n'est pas encore complètement démarré (patience, jeune padawan), soit qu'il y a un souci d'initialisation. Dans ce cas, un petit docker logs consul peut t'éclairer sur ce qui foire.

Ton interface devrait t'afficher ton (tes ?) nœud(s) de Consul avec un petit Active voter. Si tu vois ça, c'est que Consul a bien pris ses responsabilités de leader du cluster (même si on n'a qu'un seul nœud, il faut bien quelqu'un pour commander !)

Si c'est le cas, félicitations, c'est que ça tourne !

Ensuite on va vérifier l'état de la communication entre le Vault et le Consul, pour ce faire clique sur ton serveur "synology-01-consul". C'est là que ça devient intéressant, on va voir si nos deux services se parlent comme de vieux copains ou s'ils se boudent encore...

consul est healthy mais vault est scellé

Et là, surprise ! Tu noteras le Vault Sealed Status au-dessus du HealthCheck du Consul. Un beau rouge bien visible, impossible de le louper !

C'est tout à fait normal et attendu, on vient à peine de créer le Vault qui est scellé pour le moment. D'où le pavé rouge du dessus où j'ai bien bien BIEN insisté pour que tu conserves les 5 clés que l'initialisation t'a générées. J'espère que tu ne les as pas perdues dans un coin de ton bureau ou pire, dans un fichier texte nommé "passwords.txt" sur le bureau... 😱

Point d'attention sur le seal

À chaque fois que le service détectera le moindre truc, le Vault se mettra en "sécurité". C'est comme un chien de garde numérique qui se réveille dès qu'il entend du bruit !

C'est son mécanisme pour protéger tes secrets, c'est très bien comme ça mais cela nécessitera que tu viennes toi-même le déverrouiller. Oui, c'est chiant, mais c'est le prix de la sécurité !

Sache enfin que le verrouillage n'est pas qu'esthétique, il bloque complètement les accès. Tant que tu ne le débloques pas, c'est comme coffre-fort fermé : impossible de récupérer ou déposer quoi que ce soit dedans ! Même les applications qui essaient d'accéder aux secrets vont se prendre un beau "403 Forbidden".

Je sais qu'il y a des petits malins qui s'amusent à coder des scripts pour unseal automatiquement le Vault. NE LE FAIS PAS : ce sont des idiots. C'est comme construire un coffre-fort ultra-sécurisé… puis percer un trou dedans parce que t'as la flemme de tourner la clé. Sérieusement, qui ferait ça dans une banque ? Bah là, c'est pareil !

En gros :

Redémarrage = Vault scellé = Toi qui dois te lever pour le déverrouiller. C'est le côté moins fun de la sécurité...

Avant de quitter l'interface Consul, si tu vas dans la section Key/Value tu devrais voir une entrée "vault" apparaître. C'est le moment "aha !" où tu réalises que tout commence à bien communiquer !

consul doit afficher le volume vault

Oui c'est en effet là que Vault va venir écrire tout son fourbi ! C'est un peu pour ça qu'on a installé Consul d'ailleurs, histoire que les données ne se baladent pas sur un disque en vrac. Consul fait office de backend de stockage distribué, bien plus classe qu'un simple fichier JSON qui traîne...

Fun fact :

Si tu regardes bien, tu verras que Vault stocke ses données sous forme de clés-valeurs dans Consul. C'est du JSON encodé, mais heureusement on n'aura jamais à triturer ça à la main (ouf !). De toute façon si tu t'amuses à aller voir dedans, tout est chiffré et donc illisible ! Eh oui, c'est à ça que vont servir les clés, à déchiffrer le tout !

Maintenant, il est temps de déverrouiller (enfin !) le Vault, sans quoi comme on ne peut pas y accéder on ne pourra ni lire ni écrire dedans. Direction http://__NAS_IP__:8200. Tu devrais voir une interface dans le genre :

Note :

Si l'interface met du temps à charger, c'est normal, Vault prend son temps pour s'assurer que tout est sécurisé. La paranoia, c'est son truc !

consul doit afficher le volume vault

Là il va te falloir rentrer 3 des 5 clés qu'il nous a générées à l'étape précédente. Peu importe lesquelles du moment que les 3 sont différentes ! C'est le moment de sortir ton petit carnet secret (ou ton gestionnaire de mots de passe, soyons modernes).

Explication technique (pour les curieux) :

En fait Vault utilise le 🔐 Shamir's Secret Sharing qui permet de "diviser un secret (comme une clé maîtresse de chiffrement) en plusieurs parts, appelées shares, et définir un seuil minimal de parts à réunir pour reconstituer le secret." Bref comme on dit dans le milieu : cherche pas, c'est magique mais c'est bien pratique pour par exemple séparer ta clé sur plusieurs personnes de confiance et qu'une seule ne soit pas capable de déverrouiller seule les infos en cas de pépins :D

En pratique :

On a 5 clés, il en faut 3 pour déverrouiller. Donc même si tu perds 2 clés, tu peux encore accéder à tes secrets. Mais si tu en perds 3... ben c'est mort, il faudra tout réinitialiser (et dire au revoir à tes données). D'où l'importance de bien les sauvegarder !

Note :

Pas besoin que tout le monde fasse la queue derrière le même ordinateur avec son petit papier si les clés sont réparties entre plusieurs personnes. Chacun(e) peut les saisir depuis son propre poste !

Une fois déverrouillé, le Vault te demandera ta clé root. Forcément on n'a que ce seul token de connexion possible pour l'instant mais on va y remédier ! Cette clé root, c'est un peu comme le compte administrateur ultime : elle peut tout faire, alors on va vite créer des comptes plus limités par la suite.

Rappel important :

Cette clé root, c'est celle qui était affichée lors de l'initialisation, pas les 5 clés de déverrouillage. Si tu l'as perdue... ben tu recommences tout depuis le début mais bon je l'avais écrit en rouge, c'etait pas pour rien !

Normalement à ce stade tu devrais voir l'interface du Vault (presque) vide. C'est normal, on vient de naître dans l'écosystème Vault, il n'y a encore rien dedans ! C'est comme un frigo neuf : il faut le remplir maintenant.

L'interface est plutôt clean, HashiCorp a fait du bon boulot sur l'UX. Tu vas voir, c'est bien plus user-friendly qu'on pourrait s'y attendre pour un outil de sécurité enterprise.

Si tu retournes sur celle du Consul là où on avait le status du Vault Seal, tu ne devrais plus le voir en rouge mais comme ça :

consul doit afficher le volume vault
Moment de vérité :

Si tout est vert, c'est que ton installation de Consul et de Vault est opérationnelle ! 🎉

Tu as maintenant une base solide pour gérer tes secrets de manière professionnelle. Consul stocke tout ça proprement, Vault chiffre et sécurise, et toi tu peux dormir tranquille en sachant que tes mots de passe ne traînent plus dans des fichiers texte un peu partout.

🎯 Récap de ce qu'on a accompli :
  • ✅ Consul est up and running, prêt à stocker nos données
  • ✅ Vault communique avec Consul (ils sont copains maintenant)
  • ✅ Le processus de déverrouillage fonctionne (tes clés sont bonnes !)
  • ✅ L'interface web répond correctement

🔧 Phase 2 : Configuration de Vault (le fun commence)

Maintenant qu'on a un Vault initialisé, on va le configurer pour qu'il devienne notre petite PKI personnelle. C'est là que ça devient intéressant !

Étape 1 : Configuration DNS

Avant tout, il faut créer des entrées DNS pour nos services. Va dans ton routeur (ou ton serveur DNS) et ajoute :

  • vault.internal → IP de ton Synology
  • consul.internal → IP de ton Synology

Ensuite, on n'oublie pas d'aller configurer le reverse-proxy (celui du Synology dans mon cas) pour faire pointer nos FQDN tout frais en HTTPS dans :
Panneau de configuration > Portail de connexion > Avancé > Proxy inversé.
Chez moi, ça donne ça.

reverse proxy consul
reverse proxy vault

Puis, il va te falloir installer le CLI Vault sur ta machine (pas sur le NAS, le système n'est pas standard tu ne pourras pas le faire).

Documentation officielle pour installer le CLI
Explication :

L'interface Web ne permet pas de générer le type d'identifiant dont on va avoir besoin, c'est pour ça que l'on installe le CLI.

Sache que tu peux très bien générer tout ce qu'il faudra par la suite également avec Terraform plutôt qu'en CLI, le provider intégré sait déjà configurer Vault.

Une fois que c'est fait, tu peux te connecter à Vault via le CLI depuis ta machine:

# Configuration de l'adresse Vault
export VAULT_ADDR="https://vault.internal" 

# On ignore le self-signed certificat pour la configuration
export VAULT_SKIP_VERIFY=true 

# Connexion avec le token root
vault login
# Colle ton token root ici
🧰 Caisse à Outils

Pour plus de détails sur le fonctionnement des reverses proxy consultez l'article ici.

Étape 2 : Configuration AppRole

AppRole, c'est le système d'authentification qu'on va utiliser pour que Vault-Agent puisse se connecter à Vault automatiquement. C'est comme un couple login/password, mais en plus sécurisé.

# Activation de l'authentification AppRole
vault auth enable approle

# Création du rôle pour vault-agent
vault write auth/approle/role/vault-agent \
    token_ttl=24h \
    token_max_ttl=72h \
    secret_id_ttl=0 \
    secret_id_num_uses=0 \
    bind_secret_id=true \
    policies=vault-agent-policy
🐙 Version Terraform

Pour automatiser cette configuration, consultez ce fichier Terraform sur GitHub ici.

Ensuite, on récupère les identifiants (c'est ça qu'on va mettre dans nos secrets Docker pour le Vault-Agent avec de vrai valeurs d'authentification cette fois) :

# Création des secrets pour l'authentification (sur ton PC qui à Vault CLI)

# Récupération du role_id (c'est comme un username)
vault read -field=role_id auth/approle/role/vault-agent/role-id

# Génération d'un secret_id (c'est comme un password)
vault write -f -field=secret_id auth/approle/role/vault-agent/secret-id
# Enregistrement des secrets pour l'authentification (sur ton NAS qui à Docker)
echo "_ROLE_ID_" | docker secret create vault_role_id -
echo "_SECRET_ID_" | docker secret create vault_secret_id -
Explication :

Le role_id est fixe et peut être réutilisé. Le secret_id est unique et peut être régénéré. C'est ce couple qui permet à Vault-Agent de s'authentifier.

Dans notre configuration, on a choisi que le secret_id n'a pas de TTL (secret_id_ttl=0). En clair, le Vault-agent utilise le couple role_id + secret_id pour générer un token d'authentification, qu'il va ensuite rafraîchir toutes les 24 heures, avec une tolérance maximale de 72 heures (token_ttl=24h et token_max_ttl=72h).

Si on avait donné un TTL au secret_id, ça voudrait dire qu'il expire au bout d'un certain temps. Du coup, il faudrait arrêter l'agent, générer un nouveau secret_id manuellement, le déployer sur toutes les machines, puis redémarrer l'agent partout… Autant dire une galère sans fin, pas très pratique ni scalable !

Avec secret_id_ttl=0 et secret_id_num_uses=0 (utilisations illimitées), le secret_id ne périme jamais, ce qui simplifie grandement la gestion côté Vault-agent, qui peut tourner en continu sans interruption.

Étape 3 : Création des politiques

Les politiques Vault, c'est comme les permissions sur un système de fichiers : elles définissent qui peut faire quoi. Notre Vault-Agent aura besoin de permissions spécifiques :

# Création de la politique pour vault-agent
vault policy write vault-agent-policy - <<EOF
# Permission de créer des certificats
path "pki/issue/internal-server" {
    capabilities = ["create", "update"]
}

# Permission de lire les infos de l'autorité de certification
path "pki/ca" {
    capabilities = ["read"]
}

path "pki/ca/pem" {
    capabilities = ["read"]
}
EOF

# Association de la politique au rôle AppRole
vault write auth/approle/role/vault-agent policies="vault-agent-policy"
Principe de moindre privilège :

Notre agent ne peut que créer des certificats et lire les infos de la CA. Il ne peut pas lire d'autres secrets ou modifier la configuration. C'est ça, la sécurité !

🐙 Version Terraform

Pour automatiser cette configuration, consultez ce fichier Terraform sur GitHub ici.

🏛️ Phase 3 : Création de notre PKI (on devient une autorité de certification)

Maintenant, on va transformer Vault en autorité de certification. En gros, on va créer notre propre "Let's Encrypt", mais en version maison, qui va gérer tout seul les certificats pour notre réseau interne — sans avoir à sortir ces horribles commandes openssl qui donnent des sueurs froides !

Asterix & Obelix A38

Le sujet peut sembler un peu complexe au premier abord, mais ne t'inquiétes pas, je suis là pour te guider. Pour rendre tout ça plus clair et surtout plus sympa, on va utiliser une analogie simple et familière : celle d'une mairie. Tu vas voir, ça va nous aider à mieux comprendre cet univers un peu technique !

Étape 1 : Certificat racine

On va commencer par activer le moteur de gestion des certificats : notre PKI (le bureau des papiers d'identité d'une mairie) ainsi que créer le certificat racine. Ce certificat racine, c'est un peu comme le maire principal de notre ville - il va pouvoir tamponner et valider tous les autres certificats de nos services. Une fois qu'on aura notre maire en chef installé à la mairie, on pourra créer autant de certificats "citoyens" qu'on veut pour nos différents services, tous avec le tampon officiel qui va bien.

# Activation du moteur PKI
vault secrets enable pki

# Configuration de la durée de vie maximale (43800h = 5 an)
vault secrets tune -max-lease-ttl=43800h pki

# Génération du certificat racine
vault write -field=certificate pki/root/generate/internal \
    common_name="Internal CA" \
    issuer_name="internal-issuer" \
    ttl=43800h

# Configuration des URLs de distribution
vault write pki/config/urls \
    issuing_certificates="http://vault.internal/v1/pki/ca" \
    crl_distribution_points="http://vault.internal/v1/pki/crl"
C'est quoi tout ça ?

Le détail serait bien trop long à expliquer ici, la certification c'est un immense concept de sécurité informatique qu'il faut prendre le temps de présenter pas à pas (et crois-moi, ça mérite son propre article !). Pour le moment, contentons-nous du jargon essentiel pour comprendre ce qui se passe :

  • common_name: c'est le nom de ton maire. Oui, "Hidalgo" marcherait aussi, mais là c'est pas son mandat !
  • issuer_name : c'est l'étiquette qu'on colle sur son tampon officiel.
  • ttl : Time To Live, c'est le mandat de ton maire : pendant 5 ans comme les présidentielles, il pourra délivrer des certificats durant cette période avant de devoir prendre sa retraite !
  • issuing_certificates : où venir chercher les certificats
  • crl_distribution_points : où consulter la liste des certificats révoqués (les citoyens déchus… eh oui, on est un peu certificatophobe 😬)

Ne t'inquiète pas si ça paraît compliqué, l'important c'est que Vault va s'occuper de tout ça pour nous !

🧰 Caisse à Outils

Pour plus de détails sur le fonctionnement des certificats de sécurité consultez l'article ici.

🐙 Version Terraform

Pour automatiser cette configuration, consultez ce fichier Terraform sur GitHub ici.

Étape 2 : Rôle de certificat

Maintenant, on va créer un "rôle" qui définit quel type de certificats on peut générer - c'est comme créer un tampon spécialisé à la mairie. Au lieu d'avoir un seul tampon universel pour tout (ce qui serait un peu le bordel), on crée des tampons spécifiques : un pour les certificats de serveurs web, un autre pour les bases de données, etc. Notre rôle va dire "OK, avec ce tampon-là, tu peux créer des certificats pour tel ou tel usage, mais pas n'importe quoi !" :

# Création du rôle pour les certificats serveur
vault write pki/roles/internal-server \
    allowed_domains="internal" \
    allow_subdomains=true \
    max_ttl="2160h" \
    allow_any_name=false \
    enforce_hostnames=true \
    key_usage="DigitalSignature,NonRepudiation,KeyEncipherment,DataEncipherment" \
    ext_key_usage="server_auth" \
    basic_constraints="CA:FALSE"
Traduction en français :
  • allowed_domains : On signe uniquement pour le domaine .internal (notre territoire national, quel beau pays, hein ? 😄)
  • allow_subdomains : ... et ses sous-domaines (toutes nos régions : vault.internal, api.internal, etc.)
  • max_ttl : Durée de vie maximale des certificats (2160h = 90 jours)
  • allow_any_name : Pas question de signer n'importe quel nom au hasard
  • enforce_hostnames : Le nom sur le certificat doit correspondre à une vraie machine
  • key_usage : Fonctions de la clé, ce qu'elle peut faire (signature, chiffrement…)
  • ext_key_usage : Usage spécifique, ce pour quoi le certificat va être utilisé : (authentification serveur, client, etc.)
  • basic_constraints : Contraintes du certificat qui ici ne peut pas devenir lui-même une autorité pour signer d'autres certificats.

Rien de bien compliqué, ce sont juste nos règles locales : comme dans SimCity, mais avec plus de Matrix dedans.

Et tout ce qui ne respectera pas ces critères (libre à toi de les modifier hein évidemment) eh bien le maire va te rembarrer sec ! Comme à la préfecture quand il te manque une photocopie : pas de négociation, c'est non et au revoir.

🐙 Version Terraform

Pour automatiser cette configuration, consultez ce fichier Terraform sur GitHub ici.

Étape 3 : Test de génération

Allez, on fait un petit test pour voir si notre PKI fonctionne :

# Test de génération d'un certificat
vault write pki/issue/internal-server \
    common_name="test.internal" \
    ttl="720h"

Si ça marche, tu vas voir plein d'informations s'afficher, notamment le certificat généré - un peu comme quand tu demandes une carte d'identité et qu'ils te sortent une liasse de papiers avec plein de tampons officiels ! Si tu vois défiler des lignes de texte cryptique avec des "BEGIN CERTIFICATE" et des caractères bizarres, c'est bon signe ! 🎉 Ça veut dire que notre maire numérique a bien fait son boulot et a pondu un beau certificat tout frais !

D'ailleurs, essaie de certifier test.external par exemple et tu vas voir que ça ne marche pas car on a dit à notre maire de certifier "internal" et RIEN d'autre ! D'ailleurs comment le pourrait-il vu qu'il n'a pas le tampon pour le faire ??? Si tu demandais au maire de Paris de te délivrer un passeport américain - il te regarderait bizarrement en mode "Désolé, moi je ne m'occupe que des affaires parisiennes !" Notre PKI est pareil : elle est très à cheval sur son territoire et refuse catégoriquement de signer quoi que ce soit qui ne soit pas dans son domaine de compétence.

🤔 Et les erreurs de certificat "non reconnu" ?

Il faudra déployer le CA sur tes machines ! Enfin sa clé publique pour être précis. Parce que là, on a créé notre propre maire avec son propre tampon, mais les navigateurs et applications ne le connaissent pas encore. C'est comme si tu débarquais à l'aéroport avec un passeport délivré par la République de ton salon ou si tu brandissais une carte d'identité dessiné sous Paint - techniquement valide selon tes règles, mais personne ne va le reconnaître ! Pour que tes services fassent confiance à nos certificats "maison", il faudra installer notre certificat racine sur chaque machine. Attention, on installe juste le "registre d'authenticité des signatures" du maire, pas ses pouvoirs ! Le maire reste dans notre mairie, sa clef privé ne sortira JAMAIS du Vault. Tes machines sauront reconnaître "Ah oui, ce certificat vient bien de notre maire légitime", mais elles ne pourront pas créer des certificats pour autant - c'est juste pour dire "Tu peux lui faire confiance !""

Pour le récupérer rien de plus simple :

Va sur ton Vault : https://vault.internal > pki > issuer > internal-issuer

En haut à droite tu as un onglet Downloads

  • PEM format : le CA générique pour les systèmes Linux
    Il te suffira de le déposer au chemin approprié en fonction de ta distribution et de lancer la commande d'intégration
  • DER format : le CA reformaté pour système Windows
    C'est encore plus facile, il te suffit de double-cliquer dessus et de suivre les interfaces pour l'installer dans les 'certifications racine' de ton ordinateur
  • ne pas aller sur l'interface web de synology

Je précise que le CA n'est nécessaire que sur les machines qui ne seront pas gérées par le Vault-agent : ton PC, ton téléphone etc. Sinon c'est lui qui peut s'en charger et qui s'en chargera. C'est son taff après tout !

🔧 Phase 4 : Configuration de Synology (la partie fun)

Maintenant, on va préparer Synology pour recevoir nos beaux certificats automatiquement générés. C'est là que ça devient vraiment intéressant ! Notre objectif va être de retirer l'alerte de securité de l'interface web de Vault et Consul sur notre navigateur web donc n'oublie pas de récuperer et installer le CA !

Étape 1 : Préparation des dossiers

Synology stocke ses certificats dans un endroit bien précis. On va créer notre propre dossier de certificats :

# Connexion SSH à ton Synology
# Création du dossier pour nos certificats
sudo mkdir -p /usr/syno/etc/certificate/_archive/myCert

# Changement des permissions
sudo chown root:root /usr/syno/etc/certificate/_archive/myCert
sudo chmod 755 /usr/syno/etc/certificate/_archive/myCert
Étape 2 : Le fichier INFO

Synology utilise un fichier JSON pour lister tous les certificats disponibles. On va ajouter le nôtre :

# Backup du fichier original (on ne sait jamais)
sudo cp /usr/syno/etc/certificate/_archive/INFO /usr/syno/etc/certificate/_archive/INFO.backup

# Édition du fichier
sudo nano /usr/syno/etc/certificate/_archive/INFO

Ajoute cette section à la place la dernière accolade fermante du fichier :

,"myCert": {
    "desc": "Certificat auto-généré par Vault",
    "services": [
      {
      }
    ],
    "user_deletable": false
  }
}
Attention :

Respecte bien la syntaxe JSON ! Une virgule en trop ou en moins et tout plante. Si tu as peur, fais un backup du fichier avant de le modifier.

Danger :

Pendant la procédure, ne va JAMAIS sur l'interface web des certificats de Synology

ne pas aller sur l'interface web de synology

Pourquoi ? Eh bien tout simplement car ils ont eu la brillante idée d'intégrer un checker auto-générant si tu cliques sur l'onglet. Ce n'est pas dramatique mais il faudra nettoyer le contenu du dossier que tu viens de créer des certificats auto-générés par Synology.

De la même manière, n'utilise pas le certificat que l'on génère pour la configuration par défaut de ton NAS, du moins tant que tu n'as pas généré des certificats valides. En effet, l'accès à l'interface web va provoquer une vérification du certificat et donc une génération auto-signée par le CA de Synology s'il n'arrive pas à l'utiliser (mal formé, expiré, etc.).

En gros, n'utilise ce certificat que pour tes services à toi, pas pour ceux de Synology et il ne viendra pas t'embeter même si tu passes par son reverse proxy.

Note :

Si tu veux automatiser les services qui devront utiliser nos certificats, tu peux les indiquer sous forme de liste dans le tableau "services". Mais attention à ne pas dupliquer des services déjà associés à d'autres certificats, sinon ton Synology risque de ne pas apprécier ! Dans cet article, on va rester simple et les appliquer via l'interface Web, une fois les certificats déployés.

🤖 Phase 5 : Lancement Vault-Agent

Bon, on y est. Le moment où tout ce qu'on a préparé en amont va (normalement) fonctionner. On va relancer notre stack docker-compose pour cette fois activer Vault-agent (que l'on avait retiré jusqu'ici) et déclencher le déploiement automatique de nos certificats.

Étape 1 : Déploiement de la stack Vault-Agent

Une fois que tu as les identifiants corrects et que Vault est configuré, tu peux redéployer la stack complète avec le Vault-Agent pour activer la mise à jour automatique des certificats :

# On déploie maintenant la stack complète avec Vault-Agent
docker stack deploy -c /volume1/docker/secret/swarm_vault_agent.yml  secret

Ensuite, assure-toi que Vault est bien unsealed (c'est-à-dire déverrouillé). Sinon, rien ne bougera côté génération de certificats. Si Vault est encore scellé, connecte-toi à l'interface web (https://vault.internal) ou lance plusieurs fois la commande :

vault operator unseal

Cela déverrouille Vault avec les clés d'unseal, nécessaires pour que Vault puisse accéder à ses secrets et délivrer tes certificats.

À partir de là, Vault-Agent va :

  • S'authentifier auprès de Vault grâce au mécanisme AppRole (plus sécurisé qu'un simple token)
  • Se connecter au serveur Vault pour demander un certificat TLS via l'API PKI
  • Générer le certificat en fonction du template que tu as défini (avec les SAN, TTL, etc.)
  • Déployer les certificats au bon endroit sur ton NAS Synology (pour que DSM puisse les utiliser)
  • Redémarrer automatiquement nginx (le serveur web du NAS) pour appliquer les nouveaux certificats en direct

En clair, tu passes d'une config manuelle et casse-tête à une machine bien huilée qui bosse toute seule. Tu peux aller te faire un café pendant que ça tourne.

Étape 2 : Vérification des certificats

Maintenant que Vault-Agent a bossé, il est temps de vérifier que tout est bien généré et à sa place :

ls -l /usr/syno/etc/certificate/_archive/myCert/

Tu devrais voir apparaître :

certificat installé sur le serveur
  • privkey.pem → ta clé privée (ultra sensible, permissions serrées à 0600)
  • cert.pem → le certificat public
  • fullchain.pem → certificat + chaîne complète (certificat + CA)
  • all-certs → fichier utilisé par Vault pour gérer le suivi des certificats

Attention : Le redémarrage de NGINX est nécessaire pour que DSM prenne en compte les nouveaux certificats, mais cela redémarre aussi Docker. Cela veut dire que tous tes services dans ta stack Consul-Vault (et les autres) vont redémarrer aussi. Patience, attends que tout soit bien reparti avant d'aller plus loin et n'oublie pas qu'il faudra à nouveau unseal ton Vault.

Si tout est là, félicitations !

Ton Vault-Agent a fait son taf, les certificats sont signés, frais et stockés pile là où Synology les attend. Plus de risques d'erreur humaine ou de certificat périmé qui fait planter ton NAS.

Rappel important

Le fichier all-certs joue le rôle de déclencheur principal pour le renouvellement automatique. Si tu le supprimes, Vault-Agent va détecter son absence et générer un nouveau certificat lors de la prochaine vérification programmée. C'est super pratique si tu veux ajouter un SAN, ajuster la durée de vie, ou corriger une erreur dans le template de départ.

Mais attention : Vault-Agent ne compare pas le contenu sur l'hôte du fichier, il se base uniquement sur ce qu'il "sait" — c'est-à-dire la configuration initiale définie dans le template (durée, SAN, CN, etc) et le Vault. Si tu modifies un paramètre mais que tu ne redémarres pas l'agent, il ne fera pas forcément de mise à jour immédiate.

Étape 3 : Activation dans DSM

Direction DSM maintenant, dans Panneau de configuration > Sécurité > Certificat.

Tu verras apparaître les certificats générés par Vault. Il ne reste plus qu'à les assigner aux services que tu souhaites sécuriser (Vault, Consul, ton API etc, etc.) via l'onglet "Paramètres".

certificat installé dans DSM

Ce qui est génial, c'est que le lien entre les services (ceux que tu as déclarés dans le reverse proxy) et les certificats est fait à un niveau supérieur, via le système de key-value JSON qu'on a modifié manuellement. En clair : ce n'est pas le contenu du certificat qui détermine quel service l'utilise, mais bien une référence déclarative dans un fichier de config système.

Résultat : tu peux régénérer tes certificats, les renouveler, les faire bénir par trois PKI différentes… tant que tu ne touches pas à la déclaration JSON dans le fameux fichier INFO, les assignations resteront intactes !

Attention quand même : si tu modifies manuellement le fichier INFO (ou que tu fais une erreur de syntaxe, là oui, il faudra repasser par la case “Assignation manuelle” dans DSM.

Bref, tant que tu laisses le INFO tranquille, DSM reste zen.

Étape 4 : Et maintenant ?

Avec ta PKI maison qui tourne comme une horloge suisse, tu vas pouvoir :

  • Étendre facilement ta gestion des certificats : Ajoute autant de templates Vault que nécessaire, chacun adapté à un service ou un environnement spécifique.
  • Déployer partout : Sur des machines physiques, des VM, des containers, des API internes, des reverse proxies, bref, tous les points qui ont besoin de TLS.
  • Automatiser la maintenance : Intègre cette gestion dans un repo Git, un pipeline CI/CD, et finis les manips manuelles et les oublis. Moi je vous en propose un ...roulement de tambour... : ANSIBLE !

Pourquoi c'est cool ? Parce que tu gagnes en sécurité (plus de certificats périmés), en fiabilité (moins d'erreurs humaines), et en temps (moins de support à gérer). Et en prime, tu prépares ton infrastructure pour évoluer vers du Kubernetes, des clusters multi-serveurs, et des architectures modernes.

Envie d'aller plus loin ?

Dans le prochain article, on passera à la vitesse supérieure : on intégrera un ACME challenge pour obtenir des certificats publics juste avec Vault (pas de cerbot ou autres), signés par une vraie autorité de certification (genre Let's Encrypt). Fini les certificats auto-signés, place à la gloire publique pour exposer sur Internet nos applications ! Stay tuned !

Debug & Commandes utiles

Si jamais ça coince ou que tes certificats ne sont pas pris en compte, voici quelques commandes clés pour redémarrer les services essentiels sur ton Synology qui risquent de ce bloquer:

/usr/syno/bin/synosystemctl restart nginx
synopkg start FileService
synopkg start ContainerManager

Ces commandes permettent de relancer :

  • NGINX : le serveur web qui gère notamment l'interface DSM et la prise en charge des certificats TLS.
  • FileService : le service de gestion des fichiers, souvent impacté lors de changements dans les accès sécurisés.
  • ContainerManager : le gestionnaire des conteneurs Docker, nécessaire pour que Vault-Agent et autres services fonctionnent correctement.

Redémarrer ces services évite d'avoir à redémarrer complètement ton NAS, ce qui est beaucoup plus pratique et rapide.

Quand ça coince, c'est souvent NGINX qui se retrouve avec des certificats dans un état ambigu : un bout à moitié remplacé, un autre qui traîne… Résultat, il ne sait plus quoi charger et plante en silence. Tu peux confirmer ça avec journalctl, mais franchement, le plus rapide, c'est de faire un reset en bonne et due forme.

Voici la procédure simple pour revenir à un état propre :

  1. Commence par vider manuellement ton dossier de certificats (ou le renommer si tu veux garder une copie).
  2. Va dans l'interface DSM, section Panneau de configuration > Sécurité > Certificat.
  3. DSM va détecter l'absence de certificats et générer automatiquement un nouveau certificat auto-signé via son propre CA.
  4. Redémarre NGINX pour qu'il prenne en compte les nouveaux certificats :
/usr/syno/bin/synosystemctl restart nginx

Et enfin — oui, c'est contre-intuitif mais important — vide à nouveau le dossier de certificats car il va te faire plein de fichiers dont tu n'as pas besoin.

Et voilà ! Tu es revenu à zéro proprement, avec un DSM content, un NGINX relancé, et un Vault prêt à redéposer ses petits PEM tout frais.

Cette astuce combinée aux commandes de redémarrage te permet de restaurer rapidement un état fonctionnel, notamment si un certificat est corrompu ou mal configuré, sans provoquer d'interruption longue sur ton système.

Petit conseil

Pas de panique : ce ne sont que des certificats. Tant que tu ne remplaces pas les certificats utilisés par Synology pour ses services internes, rien de vraiment casse-cou — tu peux y aller franco, tu ne vas pas le casser ton NAS !