はじめに
NetworkPolicyは、Kubernetesクラスター内のPod間の通信を制御するためのルールです。デフォルトではKubernetesのPodはすべてのPodに通信可能ですが、NetworkPolicyを使うことで特定の通信だけを許可できます。AWSのSecurity Group(SG)、AzureのNetwork Security Group(NSG)、Linuxのfirewalldやiptablesといった仕組みと同様に、KubernetesではNetworkPolicyを使って通信を制御できます。
AWSのSecurity Groupがインバウンドとアウトバウンドのルールを持つように、NetworkPolicyでも外からの通信を制御するIngressと、外への通信を制御するEgressを定義できます。
今回はあるPodに対してNetworkPolicyのEgressルールを定義し、外部に対してFQDN(ホスト名やドメイン名)でアクセスする際の注意点について、実際にリソースを作りながら説明します。
先に要約(TL;DR)
NetworkPolicyのEgressルールを定義して、外部に対してFQDN(ホスト名やドメイン名)でアクセス可能にするには、DNSにアクセスするためのルールを自分で追加 する必要があります。
環境情報
Kubernetesのバージョン
Kubernetesクラスターはv1.32.2で構築しています。
$ kubectl version | grep -i server
Server Version: v1.32.2
CNI
Ciliumのv1.16.5を使っています。
$ kubectl get ds -n kube-system -l k8s-app=cilium -owide | awk '{print $1 " " $10}' | column -t
NAME CONTAINERS
cilium quay.io/cilium/cilium:v1.16.5@sha256:758ca0793f5995bb938a2fa219dcce63dc0b3fa7fc4ce5cc851125281fb7361d
検証用リソースの作成
以下のリソースを作成します。
curl
外部にHTTPリクエストを送信するためのPodです。
kubectl run curl -n default --image=alpine/curl --command -- tail -f /dev/null
後ほど、NetworkPolicyのEgressルールをこのPodに適用します。
nginx
HTTPのサービスを提供するPodです。
kubectl run nginx -n default --image=nginx
後ほどcurlのPodからここにアクセスして、NetworkPolicyの設定がどのように効いてくるのかを確認します。
nginxは以下のコマンドでServiceリソースも作っておきましょう。
kubectl expose pod nginx -n default --port=80 --target-port=80 --name=nginx
作成されたリソースの確認
defaultのnamespaceに次のようなリソースが作られました。
$ kubectl get po -n default
NAME READY STATUS RESTARTS AGE
curl 1/1 Running 0 25s
nginx 1/1 Running 0 20s
$ kubectl get svc -n default | grep -v ^kubernetes | column -t
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.98.61.53 <none> 80/TCP 24s
curl から nginx につながることの確認
NetworkPolicyを作る前に、現在の状態でcurlのPodからnginxのPodにアクセスできることを確認します。
nginxのPodのIPアドレスで接続可能かの確認
nginxのPodのIPアドレスを事前に確認します。
$ kubectl get pod -n default -owide | grep ^nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 13m 192.168.1.88 ip-10-0-0-4 <none> <none>
192.168.1.88
ということがわかったので、curlのPodからアクセスしてみましょう。
$ kubectl exec -it curl -- curl -I 192.168.1.88 2>/dev/null | head -n 1
HTTP/1.1 200 OK
問題なくアクセスできました。
nginxのService経由で接続可能かの確認
続いて、nginxのServiceリソースを経由してアクセスできるかも確認しておきます。
kubectl exec -it curl -- sh
コンテナ内でcurlを実行すると、ServiceのIPアドレスだけではなくて、FQDNでもアクセス可能なことがわかります。
/ # curl -I 10.98.61.53 2>/dev/null | head -n 1
HTTP/1.1 200 OK
/ # curl -I nginx 2>/dev/null | head -n 1
HTTP/1.1 200 OK
/ # curl -I nginx.default.svc.cluster.local 2>/dev/null | head -n 1
HTTP/1.1 200 OK
※ 10.98.61.53
は nginx
のServiceリソースのクラスターIPで、先ほど kubectl get svc
を実行した際に出力されていたのを使いました。
EgressのNetworkPolicyを作成
続いて、「curlのPodから外への通信は、ポート80のTCPのみを許可する」NetworkPolicyのマニフェストを作成します。
「適用対象がcurlのPodである」ことをNetworkPolicyのマニフェストで表現するために、curlのPodに付与されているラベルを確認します。
$ kubectl get pod curl --show-labels
NAME READY STATUS RESTARTS AGE LABELS
curl 1/1 Running 0 37m run=curl
run=curl
というラベルが付いていることがわかりました。この情報を元に作成したNetworkPolicyのマニフェストが以下です。
apiVersion: "networking.k8s.io/v1"
kind: NetworkPolicy
metadata:
name: curl-allow-external-80
namespace: default
spec:
podSelector: # ルールの適用対象がcurlのPodであることを表す
matchLabels:
run: curl
policyTypes: # Egressのルールのみを適用(Ingressは対象外)
- Egress
egress: # 80番ポートでTCPのみを許可
- ports:
- port: 80
protocol: TCP
上記のマニフェストを元に、NetworkPolicyを作成します。
kubectl apply -f curl-allow-external-80.netpol.yaml
どのようなルールが作成されたかを確認する場合は、以下のようにkubectl describe netpol
を実行するとわかりやすく表示されます。
$ kubectl describe netpol -n default curl-allow-external-80
Name: curl-allow-external-80
Namespace: default
Created on: 2025-03-16 17:14:01 +0900 JST
Labels: <none>
Annotations: <none>
Spec:
PodSelector: run=curl
Not affecting ingress traffic
Allowing egress traffic:
To Port: 80/TCP
To: <any> (traffic not restricted by destination)
Policy Types: Egress
curl から nginx につながることの確認(NetworkPolicy作成後)
NetworkPolicyを作成した後に、curlのPodからnginxのサービスにアクセスできるかを確認します。「Egressでアクセス制限はしているけど、80番のポートは空けているからnginxのサービスにはアクセスできるはずだ」という前提です。
まずは、curlのPodのコンテナに入ります。
kubectl exec -it curl -- sh
curlのPod、およびServiceのIPアドレスにアクセスすると、想定通りつながります
/ # curl -I 192.168.1.88 2>/dev/null | head -n 1
HTTP/1.1 200 OK
/ # curl -I 10.98.61.53 2>/dev/null | head -n 1
HTTP/1.1 200 OK
ところが、curlのServiceに対応するFQDNを使うと、アクセスできません...
/ # curl -I nginx
curl: (6) Could not resolve host: nginx
/ # curl -I nginx.default.svc.cluster.local
curl: (6) Could not resolve host: nginx.default.svc.cluster.local
出力からわかるように、「curlの引数で指定したFQDNを名前解決できない」のが原因です。
AWS、Azureなどのクラウドプロバイダーで今回と同様にアウトバウンドルールを作ると、各クラウドプロバイダーが提供するデフォルトのDNSで名前解決してくれますが、KubernetesのNetworkPolicyはそうではないようです。そのため、「DNSの名前解決を許可するためのEgressルール」を自前で追加する必要があります。
kube-dnsにアクセスするためのEgressルールを追加
KubernetesクラスターのDNSは、kube-dns
というServiceリソースで提供されます。このリソースは、以下のコマンドで kube-system
というnamespaceでデプロイされていることがわかります。
$ kubectl get svc -n kube-system -l k8s-app=kube-dns
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 20d
ちなみにkube-dns
のServiceのバックエンドにあるDNSのPodは、coredns
と呼ばれるサービスです。
$ kubectl get pod -n kube-system -l k8s-app=kube-dns
NAME READY STATUS RESTARTS AGE
coredns-668d6bf9bc-2mjdf 1/1 Running 0 20d
coredns-668d6bf9bc-krz5b 1/1 Running 0 20d
上記コマンドの引数で指定していますが、DNS関連のリソースにはk8s-app=kube-dns
というラベルがついています。
そこで、curlのPodが、この kube-dns
のService(= coredns
のPod)にアクセスするためのルールを以下のように作ります。
apiVersion: "networking.k8s.io/v1"
kind: NetworkPolicy
metadata:
name: "curl-allow-dns"
namespace: default
spec:
podSelector: # ルールの適用対象がcurlのPodであることを表す
matchLabels:
run: curl
policyTypes: # Egressのルールのみを適用(Ingressは対象外)
- Egress
egress:
- to:
- namespaceSelector: # kube-systemのnamespaceが対象
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector: # kube-dnsのService(=corednsのPod)が対象
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
to:
の部分がやや複雑になっていますが、「『namespaceがkube-system
』かつ『k8s-app=kube-dns
のラベルがついている』リソースの53ポートに、UDPまたはTCPでアクセス可能」というルールを記述しています。
上記のマニフェストを元に、新たなNetworkPolicyを作成します。
kubectl apply -f curl-allow-dns.netpol.yaml
以下のようなルールが追加されました。
$ kubectl describe netpol -n default curl-allow-dns
Name: curl-allow-dns
Namespace: default
Created on: 2025-03-16 18:09:56 +0900 JST
Labels: <none>
Annotations: <none>
Spec:
PodSelector: run=curl
Not affecting ingress traffic
Allowing egress traffic:
To Port: 53/UDP
To Port: 53/TCP
To:
NamespaceSelector: kubernetes.io/metadata.name=kube-system
PodSelector: k8s-app=kube-dns
Policy Types: Egress
curl から nginx にFQDNでもつながることの確認(NetworkPolicy修正後)
再度、curlのPodからFQDNでつながるかを確認します。
kubectl exec -n default -it curl -- sh
/ # curl -I nginx 2>/dev/null | head -n 1
HTTP/1.1 200 OK
/ # curl -I nginx.default.svc.cluster.local 2>/dev/null | head -n 1
HTTP/1.1 200 OK
問題なく繋がりました!
Kubernetesクラスター内部だけでなく、外部の80ポートのサーバにもFQDNでつながることも確認できます。
/ # curl -I google.com 2>/dev/null | head -n 1
HTTP/1.1 301 Moved Permanently
/ # curl -I example.com 2>/dev/null | head -n 1
HTTP/1.1 200 OK
おわりに
Kubernetesの標準リソースの中で、NetworkPolicyはやや取っ付きにくい印象を持っていましたが、今回の検証で理解を深めることができました。本記事が皆さまのお役に立てれば幸いです。