CilinumNetwokPolicy をしっかりと理解する

# 目的
- CilinumNetworkPolicy(以下、CNP) 設定方法をしっかりと理解する
- 一部の CNI ネットワークプロバイダーでサポート可能な NetworkPolicy との違いを理解する
- L7 設定時の認証要求設定を理解する(required?)

# 手段
マニュアルベースで手を動かしていきます。

### ざっくり理解する
![スライド1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474478/74b210c7-5b1f-3329-59c0-fdd8ee205ffd.jpeg)
![スライド2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474478/0804fdd0-ab8c-2f64-3484-bb1734583ae1.jpeg)
![スライド3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474478/d74d26e7-ed46-dedc-f278-e1dde5c56937.jpeg)
![スライド4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474478/f2add58b-cd06-bb19-663f-6e536934eb1e.jpeg)



killercoda の Playgroud を活用。

### 概要を理解する
前提として、k8s 環境で下記 NetworkPolicy を利用できる。
  1. Kubernetes NetworkPolicy
  2. CNP <--- 今回の学習範囲
  3. CiliumClusterwideNetworkPolicy <--- "2" の派生形だと推測。(Namespaceを指定しないタイプの CNP とのこと)

k8s 環境は cilium をサポートしていれば、CNP は全ての node に対して配布及び適用される。(厳密には、CNP の設定方法に依存するようです(ex: CLI, API 経由、等々..)

CNP はポリシー適用の設定が存在する。いわゆる、WAF の検知モード、ブロッキング、のように、設定したポリシーの適用レベルを制御できる。

  - default:
    - policy enforcement のデフォルト設定。このモードでは、全ての ingress/egress トラフィックは許可される。ポリシーに該当するトラフィックは default-deny 状態に遷移する。つまり、明示的にポリシーで許可された通信のみが成功する。言い換えると、policy enforcement がデフォルト設定の場合、CNP ポリシーが無ければ、全ての通信が許可される。逆に特定エンドポイントと通信の方向(ingress or egress)で特定された通信はdefault-deny に遷移する。
  - always:
    - 全てのエンドポイントに対してポリシーは有効化/適用される設定。
  - never:
    - ポリシー設定自体を無効にする。全てのエンドポイントに対して設定したポリシーは適用されない設定。言い換えると、CNP を disable にした状態。全ての ingress/egress 通信は許可される。

``` EnableDefaultDeny.yaml
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy # CNPを指定
metadata:
  name: intercept-all-dns
spec:
  endpointSelector: # endpointSelector で本ポリシー適用対象を指定。nodeSelector 設定も存在する
                    # nodeSelector は CiliumClusterwideNetworkPolicy でのみ利用可能
                    # 基本的に Namespace, Pod  に紐づく label で適用対象を指定
    matchExpressions:                       # matchExpressions で条件を指定可能
      - key: "io.kubernetes.pod.namespace"
        operator: "NotIn"                   # NotIn なので、key に対して value が含まれない、ことが条件
        values:
        - "kube-system"
      - key: "k8s-app"
        operator: "NotIn"
        values:
        - kube-dns
  enableDefaultDeny: # ここが DefaultDeny 設定を制御する設定項目
    egress: false    # この egress 及び ingress を false にすれば、default mode が適用されない(無視される)
    ingress: false
  egress:              # egress や ingress を指定して、どの方向のトラフィックを制御するのか指定する
    - toEndpoints:     # ここでは、to Endpionts で指定したアウトバウンド通信が適用対象となる
        - matchLabels: # 下記 label を有する Pod が適用対象
            io.kubernetes.pod.namespace: kube-system
            k8s-app: kube-dns
      toPorts: # ポートとプロトコルを指定
        - ports:
          - port: "53"
            protocol: TCP
          - port: "53"
            protocol: UDP
          rules:
            dns:
              - matchPattern: "*"

```

CiliumNetworkPolicy(以下、CNP) は下記設定を実現可能です。
  - 特定通信に対する allow 及び deny
  - 条件は L3/L4/L7 レイヤーで指定可能
    - L7 レイヤーは認証必須として設定可能

基本的な設定内容は CNP を適用する対象 Namespace or Pod(いずれも labelで指定)を指定する。まずは、基本的な設定項目を確認します。


### L3 サンプル
L3 ポリシーは下記条件で指定可能です。
  - Endpoints Based: ポリシー内にアドレスのような固定値を含めない
  - Services Based: こちらもポリシー内にアドレスのハードコードを防げる。また、Servcie オブジェクトが IP アドレスを隠蔽してくれる
  - Entities Based: ローカルホストや外部の到達可能なエンドポイントが含まれる
  - NodeBased: remote-node エンティティの拡張版。node単位で設定したいときに使うようだ
  - IP/CIDR Based: いわゆる、192.168.1.1/32, 192.168.1.0/24 で設定する形式
  - DNS Based: DNS lookup で解決可能なエンティティを指定可能

``` SimpleIngressAllow.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
spec:
  endpointSelector: # "role: backend " label を有する Pod が適用対象
    matchLabels:
      role: backend
  ingress:          # "role: backend " label を有するインバウンド通信が適用対象
  - fromEndpoints:  # つまり、"role: backend " label を有する Podに対する、
    - matchLabels:  # "role: backend " label を有する Pod からのインバウンド通信が許可される
        role: frontend
```

``` IngressAllowAllEndpoints.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "allow-all-to-victim"
spec:
  endpointSelector: # "role: victim" label を有する pod が対象
      matchLabels:
      role: victim
  ingress:
  - fromEndpoints: # fromEndPoints をブランク {} 指定で全ての Pod が対象になる
    - {}           # つまり、全 Pod から "role: victim" label を有する pod への通信が許可される
```

注意として、ポリシーはあくまで sender(egress) と recevier(ingress) の両方の設定で許可された通信でないと成功しない。上記設定のように、recevier のみの設定だけでは不十分で、sender の設定が存在しないと通信は成立しない。管理面で考えると、ingressとegressを併記した方が分かり易そうに思う。

``` SimpleEgressAllow.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-egress-rule"
spec:
  endpointSelector: # "role: frontend" を有する Pod から "role: backend" を有する Pod へのアウトバウンド通信を許可
    matchLabels:    # 通信成功には受け側の igress ポリシーが必要
      role: frontend
  egress:
  - toEndpoints:
    - matchLabels:
        role: backend
```

``` Ingress-EgressDefaultDeny.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "deny-all-egress"
spec:
  endpointSelector: # 当該ポリシー以外にポリシーが存在しなければ、
    matchLabels:    # "role: restricted" を有する Pod の Ingress 及び Egress を default-deny にできる？
      role: restricted
  egress:
  - {}
```

``` AdditionalLabelRequirements.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "requires-rule"
specs:
  - description: "For endpoints with env=prod, only allow if source also has label env=prod"
    endpointSelector:
      matchLabels:
        env: prod
    ingress:
    - fromRequires:  # fromRequires は到達可能な基本要件を指定する
      - matchLabels: # "env: prod" label を有する Pod から "env: prod" label を有するエンドポイント 
          env: prod  # に通信可能
```

上記ポリシーだけでは通信を許可するポリシーとして成立しない。
下記ポリシーを組み合わせれば、通信を許可するポリシーとして成立する。

``` fromRequires.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-rule"
specs:
  - description: "For endpoints with env=prod, allow if source also has label role=frontend"
    endpointSelector:  # 上記ポリシーと合わせると、"env: prod" に対するインバウンド通信は、
      matchLabels:     # "role: frontend" 及び "env: prod" を有するエンドポイントから許可される
        env: prod
    ingress:
    - fromEndpoints:
      - matchLabels:
          role: frontend
```

#### ここからはやってみようのコーナー！！

テスト環境は以下の通り。各 Namespace に nginx を1つ起動する。
  - namespace
    - dev
    - stg

``` dev.yaml
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  name: dev
spec: {}
status: {}
EOF
```

``` stg.yaml
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  name: stg
spec: {}
status: {}
EOF
```

  - pod * 2
    - type: source
    - type: dest
    -

``` source.yaml
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    type: source
  name: source
  namespace: dev
spec:
  containers:
  - image: nginx
    name: source
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
EOF
```

``` dest.yaml
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    type: dest
  name: dest
  namespace: stg
spec:
  containers:
  - image: nginx
    name: dest
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
EOF
```

Pod 状態を確認する。

``` sh
controlplane $ k get po -n dev -n stg
NAME     READY   STATUS    RESTARTS   AGE
dest     1/1     Running   0          12s
source   1/1     Running   0          45s
```

続いて、CNPを作成する。

``` fromRequires.yaml
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "requires-rule"
specs:
  - description: "For endpoints with env=prod, only allow if source also has label env=prod"
    endpointSelector:
      matchLabels:
        type: dest
    ingress:
    - fromRequires:
      - matchLabels:
          type: source 
EOF
```

``` sh
controlplane $ k apply -f fromRequires.yaml 
ciliumnetworkpolicy.cilium.io/requires-rule created
controlplane $ k describe cnp requires-rule 
Name:         requires-rule
Namespace:    default # CNP に対して namespace 指定していないため、default
Labels:       <none>  # default だと dev,stg namespace に適用されないのでは？
Annotations:  <none>
API Version:  cilium.io/v2
Kind:         CiliumNetworkPolicy
Metadata:
  Creation Timestamp:  2024-12-03T09:41:52Z
  Generation:          1
  Resource Version:    4174
  UID:                 35d81c5a-0eec-4f22-ac59-e9fa43cfcb45
Specs:
  Description:  For endpoints with env=prod, only allow if source also has label env=prod
  Endpoint Selector:
    Match Labels:
      Type:  dest
  Ingress:
    From Requires:
      Match Labels:
        Type:  source
Status:
  Conditions:
    Last Transition Time:  2024-12-03T09:41:52Z
    Message:               Policy validation succeeded
    Status:                True
    Type:                  Valid
Events:                    <none>
```

続いて Pod の IP アドレスを確認する。

``` sh
controlplane $ k get po -A -o wide |grep -E 'dev|stg'
dev                  source                                    1/1     Running   0             84s   192.168.0.40    controlplane   <none>           <none>
stg                  dest                                      1/1     Running   0             15m   192.168.0.113   controlplane   <none>           <none>
```

``` sh
controlplane $ k exec -it -n dev source -- curl --HEAD http://192.168.0.113
HTTP/1.1 200 OK # sorce pod から dest pod の curl に成功
```

二つ目の CNP を設定する。

``` sh
controlplane $ k apply -f fromRequires2.yaml 
ciliumnetworkpolicy.cilium.io/l3-rule created
controlplane $ k describe cnp l3-rule 
Name:         l3-rule
Namespace:    default # こちらの CNP も namesapce 指定無し
Labels:       <none>
Annotations:  <none>
API Version:  cilium.io/v2
Kind:         CiliumNetworkPolicy
Metadata:
  Creation Timestamp:  2024-12-03T09:50:58Z
  Generation:          1
  Resource Version:    5155
  UID:                 8a502978-d47c-4774-9fde-b7109beffca3
Specs:
  Description:  For endpoints with env=prod, allow if source also has label role=frontend
  Endpoint Selector:
    Match Labels:
      Type:  dest
  Ingress:
    From Endpoints:
      Match Labels:
        Role:  nginx
Status:
  Conditions:
    Last Transition Time:  2024-12-03T09:50:58Z
    Message:               Policy validation succeeded
    Status:                True
    Type:                  Valid
Events:                    <none>
```

sorce nginx に labels を追加する。

``` sh
controlplane $ kubectl -n dev label pods source role=nginx
pod/source labeled
controlplane $ k get po -A -o wide --show-labels |grep -E 'dev|stg'        
dev                  source                                    1/1     Running   0             3m53s   192.168.0.40    controlplane   <none>           <none>            role=nginx,type=source
stg                  dest                                      1/1     Running   0             18m     192.168.0.113   controlplane   <none>           <none>            type=dest
```

source -> dest への curl は成功する。CNP は動作していない。
正確には、namespace は default なので、default 所属の pod に適用されるポリシーとなる。

``` sh
controlplane $ k exec -it -n dev source -- curl --HEAD http://192.168.0.113
HTTP/1.1 200 OK
```

CNP の namespace を dev に変更する。

``` sh
controlplane $ k edit cnp requires-rule
controlplane $ k replace -f /tmp/kubectl-edit-4148436964.yaml --force
controlplane $ k edit cnp l3-rule 
controlplane $ k replace -f /tmp/kubectl-edit-2320713019.yaml --force

controlplane $ k describe -n dev cnp requires-rule |grep -i namespace
Namespace:    dev
controlplane $ k describe -n dev cnp l3-rule |grep -i namespace
Namespace:    dev
```

正しい結果が得られていない。全 CNP を削除して、一つずつ確認する。

``` sh
controlplane $ k describe -n stg cnp l3-allow 
Name:         l3-allow
Namespace:    stg # stg に適用する
Labels:       <none>
Annotations:  <none>
API Version:  cilium.io/v2
Kind:         CiliumNetworkPolicy
Metadata:
  Creation Timestamp:  2024-12-03T10:16:40Z
  Generation:          1
  Resource Version:    7959
  UID:                 9e48c541-744c-472f-8f6c-8e0b35b1e06e
Spec:
  Endpoint Selector:
    Match Labels:
      Type:  dest
  Ingress:
    From Endpoints:
      Match Labels:
        Type:  source # なぜ Type の T は大文字なのか？
Status:
  Conditions:
    Last Transition Time:  2024-12-03T10:16:40Z
    Message:               Policy validation succeeded
    Status:                True
    Type:                  Valid
Events:                    <none>

controlplane $ k exec -it -n dev source -- curl --HEAD -m 2 http://192.168.0.113
curl: (28) Connection timed out after 2000 milliseconds
command terminated with exit code 28 

# curl に失敗する。

controlplane $ k get po -A -o wide --show-labels |grep -E 'dev|stg'
dev                  source                                    1/1     Running   0             26m   192.168.0.40    controlplane   <none>           <none>            type=source
stg                  dest                                      1/1     Running   0             40m   192.168.0.113   controlplane   <none>           <none>            type=dest

# label は正しいように見える。

controlplane $ k get po -A -o wide --show-labels |grep -E 'dev|stg'
dev                  source                                    1/1     Running   0             28m   192.168.0.40    controlplane   <none>           <none>            Type=source,type=source
stg                  dest                                      1/1     Running   0             43m   192.168.0.113   controlplane   <none>           <none>            Type=dest,type=dest
controlplane $ k exec -it -n dev source -- curl --HEAD -m 2 http://192.168.0.113
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28

# 大文字の Type=source タグを付与しても curl は失敗。
# 何か根本的に違う気がする。CNP 設定を見直す。
```

これは receiver のポリシーとなる。通信を許可するには sender のポリシーが必要となる。

``` sh
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l3-allow"
  namespace: stg
spec:
  endpointSelector:
    matchLabels:
      type: dest
  ingress:
  - fromEndpoints:
    - matchLabels:
        type: source
 ```  

``` sh
controlplane $ k exec -it -n dev source -- curl --HEAD -m 2 http://192.168.0.80
curl: (28) Connection timed out after 2001 milliseconds
```

ここまで receiver 及び sender の CNP を作成すれば良いと理解していました。しかし、正しく理解できていないようです。
Killercoda のシナリオを紐解いて理解を深める方針に切り替えます。

``` policy1.yaml
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: policy1
  namespace: app
spec:
  endpointSelector:
    matchLabels: {}
  egress:
  - toEndpoints:
    - matchLabels:
        io.kubernetes.pod.namespace: data
        id: data
EOF

# CNP 作成

controlplane $ k get po -A -o wide --show-labels|grep -E 'id=app|id=data|id=manager'
app                  app1                                      1/1     Running   0             12m   192.168.0.206   controlplane   <none>           <none>            id=app
app                  app2                                      1/1     Running   0             12m   192.168.0.82    controlplane   <none>           <none>            id=app
app                  manager1                                  1/1     Running   0             12m   192.168.0.153   controlplane   <none>           <none>            id=manager
app                  manager2                                  1/1     Running   0             12m   192.168.0.87    controlplane   <none>           <none>            id=manager
data                 data-001                                  1/1     Running   0             12m   192.168.0.172   controlplane   <none>           <none>            id=data
data                 data-002                                  1/1     Running   0             12m   192.168.0.241   controlplane   <none>           <none>            id=data
# POD IP 確認

controlplane $ k -n app exec app1 -- curl --head 192.168.0.172
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0   615    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
HTTP/1.1 200 OK
# data namespace 宛は成功

controlplane $ k -n app exec app1 -- curl --head -m 2 192.168.0.153
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
# data namespace 宛は許可していない。CNP の namespace を default に変更してみる。

controlplane $ k describe cnp policy1 |grep -i namespace
Namespace:    default
# CNP の namespace を app -> default に変更

controlplane $ k -n app exec app1 -- curl --head 192.168.0.172
HTTP/1.1 200 OK
# data namespace 宛は成功

controlplane $ k -n app exec app1 -- curl --head -m 2 192.168.0.153
HTTP/1.1 200 OK
# manager namespace 宛は成功
# つまり、上記 CURL リクエストは CNP 対象ではない。namespace は spec.endpointSelector に
# 指定する対象が所属する namespace を指定するのか？
```

続いて、CNP の namespace を送信先(namespace: manager)に変更してみる。

``` sh
controlplane $ k describe -n data cnp policy1 |grep -i namespace
Namespace:    data
# CNP の namespace を defatul -> data に変更

controlplane $  k -n app exec app1 -- curl --head 192.168.0.172
HTTP/1.1 200 OK
# data namespace 宛は成功

controlplane $ k -n app exec app1 -- curl --head -m 2 192.168.0.153
HTTP/1.1 200 OK
# manager namespace 宛は成功
# つまり、上記 CURL リクエストは CNP 対象ではない。
```

上記結果から、CNP が所属する namespace と CNP 適用対象のエンドポイント(pod等)は一致する必要がある。あと、receiver と sender の概念が理解できていない。片側の policy で動作してると思うんですが...振り返ると、policy enforcement のデフォルト設定は当該通信に該当するポリシーが存在しない場合、default-allow なので、今回のケースもsenderはdefault-allowであるため、通信が成功すると推測。

仕切り直して、namespace * 3で検証する。
  - namespace
    - dev
    - stg
    - prod

``` sh
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  name: dev
spec: {}
status: {}
EOF

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  name: stg
spec: {}
status: {}
EOF

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  name: prod
spec: {}
status: {}
EOF
```

  - pod * 3
    - type: source
    - type: dest
    - type: manager

``` sh
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    type: source
  name: source
  namespace: dev
spec:
  containers:
  - image: nginx
    name: source
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
EOF

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    type: dest
  name: dest
  namespace: stg
spec:
  containers:
  - image: nginx
    name: dest
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
EOF

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    type: manager
  name: manager
  namespace: prod
spec:
  containers:
  - image: nginx
    name: manager
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
EOF

controlplane $ k get po -A -o wide --show-labels | grep -E 'dev|stg|prod'
dev                  source                                    1/1     Running   0             2m     192.168.0.108   controlplane   <none>           <none>            type=source
prod                 manager                                   1/1     Running   0             67s    192.168.0.152   controlplane   <none>           <none>            type=manager
stg                  dest                                      1/1     Running   0             104s   192.168.0.37    controlplane   <none>           <none>            type=dest
# POD IP 確認

controlplane $ k exec -n dev source -- curl --head 192.168.0.37
HTTP/1.1 200

controlplane $ k exec -n dev source -- curl --head 192.168.0.152
 HTTP/1.1 200 OK 
# dev から stg 及び prod に curl 成功

cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: policy1
  namespace: dev
spec:
  endpointSelector:
    matchLabels: {}
  egress:
  - toEndpoints:
    - matchLabels:
        io/metadata.name: stg
        type: dest
EOF
# CNP 作成

k exec -n dev source -- curl --head -m 2 192.168.0.37
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28

k exec -n dev source -- curl --head -m 2 192.168.0.152
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
# 両方とも失敗する

controlplane $ k get  -n dev cnp policy1 -oyaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"cilium.io/v2","kind":"CiliumNetworkPolicy","metadata":{"annotations":{},"name":"policy1","namespace":"dev"},"spec":{"egress":[{"toEndpoints":[{"matchLabels":{"io/metadata.name":"stg","type":"dest"}}]}],"endpointSelector":{"matchLabels":{}}}}
  creationTimestamp: "2024-12-04T10:25:30Z"
  generation: 3
  name: policy1
  namespace: dev
  resourceVersion: "8185"
  uid: 3bd86930-7ea5-4d6e-b053-ebaf9c46b93f
spec:
  egress:
  - toEndpoints:
    - matchLabels:
        io.kubernetes.pod.namespace: stg # この labels を追加すると通信は成功する
        type: dest                       # この label 名はどのように可能可能か？
  endpointSelector:
    matchLabels: {}

k exec -n dev source -- curl --head -m 2 192.168.0.37
HTTP/1.1 200 OK

k exec -n dev source -- curl --head -m 2 192.168.0.152
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
curl: (28) Connection timed out after 2000 milliseconds
# CNP は正しく動作している
```

"io.kubernetes.pod.namespace: stg" の確認方法は？

[ここ](https://github.com/cilium/cilium/issues/30149)で議論されている。
[マニュアル](https://docs.cilium.io/en/v1.14/operations/performance/scalability/identity-relevant-labels/#including-labels)にも説明が存在する。
取り合えず、マニュアルを読んでみる。

[ここのサンプル](https://cilium.io/blog/2018/09/19/kubernetes-network-policies/)はとても分かり易い。
つまり、CNP において、namespace 単位で制限を掛けたい場合、"io.kubernetes.pod.namespace: stg" の記述方法がお作法となる。マニュアルに利用可能な label 一覧があるため、そちらを参照すると理解が深まると思います。


### L4 サンプル
L4 レイヤポリシーは L3 ポリシーに追加可能、独立して設定も可能です。(注意：L4ポリシーが存在しない場合、ICMP を含むすべてのポートは許可される。)

```Example(l4).yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l4-rule"
spec:
  endpointSelector: # 適用対象は "app: myService" を有するエンドポイント
    matchLabels:
      app: myService
  egress:
    - toPorts: # TCP かつ　80でアウトバウンド通信
      - ports:
        - port: "80"
          protocol: TCP
```

```ExamplePortRanges.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "l4-port-range-rule"
spec:
  endpointSelector:
    matchLabels:
      app: myService
  egress:
    - toPorts:
      - ports:
        - port: "80" # 対象は TCP 80～444 ポートのアウトバウンド通信
          endPort: 444
          protocol: TCP

```

```CIDR-dependentLayer4Rule.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "cidr-l4-rule"
spec:
  endpointSelector: # 対象は "role: crawler" を有するエンドポイント
    matchLabels:
      role: crawler
  egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
  - toCIDR:
    - 192.0.2.0/24
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
```

```Example(ICMP/ICMPv6).yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "icmp-rule"
spec:
  endpointSelector: # 対象は "app: myService" を有するエンドポイント
    matchLabels:
      app: myService
  egress: # アウトバウンド通信
  - icmps:
    - fields: 
      - type: 8 # IPv4 の type 8 
        family: IPv4
      - type: EchoRequest # IPv6 の EchoRequest
        family: IPv6
```

検証環境で 80 版を Deny する。

```sh
controlplane $  k get po -A -o wide --show-labels | grep -E 'dev|stg|prod'
dev                  source                                    1/1     Running   0             27s     192.168.0.141   controlplane   <none>           <none>            type=source
prod                 manager                                   1/1     Running   0             27s     192.168.0.106   controlplane   <none>           <none>            type=manager
stg                  dest                                      1/1     Running   0             27s     192.168.0.102   controlplane   <none>           <none>            type=dest
controlplane $ 
controlplane $ k exec -n dev source -- curl --head 192.168.0.106
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 HTTP/1.1 200 OK   0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
 # dev -> prod への 80ポートは成功
 ```

CNP を作成する。

```cidr-l4-rule.yaml
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "cidr-l4-rule"
  namespace: dev
spec:
  endpointSelector: # 対象は "role: crawler" を有するエンドポイント
    matchLabels:
      type: source
      io.kubernetes.pod.namespace: dev
  egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
  - toCIDR:
    - 192.168.0.106/32
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
EOF

k exec -n dev source -- curl --head -m2 192.168.0.106
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
# 192.168.0.106 宛は失敗する
```

CNP内容を変更してみる。

```sh
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "cidr-l4-rule"
  namespace: dev
spec:
  endpointSelector: # 対象は "role: crawler" を有するエンドポイント
    matchLabels:
      type: source
      io.kubernetes.pod.namespace: dev
  egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
  - toPorts:
    - ports:
      - port: "80"
        protocol: TCP
EOF

controlplane $ k exec -n dev source -- curl --head 192.168.0.106
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0   615    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
HTTP/1.1 200 OK

controlplane $ k exec -n dev source -- curl --head 192.168.0.102
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0   615    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
HTTP/1.1 200 OK
# 80 は allow

controlplane $ k exec -n dev source -- curl --head -m2 https://192.168.0.106
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
curl: (28) Connection timed out after 2001 milliseconds
command terminated with exit code 28
controlplane $ k exec -n dev source -- curl --head -m2 https://192.168.0.102
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
curl: (28) Connection timed out after 2000 milliseconds
command terminated with exit code 28
# 443通信は deny 
```

80 のみを deny に変更する。

``` sh
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "cidr-l4-rule"
  namespace: dev
spec:
  endpointSelector: # 対象は "role: crawler" を有するエンドポイント
    matchLabels:
      type: source
      io.kubernetes.pod.namespace: dev
  egress: # 対象は、アウトバウンド通信、192.0.2.0/24 宛、TCP:80ポート通信
  - toPorts:
    - ports:
      - port: "80"
        protocol: TCP
EOF
# 80 番の Deny 設定作成に伴い、default-deny モードに移行する。故に、当該エンドポイントに対して、明示的に許可ポリシーを設定しない限り、アウトバウンド通信は許可されない。

### L7 サンプル

```AllowGET-public.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
spec:
  description: "Allow HTTP GET /public from env=prod to app=service"
  endpointSelector:
    matchLabels:
      app: service # 対象
  ingress:
  - fromEndpoints: # 対象は "env: prod" を有するエンドポイントからのインバウンド通信
    - matchLabels: # かつ、TCP:80 で、GET /public のみ許可する
        env: prod
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/public"
```

GET / で設定してみる。

``` sh
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      type: source
      io.kubernetes.pod.namespace: dev
  ingress:
  - fromEndpoints:
    - matchLabels: 
        type: dest
        io.kubernetes.pod.namespace: stg
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/"
EOF
```

### 最後に L7 で認証を requiredする方法は？
[マニュアル](https://docs.cilium.io/en/stable/network/servicemesh/mutual-authentication/mutual-authentication-example/#enforce-mutual-authentication)に明記されている。

``` sh
authentication:
    mode: "required"
```

L7 設定に authentication 設定を追加してみる。

``` sh
cat <<EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
  namespace: dev
spec:
  endpointSelector:
    matchLabels:
      type: source
      io.kubernetes.pod.namespace: dev
  ingress:
  - fromEndpoints:
    - matchLabels: 
        type: dest
        io.kubernetes.pod.namespace: stg
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/"
    authentication:
      mode: "required"
EOF

# 上記 yaml で設定可能

controlplane $ k describe -n dev cnp rule1 
Name:         rule1
Namespace:    dev
Labels:       <none>
Annotations:  <none>
API Version:  cilium.io/v2
Kind:         CiliumNetworkPolicy
Metadata:
  Creation Timestamp:  2024-12-10T10:23:43Z
  Generation:          1
  Resource Version:    6643
  UID:                 b3635ede-e4c4-4bab-8813-3fafe574c2f0
Spec:
  Endpoint Selector:
    Match Labels:
      io.kubernetes.pod.namespace:  dev
      Type:                         source
  Ingress:
    Authentication:
      Mode:  required
    From Endpoints:
      Match Labels:
        io.kubernetes.pod.namespace:  stg
        Type:                         dest
    To Ports:
      Ports:
        Port:      80
        Protocol:  TCP
      Rules:
        Http:
          Method:  GET
          Path:    /
Status:
  Conditions:
    Last Transition Time:  2024-12-10T10:23:43Z
    Message:               Policy validation succeeded
    Status:                True
    Type:                  Valid
Events:                    <none>
# describe結果
```


# あとがき
Networkpolicyは別の記事に書きます。。。


# ソース
- [Overview of Network Policy](https://docs.cilium.io/en/latest/security/policy/index.html)
- [Deny Policy](https://docs.cilium.io/en/latest/security/policy/language/#deny-policies)