CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
10 bisher ungehaertete Archive-Backup/Reconcile/Scrub-Oneshot-Vorlagen (deploy/systemd/) bekommen die etablierten Sandbox-Direktiven (NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, ggf. ReadWritePaths). Drei Vorlagen ohne Dateisystemzugriff (reconcile, retention-notify, scrub) bekommen bewusst keine ReadWritePaths, begruendet dokumentiert (S3-kompatible Storage-Abstraktion statt lokaler Pfade). Neue, getrennte Postgres-Laufzeit-Rolle (nexarch_tenant_runtime, OHNE CREATEDB) statt der bisher gemeinsam genutzten CREATEDB-Rolle fuer NEXARCH_TENANT_DSN_TEMPLATE. Provisioner.Provision() berechtigt die neue Rolle je frisch angelegter Tenant-DB (GRANT CONNECT + Schema- Rechte inkl. Default-Privileges), Tenant.DBDSN wird jetzt mit dieser Rolle statt der CREATEDB-Rolle gebaut. Neue Umgebungsvariable NEXARCH_TENANT_RUNTIME_DSN_TEMPLATE, deploy/install.sh legt die Rolle automatisch an. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
218 lines
11 KiB
Bash
Executable File
218 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# NEXARCH Core – Installer/Deployer fuer eine Debian/systemd-Zielumgebung.
|
||
#
|
||
# Baut alle Core-Binaries (cmd/core + die *-devserver), legt Postgres-Rolle
|
||
# und Registry-Datenbank an (falls nicht vorhanden), wendet die
|
||
# Registry-Migrationen an, generiert Secrets (falls nicht vorhanden) und
|
||
# installiert+startet jeden Dienst als eigenen systemd-Service.
|
||
#
|
||
# Idempotent: mehrfacher Aufruf ist sicher — vorhandene Rolle/DB/Secrets
|
||
# werden nicht ueberschrieben, Migrationen nur angewendet, wenn die
|
||
# Registry-DB noch leer ist (siehe Abschnitt "Migrationen" unten).
|
||
#
|
||
# Aufruf (als root): ./deploy/install.sh
|
||
#
|
||
# Ueberschreibbar per Umgebungsvariable:
|
||
# NEXARCH_INSTALL_DIR (Default: /opt/nexarch-core)
|
||
# NEXARCH_PG_ROLE (Default: nexarch_core)
|
||
# NEXARCH_PG_REGISTRY_ROLE (Default: nexarch_registry)
|
||
# NEXARCH_PG_REGISTRY_DB (Default: nexarch_registry)
|
||
# NEXARCH_BRANCH (Default: aktueller Branch dieses Checkouts)
|
||
#
|
||
# TEN-09 (Registry-Haertung): es gibt zwei getrennte Postgres-Rollen statt
|
||
# einer gemeinsamen Superuser-aehnlichen Rolle (der Keycloak-Fehler, den
|
||
# das Ticket ausdruecklich vermeiden soll):
|
||
# - NEXARCH_PG_ROLE (nexarch_core): hat CREATEDB, wird NUR fuer
|
||
# NEXARCH_ADMIN_DSN/NEXARCH_TENANT_DSN_TEMPLATE genutzt (Tenant-DB-
|
||
# Provisioning/Zugriff, TEN-01). Diese Rolle wird NIEMALS fuer die
|
||
# Registry-Verbindung selbst verwendet.
|
||
# - NEXARCH_PG_REGISTRY_ROLE (nexarch_registry): Eigentuemerin der
|
||
# Registry-Datenbank, OHNE CREATEDB, wird fuer NEXARCH_REGISTRY_DSN
|
||
# genutzt. Da neu angelegte Tenant-Datenbanken PUBLIC das CONNECT-Recht
|
||
# explizit entziehen (siehe internal/tenant/provisioner.go), hat diese
|
||
# Rolle keinerlei Zugriff auf Tenant-Datenbanken (Akzeptanzkriterium 1).
|
||
set -euo pipefail
|
||
|
||
INSTALL_DIR="${NEXARCH_INSTALL_DIR:-/opt/nexarch-core}"
|
||
PG_ROLE="${NEXARCH_PG_ROLE:-nexarch_core}"
|
||
PG_REGISTRY_ROLE="${NEXARCH_PG_REGISTRY_ROLE:-nexarch_registry}"
|
||
PG_TENANT_RUNTIME_ROLE="${NEXARCH_PG_TENANT_RUNTIME_ROLE:-nexarch_tenant_runtime}"
|
||
PG_DB="${NEXARCH_PG_REGISTRY_DB:-nexarch_registry}"
|
||
ENV_FILE=/etc/nexarch/core.env
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
|
||
if [ "$(id -u)" -ne 0 ]; then
|
||
echo "Bitte als root ausfuehren (legt Systembenutzer, systemd-Units und Postgres-Rolle an)." >&2
|
||
exit 1
|
||
fi
|
||
|
||
echo "== 1. Systembenutzer =="
|
||
if ! id nexarch >/dev/null 2>&1; then
|
||
useradd --system --no-create-home --shell /usr/sbin/nologin nexarch
|
||
echo "Benutzer 'nexarch' angelegt."
|
||
else
|
||
echo "Benutzer 'nexarch' existiert bereits."
|
||
fi
|
||
|
||
echo "== 2. Postgres-Rollen und Registry-Datenbank =="
|
||
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname = '${PG_ROLE}'" | grep -q 1; then
|
||
PG_PASSWORD="$(openssl rand -hex 24)"
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -c "CREATE ROLE ${PG_ROLE} WITH LOGIN PASSWORD '${PG_PASSWORD}';"
|
||
echo "Rolle '${PG_ROLE}' angelegt."
|
||
else
|
||
echo "Rolle '${PG_ROLE}' existiert bereits — Passwort wird NICHT neu gesetzt (siehe ${ENV_FILE} fuer das vorhandene)."
|
||
PG_PASSWORD=""
|
||
fi
|
||
|
||
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname = '${PG_REGISTRY_ROLE}'" | grep -q 1; then
|
||
PG_REGISTRY_PASSWORD="$(openssl rand -hex 24)"
|
||
# Bewusst OHNE CREATEDB — diese Rolle darf ausschliesslich die Registry-DB
|
||
# nutzen, nie eine Tenant-Datenbank anlegen oder verwalten (Akzeptanz-
|
||
# kriterium 1 / "bekannte Fehler vermeiden": keine gemeinsame Rolle mit
|
||
# Tenant-Datenbankzugriff).
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -c "CREATE ROLE ${PG_REGISTRY_ROLE} WITH LOGIN PASSWORD '${PG_REGISTRY_PASSWORD}' NOCREATEDB;"
|
||
echo "Rolle '${PG_REGISTRY_ROLE}' angelegt."
|
||
else
|
||
echo "Rolle '${PG_REGISTRY_ROLE}' existiert bereits — Passwort wird NICHT neu gesetzt (siehe ${ENV_FILE} fuer das vorhandene)."
|
||
PG_REGISTRY_PASSWORD=""
|
||
fi
|
||
|
||
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname = '${PG_TENANT_RUNTIME_ROLE}'" | grep -q 1; then
|
||
PG_TENANT_RUNTIME_PASSWORD="$(openssl rand -hex 24)"
|
||
# OPS-12/F-15: separate Rolle OHNE CREATEDB fuer die laufende
|
||
# Tenant-DB-Verbindung aller Dienste -- die CREATEDB-faehige Rolle
|
||
# (PG_ROLE) wird nur noch intern beim Provisioning verwendet, nie mehr
|
||
# an konsumierende Dienste weitergegeben.
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -c "CREATE ROLE ${PG_TENANT_RUNTIME_ROLE} WITH LOGIN PASSWORD '${PG_TENANT_RUNTIME_PASSWORD}' NOCREATEDB;"
|
||
echo "Rolle '${PG_TENANT_RUNTIME_ROLE}' angelegt."
|
||
else
|
||
echo "Rolle '${PG_TENANT_RUNTIME_ROLE}' existiert bereits — Passwort wird NICHT neu gesetzt (siehe ${ENV_FILE} fuer das vorhandene)."
|
||
PG_TENANT_RUNTIME_PASSWORD=""
|
||
fi
|
||
|
||
if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname = '${PG_DB}'" | grep -q 1; then
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -c "CREATE DATABASE ${PG_DB} OWNER ${PG_REGISTRY_ROLE};"
|
||
echo "Datenbank '${PG_DB}' angelegt (Eigentuemerin: ${PG_REGISTRY_ROLE})."
|
||
else
|
||
echo "Datenbank '${PG_DB}' existiert bereits."
|
||
fi
|
||
# Fuer Tenant-Provisioning (CREATE DATABASE pro Mandant, Modell C) braucht
|
||
# NUR die Admin-/Provisioning-Rolle CREATEDB-Recht — NICHT die Registry-Rolle.
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -c "ALTER ROLE ${PG_ROLE} WITH CREATEDB;" >/dev/null
|
||
# Verteidigungslinie 2: selbst falls die Registry-Rolle irgendwann doch
|
||
# CONNECT-Rechte auf einer Tenant-Datenbank erhalten sollte, bleibt sie ohne
|
||
# CREATEDB unfaehig, selbst Tenant-Datenbanken anzulegen/zu uebernehmen.
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -c "ALTER ROLE ${PG_REGISTRY_ROLE} WITH NOCREATEDB;" >/dev/null
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -c "ALTER ROLE ${PG_TENANT_RUNTIME_ROLE} WITH NOCREATEDB;" >/dev/null
|
||
|
||
echo "== 3. Secrets/Konfiguration =="
|
||
mkdir -p /etc/nexarch
|
||
if [ ! -f "$ENV_FILE" ]; then
|
||
if [ -z "${PG_PASSWORD}" ] || [ -z "${PG_REGISTRY_PASSWORD}" ] || [ -z "${PG_TENANT_RUNTIME_PASSWORD}" ]; then
|
||
echo "FEHLER: mindestens eine Rolle existierte bereits, aber ${ENV_FILE} fehlt — Passwort kann nicht rekonstruiert werden." >&2
|
||
echo "Entweder ${ENV_FILE} von Hand mit den bekannten Passwoertern anlegen, oder die Rollen droppen und erneut installieren." >&2
|
||
exit 1
|
||
fi
|
||
|
||
AUDIT_ADMIN_TOKEN="$(openssl rand -hex 32)"
|
||
# QA-15 F-01..F-03: Dienst-Token fuer interne Admin-Endpunkte (ohne sie starten die Dienste nicht).
|
||
CORE_ADMIN_TOKEN="$(openssl rand -hex 32)"
|
||
LICADMIN_ADMIN_TOKEN="$(openssl rand -hex 32)"
|
||
TENANTADMIN_ADMIN_TOKEN="$(openssl rand -hex 32)"
|
||
|
||
# Ed25519-Schluesselpaar fuer die Lizenzsignatur (LIC-01/LIC-04):
|
||
# licadmin-devserver prueft Lizenzen nur mit dem OEFFENTLICHEN Schluessel,
|
||
# der private Schluessel wird NICHT deployt, sondern separat fuer das
|
||
# (noch zu bauende) offline Lizenz-Ausstellungswerkzeug aufbewahrt.
|
||
KEYPAIR_DIR=/etc/nexarch/license-signing
|
||
mkdir -p "$KEYPAIR_DIR"
|
||
chmod 700 "$KEYPAIR_DIR"
|
||
if [ ! -f "$KEYPAIR_DIR/private.b64" ]; then
|
||
go run "$SCRIPT_DIR/gen-ed25519-keypair.go" > "$KEYPAIR_DIR/keypair.txt"
|
||
grep '^PRIVATE=' "$KEYPAIR_DIR/keypair.txt" | cut -d= -f2 > "$KEYPAIR_DIR/private.b64"
|
||
grep '^PUBLIC=' "$KEYPAIR_DIR/keypair.txt" | cut -d= -f2 > "$KEYPAIR_DIR/public.b64"
|
||
chmod 600 "$KEYPAIR_DIR"/*.b64 "$KEYPAIR_DIR/keypair.txt"
|
||
rm -f "$KEYPAIR_DIR/keypair.txt"
|
||
fi
|
||
LICENSE_PUBLIC_KEY="$(cat "$KEYPAIR_DIR/public.b64")"
|
||
|
||
cat > "$ENV_FILE" <<EOF
|
||
# Automatisch generiert von deploy/install.sh am $(date -Iseconds).
|
||
# NICHT ins Repository committen — enthaelt Zugangsdaten/Secrets.
|
||
NEXARCH_REGISTRY_DSN=postgresql://${PG_REGISTRY_ROLE}:${PG_REGISTRY_PASSWORD}@localhost:5432/${PG_DB}?sslmode=disable
|
||
NEXARCH_ADMIN_DSN=postgresql://${PG_ROLE}:${PG_PASSWORD}@localhost:5432/postgres?sslmode=disable
|
||
NEXARCH_TENANT_DSN_TEMPLATE=postgresql://${PG_ROLE}:${PG_PASSWORD}@localhost:5432/%s?sslmode=disable
|
||
NEXARCH_TENANT_RUNTIME_DSN_TEMPLATE=postgresql://${PG_TENANT_RUNTIME_ROLE}:${PG_TENANT_RUNTIME_PASSWORD}@localhost:5432/%s?sslmode=disable
|
||
NEXARCH_TENANT_SCHEMA_PATH=${INSTALL_DIR}/migrations/tenant
|
||
NEXARCH_LISTEN_ADDR=127.0.0.1:8080
|
||
NEXARCH_CORE_ADMIN_TOKEN=${CORE_ADMIN_TOKEN}
|
||
NEXARCH_LICADMIN_ADMIN_TOKEN=${LICADMIN_ADMIN_TOKEN}
|
||
NEXARCH_TENANTADMIN_ADMIN_TOKEN=${TENANTADMIN_ADMIN_TOKEN}
|
||
# Kommagetrennte, explizite Ursprungslisten fuer die Verwaltungsoberflaeche (kein *):
|
||
# NEXARCH_LICADMIN_CORS_ORIGINS=https://admin.example.test
|
||
# NEXARCH_TENANTADMIN_CORS_ORIGINS=https://admin.example.test
|
||
NEXARCH_AUDIT_ADMIN_TOKEN=${AUDIT_ADMIN_TOKEN}
|
||
NEXARCH_LICENSE_PUBLIC_KEY=${LICENSE_PUBLIC_KEY}
|
||
EOF
|
||
chmod 600 "$ENV_FILE"
|
||
chown nexarch:nexarch "$ENV_FILE"
|
||
echo "${ENV_FILE} angelegt (Secrets generiert)."
|
||
else
|
||
echo "${ENV_FILE} existiert bereits — wird nicht ueberschrieben."
|
||
fi
|
||
|
||
echo "== 4. Build =="
|
||
mkdir -p "$INSTALL_DIR/bin"
|
||
cd "$REPO_ROOT"
|
||
for svc in core auditlog-devserver licadmin-devserver metrics-devserver secheaders-devserver statuspage-devserver tenantadmin-devserver; do
|
||
go build -o "$INSTALL_DIR/bin/${svc}" "./cmd/${svc}"
|
||
echo "gebaut: ${svc}"
|
||
done
|
||
cp -r migrations "$INSTALL_DIR/migrations"
|
||
chown -R nexarch:nexarch "$INSTALL_DIR"
|
||
|
||
echo "== 5. Registry-Migrationen =="
|
||
# shellcheck disable=SC1090
|
||
source "$ENV_FILE"
|
||
ALREADY_MIGRATED=$(sudo -u postgres psql -tAc "SELECT to_regclass('public.tenants') IS NOT NULL" -d "${PG_DB}")
|
||
if [ "$ALREADY_MIGRATED" = "t" ]; then
|
||
echo "Registry-Datenbank hat bereits Tabellen — Migrationen werden uebersprungen (nicht idempotent, siehe Skript-Kommentar)."
|
||
else
|
||
for f in $(find "$INSTALL_DIR/migrations" -maxdepth 1 -name '*.up.sql' | sort); do
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -d "${PG_DB}" -f "$f" >/dev/null
|
||
echo "angewendet: $(basename "$f")"
|
||
done
|
||
fi
|
||
# Migrationen laufen als 'postgres' (fuer CREATE EXTENSION etc.) — die von
|
||
# 'postgres' angelegten Tabellen gehoeren deshalb NICHT automatisch der
|
||
# Registry-Rolle. Ohne diese expliziten GRANTs (inkl. ALTER DEFAULT
|
||
# PRIVILEGES fuer kuenftige Migrationen) koennte NEXARCH_REGISTRY_DSN gar
|
||
# nicht auf die Registry-Tabellen zugreifen.
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -d "${PG_DB}" -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${PG_REGISTRY_ROLE};" >/dev/null
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -d "${PG_DB}" -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${PG_REGISTRY_ROLE};" >/dev/null
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -d "${PG_DB}" -c "ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO ${PG_REGISTRY_ROLE};" >/dev/null
|
||
sudo -u postgres psql -v ON_ERROR_STOP=1 -d "${PG_DB}" -c "ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO ${PG_REGISTRY_ROLE};" >/dev/null
|
||
|
||
echo "== 6. systemd-Units =="
|
||
for tmpl in "$SCRIPT_DIR"/systemd/*.service.tmpl; do
|
||
name="$(basename "$tmpl" .service.tmpl)"
|
||
sed "s#__INSTALL_DIR__#${INSTALL_DIR}#g" "$tmpl" > "/etc/systemd/system/${name}.service"
|
||
done
|
||
systemctl daemon-reload
|
||
for tmpl in "$SCRIPT_DIR"/systemd/*.service.tmpl; do
|
||
name="$(basename "$tmpl" .service.tmpl)"
|
||
systemctl enable --now "${name}.service"
|
||
done
|
||
|
||
echo "== Fertig =="
|
||
echo "Status pruefen mit: systemctl status 'nexarch-*'"
|
||
echo "Health-Checks:"
|
||
echo " curl -s http://localhost:8080/healthz"
|
||
echo " curl -s http://localhost:8081/healthz # licadmin"
|
||
echo " curl -s http://localhost:8082/healthz # tenantadmin"
|
||
echo " curl -s http://localhost:8083/healthz # auditlog"
|
||
echo " curl -s http://localhost:8084/healthz # statuspage"
|
||
echo " curl -s http://localhost:8085/healthz # metrics"
|
||
echo " curl -s http://localhost:8086/unprotected # secheaders (kein /healthz)"
|