#!/usr/bin/env sh set -eu cd "$(dirname "$0")" command -v docker >/dev/null 2>&1 || { echo "Docker fehlt." >&2; exit 1; } docker compose version >/dev/null 2>&1 || { echo "Docker Compose Plugin fehlt." >&2; exit 1; } PRESET_ENROLLMENT_KEY="${ENROLLMENT_KEY:-}" randhex() { n="$1" if command -v openssl >/dev/null 2>&1; then openssl rand -hex "$n" else dd if=/dev/urandom bs="$n" count=1 2>/dev/null | od -An -tx1 | tr -d ' \n' fi } get_env() { key="$1" awk -F= -v k="$key" '$1==k {sub(/^[^=]*=/, ""); print; exit}' .env 2>/dev/null || true } set_env() { key="$1"; value="$2"; tmp=".env.tmp.$$" awk -v k="$key" -v v="$value" ' BEGIN {done=0} index($0,k"=")==1 {print k"="v; done=1; next} {print} END {if(!done) print k"="v} ' .env > "$tmp" mv "$tmp" .env } # Start from a complete template, but preserve all values from an existing .env. [ -f .env ] || : > .env while IFS= read -r line || [ -n "$line" ]; do case "$line" in ''|'#'*) continue ;; esac key=${line%%=*} if ! grep -q "^${key}=" .env 2>/dev/null; then printf '%s\n' "$line" >> .env fi done < .env.example # Generate secrets only if absent/blank/placeholder. Existing installations keep their credentials. for key in POSTGRES_PASSWORD CLICKHOUSE_PASSWORD CLICKHOUSE_GRAFANA_PASSWORD UI_PASSWORD GRAFANA_ADMIN_PASSWORD GARAGE_SECRET_KEY GARAGE_RPC_SECRET GARAGE_ADMIN_TOKEN GARAGE_METRICS_TOKEN; do val="$(get_env "$key")" if [ -z "$val" ] || [ "$val" = "CHANGE_ME" ]; then case "$key" in POSTGRES_PASSWORD|CLICKHOUSE_PASSWORD|CLICKHOUSE_GRAFANA_PASSWORD) val="$(randhex 24)" ;; UI_PASSWORD|GRAFANA_ADMIN_PASSWORD) val="$(randhex 18)" ;; *) val="$(randhex 32)" ;; esac set_env "$key" "$val" fi done val="$(get_env GARAGE_ACCESS_KEY)" if [ -z "$val" ] || [ "$val" = "CHANGE_ME" ]; then set_env GARAGE_ACCESS_KEY "GK$(randhex 16)" fi val="$(get_env ENROLLMENT_KEY)" if [ -n "$PRESET_ENROLLMENT_KEY" ]; then set_env ENROLLMENT_KEY "$PRESET_ENROLLMENT_KEY" elif [ -z "$val" ] || [ "$val" = "CHANGE_ME" ]; then set_env ENROLLMENT_KEY "$(randhex 32)" fi chmod 600 .env 2>/dev/null || true # Validate required settings without sourcing .env as shell code. Docker Compose parses # .env itself; this also keeps values with shell-special characters safe. for key in POSTGRES_PASSWORD CLICKHOUSE_PASSWORD CLICKHOUSE_GRAFANA_PASSWORD UI_PASSWORD GRAFANA_ADMIN_PASSWORD ENROLLMENT_KEY GARAGE_ACCESS_KEY GARAGE_SECRET_KEY GARAGE_RPC_SECRET GARAGE_ADMIN_TOKEN GARAGE_METRICS_TOKEN; do val="$(get_env "$key")" if [ -z "$val" ] || [ "$val" = "CHANGE_ME" ]; then echo "Ungültige .env: $key fehlt oder ist noch CHANGE_ME." >&2 exit 1 fi done UI_PORT_VALUE="$(get_env UI_PORT)"; [ -n "$UI_PORT_VALUE" ] || UI_PORT_VALUE=8080 INGRESS_PORT_VALUE="$(get_env INGRESS_PORT)"; [ -n "$INGRESS_PORT_VALUE" ] || INGRESS_PORT_VALUE=8090 CLICKHOUSE_USER_VALUE="$(get_env CLICKHOUSE_USER)"; [ -n "$CLICKHOUSE_USER_VALUE" ] || CLICKHOUSE_USER_VALUE=siem CLICKHOUSE_DB_VALUE="$(get_env CLICKHOUSE_DB)"; [ -n "$CLICKHOUSE_DB_VALUE" ] || CLICKHOUSE_DB_VALUE=siem CLICKHOUSE_PASSWORD_VALUE="$(get_env CLICKHOUSE_PASSWORD)" CLICKHOUSE_HTTP_PORT_VALUE="$(get_env CLICKHOUSE_HTTP_PORT)"; [ -n "$CLICKHOUSE_HTTP_PORT_VALUE" ] || CLICKHOUSE_HTTP_PORT_VALUE=8123 CLICKHOUSE_NATIVE_PORT_VALUE="$(get_env CLICKHOUSE_NATIVE_PORT)"; [ -n "$CLICKHOUSE_NATIVE_PORT_VALUE" ] || CLICKHOUSE_NATIVE_PORT_VALUE=9000 UI_USERNAME_VALUE="$(get_env UI_USERNAME)"; [ -n "$UI_USERNAME_VALUE" ] || UI_USERNAME_VALUE=admin UI_PASSWORD_VALUE="$(get_env UI_PASSWORD)" ENROLLMENT_KEY_VALUE="$(get_env ENROLLMENT_KEY)" echo "Prüfe Host-Kompatibilität und Portbelegung ..." ./preflight.sh # preflight may have changed ports or the ClickHouse image. Read final values again. UI_PORT_VALUE="$(get_env UI_PORT)"; [ -n "$UI_PORT_VALUE" ] || UI_PORT_VALUE=8080 INGRESS_PORT_VALUE="$(get_env INGRESS_PORT)"; [ -n "$INGRESS_PORT_VALUE" ] || INGRESS_PORT_VALUE=8090 GRAFANA_PORT_VALUE="$(get_env GRAFANA_PORT)"; [ -n "$GRAFANA_PORT_VALUE" ] || GRAFANA_PORT_VALUE=3000 CLICKHOUSE_HTTP_PORT_VALUE="$(get_env CLICKHOUSE_HTTP_PORT)"; [ -n "$CLICKHOUSE_HTTP_PORT_VALUE" ] || CLICKHOUSE_HTTP_PORT_VALUE=8123 CLICKHOUSE_NATIVE_PORT_VALUE="$(get_env CLICKHOUSE_NATIVE_PORT)"; [ -n "$CLICKHOUSE_NATIVE_PORT_VALUE" ] || CLICKHOUSE_NATIVE_PORT_VALUE=9000 # Validate the final config before pulling/building anything. echo "Prüfe Compose-Konfiguration ..." docker compose config >/dev/null echo "Baue und starte Greenfield SIEM ..." docker compose build --pull docker compose up -d --remove-orphans http_ok() { url="$1" if command -v curl >/dev/null 2>&1; then curl -fsS "$url" >/dev/null 2>&1; return $?; fi if command -v wget >/dev/null 2>&1; then wget -q -O /dev/null "$url" >/dev/null 2>&1; return $?; fi return 2 } echo "Prüfe Readiness ..." i=0 while [ "$i" -lt 90 ]; do api_ok=0; ingress_ok=0; ch_ok=0; garage_ok=0; grafana_ok=0 http_ok "http://127.0.0.1:${UI_PORT_VALUE}/readyz" && api_ok=1 || true http_ok "http://127.0.0.1:${INGRESS_PORT_VALUE}/readyz" && ingress_ok=1 || true docker compose exec -T clickhouse clickhouse-client --user "${CLICKHOUSE_USER_VALUE}" --password "${CLICKHOUSE_PASSWORD_VALUE}" --query 'SELECT 1' >/dev/null 2>&1 && ch_ok=1 || true docker compose exec -T garage /garage status >/dev/null 2>&1 && garage_ok=1 || true http_ok "http://127.0.0.1:${GRAFANA_PORT_VALUE}/api/health" && grafana_ok=1 || true if [ "$api_ok" -eq 1 ] && [ "$ingress_ok" -eq 1 ] && [ "$ch_ok" -eq 1 ] && [ "$garage_ok" -eq 1 ] && [ "$grafana_ok" -eq 1 ]; then break fi i=$((i+1)) sleep 2 done if [ "$i" -ge 90 ]; then echo "Mindestens ein Dienst wurde nicht bereit. Diagnose:" >&2 docker compose ps >&2 || true echo >&2 docker compose logs --tail=120 garage clickhouse clickhouse-schema postgres postgres-schema redpanda redpanda-init ingress processor detector api grafana >&2 || true exit 1 fi echo echo "Greenfield SIEM läuft." echo "UI: http://127.0.0.1:${UI_PORT_VALUE}/ui" echo "Ingress: http://127.0.0.1:${INGRESS_PORT_VALUE}/ingest" echo "Grafana: http://127.0.0.1:${GRAFANA_PORT_VALUE}/" echo "Grafana-Login: $(get_env GRAFANA_ADMIN_USER) / $(get_env GRAFANA_ADMIN_PASSWORD)" echo "UI-Login: ${UI_USERNAME_VALUE} / ${UI_PASSWORD_VALUE}" echo "ClickHouse: user=${CLICKHOUSE_USER_VALUE} db=${CLICKHOUSE_DB_VALUE} http=127.0.0.1:${CLICKHOUSE_HTTP_PORT_VALUE} native=127.0.0.1:${CLICKHOUSE_NATIVE_PORT_VALUE}" echo "Enrollment-Key: ${ENROLLMENT_KEY_VALUE}" echo "Alle Parameter stehen vollständig in .env."