
LeitWacht v1.21.0
Leitwacht control plane — seguridad en tiempo de ejecución para GitLab Runner CI/CD: creación de políticas, multi-inquilino, auditoría, integración con GitLab
GitLab Runner Leitwacht
Control de salida de red para contenedores CI/CD de GitLab Runner. Leitwacht previene ataques a la cadena de suministro que exfiltran secretos, controlando lo que cada contenedor de trabajo puede acceder en la red.
Cómo Funciona
Leitwacht es un agente independiente que se ejecuta junto a GitLab Runner en cada nodo. Intercepta eventos de inicio de contenedores, adjunta primitivas de control e informa toda la actividad de red a un backend central. No se requieren modificaciones en GitLab Runner.
Pila de Control (por contenedor)
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)
Compartición a Nivel de Pod (Kubernetes)
Todos los contenedores en un pod de K8s comparten un único espacio de nombres de red. Leitwacht crea un solo NetnsContext por pod que posee el estado compartido (proxy DNS, reglas nftables, mapas eBPF). Cada contenedor obtiene su propio adjunto cgroup-skb para que ningún hermano pueda eludir el filtro.
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
Comunicación Agente-Backend
+------------------+ 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. |
+------------------+ +------------------+
Arquitectura
Componentes
| Componente | Descripción |
|---|---|
Agente (cmd/agent) | DaemonSet en nodos runner. Observa containerd en busca de contenedores de trabajo, adjunta eBPF + proxy DNS, reporta violaciones. Requiere root + Linux. |
Servidor (cmd/server) | Backend central. API REST para el frontend, gRPC para agentes. Almacena políticas, violaciones, líneas base. Postgres o SQLite. |
Frontend (frontend/) | SPA en SvelteKit. Gestión de políticas, visor de violaciones, gestión de líneas base, detección de anomalías, registro de auditoría. |
Paquetes Clave
Este repositorio es el servidor EE + el agente con licencia EE. El plano de datos de control de red mostrado en los diagramas anteriores (eBPF, proxy DNS, nftables, el observador de containerd) reside en el módulo separado de Community Edition con licencia MPL-2.0
leitwacht-agent, que este repositorio consume como dependencia;cmd/agent+ee/son la compilación EE que lo reutiliza y lo conecta al backend.
| Paquete | Propósito |
|---|---|
internal/agentgrpc | Servidor gRPC: recibe informes de violaciones de agentes, sirve flujos de políticas y reglas |
internal/api | Manejadores de API REST, middleware de autenticación, ámbito RBAC |
internal/auth | Autenticación OIDC (GitLab), sesiones, CSRF |
internal/store | Acceso a datos GORM (proyectos, violaciones, líneas base) + ClickHouse |
internal/license, internal/licensemgr | Verificación de licencia EE (Ed25519 JWS) + ciclo de vida y puerta de configuración (ver LICENSE) |
internal/notify | Entrega de alertas (correo/SES, Slack) + plantillas |
internal/retention | Retención/poda de datos |
internal/mcp, internal/mcpauth | Servidor MCP + autenticación |
ee/reporter | Cliente gRPC del agente EE que envía violaciones y avisos al backend |
ee/policycache, ee/rulewatcher, ee/rulestore | Caché de políticas del agente EE + observación/almacenamiento de reglas (BBolt) |
contract/ | Módulo Go compartido: fuentes .proto + tipos de cable agentpb generados |
Despliegue
Requisitos Previos
- Clúster Kubernetes con runtime containerd
- GitLab Runner usando el ejecutor Kubernetes
FF_NETWORK_PER_BUILD=trueen los runners (cada trabajo obtiene su propio espacio de nombres de red)
Helm Charts
Servidor (helm/) — publicado como un chart OCI. Necesita Postgres + ClickHouse y algunos secretos precreados (DSN DB, claves de autenticación). Consulte docs/DEPLOY.md para el runbook completo.
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>
Agente (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"
Modos de Control
| Modo | Política DNS | Filtro eBPF | Caso de Uso |
|---|---|---|---|
| audit | Reenviar todo, registrar violaciones | Permitir todo, registrar descartes | Construcción de línea base, despliegue inicial |
| block | NXDOMAIN para no permitidos | Descartar IPs no permitidas | Control en producción |
| allow | Sin control | Sin control | Proyectos exentos |
Desarrollo
Compilación
# Backend
go build ./cmd/agent
go build ./cmd/server
# Frontend
cd frontend && pnpm install && pnpm build
# Regeneración de contrato gRPC (editar contract/proto/**, luego):
cd contract && buf generate proto/
El plano de datos eBPF no está en este repositorio — reside en el módulo
leitwacht-agent(CE). Regenerar sus esqueletos BPF allí (ver Makefile de ese repo:make ebpf-generate).
Pruebas
# Pruebas unitarias (cualquier plataforma)
go test ./...
# Pruebas de integración / e2e que requieren sidecars (ClickHouse + Postgres) viven en
# internal/store, internal/api, internal/agentgrpc — iniciarlos primero.
# Frontend
cd frontend && pnpm test
Linting
# Backend
golangci-lint run ./...
# Frontend
cd frontend && pnpm lint
Documentación
| Documento | Contenido |
|---|---|
| docs/DEPLOY.md | Runbook completo de despliegue en Kubernetes (secretos, ClickHouse, migraciones, instalación de licencia, exposición) |
| docs/MULTI_ORG.md | Configuración multi-org / multi-tenant |
| LICENSE, NOTICE | Términos BSL 1.1 (Licencia de Cambio: MPL-2.0) y la división de licencias CE/EE |
leitwacht-agent | El agente de plano de datos de Community Edition MPL-2.0 (control eBPF / proxy DNS / nftables) — el motor detrás de los diagramas anteriores |