OpenShiftでKubernetes NetworkPolicyのIngressルールを試す。

2021/12/23 23:40修正
#### 経緯・課題
　OpenShiftにおけるNetworkセキュリティの検討のため、Kubernetes NetworkPolicyのルールと挙動をいくつか実機検証しました。
　参考にしたレシピはこちら
　https://github.com/ahmetb/kubernetes-network-policy-recipes/blob/master/04-deny-traffic-from-other-namespaces.md

---
#### 前提・環境など
・IBM Cloud OpenShift 4.8.21 (Managed,クラシックネットワーク環境)
・作業環境：MacOS Big Sur (11.6)

---
#### 参考知識
・[RedHat: OCP4.8 Networking manual](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.8/html-single/networking/index)
・[kubernetes:ラベルとセレクター](https://kubernetes.io/ja/docs/concepts/overview/working-with-objects/labels/)
・[redhat:oc label podコマンド](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.1/html/cli_reference/cli-application-management-commands_cli-developer-commands#label)
・[ラベルとjsonpathを使ったkubectl/ocの出力のフォルタリング](https://zaki-hmkc.hatenablog.com/entry/2019/12/30/093754)

---
#### Kubernetes ネットワークポリシーについて
　クラスタ間の通信制御を行うルールです。
　基本的にネットワーク制御のモジュールであるKubernetes CNIの種類によらず動作します。(Kubernetes CNIの種別や確認方法については[こちら](https://qiita.com/morier/items/9b0177ba50679ce9a343)で紹介しているので参考にしてください。

　今回の検証環境ではIngress(インバウンド通信の許可)ルールのみ扱っています。Egress(アウトバウンド通信の許可)については、OpenShift環境ではサポート有無が分かれるので、アウトバウンド制御に関してはEgress Firewall (OCP4.6ではEgress Network Policyとも呼びます)を利用するか、OCP4.8をオンプレ導入するなどしてEgressルールがサポートされるOVN-KubernetesをCNIとして利用することになります。

#### 記述ルール

#####1.基本ルール

######スコープ
　　　<dl>
　　　　<dt>metadata.namespace</dt>
　　　　<dd>ネットワークポリシーを有効にするnamespaceを指定します。マニフェストyamlで指定することも、oc apply時の -n オプションで指定することもできます。いずれの指定もない場合は、作業中のprojectがスコープになります。</dd> 
　　　  <dt>spec.podSelector</dt>
　　  　<dd>ネットワークポリシーを有効にするpodを指定します。matchLabelsを用いてラベルを指定します。このスコープを指定する場合、上記のnamespaceと合わせてアンド条件を満たすpodにルールが適用されます。同一namespace内でも、podSelectorに合致しないpodに関しては、default設定(ingressは全て許可)のまま、もしくは他に設定中の有効なルールが適用されます。(例えばインバウンドを全てのpodで拒否するルールと併用している場合など)</dd>
　　　</dl>
 
　　　
　######許可(from記述)
<dl>
       <dt>spec.ingress.from.podSelector</dt>
　　  　<dd>インバウンド通信を許可する送信元のpod群を指定します。matchLabelsを用いてラベルを指定します。</dd>
       <dt>spec.ingress.from.namespaceSelector</dt>
　　  　<dd>インバウンド通信を許可する送信元のnamespaceを指定します。</dd>
</dl>

　　　　


#####2.Selector指定の注意点(省略時の解釈がややこしい）
　Kubernetes Network Policyの記述ルールは、省略した時のルールがややこしかったので整理しておきます。(あくまで省略した場合の仕様の紹介で、可読性が悪くなるため明示的に対象podのラベル、namespaceを記述することがレシピの中でも推奨されています)


###### 01.全て拒否(許可ルール無し、を指定する)
　以下は、適用したnamespaceにおいて、全てのインバウンド通信を拒否するルールです。
　・　spec.ingress → 許可ルール指定無し (つまり全て拒否)
　このように、<b>"ingress:[] "</b> で指定することにより、許可ルールなしを宣言することができます。
　このNetworkPolicyを基本として、同一のnamespaceに追加で許可ルールを追加していくことになります。

>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433398/2890412a-2429-5441-973e-26bc0f983b21.png)

###### 02.他のnamespaceからの通信を拒否(同一namespaceからの通信のみを許可)
　以下は、同一namespace内の通信を全て許可するルールです。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433398/c91a583a-062d-bf67-4c64-15f1e823c131.png)

　先程との違いは、<b>"spec.podSelector.matchLabels"</b>と<b>"spec.ingress.from.podSelector:" </b>のように、podSelector行まで追記しているところですが、ここで注意が必要なのは、これらmatchLabels、podSelectorの中身を省略する場合は、<b>全てのpod</b>を宣言していると解釈される点です。

　[04-deny-traffic-from-other-namespaces.md](https://github.com/ahmetb/kubernetes-network-policy-recipes/blob/master/04-deny-traffic-from-other-namespaces.md)を見ると、以下のように説明されています。
>it applies the policy to ALL pods in default namespace as the spec.podSelector.matchLabels is empty and therefore selects all pods.
>it allows traffic from ALL pods in the default namespace, as spec.ingress.from.podSelector is empty and therefore selects all pods.

　ここでは、スコープはtesta namespaceの全てのpodが対象になり、かつ、インバウンド通信は同一namespace内からのみ許可になります。

　namespaceSelector指定の省略については、[05-allow-traffic-from-all-namespaces.md](
https://github.com/ahmetb/kubernetes-network-policy-recipes/blob/master/05-allow-traffic-from-all-namespaces.md)に説明があります。以下のように説明されており、namespaceSelector記述自体を省くと、metadata.namespaceと同一のnamespaceを許可する、と解釈されることがわかります。一方、このケースには相当しませんが、namespaceSelector:{}として、{}内のリストを省略する場合は、全てのnamespaceの全てのpodを許可する、と解釈されることがわかります。

>・Selects all pods in all namespaces (namespaceSelector: {}).
>・By default, if you omit specifying a namespaceSelector it does not select any namespaces, which means it will allow traffic only from the namespace the NetworkPolicy is deployed to.

###### 03.spec.ingress.from.<セレクター>のわかりづらい点
　上述の[05-allow-traffic-from-all-namespaces.md](https://github.com/ahmetb/kubernetes-network-policy-recipes/blob/master/05-allow-traffic-from-all-namespaces.md)にspec.ingress.fromまでの記述の場合の補足説明があります。（podSelectorもnamespaceSelectorも記述無し、from行まで記述）
　このパターンでは、全namespaceの全podを対象としたことになります。
>Note: Dropping all selectors from the spec.ingress.from item has the same effect of matching all pods in all namespaces.e.g.:
>...
>   ingress:
     - from:

　<b> podSelector:{} </b>１行を書くかどうかの差で、対象namespaceが全く変わってしまう(同一namespaceの全てのpodを許可 OR 全てのnamespaceの全てのpodを許可ので、注意が必要です。

　また、最初のdeny-by-defaultと比較しても、spec.ingress.from指定の<b>from行</b>１行を書くかどうかの差で、イングレスを全て拒否、全て許可、と<b>対象範囲が真逆</b>になってしまうので、注意深く記載が必要な点です。


###### 04.spec.ingress.from のAND記述(podSelectorとnamespaceSelector両方のラベルに合致する対象の許可)
　下の例は、[07-allow-traffic-from-some-pods-in-another-namespace.md](https://github.com/ahmetb/kubernetes-network-policy-recipes/blob/master/07-allow-traffic-from-some-pods-in-another-namespace.md)のレシピで、namespaceSelectorとpodSelector両方を満たすpod(AND条件)の指定方法です。
　二つ目のpodSelectorのインデントに<b>"-"</b>がないところがポイントです。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433398/f9854a16-7a3a-c2be-4e5b-76709336b136.png)


###### 05.spec.ingress.from のOR記述(条件に合致する複数パターンの対象の許可)
　下の例は、[10-allowing-traffic-with-multiple-selectors.md](https://github.com/ahmetb/kubernetes-network-policy-recipes/blob/master/10-allowing-traffic-with-multiple-selectors.md)のレシピで、複数マイクロサービスからのインバウンドを許可するなどユースケースで、OR条件でパターンを記述する際の書式例です。
　ここでは３パターンのマイクロサービスを宣言しています。二つ目、三つ目のpodSelectorの頭に <b> "-"</b> があるところがポイントです。

>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433398/a0c4ea4b-438e-2df4-4e28-67f7f89c6e2b.png)


##### 3.labelづけ、label確認など
　各Selectorで参照するラベル付は、以下のようにします。

>(コマンド書式)
>$ oc label pod/<"pod名"> <"key">=<"value">
>$ oc label namespace/<"namespace名"> <"key">=<"value">

>(webという名称のpodに、"app=web"というラベルをつける場合)
>$ oc label pod/web app=web

>(testbという名称のnamespaceに、"purpose=production"というラベルをつける場合)
>$ oc label namespace/testb purpose=production

　設定済みのラベルは以下のようにして確認します。

>$ oc get pod --show-labels
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433398/19773efd-a952-6b8b-1f5a-2f38b2e61328.png)
>$ oc get namespace <"namespace名"> --show-labels
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/433398/e331ee0a-6fb6-db73-d8c5-1aaffb16511e.png)

　testb namespaceでは、namespace作成時にすでにkubernetes.io/metadata.name=testbが割り振られています。ここでは、"purpose=production",および"team=operations"がユーザー後付けでラベル付けされています。

#### 終わりに
　OCP4.8のOVN-kubernetesではネットワークポリシーのegressルールもサポートされるということなので、そのうち試したいです。また、他ネットワーク関連のOCP機能など整理していきたいと思います。