1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NetworkPolicyのEgressルールで名前解決を可能にする

Posted at

はじめに

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.53nginx の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のマニフェストが以下です。

curl-allow-external-80.netpol.yaml
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)にアクセスするためのルールを以下のように作ります。

curl-allow-dns.netpol.yaml
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はやや取っ付きにくい印象を持っていましたが、今回の検証で理解を深めることができました。本記事が皆さまのお役に立てれば幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?