Deuxieme application du patron devis/applicateur aux services. Trouve a la premiere execution : sur infra-pki-01 — l'autorite elle-meme — le certificat etait expire depuis plus de 8 h et le renouvellement echouait toutes les 14 minutes sur « 'step ca renew' requires the '--ca-url' flag ». Rien ne le signalait. Cause : sur l'hote de l'AC, /etc/step est le STEPPATH du SERVEUR, pas un amorcage client — pas de defaults.json, et l'unite de renouvellement en dependait. La lecon etait deja ecrite dans le commentaire de la tache d'emission (« l'autorite ne bootstrape pas »), jamais reportee sur l'unite. Le role ne pouvait pas non plus se soigner : la re-emission ne regardait que la FORME (cert absent ou SAN manquant), jamais la validite. client_pki verifie desormais l'echeance (client_pki_marge_renouvellement). Ce qu'il a fallu desapprendre : les certificats vivent 24 h et se renouvellent toutes les ~14 min ; « empreinte servie != empreinte disque » est l'etat NORMAL. Comparer les empreintes aurait donne un verificateur qui crie en permanence. Le signal est l'echeance de ce qui est SERVI, plus l'absence de client_pki_reload_services. Le devis a d'abord menti, du defaut meme qu'il traque : include_vars au niveau du play prime sur les group_vars. Et le premier correctif a PARU marcher — set_fact accepte un dictionnaire entier en argument libre sans erreur et n'en fait rien. Il faut reimposer cle par cle. Les deux devis sont corriges et le piege est consigne dans docs/devis-services.md avant d'ecrire le prochain. Verifie dans les deux sens : CONFORME sur 14 hotes ; sur un releve ou l'on rejoue une copie perimee en memoire, 2 ecarts et code de sortie 1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
7 KiB
YAML
182 lines
7 KiB
YAML
---
|
|
# L'empreinte du root CA est la SOURCE DE VÉRITÉ de l'autorité elle-même : on la
|
|
# dérive à chaud (robuste au from-zero — une AC régénérée a une empreinte neuve).
|
|
# `client_pki_ca_fingerprint_override` permet d'épingler explicitement si besoin.
|
|
- name: Dériver l'empreinte du root CA depuis l'autorité
|
|
ansible.builtin.command:
|
|
cmd: "step certificate fingerprint {{ serveur_step_ca_steppath | default('/etc/step-ca') }}/certs/root_ca.crt"
|
|
delegate_to: "{{ groups['serveur_step_ca'][0] }}"
|
|
changed_when: false
|
|
check_mode: false
|
|
register: client_pki_fingerprint_ac
|
|
|
|
- name: Retenir l'empreinte (dérivée de l'AC, sauf override explicite)
|
|
ansible.builtin.set_fact:
|
|
client_pki_ca_fingerprint: "{{ client_pki_ca_fingerprint_override | default(client_pki_fingerprint_ac.stdout | trim, true) }}"
|
|
|
|
- name: Exiger l'empreinte AC et le mot de passe provisioner (Vault)
|
|
ansible.builtin.assert:
|
|
that:
|
|
- client_pki_ca_fingerprint | length > 0
|
|
- client_pki_provisioner_password | length > 0
|
|
fail_msg: >-
|
|
Empreinte du root CA indisponible (step-ca joignable et déployé ?) ;
|
|
client_pki_provisioner_password requis (via Ansible Vault).
|
|
|
|
- name: Assurer le repertoire des trousseaux apt
|
|
ansible.builtin.file:
|
|
path: /etc/apt/keyrings
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Telecharger la cle de signature Smallstep
|
|
ansible.builtin.get_url:
|
|
url: "{{ client_pki_depot_cle_url }}"
|
|
dest: "{{ client_pki_depot_cle_fichier }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
|
|
- name: Ajouter le depot apt Smallstep
|
|
ansible.builtin.template:
|
|
src: smallstep.sources.j2
|
|
dest: /etc/apt/sources.list.d/smallstep.sources
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
|
|
- name: Installer step-cli
|
|
ansible.builtin.apt:
|
|
name: "{{ client_pki_paquets }}"
|
|
state: present
|
|
update_cache: true
|
|
|
|
- name: Creer le repertoire STEPPATH des certificats
|
|
ansible.builtin.file:
|
|
path: "{{ client_pki_steppath }}/certs"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deployer le mot de passe du provisioner
|
|
ansible.builtin.copy:
|
|
content: "{{ client_pki_provisioner_password }}"
|
|
dest: "{{ client_pki_steppath }}/provisioner.pass"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|
|
|
|
# L'AUTORITE EST UN CAS A PART, mais pas une exception. Elle n'a pas a « s'enroler »
|
|
# aupres d'elle-meme : sa racine est deja sur son disque, et un bootstrap la ferait
|
|
# aller la chercher par le reseau, chez elle, en verifiant une empreinte qu'elle vient
|
|
# de produire. Elle a en revanche besoin de CERTIFICATS comme tout le monde — sans quoi
|
|
# ses propres services (node_exporter) restent en clair, et `client_metrique` echoue.
|
|
#
|
|
# La distinction est donc : pas d'enrolement, mais emission locale. C'est ce qui permet
|
|
# de retirer l'exemption de `client_pki` sans contredire « l'AC est la source de la
|
|
# confiance » — elle l'est, et c'est precisement pourquoi elle peut se signer elle-meme.
|
|
- name: Reconnaitre l'hote qui PORTE l'autorite
|
|
ansible.builtin.set_fact:
|
|
client_pki_est_autorite: "{{ inventory_hostname in (groups['serveur_step_ca'] | default([])) }}"
|
|
|
|
- name: Etablir la confiance dans l'AC interne (bootstrap + installation racine)
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
step ca bootstrap
|
|
--ca-url {{ client_pki_ca_url }}
|
|
--fingerprint {{ client_pki_ca_fingerprint }}
|
|
--install --force
|
|
creates: "{{ client_pki_steppath }}/certs/root_ca.crt"
|
|
environment:
|
|
STEPPATH: "{{ client_pki_steppath }}"
|
|
when: not client_pki_est_autorite | bool
|
|
|
|
- name: Poser la racine depuis le disque local (l'autorite ne s'enrole pas aupres d'elle-meme)
|
|
ansible.builtin.copy:
|
|
src: "{{ serveur_step_ca_steppath | default('/etc/step-ca') }}/certs/root_ca.crt"
|
|
dest: "{{ client_pki_steppath }}/certs/root_ca.crt"
|
|
remote_src: true
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
when: client_pki_est_autorite | bool
|
|
|
|
- name: Rendre le certificat racine lisible par tous (cert public, requis par les clients TLS)
|
|
ansible.builtin.file:
|
|
path: "{{ client_pki_steppath }}/certs/root_ca.crt"
|
|
mode: "0644"
|
|
|
|
# La validite, pas seulement la forme. Sans ce controle, un certificat expire mais
|
|
# portant les bons SAN ne declenchait AUCUNE re-emission : le role ne savait pas se
|
|
# soigner, et sur l'hote de l'autorite — ou le renouvellement automatique etait casse —
|
|
# rien ne pouvait plus le rattraper. Constate le 2026-08-08.
|
|
- name: Verifier que le certificat d hote est encore valide
|
|
ansible.builtin.command:
|
|
cmd: "openssl x509 -in {{ client_pki_cert }} -noout -checkend {{ client_pki_marge_renouvellement }}"
|
|
register: client_pki_validite
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: Lire les SAN du certificat d'hote existant (detection de derive)
|
|
ansible.builtin.command:
|
|
cmd: "openssl x509 -in {{ client_pki_cert }} -noout -ext subjectAltName"
|
|
register: client_pki_san_actuels
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
# Re-emet si le cert est absent OU si un SAN voulu manque (ex: nouvelle exposition
|
|
# ajoutee au plan -> client_pki_sans mis a jour). Plus de garde 'creates' aveugle.
|
|
- name: Obtenir / re-emettre le certificat d'hote (absent, perime ou SAN derives)
|
|
ansible.builtin.command:
|
|
# `--ca-url` et `--root` EXPLICITES : sur un hote ordinaire ils sont redondants avec
|
|
# le `defaults.json` qu'ecrit `step ca bootstrap`, mais l'autorite ne bootstrape pas
|
|
# — elle n'aurait donc aucune de ces deux valeurs. Les nommer ici vaut mieux que de
|
|
# dependre d'un fichier ecrit par une etape qu'on saute volontairement.
|
|
cmd: >-
|
|
step ca certificate {{ client_pki_nom_cert }}
|
|
{{ client_pki_cert }} {{ client_pki_cle }}
|
|
--ca-url {{ client_pki_ca_url }}
|
|
--root {{ client_pki_steppath }}/certs/root_ca.crt
|
|
--provisioner {{ client_pki_provisioner }}
|
|
--provisioner-password-file {{ client_pki_steppath }}/provisioner.pass
|
|
{% for s in client_pki_sans | select | unique %}--san {{ s }} {% endfor %}
|
|
--force
|
|
environment:
|
|
STEPPATH: "{{ client_pki_steppath }}"
|
|
when: >-
|
|
client_pki_san_actuels.rc != 0
|
|
or client_pki_validite.rc != 0
|
|
or (client_pki_sans | select | unique | reject('equalto', '')
|
|
| reject('in', client_pki_san_actuels.stdout | default(''))
|
|
| list | length > 0)
|
|
changed_when: true
|
|
notify: Recharger les consommateurs du cert
|
|
|
|
- name: Deployer l'unite systemd de renouvellement
|
|
ansible.builtin.template:
|
|
src: cert-renewer@.service.j2
|
|
dest: /etc/systemd/system/cert-renewer@.service
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Recharger systemd
|
|
|
|
- name: Deployer le minuteur de renouvellement
|
|
ansible.builtin.template:
|
|
src: cert-renewer@.timer.j2
|
|
dest: /etc/systemd/system/cert-renewer@.timer
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Recharger systemd
|
|
|
|
- name: Activer le renouvellement automatique du certificat d'hote
|
|
ansible.builtin.systemd:
|
|
name: "cert-renewer@{{ client_pki_nom_cert }}.timer"
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|