NetworkPolicy
Объект в кластере k8s, позволяющий ограничивать доступ к ресурсам по сети внутри кластера.
- Зависит от используемого CNI. Если плагин не поддерживает netpol, то они не будут работать. Хотя и можно их создать. Flannel, например, не поддерживает их;
- Используется на уровне namespace;
- С его помощью можно заблокировать трафик к какому-либо поду;
- Можно заблокировать доступ к ресурсам для сторонних ip адресов;
- Когда открываем входящий трафик (ingress), то исходящий трафик (egress) открывается автоматически;
- Если политики не определены, Kubernetes по умолчанию разрешает весь трафик. Все pod'ы свободно могут обмениваться информацией между собой;
- Порядок политик в ветках ingress/egress не играет значения;
- Каждый ресурс, затронутый хотя бы одной из политик, становится изолированным в соответствии с дизъюнкцией (логическим ИЛИ) всех политик, которые его выбрали;
- Применяется к ресурсу, используя labels/selector;
- Может работать с протоколами TCP, UDP, SCTP.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: internal-policy namespace: default spec: podSelector: matchLabels: name: internal policyTypes: - Egress - Ingress ingress: - from: - podSelector: matchLabels: name: payroll ports: - port: 8080 protocol: TCP egress: - to: - podSelector: matchLabels: name: mysql ports: - protocol: TCP port: 3306 - to: - podSelector: matchLabels: name: payroll ports: - protocol: TCP port: 8080 - ports: - port: 53 protocol: UDP - port: 53 protocol: TCP EOF
Открываем трафик к поду с PostgreSQL.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.postgres namespace: default spec: podSelector: matchLabels: app: postgres ingress: - from: - podSelector: matchLabels: app: balance policyTypes: - Ingress EOF
Открываем входящий трафик для определённых подов или определённого namespace или определённых адресов.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: app: db policyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: matchLabels: ns: prod - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978 EOF
Если опустить policyTypes
, политика будет интерпретироваться следующим образом:
- По умолчанию предполагается, что она определяет ingress-сторону. Если явных указаний на этот счет в политике не содержится, система будет считать, что весь трафик запрещен.
- Поведение на egress-стороне будет определяться наличием или отсутствием соответствующего egress-параметра.
В соответствии с приведенной выше логикой в случае, если параметры ingress
и/или egress
опущены, политика будет запрещать весь трафик.
Блок namespaceSelector говорит о том, на какой namespace влияют правила NetworkPloicy.
- namespaceSelector: matchLabels: project: default
Namespace выбирается с помощью label.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all namespace: default spec: podSelector: {} ingress: - {} policyTypes: - Ingress EOF
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: default spec: podSelector: {} policyTypes: - Ingress EOF
Закрыть весь исходящий трафик.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-egress namespace: default spec: podSelector: {} policyTypes: - Egress EOF
Включить входящий трафик к определённому набору подов в конкретном namespace.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all-to-balance namespace: default spec: podSelector: matchLabels: app: balance ingress: - {} policyTypes: - Ingress EOF
Действительно для подов с лейблами app=balance и из namespace default.
Политика разрешает весь входящий (ingress) и исходящий (egress) трафик, включая доступ к любому IP за пределами кластера
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-all namespace: default spec: podSelector: {} ingress: - {} egress: - {} policyTypes: - Ingress - Egress EOF
Входящий трафик разрешается от подов с разными лейблами.
cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default.postgres namespace: default spec: ingress: - from: - podSelector: matchLabels: app: indexer - podSelector: matchLabels: app: admin podSelector: matchLabels: app: postgres policyTypes: - Ingress EOF
Для подов с лейблами app=postgres разрешён входящий трафик со стороны подов с лейблами app=indexer и app=admin
Пример с созданием трёх подов и хождением трафика между ними с разными правилами NetworkPolicy
kubectl create ns test kubens test kubectl create deploy cam-nginx1 --image=nginx:latest --replicas=1 kubectl create deploy cam-nginx2 --image=nginx:latest --replicas=1 kubectl create deploy cam-nginx3 --image=nginx:latest --replicas=1
kubectl get po --show-labels -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS cam-nginx1-7d55b6dcc6-7qsck 1/1 Running 0 72m 10.95.129.26 cl1qncbjrtn09rafjhns-emol <none> <none> app=cam-nginx1,pod-template-hash=7d55b6dcc6 cam-nginx2-f57b74467-f7lmj 1/1 Running 0 73m 10.95.128.31 cl1qncbjrtn09rafjhns-esuj <none> <none> app=cam-nginx2,pod-template-hash=f57b74467 cam-nginx3-d4d99bbc8-8mbqr 1/1 Running 0 51s 10.95.129.27 cl1qncbjrtn09rafjhns-emol <none> <none> app=cam-nginx3,pod-template-hash=d4d99bbc8
# Пингую с первого пода второй kubectl exec --tty --stdin cam-nginx1-7d55b6dcc6-7qsck -- ping 10.95.128.31 PING 10.95.128.31 (10.95.128.31) 56(84) bytes of data. 64 bytes from 10.95.128.31: icmp_seq=1 ttl=61 time=1.16 ms
Без правил netpol, ping между подами проходит без помех. Т.е. по дефолту используются правила "Всем всё".
Создаём правило на входящий трафик для пода с лейблом cam-nginx2. Разрешается входящий трафик с пода cam-nginx1.
# Правило установлено для второго пода cam-nginx2-f57b74467-f7lmj. cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.nginx namespace: test spec: podSelector: matchLabels: app: cam-nginx2 ingress: - from: - podSelector: matchLabels: app: cam-nginx1 policyTypes: - Ingress EOF
kubectl get netpol NAME POD-SELECTOR AGE access.nginx app=cam-nginx2 31s kubectl describe netpol access.nginx Name: access.nginx Namespace: test Created on: 2023-05-06 01:12:20 +0300 MSK Labels: <none> Annotations: <none> Spec: PodSelector: app=cam-nginx2 Allowing ingress traffic: To Port: <any> (traffic allowed to all ports) From: PodSelector: app=cam-nginx1 Not affecting egress traffic Policy Types: Ingress
# Пингую с первого пода второй kubectl exec --tty --stdin cam-nginx1-7d55b6dcc6-7qsck -- ping 10.95.128.31 PING 10.95.128.31 (10.95.128.31) 56(84) bytes of data. 64 bytes from 10.95.128.31: icmp_seq=1 ttl=61 time=1.27 ms
# Пингую с третьего пода второй. Нет соединения. kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-8mbqr -- ping 10.95.128.31 PING 10.95.128.31 (10.95.128.31) 56(84) bytes of data.
# Пингую со второго пода первый kubectl exec --tty --stdin cam-nginx2-f57b74467-f7lmj -- ping 10.95.129.26 PING 10.95.129.26 (10.95.129.26) 56(84) bytes of data. 64 bytes from 10.95.129.26: icmp_seq=1 ttl=61 time=54.2 ms # Пингую со второго пода третий kubectl exec --tty --stdin cam-nginx2-f57b74467-f7lmj -- ping 10.95.129.27 PING 10.95.129.27 (10.95.129.27) 56(84) bytes of data. 64 bytes from 10.95.129.27: icmp_seq=1 ttl=61 time=0.955 ms
Если пингуем с пода с которого трафик разрешён, то пинг проходит. Если с того, с которого не разрешён входящий трафик, то пинг не проходит.
С пода, на который установлена политика можно пинговать оба пода.
Разрешаем исходящий трафик с пода cam-nginx2 на под cam-nginx3
# Правило установлено для второго пода. cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.nginx namespace: test spec: podSelector: matchLabels: app: cam-nginx2 ingress: - from: - podSelector: matchLabels: app: cam-nginx1 egress: - to: - podSelector: matchLabels: app: cam-nginx3 policyTypes: - Ingress - Egress EOF
kubectl describe netpol access.nginx Name: access.nginx Namespace: test Created on: 2023-05-06 01:12:20 +0300 MSK Labels: <none> Annotations: <none> Spec: PodSelector: app=cam-nginx2 Allowing ingress traffic: To Port: <any> (traffic allowed to all ports) From: PodSelector: app=cam-nginx1 Allowing egress traffic: To Port: <any> (traffic allowed to all ports) To: PodSelector: app=cam-nginx3 Policy Types: Ingress, Egress
# Пингую со второго пода первый kubectl exec --tty --stdin cam-nginx2-f57b74467-f7lmj -- ping 10.95.129.26 PING 10.95.129.26 (10.95.129.26) 56(84) bytes of data. ^C --- 10.95.129.26 ping statistics --- 17 packets transmitted, 0 received, 100% packet loss, time 16363ms # Пингую со второго пода третий kubectl exec --tty --stdin cam-nginx2-f57b74467-f7lmj -- ping 10.95.129.27 PING 10.95.129.27 (10.95.129.27) 56(84) bytes of data. 64 bytes from 10.95.129.27: icmp_seq=1 ttl=61 time=1.08 ms # Пингую с первого пода третий kubectl exec --tty --stdin cam-nginx1-7d55b6dcc6-7qsck -- ping 10.95.129.27 PING 10.95.129.27 (10.95.129.27) 56(84) bytes of data. 64 bytes from 10.95.129.27: icmp_seq=1 ttl=63 time=0.065 ms
После установки блока egress исходящий трафик может уходить только на те поды, которые прописаны в блоке egress.
kubectl get po -owide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS cam-nginx1-7d55b6dcc6-bhrgz 1/1 Running 0 12h 10.95.130.13 cl1qncbjrtn09rafjhns-ataw <none> <none> app=cam-nginx1,pod-template-hash=7d55b6dcc6 cam-nginx2-f57b74467-vf4j7 1/1 Running 0 12h 10.95.130.14 cl1qncbjrtn09rafjhns-ataw <none> <none> app=cam-nginx2,pod-template-hash=f57b74467 cam-nginx3-d4d99bbc8-gmkqz 1/1 Running 0 12h 10.95.130.15 cl1qncbjrtn09rafjhns-ataw <none> <none> app=cam-nginx3,pod-template-hash=d4d99bbc8 cam-nginx4-56695f4c8c-2pl78 1/1 Running 0 12h 10.95.130.3 cl1qncbjrtn09rafjhns-ataw <none> <none> app=cam-nginx4,pod-template-hash=56695f4c8c
Разрешаем входящий трафик на второй под с конкретного адреса или с подов определённым селектором.
# Правило установлено для второго пода cam-nginx2-f57b74467-vf4j7. # Блок ipBlock и podSelector в блоке ingress имеют логику ИЛИ. # Т.е доступ к поду будет, если сработает одно из правил. # Входящий трафик разрешён с адреса 10.95.130.3/32 или от пода с лейблом cam-nginx3 # Исходящий трафик разрешён на под с лейблом cam-nginx3 cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.nginx namespace: test spec: podSelector: matchLabels: app: cam-nginx2 ingress: - from: - ipBlock: cidr: 10.95.130.3/32 - podSelector: matchLabels: app: cam-nginx3 egress: - to: - podSelector: matchLabels: app: cam-nginx3 policyTypes: - Ingress - Egress EOF
kubectl exec --tty --stdin cam-nginx4-56695f4c8c-2pl78 -- ping 10.95.130.14 PING 10.95.130.14 (10.95.130.14) 56(84) bytes of data. 64 bytes from 10.95.130.14: icmp_seq=1 ttl=63 time=0.075 ms kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- ping 10.95.130.14 PING 10.95.130.14 (10.95.130.14) 56(84) bytes of data. 64 bytes from 10.95.130.14: icmp_seq=1 ttl=63 time=0.072 ms
Если в спецификации NetworkPolicy поменять адрес в блоке ipBlock на 10.95.130.3/32, то доступ с четвёртого пода ко второму пропадает.
В примере выше между блоками ipBlock и podSelector используется логика ИЛИ.
Ограничение доступа по портам.
# До ограничения доступа на порты kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- nc -vz 10.95.130.14 80 Connection to 10.95.130.14 80 port [tcp/*] succeeded!
# Правило установлено для второго пода cam-nginx2-f57b74467-vf4j7. # Ограничим доступ к поду по 80 порту. cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.nginx namespace: test spec: podSelector: matchLabels: app: cam-nginx2 ingress: - from: - ipBlock: cidr: 10.95.130.3/32 - podSelector: matchLabels: app: cam-nginx3 ports: - port: 443 protocol: TCP egress: - to: - podSelector: matchLabels: app: cam-nginx3 ports: - port: 443 protocol: TCP policyTypes: - Ingress - Egress EOF
# После ограничения доступа на порты kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- nc -vz 10.95.130.14 80 command terminated with exit code 130 # Поменял порт с 443->80 kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- nc -vz 10.95.130.14 80 Connection to 10.95.130.14 80 port [tcp/*] succeeded!
Ограничение доступа по namespace.
# Создаём ещё 1 namespace, под. И из этого пода попингуем тестовый под. kubectl create ns prod kubens prod kubectl create deploy cam-nginxx3 --image=nginx:latest --replicas=1 kubectl get po -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES cam-nginxx3-6b4df76bb-jb8f7 1/1 Running 0 16s 10.95.130.22 cl1qncbjrtn09rafjhns-ataw <none> <none> # До ограничения доступа по ns kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- ping 10.95.130.14 PING 10.95.130.14 (10.95.130.14) 56(84) bytes of data. 64 bytes from 10.95.130.14: icmp_seq=1 ttl=63 time=0.091 ms
# Правило установлено для второго пода cam-nginx2-f57b74467-vf4j7. # Входящий трафик разрешён только из ns с лейблом test по 80 порту. # Исходящий трафик разрешён только по 80 порту на поды из ns test. cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.nginx namespace: test spec: podSelector: matchLabels: app: cam-nginx2 ingress: - from: - namespaceSelector: matchLabels: ns: test ports: - port: 80 protocol: TCP egress: - to: - namespaceSelector: matchLabels: ns: test ports: - port: 80 protocol: TCP policyTypes: - Ingress - Egress EOF
# После применения политики ограничения по namspace kubectl exec --tty --stdin cam-nginxx3-6b4df76bb-jb8f7 — ping 10.95.130.14 PING 10.95.130.14 (10.95.130.14) 56(84) bytes of data. ^C -— 10.95.130.14 ping statistics -— 8 packets transmitted, 0 received, 100% packet loss, time 7176ms
Логика И. Ограничить поды по неймспейсам И лейблам.
# В данном примере доступ к поду 2 будет у пода из namespace с лейблом ns: test # И у этого пода должен быть лейбл cam-nginx3 # Исходящий трафик идёт только по 80 порту на под с лейблом cam-nginx3 cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.nginx namespace: test spec: podSelector: matchLabels: app: cam-nginx2 ingress: - from: - namespaceSelector: matchLabels: ns: test podSelector: matchLabels: app: cam-nginx3 egress: - to: - podSelector: matchLabels: app: cam-nginx3 ports: - port: 80 protocol: TCP policyTypes: - Ingress - Egress EOF
# Подключение из другого ns не удалось. kubectl exec --tty --stdin cam-nginxx3-6b4df76bb-jb8f7 -- ping 10.95.130.14 PING 10.95.130.14 (10.95.130.14) 56(84) bytes of data. ^C --- 10.95.130.14 ping statistics --- 5 packets transmitted, 0 received, 100% packet loss, time 4084ms # Подключение из того же ns. kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- ping 10.95.130.14 PING 10.95.130.14 (10.95.130.14) 56(84) bytes of data. 64 bytes from 10.95.130.14: icmp_seq=1 ttl=63 time=0.066 ms
Логика И работает не со всеми блоками в ingress/egress. Например, если использовать вместе ipBlock и podSelector, то правило не применится и будет выдана ошибка.
Разрешим входящий и исходящий трафик к поду, но только по 80 и 443 портам.
# Входящий трафик разрешён для всех по 80 и 443 портам. # Исходящий трафик разрешён для всех по 80 и 443 портам. cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access.nginx namespace: test spec: podSelector: matchLabels: app: cam-nginx2 ingress: - from: - ipBlock: cidr: 0.0.0.0/0 ports: - port: 80 protocol: TCP - port: 443 protocol: TCP - port: 53 protocol: TCP - port: 53 protocol: UDP egress: - to: - ipBlock: cidr: 0.0.0.0/0 ports: - port: 80 protocol: TCP - port: 443 protocol: TCP - port: 53 protocol: TCP - port: 53 protocol: UDP policyTypes: - Ingress - Egress EOF
kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- curl 10.95.130.14 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> kubectl exec --tty --stdin cam-nginx3-d4d99bbc8-gmkqz -- ping 10.95.130.14 PING 10.95.130.14 (10.95.130.14) 56(84) bytes of data. ^C --- 10.95.130.14 ping statistics --- 4 packets transmitted, 0 received, 100% packet loss, time 3056ms
Если создать сервис, то сайт под управлением Nginx свободно отображается.