$ErrorActionPreference = "Stop" Set-Location $PSScriptRoot if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { throw "Docker fehlt." } docker compose version | Out-Null function Hex([int]$bytes) { $b = New-Object byte[] $bytes [Security.Cryptography.RandomNumberGenerator]::Fill($b) return ([BitConverter]::ToString($b)).Replace("-","").ToLower() } function Read-EnvFile([string]$path) { $map = [ordered]@{} if (Test-Path $path) { foreach ($line in [IO.File]::ReadAllLines($path)) { if ($line -match '^([^#=][^=]*)=(.*)$') { $map[$matches[1]] = $matches[2] } } } return $map } $template = Read-EnvFile ".env.example" $current = Read-EnvFile ".env" foreach ($k in $template.Keys) { if (-not $current.Contains($k)) { $current[$k] = $template[$k] } } $presetEnrollment = $env:ENROLLMENT_KEY $secretLengths = @{ POSTGRES_PASSWORD=24; CLICKHOUSE_PASSWORD=24; UI_PASSWORD=18; GARAGE_SECRET_KEY=32; GARAGE_RPC_SECRET=32; GARAGE_ADMIN_TOKEN=32; GARAGE_METRICS_TOKEN=32 } foreach ($k in $secretLengths.Keys) { if ([string]::IsNullOrWhiteSpace($current[$k]) -or $current[$k] -eq 'CHANGE_ME') { $current[$k] = Hex $secretLengths[$k] } } if ([string]::IsNullOrWhiteSpace($current['GARAGE_ACCESS_KEY']) -or $current['GARAGE_ACCESS_KEY'] -eq 'CHANGE_ME') { $current['GARAGE_ACCESS_KEY'] = "GK$(Hex 16)" } if (-not [string]::IsNullOrWhiteSpace($presetEnrollment)) { $current['ENROLLMENT_KEY'] = $presetEnrollment } elseif ([string]::IsNullOrWhiteSpace($current['ENROLLMENT_KEY']) -or $current['ENROLLMENT_KEY'] -eq 'CHANGE_ME') { $current['ENROLLMENT_KEY'] = Hex 32 } # Write a complete UTF-8 .env. This fixes partial .env files from earlier releases # and preserves custom keys that are not part of the current template. $lines = New-Object System.Collections.Generic.List[string] $templateKeys = New-Object 'System.Collections.Generic.HashSet[string]' foreach ($line in [IO.File]::ReadAllLines(".env.example")) { if ($line -match '^([^#=][^=]*)=(.*)$') { $key = $matches[1] [void]$templateKeys.Add($key) $lines.Add("$key=$($current[$key])") } else { $lines.Add($line) } } foreach ($key in $current.Keys) { if (-not $templateKeys.Contains([string]$key)) { $lines.Add("$key=$($current[$key])") } } $utf8NoBom = New-Object System.Text.UTF8Encoding($false) [IO.File]::WriteAllLines((Join-Path $PSScriptRoot '.env'), $lines, $utf8NoBom) foreach ($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')) { if ([string]::IsNullOrWhiteSpace($current[$key]) -or $current[$key] -eq 'CHANGE_ME') { throw "Ungueltige .env: $key fehlt oder ist noch CHANGE_ME." } } Write-Host "Pruefe Compose-Konfiguration ..." docker compose config | Out-Null Write-Host "Baue und starte Greenfield SIEM ..." docker compose build --pull docker compose up -d --remove-orphans Write-Host "Pruefe Readiness ..." $healthy = $false for ($i = 0; $i -lt 90; $i++) { $apiOk = $false; $ingressOk = $false; $chOk = $false; $garageOk = $false try { Invoke-WebRequest -UseBasicParsing -TimeoutSec 2 "http://127.0.0.1:$($current.UI_PORT)/readyz" | Out-Null; $apiOk = $true } catch {} try { Invoke-WebRequest -UseBasicParsing -TimeoutSec 2 "http://127.0.0.1:$($current.INGRESS_PORT)/readyz" | Out-Null; $ingressOk = $true } catch {} docker compose exec -T clickhouse clickhouse-client --user $current.CLICKHOUSE_USER --password $current.CLICKHOUSE_PASSWORD --query "SELECT 1" *> $null if ($LASTEXITCODE -eq 0) { $chOk = $true } docker compose exec -T garage /garage status *> $null if ($LASTEXITCODE -eq 0) { $garageOk = $true } if ($apiOk -and $ingressOk -and $chOk -and $garageOk) { $healthy = $true; break } Start-Sleep -Seconds 2 } if (-not $healthy) { Write-Host "Mindestens ein Dienst wurde nicht bereit. Diagnose:" -ForegroundColor Red docker compose ps docker compose logs --tail=120 garage clickhouse clickhouse-schema postgres postgres-schema redpanda redpanda-init ingress processor api throw "Deployment nicht vollstaendig bereit." } Write-Host "" Write-Host "Greenfield SIEM laeuft." Write-Host "UI: http://127.0.0.1:$($current.UI_PORT)/ui" Write-Host "Ingress: http://127.0.0.1:$($current.INGRESS_PORT)/ingest" Write-Host "UI-Login: $($current.UI_USERNAME) / $($current.UI_PASSWORD)" Write-Host "ClickHouse: user=$($current.CLICKHOUSE_USER) db=$($current.CLICKHOUSE_DB) http=127.0.0.1:$($current.CLICKHOUSE_HTTP_PORT) native=127.0.0.1:$($current.CLICKHOUSE_NATIVE_PORT)" Write-Host "Enrollment-Key: $($current.ENROLLMENT_KEY)" Write-Host "Alle Parameter stehen vollstaendig in .env."