Files
sshkeyvault/docker-compose.yml
T
florian.wildeandClaude Opus 5.5 8f5b31c0cb feat(benachrichtigung): E-Mail und Webhook bei Drift und Ablauf (#028)
Drift, ablaufende und abgelaufene Schluessel, unerreichbare Hosts und
geaenderte Host-Keys sollen auffallen, ohne dass jemand taeglich in die
Oberflaeche schaut. Versand per SMTP (Microsoft 365, STARTTLS) und per
Webhook mit HMAC-Signatur, eingestellt unter /einstellungen.

Die Anlaesse liest der Worker aus der Datenbank, statt sie im Abgleich
auszuloesen: der Abgleich-Ablauf bleibt unberuehrt, und ein verpasster
Lauf holt sich der naechste. Ein Versandprotokoll (Zustellung) mit
Anlass-Fingerabdruck verhindert Doppelmeldungen und haelt Fehlschlaege
fuer begrenzte Wiederholungen fest. Taeglich eine Zusammenfassung,
geaenderte Host-Keys im Fuenf-Minuten-Takt sofort.

Versand und Testnachricht laufen im Worker, damit der Webprozess keinen
Weg zu Mailserver oder Webhook-Ziel braucht. Passwort und Geheimnis
liegen nur verschluesselt in der Datenbank; die Seite zeigt "gesetzt".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:54:43 +02:00

126 lines
4.6 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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}
# Nur zur Anzeige unter /einstellungen; ausgefuehrt werden die Zeitplaene im Worker.
SSHKV_SYNC_CRON: ${SSHKV_SYNC_CRON:-30 2 * * *}
SSHKV_MELDUNG_CRON: ${SSHKV_MELDUNG_CRON:-0 7 * * *}
# 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 * * *}
# Benachrichtigungen (#028): taegliche Zusammenfassung; Links in den Nachrichten zeigen
# auf SSHKV_APP_URL. Den Weg zu Mailserver und Webhook-Ziel hat der Worker ueber "aussen".
SSHKV_MELDUNG_CRON: ${SSHKV_MELDUNG_CRON:-0 7 * * *}
SSHKV_APP_URL: ${SSHKV_APP_URL}
secrets:
- master_key
# Der Worker braucht die Datenbank und den Weg zu den Hosts (und zu Mailserver/Webhook).
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: