Сетевые политики Kubernetes
Сетевые политики Kubernetes

Как можно изолировать поды и пространства имён? В этом нам помогут сетевые модели Kubernetes.

Сетевая модель Kubernetes определяет, что:

  • Каждый под получает собственный IP-адрес.

  • Поды могут взаимодействовать с любыми другими подами в кластере при помощи IP-адресов подов (без NAT).

  • Изоляция (ограничение того, с чем может взаимодействовать каждый под) определяется сетевыми политиками.

Давайте создадим два пространства имён и развернём в них несколько подов, а после этого потестируем, как именно поды взаимодействуют друг с другом в кластере Kubernetes.

Взаимодействие между подами в пределах одного пространства имён

Создайте новое пространство имён с именем prod и разверните в нём три пода.

# Create a new namespace and add label to that namespaces
> kubectl create ns prod
> kubectl label ns prod team=prod

# Change context to the new context (namespace)
> kubectl config set-context --current --namespace=prod

# Deploy three webservers into the prod namespace
> kubectl run webserver-1 --image=nginx --labels='role=webserver-1'
> kubectl run webserver-2 --image=nginx --labels='role=webserver-2'
> kubectl run webserver-3 --image=nginx --labels='role=webserver-3'

# Fetch the IP-Addresses of the pods
> kubectl get pods -o wide
-----------------------------------------------------------------
NAME          READY   STATUS    RESTARTS   AGE     IP              
webserver-1   1/1     Running   0          3m7s    10.244.189.206
webserver-2   1/1     Running   0          2m21s   10.244.151.207
webserver-3   1/1     Running   0          2m15s   10.244.151.208
-----------------------------------------------------------------

Создайте другое пространство имён с именем dev и разверните в нём два пода.

# Create a new namespace and add label to that namespaces
> kubectl create ns dev
> kubectl label ns dev team=dev


# Deploy two pod into the dev namespace
> kubectl run testserver-1 --image=nginx --labels='role=testserver-1' -n dev
> kubectl run testserver-2 --image=nginx --labels='role=testserver-2' -n dev


# Fetch the IP-Addresses of the pods
> kubectl get pods -o wide -A | grep -i 
------------------------------------------------------------------------------
NAMESPACE   NAME          READY    STATUS    RESTARTS   AGE     IP              
dev         testserver-1   1/1     Running   0          3m7s    10.244.189.207
dev         testserver-2   1/1     Running   0          2m21s   10.244.189.208
------------------------------------------------------------------------------

Теперь давайте посмотрим, могут ли два пода (webserver-1 и webserver-2) взаимодействовать друг с другом в одном пространстве имён (prod):

# Dive into the webserver-1 resides in the "prod" namespace
> kubectl exec -it webserver-1 -n prod bash

# Run curl to access pod named "webserver-2" resides in the same namespace
root@webserver-1:>  curl 10.244.151.207        #ip-address of the "webserver-2" pod
------------------------------------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>             # Successfully accessed
<style>
....
------------------------------------------------------------------------------------------

Аналогичным образом webserver-2 может взаимодействовать с webserver-3.

Взаимодействие между подами из разных пространств имён

Теперь мы протестируем связь между двумя пространствами имён. Давайте посмотрим, сможет ли под (testserver-1), находящийся в пространстве имён “dev”, взаимодействовать с подом (webserver-1), находящимся в пространстве имён “prod”:

# Dive into the pod "testserver-1" resides in the "dev" namespace 
> kubectl exec -it testserver-1 -n dev bash

# Run curl to access the pod named "webserver-1" resides in the "prod" namespace
root@testserver-1:>  curl 10.244.189.206        #ip-address of the "webserver-1" pod
------------------------------------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>        # Successfully accessed
<style>
...
------------------------------------------------------------------------------------------

Мы увидели, что под может свободно взаимодействовать с другими подами; даже поды из разных пространств имён могут взаимодействовать друг с другом. В многопользовательской производственной среде распространена практика распределения доступов между подами. В Kubernetes распределение доступов между подами осуществляется с помощью сетевых политик (Network Policy).

Сетевая политика (Network Policy)

Сетевая политика позволяет нам ограничивать входящий и исходящий трафик в поды и из подов. Используя сетевые политики, мы можем сдерживать входящий трафик баз данных подов только backend подами, а также мы можем ограничить входящий трафик backend пода только frontend подом. В результате, если абстрактный злоумышленник получит доступ к frontend приложениям, он не сможет напрямую получить доступ к базам данных подов или любым другим подам.

Что касается программного и аппаратного обеспечения, то управление трафиком может осуществляться при помощи брандмауэров. Но, в случае с Kubernetes, функциональность управления трафиком реализуется сетевыми плагинами и контролируется сетевыми политиками.

Предварительные условия

Сетевой плагин реализует сетевые политики. Для применения сетевых политик вы должны использовать решение, поддерживающее сетевые политики.

Подробнее о сетевых плагинах и процессе установки.

Пошаговая инструкция

Давайте пошагово пройдёмся по сетевой политике, представленной ниже.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: database-tier
spec:
  podSelector:               # To which the policy applies. 
    matchLabels:
      role: db
  policyTypes:               # Include either Ingress, Egress, or both.
    - Ingress
    - Egress
  ingress:                   # Allows traffic which matches both the "from" and "ports" sections.
    - from:
        - namespaceSelector: # Allows a all pods within a praticular namespace.
            matchLabels:
              tier: web-tier
        - podSelector:       # Allows a particular pod.
            matchLabels:
              role: frontend
      ports:                 # Allows a particular "protocol" and "port".
        - protocol: TCP
          port: 8080
  egress:                    # Allows traffic which matches both the "to" and "ports" sections.
    - to:
      - ipBlock:             # Allows a particular IP CIDR ranges.
          cidr: 10.3.0.0/16
          except:            # Restricts a particular IP CIDR ranges.
            - 10.3.1.0/24
      ports:                
        - protocol: TCP
          port: 32000        # Targeting a range of ports.
          endPort: 32768     # The "endPort" must be equal to or greater than the "port".
          
---

Предостережения

namespaceSelector И podSelector

Если мы хотим разрешить связь с определённым под'ом или от него ( to/from) в пределах определённого пространства имён, мы должны настроить правила следующим образом —

..
  ingress:
  - from:
    - namespaceSelector:      #
        matchLabels:          #
          tier: database      #  Allows after matching                           
      podSelector:            #  namespaceSelector AND podSelector
        matchLabels:          #
          role: db            #
  ...

namespaceSelector ИЛИ podSelector  

Если мы хотим разрешить трафику входить или выходить из опредёленного пространства имён (to/from) или определённым группам подов впускать и выпускать трафик из любых пространств имён (to/from), тогда мы должны настроить правила следующим образом —

...
  ingress:
  - from:
    - namespaceSelector:      #
        matchLabels:          #
          tier: database      #  Allows after matching                           
    - podSelector:            #  namespaceSelector OR podSelector
        matchLabels:          #
          role: db            #
  ...

1. Входящий трафик

1.1 Разрешите входящий трафик из подов в том же пространстве имён

# Create the network-policy using the following manifest file:
> kubectl create -f \
https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-allow-ingress.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-webserver-1
  namespace: prod
spec:
  podSelector:
    matchLabels:
      role: webserver-1
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: webserver-2
      ports:
        - port: 80
---

Если мы создадим сетевую политику, используя вышеприведённый файл манифеста, то наш webserver-1 разрешит только входящий трафик с webserver-2, но поскольку мы ничего не настраивали для исходящего трафика, webserver-1 может отправить исходящий трафик на webserver-2 и webserver-3. Посмотрите на следующее изображение для лучшего понимания:

1.1.0 Входящий тест

# Dive into the webserver-2 pod
> kubectl exec -it webserver-2 bash

# Try to reach webserver-1 pod
> curl 10.244.189.206         #IP address of webserver-1 pod
---------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
---------------------------------------------------------------

# Exit from the webserver-2 pod
# Dive into the webserver-3 pod
> kubectl exec -it webserver-3 bash

# Try to reach webserver-1 pod
> curl 10.244.189.206         #IP address of webserver-1 pod
---------------------------------------------------------------
You can see that from webserver-3 to webserver-1 is unreachable
---------------------------------------------------------------

1.2 Разрешить входящий трафик из подов в другом пространстве имён

# Create a network policy using the following manifest file:
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-allow-ingress-from-different-ns.yaml
  
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-ingress-from-different-ns
  namespace: prod
spec:
  podSelector:
    matchLabels:
      role: webserver-1
  ingress:
  - from:
    - podSelector:
        matchLabels:
          role: testserver-1
      namespaceSelector:
        matchLabels:
          team: dev
    ports:
    - port: 80
---

Если мы создадим сетевую политику, используя вышеприведённый файл манифеста, то webserver-1 разрешит только входящий трафик с testserver-1, который находится в другом пространстве имён с именем dev. Посмотрите на следующее изображение для лучшего понимания:

Теперь позвольте мне спросить вас: testserver-1 из пространства имён dev может получить доступ только к webserver-1 или также может получить доступ ко всем существующим площадкам в пространстве имён prod?

Ответ ДА!

Прямо сейчас testserver1 из пространства имён dev сможет получить доступ не только к поду webserver-1 или группе подов, но также ко всем существующим подам в пространстве имён prod.

Если вы не хотите задействовать все поды в разных пространствах имён, лучше всего по умолчанию применить к пространствам имён сетевую политику deny-all ingress.

1.3 Создайте политику deny-all ingress по умолчанию

Следующая сетевая политика реализует политику deny-all ingress по умолчанию, которая запрещает весь входящий трафик на поды в определённом пространстве имён.

# Create default deny-all ingress network policy using the following manifest file: 
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-default-deny-all-ingress.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: prod
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
  - Ingress
---

Весь входящий трафик на поды в определённом пространстве имён (prod) будет запрещён, если только трафик явно не разрешён другой сетевой политикой.

Когда мы применим политику deny-all ingress по умолчанию вместе со входом из другого пространства имён, после этого из разных пространств имён можно будет получить доступ только к выбранным подам.

2. Исходящий трафик

2.1 Разрешите исходящий трафик для подов в том же пространстве имён

# Create the network-policy using the following manifest file:
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-allow-egress-from-pod.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-from-webserver-3
  namespace: prod
spec:
  podSelector:
    matchLabels:
      role: webserver-1
  policyTypes:
    - Egress
  egress:
    - to:
      - podSelector:
            matchLabels:
              role: webserver-3
      ports:
        - protocol: TCP
          port: 80
---

Когда мы создадим сетевую политику, используя приведённый выше файл манифеста, ваш webserver-1 сможет отправлять только исходящий трафик на webserver-3. Но поскольку мы ничего не настраивали для входящего трафика, webserver-1 разрешит входящий трафик из любого пространства. Посмотрите на следующее изображение для лучшего понимания:

2.1.0 Исходящий тест

# Dive into the webserver-1 pod
> kubectl exec -it webserver-1 bash

# Try to reach webserver-2 pod
> curl 10.244.151.207        #IP address of webserver-2 pod
---------------------------------------------------------------
You will see, webserver-2 is unreachable from webserver-1
---------------------------------------------------------------

# Try to reach webserver-3 pod
> curl 10.244.151.208        #IP address of webserver-3 pod
---------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
---------------------------------------------------------------

2.2 Разрешите исходящий трафик для подов в разных пространствах имён

# Create network policy using the following manifest file: 
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-allow-egress-different-namespace.yaml

---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-egress-different-namespace
  namespace: prod
spec:
  podSelector:
    matchLabels:
      role: webserver-1
  egress:
  - to:
    - podSelector:
        matchLabels:
          role: testserver-1
      namespaceSelector:
        matchLabels:
          team: dev
    ports:
    - port: 80
---

Если мы создадим сетевую политику, используя приведённый выше файл манифеста, то наш под webserver-1 в пространстве имён prod сможет отправлять исходящий трафик только на определённые поды (testserver-1) в пространстве имён dev. Другие поды, находящиеся в пространстве имён dev, не смогут быть доступны с подов webserver-1. Для лучшего понимания:

В приведённой выше иллюстрации мы увидели, что webserver-1 может отправлять трафик только на под testserver-1. Но мы также должны иметь в виду, что не только под webserver-1 может отправлять трафик в под testserver-1, но и другие поды, находящиеся в пространстве имён prod, могут отправлять трафик в пространство имён dev.

Если вы не хотите разрешать другим подам в пространстве имён prod отправлять исходящий трафик в пространство имён dev или предотвращать случайный доступ к подам, рекомендуется применять политику по умолчанию deny-all egress наряду с другими политиками.

# Create default deny-all-egress network policy using the following manifest file: 
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-default-deny-all-egress.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: prod
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
  - Egress
---

Выбранные поды смогут отправлять исходящий трафик в разные пространства имён только при условии, что мы применим политику deny-all ingress.

3. Deny all ingress по умолчанию и весь исходящий трафик

Мы можем создать default-политику для пространства имён, которая предотвращает весь входящий и исходящий трафик, создав следующую NetworkPolicy в этом пространстве имён

# Create default deny-all network policy using the following manifest file: 
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-default-deny-all.yaml
  
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny-all
  namespace: prod
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
  - Ingress
  - Egress
---

Это гарантирует, что всем подам в определенном пространстве имён (в данном случае - prod) не будет разрешён входящий или исходящий трафик, если только трафик явно не разрешён другой сетевой политикой.

slurm.club/3FY0iSL
slurm.club/3FY0iSL

Бонус

Давайте ещё чуть подробнее рассмотрим сетевые политики.

Предположим, у нас имеется несколько пространств имён в кластере k8s, но у нас также есть требование изолировать наше пространство имён prod от других пространств имён. Единственное пространство имён, которое может получить доступ к пространству имён prod, — это пространство имён dev. Давайте посмотрим, как мы можем этого достичь:

Чтобы достичь вышеописанной цели, для начала нужно применить сетевые политики deny-all ingress и all-egress traffic по умолчанию к пространству имён prod. Затем нужно применить другую сетевую политику к пространству имён prod. Таким образом, все поды в пространстве имён prod смогут разрешать входящий трафик только из пространства имён dev. Создайте сетевую политику, используя следующий файл манифеста —

# Allow ingress from dev namespace by creating a network policy using the following manifest file: 
> kubectl create -f \
  https://raw.githubusercontent.com/shamimice03/Kubernetes/main/Security/netpol-from-ns.yaml

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dev-namespace-ingress
  namespace: prod
spec:
  podSelector:
    matchLabels: {}
  ingress:
    - from:
      - podSelector:
          matchLabels: {}
        namespaceSelector:
           matchLabels:
              team: dev
---

После применения сетевой политики, показанной выше, только пространство имён dev сможет отправлять трафик в пространство имён prod.

Комментарии (0)