
LeitWacht v1.21.0
Leitwacht Control Plane – Laufzeitsicherheit für GitLab Runner CI/CD: Richtlinienerstellung, Multi-Tenancy, Audit, GitLab-Integration
GitLab Runner Leitwacht
Netzwerk-Egress-Erzwingung für GitLab Runner CI/CD-Container. Leitwacht verhindert Supply-Chain-Angriffe, die Secrets exfiltrieren, indem gesteuert wird, worauf jeder Job-Container im Netzwerk zugreifen kann.
So funktioniert es
Leitwacht ist ein eigenständiger Agent, der auf jedem Knoten zusammen mit GitLab Runner läuft. Er fängt Container-Start-Ereignisse ab, bindet Erzwingungsprimitive ein und meldet alle Netzwerkaktivitäten an ein zentrales Backend. Es sind keine Änderungen an GitLab Runner erforderlich.
Erzwingungs-Stack (pro Container)
Container-Prozess
|
| DNS-Abfrage (Port 53)
v
nftables-Weiterleitung ──> DNS-Proxy (:15353)
|
|── Allowlist-Prüfung (FQDN-Übereinstimmung)
|── Blockieren: NXDOMAIN-Antwort
|── Erlauben: Weiterleitung an Upstream, hinzufügen der aufgelösten IPs zur eBPF-Allow-Map
|── Aufzeichnung: Domain, aufgelöste IPs, PID, Prozessname
v
Upstream-DNS (CoreDNS / extern)
Container-Prozess
|
| TCP/UDP-Egress (beliebiger Port)
v
eBPF cgroup-skb/egress-Filter
|── IP in Allow-Map? ──> ERLAUBEN (gezählt)
|── IP in vertrauenswürdigen CIDRs? ──> ERLAUBEN (Pod-Netz, Service-Netz)
|── Cloud-Metadaten (169.254.169.254)? ──> BLOCKIEREN (immer)
|── DoT (Port 853)? ──> BLOCKIEREN (immer)
|── IPv6 (nicht Loopback)? ──> BLOCKIEREN (immer)
|── Standard ──> BLOCKIEREN (Drop-Ereignis an Ringbuf)
Pod-Level-Sharing (Kubernetes)
Alle Container in einem K8s-Pod teilen sich einen Netzwerk-Namensraum. Leitwacht erstellt einen einzigen NetnsContext pro Pod, der den gemeinsamen Zustand (DNS-Proxy, nftables-Regeln, eBPF-Maps) besitzt. Jeder Container erhält seine eigene cgroup-skb-Anbindung, sodass kein Geschwistercontainer den Filter umgehen kann.
Pod (gemeinsamer Netns)
+-- NetnsContext (1 pro Pod)
| +-- DNS-Proxy (1 Goroutine-Paar)
| +-- nftables-Weiterleitungsregeln
| +-- eBPF-Programm + Maps
| +-- Ringbuf-Leser
|
+-- Container A
| +-- CgroupLink (eBPF an A's Cgroup gebunden)
| +-- ViolationRecorder
|
+-- Container B
+-- CgroupLink (eBPF an B's Cgroup gebunden)
+-- ViolationRecorder
Agent-Backend-Kommunikation
+------------------+ gRPC +------------------+
| Leitwacht-Agent | ──────────────────────> | Leitwacht-Server |
| (pro Knoten) | | (zentral) |
| | GetPolicy(project) | |
| - Watcher | <───────────────────── | - Policy-Speicher|
| - Enforcer | | - Violation-DB |
| - DNS-Proxy | ReportViolations(job) | - REST-API |
| - Reporter | ──────────────────────> | - Frontend SPA |
| - Honeypot LSM | | - Benachrichtigungen|
| | Heartbeat() | - Baselines |
| | ──────────────────────> | - Anomalieerkenn.|
+------------------+ +------------------+
Architektur
Komponenten
| Komponente | Beschreibung |
|---|---|
Agent (cmd/agent) | DaemonSet auf Runner-Knoten. Überwacht containerd auf Job-Container, bindet eBPF + DNS-Proxy ein, meldet Verstöße. Erfordert root + Linux. |
Server (cmd/server) | Zentrales Backend. REST-API für das Frontend, gRPC für Agenten. Speichert Richtlinien, Verstöße, Baselines. Postgres oder SQLite. |
Frontend (frontend/) | SvelteKit SPA. Richtlinienverwaltung, Verstoßanzeige, Baseline-Verwaltung, Anomalieerkennung, Prüfprotokoll. |
Wichtige Pakete
Dieses Repository ist der EE-Server + der EE-lizenzierte Agent. Die Netzwerk-Erzwingungs-Datenebene, die in den obigen Diagrammen gezeigt wird (eBPF, DNS-Proxy, nftables, der containerd-Watcher), befindet sich im separaten, MPL-2.0 Community-Edition-Modul
leitwacht-agent, das dieses Repository als Abhängigkeit nutzt;cmd/agent+ee/sind der EE-Build, der es wiederverwendet und mit dem Backend verbindet.
| Paket | Zweck |
|---|---|
internal/agentgrpc | gRPC-Server: nimmt Agent-Meldungen über Verstöße entgegen, stellt Richtlinien- und Regel-Streams bereit |
internal/api | REST-API-Handler, Authentifizierungs-Middleware, RBAC-Bereichsbegrenzung |
internal/auth | OIDC (GitLab)-Authentifizierung, Sitzungen, CSRF |
internal/store | GORM-Datenzugriff (Projekte, Verstöße, Baselines) + ClickHouse |
internal/license, internal/licensemgr | EE-Lizenzprüfung (Ed25519 JWS) + Lebenszyklus- und Konfigurations-Gate (siehe LIZENZ) |
internal/notify | Alarmzustellung (E-Mail/SES, Slack) + Vorlagen |
internal/retention | Datenaufbewahrung / Bereinigung |
internal/mcp, internal/mcpauth | MCP-Server + Authentifizierung |
ee/reporter | EE-Agent gRPC-Client, der Verstöße und Hinweise an das Backend sendet |
ee/policycache, ee/rulewatcher, ee/rulestore | EE-Agent-Richtlinien-Cache + Regelbeobachter/-Speicher (BBolt) |
contract/ | Gemeinsames Go-Modul: .proto-Quellen + generierte agentpb-Drahttypen |
Bereitstellung
Voraussetzungen
- Kubernetes-Cluster mit containerd-Laufzeit
- GitLab Runner mit Kubernetes Executor
FF_NETWORK_PER_BUILD=trueauf den Runnern (jeder Job erhält einen eigenen Netzwerk-Namensraum)
Helm-Charts
Server (helm/) — als OCI-Chart veröffentlicht. Benötigt Postgres + ClickHouse und
einige vorab erstellte Secrets (DB-DSN, Auth-Keys). Siehe
docs/DEPLOY.md für das vollständige Runbook.
helm install leitwacht \
oci://registry.gitlab.com/leitwacht/leitwacht/charts/leitwacht --version 1.18.0 \
--set image.tag=1.18.0 \
--set ingress.domain=example.com --set ingress.hosts[0]=leitwacht.example.com \
--set grpcIngress.domain=example.com --set grpcIngress.hosts[0]=grpc-leitwacht.example.com \
--set app.oidcIssuer=https://gitlab.com --set app.oidcClientId=<oauth-app-id> \
--set clickhouse.embedded.password=<password>
Agent (helm/agent/):
helm install leitwacht-agent \
oci://registry.gitlab.com/leitwacht/leitwacht/charts/leitwacht-agent --version 1.18.0 \
--set agent.backendAddr="leitwacht.namespace.svc.cluster.local:9090" \
--set agent.defaultAction=audit \
--set agent.dnsUpstream="10.96.0.10:53,1.1.1.1:53" \
--set agent.trustedCIDRs="10.244.0.0/16,10.96.0.0/12"
Erzwingungsmodi
| Modus | DNS-Richtlinie | eBPF-Filter | Anwendungsfall |
|---|---|---|---|
| audit | Alle weiterleiten, Verstöße aufzeichnen | Alle erlauben, Abweisungen aufzeichnen | Baseline-Erstellung, Erstausrollen |
| block | NXDOMAIN für nicht erlaubte | Nicht erlaubte IPs abweisen | Produktionserzwingung |
| allow | Keine Erzwingung | Keine Erzwingung | Ausgenommene Projekte |
Entwicklung
Build
# Backend
go build ./cmd/agent
go build ./cmd/server
# Frontend
cd frontend && pnpm install && pnpm build
# gRPC-Vertragsneugenerierung (edit contract/proto/**, dann):
cd contract && buf generate proto/
Die eBPF-Datenebene befindet sich nicht in diesem Repository – sie lebt im
leitwacht-agent(CE)-Modul. Generieren Sie dort ihre BPF-Skelette neu (siehe Makefile dieses Repos:make ebpf-generate).
Test
# Unit-Tests (beliebige Plattform)
go test ./...
# Integrations- / e2e-Tests, die Sidecars benötigen (ClickHouse + Postgres) befinden sich unter
# internal/store, internal/api, internal/agentgrpc – diese zuerst starten.
# Frontend
cd frontend && pnpm test
Lint
# Backend
golangci-lint run ./...
# Frontend
cd frontend && pnpm lint
Dokumentation
| Dokument | Inhalt |
|---|---|
| docs/DEPLOY.md | Vollständiges Kubernetes-Bereitstellungs-Runbook (Secrets, ClickHouse, Migrationen, Lizenzinstallation, Exposition) |
| docs/MULTI_ORG.md | Multi-Organisations- / Multi-Mandanten-Setup |
| LICENSE, NOTICE | BSL 1.1-Bedingungen (Wechsellizenz: MPL-2.0) und die CE/EE-Lizenzaufteilung |
leitwacht-agent | Das MPL-2.0 Community-Edition-Datenebenen-Agent (eBPF / DNS-Proxy / nftables-Erzwingung) – die Engine hinter den obigen Diagrammen |