
New releaseAug 20, 2026
Leitwacht control plane — runtime security for GitLab Runner CI/CD: policy authoring, multi-tenancy, audit, GitLab integration
Network egress enforcement for GitLab Runner CI/CD containers. Leitwacht prevents supply chain attacks from exfiltrating secrets by controlling what each job container can access on the network.
Leitwacht is a standalone agent that runs alongside GitLab Runner on each node. It intercepts container start events, attaches enforcement primitives, and reports all network activity to a central backend. No modifications to GitLab Runner required.
Container Process
|
| DNS query (port 53)
v
nftables redirect ──> DNS Proxy (:15353)
|
|── Allowlist check (FQDN match)
|── Block: NXDOMAIN response
|── Allow: forward to upstream, add resolved IPs to eBPF allow map
|── Record: domain, resolved IPs, PID, process name
v
Upstream DNS (CoreDNS / external)
Container Process
|
| TCP/UDP egress (any port)
v
eBPF cgroup-skb/egress filter
|── IP in allow map? ──> ALLOW (counted)
|── IP in trusted CIDRs? ──> ALLOW (pod net, svc net)
|── Cloud metadata (169.254.169.254)? ──> BLOCK (always)
|── DoT (port 853)? ──> BLOCK (always)
|── IPv6 (non-loopback)? ──> BLOCK (always)
|── Default ──> BLOCK (drop event to ringbuf)
All containers in a K8s pod share one network namespace. Leitwacht creates a single NetnsContext per pod that owns the shared state (DNS proxy, nftables rules, eBPF maps). Each container gets its own cgroup-skb attachment so no sibling can bypass the filter.
Pod (shared netns)
+-- NetnsContext (1 per pod)
| +-- DNS Proxy (1 goroutine pair)
| +-- nftables redirect rules
| +-- eBPF program + maps
| +-- Ringbuf reader
|
+-- Container A
| +-- CgroupLink (eBPF attached to A's cgroup)
| +-- ViolationRecorder
|
+-- Container B
+-- CgroupLink (eBPF attached to B's cgroup)
+-- ViolationRecorder
+------------------+ gRPC +------------------+
| Leitwacht Agent | ──────────────────────> | Leitwacht Server |
| (per node) | | (central) |
| | GetPolicy(project) | |
| - Watcher | <───────────────────── | - Policy store |
| - Enforcer | | - Violation DB |
| - DNS Proxy | ReportViolations(job) | - REST API |
| - Reporter | ──────────────────────> | - Frontend SPA |
| - Honeypot LSM | | - Notifications |
| | Heartbeat() | - Baselines |
| | ──────────────────────> | - Anomaly det. |
+------------------+ +------------------+
| Component | Description |
|---|---|
Agent (cmd/agent) | DaemonSet on runner nodes. Watches containerd for job containers, attaches eBPF + DNS proxy, reports violations. Requires root + Linux. |
Server (cmd/server) | Central backend. REST API for the frontend, gRPC for agents. Stores policies, violations, baselines. Postgres or SQLite. |
Frontend (frontend/) | SvelteKit SPA. Policy management, violation viewer, baseline management, anomaly detection, audit log. |
This repository is the EE server + EE-licensed agent. The network-enforcement data plane shown in the diagrams above (eBPF, DNS proxy, nftables, the containerd watcher) lives in the separate, MPL-2.0 Community-Edition module
leitwacht-agent, which this repo consumes as a dependency;cmd/agent+ee/are the EE build that reuses it and wires it to the backend.
| Package | Purpose |
|---|---|
internal/agentgrpc | gRPC server: ingests agent violation reports, serves policy + rule streams |
internal/api | REST API handlers, auth middleware, RBAC scoping |
internal/auth | OIDC (GitLab) authentication, sessions, CSRF |
internal/store | GORM data access (projects, violations, baselines) + ClickHouse |
internal/license, internal/licensemgr | EE license verify (Ed25519 JWS) + lifecycle and config gate (see LICENSE) |
internal/notify | Alert delivery (email/SES, Slack) + templates |
internal/retention | Data-retention / pruning |
internal/mcp, internal/mcpauth | MCP server + auth |
ee/reporter | EE agent gRPC client pushing violations + advisories to the backend |
ee/policycache, ee/rulewatcher, ee/rulestore | EE agent policy cache + rule watch/store (BBolt) |
contract/ | Shared Go module: .proto sources + generated agentpb wire types |
FF_NETWORK_PER_BUILD=true on runners (each job gets its own network namespace)Server (helm/) — published as an OCI chart. Needs Postgres + ClickHouse and
a few pre-created secrets (DB DSN, auth keys). See
docs/DEPLOY.md for the full 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"
| Mode | DNS Policy | eBPF Filter | Use Case |
|---|---|---|---|
| audit | Forward all, record violations | Allow all, record drops | Baseline building, initial rollout |
| block | NXDOMAIN for non-allowlisted | Drop non-allowlisted IPs | Production enforcement |
| allow | No enforcement | No enforcement | Exempted projects |
# Backend
go build ./cmd/agent
go build ./cmd/server
# Frontend
cd frontend && pnpm install && pnpm build
# gRPC contract regeneration (edit contract/proto/**, then):
cd contract && buf generate proto/
The eBPF data plane is not in this repo — it lives in the
leitwacht-agent(CE) module. Regenerate its BPF skeletons there (see that repo's Makefile:make ebpf-generate).
# Unit tests (any platform)
go test ./...
# Integration / e2e tests requiring sidecars (ClickHouse + Postgres) live under
# internal/store, internal/api, internal/agentgrpc — bring those up first.
# Frontend
cd frontend && pnpm test
# Backend
golangci-lint run ./...
# Frontend
cd frontend && pnpm lint
| Document | Contents |
|---|---|
| docs/DEPLOY.md | Full Kubernetes deployment runbook (secrets, ClickHouse, migrations, license install, exposure) |
| docs/MULTI_ORG.md | Multi-org / multi-tenant setup |
| LICENSE, NOTICE | BSL 1.1 terms (Change License: MPL-2.0) and the CE/EE license split |
leitwacht-agent | The MPL-2.0 Community-Edition data-plane agent (eBPF / DNS proxy / nftables enforcement) — the engine behind the diagrams above |