#はじめに
クラスタ内の全てのPodは、Pod同士で通信可能です。しかし、別サービスのシステムで共有する場合など通信させたくない場合もあると思います。
NetworkPolicyを使用するとPod同士の通信を制御することができます。NetworkPolicyを使用することで、特定のPodやプロトコルの通信を拒否/許可することができます。
#NetworkPolicyの設定概要
##前提条件
NetworkPolicyを使用するには、NetworkPolicyをサポートするネットワークプロバイダーでクラスタを構築している必要があります。サポートするネットワークプロバイダーは以下になります。
- Calico
- Cilium
- Kube-router
- Romana
- Weave Net
今回はクラスタにCalicoを使用しています。
##policyType
NetworkPolicyを設定するには、向きを意識する必要があります。通信の向きをpolicyTypeで指定します。指定するパラメータは以下の2つです。
- Ingress:あるPodからの通信(インバウンド)
- Egress:あるPodへの通信(アウトバウンド)
##設定Policy
NetworkPolicyで設定できるPolicyは以下の3つがあります。以下の3つをNamespaceごとに指定します。また、IngressとEgressもそれぞれ指定しますが、向きが異なるだけで設定できるPolicyは同じです。
Policy | 概要 |
---|---|
podSelector | あるPodからの/への通信を許可 |
namespaceSelector | あるNamespace内のPodからの/への通信を許可 |
ipBlock | あるIPアドレスからの/への通信を許可 |
PodのIPアドレスはデプロイのたびに自動で割り振られますので、クラスタ内での制御はpodSelectorとnamespaceSelectorを主に使用します。今回はこの2つの動作を確認したいと思います。
ipBlockはクラスタ外との通信を制御する場合に利用することをお勧めします。
#podSelector
今回は以下のように3つのPod間での通信制御を確認します。
nginx-app1からnginx-app3への通信のみ許可して、そのほかは拒否するように設定します。
##Podのデプロイ
まずは以下3つのPodをデプロイします。
apiVersion: v1
kind: Pod
metadata:
name: nginx-app1
labels:
app: app1
spec:
containers:
- name: nginx-app1
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-app2
labels:
app: app2
spec:
containers:
- name: nginx-app2
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-app3
labels:
app: app3
spec:
containers:
- name: nginx-app3
image: nginx:latest
ports:
- containerPort: 80
---
$ kubectl apply -f nginx-pod.yaml
pod/nginx-app1 created
pod/nginx-app2 created
pod/nginx-app3 created
$ kubectl get pod -o wide -L app
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP
nginx-app1 1/1 Running 0 20s 192.168.69.195 k8s-worker02 <none> <none> app1
nginx-app2 1/1 Running 0 19s 192.168.69.212 k8s-worker02 <none> <none> app2
nginx-app3 1/1 Running 0 19s 192.168.69.202 k8s-worker02 <none> <none> app3
コンテナにログインして、コンテナ間で通信できることを確認します。
$ kubectl exec -it nginx-app1 /bin/bash
root@nginx-app1:/# curl 192.168.69.212
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
・・・
root@nginx-app1:/# curl 192.168.69.202
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
・・・
nginx-app1からnginx-app2およびnginx-app3へ通信できていることが確認できます。
また、ログは省略しますが、nginx-app2/nginx-app3からも同様に各Podへ通信できることを確認しています。
##デフォルトポリシーの設定
NetworkPolicyは特定のPodやNamespaceに対して通信を許可する設定になりますので、まずはデフォルトで全ての通信を拒否しておいて、個別にPodやNamespaceに対して許可していきます。
デフォルトポリシーは「全てのIngress(インバウンド)は拒否して、全てのEgress(アウトバウンド)は許可する」など、環境やシステムごとに異なると思います。今回は「全てのIngress/Egressを拒否する」とします。
以下のマニフェストをapplyします。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
$ kubectl apply -f deny-all.yaml
networkpolicy.networking.k8s.io/default-deny-all created
$ kubectl get networkpolicies
NAME POD-SELECTOR AGE
default-deny-all <none> 20s
$ kubectl describe networkpolicies default-deny-all
Name: default-deny-all
Namespace: default
Created on: 2020-07-01 21:41:26 +0900 JST
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"networking.k8s.io/v1","kind":"NetworkPolicy","metadata":{"annotations":{},"name":"default-deny-all","namespace":"default"},...
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:
<none> (Selected pods are isolated for egress connectivity)
Policy Types: Ingress, Egress
念のためPodにログインして拒否されることを確認します。
$ kubectl exec -it nginx-app1 /bin/bash
root@nginx-app1:/# curl -m 30 192.168.69.212
curl: (28) Connection timed out after 30002 milliseconds
root@nginx-app1:/# curl -m 30 192.168.69.202
curl: (28) Connection timed out after 30002 milliseconds
nginx-app2/nginx-app3からも同様に各Podへの通信が拒否されています。
##PodSelectorの設定
現時点では全てのPod間での通信が拒否されていますので、nginx-app1からnginx-app3への通信を許可するようにNetworkPolicyを設定します。
この場合は、nginx-app3のIngressと、nginx-app1のEgressを許可します。ラベル単位で設定しますので、2つのNetworkPolicyを設定します。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: pod-selector1
spec:
podSelector:
matchLabels:
app: app3 #設定対象のPodのラベル
policyTypes:
- Ingress #Ingressを許可する
ingress:
- from:
- podSelector:
matchLabels:
app: app1 #どのラベルのPodからの通信を許可するか
ports:
- protocol: TCP #許可するプロトコル
port: 80 #許可するポート
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: pod-selector2
spec:
podSelector:
matchLabels:
app: app1 #設定対象のPodのラベル
policyTypes:
- Egress #Egressを許可する
egress:
- to:
- podSelector:
matchLabels:
app: app3 #どのラベルのPodへの通信を許可するか
ports:
- protocol: TCP #許可するプロトコル
port: 80 #許可するポート
$ kubectl apply -f podSelector.yaml
networkpolicy.networking.k8s.io/pod-selector1 created
networkpolicy.networking.k8s.io/pod-selector2 created
$ kubectl get networkpolicies
NAME POD-SELECTOR AGE
default-deny-all <none> 36m
pod-selector1 app=app3 9s
pod-selector2 app=app1 9s
###動作確認
nginx-app1にログインして動作を確認します。
$ kubectl exec -it nginx-app1 /bin/bash
root@nginx-app1:/# curl -m 10 192.168.69.202
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
root@nginx-app1:/# curl -m 10 192.168.69.212
curl: (28) Connection timed out after 10001 milliseconds
nginx-app3(192.168.69.202)へは疎通できていますが、nginx-app2(192.168.69.212)へは拒否されていますね。
反対にnginx-app3からnginx-app1へ疎通できないことも確認します。
$ kubectl exec -it nginx-app3 /bin/bash
root@nginx-app3:/# curl -m 10 192.168.69.195
curl: (28) Connection timed out after 10003 milliseconds
タイムアウトしていますね。
#namespaceSelector
次はnamespaceSelectorを使用して、Namespace単位で通信を制御したいと思います。
以下の図のような設定にしたいと思います。
- 同一クラスタ内に二つのNamespace(production/staging)がある。
- productionからstagingへの通信は許可するが、stagingからproductionへの通信は拒否する。
- 同一Namespace内の通信は許可する。
##事前準備
###Namespaceの作成
productionとstagingのNamespaceを作成します。NetworkPolicyはラベルで制御しますので、各Namespaceにもラベルを付与します。
以下のマニフェストをapplyしてラベルを作成します。
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
env: prd
---
apiVersion: v1
kind: Namespace
metadata:
name: staging
labels:
env: stg
###Podの作成
以下のマニフェストの4つのPodを作成します。NamespaceとLabelは上図のように設定します。
apiVersion: v1
kind: Pod
metadata:
name: nginx-prd-app1
namespace: production
labels:
app: app1
spec:
containers:
- name: nginx-prd-app1
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-prd-app2
namespace: production
labels:
app: app2
spec:
containers:
- name: nginx-prd-app2
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-stg-app1
namespace: staging
labels:
app: app1
spec:
containers:
- name: nginx-stg-app1
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-stg-app2
namespace: staging
labels:
app: app2
spec:
containers:
- name: nginx-stg-app2
image: nginx:latest
ports:
- containerPort: 80
$ kubectl apply -f nginx-pod-ns.yaml
pod/nginx-prd-app1 created
pod/nginx-prd-app2 created
pod/nginx-stg-app1 created
pod/nginx-stg-app2 created
$ kubectl get pod -n production -o wide -L app
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP
nginx-prd-app1 1/1 Running 1 7h18m 192.168.69.194 k8s-worker02 <none> <none> app1
nginx-prd-app2 1/1 Running 1 7h18m 192.168.69.227 k8s-worker02 <none> <none> app2
$ kubectl get pod -n staging -o wide -L app
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP
nginx-stg-app1 1/1 Running 1 7h18m 192.168.69.238 k8s-worker02 <none> <none> app1
nginx-stg-app2 1/1 Running 1 7h18m 192.168.69.224 k8s-worker02 <none> <none> app2
##NetworkPolicyの設定
###デフォルトポリシーの設定
productionのIngress/Egressを全て拒否します。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all-prd
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
$ kubectl apply -f deny-all-prd.yaml
networkpolicy.networking.k8s.io/default-deny-all-prd created
$ kubectl get networkpolicies -n production
NAME POD-SELECTOR AGE
default-deny-all-prd <none> 18s
現時点では以下の図のように制御されています。
###Namespace間通信の設定
以下のNetworkPolicyでproductionからstagingへの通信を許可します。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: namespace-selector
namespace: production
spec:
podSelector: {} #Namespace内の全てのPodが対象
policyTypes:
- Egress #Egressを設定
egress:
- to:
- namespaceSelector: #Namespace単位で設定
matchLabels:
env: stg #「env: stg」ラベルを持つNamespaceへの通信を許可
ports:
- protocol: TCP #許可するプロトコル
port: 80 #許可するポート
$ kubectl apply -f namespaceSelector.yaml
networkpolicy.networking.k8s.io/namespace-selector created
この時点では以下のように制御されています。
###Pod間通信の設定
最後にproduction内のPod間通信を許可します。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: pod-selector-prd-app1
namespace: production
spec:
podSelector:
matchLabels:
app: app1
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: app2
ports:
- protocol: TCP
port: 80
egress:
- to:
- podSelector:
matchLabels:
app: app2
ports:
- protocol: TCP
port: 80
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: pod-selector-prd-app2
namespace: production
spec:
podSelector:
matchLabels:
app: app2
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: app1
ports:
- protocol: TCP
port: 80
egress:
- to:
- podSelector:
matchLabels:
app: app1
ports:
- protocol: TCP
port: 80
$ kubectl apply -f podSelector-prd.yaml
networkpolicy.networking.k8s.io/pod-selector-prd-app1 created
networkpolicy.networking.k8s.io/pod-selector-prd-app2 created
これで目標としていた以下のように制御されています。
##動作確認
コンテナにログインして疎通を確認します。まずはproductionからです。
$ kubectl exec -it -n production nginx-prd-app1 /bin/bash
root@nginx-prd-app1:/# curl -m 10 192.168.69.227
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
・・・
root@nginx-prd-app1:/# curl -m 10 192.168.69.238
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
・・・
root@nginx-prd-app1:/# curl -m 10 192.168.69.224
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
・・・
productionからは全てのPodに疎通できていますね。
次にstagingです。
$ kubectl exec -it -n staging nginx-stg-app1 /bin/bash
root@nginx-stg-app1:/# curl -m 10 192.168.69.194
curl: (28) Connection timed out after 10000 milliseconds
root@nginx-stg-app1:/# curl -m 10 192.168.69.227
curl: (28) Connection timed out after 10005 milliseconds
root@nginx-stg-app1:/# curl -m 10 192.168.69.224
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
・・・
productionへの通信は全て拒否されています。staging内でのみ通信可能ですね。
#まとめ
NetworkPolicyを使用することで細かく通信を制御できますね。
今回はいくつかのポリシーを組み合わせて想定した制御に設定しました。そのため、パズルみたいな感じで少し考えてしまいました。もしかしたら、もっとスマートに設定できるのかも知れません。