LE CONSTAT. 39 roles declarent leurs flux, 32 leur empreinte, 32 leur authentification — tous derives. Et 19 groupes sur 19 declaraient une surveillance en prose que RIEN n executait ; Icinga en surveillait deux. La carte disait ce qui etait surveille, et personne ne surveillait. LE MECANISME. Un role declare ses sondes dans meta/supervision.yml et depose lui-meme son script dans /usr/local/lib/setops/sondes/. Le porteur client_sante les fait toutes tourner et pousse un resultat passif par sonde, sans savoir ce qu elles mesurent. serveur_icinga derive les objets Service ET le filtre de permission d API des memes declarations. Ajouter une sonde ne demande de toucher ni au porteur ni a Icinga. PREMIERE SONDE : client_pki/certificat. Heures restantes sur le certificat reellement pose, chaine verifiee, et empreinte SERVIE comparee au disque quand un service le consomme. 14/14 au tenant, 7/7 au site. QUATRE OBSTACLES, ET TROIS SONT LA MEME LECON. La sonde a rendu 14/14 en CRITIQUE sur une PKI saine : openssl verify -CAfile racine ne trouve pas l intermediaire qui signe nos certificats. step certificate verify, lui, repond VALIDE. Deployee au site, elle a rendu 5/7 : le seuil d avertissement (12 h) etait AU-DESSUS du point de renouvellement (8 h, le tiers restant). Elle criait avant que le mecanisme ne soit cense agir. Seuils ramenes a 6 h et 3 h. Un seuil se DERIVE du moment ou le mecanisme surveille agit. Une alarme toujours allumee ne vaut pas mieux qu une alarme jamais allumee : elle apprend a ne plus regarder. Une sonde se prouve DEUX FOIS, verte sur le sain et rouge sur le casse. Le filtre d API etait ecrit avant la lecture des declarations : les services auraient existe et Icinga aurait refuse leurs resultats. Et mon controle negatif a casse un service reel : substituer le certificat d hote a fait propager un cert sans sa clef vers node_exporter. Un controle negatif se fait sur une COPIE. P64 tient les deux bouts : declaree sans etre deposee, ou deposee sans etre declaree. Trois controles negatifs rejoues. make prouver : CONFORME, 64 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
129 lines
6.7 KiB
YAML
129 lines
6.7 KiB
YAML
---
|
|
client_pki_paquets:
|
|
- step-cli
|
|
|
|
client_pki_steppath: "/etc/step"
|
|
|
|
# L'AC INTERNE SE DÉRIVE DU GROUPE QUI LA PORTE, elle ne se nomme pas (2026-08-25).
|
|
#
|
|
# Cette URL écrivait `infra-pki-01` EN DUR. Chez un tenant ça marchait toujours : la
|
|
# nomenclature nomme sa PKI ainsi, donc le littéral et la réalité coïncidaient. Le SITE
|
|
# est le premier à l'appeler autrement — `site-pki-01` — et les cinq machines ont essayé
|
|
# de s'enrôler auprès d'un hôte qui n'existe nulle part :
|
|
#
|
|
# lookup infra-pki-01.genese.internal ... no such host
|
|
#
|
|
# Le message accusait le DNS, alors que c'était le nom qui était faux. Un littéral qui a
|
|
# raison par coïncidence est un bogue qui attend son premier cas particulier.
|
|
#
|
|
# `groups['serveur_step_ca']` dit qui porte l'autorité, dans un tenant comme sur un site.
|
|
# Pas de repli : sans autorité déclarée, s'adresser à un nom inventé produirait exactement
|
|
# la panne qu'on vient de corriger. Le rôle refuse (voir tasks/main.yml).
|
|
client_pki_ca_hote: "{{ (groups['serveur_step_ca'] | default([])) | first | default('', true) }}"
|
|
client_pki_ca_url: "https://{{ client_pki_ca_hote }}.{{ domaine_interne }}:8443"
|
|
client_pki_provisioner: "admin@{{ domaine_interne }}"
|
|
|
|
# Identite de l'hote.
|
|
client_pki_nom_cert: "{{ ansible_fqdn | default(ansible_hostname) }}"
|
|
client_pki_cert: "{{ client_pki_steppath }}/certs/{{ client_pki_nom_cert }}.crt"
|
|
client_pki_cle: "{{ client_pki_steppath }}/certs/{{ client_pki_nom_cert }}.key"
|
|
|
|
# LE CERTIFICAT DOIT COUVRIR LES NOMS SOUS LESQUELS ON APPELLE LE SERVICE (2026-08-25).
|
|
#
|
|
# Les SANs ne portaient que l'identite de la MACHINE — FQDN, nom court, IP. Or un service
|
|
# se declare `expose:` au plan, sous un nom qui lui survivra : `forge.genese.internal`
|
|
# reste le nom de la forge meme si elle demenage de `site-forge-01` a `site-forge-02`.
|
|
#
|
|
# Ce nom etait publie partout — plancher /etc/hosts, zone DNS — et couvert nulle part. Le
|
|
# clonage du genome a bute dessus : « certificate subject name (site-forge-01.genese
|
|
# .internal) does not match target hostname 'forge.genese.internal' ». Du TLS correct, sur
|
|
# un nom que personne ne peut appeler : la meme panne que le depot a deja rencontree cote
|
|
# resolution, ici cote certificat.
|
|
#
|
|
# ON N'AJOUTE QUE CE QUE CET HOTE SERT REELLEMENT : `edge` nomme le groupe qui rend le
|
|
# service, et l'hote doit en faire partie. Un certificat qui revendiquerait le nom d'un
|
|
# service rendu ailleurs serait une usurpation, pas une commodite.
|
|
client_pki_expositions: "{{ hosts_statiques_expositions | default([]) }}"
|
|
client_pki_sans: >-
|
|
{{ ([client_pki_nom_cert,
|
|
ansible_hostname | default(''),
|
|
ansible_host | default('')]
|
|
+ (client_pki_expositions
|
|
| selectattr('edge', 'defined')
|
|
| selectattr('fqdn', 'defined')
|
|
| selectattr('edge', 'in', group_names)
|
|
| map(attribute='fqdn') | list))
|
|
| select | unique | list }}
|
|
|
|
# L'empreinte du root CA n'est PAS un intrant : elle est DERIVEE a chaud depuis l'AC
|
|
# (tasks/main.yml), parce qu'un from-zero regenere l'autorite avec une empreinte neuve.
|
|
# La stocker en voute donnerait une valeur perimee des la premiere reconstruction.
|
|
# Pour epingler explicitement une empreinte : `client_pki_ca_fingerprint_override`.
|
|
client_pki_ca_fingerprint: ""
|
|
|
|
# Secret OBLIGATOIRE (Ansible Vault).
|
|
client_pki_provisioner_password: "{{ vault_step_ca_provisioner_password | default('') }}" # rempli depuis la voute (vault_step_ca_provisioner_password)
|
|
|
|
# Depot apt officiel Smallstep (partage avec serveur_step_ca).
|
|
client_pki_depot_cle_url: "https://packages.smallstep.com/keys/apt/repo-signing-key.gpg"
|
|
client_pki_depot_cle_fichier: "/etc/apt/keyrings/smallstep.asc"
|
|
client_pki_depot_uri: "https://packages.smallstep.com/stable/debian"
|
|
client_pki_depot_suite: "debs"
|
|
|
|
# QUI PEUT LIRE LA CLE PRIVEE (2026-08-25).
|
|
#
|
|
# Elle est en `0600 root:root`, et c'est le bon defaut. Les consommateurs TLS habituels —
|
|
# nginx, Postfix, Dovecot — demarrent en root, lisent la cle, puis deprivilegient : ils
|
|
# n'ont jamais eu besoin d'autre chose.
|
|
#
|
|
# Forgejo tourne en `git` DES LE DEPART. Sur la forge du site, qui sert le genome sans
|
|
# edge devant elle, la cle etait donc illisible par le seul processus qui en a besoin.
|
|
#
|
|
# Un GROUPE lecteur et `0640` reglent ca sans ouvrir la cle a tout le monde. On declare le
|
|
# groupe explicitement, service par service : elargir par defaut serait exactement le
|
|
# genre de commodite qui finit par rendre une cle privee lisible par `nogroup`.
|
|
client_pki_cle_groupe: "root"
|
|
client_pki_cle_mode: "0600"
|
|
|
|
# LE CERTIFICAT, LUI, EST PUBLIC — et l'etait deja avant d'etre sur ce disque : il est
|
|
# presente a chaque poignee de main, a quiconque se connecte. Le garder en `0600` ne
|
|
# protegeait rien et empechait tout service non-root de le lire. Le certificat racine est
|
|
# d'ailleurs deja en `0644` juste a cote, pour la meme raison.
|
|
client_pki_cert_mode: "0644"
|
|
|
|
# Services a recharger apres un renouvellement de cert : les VRAIS consommateurs
|
|
# (nginx sur l'edge, postfix/dovecot sur le mail, slapd sur l'annuaire). Sans ca,
|
|
# le cert est renouvele sur disque mais le service sert l'ancien jusqu'a un reload.
|
|
client_pki_reload_services: []
|
|
|
|
# Marge avant echeance (secondes) sous laquelle le role RE-EMET le certificat plutot que
|
|
# d'attendre le renouvellement automatique. 3600 = une heure : large devant le minuteur
|
|
# (~14 min), serre devant la duree de vie (24 h).
|
|
client_pki_marge_renouvellement: 3600
|
|
|
|
# --- Sonde de supervision (docs/supervision-conception.md) --------------------------
|
|
#
|
|
# LES SEUILS DECOULENT DU MOMENT OU LE RENOUVELLEMENT EST DU — pas d'une intuition.
|
|
#
|
|
# `cert-renewer@.service` porte `ExecCondition=step certificate needs-renewal`, qui dit
|
|
# oui au TIERS RESTANT : 8 h pour un certificat de 24 h. Le minuteur repasse toutes les
|
|
# 15 minutes (`OnCalendar=*:1/15`). En marche normale, un certificat ne descend donc
|
|
# jamais durablement sous 8 h.
|
|
#
|
|
# PREMIERE VERSION FAUSSE, ET INSTRUCTIVE (2026-09-09) : avert a 12 h, crit a 4 h. Deux
|
|
# machines du site sur sept sont passees en AVERTISSEMENT — sur des certificats
|
|
# parfaitement sains, simplement pas encore eligibles au renouvellement. La sonde criait
|
|
# AVANT que le mecanisme ne soit cense agir.
|
|
#
|
|
# Les seuils sont donc SOUS le point de renouvellement :
|
|
# 6 h -> le renouvellement est du depuis 2 h et n'a pas abouti : huit fenetres de
|
|
# quinze minutes manquees. Ce n'est plus un hoquet.
|
|
# 3 h -> plus aucune marge ; la panne est certaine au prochain cycle.
|
|
#
|
|
# Un seuil au-dessus du point de renouvellement ne previent pas : il ment.
|
|
client_pki_sonde_avert_h: 6
|
|
client_pki_sonde_crit_h: 3
|
|
|
|
# Port local ou comparer le certificat SERVI a celui du disque. Vide = pas de comparaison
|
|
# (l'hote n'expose rien en TLS, ou son consommateur n'ecoute pas en local).
|
|
client_pki_sonde_port: ""
|