Pular para o conteúdo principal

Network Policies

Vamos iniciar com o básico de redes...

alt text

As respostas ficam permitidas para quem enviou a requisição. Precisamos controlar o que ela pode receber e para onde elas podem ir.

Neste cenário:

A aplicação frontend deve permitir um tráfego de entrada na porta 443 HTTPS que ela escuta para a Internet e deve permitir a comunicação com o backend na porta 5000.

A aplicação backend somente deve aceitar solicitação na porta 5000, responde-las, e pode fazer solicitação para o database na porta 3306.

O database somente aceita solicitações na porta 3306 e respondê-las, não aceitando sair para lugar nenhum.

Para isso precisamos criar regras para os ingress e egress dessas aplicações.

Ingress Rule são as regras de entrada e Egress Rule são as regras de saída.

Quais regras são necessárias para que tudo funcione?

  • Frontend

    • Permitir porta 443 em ingress
    • Permitir backend:5000 em egress
  • Backend

    • Permitir porta 5000 em ingress
    • Permitir database:3306 de egress
  • Database

    • Permitir porta 3306 em ingress

Um dos pré requisitos para o workflow no kubernetes é que qualquer pod possa se comunicar com os outros sem nenhuma configuração adicional como rotas.

alt text

Nesta solução de redes todos os pods estão é uma rede virtual que se estende através dos nodes no cluster kubernetes e todos eles, POR PADRÃO, podem chegar aos outros através de ips, nomes e services configurados para este propósito.

O Kubernetes é configurado por padrão com uma regra (All Allow) que permite o tráfego de qualquer pod para qualquer outro pod.

Voltando ao cenário proposto, tanto o frontend, backend e database podem se comunicar entre si sem nenhum impedimento dentro do cluster. As regras estão indo além do que queremos; para isso precisamos restringir esses pods.

Políticas de rede são aplicadas aos pods, não nos pods. É uma regra completamente independente do pod.

Por exemplo, precisamos que o database somente permita tráfego vindo dos pods backend na porta 3306.

Para vincular uma regra de rede a um pod, usamos a mesma técnica que vinculamos replicasets com pods e services com pods, usando labels e selectors.

networkpolicy

alt text

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Será aplicada em todos os pods que tem a label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Regras de entrada aceita o tráfego de pods que tem a label app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: backend
# Na porta 3306 usando protocolo tcp
ports:
- protocol: TCP
port: 3306

Só existe efeito se tiver ingress e ou egress no policy types pois a regra deve ser aplicada ou na entrada ou na saída.

O CNI usado para solução de network no cluster deve suportar o network policies, caso contrário não surtirá efeito.

Calico, Cilium, Weavenet, Kube-router suportam nativamente network policies

Flannel não suporta.

Agora vamos pensar no seguinte. Temos diferentes pods que podem ter o mesmo label, mas em diferentes namespaces.

alt text

Dessa forma podemos também fechar a nível de namespace.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Será aplicada em todos os pods que tem a label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Regras de entrada aceita o tráfego de pods que tem a label app: backend e estão no namespace prod.
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
# Na porta 3306 usando protocolo tcp
ports:
- protocol: TCP
port: 3306

Se somente usarmos o namespaceSelector todos os pods dentro do namespace poderiam ter acesso ao database.

E se um servidor de backup precisasse acessar diretamente esse database? Na regra anterior não funcionaria.

Porém se analisarmos melhor o yaml veremos que from é uma lista de regras que são permitidas. Se alguma das regras satisfazer então pode passar. No exemplo acima somente temos uma regra (ser do namespace prod e ter a label backend). Vamos adicionar então uma outra regra.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Será aplicada em todos os pods que tem a label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Regras de entrada aceita o tráfego de pods que tem a label app: backend e estão no namespace prod.
ingress:
- from:
########### 1 regra ##########
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
########### 2 regra ##########
- ipBlock:
cidr: 192.168.5.10/32 #Ip do servidor de backup
# Na porta 3306 usando protocolo tcp
ports:
- protocol: TCP
port: 3306

Isso mostra que temos duas regras de entrada e qualquer uma que for satisfeita é suficiente para permitir a conexão.

Regra 1 - Estar no namespace de prod e o pod precisa ter a label app: backend Regra 2 - A conexão precisaria vir de 192.168.5.10

Se tivesse feito isso, apenas cometido o erro de colocar um - na frente de namespaceSelector.

  ingress:
- from:
- podSelector:
matchLabels:
app: backend
- namespaceSelector:
matchLabels:
name: prod
- ipBlock:
cidr: 192.168.5.10/32 #Ip do servidor de backup

Teria 3 regras diferentes:

Regra 1 - Qualquer pod que tenha a label app: backend não interessando o namespace poderia ser permitido.

Regra 2 - Qualquer pod no namespace prod seria permitido

Regra 3 - A requisição vinda do ip 192.168.5.10 seria permitida

Basta um erro aqui que tudo pode mudar!

Mas vamos imaginar que o database quem conecta com o backup na porta 80 e não o contrário, logo.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Adicionado também regra de saída
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80

Tanto o from do ingress e o to do egress também são listas. Logo poderíamos ainda ter mais regras dentro da mesma police.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Adicionado também regra de saída
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
ports:
- protocol: TCP
port: 3306

- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: staging
ports:
- protocol: TCP
port: 3306

egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80

- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 443

Eu sei que isso poderia ser mais simples, mas foi só para ilustrar.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Adicionado também regra de saída
ingress:
- from:
- podSelector:
matchLabels:
app: backend
matchExpressions:
- key: name
operator: In
values:
- prod
- staging
ports:
- protocol: TCP
port: 3306

egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80
ports:
- protocol: TCP
port: 443