Gegenbuch als Docker-Setup, lokales Buch auf eigener Maschine
Betreiber-Entscheidung: Das Gegenbuch laeuft auf einer eigenen Maschine fuer Prod und Staging; ein externes Git-Repository entfaellt, das Buch liegt lokal. Die Trennung, auf die es ankommt, ist damit gegeben - wer OpenCRM uebernimmt, kommt nicht ans Buch. Richtung bewusst so herum: Das Gegenbuch holt ueber HTTPS mit einem Token, das nur audit:read kann. OpenCRM kennt weder Adresse noch Schluessel des Gegenbuchs. Kein SSH-Zugang zum CRM noetig. tools/audit-notary/ enthaelt jetzt Dockerfile, entrypoint.sh, docker-compose.yml und .env.example. Zwei Dienste (prod, staging) mit getrennten Verzeichnissen und Schluesseln, gesteuert ueber COMPOSE_PROFILES - dasselbe Muster wie beim Caddy-Profil im Hauptstack. Der Signaturschluessel wird beim ersten Start auf der Gegenbuch-Maschine erzeugt. Lokaler Betrieb ist jetzt ein vollwertiger Modus statt eines Testschalters. Die Erfolgsmeldung benennt bei jedem Lauf, was abgedeckt ist und was nicht - statt der frueheren pauschalen Formulierung "kein Manipulationsschutz", die im Einsatz auf eigener Maschine schlicht falsch war. Verifiziert mit echtem Docker-Build gegen eine CRM-Attrappe: Genesis ohne Bestaetigung -> Code 4; mit Bestaetigung Normalbetrieb exit 0; Eintrag veraendert -> Alarm exit 2; Eintraege geloescht -> Alarm exit 2; Siegel verschwunden -> Alarm. Behoben beim Bauen: useradd -u 1000 || true verschluckte, dass UID 1000 im Node-Image vergeben ist - der Container startete gar nicht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+70
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
# Gegenbuch-Schleife: einrichten, dann in festem Takt beglaubigen.
|
||||
set -uo pipefail
|
||||
|
||||
: "${INSTANZ:?INSTANZ fehlt (z. B. prod oder staging)}"
|
||||
: "${CRM_URL:?CRM_URL fehlt – von welcher OpenCRM-Instanz soll geholt werden?}"
|
||||
: "${CRM_TOKEN:?CRM_TOKEN fehlt – Token eines Benutzers mit audit:read}"
|
||||
|
||||
INTERVALL="${NOTARY_INTERVAL:-3600}"
|
||||
BUCH=/gegenbuch/buch
|
||||
SCHLUESSEL=/gegenbuch/schluessel
|
||||
export NOTARY_STATE_FILE=/gegenbuch/beobachtungen.jsonl
|
||||
export NOTARY_ALLOW_LOCAL=true # Buch liegt auf dieser Maschine
|
||||
export NOTARY_PUSH=false # es gibt kein Ziel zum Pushen
|
||||
|
||||
echo "[$INSTANZ] Gegenbuch startet – Quelle: $CRM_URL, Takt: ${INTERVALL}s"
|
||||
|
||||
# --- Signaturschlüssel: einmalig auf DIESER Maschine erzeugen ---------------
|
||||
# Er verlässt den Rechner nie. OpenCRM kennt ihn nicht.
|
||||
mkdir -p "$SCHLUESSEL"
|
||||
if [ ! -f "$SCHLUESSEL/id_ed25519" ]; then
|
||||
echo "[$INSTANZ] Erzeuge Signaturschlüssel..."
|
||||
ssh-keygen -t ed25519 -f "$SCHLUESSEL/id_ed25519" -N "" -C "gegenbuch-$INSTANZ" -q
|
||||
fi
|
||||
chmod 600 "$SCHLUESSEL/id_ed25519"
|
||||
if [ ! -f "$SCHLUESSEL/allowed_signers" ]; then
|
||||
echo "gegenbuch-$INSTANZ namespaces=\"git\" $(cat "$SCHLUESSEL/id_ed25519.pub")" \
|
||||
> "$SCHLUESSEL/allowed_signers"
|
||||
fi
|
||||
|
||||
# --- Buch anlegen, falls noch nicht vorhanden ------------------------------
|
||||
if [ ! -d "$BUCH/.git" ]; then
|
||||
echo "[$INSTANZ] Lege Gegenbuch an..."
|
||||
mkdir -p "$BUCH"
|
||||
git -C "$BUCH" init -q -b main
|
||||
fi
|
||||
cd "$BUCH" || exit 1
|
||||
|
||||
git config user.email "${NOTAR_EMAIL:-gegenbuch@localhost}"
|
||||
git config user.name "Gegenbuch $INSTANZ"
|
||||
git config gpg.format ssh
|
||||
git config user.signingkey "$SCHLUESSEL/id_ed25519.pub"
|
||||
git config gpg.ssh.allowedSignersFile "$SCHLUESSEL/allowed_signers"
|
||||
|
||||
# Ein erster Commit, damit es eine Historie gibt, an die angehängt werden kann.
|
||||
if ! git rev-parse HEAD >/dev/null 2>&1; then
|
||||
git commit -q -S --allow-empty -m "Gegenbuch $INSTANZ angelegt"
|
||||
fi
|
||||
|
||||
# --- Schleife ---------------------------------------------------------------
|
||||
# Jeder Rückgabecode außer 0 gehört angesehen. Der letzte Stand steht
|
||||
# zusätzlich in status.txt, damit eine Überwachung ihn abgreifen kann, ohne
|
||||
# Logs zu durchsuchen.
|
||||
while true; do
|
||||
ZEIT=$(date -Iseconds)
|
||||
node /opt/notary/notary.mjs
|
||||
CODE=$?
|
||||
case $CODE in
|
||||
0) LAGE="in Ordnung" ;;
|
||||
1) LAGE="Betriebsfehler (Konfiguration, CRM nicht erreichbar, Commit)" ;;
|
||||
2) LAGE="BEFUND – Widerspruch zwischen CRM und Gegenbuch" ;;
|
||||
3) LAGE="nicht abschliessend feststellbar (u. a. erster Lauf)" ;;
|
||||
4) LAGE="Gedaechtnis fehlt – Bestaetigung noetig, siehe README" ;;
|
||||
5) LAGE="Anker unvollstaendig" ;;
|
||||
*) LAGE="unbekannter Code" ;;
|
||||
esac
|
||||
echo "[$INSTANZ] $ZEIT exit=$CODE ($LAGE)"
|
||||
printf '%s exit=%s %s\n' "$ZEIT" "$CODE" "$LAGE" > /gegenbuch/status.txt
|
||||
sleep "$INTERVALL"
|
||||
done
|
||||
Reference in New Issue
Block a user