Opensourcetechブログ

OpensourcetechによるNGINX/Kubernetes/Zabbix/Neo4j/Linuxなどオープンソース技術に関するブログです。

Network Policyによるトラフィック制御(kubernetes)


LinuCエヴァンジェリストの鯨井貴博@opensourcetechです。


はじめに
今回は、Network Policyを使ってPod間の通信制御をしてみます。
なお、kubernetesは以下の記事で作成したクラスターを使っています。
DualStack(IPv4 & IPv6)のkubernetesクラスター構築(v1.26.00・ubuntu22.04)
https://www.opensourcetech.tokyo/entry/20230314/1678782139


Network Policyとは
Network Policyとは、kubernetesクラスター内のPod(コンテナ)間の通信制御の為に使われるリソースです。
https://kubernetes.io/ja/docs/concepts/services-networking/network-policies/

Network Policyでは、Layer3(IPアドレス)とLayer4(ポート番号)を使ってトラフィックの制御を行います。
なお、Network Policyの機能を使うにはkubernetesのCNIプラグインとしてNetwork PolicyをサポートしているCalicoやWeaveなどを使う必要があります。


今回使う構成
以下のように2つのNamespace(名前空間)に4つのPodを用意します。


Network Policyを設定する際に必要になるPodのlabelをweb: blue1のように定義しておきます。

kubeuser@master01:~/networkpolicy$ kubectl create ns blue --dry-run=client -o yaml > ns.yaml

kubeuser@master01:~/networkpolicy$ vi ns.yaml 

kubeuser@master01:~/networkpolicy$ cat ns.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: blue
---
apiVersion: v1
kind: Namespace
metadata:
  name: orange

kubeuser@master01:~/networkpolicy$ kubectl apply -f ns.yaml 
namespace/blue created
namespace/orange created

kubeuser@master01:~/networkpolicy$ kubectl get ns
NAME                   STATUS   AGE
blue                   Active   4s
default                Active   15d
ingress-nginx          Active   13d
kube-node-lease        Active   15d
kube-public            Active   15d
kube-system            Active   15d
kubernetes-dashboard   Active   2d9h
metallb-system         Active   15d
orange                 Active   4s

kubeuser@master01:~/networkpolicy$ kubectl run blue1 -n blue --image=nginx:latest --dry-run=client -o yaml > pods.yaml

kubeuser@master01:~/networkpolicy$ vi pods.yaml 

kubeuser@master01:~/networkpolicy$ cat pods.yaml 
apiVersion: v1
kind: Pod
metadata:
  labels:
    web: blue1
  name: blue1
  namespace: blue
spec:
  containers:
  - image: nginx:latest
    name: blue1
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    web: blue2
  name: blue2
  namespace: blue
spec:
  containers:
  - image: nginx:latest
    name: blue2
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    web: orange1
  name: orange1
  namespace: orange
spec:
  containers:
  - image: nginx:latest
    name: orange1
---
apiVersion: v1
kind: Pod
metadata:
  labels:
    web: orange2
  name: orange2
  namespace: orange
spec:
  containers:
  - image: nginx:latest
    name: orange2

kubeuser@master01:~/networkpolicy$ kubectl get pods -o wide -n blue --show-labels
NAME    READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES   LABELS
blue1   1/1     Running   0          70s   10.0.30.116   worker02   <none>           <none>            web=blue1
blue2   1/1     Running   0          70s   10.0.5.38     worker01   <none>           <none>            web=blue2

kubeuser@master01:~/networkpolicy$ kubectl get pods -o wide -n orange --show-labels
NAME      READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES   LABELS
orange1   1/1     Running   0          77s   10.0.5.37     worker01   <none>           <none>            web=orange1
orange2   1/1     Running   0          76s   10.0.30.117   worker02   <none>           <none>            web=orange2



デフォルト状態のPod間通信
Kubernetesクラスターを構築したデフォルト状態では、
Pod間の通信は自由に行えるようになっているかと思います。

kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl http://10.0.5.38
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   615  100   615    0     0   300k      0 --:--:-- --:--:-- --:--:--  300k


kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl http://10.0.5.37
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   615  100   615    0     0  68333      0 --:--:-- --:--:-- --:--:-- 68333
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl http://10.0.30.117
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   615  100   615    0     0   600k      0 --:--:-- --:--:-- --:--:--  600k
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>



All Denyの Network Policyの適用
まず、PodへのIngress(入ってくる通信)をブロックするNetwork Policyを作成します。

kubeuser@master01:~/networkpolicy$ vi np-all-deny.yaml

kubeuser@master01:~/networkpolicy$ cat np-all-deny.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: all-deny
  namespace: blue
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Ingress
  - Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: all-deny
  namespace: orange
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Ingress
  - Egress

kubeuser@master01:~/networkpolicy$ kubectl apply -f np-all-deny.yaml 
networkpolicy.networking.k8s.io/all-deny created
networkpolicy.networking.k8s.io/all-deny created


各名前空間(blue/orange)、それぞれにNetwork Policyをしました。
Podへ入ってくるトラフィック(Ingress)を全拒否(deny)、Podから送信されるトラフィック(Egress)は全許可(allow)としました。

kubeuser@master01:~/networkpolicy$ kubectl get netpol -n blue
NAME       POD-SELECTOR   AGE
all-deny   <none>         56s

kubeuser@master01:~/networkpolicy$ kubectl describe netpol all-deny -n blue
Name:         all-deny
Namespace:    blue
Created on:   2023-03-30 12:50:54 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     <none> (Allowing the specific traffic to all pods in this namespace)
  Allowing ingress traffic:
    <none> (Selected pods are isolated for ingress connectivity)
  Allowing egress traffic:
    To Port: <any> (traffic allowed to all ports)
    To: <any> (traffic not restricted by destination)
  Policy Types: Ingress, Egress

kubeuser@master01:~/networkpolicy$ kubectl get netpol -n orange
NAME       POD-SELECTOR   AGE
all-deny   <none>         74s

kubeuser@master01:~/networkpolicy$ kubectl describe netpol all-deny -n orange
Name:         all-deny
Namespace:    orange
Created on:   2023-03-30 12:50:54 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     <none> (Allowing the specific traffic to all pods in this namespace)
  Allowing ingress traffic:
    <none> (Selected pods are isolated for ingress connectivity)
  Allowing egress traffic:
    To Port: <any> (traffic allowed to all ports)
    To: <any> (traffic not restricted by destination)
  Policy Types: Ingress, Egress


左上のPodから見ると、他のコンテナとの通信がブロックされます。


実際に試してみても、NGとなります。

kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl --connect-timeout 5 http://10.0.5.38
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
curl: (28) Connection timed out after 5001 milliseconds
command terminated with exit code 28

kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl --connect-timeout 5 http://10.0.5.37
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
curl: (28) Connection timed out after 5000 milliseconds
command terminated with exit code 28

kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl --connect-timeout 5 http://10.0.30.117
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
curl: (28) Connection timed out after 5001 milliseconds
command terminated with exit code 28



一部の通信を許可するNetwork Policyの適用
以下のようにblue1からorange2への通信を許可します。

まず、yamlを作成します。
具体的には、以下のいずれか(or条件)にマッチした場合に通信が許可されます。

  • 送信元IPアドレスが10.0.30.116/32
  • 送信元名前空間のラベルに"name: blue"が含まれている
  • 送信元Podのラベルに"web: blue1"が含まれている
kubeuser@master01:~/networkpolicy$ vi np-allow.yaml 

kubeuser@master01:~/networkpolicy$ cat np-allow.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: orange2-from-blue1
  namespace: orange ・・・Network Policyを配置する名前空間
spec:
  podSelector:
    matchLabels:
      web: orange2 ・・・Network Policyの対象となるPodのラベル
  policyTypes:
  - Ingress ・・・制御するトラフィックの方向
  ingress:
  - from: ・・・トラフィックの送信元情報
    - ipBlock: ・・・・IPアドレス+プレフィックスを使った送信元の制御
        cidr: 10.0.30.116/32
    - namespaceSelector: ・・・名前空間を使った送信元の制御
        matchLabels:
          name: blue
    - podSelector:
        matchLabels:
          web: blue1 ・・・Podのラベルを使った送信元の制御
    ports: ・・・Network Policyの対象となるPodの受信ポート & プロトコル
    - protocol: TCP
      port: 80


そして、yamlを適用します。

kubeuser@master01:~/networkpolicy$ kubectl apply -f np-allow.yaml
networkpolicy.networking.k8s.io/orange2-from-blue1 created

kubeuser@master01:~/networkpolicy$ kubectl get netpol -n orange
NAME                 POD-SELECTOR   AGE
all-deny             <none>         14m
orange2-from-blue1   web=orange2    9s
kubeuser@master01:~/networkpolicy$ kubectl describe netpol orange2-from-blue1 -n orange
Name:         orange2-from-blue1
Namespace:    orange
Created on:   2023-03-30 13:05:17 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     web=orange2
  Allowing ingress traffic:
    To Port: 80/TCP
    From:
      IPBlock:
        CIDR: 10.0.30.116/32
        Except: 
    From:
      NamespaceSelector: name=blue
    From:
      PodSelector: web=blue1
  Not affecting egress traffic
  Policy Types: Ingress



Network Policyの制御を確認
では、確認してみましょう。
想定通り、blue1からorange2の通信のみ許可されました。

kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl --connect-timeout 5 http://10.0.5.38
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
curl: (28) Connection timed out after 5001 milliseconds
command terminated with exit code 28

kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl --connect-timeout 5 http://10.0.5.37
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
curl: (28) Connection timed out after 5000 milliseconds
command terminated with exit code 28

kubeuser@master01:~/networkpolicy$ kubectl exec blue1 -n blue -- curl --connect-timeout 5 http://10.0.30.117
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
100   615  100   615    0     0   600k      0 --:--:-- --:--:-- --:--:--  600k



おわりに
Network Policyですが、ネットワーク機器で使われるACL(Access List)やFWの機能と同じような位置づけと捉えられます。

今回のNetwork Policyでは名前空間・IPアドレス・Podのラベルの3つをor条件で使いましたが、どれか一つだけでもOKです。

なお、名前空間のラベルとPodのラベルをAnd条件で使う場合は、以下のように記載します。
※先頭のハイフン(-)とつけない


また、IPアドレスを使った制御は以下のように指定レンジの中から除外するIPアドレスを設定できます。


同様に複数のポート向けの通信(例:TCP/UDP 53)の場合は、 以下のようにします。
portsの前に-(ハイフン)を付ける or 付けないで挙動が変わるので注意が必要。

egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      - ports:
        - protocol: TCP
          port: 53
        - protocol: UDP
          port: 53

Opensourcetech by Takahiro Kujirai