Antrea Cluster Network Policy を使う (2021年版)

# Antrea とは

Antrea とは、オープンソースの Kubernetes CNI で、L3/4 のネットワーキングの機能とセキュリティを Kubernetes クラスターに提供するものです。オープンな仮想スイッチとして長い実績を誇る Open vSwitch をデータプレーンに利用しています。

https://github.com/antrea-io/antrea

Antrea では標準的な Kubernetes クラスターネットワーキングではサポートされない様々な機能の実装が試みられていますが、ネットワークセキュリティの観点で興味深いのが Antrea Cluster Network Policy (ACNP) です。
Antrea は標準的な NetworkPolicy もサポートしていますが、ACNP ではクラスター管理者の要求によりよく答えれるように以下のような機能を提供しています。

- ポリシーの階層化と優先度設定
- Namespace に限定されない、クラスターレベルのセキュリティ設定
- アクションルールのサポート: Allow, Drop, Reject のアクションを指定可能

本記事では、Antrea Cluster Network Policy に注目して機能の確認をしていきたいと思います。

余談ですが、本記事執筆中の2021年4月10日に Antrea は 1.0.0 に到達しました。以下は Antrea 1.0.0 をベースに記載しております。
また、上記の Project Antrea のページ以外に以下の記事も参考にさせていただきました。
https://blog.shin.do/2020/01/antrea-yet-another-cni-plugin-for-kubernetes/

# Antrea クラスターの準備

## 新しい K8s クラスタを準備  

今回は kubeadm を使って Antrea 用の Kubernetes クラスターを新たに準備しました。このあたりを参考にしています。  
https://thinkit.co.jp/article/18188

Master 1台と Worker 2台を構成しましたが、CNI を適用する前は以下のように NotReady となります。

```bash
$ kubectl get node
NAME          STATUS     ROLES                  AGE     VERSION
k8s-master    NotReady   control-plane,master   1d      v1.20.5
k8s-worker1   NotReady   <none>                 44m     v1.20.5
k8s-worker2   NotReady   <none>                 2m41s   v1.20.5
```

## クラスターに Antrea を適用

CNI として Calico ではなく Antrea を適用します。Antrea の初期セットアップについてはこちらもご参照下さい:  
https://github.com/vmware-tanzu/antrea/blob/main/docs/getting-started.md

今回は Antrea v1.0.0 を使うので、以下のように指定して適用します。

```bash
$ kubectl apply -f https://github.com/vmware-tanzu/antrea/releases/download/v1.0.0/antrea.yml
```
Latest を適用したい場合は以下でも構いません。

```bash
$ kubectl apply -f https://raw.githubusercontent.com/vmware-tanzu/antrea/main/build/yamls/antrea.yml
```

Antrea CNI を適用すると Node が Ready になりました。

```bash
$ kubectl get node
NAME          STATUS   ROLES                  AGE   VERSION
k8s-master    Ready    control-plane,master   1d    v1.20.5
k8s-worker1   Ready    <none>                 57m   v1.20.5
k8s-worker2   Ready    <none>                 15m   v1.20.5
```

### antctl のインストール  
antctl は Controller を介して Antrea の設定や状態を確認するためのコマンドラインツールです。今後必要になるかもしれないので最新バージョンをインストールしておきます。以下は 1.0.0 Linux 版の場合です。   

```bash
$ curl -Lo ./antctl "https://github.com/vmware-tanzu/antrea/releases/download/v1.0.0/antctl-linux-x86_64"
$ chmod +x ./antctl
$ mv ./antctl /usr/local/bin
$ antctl version
antctlVersion: v1.0.0
controllerVersion: v1.0.0
```

### 設定の確認

Antrea を展開した段階で、Antrea の configmap が作成されています。現在の設定がどうなっているかを確認することができます。`antrea-config-xxxxxxxxx`が作成されているので表示してみます。 

```bash
$ kubectl get configmap antrea-config-5ct9ktdt77 -n kube-system -o yaml
```

ここでは `AntreaPolicy` の設定を確認してみます。`antrea-agent.conf` および `antrea-controller.conf` 設定の中に `AntreaPolicy: True` で設定されていますが、コメントアウトされています。Antrea 1.0 からデフォルトで Antrea Network Policy が enable になっているので、この状態ですでに有効になっています。  

```yaml
apiVersion: v1
data:
  antrea-agent.conf: |
    # FeatureGates is a map of feature names to bools that enable or disable experimental features.
    featureGates:
    # Enable AntreaProxy which provides ServiceLB for in-cluster Services in antrea-agent.
    # It should be enabled on Windows, otherwise NetworkPolicy will not take effect on
    # Service traffic.
    #  AntreaProxy: true

    # Enable EndpointSlice support in AntreaProxy. Don't enable this feature unless that EndpointSlice
    # API version v1beta1 is supported and set as enabled in Kubernetes. If AntreaProxy is not enabled,
    # this flag will not take effect.
    #  EndpointSlice: false

    # Enable traceflow which provides packet tracing feature to diagnose network issue.
    #  Traceflow: true

    # Enable NodePortLocal feature to make the pods reachable externally through NodePort
    #  NodePortLocal: false

    # Enable Antrea ClusterNetworkPolicy feature to complement K8s NetworkPolicy for cluster admins
    # to define security policies which apply to the entire cluster, and Antrea NetworkPolicy
    # feature that supports priorities, rule actions and externalEntities in the future.
    #  AntreaPolicy: true

(snip...)

  antrea-controller.conf: |
    # FeatureGates is a map of feature names to bools that enable or disable experimental features.
    featureGates:
    # Enable traceflow which provides packet tracing feature to diagnose network issue.
    #  Traceflow: true

    # Enable Antrea ClusterNetworkPolicy feature to complement K8s NetworkPolicy for cluster admins
    # to define security policies which apply to the entire cluster, and Antrea NetworkPolicy
    # feature that supports priorities, rule actions and externalEntities in the future.
    #  AntreaPolicy: true

(snip...)
```

# Antrea Cluster Network Policy を試す

では実際に Antrea Cluster Network Policy を使ってみます。K8s 標準の NetworkPolicy との動作の違いも見てみたいと思います。

## サンプルアプリのデプロイ

まず、動作確認のために使用するアプリケーションをデプロイします。default namespace に Guestbook アプリをデプロイします。

```bash
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-controller.json
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-service.json
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-controller.json
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-service.json
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-controller.json
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-service.json
```

`guesｔbook`, `redis-master`, `redis-slave` の三種類のサービスとコンテナが作成されます。

```bash
$ kubectl get all -n default
NAME                     READY   STATUS    RESTARTS   AGE
pod/guestbook-hmsrw      1/1     Running   0          23h
pod/guestbook-nl7xv      1/1     Running   0          23h
pod/guestbook-s9spp      1/1     Running   0          23h
pod/redis-master-z2vjh   1/1     Running   0          23h
pod/redis-slave-cplgv    1/1     Running   0          23h
pod/redis-slave-r2tmz    1/1     Running   0          23h

NAME                                 DESIRED   CURRENT   READY   AGE
replicationcontroller/guestbook      3         3         3       23h
replicationcontroller/redis-master   1         1         1       23h
replicationcontroller/redis-slave    2         2         2       23h

NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/guestbook      LoadBalancer   10.109.117.193   <pending>     3000:30731/TCP   23h
service/kubernetes     ClusterIP      10.96.0.1        <none>        443/TCP          2d
service/redis-master   ClusterIP      10.98.195.90     <none>        6379/TCP         23h
service/redis-slave    ClusterIP      10.109.248.115   <none>        6379/TCP         23h
```

`guestbook` サービスは `LoadBalancer` タイプですが、この環境には Provider LB がまだ存在しないため、`EXTERNAL-IP` は `Pending` になっています。とりあえず、 `NodePort` を使って以下の URL にアクセスすると Guestbook の UI が表示されます。`192.168.110.91` は Worker ノードの IP アドレスです。

```http
http://192.168.110.91:30731/
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018265/b26dc86a-53db-f026-e6f6-27410df72667.png)

ここで各 Pod のラベル設定を確認しておきます。フロントエンドのコンテナである`guestbook`には`app: guestbook`の、バックエンドの`redis-master`と`redis-slave`には`app: redis`のラベルが設定されています。ラベルはこのあと NetworkPolicy や Antrea Network Policy の `PodSelector`設定で使用することになります。

```bash
$ kubectl get pod -L app
NAME                 READY   STATUS    RESTARTS   AGE     APP
guestbook-hmsrw      1/1     Running   0          23h   guestbook
guestbook-nl7xv      1/1     Running   0          23h   guestbook
guestbook-s9spp      1/1     Running   0          23h   guestbook
redis-master-z2vjh   1/1     Running   0          23h     redis
redis-slave-cplgv    1/1     Running   0          23h     redis
redis-slave-r2tmz    1/1     Running   0          23h     redis
```

## NetworkPolicy 

Antrea Cluster Network Policy を試す前に、標準の NetworkPolicy の動作を確認してみます。

### namespace 内のコンテナ間制御

以下のポリシーを適用し、namespace 内のフロントエンド`guestbook`とバックエンド`redis`の通信が阻害されることを確認します。  

```yaml
$ cat np1.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: drop-access-to-redis
spec:
  policyTypes:
  - Ingress
  podSelector:
    matchLabels:
      app: redis
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: other
$
$ kubectl apply -f np1.yaml
networkpolicy.networking.k8s.io/drop-access-to-redis created
```

再度 Guestbook の URL にアクセスすると以下のように表示され、バックエンドへの接続ができないことが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018265/2893932c-b784-a04b-cc31-d089fef80df1.png)

この NetworkPolicy を削除すると、再び接続できるようになります。

```bash
$ kubectl delete -f np1.yaml
networkpolicy.networking.k8s.io "drop-access-to-redis" deleted
```

### namespace 外からのアクセスの制御

今度はフロントエンド`guestbook`へのアクセスを禁じるポリシーを作成して適用します。

```bash
$ cat np2.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: drop-access-to-guestbook
spec:
  policyTypes:
  - Ingress
  podSelector:
    matchLabels:
      app: guestbook
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: other
$
$ kubectl apply -f np2.yaml
networkpolicy.networking.k8s.io/drop-access-to-guestbook created
```

この時、NodePort を介した外部からのアクセスは可能なままです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018265/019983b2-b724-038f-b2f2-46c7a7fa5c17.png)

しかし、フロントエンドコンテナからの ClusterIP を介したアクセスは許可されません。

```bash
$ kubectl exec guestbook-hmsrw -it -- sh


BusyBox v1.21.1 (Ubuntu 1:1.21.0-1ubuntu1) built-in shell (ash)
Enter 'help' for a list of built-in commands.

/app # wget -O - http://guestbook.default.svc.cluster.local:3000
Connecting to guestbook.default.svc.cluster.local:3000 (10.109.117.193:3000)
^C
/app # 
```

再びポリシーを削除するとフロントエンドからもアクセスできるようになります。

```bash
$ kubectl delete -f np2.yaml
networkpolicy.networking.k8s.io "drop-access-to-guestbook" deleted
```

```bash
/app # wget -O - http://guestbook.default.svc.cluster.local:3000
Connecting to guestbook.default.svc.cluster.local:3000 (10.109.117.193:3000)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    <meta charset="utf-8">
    <meta content="width=device-width" name="viewport">
    <link href="style.css" rel="stylesheet">
    <title>Guestbook</title>
  </head>
  <body>
    <div id="header">
      <h1>Guestbook</h1>
    </div>

(snip...)
```
このように、NodePort を用いた外部からの制御には NetworkPolicy は利用できません。 

### namespace 間の制御

今度は、以下のポリシーを作成して、他の namespace からのアクセスを許可します。

```bash
$ cat np3.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-access-from-other-ns
spec:
  policyTypes:
  - Ingress
  podSelector:
    matchLabels:
      app: guestbook
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          project: test
    - podSelector:
        matchLabels:
          app: other
$
$ kubectl apply -f np3.yaml
networkpolicy.networking.k8s.io/allow-access-from-other-ns created
```

このテストを行うために一時的に namespace `test` を作成し、`project: test`のラベルを付けます。

```bash
$ kubectl create namespace test
namespace/test created
$ kubectl label namespace test project=test
namespace/test labeled
```

次にテスト用コンテナを`test`namespace 内に作成します。

```bash
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-controller.json -n test
$ kubectl get pod -n test
NAME              READY   STATUS    RESTARTS   AGE
guestbook-5w6ft   1/1     Running   0          79m
guestbook-cfzfd   1/1     Running   0          79m
guestbook-jlfpw   1/1     Running   0          79m
```

このコンテナから、最初にデプロイした`default`namespace 内の Guestbook アプリにアクセスできることを確認します。

```bash
$ kubectl exec guestbook-5w6ft -n test  -it -- sh


BusyBox v1.21.1 (Ubuntu 1:1.21.0-1ubuntu1) built-in shell (ash)
Enter 'help' for a list of built-in commands.

/app # wget -O - http://guestbook.default.svc.cluster.local:3000
Connecting to guestbook.default.svc.cluster.local:3000 (10.109.117.193:3000)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    <meta charset="utf-8">
    <meta content="width=device-width" name="viewport">
    <link href="style.css" rel="stylesheet">
    <title>Guestbook</title>
  </head>
  <body>
    <div id="header">
      <h1>Guestbook</h1>
    </div>

(snip...)
```
このように、NetworkPolicy ではクラスターに閉じた形で namespace 毎のアクセス制御が可能ですが、namespace やクラスターを超えて同じルールを適用することはできません。また、ポリシーは allow ルールのみですが同時に暗黙の drop ルールが記述されます。そのため細やかな drop ルールを書くようなセキュリティの掛け方には向かないようです。
Antrea Cluster Network Policy はこれらの課題に対応しようとしています。

## Antrea Cluster Network Policy (ACNP)

ではいよいよ Antrea Cluster Network Policy を設定していきます。
Antrea 1.0 では最初から Antrea Cluster Network Policy が enable になっていますので、すぐに設定を開始できますが、まず Antrea Cluster Network Policy 独自の Tier について確認しておきます。

### Tier 

Tier はポリシーを階層化して管理するための概念で、ポリシーに対して優先度をつけたい場合に有用です。初期設定では Tier は以下のように設定されています。

```bash
$ kubectl get tier --sort-by=.spec.priority
NAME          PRIORITY   AGE
emergency     50         37m
securityops   100        37m
networkops    150        37m
platform      200        37m
application   250        37m
baseline      253        37m
```

各`tier`の`PRIORITY`が、実際に処理される優先度を表します。小さい数値ほど優先度が高くなります。たとえば最優先で処理させたいポリシーがある場合は `emergency` に設定すれば最初に処理されるようになります。標準の NetworkPolicy で設定されたポリシーは、`application` Tier の後で処理されます。

Tier は自分で作成することも可能です。ここでは新しい Tier を以下のマニフェスト`mytier.yaml`で作成してみます。

```yaml
apiVersion: security.antrea.tanzu.vmware.com/v1alpha1
kind: Tier
metadata:
  name: mytier
spec:
  priority: 10
  description: "my custom tier"
```

```bash
$ kubectl apply -f mytier.yaml
tier.security.antrea.tanzu.vmware.com/mytier created
$
$ kubectl get tiers --sort-by=.spec.priority
NAME          PRIORITY   AGE
mytier        10         42s
emergency     50         45m
securityops   100        45m
networkops    150        45m
platform      200        45m
application   250        45m
baseline      253        45m
```

### Antrea Cluster Network Policy の適用

では Antrea Cluster Network Policy を実際に作成していきます。まず以下のマニフェスト`acnp1.yaml`を作成します。

```yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-drop-access-to-redis
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - podSelector:
        matchLabels:
          app: redis
  ingress:
    - action: Allow
      from:
        - podSelector:
            matchLabels:
              app: other
      name: AllowFromOther
      enableLogging: false
```

これは NetworkPolicy のところで最初に作成した`np1.yaml`によく似ていますが、実際の動作は異なります。以下のコマンドでこのマニフェストを適用します。

```bash
$ kubectl apply -f acnp1.yaml
clusternetworkpolicy.crd.antrea.io/acnp-drop-access-to-redis created
$ 
$ kubectl get acnp
NAME                        TIER          PRIORITY   DESIRED NODES   CURRENT NODES   AGE
acnp-drop-access-to-redis   securityops   5          1               1               24s
```

この状態で Guestbook アプリにアクセスしても、バックエンドへのアクセスは阻害されないため、正常に表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018265/c1cc6c56-473a-6b53-d496-66552b092cc6.png)

NetworkPolicy ではポリシーが反映されると同時に暗黙の Deny が有効になり許可されないトラフィックはドロップされるようになりますが、ACNP では明示的に Drop ルールが書かれない限りトラフィックは許可されます。  
なお、ACNP の kind は`ClusterNetworkPolicy`ですが、上の`kubectl get acnp`コマンドのように、`acnp`という省略形で操作を行うことも可能です。

今度は、`acnp1.yaml`を修正して以下の`acp11.yaml`を作成し適用してみます。明示的にソースとなる Pod を`podSelector` で指定し、　`action`を `Drop` に変更します。

```yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-drop-access-to-redis
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - podSelector:
        matchLabels:
          app: redis
  ingress:
    - action: Drop
      from:
        - podSelector:
            matchLabels:
              app: guestbook
      name: DropFromGuestbook
      enableLogging: false
```

これを同じく適用します。

```bash
$ kubectl apply -f acnp11.yaml
clusternetworkpolicy.crd.antrea.io/acnp-drop-access-to-redis configured
```

すると、今度はバックエンドへのアクセスが阻害されるため以下の表示に変わりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018265/85ea5107-a715-66d0-04aa-92fdf152880d.png)

この ACNP では、`tier` を `securityops`、 `priority` を `5` に指定しています。`priority` は `tier` の内部での処理の優先度を表します。
上述のように `securityops` Tier に指定したポリシーは標準の Network Policy よりも優先的に処理されます。例えば、`np1.yaml`を最初に適用してバックエンドへのアクセスが阻害されている状態を作り、その上で以下の`acnp12.yaml`を適用すると、ACNP の処理が優先されてバックエンドへのアクセスが許可されるようになります。

```yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-allow-access-to-redis
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - podSelector:
        matchLabels:
          app: redis
  ingress:
    - action: Allow
      from:
        - podSelector:
            matchLabels:
              app: guestbook
      name: AllowFromGuestbook
      enableLogging: false
```

このように、Antrea Cluster Network Policy を使えば、標準の NetworkPolicy よりもより明確に送信元と宛先を指定したセキュリティポリシーの定義ができることを理解いただけたかと思います。  


### ACNP による namespace 間の制御

ここからは ACNP による namespace をまたいだ制御を見ていきたいと思います。その前に、もう一度 K8s NetworkPolicy ではどのような挙動になるかを確認しておきます。上で利用した `np2.yaml` コピーして、 Guestbook アプリ間のトラフィックを許可するように変更した `np22.yaml` を作成し適用します。

```
$ cat np22.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-access-from-to-guestbook
spec:
  policyTypes:
  - Ingress
  podSelector:
    matchLabels:
      app: guestbook
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: guestbook
$
$ kubectl apply -f np22.yaml
networkpolicy.networking.k8s.io/allow-access-from-to-guestbook created
$ 
```

現在、`default`と`test` の namespace にデプロイ済みの Pod の状態は以下のようになっています。

```
$ kubectl get pod -L app -n default
NAME                 READY   STATUS    RESTARTS   AGE   APP
guestbook-hmsrw      1/1     Running   0          28d   guestbook
guestbook-nl7xv      1/1     Running   0          28d   guestbook
guestbook-s9spp      1/1     Running   0          28d   guestbook
redis-master-blz4w   1/1     Running   0          21d   redis
redis-slave-fbsz6    1/1     Running   0          21d   redis
redis-slave-rpvqt    1/1     Running   0          21d   redis
$ 
$ kubectl get pod -L app -n test
NAME              READY   STATUS    RESTARTS   AGE   APP
guestbook-5w6ft   1/1     Running   0          27d   guestbook
guestbook-cfzfd   1/1     Running   0          27d   guestbook
guestbook-jlfpw   1/1     Running   0          27d   guestbook
$ 
```

この状態では、`test` namespace 側の Guestbook pod から `default` namespace 側にアクセスが許可されません。K8s NetworkPolicy では namespace をまたいで `podSelector` を利用できないからです。実際、`test` namespace 側の Guestbook Pod から `default` namespace 側の Guestbook pod へのアクセスは以下のように許可されません。

```
$ kubectl exec guestbook-5w6ft -n test  -it -- sh


BusyBox v1.21.1 (Ubuntu 1:1.21.0-1ubuntu1) built-in shell (ash)
Enter 'help' for a list of built-in commands.

/app # wget -O - http://guestbook.default.svc.cluster.local:3000
Connecting to guestbook.default.svc.cluster.local:3000 (10.109.117.193:3000)
wget: can't connect to remote host (10.109.117.193): Connection timed out
/app #

```

では、ACNP で同じようなポリシーを作成してトライしてみましょう。

```
$ cat acnp2.yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-allow-access-from-to-guestbook
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - podSelector:
        matchLabels:
          app: guestbook
  ingress:
    - action: Allow
      from:
        - podSelector:
            matchLabels:
              app: guestbook
      name: AllowFromGuestbook
      enableLogging: false
$
$ kubectl apply -f acnp2.yaml
clusternetworkpolicy.crd.antrea.io/acnp-allow-access-from-to-guestbook created
$ 
```
`np22.yaml` と同様に `appliedTo` と `from` の両方の `podSelector` でラベル `app: guestbook` を指定していますが、namespace は特に指定していません。また、前に作成した K8s NetworkPolicy もそのままにしてあります。

```
$ kubectl get networkpolicy
NAMESPACE   NAME                             POD-SELECTOR    AGE
default     allow-access-from-to-guestbook   app=guestbook   32m
$ 
$ kubectl get acnp
NAME                                  TIER          PRIORITY   DESIRED NODES   CURRENT NODES   AGE
acnp-allow-access-from-to-guestbook   securityops   5          2               2               10m
```

この状態で再び `test` 側の Pod から `default` 側にアクセスすると以下のように許可されます。

```
/app # wget -O - http://guestbook.default.svc.cluster.local:3000
Connecting to guestbook.default.svc.cluster.local:3000 (10.109.117.193:3000)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    <meta charset="utf-8">
    <meta content="width=device-width" name="viewport">
    <link href="style.css" rel="stylesheet">
    <title>Guestbook</title>
  </head>
  <body>
    <div id="header">
      <h1>Guestbook</h1>
    </div>

(snip...)

  </body>
</html>
-                    100% |*******************************|   922   0:00:00 ETA
/app #
```

また、ACNP では `podSelector` と `namespaceSelector` を併用して、namespace 毎に適用対象や送信元となる pod を明示的に指定することができます。`acnp2.yaml` を編集して `namespaceSelector` 条件を追加した `acnp22.yaml` を作成します。

```
$ cat acnp22.yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-allow-access-from-to-guestbook
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - podSelector:
        matchLabels:
          app: guestbook
      namespaceSelector:
        matchLabels:
          project: prod
  ingress:
    - action: Allow
      from:
        - podSelector:
            matchLabels:
              app: guestbook
          namespaceSelector:
            matchLabels:
              project: test
      name: AllowFromGuestbook
      enableLogging: false
$
$ kubectl apply -f acnp22.yaml
clusternetworkpolicy.crd.antrea.io/acnp-allow-access-from-to-guestbook configured
$ 
```

この修正により、受信側 namespace のラベル条件に `project: prod`、送信側 namespace のラベルが条件には `project: test` が追加されました。しかしながら、受信側の Guestbook pod は `default` namespace 内にありますが、namespace にラベルをまだ追加していないため、この状態では通信は失敗します。

```
/app # wget -O - http://guestbook.default.svc.cluster.local:3000
Connecting to guestbook.default.svc.cluster.local:3000 (10.109.117.193:3000)
wget: can't connect to remote host (10.109.117.193): Connection timed out
/app #
```

そこで、`default` namespace にラベルを追加します。

```
$ kubectl label ns default project=prod
namespace/default labeled
$ 
$ kubectl get ns --show-labels
NAME              STATUS   AGE   LABELS
default           Active   29d   project=prod
kube-node-lease   Active   29d   <none>
kube-public       Active   29d   <none>
kube-system       Active   29d   <none>
test              Active   27d   project=test
```

すると `test` namespace 内の Guestbook pod から `default` namespace 内の Guestbook pod にアクセスできるようになりました。

```
/app # wget -O - http://guestbook.default.svc.cluster.local:3000
Connecting to guestbook.default.svc.cluster.local:3000 (10.109.117.193:3000)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    <meta charset="utf-8">
    <meta content="width=device-width" name="viewport">
    <link href="style.css" rel="stylesheet">
    <title>Guestbook</title>
  </head>
  <body>
    <div id="header">
      <h1>Guestbook</h1>
    </div>

(snip...)

  </body>
</html>
-                    100% |*******************************|   922   0:00:00 ETA
/app #

```

このように、Antrea Cluster Network Policy では、namespace に限定されないクラスターレベルのネットワークポリシーを設定することが可能です。クラスター管理者は namespace に依存しないクラスターレベルのセキュリティポリシーを設定することができるようになります。

## ClusterGroup

ClusterGroup は ACNP の特徴的な機能の一つで、エンドポイントをグルーピングするルールを NetworkPolicy の外で作成し再利用することができます。これまでに紹介した `podSelector` や `namespaceSelector`、おなじみの `ipBlock` 以外に、K8s Service を利用する `serviceReference` やグルーピングのネスト構造を定義できる `childGroups` といった定義を使うことが可能です。

ClusterGroup を使ったルールを作成していく前に、これまでに作成してきたすべてのポリシーをいったん削除します。

```
$ kubectl get networkpolicy --no-headers | awk '{print $1}'| xargs kubectl delete networkpolicy
networkpolicy.networking.k8s.io "allow-access-from-to-guestbook" deleted
$ 
$ kubectl get acnp --no-headers | awk '{print $1}'| xargs kubectl delete acnp
clusternetworkpolicy.crd.antrea.io "acnp-allow-access-from-to-guestbook" deleted
```

そして `default` namespace 内の Pod へのすべてのトラフィックを暗黙的に不許可にするために以下の `acnp-dropany.yaml` を作成し適用します。適用先の Tier として `baseline` を指定しています。この場合、このルールは K8s NetworkPolicy を含むすべてのポリシールールが評価された後に適用されるようになります。

```
$ cat acnp-dropany.yaml

apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-drop-any-in-production
spec:
  priority: 100
  tier: baseline
  appliedTo:
    - podSelector: {}
      namespaceSelector:
        matchLabels:
          project: prod
  ingress:
    - action: Drop
      from:
        - podSelector: {}
      name: DropFromAny
      enableLogging: false
$
$ kubectl apply -f acnp-dropany.yaml
clusternetworkpolicy.crd.antrea.io/acnp-drop-any-in-production created
$
```

すべての Pod 間の通信が不許可になるため、Guestbook アプリのフロントエンドと Redis バックエンド間の通信もできなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018265/0f9117e6-79ca-ab90-0ff9-2d17e8336d6a.png)

そこで、 フロントエンドとバックエンド間の通信を許可するポリシーを記載していきますが、そこで ClusterGroup を使用してみます。ここでは、Redis バックエンドサービスのグルーピングに `serviceReference` と `childGroups` を使ってみます。

```
$ cat cg1.yaml
apiVersion: crd.antrea.io/v1alpha2
kind: ClusterGroup
metadata:
  name: cg-guestbook
spec:
  podSelector:
    matchLabels:
      app: guestbook
---
apiVersion: crd.antrea.io/v1alpha2
kind: ClusterGroup
metadata:
  name: cg-redis-master
spec:
  serviceReference:
    name: redis-master
    namespace: default
---
apiVersion: crd.antrea.io/v1alpha2
kind: ClusterGroup
metadata:
  name: cg-redis-slave
spec:
  serviceReference:
    name: redis-slave
    namespace: default
---
apiVersion: crd.antrea.io/v1alpha2
kind: ClusterGroup
metadata:
  name: cg-redis-nested
spec:
  childGroups: [cg-redis-master, cg-redis-slave]
$
$ kubectl apply -f cg1.yaml
clustergroup.crd.antrea.io/cg-guestbook created
clustergroup.crd.antrea.io/cg-redis-master created
clustergroup.crd.antrea.io/cg-redis-slave created
clustergroup.crd.antrea.io/cg-redis-nested created
$ 
```

次にこれらの ClusterGroup を利用した ACNP を `acnp3.yaml` として作成していきます。

```
$ cat acnp3.yaml
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-allow-access-to-guestbook
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - group: "cg-guestbook"
  ingress:
    - action: Allow
      from:
        - group: "cg-guestbook"
      name: AllowToGuestbook
      enableLogging: false
---
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-allow-access-to-redis
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - group: "cg-redis-nested"
  ingress:
    - action: Allow
      from:
        - group: "cg-guestbook"
      name: AllowFromGuestbookToRedis
      enableLogging: false
---
apiVersion: crd.antrea.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
  name: acnp-allow-access-to-redis-slave
spec:
  priority: 5
  tier: securityops
  appliedTo:
    - group: "cg-redis-slave"
  ingress:
    - action: Allow
      from:
        - group: "cg-redis-master"
      name: AllowToRedisSlave
      enableLogging: false
$
$ kubectl apply -f acnp3.yaml
clusternetworkpolicy.crd.antrea.io/acnp-allow-access-to-guestbook created
clusternetworkpolicy.crd.antrea.io/acnp-allow-access-to-redis created
clusternetworkpolicy.crd.antrea.io/acnp-allow-access-to-redis-slave created
$ 
```

この状態でブラウザからアクセスすると、Redis バックエンドへのアクセスも許可されて正常に応答が返ってくるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1018265/ecc5fce6-d3dc-58ac-36b7-6ff6fadbe941.png)

いかがでしたでしょうか。Antrea Cluster Network Policy では、クラスターレベルで柔軟なエンドポイントのグルーピングが可能になっていることを理解いただけたかと思います。
 
次回以降、Antrea Cluster Network Policy についてより詳細な操作方法などをご紹介していければと思います。