Die Kopie beim Syslog-Server ist der vom sshkv-Host unabhaengige Beleg: wer die Datenbank uebernimmt, kann die Hash-Kette neu rechnen, aber nicht zurueckholen, was schon beim Syslog liegt. Eigene kleine Implementierung mit node:dgram/node:net statt einer Bibliothek, weil das Format ueberschaubar ist und jede Abhaengigkeit im Sicherheitskern zaehlt. Strukturierte Daten werden nach RFC 5424 escaped und begrenzt (2048 Byte), Severity folgt dem Ergebnis, Facility standardmaessig authpriv. Der Sender wirft nie und meldet Fehler hoechstens einmal je Minute, damit ein ausgefallener Syslog-Server weder Aktionen verhindert noch das Log flutet. Web und Worker bekommen die Variablen, weil beide protokollieren. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
131 lines
4.8 KiB
YAML
131 lines
4.8 KiB
YAML
# SSHKeyVault – Betrieb mit Weboberflaeche, Worker und Datenbank.
|
||
# 1. cp .env.example .env und Werte eintragen (mindestens SSHKV_APP_SECRET, POSTGRES_PASSWORD,
|
||
# SSHKV_APP_URL, SSHKV_BREAKGLASS_PASSWORD)
|
||
# 2. Master-Key erzeugen (nur fuer den Worker lesbar, Container-Benutzer 1001):
|
||
# mkdir -p geheim && head -c 32 /dev/urandom > geheim/master.key
|
||
# sudo chown 1001:1001 geheim/master.key && sudo chmod 400 geheim/master.key
|
||
# (oder SSHKV_WORKER_USER auf den Eigentuemer der Datei setzen)
|
||
# 3. docker compose up -d
|
||
# Nur im internen Netz/VPN betreiben und einen Reverse Proxy mit TLS davorsetzen (docs/betrieb).
|
||
|
||
name: sshkv
|
||
|
||
services:
|
||
db:
|
||
image: postgres:17-alpine
|
||
container_name: sshkv-db
|
||
restart: unless-stopped
|
||
environment:
|
||
POSTGRES_USER: sshkv
|
||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD fehlt in der .env}
|
||
POSTGRES_DB: sshkv
|
||
volumes:
|
||
- datenbank:/var/lib/postgresql/data
|
||
networks: [datenbank]
|
||
healthcheck:
|
||
test: ["CMD-SHELL", "pg_isready -U sshkv"]
|
||
interval: 10s
|
||
timeout: 5s
|
||
retries: 10
|
||
|
||
# Laeuft einmal je Start: Schema einspielen, Notfall-Admin und Vorlagen anlegen.
|
||
migrate:
|
||
build:
|
||
context: .
|
||
target: bauen
|
||
image: sshkv-migrate
|
||
restart: "no"
|
||
depends_on:
|
||
db:
|
||
condition: service_healthy
|
||
command: ["sh", "-c", "pnpm prisma migrate deploy && pnpm prisma db seed"]
|
||
environment:
|
||
DATABASE_URL: postgresql://sshkv:${POSTGRES_PASSWORD}@db:5432/sshkv?schema=public
|
||
SSHKV_BREAKGLASS_USER: ${SSHKV_BREAKGLASS_USER:-notfall}
|
||
SSHKV_BREAKGLASS_PASSWORD: ${SSHKV_BREAKGLASS_PASSWORD:-}
|
||
networks: [datenbank]
|
||
|
||
web:
|
||
build: .
|
||
image: sshkv
|
||
container_name: sshkv-web
|
||
restart: unless-stopped
|
||
depends_on:
|
||
migrate:
|
||
condition: service_completed_successfully
|
||
environment:
|
||
DATABASE_URL: postgresql://sshkv:${POSTGRES_PASSWORD}@db:5432/sshkv?schema=public
|
||
SSHKV_APP_URL: ${SSHKV_APP_URL:?SSHKV_APP_URL fehlt in der .env}
|
||
SSHKV_APP_SECRET: ${SSHKV_APP_SECRET:?SSHKV_APP_SECRET fehlt in der .env}
|
||
SSHKV_OIDC_ISSUER: ${SSHKV_OIDC_ISSUER:-}
|
||
SSHKV_OIDC_CLIENT_ID: ${SSHKV_OIDC_CLIENT_ID:-}
|
||
SSHKV_OIDC_CLIENT_SECRET: ${SSHKV_OIDC_CLIENT_SECRET:-}
|
||
SSHKV_OIDC_NAME: ${SSHKV_OIDC_NAME:-authentik}
|
||
SSHKV_OIDC_GROUP_ADMIN: ${SSHKV_OIDC_GROUP_ADMIN:-sshkv-admins}
|
||
SSHKV_OIDC_GROUP_OPERATOR: ${SSHKV_OIDC_GROUP_OPERATOR:-sshkv-operators}
|
||
SSHKV_OIDC_GROUP_READER: ${SSHKV_OIDC_GROUP_READER:-sshkv-readers}
|
||
# Syslog-Kopie des Protokolls (#027) – optional, leer heisst aus.
|
||
SSHKV_SYSLOG_HOST: ${SSHKV_SYSLOG_HOST:-}
|
||
SSHKV_SYSLOG_PORT: ${SSHKV_SYSLOG_PORT:-514}
|
||
SSHKV_SYSLOG_PROTOKOLL: ${SSHKV_SYSLOG_PROTOKOLL:-udp}
|
||
SSHKV_SYSLOG_APPNAME: ${SSHKV_SYSLOG_APPNAME:-sshkv}
|
||
SSHKV_SYSLOG_FACILITY: ${SSHKV_SYSLOG_FACILITY:-authpriv}
|
||
# Absichtlich KEIN Master-Key: eine Luecke im Webprozess allein entschluesselt keine
|
||
# Private Keys. Nach aussen braucht er nur den OIDC-Anbieter.
|
||
ports:
|
||
- "${SSHKV_BIND:-127.0.0.1}:${SSHKV_PORT:-3400}:3400"
|
||
networks: [datenbank, aussen]
|
||
healthcheck:
|
||
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3400/api/gesundheit"]
|
||
interval: 30s
|
||
timeout: 5s
|
||
retries: 5
|
||
start_period: 20s
|
||
|
||
worker:
|
||
image: sshkv
|
||
container_name: sshkv-worker
|
||
restart: unless-stopped
|
||
command: ["./start.sh", "worker"]
|
||
# Muss die Master-Key-Datei lesen duerfen (0400). Standard ist der Benutzer im Abbild;
|
||
# alternativ den Eigentuemer der Datei eintragen, z. B. SSHKV_WORKER_USER=1000:1000.
|
||
user: "${SSHKV_WORKER_USER:-1001:1001}"
|
||
depends_on:
|
||
migrate:
|
||
condition: service_completed_successfully
|
||
web:
|
||
condition: service_started
|
||
environment:
|
||
DATABASE_URL: postgresql://sshkv:${POSTGRES_PASSWORD}@db:5432/sshkv?schema=public
|
||
SSHKV_APP_SECRET: ${SSHKV_APP_SECRET}
|
||
SSHKV_MASTER_KEY_FILE: /run/secrets/master_key
|
||
SSHKV_SYNC_CRON: ${SSHKV_SYNC_CRON:-30 2 * * *}
|
||
# Syslog-Kopie des Protokolls (#027) – optional, leer heisst aus.
|
||
SSHKV_SYSLOG_HOST: ${SSHKV_SYSLOG_HOST:-}
|
||
SSHKV_SYSLOG_PORT: ${SSHKV_SYSLOG_PORT:-514}
|
||
SSHKV_SYSLOG_PROTOKOLL: ${SSHKV_SYSLOG_PROTOKOLL:-udp}
|
||
SSHKV_SYSLOG_APPNAME: ${SSHKV_SYSLOG_APPNAME:-sshkv}
|
||
SSHKV_SYSLOG_FACILITY: ${SSHKV_SYSLOG_FACILITY:-authpriv}
|
||
secrets:
|
||
- master_key
|
||
# Der Worker braucht die Datenbank und den Weg zu den Hosts.
|
||
networks: [datenbank, aussen]
|
||
healthcheck:
|
||
test: ["CMD-SHELL", "pgrep -f dist/worker.mjs > /dev/null"]
|
||
interval: 60s
|
||
timeout: 5s
|
||
retries: 3
|
||
|
||
secrets:
|
||
master_key:
|
||
file: ${SSHKV_MASTER_KEY_HOST_FILE:-./geheim/master.key}
|
||
|
||
networks:
|
||
# Die Datenbank hat keinen Weg nach draussen.
|
||
datenbank:
|
||
internal: true
|
||
aussen: {}
|
||
|
||
volumes:
|
||
datenbank:
|