
LeitWacht v1.21.0
Leitwacht control plane — GitLab Runner CI/CD 向けのランタイムセキュリティ: ポリシー作成、マルチテナント、監査、GitLab 統合
GitLab Runner Leitwacht
GitLab Runner CI/CDコンテナのネットワーク出口制限。Leitwachtは、各ジョブコンテナがアクセスできるネットワークを制御することで、サプライチェーン攻撃によるシークレットの外部流出を防ぎます。
動作の仕組み
Leitwachtは、各ノード上でGitLab Runnerと並行して動作するスタンドアロンエージェントです。コンテナ起動イベントを傍受し、強制プリミティブをアタッチし、すべてのネットワークアクティビティを中央バックエンドに報告します。GitLab Runnerの変更は不要です。
強制スタック(コンテナごと)
Container Process
|
| DNS query (port 53)
v
nftables redirect ──> DNS Proxy (:15353)
|
|── Allowlistチェック(FQDN一致)
|── ブロック: NXDOMAIN応答
|── 許可: 上流に転送、解決されたIPをeBPF許可マップに追加
|── 記録: ドメイン、解決IP、PID、プロセス名
v
上流DNS(CoreDNS / 外部)
Container Process
|
| TCP/UDP egress (any port)
v
eBPF cgroup-skb/egress フィルター
|── IPが許可マップに存在? ──> 許可(カウント)
|── IPが信頼済みCIDRに含まれる? ──> 許可(Podネット、サービスネット)
|── クラウドメタデータ(169.254.169.254)? ──> ブロック(常時)
|── DoT(ポート853)? ──> ブロック(常時)
|── IPv6(非ループバック)? ──> ブロック(常時)
|── デフォルト ──> ブロック(リングバッファへのドロップイベント)
Podレベルの共有(Kubernetes)
K8s Pod内のすべてのコンテナは1つのネットワーク名前空間を共有します。LeitwachtはPodごとに1つのNetnsContextを作成し、その中で共有状態(DNSプロキシ、nftablesルール、eBPFマップ)を管理します。各コンテナには個別のcgroup-skbアタッチメントが与えられるため、同じPod内の他のコンテナがフィルターをバイパスすることはできません。
Pod(共有netns)
+-- NetnsContext(Podごとに1つ)
| +-- DNSプロキシ(1組のgoroutine)
| +-- nftablesリダイレクトルール
| +-- eBPFプログラム + マップ
| +-- Ringbufリーダー
|
+-- コンテナA
| +-- CgroupLink(eBPFがAのcgroupにアタッチ)
| +-- ViolationRecorder
|
+-- コンテナB
+-- CgroupLink(eBPFがBのcgroupにアタッチ)
+-- ViolationRecorder
エージェント-バックエンド間通信
+------------------+ gRPC +------------------+
| Leitwacht エージェント | ──────────────────────> | Leitwacht サーバー |
| (ノードごと) | | (中央) |
| | GetPolicy(project) | |
| - Watcher | <────────────────────── | - ポリシーストア |
| - Enforcer | | - 違反DB |
| - DNSプロキシ | ReportViolations(job) | - REST API |
| - Reporter | ──────────────────────> | - フロントエンドSPA |
| - Honeypot LSM | | - 通知 |
| | Heartbeat() | - ベースライン |
| | ──────────────────────> | - 異常検知 |
+------------------+ +------------------+
アーキテクチャ
コンポーネント
| コンポーネント | 説明 |
|---|---|
Agent (cmd/agent) | Runnerノード上のDaemonSet。containerdを監視してジョブコンテナを検出し、eBPF + DNSプロキシをアタッチし、違反を報告します。root権限とLinuxが必要です。 |
Server (cmd/server) | 中央バックエンド。フロントエンド用のREST API、エージェント用のgRPCを提供。ポリシー、違反、ベースラインを保存。PostgresまたはSQLite。 |
Frontend (frontend/) | SvelteKit SPA。ポリシー管理、違反ビューア、ベースライン管理、異常検知、監査ログ。 |
主要パッケージ
このリポジトリはEEサーバー + EEライセンスのエージェントです。 上の図に示すネットワーク強制データプレーン(eBPF、DNSプロキシ、nftables、containerdウォッチャー)は、別のMPL-2.0 Community-Editionモジュール
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 | アラート配信(メール/SES、Slack)+ テンプレート |
internal/retention | データ保存期間 / 整理 |
internal/mcp、internal/mcpauth | MCPサーバー + 認証 |
ee/reporter | EEエージェントgRPCクライアント、違反とアドバイザリをバックエンドにプッシュ |
ee/policycache、ee/rulewatcher、ee/rulestore | EEエージェントポリシーキャッシュ + ルール監視/保存(BBolt) |
contract/ | 共有Goモジュール: .protoソース + 生成されたagentpbワイヤタイプ |
デプロイ
前提条件
- containerdランタイムを使用するKubernetesクラスター
- Kubernetes Executorを使用するGitLab Runner
- Runnerで
FF_NETWORK_PER_BUILD=trueを設定(各ジョブが独自のネットワーク名前空間を持つ)
Helm Charts
Server (helm/) — OCIチャートとして公開。Postgres + ClickHouseといくつかの事前に作成されたシークレット(DB 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 | 強制なし | 強制なし | 免除されたプロジェクト |
開発
ビルド
# バックエンド
go build ./cmd/agent
go build ./cmd/server
# フロントエンド
cd frontend && pnpm install && pnpm build
# gRPCコントラクト再生成 (contract/proto/** を編集後):
cd contract && buf generate proto/
eBPFデータプレーンはこのリポジトリには含まれていません —
leitwacht-agent(CE) モジュールにあります。BPFスケルトンはそちらで再生成してください(そちらリポジトリのMakefile参照:make ebpf-generate)。
テスト
# 単体テスト(任意のプラットフォーム)
go test ./...
# サイドカー(ClickHouse + Postgres)が必要な統合 / e2eテストは
# internal/store、internal/api、internal/agentgrpc 以下にあります。最初にそれらを起動してください。
# フロントエンド
cd frontend && pnpm test
リント
# バックエンド
golangci-lint run ./...
# フロントエンド
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 | MPL-2.0 Community-Editionデータプレーンエージェント(eBPF / DNSプロキシ / nftables強制) — 上の図のエンジン部分 |