Les 33 roles serveur_* declarent maintenant une sonde. Les huit qui manquaient
sont ceux dont la verite ne ressemble pas a « ce service repond-il ».
Quatre marqueurs du site : ce que tasks/main.yml verifie UNE FOIS au deploiement
cesse d etre vrai sans que rien ne tombe. La racine du cache se retrouve chainee,
la forge du genome repond en n ayant plus rien dedans, un locataire n est plus
admis a resoudre, l isolation d un depot glisse.
serveur_ops_site ne sert rien : il detient un pouvoir. La sonde verifie que la
carte est la, que la voute du site est chiffree et que sa cle est en 0600 — sans
lire le contenu d aucun des trois.
serveur_icingaweb2 surveille la vitrine de la supervision elle-meme : si la
console meurt, tout reste vert et l exploitant est aveugle.
DEFAUT 1 — quatre gabarits qu Ansible aurait refuse de rendre. Jinja lit le
{# de ${#tableau[@]} comme un debut de commentaire. Le depot connaissait le
remede et l appliquait la ou quelqu un s etait fait prendre, nulle part ailleurs.
P76 rend desormais chaque gabarit de role, avec les delimiteurs qu Ansible en
tirerait — pas une recherche de motif.
DEFAUT 2 — la doctrine promettait 54 greffons et citait check_pgsql. La flotte a
monitoring-plugins-basic : 53, sans check_pgsql ni check_dns ni check_ldap. Le
paquet qui les porte traine samba et snmp sur chaque machine. Un greffon absent
sort en 127, qui n est pas un code Nagios.
21 controles negatifs sur les machines reelles du site. ansible-lint production
0/91, harnais 75 OK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
191 lines
8.6 KiB
YAML
191 lines
8.6 KiB
YAML
---
|
|
# CE RÔLE MARQUE UN DÉPÔT ; IL N'EN INSTALLE PAS.
|
|
#
|
|
# Il présuppose `serveur_backup` sur le même hôte et lit ses variables. Le message qu'on
|
|
# obtiendrait sans lui — « variable non définie » — envoie chercher une faute de frappe
|
|
# alors que la cause est une déclaration incomplète, un cran au-dessus.
|
|
- name: Le rôle qui installe le dépôt est-il bien là ?
|
|
ansible.builtin.assert:
|
|
that:
|
|
- serveur_backup_site_racine | length > 0
|
|
- serveur_backup_site_utilisateur | length > 0
|
|
fail_msg: >-
|
|
`serveur_backup_site` marque un dépôt existant, il n'en installe aucun.
|
|
Cet hôte doit AUSSI porter `serveur_backup`. Ajouter les deux rôles à sa
|
|
déclaration, dans cet ordre.
|
|
|
|
# ÉCRIRE, PUIS RELIRE (D-68). La seule prise de ce rôle sur le réel est de vérifier que le
|
|
# dépôt qu'il désigne peut RÉELLEMENT recevoir. Sans ça, il attribuerait la sauvegarde de
|
|
# tous les locataires à un répertoire qui n'existe pas, ou à un disque plein — et la panne
|
|
# arriverait au moment de l'écriture, chez le locataire, en ressemblant à autre chose.
|
|
- name: La racine des dépôts existe-t-elle vraiment ?
|
|
ansible.builtin.stat:
|
|
path: "{{ serveur_backup_site_racine }}"
|
|
register: serveur_backup_site_racine_etat
|
|
|
|
- name: Combien de place reste-t-il devant les locataires ?
|
|
ansible.builtin.command:
|
|
argv: ["df", "-BG", "--output=avail", "{{ serveur_backup_site_racine }}"]
|
|
register: serveur_backup_site_place
|
|
changed_when: false
|
|
failed_when: false
|
|
when: serveur_backup_site_racine_etat.stat.exists
|
|
|
|
- name: Exiger un dépôt qui peut recevoir
|
|
ansible.builtin.assert:
|
|
that:
|
|
- serveur_backup_site_racine_etat.stat.exists
|
|
- serveur_backup_site_racine_etat.stat.isdir
|
|
- serveur_backup_site_espace_libre_go | int >= serveur_backup_site_espace_minimal_go | int
|
|
fail_msg: >-
|
|
{{ serveur_backup_site_racine }}
|
|
{{ serveur_backup_site_racine_etat.stat.exists
|
|
| ternary('n a plus que ' ~ serveur_backup_site_espace_libre_go ~ ' Go libres (minimum '
|
|
~ serveur_backup_site_espace_minimal_go ~ ')', 'n existe pas') }}.
|
|
Un dépôt qui se remplit n'échoue pas à l'ouverture : il échoue AU MILIEU d'un
|
|
instantané, chez le locataire, et la panne se lit comme une erreur réseau.
|
|
success_msg: >-
|
|
Dépôt du site prêt : {{ serveur_backup_site_racine }},
|
|
{{ serveur_backup_site_espace_libre_go }} Go libres.
|
|
vars:
|
|
# `df --output=avail -BG` rend « 123G » en deuxieme ligne. On decoupe plutot que
|
|
# d'employer une expression a groupe : celles-ci rendent `None` quand rien ne
|
|
# correspond, et le gabarit explose au lieu de se taire — paye trois fois cette
|
|
# semaine.
|
|
serveur_backup_site_espace_libre_go: >-
|
|
{{ (serveur_backup_site_place.stdout_lines | default(['', '0G']) | last
|
|
| default('0G', true)) | trim | replace('G', '') | default('0', true) }}
|
|
when: not ansible_check_mode
|
|
|
|
# --- UN COMPTE PAR LOCATAIRE ---------------------------------------------------
|
|
#
|
|
# POURQUOI PAS UN SEUL COMPTE AVEC PLUSIEURS CLES.
|
|
#
|
|
# `serveur_backup` pose UN compte `restic` et UNE clé : c'est juste pour le dépôt d'un
|
|
# écosystème, où il n'y a qu'un déposant. Ici il y en a plusieurs, et la même clé pour
|
|
# tous donnerait à chaque locataire la lecture — et l'effacement — des instantanés de
|
|
# tous les autres. Une sauvegarde qui expose ce qu'elle protège ne protège rien ; une
|
|
# sauvegarde qu'un voisin peut effacer ne protège rien non plus.
|
|
#
|
|
# La séparation par répertoire seule ne suffit pas : elle tient tant que personne ne
|
|
# tape `cd ..`. Le compte Unix, lui, est une frontière que le noyau fait respecter.
|
|
#
|
|
# Ce que ça ne protège PAS, et qu'il faut savoir : le site voit les octets. Il ne peut
|
|
# pas les lire — restic chiffre CHEZ LE CLIENT, avec un mot de passe qui reste dans la
|
|
# voûte du locataire, que le site ne détient pas. Le site héberge du chiffré ; il ne
|
|
# peut ni le lire, ni le déchiffrer, ni le reconstituer. C'est la même frontière que
|
|
# pour les voûtes : le site sert, il ne sait pas.
|
|
# LA RACINE PARTAGÉE — À ROOT, TRAVERSABLE, NON LISTABLE.
|
|
#
|
|
# 0711 et non 0755 : personne n'a besoin d'ÉNUMÉRER les locataires. Un locataire
|
|
# traverse pour atteindre son propre répertoire, et ne peut pas apprendre qui d'autre
|
|
# dépose ici. La liste des voisins est déjà une information.
|
|
- name: La racine partagée n'appartient à aucun déposant
|
|
ansible.builtin.file:
|
|
path: "{{ serveur_backup_site_parent }}"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0711"
|
|
|
|
# Deux littéraux doivent s'accorder : le parent déclaré ici, et le `serveur_backup_racine`
|
|
# que `meta/main.yml` passe à l'installateur. S'ils divergent, le site déposerait hors de
|
|
# sa propre racine et plus rien ne le dirait.
|
|
- name: Le dépôt du site vit-il bien sous la racine partagée ?
|
|
ansible.builtin.assert:
|
|
that:
|
|
- serveur_backup_site_racine is match(serveur_backup_site_parent ~ "/.+")
|
|
fail_msg: >-
|
|
Le dépôt du site ({{ serveur_backup_site_racine }}) n'est pas sous la racine
|
|
partagée ({{ serveur_backup_site_parent }}). `meta/main.yml` et
|
|
`defaults/main.yml` ne s'accordent plus.
|
|
|
|
- name: Ouvrir un compte pour chaque locataire du dépôt
|
|
ansible.builtin.user:
|
|
name: "{{ item.compte }}"
|
|
home: "{{ serveur_backup_site_parent }}/{{ item.compte }}"
|
|
shell: /bin/bash
|
|
create_home: true
|
|
system: true
|
|
loop: "{{ serveur_backup_site_locataires }}"
|
|
loop_control:
|
|
label: "{{ item.nom }} -> {{ item.compte }}"
|
|
|
|
# 0700 : le home d'un locataire n'est lisible que par lui. Sans ça, le compte Unix
|
|
# séparerait l'écriture mais pas la lecture — la moitié du problème résolue se lirait
|
|
# comme le problème résolu.
|
|
- name: Refermer le dépôt de chaque locataire sur lui-même
|
|
ansible.builtin.file:
|
|
path: "{{ serveur_backup_site_parent }}/{{ item.compte }}"
|
|
state: directory
|
|
owner: "{{ item.compte }}"
|
|
group: "{{ item.compte }}"
|
|
mode: "0700"
|
|
loop: "{{ serveur_backup_site_locataires }}"
|
|
loop_control:
|
|
label: "{{ item.compte }}"
|
|
|
|
# `exclusive: true` : le site est AUTORITÉ sur qui entre chez lui. Une clé ajoutée à la
|
|
# main — par dépannage, par un ancien locataire, par erreur — disparaît au prochain
|
|
# passage. Sans ce drapeau, `authorized_key` n'ajoute jamais que des lignes : une clé
|
|
# retirée du plan resterait valide pour toujours, et rien ne le dirait.
|
|
- name: N'autoriser chez chaque locataire que SA clé
|
|
ansible.posix.authorized_key:
|
|
user: "{{ item.compte }}"
|
|
key: "{{ item.pubkey }}"
|
|
exclusive: true
|
|
state: present
|
|
loop: "{{ serveur_backup_site_locataires }}"
|
|
loop_control:
|
|
label: "{{ item.nom }}"
|
|
|
|
# ÉCRIRE, PUIS RELIRE (D-68). On vient de déclarer une isolation ; on la constate.
|
|
# Deux locataires qui partageraient un home, ou un home lisible par le groupe, se
|
|
# liraient dans le journal comme un succès.
|
|
- name: Relire ce que le noyau applique vraiment
|
|
ansible.builtin.stat:
|
|
path: "{{ serveur_backup_site_parent }}/{{ item.compte }}"
|
|
register: serveur_backup_site_homes
|
|
loop: "{{ serveur_backup_site_locataires }}"
|
|
loop_control:
|
|
label: "{{ item.compte }}"
|
|
|
|
- name: Exiger une isolation réelle entre locataires
|
|
ansible.builtin.assert:
|
|
that:
|
|
- item.stat.exists
|
|
- item.stat.pw_name == item.item.compte
|
|
- item.stat.mode == "0700"
|
|
fail_msg: >-
|
|
Le dépôt de {{ item.item.nom }} n'est pas isolé :
|
|
propriétaire « {{ item.stat.pw_name | default('?') }} »,
|
|
permissions « {{ item.stat.mode | default('?') }} » (attendu : {{ item.item.compte }}, 0700).
|
|
Tant que ce n'est pas vrai, un locataire peut lire — ou effacer — les instantanés
|
|
d'un autre.
|
|
loop: "{{ serveur_backup_site_homes.results | default([]) }}"
|
|
loop_control:
|
|
label: "{{ item.item.compte }}"
|
|
when: not ansible_check_mode
|
|
|
|
# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et
|
|
# pousse le resultat.
|
|
#
|
|
# LES DEUX ASSERTIONS CI-DESSUS SONT DES PHOTOS. L'espace se consomme tous les jours et
|
|
# l'etanchéite peut glisser a la premiere main qui passe — or aucune de ces deux derives
|
|
# ne fait echouer quoi que ce soit avant qu'il ne soit trop tard. La sonde les rejoue
|
|
# toutes les quinze minutes.
|
|
- name: Assurer le repertoire des sondes de supervision
|
|
ansible.builtin.file:
|
|
path: /usr/local/lib/setops/sondes
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deposer la sonde « depot-locataires »
|
|
ansible.builtin.template:
|
|
src: sonde-depot-locataires.sh.j2
|
|
dest: /usr/local/lib/setops/sondes/depot-locataires.sh
|
|
owner: root
|
|
group: root
|
|
mode: "0750"
|