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
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
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.
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.
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.
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
.
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.
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.
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.
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"]
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