NetworkPolicyでtrafficを制御しよう

ogp

はじめに

こんにちは。enechainのPlatform Engineering Deskで働いているsoma00333です。

enechainではproductのdeploy先としてGKEを採用しており、Platform Engineering DeskではKubernetes Clusterの運用業務を行っています。 enechainは「エネルギーの取引所を作る」というmissionを持っており、productも増えてきています。 Platform Engineering Deskも今後ますますsecurityに力を入れていく予定です。

前回は、Platform Engineering Deskのsecurityに関する取り組みの一例として、Pod Security Admissionを紹介しました。

Pod Security Admissionの紹介

今回は、引き続きsecurityに関する取り組みの一例としてNetworkPolicyを紹介します。

NetworkPolicyについて

概要

NetworkPolicyはPodに対してPod間の通信や外部のendpointへの通信を制御するためのResourceです。 NetworkPolicyを利用することで、不正アクセスや意図しない通信を防ぎ、Kubernetesクラスタ内のセキュリティを向上させることができます。

defaultではPodは分離されていない状態となるため、すべてのsourceからのtrafficを受信してしまいます。しかし、Podに対してNetworkPolicyを正しく設定すると、Podは意図しないすべての通信を拒否するようになります。

Podが通信できる対象は以下の3つの識別子の組み合わせによって識別されます。

  1. podSelector: 許可されている他のPod
  2. namespaceSelector: 許可されているNamespace
  3. ibBlock: IPブロック

以下に公式documentで紹介されているmanifestの例を記載します。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

podSelectorについて

このfieldでNetworkPolicyを適用するPodを選択します。

podSelectorを空にすると、NetworkPolicyの属するNamespace上の全てのPodが対象となります。

Policyの例では、label "role=db"を持つPodを選択しています。

 podSelector:
    matchLabels:
      role: db

policyTypesについて

このfieldでNetworkPolicyがIngress(選択したPodへの内向きのtraffic)のPolicyか、Egress(選択したPodからの外向きのtraffic)のPolicyか、または両方なのかを指定します。 policyTypesを指定しなかった場合、defaultで常に Ingressが指定され、NetworkPolicyにegress ruleが1つでもあればEgressも設定されます。

Policyの例では、IngressとEgressの両方を選択しています。

  policyTypes:
  - Ingress
  - Egress

ingress / egressについて

設定項目は以下のとおりです。

設定項目 概要
ingress このfieldで、許可するingressのruleをlistで指定します。各ruleはfromとports sectionの両方に一致するtrafficを許可します。sourceの選択方法はipBlock、namespaceSelector、podSelectorの3つがあります。また、fromを省略したり空で指定した場合は全てのtrafficが対象となります。
egress このfieldで、許可するegressのruleをlistで指定します。各ruleはtoとports sectionの両方に一致するtrafficを許可します。sourceの選択方法はipBlock、namespaceSelector、podSelectorの3つがあります。また、toを省略したり空で指定した場合は全てのtrafficが対象となります。
ipBlock 特定のIPのCIDRの範囲を選択して、ingressの送信元またはegressの送信先を許可します。指定できるfieldは以下の通りです。1.cidr : CIDR表記でIP addressの範囲を指定 2.except : cidr で指定した範囲内から除外したい範囲をCIDRで指定
namespaceSelector 特定のNamespaceを選択して、そのNamespace内のすべてのPodについて、ingressの送信元またはegressの送信先を許可します。このfieldが与えられたが空だった場合、全てのNamespaceが選択されます。
podSelector NetworkPolicyと同じNamespace上の特定のPodを選択して、ingressの送信元またはegressの送信先を許可します。このfieldが与えられたが空だった場合、NetworkPolicyが属しているNamespaceの全てのPodが選択されます。
ports このfieldで、許可するport番号をlistで指定します。このfieldを省略したり空で指定した場合は全てのportが対象となります。指定できるfieldは以下の通りです。1.port : port名か番号でportを指定 2.protocol : TCP / UDP / SCTP からprotocolを指定

Policyの例

以上より、Policyの例ではingressとegressに対して以下のような制御を行なっています

  • ingress: TCPの6379番portへの接続かつ次の送信元からのものを許可する
    • default Namespace内のlabel "role=frontend"が付いたすべてのPod
    • label "project=myproject"が付いたNamespace内のすべてのPod
    • 172.17.1.0/24の範囲を除く172.17.0.0/16の範囲内のすべてのIP address
  • egress: TCPの5978番port上でのCIDR 10.0.0.0/24への接続を許可する
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

NetworkPolicyの設定

enechainで行っているNetworkPolicyの設定を紹介します。

前提

enechainではGKEのStandard Clusterを運用しており、network制御はDataplane V2を使用しています。Dataplane V2ではdefaultでNetworkPolicyの使用が可能です。 また、環境へのアクセスをWhitelistでIP制限しており、AWS Client VPNを利用して、アクセスしています

enechainではapplicationの標準構成としてfrontend, bff, backendの3構成を使用するよう標準化が進んでおり、backendやmicroserviceはinternalな通信を行います。

以下の図のような構成です。

architecture

Platform Engineeringに関する詳細はこちらのblogをご覧ください。

マイクロサービスを量産するための技術基盤

狙い

全体

基本的な構成としてingressを全て拒否かつegressを全て許可し、podごとにingressを設定する方針をとっています。

egressを全て許可している理由は以下です

  • Ingressを絞るだけでも外部からの不正アクセスを防ぎ、セキュリティリスクを十分に軽減することができる
  • 初期設定としてシンプルで扱いやすく、また必要に応じて後からegressを制限する方が初期段階での設定ミスのリスクが少なくなる
  • egressを絞ると、必要な外部アクセスにアクセスできなくなるリスクがあり、運用上の障害や利便性の低下につながる

ただ、一部Security要件の高いものに関してはegressも絞っています。enechainのSecurity要件は日に日に高くなってきており、今後egressの明示的な管理も視野に入れています。

frontend

frontendのingressで明示的に許可しているのは以下の内容です

  • GCLBのhealth checkのSource IP Range
  • DatadogのService Discoveryを利用するためのDatadog用のNamespace

bff

bffのingressで明示的に許可しているのは以下の内容です

  • GCLBのhealth checkのSource IP Range

backend

backendのingressで明示的に許可しているのは以下の内容です

  • bffのpod

microservice

microserviceのingressで明示的に許可しているのは以下の内容です

  • 通信対象のpod
  • (通信対象のpodが存在するNamespace)

一部namespace selectorが使用されていますが、明示的なpod selectorでの管理へと移行予定です。

dev環境の設定

dev環境ではdebug時にlocal pcからアクセスできるように、ingressでいくつかのIP Rangeを許可しています

  • Debug用のIP Address
    • enechainのofficeのIP Range
    • AWS Client VPNのIP Range

実装

実際の実装例を紹介します。

全て

全てのPodに対してIngressの拒否とegressの許可設定をします

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: deny-all-networkpolicy
spec:
  policyTypes:
  - Ingress
  - Egress
  podSelector: {}
  egress:
  - {}

frontend

frontendのnetwork policyは実際に以下の内容を設定しています。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: <service-name>-frontend
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: <service-name>-frontend
      app.kubernetes.io/component: frontend
      app.kubernetes.io/part-of: <service-name>
  policyTypes:
  - Ingress
  ingress:
  - from:
    ### GCLBのhealth checkのSource IP Range
    - ipBlock:
        cidr: 35.191.0.0/16  # ingress probe
    - ipBlock:
        cidr: 130.211.0.0/22 # ingress probe

    ### DatadogのService Discoveryを利用するためのDatadog用のNamespace
    - namespaceSelector:
        matchLabels:
              kubernetes.io/metadata.name: <datadog-namespace>

    ports:
    - protocol: TCP
      port: xxxx

bff

bffのnetwork policyは実際に以下の内容を設定しています。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: <service-name>-bff
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: <service-name>-bff
      app.kubernetes.io/component: bff
      app.kubernetes.io/part-of: <service-name>
  policyTypes:
  - Ingress
  ingress:
  - from:
    ### GCLBのhealth checkのSource IP Range
    - ipBlock:
        cidr: 35.191.0.0/16  # ingress probe
    - ipBlock:
        cidr: 130.211.0.0/22 # ingress probe

    ports:
    - protocol: TCP
      port: xxxx

backend

backendのnetwork policyは実際に以下の内容を設定しています。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: <service-name>-backend
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: <service-name>-backend
      app.kubernetes.io/component: backend
      app.kubernetes.io/part-of: <service-name>
  policyTypes:
  - Ingress
  ingress:
  - from:
    ### bffのpod
    - podSelector:
          matchLabels:
              app.kubernetes.io/name: <service-name>-bff
              app.kubernetes.io/component: bff
              app.kubernetes.io/part-of: <service-name>

    ports:
    - protocol: TCP
      port: xxxx

microservice

microserviceのnetwork policyは実際に以下の内容を設定しています。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: <service-name>
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: <service-name>
      app.kubernetes.io/component: microservice
      app.kubernetes.io/part-of: <service-name>
  policyTypes:
  - Ingress
  ingress:
  - from:
      ### 他のserviceのpod
      - podSelector:
          matchLabels:
            app.kubernetes.io/name: <other-service-name>
              app.kubernetes.io/component: <other-component-name>
              app.kubernetes.io/part-of: <other-service-name>

    ports:
    - protocol: TCP
      port: xxxx

課題と今後の取り組み

現状の課題と今後の取り組みについてまとめます。

ingress/egressの設定を見直す

ingress

以下のような課題があり、セキュリティ要件が高まるのに合わせて設定を見直す予定です。

  • 一部設定でpod selectorではなくnamespace selectorを利用してしまっている
  • 一部設定でCloud Nat用のIP Range(CI/CD用のGKE Clusterの存在するGCP ProjectのCloud NatのIP Range)を許可している
  • productionの一部設定でDebug用のIP Addressを許可している

egress

基本的にはegressは全て許可しているため、productのsecurity要件に応じて明示的な管理が必要です。

OPA(Open Policy Agent)等でポリシーを強制する

現状ポリシー制御を行っているわけではないので、reviewプロセスの中でどうしても漏れが発生します。設定ミスを無くしたり、設定漏れをなくすためにも、OPAをはじめとしたポリシー制御の仕組みの導入を検討しています。

また、security要件の高いproductを中心に、監視体制を強化していく予定です。

おわりに

今回はsecurityに関する取り組みの一例としてNetworkPolicyについて紹介しました。 enechainのPlatform Engineering Deskは横軸engineeringを推進できる非常に魅力的な環境です。

enechainではPlatform Engineerはもちろん、一緒に事業・組織を盛り上げてくれるSWE/Designer/EMを募集しています。

興味を持っていただけたら、是非カジュアルにお話しましょう。カジュアル面談は以下のフォームからお申し込みいただけます。お待ちしております!

カジュアル面談 herp.careers

SREポジション herp.careers herp.careers