アップデート一覧に戻る
New releaseAug 20, 2026

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/agentgrpcgRPCサーバー: エージェントからの違反レポートを受信し、ポリシー + ルールストリームを提供
internal/apiREST APIハンドラー、認証ミドルウェア、RBACスコープ
internal/authOIDC(GitLab)認証、セッション、CSRF
internal/storeGORMデータアクセス(プロジェクト、違反、ベースライン)+ ClickHouse
internal/licenseinternal/licensemgrEEライセンス検証(Ed25519 JWS)+ ライフサイクルおよび設定ゲート(LICENSE参照)
internal/notifyアラート配信(メール/SES、Slack)+ テンプレート
internal/retentionデータ保存期間 / 整理
internal/mcpinternal/mcpauthMCPサーバー + 認証
ee/reporterEEエージェントgRPCクライアント、違反とアドバイザリをバックエンドにプッシュ
ee/policycacheee/rulewatcheree/rulestoreEEエージェントポリシーキャッシュ + ルール監視/保存(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.mdKubernetesへの完全なデプロイ手順書(シークレット、ClickHouse、マイグレーション、ライセンスインストール、公開)
docs/MULTI_ORG.mdマルチ組織 / マルチテナント設定
LICENSENOTICEBSL 1.1条件(変更ライセンス: MPL-2.0)とCE/EEライセンスの分割
leitwacht-agentMPL-2.0 Community-Editionデータプレーンエージェント(eBPF / DNSプロキシ / nftables強制) — 上の図のエンジン部分

カテゴリ