A criação de Network Policies é melhor entendida através de exemplos. Vamos supor o seguinte cenário:

Estamos executando um Pod que expõe uma API para alguns consumidores, este Pod lida com o processamento de pagamentos. A companhia esta em processo de migração de sua antiga aplicação legada de processamento de pagamento para uma nova. Portanto, você só deve permitir o acesso a nova aplicação a partir de clientes que estão aptos a se comunicar adequadamente. Neste momento, existem dois consumidores —uma mercearia e uma cafeteria— sendo executados em Pods diferentes.

A cafeteria esta pronta para consumir a nova API de processamento de pagamentos enquanto a mercearia ainda não foi atualizada. A figura abaixo mostra os Pods, seus labels e as restrições de rede que devem ser aplicadas.

Figure 7-7. Limiting traffic to and from a Pod

Figure 7-7. Limiting traffic to and from a Pod

Não podemos criar uma Network Policy de forma imperativa com o comando create. Ao invés disso, teremos que usar a abordagem declarativa. O manifesto abaixo demonstra a Network Policy para o cenário descrito acima.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: payment-processor
      role: api
  ingress:
	  - from:
	    - podSelector:
	        matchLabels:
	          app: coffee-shop
						role: backend

Criando Caso de Uso Exposto Acima

Todos os deployments criados abaixo possuem um init container para redefinir a página inicial do NGINX e facilitar a visualização.

Avaliando Versões das APIs Disponíveis Antes de Iniciar

$ kubectl api-resources | grep "^network"
	networkpolicies    netpol  networking.k8s.io/v1  true  NetworkPolicy

$ kubectl api-resources | grep "^deployment"
	deployments        deploy  apps/v1               true  Deployment

Payment Processor - Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-processor-deployment    
  namespace: develop                    # 
  labels:                               # DEPLOYMENT LABELS
    section: 07-services-e-networking   #
spec:
  replicas: 1
  selector:                             #
    matchLabels:                        # REPLICASET SELECTOR LABELS
      app: payment-processor            #
  template:
    metadata:                           #
      labels:                           # POD LABELS
        app: payment-processor          #
    spec:
      initContainers:
        - name: init-homepage
          image: busybox
          command: ["/bin/sh", "-c"]
          args: ["echo \\"<h1> Hello, this is Payment processor API </h1>\\" > usr/share/nginx/html/index.html;"]
          volumeMounts:
            - name: init-html-volume
              mountPath: /usr/share/nginx/html
      containers:                       #
        - image: nginx                  # CONTAINER DEFINITION
          name: payment-container       #
          ports:
            - name: payment-port
              containerPort: 80
          volumeMounts:
            - name: init-html-volume
              mountPath: /usr/share/nginx/html
      volumes:
        - name: init-html-volume
          emptyDir: {}
      restartPolicy: Always

Coffee Shop - Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: coffee-shop-deployment
  namespace: develop
  labels:
    section: 07-services-e-networking
spec:
  replicas: 1
  selector:
    matchLabels:
      app: coffee-shop
  template:
    metadata:
      labels:
        app: coffee-shop
    spec:
      initContainers:
        - name: init-html-homepage
          image: busybox
          command: ["/bin/sh", "-c"]
          args: ["echo \\"<h1> Hello, this is Coffee shop API </h1>\\" > usr/share/nginx/html/index.html;"]
          volumeMounts:
            - name: init-html-volume
              mountPath: /usr/share/nginx/html
      containers:
        - image: nginx
          name: coffee-container
          ports:
            - name: coffee-port
              containerPort: 80
          volumeMounts:
            - name: init-html-volume
              mountPath: /usr/share/nginx/html/
      volumes:
        - name: init-html-volume
          emptyDir: {}
      restartPolicy: Always

Groceries Shop - Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: groceries-shop-deployment
  namespace: develop
  labels:
    section: 07-services-e-networking
spec:
  replicas: 1
  selector:
    matchLabels:
      app: groceries-shop
  template:
    metadata:
      labels:
        app: groceries-shop
    spec:
      initContainers:
        - name: init-hompage
          image: busybox                     
          command: ["/bin/sh", "-c"]
          args: ["echo \\"<h1> Hello, this is Groceries shop API </h1>\\" > /usr/share/nginx/html/index.html;"]
          volumeMounts:
            - name: init-html-volume
              mountPath: /usr/share/nginx/html/
      containers:
        - name: groceries-container
          image: nginx  
          ports:
            - name: groceries-port
              containerPort: 80
          volumeMounts:
            - name: init-html-volume
              mountPath: /usr/share/nginx/html/
      volumes:
        - name: init-html-volume
          emptyDir: {}
      restartPolicy: Always

Network Policy - Habilitando conexões apenas para Pods do Coffee Shop Deployment

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-policy
spec:
  podSelector:
    matchLabels:
      app: payment-processor
  policyTypes:
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchLabels:
            name: develop
        podSelector:
          matchExpressions:
            - key: app
              operator: In
              values: ["coffee-shop"]

Aplicando Manifestos

$ kubectl apply -f payment-deployment.yaml
$ kubectl apply -f coffee-deployment.yaml
$ kubectl apply -f groceries-deployment.yaml
	deployment.apps/payment-processor-deployment created
	deployment.apps/coffee-shop-deployment created
	deployment.apps/groceries-shop-deployment created

$ kubectl apply -f network-policy.yaml
	networkpolicy.networking.k8s.io/payment-policy created

$ kubectl get pods -o wide
	NAME                                            IP           
	coffee-shop-deployment-7f67cb8d8b-fjwxl         10.244.2.176
	groceries-shop-deployment-7dfb45cfb7-48rbn      10.244.2.58
	payment-processor-deployment-78987cc877-rb2jg   10.244.1.165

Testes de Conectividade Antes e Depois da Network Policy ser Aplicada

#########################################
### ANTES DE APLICAR A NETWORK POLICY ###
#########################################
# coffee shop consegue acessar o payment processor
$ kubectl exec -it coffee-shop-deployment-7f67cb8d8b-fjwxl -- bash
	# curl 10.244.1.165
	<h1> Hello, this is Payment processor API </h1>

# groceries shop consegue acessar o payment processor
$ kubectl exec -it groceries-shop-deployment-7dfb45cfb7-48rbn -- bash
	# curl 10.244.1.165
	<h1> Hello, this is Payment processor API </h1>

##########################################
### DEPOIS DE APLICAR A NETWORK POLICY ###
##########################################
# coffee shop consegue acessar o payment processor
$ kubectl exec -it coffee-shop-deployment-7f67cb8d8b-fjwxl -- bash
	# curl 10.244.1.165
	<h1> Hello, this is Payment processor API </h1>

# groceries shop não consegue acessar o payment processor
$ kubectl exec -it groceries-shop-deployment-7dfb45cfb7-48rbn -- bash
	# curl 10.244.1.165
	curl: (28) Failed to connect to 10.244.1.165 port 80:Connection timed out

Criando Pods Temporários ao Invés dos Deployments

# Simulando Coffee Shop (acesso permitido)
$ kubectl run coffeeshop --rm -it --image=busybox \\
  --restart=Never -l app=coffeeshop,role=backend -- /bin/sh
		# wget --spider --timeout=1 10.0.0.51
		Connecting to 10.0.0.51 (10.0.0.51:80)
		remote file exists
		# exit
		pod "coffeshop" deleted

# Simulando Grocery Shop (acesso negado)
$ kubectl run grocery-store --rm -it --image=busybox \\
  --restart=Never -l app=grocery-store,role=backend -- /bin/sh
		# wget --spider --timeout=1 10.0.0.51
		Connecting to 10.0.0.51 (10.0.0.51:80)
		wget: download timed out
		# exit
		pod "grocery-store" deleted

Exemplos Regras Ingress e Egress

Todos os exemplos abaixo foram criados após a instalação do Cilium no cluster Kind(kubernetes in docker). A kindnet, isto é, a CNI(Container Network Interface) padrão do Kind não suporta Network Policies e por isso foi substituída pelo Cilium.

A regra abaixo bloqueia todo o tráfego de entrada no Pod payment-processor, não há exceções, não importa o namespace ou quem estiver realizando a requisição.

Untitled

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-processor
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Liberando acesso ao Pod payment-processor para todos os Pods do namespace homolog. Neste exemplo, Pods do namespace develop não conseguirão se comunicar com o payment-processor. Por mais que estejam no mesmo namespace, não existe uma regra para permitir tal ação. Para inverter o diagrama, isto é, permitir que apenas os Pods do namespace develop conversem com o payment-processor basta alterar o atributo name de matchLabels para develop.

Untitled

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-policy
spec:
  podSelector:
    matchLabels:
      app: payment-processor
  policyTypes:
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchLabels:
            name: homolog

Também podemos permitir acesso ao Pod payment-processor a partir de qualquer Pod nos namespaces homolog e develop adicionando outro namespaceSelector a NetworkPolicy.

Untitled

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-policy
spec:
  podSelector:
    matchLabels:
      app: payment-processor
  policyTypes:
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchLabels:
            name: homolog
      - namespaceSelector:
          matchLabels:
            name: develop

Para melhorar o exemplo acima é possível condensar os dois namespaces no mesmo namespaceSelector. No exemplo abaixo, e nos anteriores, ambos namespaces possuem o label name definido. Se o label name não estivesse definido, seria possível utilizar a chave kubernetes.io/metadata.name ****no namespaceSelector.matchExpressions.

Untitled

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-policy
spec:
  podSelector:
    matchLabels:
      app: payment-processor
  policyTypes:
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchExpressions:
            - key: name                      # *kubernetes.io/metadata.name*
              operator: In
              values: ["develop", "homolog"]

Liberando acesso ao Pod payment-processor para um Pod específico do namespace homolog. No exemplo abaixo o Pod com label app=nginx no namespace homolog conseguirá se comunicar com o payment-processor.

Untitled

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-policy
spec:
  podSelector:
    matchLabels:
      app: payment-processor
  policyTypes:
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchLabels:
            name: homolog
        podSelector:
          matchLabels:
						app: nginx

Permitindo que uma lista arbitrária de Pods que estejam no namespace homolog acessem o payment-processor utilizando matchExpresions.

Untitled

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-policy
spec:
  podSelector:
    matchLabels:
      app: payment-processor
  policyTypes:
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchLabels:
            name: homolog
        podSelector:
          matchExpressions:
            - key: app
              operator: In
              values: ["head-shop", "nginx"]

Ta, mas e os Pods coffee-shop e groceries-shop, eles deveriam ser capazes de utilizar o payment-processor! Simples, basta seguir o mesmo procedimento realizado para os Pods head-shop e nginx do namespace homolog. Para realizar uma restrição remove-se o Pod da lista dos matchExpressions, para realizar uma permissão faz-se o inverso.

Untitled

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-policy
spec:
  podSelector:
    matchLabels:
      app: payment-processor
  policyTypes:
    - Ingress
  ingress:
    - from:
      - namespaceSelector:
          matchLabels:
            name: homolog
        podSelector:
          matchExpressions:
            - key: app
              operator: In
              values: ["head-shop", "nginx"]
      - namespaceSelector:
          matchLabels:
            name: develop
        podSelector:
          matchExpressions:
            - key: app
              operator: In
              values: ["coffee-shop", "groceries-shop"]

Outros Exemplos de Network Policy

Como bloquear todo o tráfego de saída de um Pod exceto resposta as requisições realizadas por Pods clientes? simples! Nos exemplos abaixo o Pod payment-processor não será capaz de realizar requisições a quaisquer Pods do cluster mas responderá as requisições realizadas pelo Pod coffee-shop.

Opção 01

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: payment-processor
      role: api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: coffee-shop
              role: backend

Opção 02, a única diferença é a declaração explicita de um array vazio nas regras de saída(egress)).

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: payment-processor
      role: api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: coffee-shop
              role: backend
	egress: []

E como liberar todo o tráfego de saída?

Opção 01

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: payment-processor
      role: api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: coffee-shop
              role: backend
	egress:
		- {}

Opção 02, simplesmente não declaramos o egress.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: payment-processor
      role: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: coffee-shop
              role: backend

Documentação Kubernetes