
Envoy xDSに似ているが、eBPFフィルタ向け
Envoy xDSのように、ただしeBPFフィルタ用。
NetfenceはVM/コンテナホスト上でデーモンとして実行され、cgroupやネットワークインターフェースにeBPFフィルタプログラムを自動的に注入します。内蔵のDNSサーバーが許可されたドメインを解決し、IP許可リストを更新します。
Netfenceデーモンは、ローカルのUnixソケットAPIのみで駆動することも、gRPCを介して実装する中央コントロールプレーンに接続し、バックエンドと許可リスト/拒否リストを同期することもできます。
コントロールプレーンは、ALLOW *.pypi.orgやALLOW 10.0.0.0/16のようなネットワークルールを接続されたインターフェース/cgroupにプッシュします。VM/コンテナがDNSに問い合わせると、Netfenceはそれを解決し、IPをeBPFフィルタに追加し、未知のIPへのトラフィックをホストから出る前にドロップします。ウォームパスのオーバーヘッドは、現在のベンチマークでは通常のソケット接続と事実上区別がつきません。
許可リストモードでは、IPv4リンクローカル(169.254.0.0/16)はデフォルトでは自動的に許可されなくなりました。そのため、クラウドメタデータサービス(169.254.169.254)は明示的に許可リストに追加されない限りブロックされます。これは意図的です:メタデータサービスは認証情報の盗難対象であり、サンドボックス化されたワークロードが暗黙的にアクセスできるようになってはいけません。localhost(127.0.0.0/8, ::1)とIPv6近隣探索(fe80::/10, ff02::/16)はデフォルトで許可されたままなので、基本的な接続性とNDPは動作し続けます。ワークロードにメタデータサービスを許可するには、169.254.169.254/32を許可リストに追加してください(コントロールプレーンを介したアタッチメントごとの除外オーバーライドは今後のフォローアップとして計画されています)。
IPv4ブロードキャスト(255.255.255.255)とマルチキャスト(224.0.0.0/4)は除外されておらず、ポリシーの対象となります。そのため、TC許可リストモードでは、DHCP更新ブロードキャストのようなトラフィックは明示的に許可リストに追加されない限りブロックされます。除外チェックは拒否リストの前に実行されるため、除外された範囲をブロックするにはその除外をオフにする必要があります。そして、IPv4リンクローカルがデフォルトでオフになったため、拒否リストモードでもメタデータサービスをブロックできます。
他のオプションが通常サポートしない、このソリューションのいくつかの主要な利点:
secretdata.someattacker.comのようなデータ流出はありません)私の知る限り、これらの機能をすべて同時に提供する他のソリューションはありません。
既知の制限: cgroupアタッチメントはソケット層(connect/sendmsgフック)でフィルタリングするため、CAP_NET_RAWを持つプロセスはそれらをバイパスするrawパケットを作成できます。CAP_NET_RAWを保持する可能性のあるワークロードには、デバイス層でフィルタリングするTC(インターフェース)アタッチメントを使用してください。
ただし、これはhttpjailのようなものよりも少しオーバーヘッドがあります。
これらの数値は、linux/arm64上の特権Docker Linuxゲートでmake bench-dockerを使用して測定されました。値は5つのサンプルの中央値です。
ウォームソケットベンチマークは、接続されたUDPソケットを使用して、TCPハンドシェイクのレイテンシからcgroup/connect4 eBPFフックのコストを分離します。このパスでは、DNSはすでにドメインを解決しており、IPはまだTTL内にあり、IP/CIDRはすでにeBPFマップに存在しています。
| パス | 中央値レイテンシ |
|---|---|
| 通常のソケット接続、eBPFなし | ~2.647 us |
| ウォーム許可リスト、保護されたLPMヒット | ~2.691 us |
| ウォーム許可リスト、DNS正確なホストヒット | ~2.741 us |
| 許可リストミス、ローカルブロック | ~1.652 us |
通常、保護されたLPM、DNS正確なホストの各接続パス間の測定されたばらつきは、サンプルノイズの範囲内です。
現在、「カーネルミスが親プロセスに問い合わせる」パスはありません。cgroup許可リストミスはeBPFによってローカルで判断され、即座にブロックされます。
これらの数値はDNSサーバーパスを測定しており、ウォームソケット接続パスではありません。
| パス | 中央値レイテンシ |
|---|---|
| プロキシクエリコールド、インプロセスポリシー関数 | ~31.336 us |
| プロキシクエリウォーム | ~27.964 us |
| ローカルアップストリームを使用した許可リストクエリコールド | ~53.510 us |
| ローカルアップストリームを使用した許可リストクエリウォーム | ~53.432 us |
コールド行は、実際のアタッチメントミューテーションバリアを通じて同期し、クエリ間でベンチマークの所有権グラフと偽の正確なマップスナップショットをクリアします。タイマーはUDPスケジューラの局所性を維持するために連続的に動作し、ns/opは別途報告されるfixture-reset-ns/opの壁時間(クライアントがパケットを受信した後の以前のハンドラのテールを含む)を差し引くことで、現在のクライアントExchangeを測定します。raw-total-ns/opは両方を合わせて報告します。リセットは設定されたポリシードメインとバッキングストレージを保持し、ベンチマークはクエリごとに1回の物理的な正確なマップ追加をアサートします。ウォーム行は所有権を一度プライムし、実行全体で1回の物理的な追加をアサートします。
以下の内部所有権マイクロベンチマークはスケーラビリティ診断であり、エンドツーエンドのDNSクエリパス受け入れ行ではありません。キャッシュされたヘルパーはテストとベンチマークのためだけに保持されます。これは一度に1つのレコードをラップし、ドメイン検証を繰り返します。それと通常のリゾルバトラフィックは両方ともアタッチメントミューテーションバリアを通過しますが、通常のリゾルバトラフィックは各完全な応答を1つのトランザクションとして受け入れます。
| 内部スケーラビリティ診断 | 現在の中央値 | メモリ/アロケーション |
|---|---|---|
| コールド新規キー受け入れ、空の所有権グラフ | ~370.3 ns | 232 B, 5 allocs/op |
| コールド新規キー受け入れ、4,095件の無関係なエントリ | ~451.2 ns | 232 B, 5 allocs/op |
| 物理容量のプレッシャーとLRU置換 | ~3.820 ms | ~4.23 MB (4,226,243 B), 4,336 allocs/op |
| 枯渇した物理バジェット事前チェック | ~611.9 ns | 344 B, 9 allocs/op |
| 最大エッジプレッシャー、64アドレス応答 | ~6.849 ms | ~7.66 MB (7,658,774 B), 2,233 allocs/op |
| 最大グラフワークガード、許可された完全計画 | ~2.763 ms | ~4.26 MB (4,264,386 B), 3,074 allocs/op |
| 最大グラフワークガード、枯渇した事前プロジェクション拒否 | ~10.935 us | 8.76 KB (8,760 B), 14 allocs/op |
| 数値上限付近のチャーンバジェット操作 | ~18.98 ns | 0 B, 0 allocs/op |
| 一貫した所有権統計スナップショット | ~2.094 ns | 0 B, 0 allocs/op |
| 4,095エントリにわたるno-op有効期限スキャン | ~74.849 us/scan | 0 B, 0 allocs/op |
+------------------+ +-------------------------+ | Your Control |<------->| Daemon (per host) | | Plane (gRPC) | stream | | +------------------+ | +-------------------+ | | | DNS Server | | | | (per-attachment) | | | +-------------------+ | +-------------------------+ | +------+------+ | | TC Filter Cgroup Filter (veth, eth) (containers)
各アタッチメントには、デーモンによってプロビジョニングされた固有のDNSアドレス(ポート)が割り当てられます。コンテナ/VMは、割り当てられたDNSアドレスを使用するように設定する必要があります。通常のワークロードDNSトラフィックをフィルタリングしても、透過的にリダイレクトされることはありません。
### DNSリゾルバのトポロジと動作
`dns.listen_addr` は、アタッチされたすべてのワークロードが到達できる具体的なIPv4またはIPv6アドレスを1つ指定する必要があります。ワイルドカードアドレスは、リゾルバエンドポイントとしてアドバタイズできないため拒否されます。設定されたホスト名はデーモン起動時に1回解決され、その結果得られた具体的なIPが、バインド、アドバタイズ、永続化、フィルタのブートストラップに使用されます。デフォルトの `127.0.0.1` は、ワークロードがデーモンのネットワーク名前空間を共有する場合にのみ適切です。別の名前空間にあるコンテナやVMは、通常、代わりに到達可能なホスト/ブリッジアドレスが必要です。```yaml
dns:
listen_addr: 10.0.0.1
port_min: 11000
port_max: 11500
# Daemon-global fallback when DnsConfig.upstream_servers is empty.
upstream: 1.1.1.1:53
# Hard daemon ceilings for each attachment's bounded DNS exact ownership.
# Zero/unset uses these defaults (max_ips_per_family instead derives from
# filter.max_dns_rule_entries).
max_ips_per_family: 4096
max_ips_per_response: 64
max_ips_per_policy_domain: 1024
max_tracked_domains: 1024
max_ownership_edges: 8192
# Rolling physical-admission/LRU mutation budget and slow-planning work
# allowance. The window is daemon-global and immutable until restart;
# DnsConfig.max_churn_units may only lower the daemon ceiling.
max_churn_units: 8192
churn_window: 1m
dns_addressの具体的なアドレスを返します。ワークロードのリゾルバとしてこの正確なアドレスを設定します。NetfenceはリスナーIPに対して保護された、期限切れにならない/32または/128許可エントリをインストールするため、許可リストモードはコントロールプレーンのDNS-IPルールなしでブートストラップできます。現在のフィルタはIPプレフィックスを強制し、送信先ポートは強制しないため、その保護されたエントリはリスナーIPのすべてのポート(DNSポートだけでなく)を許可します。これはcgroupアタッチメントの脅威モデルにとって特に重要です。そのような広い到達可能性が受け入れられない場合は、専用のリスナーIPを使用してください。
割り当てられたエンドポイントはUDPとTCPの両方を提供します。UDP応答は、レガシークライアントの512バイト制限またはアドバタイズされたEDNSサイズに切り詰められ、必要に応じてTCフラグを設定し、ワークロードが同じエンドポイントに対してTCPで再試行できるようにします。上流解決の場合、切り詰められたUDP応答は、まず同じ上流に対してTCPで再試行されます。トランスポート障害、SERVFAIL、またはREFUSEDが発生すると、設定された次の上流に順に進みます。
DnsConfig.upstream_serversは、1つのアタッチメントに対してデーモン全体のdns.upstreamをオーバーライドします。エントリはhost:port構文(IPv6リテラルは大括弧で囲む)を使用し、正規化されて最初に見つかった順序で重複排除され、最大8つの一意のサーバに制限されます。空のリストはグローバルフォールバックを選択します。
フィルタリングモードでは、NetfenceはHTTPS/SVCB応答からipv4hintおよびipv6hintパラメータ(対応するmandatory参照を含む)を除去します。これは、ヒントされたアドレスが独立してフィルタ承認を通過していないためです。無効モードでは、上流の応答を変更せずにそのまま渡します。
DNSクエリカウンタは相互に排他的です。dns_queries_allowedはポリシーで許可されたクエリ(NXDOMAINを含む)に正常に応答した数をカウントし、dns_queries_blockedはポリシーによるREFUSED応答をカウントし、dns_queries_errorsはリゾルバ、プロキシ、フィルタ承認、応答書き込み、その他のエラーパスをカウントします。クエリは正確に1つのバケットを増分します。
フィルタリングDNSモードでのアドレスを含むすべての応答は、そのA/AAAAセクション、authorityセクション、またはadditionalセクションからのアドレスが返される前に、1つのトランザクションとしてアタッチメントの正確なIPv4/IPv6 HASH階層に組み込まれます。完全な応答を表現できない場合、リゾルバはアドレスなしでSERVFAILを返し、以前に組み込まれたワーキングセットを保持します。アドレスを返すPROXY決定はadd_to_filterを設定する必要があります。設定しない場合もSERVFAILとしてフェイルクローズします。無効DNSモードは明示的なパススルー例外です。