Retour aux mises à jour
New releaseAug 20, 2026

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

Partager

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

ComposantDescription
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.

PaquetObjectif
internal/agentgrpcServeur gRPC : ingère les rapports de violation des agents, diffuse les flux de politiques et de règles
internal/apiGestionnaires d'API REST, middleware d'authentification, portée RBAC
internal/authAuthentification OIDC (GitLab), sessions, CSRF
internal/storeAccès aux données GORM (projets, violations, bases de référence) + ClickHouse
internal/license, internal/licensemgrVérification de licence EE (Ed25519 JWS) + cycle de vie et porte de configuration (voir LICENSE)
internal/notifyDistribution d'alertes (email/SES, Slack) + modèles
internal/retentionRétention des données / élagage
internal/mcp, internal/mcpauthServeur MCP + authentification
ee/reporterClient gRPC de l'agent EE poussant les violations et avis vers le backend
ee/policycache, ee/rulewatcher, ee/rulestoreCache 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=true sur 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

ModePolitique DNSFiltre eBPFCas d'utilisation
auditTransmettre tout, enregistrer les violationsAutoriser tout, enregistrer les rejetsÉtablissement de base de référence, déploiement initial
blockNXDOMAIN pour les non-autorisés en liste blancheRejeter les IP non autoriséesContrôle en production
allowAucun contrôleAucun contrôleProjets 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

DocumentContenu
docs/DEPLOY.mdManuel complet de déploiement Kubernetes (secrets, ClickHouse, migrations, installation de licence, exposition)
docs/MULTI_ORG.mdConfiguration multi-organisation / multi-locataire
LICENSE, NOTICEConditions BSL 1.1 (Licence de changement : MPL-2.0) et la répartition des licences CE/EE
leitwacht-agentL'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

Catégories