デフォルト拒否から始めるポリシー設計
目標
ネームスペースの通信のデフォルトを拒否に覆し、必要な経路だけをラベルで選んで開くポリシーのセットを、自分で書きます。
なぜ重要なのか
NetworkPolicyでいちばん間違えやすいのは、文法ではなく方向と結合の仕方です。ポリシーは、トラフィックを送る側ではなく受け取る側に付きます。webからapiへの通信を許可したいなら、ポリシーのpodSelectorがapiでなければならず、webはingress.fromの中に登場します。また、from配列の項目同士はORで、1つの項目内のnamespaceSelectorとpodSelectorはANDです。ハイフンの位置1つで、「特定のネームスペースの特定のPod」が「そのネームスペースのすべてのPod、またはどこにあってもそのPod」に変わります。そして、エグレスを塞ぐときは、DNSの例外を必ずセットで入れてください。実際の本番環境で、デフォルト拒否だけを先に入れたために名前解決が止まり、サービス全体が連鎖的に停止した事例が繰り返し報告されています。このラボのクラスターはPod間で実際のパケットが流れないので、採点はポリシーオブジェクトの内容とセレクターの意味で行われます。文法と考え方をここで固め、実際の遮断の確認は、CNIのある環境で行えばよいのです。
ステップ
- ネームスペース
netpol-labを作成し、/opt/lab/fixtures/k8s/workloads.yamlをその中に適用してください。結果として、web、api、dbの3つのPodがそれぞれ自分の名前と同じappラベルを持ち、apiのPodは追加でtier=backendラベルを持っている必要があります。 netpol-labにNetworkPolicydefault-deny-ingressを作成してください。podSelectorは空のオブジェクト{}で、policyTypesはIngressだけ、ingressルールは1つもあってはいけません。- NetworkPolicy
allow-web-to-apiを作成してください。podSelector.matchLabels.appはapi、ingress[0].from[0]はpodSelector.matchLabels.app: webだけを持ちます(同じ項目にnamespaceSelectorを入れないでください)。許可するポートはTCP8080です。 - NetworkPolicy
default-deny-egressを作成してください。policyTypesはEgressだけです。続けてallow-dns-egressを作成し、egress[0].to[0].namespaceSelector.matchLabelsにkubernetes.io/metadata.name: kube-systemを書き、egress[0].portsにはポート53をUDPとTCPの2項目として入れてください。 - ネームスペース
monitoringを作成し、ラベルpurpose=monitoringを付けてください。そのあと、netpol-labにNetworkPolicyallow-from-monitoringを作成します。podSelector.matchLabels.appはapi、ingress[0].from[0].namespaceSelector.matchLabels.purposeはmonitoring、許可するポートは9090です。 - NetworkPolicy
allow-office-cidrを作成してください。podSelector.matchLabels.appはwebで、ingress[0].from[0].ipBlock.cidrは10.0.0.0/16、exceptは10.0.5.0/24の1つだけです。 - NetworkPolicy
api-fullを作成してください。podSelector.matchLabels.appはapi、policyTypesはIngressとEgressの両方です。ingress[0].fromには項目が2つ以上(例: webのPodとmonitoringネームスペース)入っている必要があり、egress[0].to[0]はpodSelector.matchLabels.app: db、許可するポートは5432です。 /root/ops/netpol/out/policy-map.jsonを作成してください。最上位のキーはweb、api、dbの3つで、各値はそのPodを選ぶポリシー名の配列です。空のセレクターを持つポリシーは3つのPodすべてに入っている必要があり、allow-web-to-apiはapiにだけ入っている必要があります(webやdbに入れてはいけません)。allow-office-cidrはwebに、api-fullはapiに入ります。最終的に、netpol-labのNetworkPolicyは6個以上である必要があります。
参考
- フィクスチャは、
kubectl apply -f /opt/lab/fixtures/k8s/workloads.yaml -n netpol-labで適用します。フィクスチャのファイル自体は変更しないでください。 kubernetes.io/metadata.nameは、Kubernetesがすべてのネームスペースに自動で付けてくれるラベルなので、別途ラベルを付けなくてもネームスペースを選べます。逆にpurpose=monitoringのように自分で決めたラベルは、ネームスペースに実際に付ける必要があります。- ポリシーの内容確認は、
kubectl get netpol <이름> -n netpol-lab -o yamlがいちばん早いです。kubectl apply --dry-run=clientで、タイプミスを事前に取り除いてください。 - よくある間違い1: ステップ3で
podSelectorをwebにしてしまうことです。ポリシーは受け取る側に付きます。 - よくある間違い2: ステップ4でDNSをUDPだけ開けてしまうことです。応答が大きいとTCP 53に切り替わります。
- よくある間違い3: ステップ7で、
fromの1つの項目の中に2つのセレクターを入れてしまうことです。それはORではなくANDになり、はるかに狭いルールになります。 - ラボのPodはラボごとに新しく起動するので、前のラボで作ったクラスターの状態は残っていません。ネームスペースとPodは、このラボの中で自分で作成してください。運用手順を記憶ではなくランブックとマニフェストとして残すべき理由が、まさにここにあります。
ラボ用のネームスペースとラベル付きPodを作成する
ネームスペースnetpol-labを作成し、/opt/lab/fixtures/k8s/workloads.yamlをその中に適用してください。結果として、web、api、dbの3つのPodがそれぞれ自分の名前と同じappラベルを持ち、apiのPodは追加でtier=backendラベルを持っている必要があります。
ポリシーはラベルだけで対象を選びます。フィクスチャに3つのPodが用意されているので、そのまま適用しますが、どのネームスペースに適用するのかを確認してください。
イングレスのデフォルト拒否ポリシーを作成する
netpol-labにNetworkPolicydefault-deny-ingressを作成してください。podSelectorは空のオブジェクト{}で、policyTypesはIngressだけ、ingressルールは1つもあってはいけません。
ネームスペース全体を選ぶセレクターは、空のセレクターです。すべて拒否するとは、許可ルールを1つも書かないことです。
webからapiへのポートだけを開ける
NetworkPolicyallow-web-to-apiを作成してください。podSelector.matchLabels.appはapi、ingress[0].from[0]はpodSelector.matchLabels.app: webだけを持ちます(同じ項目にnamespaceSelectorを入れないでください)。許可するポートはTCP8080です。
ポリシーは送る側ではなく、受け取る側に付きます。同じネームスペースの中なので、相手を選ぶときにネームスペースセレクターを混ぜないでください。
エグレスのデフォルト拒否とDNSの例外
NetworkPolicydefault-deny-egressを作成してください。policyTypesはEgressだけです。続けてallow-dns-egressを作成し、egress[0].to[0].namespaceSelector.matchLabelsにkubernetes.io/metadata.name: kube-systemを書き、egress[0].portsにはポート53をUDPとTCPの2項目として入れてください。
エグレスを塞ぐと、まず名前解決が死にます。DNSはUDPだけでは済まないという点と、対象のネームスペースを選ぶ標準のラベルが何かを確認してください。
別のネームスペースから来るトラフィックを許可する
ネームスペースmonitoringを作成し、ラベルpurpose=monitoringを付けてください。そのあと、netpol-labにNetworkPolicyallow-from-monitoringを作成します。podSelector.matchLabels.appはapi、ingress[0].from[0].namespaceSelector.matchLabels.purposeはmonitoring、許可するポートは9090です。
ネームスペースを選ぶには、そのネームスペースにラベルが実際に付いている必要があります。セレクターが選ぶ対象がなければ、ポリシーは何の意味も持ちません。
IP範囲の許可と例外の指定
NetworkPolicyallow-office-cidrを作成してください。podSelector.matchLabels.appはwebで、ingress[0].from[0].ipBlock.cidrは10.0.0.0/16、exceptは10.0.5.0/24の1つだけです。
ipBlockは、広く開けて狭く除外する形で使います。除外する範囲は、必ず許可する範囲の中に含まれている必要があります。
イングレスとエグレスを1つのポリシーにまとめる
NetworkPolicyapi-fullを作成してください。podSelector.matchLabels.appはapi、policyTypesはIngressとEgressの両方です。ingress[0].fromには項目が2つ以上(例: webのPodとmonitoringネームスペース)入っている必要があり、egress[0].to[0]はpodSelector.matchLabels.app: db、許可するポートは5432です。
from配列の項目同士はORです。2種類の送信元を許可するには、項目を2つに分けてください。出ていく側は、データベースのポートだけが開きます。
Podごとの適用ポリシーのマップを作成する
/root/ops/netpol/out/policy-map.jsonを作成してください。最上位のキーはweb、api、dbの3つで、各値はそのPodを選ぶポリシー名の配列です。空のセレクターを持つポリシーは3つのPodすべてに入っている必要があり、allow-web-to-apiはapiにだけ入っている必要があります(webやdbに入れてはいけません)。allow-office-cidrはwebに、api-fullはapiに入ります。最終的に、netpol-labのNetworkPolicyは6個以上である必要があります。
空のセレクターを持つポリシーは、すべてのPodに適用されます。ポリシーが選ぶのは受け取る側だという点を、マップに反映してください。