Activer le chiffrement après l'installation
Vue d'ensemble
Chaque couche de chiffrement s'active indépendamment, avec sa propre procédure, son propre niveau de risque et son propre impact sur le service. Deux couches se pilotent depuis l'application (elles ne sont pas traitées ici comme des opérations d'hôte) :
- Secrets applicatifs (trousseau KEK/DEK) et rotation des clés — depuis l'interface d'administration (voir Rotation des clés).
- Sauvegardes chiffrées — une variable de configuration sur les hôtes de sauvegarde.
Les couches d'infrastructure (disque LUKS, TLS interne PostgreSQL, overlay IPsec, etcd) touchent aux hôtes et à leurs services système. Ce sont elles que les deux méthodes ci-dessous adressent.
Les couches, par niveau de risque
| Couche | Niveau | Interruption | Ce qu'elle protège |
|---|---|---|---|
| Rotation des clés (DEK) | Sûr | Aucune | Secrets applicatifs au repos (MFA, SMTP, SSO, connecteurs). |
| Sauvegardes (age) | Sûr | Aucune | Artefacts de sauvegarde au repos et en transit. |
| TLS PostgreSQL | Sûr | Aucune (redémarrage progressif) | Trafic base de données interne. |
| Disque au repos (LUKS) | Sûr | Aucune (nœud par nœud) | Volumes de données (base + stockage objet) au repos. |
| Réseau overlay (IPsec) | Fenêtre | ~1 à 2 min | Tout le trafic inter-conteneurs (cache, S3, HTTP/WS interne). |
| TLS etcd | Fenêtre | Brève (DCS) | Trafic du magasin de coordination du cluster. |
patronictl -c /etc/patroni.yml list doit montrer un seul primaire et des répliques à jour (lag 0), et le stockage objet doit avoir son quorum.Quelle méthode choisir ?
Les deux méthodes produisent exactement le même résultat. Elles diffèrent par où l'on tape les commandes.
| Méthode A — orchestrée | Méthode B — manuelle | |
|---|---|---|
| Où | Depuis une station de contrôle (une seule machine, avec accès SSH par clé aux hôtes). | En SSH sur chaque hôte concerné, tour à tour. |
| Comment | Une commande par couche ; l'orchestrateur se connecte lui-même aux hôtes, gère le séquencement et vérifie. | Les commandes système réelles (cryptsetup, clevis, patronictl, docker…), copiées-collées hôte par hôte. |
| Prérequis | Le dépôt infra/crypto/ et le .env.local d'accès SSH présents sur la station. | Aucun outillage ; un accès SSH root à chaque hôte. |
| Idéal pour | Répétabilité, plusieurs clusters, opérateur qui a déjà le dépôt de déploiement. | Transparence totale, environnements verrouillés sans la station de déploiement, compréhension pas à pas. |
Choisir l'une ou l'autre ; il n'est pas nécessaire de faire les deux. TLS etcd et Rotation des clés sont identiques quelle que soit la méthode et sont décrites une fois, à la fin.
Méthode A — orchestrée depuis une station de contrôle
L'outillage infra/crypto/retrofit.mjs tourne sur la machine d'administration (le même poste, avec le même accès SSH par clé, que le déploiement). Il se connecte lui-même à chaque hôte, applique la couche, gère le séquencement nœud par nœud et vérifie. Vous ne faites aucun SSH manuel. Toutes les commandes de cette section se lancent depuis la station de contrôle.
retrofit.mjs all n'exécute que les couches sûres, sans interruption (sauvegardes, TLS PostgreSQL, LUKS). Les couches à fenêtre de maintenance (overlay, etcd) exigent un drapeau explicite. Options communes : --dry-run (n'applique rien), --force (non interactif), --hosts a,b,c.Disque au repos (LUKS)
# station de contrôle
node infra/crypto/retrofit.mjs enable-luks --pg --force # ajouter --garage pour le stockage objet
L'orchestrateur chiffre les répliques d'abord, bascule le primaire vers une réplique chiffrée, puis chiffre l'ancien primaire — sans interruption. L'opération est idempotente (la relancer saute les nœuds déjà chiffrés).
TLS interne PostgreSQL
# station de contrôle
node infra/crypto/retrofit.mjs enable-pg-tls --force
Pose ssl=on dans la configuration du cluster et redémarre les membres un par un (répliques d'abord). L'application reste disponible (PostgreSQL accepte encore les connexions non-TLS). Le passage de l'application en TLS est une seconde passe séparée.
Sauvegardes chiffrées (age)
Prérequis : age installé sur les hôtes de sauvegarde et une clé générée (voir la méthode manuelle pour age-keygen) ; garder la clé privée en séquestre hors cluster.
# station de contrôle
node infra/crypto/retrofit.mjs enable-backup-encryption --recipient age1votreclépublique --force
Chiffrement du réseau overlay (IPsec)
S'assurer d'abord que le fichier de pile déclare l'overlay chiffré, puis :
# station de contrôle
node infra/crypto/retrofit.mjs enable-overlay-ipsec --window --force
Méthode B — manuelle, hôte par hôte
Sans outillage : l'administrateur se connecte en SSH sur les hôtes concernés et exécute les commandes en root (sudo -i, ou sudo devant chaque commande). Chaque bloc de commandes ci-dessous indique sur quel hôte se connecter. Les « hôtes de base » sont les nœuds portant le rôle « db » ; les hôtes « manager » pilotent l'orchestrateur de conteneurs.
Disque au repos (LUKS)
Migration un nœud à la fois, sans interruption : chiffrer les répliques d'abord, basculer le primaire vers une réplique déjà chiffrée, chiffrer l'ancien primaire en dernier. Sur chaque nœud, le clair est mis de côté, le volume chiffré est monté vide, la base se re-synchronise dessus.
Étape 1 — préparer les serveurs de clés Tang (une fois, sur chaque hôte de base) :
# SSH sur CHAQUE hôte de base, en root
apt-get install -y tang clevis clevis-luks cryptsetup
mkdir -p /etc/systemd/system/tangd.socket.d
printf '[Socket]\nListenStream=\nListenStream=7500\n' > /etc/systemd/system/tangd.socket.d/override.conf
systemctl daemon-reload
systemctl enable --now tangd.socket
Étape 2 — repérer les rôles. Ne jamais commencer par le primaire.
# SSH sur n'importe quel hôte de base
patronictl -c /etc/patroni.yml list # colonne "Role" : Leader = primaire, Replica = réplique
Étape 3 — chiffrer une réplique. Remplacer PAIR1/PAIR2 par les IP des deux autres hôtes de base.
# SSH sur la réplique à chiffrer, en root
systemctl stop patroni
# a) mettre le clair de côté et préparer un point de montage vide
mv /var/lib/postgresql/17/main /var/lib/postgresql/17/main.preluks
mkdir -p /var/lib/postgresql/17/main
# b) créer le conteneur LUKS2 et le lier aux serveurs Tang des pairs (Clevis SSS, seuil 1)
fallocate -l 4G /var/lib/vaks-pgdata.luks
PASS=$(openssl rand -base64 32)
printf '%s' "$PASS" | cryptsetup luksFormat --type luks2 --batch-mode /var/lib/vaks-pgdata.luks -
printf '%s' "$PASS" | clevis luks bind -d /var/lib/vaks-pgdata.luks -k - -y sss \
'{"t":1,"pins":{"tang":[{"url":"http://PAIR1:7500"},{"url":"http://PAIR2:7500"}]}}'
printf '%s\n' "$PASS" > /root/vaks-pgdata.luks.recovery.key # clé de SECOURS : exfiltrer hors cluster, puis supprimer d'ici
chmod 600 /root/vaks-pgdata.luks.recovery.key
# c) formater, monter, rendre la propriété à postgres
clevis luks unlock -d /var/lib/vaks-pgdata.luks -n pgdata_crypt
mkfs.ext4 -q /dev/mapper/pgdata_crypt
mount /dev/mapper/pgdata_crypt /var/lib/postgresql/17/main
rm -rf /var/lib/postgresql/17/main/lost+found
chown -R postgres:postgres /var/lib/postgresql/17/main
chmod 700 /var/lib/postgresql/17/main
Étape 4 — déverrouillage automatique au démarrage (même réplique). Sans cette unité, le volume resterait verrouillé après un redémarrage.
# SSH sur la même réplique, en root
cat > /usr/local/sbin/vaks-cryptmount.sh <<'EOF'
#!/bin/bash
set -e
F="$1"; N="$2"; M="$3"
for i in $(seq 1 30); do
[ -e "/dev/mapper/$N" ] && break
clevis luks unlock -d "$F" -n "$N" 2>/dev/null && break || true
sleep 2
done
mountpoint -q "$M" || mount "/dev/mapper/$N" "$M"
EOF
chmod 755 /usr/local/sbin/vaks-cryptmount.sh
cat > /etc/systemd/system/vaks-cryptmount-pgdata.service <<'EOF'
[Unit]
Description=Unlock+mount pgdata (Clevis/Tang)
DefaultDependencies=no
Wants=network-online.target
After=network-online.target
Before=patroni.service
ConditionPathExists=/var/lib/vaks-pgdata.luks
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/vaks-cryptmount.sh /var/lib/vaks-pgdata.luks pgdata_crypt /var/lib/postgresql/17/main
[Install]
WantedBy=multi-user.target
EOF
mkdir -p /etc/systemd/system/patroni.service.d
printf '[Unit]\nRequires=vaks-cryptmount-pgdata.service\nAfter=vaks-cryptmount-pgdata.service\n' \
> /etc/systemd/system/patroni.service.d/10-cryptmount.conf
systemctl daemon-reload
systemctl enable vaks-cryptmount-pgdata.service
# relancer Patroni : la réplique se re-clone (basebackup) sur le volume chiffré
systemctl start patroni
watch patronictl -c /etc/patroni.yml list # attendre l'état "streaming", lag 0, puis Ctrl-C
Étape 5 — répéter les étapes 3-4 sur chaque réplique. Puis basculer le primaire et chiffrer l'ancien primaire en dernier :
# SSH sur n'importe quel hôte de base
patronictl -c /etc/patroni.yml switchover --candidate <réplique-déjà-chiffrée> --force
Étape 6 — vérifier puis nettoyer le clair une fois la validation faite.
# SSH sur chaque hôte de base
mount | grep pgdata_crypt # volume chiffré monté
cryptsetup luksDump /var/lib/vaks-pgdata.luks | grep -i cipher # aes-xts-plain64
patronictl -c /etc/patroni.yml list # 1 primaire + répliques à jour
rm -rf /var/lib/postgresql/17/main.preluks # SUPPRIMER le clair APRÈS validation
/var/lib/vaks-garage.luks monté sur /srv/vaks/garage — un nœud à la fois, en vérifiant le quorum entre chaque.TLS interne PostgreSQL
Étape 1 — installer le certificat serveur, sur chaque hôte de base :
# SSH sur CHAQUE hôte de base, en root
install -D -o postgres -g postgres -m 644 /etc/vaks/server.crt /etc/vaks/pg/server.crt
install -D -o postgres -g postgres -m 600 /etc/vaks/server.key /etc/vaks/pg/server.key
Étape 2 — activer ssl=on (une fois) puis redémarrer les membres un par un. Remplacer vakspm-cluster par le scope: de /etc/patroni.yml.
# SSH sur n'importe quel hôte de base, en root
patronictl -c /etc/patroni.yml edit-config --force \
-s postgresql.parameters.ssl=on \
-s postgresql.parameters.ssl_cert_file=/etc/vaks/pg/server.crt \
-s postgresql.parameters.ssl_key_file=/etc/vaks/pg/server.key
# redémarrage un par un — répliques d'abord, primaire en DERNIER
patronictl -c /etc/patroni.yml restart vakspm-cluster <réplique> --force
patronictl -c /etc/patroni.yml restart vakspm-cluster <primaire> --force
# vérifier (sur le primaire)
sudo -u postgres psql -p 5432 -tAc 'SHOW ssl' # doit renvoyer : on
Sauvegardes chiffrées (age)
Additif et réversible. Prérequis : les sauvegardes hôte déjà installées (/etc/vaks-backup.env existe).
# SSH sur CHAQUE hôte de sauvegarde, en root
apt-get install -y age
age-keygen -o /root/vaks-backup-age.key # affiche la clé PUBLIQUE age1… ; garde la PRIVÉE en séquestre hors cluster
echo 'BACKUP_AGE_RECIPIENT=age1votreclépublique' >> /etc/vaks-backup.env
age séquestrée hors cluster, une sauvegarde chiffrée est irrécupérable.Chiffrement du réseau overlay (IPsec)
Étape 1 — déclarer l'overlay chiffré dans le fichier de pile déployé (sur un hôte manager), sous le réseau vakspm :
# SSH sur un hôte manager — éditer docker-stack.gen.yml
# networks:
# vakspm:
# driver: overlay
# attachable: true
# driver_opts: <-- ajouter
# encrypted: "true" <-- ajouter
Étape 2 — recréer le réseau (drapeau non modifiable à chaud) :
# SSH sur un hôte manager, en root
docker stack rm vaks-pm # arrête les services (~1-2 min)
sleep 20 # laisser le réseau overlay disparaître
docker network ls | grep vaks-pm_vakspm || echo "réseau supprimé, prêt à redéployer"
docker stack deploy -c ~/vakspm-cluster/docker-stack.gen.yml vaks-pm
docker network inspect vaks-pm_vakspm -f '{{index .Options "encrypted"}}' # true
TLS etcd (commun aux deux méthodes)
Chiffre le trafic du magasin de coordination du cluster. Cette couche est la plus délicate — elle se fait toujours par reconstruction manuelle, quelle que soit la méthode choisie pour les autres couches.
etcdctl member update) sur un cluster vivant a déjà provoqué une panne (magasin de coordination hors service, base en lecture seule ~15 min). La seule méthode supportée est la reconstruction complète : la donnée etcd est jetable, Patroni la reconstruit.Sous fenêtre de maintenance (la base passe brièvement en lecture seule). Sur chaque hôte etcd :
# SSH sur CHAQUE hôte etcd, en root — fenêtre de maintenance
systemctl stop patroni
systemctl stop etcd
mv /var/lib/etcd /var/lib/etcd.old && mkdir -p /var/lib/etcd
# re-amorcer etcd EN TLS d'emblée à partir du certificat interne /etc/vaks/server.*
ETCD_INTERNAL_TLS=1 bash ~/vakspm-cluster/bootstrap/10-etcd.sh
# une fois les 3 nœuds etcd repartis en https, relancer Patroni ; il recrée son état
systemctl start patroni
patronictl -c /etc/patroni.yml list # cluster reformé, ssl re-capturé
patronictl list à la fin.Garde de la KEK — trousseau local, KMS ou HSM
Rappel du modèle à enveloppe : chaque secret est chiffré par une DEK (par organisation), et chaque DEK est enveloppée (wrap) par la clé maître, la KEK. Par défaut la KEK vit dans un trousseau de fichiers sur les hôtes. On peut déplacer sa garde vers un KMS ou un HSM on-premise, à chaud et sans re-chiffrer les données — seules les DEK sont re-enveloppées.
HSM on-premise (PKCS#11). L'application ne parle jamais PKCS#11 directement : un sidecar « key-broker » (image infra/hsm-broker/) isole la bibliothèque du fournisseur et le PIN, et n'expose que /wrap et /unwrap sur le réseau interne. La KEK ne quitte jamais le HSM (le wrap/unwrap s'exécute dedans).
- Déployer le sidecar broker sur l'overlay interne, avec la bibliothèque PKCS#11 du fournisseur montée en lecture seule et le PIN + un jeton partagé montés en secrets (voir
infra/hsm-broker/README.md). - Sur les services
api,workeretcontrolplane, poser les variables puis faire un rolling update :# sur un hôte manager — env des services applicatifs CRYPTO_HSM_BROKER_URL=http://hsm-broker:8081 CRYPTO_HSM_KEY_LABEL=<label de la KEK dans le HSM> CRYPTO_HSM_BROKER_TOKEN=<jeton partagé> CRYPTO_KEY_PROVIDER=hsm # les NOUVELLES DEK sont désormais wrappées par le HSM - Re-envelopper les DEK existantes sous la KEK du HSM (par organisation), depuis Admin → Sécurité et conformité → Clés de chiffrement → Rotation KEK, ou par l'API :
POST /api/v1/admin/crypto/rewrap-keks - Vérifier :
GET /api/v1/admin/crypto/statusrenvoie chaque DEK enhsm:<label>("activeKek":"hsm:…").
CRYPTO_KEY_PROVIDER=local + rewrap-keks ; garder le trousseau local monté jusqu'à validation. ⚠️ Si le HSM et sa sauvegarde de clé sont perdus, les secrets sont irrécupérables par design — suivre la procédure de sauvegarde/escrow de la KEK du fournisseur.Un KMS (AWS KMS ou endpoint compatible) suit exactement le même schéma, avec CRYPTO_KEY_PROVIDER=kms et CRYPTO_KMS_KEY_ID / _REGION / _ENDPOINT. Les blobs local, kms et hsm coexistent (routage par schéma de kekRef) : un état mixte pendant la migration est normal.
Rotation des clés de données (DEK)
Indépendant de l'infrastructure et identique aux deux méthodes. Vaks PM chiffre les secrets sensibles au repos (secrets multifacteurs, mot de passe SMTP, secrets d'authentification unique et de connecteurs) avec une clé de données (DEK) par organisation, elle-même enveloppée par la clé maître (KEK) — voir le modèle sous Sécurité. La rotation de la DEK re-chiffre l'ensemble de ces secrets sous une clé fraîche, sans interruption.
- Ajouter ou activer une KEK réelle (au lieu de la clé maître par défaut) : opération à chaud, appliquée sur simple re-enveloppement, les données chiffrées n'étant jamais touchées.
- Faire tourner la DEK (par exemple après suspicion de compromission) depuis Admin → Sécurité et conformité → Clés de chiffrement, ou par l'API d'administration. L'opération est transactionnelle, reprenable, et couvre l'intégralité des secrets stockés.