GKE において、FQDN に基づく Network Policy が定義できるようになりました。
この記事では、FQDN Network Policy の概要と、実際に使ってみた様子をご紹介したいと思います。
リリースノートはこちら。
FQDN Network Policy 登場の背景と概要
GKE では現在 2 つのネットワーキングの実装(Calico と Dataplane V2 (cillium))1 が提供されており、iptable に基づく制御か、eBPF に基づく制御か、といった違いはあるものの、いずれも Kubernetes Network Policy を利用することができました。
Kubernetes Network Policy では、クラスタ内における Pod 間通信であれば、namespaceSelector や podSelector といったラベルで送信元や宛先のワークロードを選択することができます。
一方、送信元や宛先がクラスタ外となる場合には、ipBlocks で対象の IP アドレスレンジを指定する以外に Network Policy で通信を制御する方法はありませんでした。
今回新たに登場した FQDN Network Policy では、Pod から送信する通信の宛先がクラスタ外となる場合に、FQDN で宛先を指定することができるようになりました。
これにより、宛先の IP アドレスが変動するような場合でも、FQDN に基づいて名前解決ができれば Network Policy を変更することなく下り通信(クラスタ内からクラスタ外への通信)を制御することができます。
許可する FQDN の指定には、正規表現を用いたパターンマッチングを利用することも可能となっています。
現時点では許可設定のみ行うことができ、明示的な拒否設定はできません。
Kubernetes Network Policy と同様、ポリシーを適用すると、対象のワークロードは許可された宛先以外には通信できなくなります(未許可のドメインに対しても dig や nslookup による名前解決は行うことができますが、実際には解決された IP アドレス宛てに通信することができなくなります)。
ちなみに、この記事の執筆時点ではプレビューでの提供となっていますが、今後 GA した際には有料の機能となるようです。
FQDN network policy is a paid feature, but payment will not be required at this time until FQDN network policy becomes a generally available offering.
利用にあたり、現時点では以下のような制約があります。
(なお、制約の正確なリストは 公式ドキュメント を参照ください)
- 1.26.4-gke.500+ もしくは 1.27.1-gke.400+ の GKE が必要(Standard でも Autopilot でも利用可能)
- 対象の GKE クラスタで Dataplane V2 が有効化されていること
- GKE のクラスタ内 DNS の機能である kube-dns または Cloud DNS が使われていること(自己管理の DNS サーバはサポート対象外)
- Anthos Service Mesh を有効化している環境では利用できない
- FQDNNetworkPolicy リソースのネットワーク ポリシー ロギングはサポートされていない
実機検証
今回は 1.26.4-gke.500 の GKE Standard のクラスタを用意しました。
FQDN Network Policy の有効化は、クラスタの作成時でも、作成済みのクラスタに後からでも行えます。
今回は FQDN Network Policy を有効化せずに既に作成していたクラスタに対して、後から有効化してみることにします。
$ gcloud container clusters describe sample-cluster currentMasterVersion: 1.26.4-gke.500 currentNodeVersion: 1.26.4-gke.500 name: sample-cluster networkConfig: dnsConfig: clusterDns: CLOUD_DNS clusterDnsScope: CLUSTER_SCOPE enableFqdnNetworkPolicy: true ... $ gcloud beta container clusters update sample-cluster --enable-fqdn-network-policy Updating sample-cluster ...done. Updated [https://container.googleapis.com/v1beta1/projects/**********/zones/us-central1-c/clusters/sample-cluster]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/... $ kubectl rollout restart ds -n kube-system anetd daemonset.apps/anetd restarted
上記の手順によってクラスタに FQDNNetworkPolicy というリソースを扱うための CRD が展開されます。
展開された CRD は kubectl get/describe crd コマンドなどで確認することができます。
$ kubectl describe crd fqdnnetworkpolicies.networking.gke.io Name: fqdnnetworkpolicies.networking.gke.io Namespace: Labels: addonmanager.kubernetes.io/mode=Reconcile Annotations: components.gke.io/layer: addon controller-gen.kubebuilder.io/version: v0.6.2 API Version: apiextensions.k8s.io/v1 Kind: CustomResourceDefinition Spec: Conversion: Strategy: None Group: networking.gke.io Names: Kind: FQDNNetworkPolicy List Kind: FQDNNetworkPolicyList Plural: fqdnnetworkpolicies Short Names: fqdnnp Singular: fqdnnetworkpolicy Scope: Namespaced ...
kube-system に展開されている anetd (Dataplane V2 を構成するサービスのひとつ)を再起動する必要がありますので注意しましょう。
(私はこの手順を見逃して少々の時間を溶かしました)
準備ができたので FQDNNetworkPolicy を定義して適用してみましょう。
動作確認のために Nginx のサンプルアプリも展開します。
$ cat fqdnnetpol.yaml apiVersion: networking.gke.io/v1alpha1 kind: FQDNNetworkPolicy metadata: name: allow-out-fqdnnp spec: podSelector: matchLabels: app: curl-client egress: - matches: - pattern: "*.yourdomain.com" - name: "www.google.com" ports: - protocol: "TCP" port: 443 $ kubectl apply -f fqdnnetpol.yaml fqdnnetworkpolicy.networking.gke.io/allow-out-fqdnnp created $ kubectl run nginx --image nginx pod/nginx created $ kubectl get pod nginx --show-labels NAME READY STATUS RESTARTS AGE LABELS nginx 1/1 Running 0 3d23h app=curl-client
今回はコンテナに kubectl exec コマンドでログインして動作を確認します。
まずは FQDNNetworkPolicy で許可したドメインと、そうでないドメインへそれぞれ nslookup および curl コマンドを実行してみましょう。
$ kubectl exec nginx -it -- bash root@nginx:/# nslookup www.google.com Server: 169.254.169.254 Address: 169.254.169.254#53 Non-authoritative answer: Name: www.google.com Address: 142.250.1.99 Name: www.google.com Address: 142.250.1.104 Name: www.google.com Address: 142.250.1.105 Name: www.google.com Address: 142.250.1.106 Name: www.google.com Address: 142.250.1.147 Name: www.google.com Address: 142.250.1.103 Name: www.google.com Address: 2607:f8b0:4001:c0f::67 Name: www.google.com Address: 2607:f8b0:4001:c0f::69 Name: www.google.com Address: 2607:f8b0:4001:c0f::93 Name: www.google.com Address: 2607:f8b0:4001:c0f::68 root@nginx:/# curl -I -m 3 https://www.google.com HTTP/2 200 ... root@nginx:/# nslookup www.cloudflare.com Server: 169.254.169.254 Address: 169.254.169.254#53 Non-authoritative answer: Name: www.cloudflare.com Address: 104.16.123.96 Name: www.cloudflare.com Address: 104.16.124.96 Name: www.cloudflare.com Address: 2606:4700::6810:7c60 Name: www.cloudflare.com Address: 2606:4700::6810:7b60 root@nginx:/# curl -I -m 3 https://www.cloudflare.com curl: (28) Connection timed out after 3000 milliseconds
期待どおり、許可されたドメインにのみアクセスできる状態となっています。
許可されていないドメインへの通信はエラー返却ではなくドロップになるため、アプリケーションの視点ではリトライやタイムアウトの上限に達するまで通信の失敗に気付けない点に注意が必要です。
ちなみに、適用したポリシーはプロトコルを指定した許可設定のため、http (port 80) でアクセスしようとすると、許可されたドメインであっても通信は失敗(ドロップ)します。
root@nginx:/# curl -I -m 3 http://www.google.com curl: (28) Connection timed out after 3000 milliseconds
なお、プロトコルは TCP, UDP, ALL の 3 つから選択でき、無指定の場合は ALL になります。
ポリシーの ports フィールド以下を省略して、プロトコルやポート番号を問わない設定とすれば、http でも対象のドメインにアクセスできるようになります。
$ kubectl explain FQDNNetworkPolicy.spec.egress.ports.protocol GROUP: networking.gke.io KIND: FQDNNetworkPolicy VERSION: v1alpha1 FIELD: protocol <string> DESCRIPTION: Protocol is the L4 protocol. Valid options are "TCP", "UDP", or "ALL". If Protocol is missing or empty, it defaults to allowing all protocols. $ cat fqdnnetpol.yaml apiVersion: networking.gke.io/v1alpha1 kind: FQDNNetworkPolicy metadata: name: allow-out-fqdnnp spec: podSelector: matchLabels: app: curl-client egress: - matches: - pattern: "*.yourdomain.com" - name: "www.google.com" $ kubectl apply -f fqdnnetpol.yaml fqdnnetworkpolicy.networking.gke.io/allow-out-fqdnnp configured $ kubectl exec nginx -it -- bash root@nginx:/# curl -I -m 3 http://www.google.com HTTP/1.1 200 OK ...
利用時の注意点
公式ドキュメントでも言及されていますが、FQDN Network Policy と Kubernetes Network Policy には上下関係がありません。
そのため、同じワークロードに対して両方のポリシーが定義されている場合は、どちらかひとつでも一致する許可設定があればその通信は許可(クラスタ外に送信)されます。
どちらか一方だけのポリシーを見ても許可設定の全量が把握できないため、利用する際は注意が必要です。
まとめ
GKE で FQDN に基づく Network Policy が利用できるようになりました。
クラスタ外の通信先の IP アドレスが動的に変動するなどの場合に活躍してくれそうな機能ですね。
※ 2023/6/29 追記 記事公開の直前に Autopilot でも利用可能となったとアナウンスがあったため、GKE Standard のみ利用可能としていた記載を更新しました
- 厳密には、Calico と Dataplane V2 の他に GKE のネイティブ CNI という実装もありますが、こちらは Network Policy を利用できないためここでは割愛しています↩