Antrea の FQDN based filtering を試してみる

本記事は、[TUNA-JP Advent Calendar](https://qiita.com/advent-calendar/2021/tuna-jp) 13日目の投稿です。今回は、Antrea 1.3 で導入された FQDN based filtering について紹介したいと思います。

## はじめに

Antrea って何？という点については、@iwaseyusuke さんが 11日目の投稿「[Antrea が OVS に書き込んでる Flow Table を読んでみよう (苦行)](https://qiita.com/iwaseyusuke/items/369e66b6714102f6d843)」で紹介されているので、今回は省略したいと思います。

Antrea は、通常の Kubernetes の Network Policy をサポートしているのですが、それだけではなく独自に拡張した Antrea Cluster Network Policy（ACNP）と呼ばれる CRD があります。 通常の Network Policy は開発者が自身のアプリケーションを保護することが目的ですが、ACNP は、管理者がクラスタ全体にセキュリティポリシーを設定できることを目的としています。そのため、ACNP が適用される順番は、Kubernetes の Network Policy の前になります。

ちなみに、Antrea Network Policy (ANP) と呼ばれる CRD も存在します。Namespace が対象である点が ACNP との違いです。ACNP と比較していくつか制限がありますが、今回試す FDQN based filtering は ANP でも使用できます。

FDQN based filtering とは、Fully Qualified Domain Name (FQDN) をもとに egress 通信を制御できる機能です。例えば、以下のように、`app: test` のラベルにマッチする Pod から `example.com` への通信をドロップするルールを作成することができます。

```yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-fqdn-drop-example
spec:
  priority: 1
  appliedTo:
  - podSelector:
      matchLabels:
        app: test
  egress:
  - action: Drop
    to:
      - fqdn: "example.com"
```

通信を制御するためには、最終的には IP アドレスを知る必要がありますが、Antrea ではどうやって実装しているのか気になってので、今回調べてみました。

## 環境

今回、検証用に作成した環境は以下になります。

・ノード OS: Ubuntu 18.04
・Open vSwitch: 2.13.5
・Kubernetes: 1.22
・Antrea: 1.4

## FQDN based filtering を試す

FQDN based filtering を試すために、まずは busybox の Pod をデプロイします。

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  containers:
  - image: busybox
    command:
      - sleep
      - "36000"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
```

デプロイ後、busybox の Pod から google.com への ping が通ることを確認します。

```bash
$ kubectl exec -it busybox -- ping google.com
PING google.com (74.125.68.102): 56 data bytes
64 bytes from 74.125.68.102: seq=0 ttl=99 time=43.156 ms
64 bytes from 74.125.68.102: seq=1 ttl=100 time=41.788 ms
64 bytes from 74.125.68.102: seq=2 ttl=100 time=42.376 ms
```

次に、以下の ACNP を作成します。`app: busybox` のラベルにマッチする Pod から `google.com` への通信をドロップするルールになっています。

```yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-fqdn-drop-google
spec:
  priority: 1
  appliedTo:
  - podSelector:
      matchLabels:
        app: busybox
  egress:
  - action: Drop
    to:
      - fqdn: "google.com"
```

上記 ACNP の作成後、google.com への ping が失敗するようになりました。ちゃんと、作成したポリシーが動作していることが分かります。


```bash
$ kubectl exec -it busybox -- ping google.com
PING google.com (74.125.68.102): 56 data bytes
^C
--- google.com ping statistics ---
30 packets transmitted, 0 packets received, 100% packet loss
command terminated with exit code 1
```

## どうやって FQDN based filtering を実現しているのか？

さて、どうやって FQDN based filtering を実現しているのか？という話ですが、busybox Pod が動作しているノード上の Antrea Agent が CoreDNS に問い合わせを行って名前解決を行い、取得した IP アドレスをもとに Flow を作成しています。

Antrea Agent のログを見ると分かりやすいのですが、ACNP が作成されたら、名前解決を行って、得られた IP アドレスをもとに Flow を作成するという流れになっています。

```
I1212 02:30:24.487325       1 networkpolicy_controller.go:192] NetworkPolicy AntreaClusterNetworkPolicy:acnp-fqdn-drop-google applied to Pods on this Node
I1212 02:30:24.489093       1 fqdn.go:682] "Making DNS request" fqdn="google.com" dnsServer="10.96.0.10:53"
I1212 02:30:24.493767       1 fqdn.go:603] "Received DNS Packet with valid Answer" IPs=map[74.125.68.100:74.125.68.100 74.125.68.101:74.125.68.101 74.125.68.102:74.125.68.102 74.125.68.113:74.125.68.113 74.125.68.138:74.125.68.138 74.125.68.139:74.125.68.139] TTL=2
I1212 02:30:24.493902       1 fqdn.go:483] "Reconciling dirty rule" ruleID="5c0eef29c581d662"
I1212 02:30:24.499606       1 networkpolicy_controller.go:542] "Rule realization was done" ruleID="5c0eef29c581d662"
```

名前解決の処理は ACNP を作成した1回だけでなく定期的に行われるので、IP アドレスに変更があった場合も変更を検知して新しい Flow が作成されます。

`antctl` コマンドを使って Flow Table を確認すると、以下のように egress 通信用の Flow が作成されていることが分かります。

```
# antctl get ovsflows
~~ snip ~~
table=AntreaPolicyEgressRule, n_packets=11, n_bytes=1078, priority=14900,conj_id=3 actions=load:0x3->NXM_NX_REG3[],load:0x1->NXM_NX_REG0[20],goto_table:EgressMetric
table=AntreaPolicyEgressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_src=192.168.1.2 actions=conjunction(3,1/2)
table=AntreaPolicyEgressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_dst=74.125.68.101 actions=conjunction(3,2/2)
table=AntreaPolicyEgressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_dst=74.125.68.139 actions=conjunction(3,2/2)
table=AntreaPolicyEgressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_dst=74.125.68.113 actions=conjunction(3,2/2)
table=AntreaPolicyEgressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_dst=74.125.68.100 actions=conjunction(3,2/2)
table=AntreaPolicyEgressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_dst=74.125.68.102 actions=conjunction(3,2/2)
table=AntreaPolicyEgressRule, n_packets=0, n_bytes=0, priority=14900,ip,nw_dst=74.125.68.138 actions=conjunction(3,2/2)
```
※ Flow Table の詳細は、[@iwaseyusuke さんの投稿](https://qiita.com/iwaseyusuke/items/369e66b6714102f6d843) で解説されています！！

FQDN 周りの処理は主に [fqdn.go](https://github.com/antrea-io/antrea/blob/main/pkg/agent/controller/networkpolicy/fqdn.go) で実装されているので、より詳細が知りたい方はこのあたりのコードを確認いただければと思います。

## おわりに

今回は Antrea の FQDN based filtering について調べてみました。FQDN から Flow を作成するまでの処理を見て、ルール数が増えると CoreDNS に負荷がかかるんじゃないかなと思いましたが、FQDN ベースのルールはそんなにたくさん作らないですかね...？ 機会があれば、大量に作成したときのパフォーマンスについて調べてみようと思います。