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
kubernetes:ラベルとセレクター
redhat:oc label podコマンド
ラベルとjsonpathを使ったkubectl/ocの出力のフォルタリング


Kubernetes ネットワークポリシーについて

 クラスタ間の通信制御を行うルールです。
 基本的にネットワーク制御のモジュールであるKubernetes CNIの種類によらず動作します。(Kubernetes CNIの種別や確認方法についてはこちらで紹介しているので参考にしてください。

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

記述ルール

1.基本ルール
スコープ

   


    
metadata.namespace

    
ネットワークポリシーを有効にするnamespaceを指定します。マニフェストyamlで指定することも、oc apply時の -n オプションで指定することもできます。いずれの指定もない場合は、作業中のprojectがスコープになります。

   
spec.podSelector

    
ネットワークポリシーを有効にするpodを指定します。matchLabelsを用いてラベルを指定します。このスコープを指定する場合、上記のnamespaceと合わせてアンド条件を満たすpodにルールが適用されます。同一namespace内でも、podSelectorに合致しないpodに関しては、default設定(ingressは全て許可)のまま、もしくは他に設定中の有効なルールが適用されます。(例えばインバウンドを全てのpodで拒否するルールと併用している場合など)

   

   
 ######許可(from記述)


spec.ingress.from.podSelector

    
インバウンド通信を許可する送信元のpod群を指定します。matchLabelsを用いてラベルを指定します。

spec.ingress.from.namespaceSelector

    
インバウンド通信を許可する送信元のnamespaceを指定します。

    

2.Selector指定の注意点(省略時の解釈がややこしい)

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

01.全て拒否(許可ルール無し、を指定する)

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

02.他のnamespaceからの通信を拒否(同一namespaceからの通信のみを許可)

 以下は、同一namespace内の通信を全て許可するルールです。

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

 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に説明があります。以下のように説明されており、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に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:

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

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

04.spec.ingress.from のAND記述(podSelectorとnamespaceSelector両方のラベルに合致する対象の許可)

 下の例は、07-allow-traffic-from-some-pods-in-another-namespace.mdのレシピで、namespaceSelectorとpodSelector両方を満たすpod(AND条件)の指定方法です。
 二つ目のpodSelectorのインデントに"-"がないところがポイントです。

05.spec.ingress.from のOR記述(条件に合致する複数パターンの対象の許可)

 下の例は、10-allowing-traffic-with-multiple-selectors.mdのレシピで、複数マイクロサービスからのインバウンドを許可するなどユースケースで、OR条件でパターンを記述する際の書式例です。
 ここでは3パターンのマイクロサービスを宣言しています。二つ目、三つ目のpodSelectorの頭に "-" があるところがポイントです。

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

$ oc get namespace <"namespace名"> --show-labels

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

終わりに

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