
LeitWacht v1.21.0
Leitwacht control plane — безопасность выполнения для GitLab Runner CI/CD: создание политик, мультиарендность, аудит, интеграция с GitLab
GitLab Runner Leitwacht
Принудительное соблюдение сетевого исходящего трафика для контейнеров CI/CD GitLab Runner. Leitwacht предотвращает атаки на цепочку поставок, связанные с эксфильтрацией секретов, контролируя доступ каждого контейнера задания к сети.
Как это работает
Leitwacht — это автономный агент, работающий вместе с GitLab Runner на каждом узле. Он перехватывает события запуска контейнеров, подключает примитивы принуждения и сообщает всю сетевую активность в центральный бэкенд. Не требуется никаких изменений в GitLab Runner.
Стек принуждения (на контейнер)
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)
Общий доступ на уровне подов (Kubernetes)
Все контейнеры в поде K8s используют одно сетевое пространство имен. Leitwacht создает один NetnsContext на под, который владеет общим состоянием (DNS-прокси, правила nftables, карты eBPF). Каждый контейнер получает собственное присоединение cgroup-skb, чтобы ни один соседний контейнер не мог обойти фильтр.
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. |
+------------------+ +------------------+
Архитектура
Компоненты
| Компонент | Описание |
|---|---|
Agent (cmd/agent) | DaemonSet на узлах раннеров. Следит за containerd для контейнеров заданий, подключает eBPF + DNS-прокси, сообщает о нарушениях. Требует root + Linux. |
Server (cmd/server) | Центральный бэкенд. REST API для фронтенда, gRPC для агентов. Хранит политики, нарушения, базовые линии. Postgres или SQLite. |
Frontend (frontend/) | SvelteKit SPA. Управление политиками, просмотр нарушений, управление базовыми линиями, обнаружение аномалий, журнал аудита. |
Ключевые пакеты
Этот репозиторий содержит EE сервер и агента под лицензией EE. Плоскость данных принудительного соблюдения сетевых правил, показанная на диаграммах выше (eBPF, DNS-прокси, nftables, наблюдатель containerd), находится в отдельном модуле Community Edition под лицензией MPL-2.0
leitwacht-agent, который этот репозиторий использует как зависимость;cmd/agent+ee/— это сборка EE, которая повторно использует его и подключает к бэкенду.
| Пакет | Назначение |
|---|---|
internal/agentgrpc | gRPC сервер: принимает отчеты о нарушениях от агентов, предоставляет потоки политик и правил |
internal/api | Обработчики REST API, промежуточное ПО аутентификации, разграничение RBAC |
internal/auth | Аутентификация OIDC (GitLab), сессии, CSRF |
internal/store | Доступ к данным через GORM (проекты, нарушения, базовые линии) + ClickHouse |
internal/license, internal/licensemgr | Проверка лицензии EE (Ed25519 JWS) + жизненный цикл и шлюз конфигурации (см. LICENSE) |
internal/notify | Доставка оповещений (email/SES, Slack) + шаблоны |
internal/retention | Хранение данных / очистка |
internal/mcp, internal/mcpauth | MCP сервер + аутентификация |
ee/reporter | gRPC клиент агента EE, отправляющий нарушения и уведомления в бэкенд |
ee/policycache, ee/rulewatcher, ee/rulestore | Кэш политик агента EE + наблюдение/хранение правил (BBolt) |
contract/ | Общий Go модуль: исходники .proto + сгенерированные типы проводов agentpb |
Развертывание
Предварительные требования
- Кластер Kubernetes с рантаймом containerd
- GitLab Runner, использующий исполнитель Kubernetes
FF_NETWORK_PER_BUILD=trueна раннерах (каждое задание получает собственное сетевое пространство имен)
Helm-чарты
Server (helm/) — опубликован как OCI-чарт. Требует Postgres + ClickHouse и несколько предварительно созданных секретов (DSN БД, ключи аутентификации). Полное руководство см. в docs/DEPLOY.md.
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"
Режимы принуждения
| Режим | Политика DNS | Фильтр eBPF | Сценарий использования |
|---|---|---|---|
| audit | Пересылать все, записывать нарушения | Разрешать все, записывать блокировки | Построение базовой линии, начальное развертывание |
| block | NXDOMAIN для не разрешенных | Отбрасывать IP, не входящие в белый список | Применение в продакшене |
| allow | Без принуждения | Без принуждения | Проекты, освобожденные от контроля |
Разработка
Сборка
# 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/
Плоскость данных eBPF не находится в этом репозитории — она находится в модуле
leitwacht-agent(CE). Сгенерируйте заново его скелеты BPF там (см. 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
Документация
| Документ | Содержимое |
|---|---|
| docs/DEPLOY.md | Полное руководство по развертыванию в Kubernetes (секреты, ClickHouse, миграции, установка лицензии, публикация) |
| docs/MULTI_ORG.md | Настройка для нескольких организаций / мультитенантность |
| LICENSE, NOTICE | Условия BSL 1.1 (смена лицензии на MPL-2.0) и разделение лицензий CE/EE |
leitwacht-agent | Агент плоскости данных Community Edition под лицензией MPL-2.0 (принуждение через eBPF / DNS-прокси / nftables) — движок, лежащий в основе приведенных выше диаграмм |