Alerter sur une divergence
Lorsque le ledger constate que l’histoire scellée ne correspond plus au WAL, il refuse de sceller et sort avec un code non nul. C’est délibéré : un journal réécrit est un incident, et un service qui réessaierait toutes les trente secondes le déguiserait en battement de cœur. Ces conditions ne se réparent jamais d’elles-mêmes :
| Erreur | Signification |
|---|---|
DivergedLog | l’enregistrement à la frontière scellée ne hache plus vers la tête scellée : le WAL a été réécrit après le scellement |
TruncatedLog | des enregistrements scellés ont disparu du WAL |
UnauthenticatedRecord | un enregistrement dont aucune clé d’origine de confiance ne répond : quelqu’un d’autre que la passerelle a écrit dans le WAL |
StoreBroken | le store de points de contrôle est lui-même corrompu |
KeyConflict | un identifiant de clé réutilisé avec un matériel de clé différent |
Tout le reste (un hoquet d’E/S, un segment de WAL surpris en cours d’écriture)
fait l’objet d’une nouvelle tentative dans la boucle du mode run et ne
provoque jamais de sortie. Toute sortie du mode run appartient donc, par
construction, à la classe de ce qui ne se répare jamais tout seul.
Sortir n’est que la moitié du travail ; qui est prévenu ? Pas par le ledger lui-même, qui ne fait jamais aucun appel réseau : un webhook qui ne peut pas quitter l’enclave est une alerte que personne ne reçoit. Prévenir quelqu’un est le travail du superviseur, et sur les déploiements visés le superviseur est systemd.
Le motif : OnFailure=
Section intitulée « Le motif : OnFailure= »[Unit]Description=Obsign ledger — seals the gateway audit logOnFailure=obsign-alert@%n.service
[Service]Type=execUser=obsign-ledgerExecStart=/usr/local/bin/obsign-ledger run \ --wal /var/lib/obsign/wal --chain-id prod \ --store /var/lib/obsign/ledger \ --hsm-module /usr/lib/pkcs11/vendor.so \ --hsm-key-label seal-prod \ --hsm-pin-file /etc/obsign/hsm-pin \ --key-id seal-prod# No Restart=. Transient failures are retried inside the loop and never# exit; whatever does exit never self-heals. Restart=on-failure would# re-detect the same divergence at every start — the heartbeat the exit# code exists to avoid.Restart=no
[Install]WantedBy=multi-user.targetL’unité d’alerte (obsign-alert@.service, un gabarit instancié avec le nom de
l’unité en échec) exécute un script propre au site. Tout ce qu’il contient est
local ou reste sur le réseau d’administration :
#!/bin/shunit="$1"
# 1. A critical journal entry — the SIEM collector that already reads# this host's journal picks it up without any new plumbing.echo "AUDIT INCIDENT: $unit failed — sealed history no longer matches \the WAL. journalctl -u $unit for the refusal." | systemd-cat -t obsign -p crit
# 2. Everyone logged in on the host, immediately.echo "AUDIT INCIDENT: $unit failed — see journalctl -u $unit" | wall
# 3. Local mail through the site's internal relay, evidence attached.{ printf 'Subject: [obsign] AUDIT INCIDENT on %s: %s\n\n' "$(hostname)" "$unit" journalctl -u "$unit" -n 50 --no-pager} | sendmail ops@site.internalLe message de refus du ledger (quel numéro de séquence a divergé, si un préfixe authentique a été scellé) se trouve sur stderr, donc dans le journal de l’unité en échec. L’alerte n’a qu’à le désigner.
Slack, sans rompre l’air-gap
Section intitulée « Slack, sans rompre l’air-gap »Une alerte dans un outil de discussion vaut la peine, à condition que la même
règle tienne : l’appel réseau vit dans le superviseur, pas dans le ledger.
Le script livré obsign-slack-alert.sh publie vers un Incoming Webhook sous
trois contraintes :
- Au mieux, jamais porteur. Il s’exécute aux côtés des canaux locaux
(journal,
wall, courrier), qui délivrent toujours à l’intérieur de l’enclave. Si Slack est injoignable, le script consigne un avertissement et sort avec0: un canal d’alerte ne doit jamais devenir l’incident. - Borné. Délais de connexion et délais totaux ; l’URL du webhook est lue depuis un fichier appartenant à root, jamais figée dans une unité ou dans un argument.
- Conscient de la sortie réseau. Il honore
https_proxy; un site sans sortie fait tourner à la place un relais de type store-and-forward sur le réseau d’administration. L’enclave elle-même n’ouvre jamais de connexion sortante.
Déploiements de type cron : seal + un timer
Section intitulée « Déploiements de type cron : seal + un timer »Le seal en une passe est le mode adapté à cron et à l’air-gap, avec un
ajout. Un timer continue de se déclencher après un échec, et chaque passe
redétecterait et réalerterait sur la même divergence. Le verrou arrête le
scellement après le premier incident jusqu’à ce qu’un opérateur le lève
explicitement :
# obsign-seal.service[Unit]Description=Obsign sealing passOnFailure=obsign-alert@%n.service# The latch: once the flag exists the timer stops producing alerts (and# seals) until the operator removes the file. Clearing it is a deliberate# act that concludes an investigation, not a retry.ConditionPathExists=!/var/lib/obsign/ledger/INCIDENT
[Service]Type=oneshotUser=obsign-ledgerExecStart=/usr/local/bin/obsign-ledger seal ...ExecStopPost=/bin/sh -c '[ "$$SERVICE_RESULT" = success ] || \ touch /var/lib/obsign/ledger/INCIDENT'Une réserve honnête : contrairement au mode run, un seal en une passe ne
peut pas réessayer en interne, si bien qu’une défaillance passagère (HSM
brièvement injoignable) déclenche elle aussi l’alerte. La fin du journal dit
dans quelle classe vous êtes. Si cette distinction compte à votre échelle,
faites tourner le démon ; toutes ses sorties sont de classe incident.
Ledgers conteneurisés
Section intitulée « Ledgers conteneurisés »L’image distroless du ledger n’embarque pas systemd : gardez le superviseur à
l’extérieur. Lancez le conteneur depuis une unité systemd (ExecStart=docker run --rm ...) : le code de sortie du conteneur devient celui de l’unité, et
OnFailure= s’applique sans changement. Une politique compose
restart: always sur le ledger reconvertit l’incident en battement de cœur,
sans que personne ne soit prévenu.
Quand elle se déclenche
Section intitulée « Quand elle se déclenche »Traitez-la comme un incident. Le WAL et le store du ledger sont la preuve ; ne
les « réparez » pas pour repasser le scellement au vert. obsign-ledger export fonctionne toujours et écrit le pack même
lorsqu’il échoue à la vérification ; un pack en échec sur le disque est
exactement ce que veut l’investigation. Le dernier point de contrôle scellé
(et son ancre RFC 3161, s’il en a une) borne le moment où la réécriture s’est
produite : tout jusqu’à to_seq reste démontrable, et cette frontière est le
point de départ de la chronologie.