View on GitHub

field-cage

A lightweight eBPF agent that monitors and restricts outbound network connections on GitHub Actions runners.

使い方

ポリシーファイル

mode: block          # 任意。audit または block。省略時は audit
                     # (いずれの場合も --mode フラグが優先されます)
allow_all_dns: false # 任意。下記「DNS の扱い」を参照(デフォルト false)

allowlist:
  - github.com
  - api.github.com
  - codeload.github.com
  - objects.githubusercontent.com
  - 1.2.3.4             # 単一 IPv4 アドレス
  - 2001:db8::1         # 単一 IPv6 アドレス
  - 10.0.0.0/8          # IPv4 CIDR サブネット(プライベート範囲)
  - 203.0.113.0/24      # 任意のプレフィックス長に対応
  - 2001:db8::/32       # IPv6 CIDR サブネット

注意: ワイルドカード(*.github.com)は非対応です。* を含むエントリはポリシー読み込み時にエラーになります。サブドメインは個別に列挙してください。

キーの厳格チェック: 未知のキーはポリシー読み込み時にエラーになります。キーの誤記(例: mdoe:)は黙ってデフォルトにフォールバックせず fail-fast します。

CIDR: CIDR エントリは eBPF の LPM trie に直接シードされるため、サブネット内の全アドレスが DNS 解決なしで許可されます。

DNS の扱い: デフォルトでは port 53 は /etc/resolv.conf に設定されたリゾルバとループバックのみ許可されます。これにより、任意ホストの port 53 を汎用の外向きトンネルとして悪用されるのを防ぎます。allow_all_dns: true を設定すると port 53 を全宛先に許可します(0.x 以前の挙動)。ただしデフォルトでも、正規リゾルバを介した DNS トンネリング(サブドメインにデータを載せた再帰解決)は遮断できません。下記の制約事項を参照してください。

スタンドアロンバイナリ

インストール

curl -fsSL https://takihito.github.io/field-cage/install.sh | sh

デフォルトでは ~/.local/bin にインストールされます(インストール自体に sudo は不要)。インストール先を変更する場合:

curl -fsSL https://takihito.github.io/field-cage/install.sh | sudo env FIELD_CAGE_INSTALL_DIR=/usr/local/bin sh

Linux(amd64 または arm64)が必要です — field-cage は eBPF に依存するため macOS / Windows 版はありません。事前ビルド済みバイナリの手動ダウンロードはReleasesページを参照してください。

実行

# Audit モード(ポリシーなし・全通信をログ出力)
sudo field-cage

# Audit モード(ポリシーファイルあり)
sudo field-cage --config policy.yml

# Block モード(デフォルト拒否。allowlist の宛先のみ許可)
# ポリシーファイルは必須(無いと全接続が拒否されるため起動しません)
sudo field-cage --config policy.yml --mode block

# バージョン表示
field-cage --version

システムによっては sudoPATH をリセットするため、sudo field-cage が “command not found” になる場合はフルパス指定(例: sudo ~/.local/bin/field-cage)するか、sudo -E/--preserve-env=PATH を使ってください。

Block モードの遮断モデル

Block モードは デフォルト拒否(default-deny) です。cgroup/connect4 および cgroup/connect6 プログラムは、宛先 IP が allowlist に無い全アウトバウンド IPv4/IPv6 接続を拒否します。IPv4-mapped IPv6 宛先(::ffff:a.b.c.d、Node.js や Java などのデュアルスタックランタイムが IPv4 ホストへ接続する際の経路)は IPv4 allowlist と照合されるため、IPv4 エントリ 1 つで両方のソケットファミリーをカバーします。allowlist は次の方法で構築されます。

  1. 起動時シード — 明示的な IP / CIDR エントリを直接追加し、allowlist の各ドメインを解決(A / AAAA)してそのアドレスを追加。
  2. DNS のライブ観測 — allowlist 対象ドメインの DNS 応答を wire 上で観測した時点で、その A / AAAA レコード IP をアプリの接続より先に allowlist へ追加。ただし信用するのは、設定済みリゾルバ(/etc/resolv.confnameserver)またはループバックを送信元とする応答のみ。それ以外の送信元の応答はログ用にキャッシュするだけで allowlist には追加しないため、送信元ポート53を偽装した偽造応答による allowlist ポイズニングを防ぎます。

ループバック(127.0.0.0/8 および ::1)はローカルサービスを動作させるため常に許可します。DNS(宛先 port 53)はシステムのリゾルバ(およびループバック)のみ許可します。これは名前解決を機能させつつ、任意ホストの port 53 を汎用の外向きトンネルとして悪用されるのを防ぐためです。allow_all_dns: true をポリシーに設定すると、従来どおり port 53 を無条件に許可します。リゾルバは /etc/resolv.conf から発見しますが、ループバックスタブ(systemd-resolved の 127.0.0.53)のみの場合は /run/systemd/resolve/resolv.conf にある上流サーバーも許可します — enforcement はスタブデーモン自身の外向きクエリにも適用されるため、上流への到達性が必要だからです。起動時にリゾルバを特定できない場合はループバックのみ許可します(fail-closed)。拒否された port 53 接続は SKIP(dns) ではなく DENY として記録されます。Block モードではポリシーファイルが必須で、無い場合は全遮断を避けるため起動を拒否します。

制約事項