Set-OPS-Public/roles/client_pki/tasks/main.yml
Daniel Allaire 15cdb7d454
Some checks are pending
verifier / verifier (push) Waiting to run
patient 0 debout — et quatre defauts que seul un ecosysteme DIFFERENT pouvait montrer
Quatre machines, zero echec, la forge repond (service actif, ecoute 3000, HTTP 200).
forge-01 121 taches, infra-pki-01 109, infra-edge-01 92, infra-dns-01 84.

1. UN TIERS INTERMITTENT ARRETAIT TOUT. `packages.smallstep.com` repond une fois sur deux ;
   `get_url` abandonne a 10 s sans reprise. Mesure AVANT d'accuser le reseau : DNS
   resolvait, la poignee TLS aboutissait, 138 Ko depuis deb.debian.org passaient en 0,09 s,
   et le chemin acceptait 1450 octets en refusant 1500 — l'attendu exact en overlay. Le
   reseau n'y etait pour rien. Reprises posees sur les trois telechargements du chemin
   critique (serveur_step_ca, client_pki, serveur_forgejo). Une dizaine d'autres restent
   sans reprise : liste dans le CHANGELOG.

2. UN `register` A ECRASE UN CHEMIN DE FICHIER. En posant la reprise, j'ai enregistre dans
   `client_pki_cle`, nom que le role utilisait deja pour la cle privee. L'ecart s'est vu 70
   taches plus loin : `step ca certificate` recevait un dict serialise a la place du
   fichier et refusait « too many positional arguments ». Un register ecrit dans l'espace
   de noms de TOUT le role.

3. LE SSO SE DECLARAIT AU LIEU DE SE DERIVER. `serveur_forgejo_oidc_actif: true` en dur
   faisait cabler une source OAuth2 vers un Keycloak inexistant. Derive desormais de
   l'inventaire. Quatrieme manifestation en deux jours de la meme hypothese — le moteur
   supposait l'ecosysteme COMPLET — apres les intrants (P32), les bases (P35) et les
   dependances causales.

AUCUN de ces defauts n'etait visible sur Chezlepro, qui porte tout et tournait deja. Ils ne
pouvaient apparaitre qu'au premier ecosysteme DIFFERENT.

make verifier 41 OK, 0 echec, 0 saute ; make test inchange.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 02:53:44 -04:00

205 lines
8.3 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"
# Recuperee UNE FOIS. Sans garde, chaque deploiement recontactait le serveur du
# fournisseur : cinq cles x quatorze hotes = soixante-dix allers-retours externes pour
# des cles deja installees, et autant d'occasions qu'un tiers lent fasse tomber le
# deploiement. Arbitrage rendu le 2026-08-09 : une plateforme souveraine ne depend pas
# de six serveurs etrangers pour redeployer ce qu'elle possede deja.
#
# CONSEQUENCE ASSUMEE : une rotation de cle amont n'est plus recuperee toute seule. Elle
# ne passe pas inapercue pour autant — `apt` refuse alors le depot, bruyamment. Pour
# forcer le rafraichissement : supprimer le fichier et rejouer le role.
- name: Cette ressource est-elle deja recuperee ? (Telecharger la cle de signature Sm)
ansible.builtin.stat:
path: "{{ client_pki_depot_cle_fichier }}"
register: telecharger_la_cle_de_signature_smallste_present
# Un serveur tiers intermittent ne doit pas arreter un deploiement de quarante
# minutes (mesure du 2026-08-23 : la meme URL pend, puis rend 200 en 0,48 s au
# second essai). Defaut de `get_url` : 10 s et aucune reprise.
- name: Telecharger la cle de signature Smallstep
when: not telecharger_la_cle_de_signature_smallste_present.stat.exists
ansible.builtin.get_url:
url: "{{ client_pki_depot_cle_url }}"
dest: "{{ client_pki_depot_cle_fichier }}"
owner: root
group: root
mode: "0644"
timeout: 30
register: client_pki_cle_depot_telechargee
retries: 5
delay: 6
until: client_pki_cle_depot_telechargee is succeeded
- 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