
LeitWacht v1.21.0
Leitwacht plan de contrôle — sécurité d'exécution pour GitLab Runner CI/CD : rédaction de politiques, multi-locataire, audit, intégration GitLab
GitLab Runner Leitwacht
Contrôle du trafic sortant réseau pour les conteneurs CI/CD de GitLab Runner. Leitwacht empêche les attaques de la chaîne d'approvisionnement en exfiltrant des secrets en contrôlant ce à quoi chaque conteneur de tâche peut accéder sur le réseau.
Comment ça marche
Leitwacht est un agent autonome qui s'exécute aux côtés de GitLab Runner sur chaque nœud. Il intercepte les événements de démarrage de conteneur, attache des primitives de mise en application et signale toute activité réseau à un backend central. Aucune modification de GitLab Runner n'est nécessaire.
Pile de contrôle (par conteneur)
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)
Partage au niveau du Pod (Kubernetes)
Tous les conteneurs d'un pod Kubernetes partagent un même espace de noms réseau. Leitwacht crée un seul NetnsContext par pod qui possède l'état partagé (proxy DNS, règles nftables, cartes eBPF). Chaque conteneur reçoit sa propre attachement cgroup-skb afin qu'aucun frère ne puisse contourner le filtre.
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
Communication Agent-Serveur
+------------------+ 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. |
+------------------+ +------------------+
Architecture
Composants
| Composant | Description |
|---|---|
Agent (cmd/agent) | DaemonSet sur les nœuds d'exécution. Surveille containerd pour les conteneurs de tâches, attache eBPF + proxy DNS, signale les violations. Nécessite root + Linux. |
Serveur (cmd/server) | Backend central. API REST pour le frontend, gRPC pour les agents. Stocke les politiques, violations, bases de référence. Postgres ou SQLite. |
Frontend (frontend/) | SPA SvelteKit. Gestion des politiques, visualisation des violations, gestion des bases de référence, détection d'anomalies, journal d'audit. |
Paquets clés
Ce dépôt est le serveur EE + l'agent sous licence EE. Le plan de données de mise en application réseau montré dans les diagrammes ci-dessus (eBPF, proxy DNS, nftables, le watcher containerd) vit dans le module distinct sous licence MPL-2.0 Community-Edition
leitwacht-agent, que ce dépôt consomme comme dépendance ;cmd/agent+ee/sont la construction EE qui le réutilise et le connecte au backend.
| Paquet | Objectif |
|---|---|
internal/agentgrpc | Serveur gRPC : ingère les rapports de violation des agents, diffuse les flux de politiques et de règles |
internal/api | Gestionnaires d'API REST, middleware d'authentification, portée RBAC |
internal/auth | Authentification OIDC (GitLab), sessions, CSRF |
internal/store | Accès aux données GORM (projets, violations, bases de référence) + ClickHouse |
internal/license, internal/licensemgr | Vérification de licence EE (Ed25519 JWS) + cycle de vie et porte de configuration (voir LICENSE) |
internal/notify | Distribution d'alertes (email/SES, Slack) + modèles |
internal/retention | Rétention des données / élagage |
internal/mcp, internal/mcpauth | Serveur MCP + authentification |
ee/reporter | Client gRPC de l'agent EE poussant les violations et avis vers le backend |
ee/policycache, ee/rulewatcher, ee/rulestore | Cache de politique de l'agent EE + surveillance/stockage des règles (BBolt) |
contract/ | Module Go partagé : sources .proto + types filaires agentpb générés |
Déploiement
Prérequis
- Cluster Kubernetes avec runtime containerd
- GitLab Runner utilisant l'exécuteur Kubernetes
FF_NETWORK_PER_BUILD=truesur les runners (chaque tâche obtient son propre espace de noms réseau)
Charts Helm
Serveur (helm/) — publié sous forme de chart OCI. Nécessite Postgres + ClickHouse et
quelques secrets pré‑créés (DSN de BDD, clés d'authentification). Voir
docs/DEPLOY.md pour le manuel complet.
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"
Modes de contrôle
| Mode | Politique DNS | Filtre eBPF | Cas d'utilisation |
|---|---|---|---|
| audit | Transmettre tout, enregistrer les violations | Autoriser tout, enregistrer les rejets | Établissement de base de référence, déploiement initial |
| block | NXDOMAIN pour les non-autorisés en liste blanche | Rejeter les IP non autorisées | Contrôle en production |
| allow | Aucun contrôle | Aucun contrôle | Projets exemptés |
Développement
Compilation
# Backend
go build ./cmd/agent
go build ./cmd/server
# Frontend
cd frontend && pnpm install && pnpm build
# Régénération du contrat gRPC (modifier contract/proto/**, puis) :
cd contract && buf generate proto/
Le plan de données eBPF n'est pas dans ce dépôt — il vit dans le module
leitwacht-agent(CE). Régénérez ses squelettes BPF là-bas (voir le Makefile de ce dépôt :make ebpf-generate).
Tests
# Tests unitaires (toute plateforme)
go test ./...
# Tests d'intégration / e2e nécessitant des sidecars (ClickHouse + Postgres) sous
# internal/store, internal/api, internal/agentgrpc — démarrer ceux-ci d'abord.
# Frontend
cd frontend && pnpm test
Lint
# Backend
golangci-lint run ./...
# Frontend
cd frontend && pnpm lint
Documentation
| Document | Contenu |
|---|---|
| docs/DEPLOY.md | Manuel complet de déploiement Kubernetes (secrets, ClickHouse, migrations, installation de licence, exposition) |
| docs/MULTI_ORG.md | Configuration multi-organisation / multi-locataire |
| LICENSE, NOTICE | Conditions BSL 1.1 (Licence de changement : MPL-2.0) et la répartition des licences CE/EE |
leitwacht-agent | L'agent de plan de données Community-Edition MPL-2.0 (eBPF / proxy DNS / contrôle nftables) — le moteur derrière les diagrammes ci-dessus |