R185-01 (MEDIUM): Die Flanke, die wir selbst gemeldet hatten, hat der
Tester live bestaetigt. seal-backlog war beim ZWEITEN Aufruf genauso
gegatet wie beim ersten ({"confirm":"SEAL"} -> 200), und das Ereignis
landete nur im Audit-Log, nicht im Alarmkanal. Sein Punkt: der
automatische Rueckhalt des Gegenbuchs haengt an `valid` - und `valid`
ueberlebt ein ersetzendes Siegel per Konstruktion. Angriff: Altzeile per
DB-Zugriff loeschen, neu siegeln, Luecke ist beglaubigt, valid wieder
true. Live reproduziert.
Zwei Schichten, in seiner Reihenfolge:
1. Alarmkanal. Neuer SecurityEventType AUDIT_SEAL_CHANGED (Migration
20260826120000). Erstes Siegeln HIGH, Ersetzen CRITICAL - geht damit
ueber sendPendingCriticalAlerts sofort per Mail raus. Die Details
halten Wurzel vorher/nachher und den vollstaendigen Vorbefund fest.
2. Gate. Steht bereits ein Siegel, verlangt der Endpunkt
{"confirm":"RESEAL"} statt SEAL, mit einem Text, der sagt, was dabei
verloren geht. Ein Austausch der Beweisgrundlage soll nicht dasselbe
Wort haben wie das Einrichten.
Und im Gegenbuch selbst: dort stand fuer den Wurzelwechsel ein
console.warn, waehrend der Rueckgabecode auf 0 blieb - also exakt das
Muster, das wir dem CRM zweimal angekreidet haben (R179, R183-02), im
Werkzeug, das dagegen gebaut wurde. Jetzt exit 2, mit alter und neuer
Wurzel samt Blattzahl; "10 Blaetter -> 9 Blaetter" zeigt die Loeschung
sofort. Auch die Erstsiegelung meldet sich, statt stillschweigend
uebernommen zu werden.
Aufloesbar gemacht: der Alarm bricht ab, BEVOR angehaengt wird - ohne
Bestaetigungsweg haette auch ein legitimes Siegeln fuer immer alarmiert
(R183-03-Falle). Neu ist NOTARY_SEAL_ACK, bewusst nicht "true", sondern
die Wurzel selbst (mind. 16 Zeichen): ein stehen gelassener Wert passt
beim naechsten Wechsel nicht mehr und kann keinen weiteren Austausch
durchwinken.
R185-02 (LOW): GET /api/audit-logs?action=<x> gab ungueltige Enum-Werte
roh an die Spalte -> 500. Zweifach schlecht: fehlende Validierung und
Fehler-Orakel (200 vs 500 verraet die Enum-Mitglieder). Jetzt 400 mit
der erlaubten Menge im Klartext. Mitgenommen: sensitivity, Datumsfelder,
Zahlenfelder, Textlaengen und ein Deckel auf limit (200), ueber den sich
sonst die ganze Tabelle an der Seitenlogik vorbei ziehen liess. Beide
Endpunkte.
Getestet ueber HTTP gegen eine Wegwerf-DB, inkl. echtem Gegenbuch-Lauf
mit SSH-signiertem lokalem Repo. Zusaetzlich nachgeholt, was der Tester
nicht herstellen konnte: vollstaendig unsigniertes Protokoll ->
kein_siegel statt der frueheren falschen Entwarnung nicht_noetig, und
seal-backlog nennt den fehlenden Schluessel als naechsten Schritt.
Gegenrichtung geprueft, R183-03 bleibt behoben.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
61 lines
2.2 KiB
YAML
61 lines
2.2 KiB
YAML
# Gegenbuch für OpenCRM
|
||
# =====================
|
||
# Gehört auf einen EIGENEN Rechner – nicht dorthin, wo OpenCRM läuft.
|
||
#
|
||
# Richtung der Verbindung (das ist der eigentliche Schutz):
|
||
# Gegenbuch ──holt lesend──> OpenCRM (HTTPS, Token nur mit audit:read)
|
||
# OpenCRM ─────────────────> (kennt das Gegenbuch nicht)
|
||
#
|
||
# Wer OpenCRM übernimmt, kommt damit nicht an dieses Buch heran. Deshalb holt
|
||
# das Gegenbuch selbst, statt sich etwas schicken zu lassen.
|
||
#
|
||
# Start:
|
||
# cp .env.example .env # Werte eintragen
|
||
# docker compose up -d
|
||
#
|
||
# Zwei Bücher auf einer Maschine sind vorgesehen (Produktion und Test): zwei
|
||
# getrennte Dienste mit getrennten Verzeichnissen und getrennten Schlüsseln.
|
||
# Welche laufen, steuert COMPOSE_PROFILES in der .env.
|
||
|
||
services:
|
||
prod:
|
||
build: .
|
||
container_name: gegenbuch-prod
|
||
restart: unless-stopped
|
||
profiles: ["prod"]
|
||
environment:
|
||
INSTANZ: prod
|
||
CRM_URL: ${PROD_CRM_URL}
|
||
CRM_EMAIL: ${PROD_CRM_EMAIL}
|
||
CRM_PASSWORD: ${PROD_CRM_PASSWORD}
|
||
NOTARY_INTERVAL: ${PROD_INTERVAL:-3600}
|
||
NOTAR_EMAIL: ${NOTAR_EMAIL:-gegenbuch@localhost}
|
||
# Nur beim allerersten Lauf einmalig auf true, danach wieder leeren:
|
||
NOTARY_GENESIS_ACK: ${PROD_GENESIS_ACK:-}
|
||
# Nur nach geklärtem Verlust des Beobachtungsspeichers, siehe README:
|
||
NOTARY_ADOPT_ACK: ${PROD_ADOPT_ACK:-}
|
||
# Nur nach einem GEWOLLTEN Siegelwechsel, mit der neuen Wurzel:
|
||
NOTARY_SEAL_ACK: ${PROD_SEAL_ACK:-}
|
||
volumes:
|
||
# Buch, Schlüssel, Beobachtungsspeicher und Statusdatei.
|
||
# Dieses Verzeichnis ist das Gegenbuch – es gehört ins Backup.
|
||
- ${PROD_DIR:-./data/prod}:/gegenbuch
|
||
|
||
staging:
|
||
build: .
|
||
container_name: gegenbuch-staging
|
||
restart: unless-stopped
|
||
profiles: ["staging"]
|
||
environment:
|
||
INSTANZ: staging
|
||
CRM_URL: ${STAGING_CRM_URL}
|
||
CRM_EMAIL: ${STAGING_CRM_EMAIL}
|
||
CRM_PASSWORD: ${STAGING_CRM_PASSWORD}
|
||
NOTARY_INTERVAL: ${STAGING_INTERVAL:-3600}
|
||
NOTAR_EMAIL: ${NOTAR_EMAIL:-gegenbuch@localhost}
|
||
NOTARY_GENESIS_ACK: ${STAGING_GENESIS_ACK:-}
|
||
NOTARY_ADOPT_ACK: ${STAGING_ADOPT_ACK:-}
|
||
NOTARY_SEAL_ACK: ${STAGING_SEAL_ACK:-}
|
||
volumes:
|
||
- ${STAGING_DIR:-./data/staging}:/gegenbuch
|