Compare commits
4 commits
fbd4094d3f
...
acc3f83574
| Author | SHA1 | Date | |
|---|---|---|---|
| acc3f83574 | |||
| a6907fed19 | |||
| 1cb85094ea | |||
| bff1396af3 |
7 changed files with 96 additions and 5 deletions
15
Makefile
15
Makefile
|
|
@ -1456,6 +1456,18 @@ verifier-deploiement: ansible-runtime ## Verifie l'etat de la flotte apres deplo
|
||||||
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)" --check --diff; \
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)" --check --diff; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# LE CLONAGE SE SERIALISE PAR STOCKAGE, et c'est la granularite de Proxmox
|
||||||
|
# lui-meme : il tient un verrou PAR STOCKAGE pendant l'allocation du disque. Deux
|
||||||
|
# clones simultanes sur le meme stockage se le disputent, et celui qui attend trop
|
||||||
|
# rend « clone failed: can't lock file '/var/lock/pve-manager/pve-storage-<nom>' -
|
||||||
|
# got timeout » — un message qui nomme le verrou, jamais le parallelisme qui l'a
|
||||||
|
# provoque. A deux VM rien ne parait ; a cinq, une creation sur cinq echoue, et la
|
||||||
|
# reconstruction s'arrete sans que rien ne dise pourquoi elle tenait a deux.
|
||||||
|
#
|
||||||
|
# LA CLE PORTE LE NOEUD ET LE STOCKAGE, donc deux stockages distincts clonent
|
||||||
|
# TOUJOURS en parallele : `flotte-creer PARALLELE=n` garde tout son sens, et seule
|
||||||
|
# la section qui contend est prise un a la fois, dans l'ordre ou la sequence du
|
||||||
|
# plan la demande.
|
||||||
cloner-vm: ansible-runtime ## Clone une VM depuis le gabarit dore — HOTE=<nom> VMID=<id>
|
cloner-vm: ansible-runtime ## Clone une VM depuis le gabarit dore — HOTE=<nom> VMID=<id>
|
||||||
@if [[ -z "$(HOTE)" || -z "$(VMID)" ]]; then \
|
@if [[ -z "$(HOTE)" || -z "$(VMID)" ]]; then \
|
||||||
printf '%s\n' 'Refus: relancer avec HOTE=nom VMID=id_clone.'; \
|
printf '%s\n' 'Refus: relancer avec HOTE=nom VMID=id_clone.'; \
|
||||||
|
|
@ -1530,7 +1542,8 @@ cloner-vm: ansible-runtime ## Clone une VM depuis le gabarit dore — HOTE=<nom>
|
||||||
;; \
|
;; \
|
||||||
esac; \
|
esac; \
|
||||||
fi; \
|
fi; \
|
||||||
ansible-playbook -i localhost, $(PLAYBOOK_PROXMOX_CLONER_VM) "$${vault_args[@]}" "$${extra_vars[@]}"
|
verrou="$${TMPDIR:-/tmp}/setops-clone-$$(printf '%s_%s' '$(NOEUD_PROXMOX)' '$(STOCKAGE_PROXMOX)' | tr -c 'A-Za-z0-9._-' '_').lock"; \
|
||||||
|
flock "$$verrou" ansible-playbook -i localhost, $(PLAYBOOK_PROXMOX_CLONER_VM) "$${vault_args[@]}" "$${extra_vars[@]}"
|
||||||
|
|
||||||
creer-vm: _instance-requise ## Cree une VM et attend qu'elle soit joignable — HOTE=<nom>
|
creer-vm: _instance-requise ## Cree une VM et attend qu'elle soit joignable — HOTE=<nom>
|
||||||
@set -e; \
|
@set -e; \
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,21 @@ client_pki_provisioner_password: "{{ vault_step_ca_provisioner_password | defaul
|
||||||
# Depot apt officiel Smallstep (partage avec serveur_step_ca).
|
# Depot apt officiel Smallstep (partage avec serveur_step_ca).
|
||||||
client_pki_depot_cle_url: "{{ client_pki_depot_schema }}://packages.smallstep.com/keys/apt/repo-signing-key.gpg"
|
client_pki_depot_cle_url: "{{ client_pki_depot_schema }}://packages.smallstep.com/keys/apt/repo-signing-key.gpg"
|
||||||
client_pki_depot_cle_fichier: "/etc/apt/keyrings/smallstep.asc"
|
client_pki_depot_cle_fichier: "/etc/apt/keyrings/smallstep.asc"
|
||||||
|
|
||||||
|
# LA MEME CLE QUE `serveur_step_ca` POSE, et c'est le DEPOT qui la porte : les deux
|
||||||
|
# fichiers ont le meme SHA-256. La chercher ici d'abord evite de sortir sur un CDN tiers
|
||||||
|
# pour des octets qu'on possede.
|
||||||
|
#
|
||||||
|
# CE N'EST PAS THEORIQUE POUR UNE FLOTTE D'AMORCAGE. Elle n'a par definition ni serveur
|
||||||
|
# d'artefacts ni cache, donc `client_pki_depot_schema` reste `https` et chaque hote sort
|
||||||
|
# EN DIRECT ; le telechargement echouait alors trois amorcages sur quatre, avec cinq
|
||||||
|
# reprises, sur une bordure amont que rien ici ne controle.
|
||||||
|
#
|
||||||
|
# Le chemin traverse `serveur_step_ca` plutot que de dupliquer le fichier : deux copies
|
||||||
|
# d'une meme cle divergent le jour ou l'amont la remplace, et rien ne dirait laquelle
|
||||||
|
# fait foi.
|
||||||
|
client_pki_depot_cle_locale: >-
|
||||||
|
{{ role_path }}/../serveur_step_ca/files/smallstep-repo-signing-key.asc
|
||||||
client_pki_depot_uri: "{{ client_pki_depot_schema }}://packages.smallstep.com/stable/debian"
|
client_pki_depot_uri: "{{ client_pki_depot_schema }}://packages.smallstep.com/stable/debian"
|
||||||
client_pki_depot_suite: "debs"
|
client_pki_depot_suite: "debs"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,36 @@
|
||||||
- telecharger_la_cle_de_signature_smallste_present.stat.exists
|
- telecharger_la_cle_de_signature_smallste_present.stat.exists
|
||||||
- (telecharger_la_cle_de_signature_smallste_present.stat.size | default(0)) == 0
|
- (telecharger_la_cle_de_signature_smallste_present.stat.size | default(0)) == 0
|
||||||
|
|
||||||
|
- name: Voir si le depot porte la cle de signature Smallstep
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ client_pki_depot_cle_locale }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
become: false
|
||||||
|
changed_when: false
|
||||||
|
check_mode: false
|
||||||
|
register: client_pki_cle_depot_locale
|
||||||
|
|
||||||
|
# LE DEPOT D'ABORD, LE RESEAU ENSUITE. `serveur_step_ca` pose deja cette cle depuis ses
|
||||||
|
# `files/` sans rien demander a personne ; le client la telechargeait. Les deux fichiers
|
||||||
|
# sont identiques — meme SHA-256 — si bien que la sortie n'achetait rien et exposait
|
||||||
|
# chaque amorcage a une bordure tierce.
|
||||||
|
- name: Poser la cle de signature Smallstep depuis le depot
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ client_pki_depot_cle_locale }}"
|
||||||
|
dest: "{{ client_pki_depot_cle_fichier }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when:
|
||||||
|
- not (telecharger_la_cle_de_signature_smallste_present.stat.exists and telecharger_la_cle_de_signature_smallste_present.stat.size | default(0) > 0)
|
||||||
|
- client_pki_cle_depot_locale.stat.exists
|
||||||
|
|
||||||
|
# LE REPLI RESTE, et il n'est pas mort : un depot qui ne porterait plus ce fichier doit
|
||||||
|
# encore pouvoir amorcer. Ce qui change est l'ORDRE — on ne sort que faute de mieux.
|
||||||
- name: Telecharger la cle de signature Smallstep
|
- name: Telecharger la cle de signature Smallstep
|
||||||
when: not (telecharger_la_cle_de_signature_smallste_present.stat.exists and telecharger_la_cle_de_signature_smallste_present.stat.size | default(0) > 0)
|
when:
|
||||||
|
- not (telecharger_la_cle_de_signature_smallste_present.stat.exists and telecharger_la_cle_de_signature_smallste_present.stat.size | default(0) > 0)
|
||||||
|
- not client_pki_cle_depot_locale.stat.exists
|
||||||
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
|
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
|
||||||
#
|
#
|
||||||
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
|
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ d'annuaire.
|
||||||
## Variables
|
## Variables
|
||||||
| Variable | Défaut | Rôle |
|
| Variable | Défaut | Rôle |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `resoudre_annuaire_hote` | `id-ldap-01` | **Le** point où le nom d'hôte est fixé (surchargeable par instance) |
|
| `resoudre_annuaire_hote` | `""` | Dérivé du PLAN (`applications.<app>.hote`) ; surchargeable par instance. Vide et non dérivé, le rôle REFUSE plutôt que de deviner |
|
||||||
| `resoudre_annuaire_port` | `636` | Port LDAPS |
|
| `resoudre_annuaire_port` | `636` | Port LDAPS |
|
||||||
| `resoudre_annuaire_encryption` | `ldaps` | Schéma de l'URI |
|
| `resoudre_annuaire_encryption` | `ldaps` | Schéma de l'URI |
|
||||||
| `resoudre_annuaire_bind_cn` | `admin` | CN du compte de bind |
|
| `resoudre_annuaire_bind_cn` | `admin` | CN du compte de bind |
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,19 @@
|
||||||
# aucune machine du plan (l'annuaire vit sur `idm-01`), et Postfix ne pouvait pas se lier.
|
# aucune machine du plan (l'annuaire vit sur `idm-01`), et Postfix ne pouvait pas se lier.
|
||||||
# Constaté le 2026-08-07. Le repli ci-dessous ne sert que si le plan est muet.
|
# Constaté le 2026-08-07. Le repli ci-dessous ne sert que si le plan est muet.
|
||||||
resoudre_annuaire_app: "openldap"
|
resoudre_annuaire_app: "openldap"
|
||||||
resoudre_annuaire_hote: "id-ldap-01"
|
# VIDE, ET C'EST LE DEFAUT LUI-MEME QUI ETAIT LE PIEGE. Un nom ecrit ici tient des
|
||||||
|
# que le plan se tait : la derivation est sautee, le role bâtit
|
||||||
|
# `ldaps://<ce nom>.<domaine>`, et le consommateur echoue a l'execution sur
|
||||||
|
# « Unable to bind » en nommant une machine qui n'est dans aucun plan.
|
||||||
|
#
|
||||||
|
# La correction qui a introduit la derivation depuis le plan a laisse ce defaut en
|
||||||
|
# place ; il ramene donc l'ancien comportement chaque fois que l'application n'est
|
||||||
|
# pas au plan. Vide, l'assertion qui suit refuse AU DEPLOIEMENT, la ou le nom
|
||||||
|
# devine ne se voyait qu'au premier bind.
|
||||||
|
#
|
||||||
|
# Une instance garde le droit de le poser explicitement : ce qui disparait est la
|
||||||
|
# valeur DEVINEE, pas la surcharge.
|
||||||
|
resoudre_annuaire_hote: ""
|
||||||
resoudre_annuaire_port: 636
|
resoudre_annuaire_port: 636
|
||||||
resoudre_annuaire_encryption: "ldaps"
|
resoudre_annuaire_encryption: "ldaps"
|
||||||
# COMPTE DE LIAISON — CHAQUE CONSOMMATEUR A LE SIEN (2026-09-13).
|
# COMPTE DE LIAISON — CHAQUE CONSOMMATEUR A LE SIEN (2026-09-13).
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,17 @@
|
||||||
resoudre_annuaire_hote: "{{ (applications | default({})).get(resoudre_annuaire_app, {}).get('hote') }}"
|
resoudre_annuaire_hote: "{{ (applications | default({})).get(resoudre_annuaire_app, {}).get('hote') }}"
|
||||||
when: ((applications | default({})).get(resoudre_annuaire_app, {}).get('hote') | default('', true)) | length > 0
|
when: ((applications | default({})).get(resoudre_annuaire_app, {}).get('hote') | default('', true)) | length > 0
|
||||||
|
|
||||||
|
- name: Exiger que le plan nomme l'hôte de l'annuaire
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- resoudre_annuaire_hote | length > 0
|
||||||
|
fail_msg: >-
|
||||||
|
L'application « {{ resoudre_annuaire_app }} » n'est pas au plan, et aucune
|
||||||
|
instance ne pose `resoudre_annuaire_hote` : rien ne nomme l'annuaire. Le rôle
|
||||||
|
qui le demande ne peut pas s'y lier. Un nom deviné ici produirait
|
||||||
|
« Unable to bind » sur une machine qui n'est dans aucun plan — et le
|
||||||
|
déploiement passerait au VERT jusque-là.
|
||||||
|
|
||||||
# set_fact (pas les defaults) pour que ces valeurs persistent dans le rôle consommateur
|
# set_fact (pas les defaults) pour que ces valeurs persistent dans le rôle consommateur
|
||||||
# après include_role (les defaults du rôle inclus ne fuient pas hors de son exécution).
|
# après include_role (les defaults du rôle inclus ne fuient pas hors de son exécution).
|
||||||
- name: Dériver la connexion à l'annuaire
|
- name: Dériver la connexion à l'annuaire
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,19 @@
|
||||||
#
|
#
|
||||||
# Quatre services interrogeaient l'annuaire avec son compte d'administration. Voir
|
# Quatre services interrogeaient l'annuaire avec son compte d'administration. Voir
|
||||||
# `defaults/main.yml` pour le raisonnement ; ici on cree les comptes et on pose les droits.
|
# `defaults/main.yml` pour le raisonnement ; ici on cree les comptes et on pose les droits.
|
||||||
|
# UNE GARDE CENSUREE NE GARDE PAS. `no_log` protege legitimement une VALEUR ; ici
|
||||||
|
# la tache n'en imprime aucune — son `fail_msg` ne porte que des NOMS, celui du
|
||||||
|
# compte et celui de la cle attendue. La censure ne cachait donc rien a personne,
|
||||||
|
# et supprimait le seul message capable de dire LEQUEL des quatre comptes manque.
|
||||||
|
#
|
||||||
|
# Ce que le deploiement rendait a sa place, une fois par compte absent :
|
||||||
|
#
|
||||||
|
# failed: [<hote>] (item=None) => {"censored": "the output has been hidden
|
||||||
|
# due to the fact that 'no_log: true' was specified for this result"}
|
||||||
|
#
|
||||||
|
# « item=None » parce que l'etiquette de boucle est censuree elle aussi : quatre
|
||||||
|
# comptes, trois echecs, et rien pour les distinguer. Le meme raisonnement a deja
|
||||||
|
# ete tenu pour le compte de liaison de `resoudre_annuaire`.
|
||||||
- name: Exiger un secret pour chaque compte de service
|
- name: Exiger un secret pour chaque compte de service
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
|
|
@ -160,7 +173,6 @@
|
||||||
loop: "{{ serveur_openldap_comptes_service }}"
|
loop: "{{ serveur_openldap_comptes_service }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.cn }}"
|
label: "{{ item.cn }}"
|
||||||
no_log: true
|
|
||||||
|
|
||||||
- name: Creer les comptes de service de l'annuaire
|
- name: Creer les comptes de service de l'annuaire
|
||||||
community.general.ldap_entry:
|
community.general.ldap_entry:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue