@@ -1,88 +1,140 @@
|
||||
#!/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; }
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
if [ ! -f .env ]; then
|
||||
POSTGRES_PASSWORD="$(randhex 24)"
|
||||
CLICKHOUSE_PASSWORD="$(randhex 24)"
|
||||
ENROLLMENT_KEY="${PRESET_ENROLLMENT_KEY:-$(randhex 32)}"
|
||||
UI_PASSWORD="$(randhex 18)"
|
||||
GARAGE_ACCESS_KEY="GK$(randhex 16)"
|
||||
GARAGE_SECRET_KEY="$(randhex 32)"
|
||||
GARAGE_RPC_SECRET="$(randhex 32)"
|
||||
GARAGE_ADMIN_TOKEN="$(randhex 32)"
|
||||
GARAGE_METRICS_TOKEN="$(randhex 32)"
|
||||
cat > .env <<EOT
|
||||
INGRESS_BIND=0.0.0.0
|
||||
INGRESS_PORT=8090
|
||||
UI_BIND=0.0.0.0
|
||||
UI_PORT=8080
|
||||
TENANT_ID=default
|
||||
UI_USERNAME=admin
|
||||
UI_PASSWORD=$UI_PASSWORD
|
||||
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||
CLICKHOUSE_PASSWORD=$CLICKHOUSE_PASSWORD
|
||||
ENROLLMENT_KEY=$ENROLLMENT_KEY
|
||||
GARAGE_ACCESS_KEY=$GARAGE_ACCESS_KEY
|
||||
GARAGE_SECRET_KEY=$GARAGE_SECRET_KEY
|
||||
GARAGE_RPC_SECRET=$GARAGE_RPC_SECRET
|
||||
GARAGE_ADMIN_TOKEN=$GARAGE_ADMIN_TOKEN
|
||||
GARAGE_METRICS_TOKEN=$GARAGE_METRICS_TOKEN
|
||||
GARAGE_BUCKET=siem-raw
|
||||
EVENT_RETENTION_DAYS=90
|
||||
ROLLUP_RETENTION_DAYS=730
|
||||
RAW_RETENTION=720h
|
||||
RAW_ARCHIVE_ENABLED=true
|
||||
KAFKA_RETENTION_MS=86400000
|
||||
KAFKA_PARTITIONS=6
|
||||
DETECTOR_INTERVAL=30s
|
||||
DETECTOR_LOOKBACK=20m
|
||||
UI_QUERY_LIMIT=500
|
||||
EOT
|
||||
chmod 600 .env 2>/dev/null || true
|
||||
|
||||
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 UI_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) val="$(randhex 24)" ;;
|
||||
UI_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
|
||||
set -a; . ./.env; set +a
|
||||
UI_USERNAME="${UI_USERNAME:-admin}"
|
||||
if [ -z "${UI_PASSWORD:-}" ]; then
|
||||
UI_PASSWORD="$(randhex 18)"
|
||||
printf '\nUI_USERNAME=%s\nUI_PASSWORD=%s\n' "$UI_USERNAME" "$UI_PASSWORD" >> .env
|
||||
export UI_USERNAME UI_PASSWORD
|
||||
|
||||
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
|
||||
sed -e "s/__GARAGE_RPC_SECRET__/$GARAGE_RPC_SECRET/g" -e "s/__GARAGE_ADMIN_TOKEN__/$GARAGE_ADMIN_TOKEN/g" -e "s/__GARAGE_METRICS_TOKEN__/$GARAGE_METRICS_TOKEN/g" deploy/garage/garage.toml.template > deploy/garage/garage.toml
|
||||
sed -e "s/__EVENT_RETENTION_DAYS__/${EVENT_RETENTION_DAYS:-90}/g" -e "s/__ROLLUP_RETENTION_DAYS__/${ROLLUP_RETENTION_DAYS:-730}/g" deploy/clickhouse/init.sql.template > deploy/clickhouse/init.sql
|
||||
chmod 600 deploy/garage/garage.toml 2>/dev/null || true
|
||||
|
||||
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 UI_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)"
|
||||
|
||||
# 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
|
||||
|
||||
echo "Prüfe Dienste ..."
|
||||
if command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1; then
|
||||
i=0
|
||||
while [ "$i" -lt 60 ]; do
|
||||
if http_ok "http://127.0.0.1:${UI_PORT:-8080}/readyz" && http_ok "http://127.0.0.1:${INGRESS_PORT:-8090}/readyz"; then break; fi
|
||||
i=$((i+1)); sleep 2
|
||||
done
|
||||
if [ "$i" -ge 60 ]; then echo "Deployment gestartet, Healthcheck noch nicht grün. Prüfe: docker compose ps && docker compose logs" >&2; exit 1; fi
|
||||
else
|
||||
echo "Hinweis: curl/wget fehlt; HTTP-Healthcheck wird übersprungen."
|
||||
docker compose ps
|
||||
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
|
||||
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
|
||||
if [ "$api_ok" -eq 1 ] && [ "$ingress_ok" -eq 1 ] && [ "$ch_ok" -eq 1 ] && [ "$garage_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 api >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Greenfield SIEM läuft."
|
||||
echo "UI: http://127.0.0.1:${UI_PORT:-8080}/ui"
|
||||
echo "Ingress: http://127.0.0.1:${INGRESS_PORT:-8090}/ingest"
|
||||
echo "UI-Login: $UI_USERNAME / $UI_PASSWORD"
|
||||
echo "Enrollment-Key: $ENROLLMENT_KEY"
|
||||
echo "Secrets stehen in .env (Datei schützen und sichern)."
|
||||
echo "UI: http://127.0.0.1:${UI_PORT_VALUE}/ui"
|
||||
echo "Ingress: http://127.0.0.1:${INGRESS_PORT_VALUE}/ingest"
|
||||
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."
|
||||
|
||||
Reference in New Issue
Block a user