Gegenbuch: Rewind-Waechter statt Probe-Push, Tor vor dem Anhaengen (R179 a/b)
(a) Probe-Push verworfen. Er haette nur den geprobten Ref beurteilt, die eigene Push-Identitaet gemessen statt die des Angreifers (Bypass-Rechte fuer Admins gehen genau dann auseinander, wenn es zaehlt), nur einen Zeitpunkt abgedeckt - und einen zerstoerungsfreien Force-Push gibt es nicht: die bestaetigende Beobachtung waere derselbe Vorgang wie der Schaden. Stattdessen ein Fast-Forward-Waechter: Der beobachtete Remote-Kopf wird ausserhalb des Klons festgehalten; beim naechsten Lauf muss der neue Kopf ein Nachfahre des alten sein. Das erkennt das Ereignis statt die Regel abzufragen und wirkt unabhaengig von serverseitigem Schutz. Ein belegter Fast-Forward gilt als Nachweis und blendet den Rewind-Vorbehalt aus. (b) Code 3 als Tor vor dem Anhaengen statt als Status danach: Der Schreiblauf signiert mit dem neuen Checkpoint zugleich ueber den Bestand darunter - ist die Basis ungeklaert, waere das Anhaengen selbst das Waschmittel. Grundlage nicht feststellbar -> nichts anhaengen, exit 3. Erster Lauf -> Basislinie, ehrlich gemeldet, exit 3. Anhaengen geklappt, Push gescheitert -> exit 3 mit "erstellt, aber NICHT verankert". Nur Anhaengen + Push + belegte Verankerung -> exit 0. Verifiziert: Basislinie exit 3; Folgelauf exit 0 ohne Vorbehalt; Rewind aus einem frischen Auditoren-Klon ohne MIN_SEQ und ohne Zusicherung -> Alarm exit 2 (bisher stilles Gruen); kaputtes Push-Ziel -> "erstellt, aber nicht verankert" exit 3, Folgelauf haelt den ungepushten Commit fail-closed an. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,29 @@ Deshalb zusätzlich:
|
||||
NOTARY_MIN_SEQ=42 node notary.mjs --check
|
||||
```
|
||||
|
||||
## Der Rewind-Wächter
|
||||
|
||||
Das Skript merkt sich bei jedem Lauf den beobachteten Remote-Kopf in einer
|
||||
Datei **außerhalb des Klons** (Standard `~/.opencrm-notary/beobachtungen.jsonl`,
|
||||
per `NOTARY_STATE_FILE` änderbar). Beim nächsten Lauf muss der neue Kopf ein
|
||||
Nachfahre des alten sein. Ist er das nicht, wurde zurückgespult – und zwar
|
||||
**unabhängig davon, ob es einen serverseitigen Schutz gibt**.
|
||||
|
||||
Das ist der Grund, warum hier *kein* Probe-Push stattfindet: Ein solcher Test
|
||||
würde die eigene Push-Identität messen, nicht die des Angreifers (Bypass-Rechte
|
||||
für Administratoren gehen genau dann auseinander, wenn es zählt), er gälte nur
|
||||
für den geprobten Ref, und die bestätigende Beobachtung wäre derselbe Vorgang
|
||||
wie der Schaden. Deshalb wird das **Ereignis** erkannt statt die Regel
|
||||
abgefragt.
|
||||
|
||||
Zwei Konsequenzen für den Betrieb:
|
||||
|
||||
- **Die Datei gehört nicht in den Klon** und sollte möglichst auf getrenntem
|
||||
Speicher liegen. Geht sie verloren, beginnt die Beobachtung von vorn – der
|
||||
erste Lauf danach meldet ehrlich „erste Beobachtung", nicht „alles gut".
|
||||
- **Der allererste Lauf endet mit Code 3.** Was man nie gesehen hat, kann man
|
||||
nicht vergleichen. Das ist kein Fehler, sondern die ehrliche Auskunft.
|
||||
|
||||
**`NOTARY_MIN_SEQ` ersetzt die serverseitige Sperre nicht.** Der Wert ist eine
|
||||
*Untergrenze* und immer nur so frisch wie deine letzte Beobachtung. Wer
|
||||
stündlich beglaubigt, aber wöchentlich prüft, läuft mit einem Wert herum, der
|
||||
@@ -131,7 +154,7 @@ Deshalb gilt jetzt:
|
||||
| 0 | alles in Ordnung, Checkpoint angehängt (bzw. Prüfung bestanden) |
|
||||
| 1 | Betriebsfehler (Konfiguration, Commit oder Push fehlgeschlagen) |
|
||||
| 2 | **Befund** – Widerspruch zwischen CRM und Gegenbuch, oder ungültige Signatur |
|
||||
| 3 | beglaubigter Stand nicht abschließend feststellbar – Remote fehlt/unerreichbar, **oder** ein Zurückspulen lässt sich nicht ausschließen |
|
||||
| 3 | beglaubigter Stand nicht abschließend feststellbar – Remote fehlt/unerreichbar, erste Beobachtung, Zurückspulen nicht ausschließbar, **oder** Checkpoint erstellt aber nicht verankert |
|
||||
|
||||
Für Cron gilt: **jeder** Code außer 0 gehört gemeldet. Code 2 ist der Alarm,
|
||||
Code 3 heißt „ich weiß es nicht" – und das ist ausdrücklich kein Freibrief.
|
||||
@@ -145,6 +168,19 @@ node notary.mjs --check
|
||||
Führt alle Kontrollen aus, hängt aber nichts an und braucht kein Schreibrecht.
|
||||
Geeignet für jemanden, der die Kette unabhängig nachvollziehen will.
|
||||
|
||||
## Anhängen ist selbst ein Beglaubigungsakt
|
||||
|
||||
Ein Schreiblauf erweitert nicht nur die Kette – er signiert damit zugleich über
|
||||
alles darunter. Deshalb prüft das Skript **vor** dem Anhängen und verweigert
|
||||
es, wenn die Grundlage nicht feststeht. Sonst wäre das Anhängen selbst das
|
||||
Waschmittel: eine frische Signatur über einen ungeklärten Vorzustand beglaubigt
|
||||
diesen mit.
|
||||
|
||||
Ebenso gilt: **erstellt ist nicht verankert.** Klappt der Push nicht, endet der
|
||||
Lauf mit Code 3 und der ausdrücklichen Auskunft „erstellt, aber NICHT
|
||||
verankert" – niemals mit 0. Der nächste Lauf hält den ungepushten Commit dann
|
||||
an, bis er geklärt ist.
|
||||
|
||||
## Was das Skript erkennt
|
||||
|
||||
| Angriff | Erkennung |
|
||||
@@ -153,6 +189,7 @@ Geeignet für jemanden, der die Kette unabhängig nachvollziehen will.
|
||||
| Einträge am Ende abgeschnitten | aktuelle höchste ID kleiner als die beglaubigte |
|
||||
| Bestandssiegel verschwunden | vorher beglaubigt, jetzt nicht mehr vorhanden |
|
||||
| Gegenbuch selbst gekürzt | Lücke in der fortlaufenden Nummer |
|
||||
| **Reihe zurückgespult (Force-Push)** | **Remote-Kopf ist kein Nachfahre des zuletzt beobachteten** |
|
||||
| Gegenbuch lokal manipuliert | Arbeitsdatei weicht vom signierten Stand ab |
|
||||
| Untergeschobener Commit | Commit ohne gültige Signatur in der Historie |
|
||||
| Nie gepushte lokale Commits | Abgleich gegen den Remote-Kopf |
|
||||
|
||||
Reference in New Issue
Block a user