Usar o Firewall do Azure para proteger clusters do Serviço Kubernetes do Azure (AKS)

Este artigo mostra como você pode proteger clusters do Serviço Kubernetes do Azure (AKS) usando o Firewall do Azure para proteger o tráfego de entrada e saída.

Fundo

O Serviço Kubernetes do Azure (AKS) oferece um cluster Kubernetes gerenciado no Azure. Para obter mais informações, consulte Serviço Kubernetes do Azure.

Apesar do AKS ser uma solução totalmente gerenciada, ele não oferece uma solução integrada para proteger o tráfego de entrada e saída entre o cluster e as redes externas. O Firewall do Azure oferece uma solução para isso.

Os clusters AKS são implantados em uma rede virtual. Esta rede pode ser gerida (criada pelo AKS) ou personalizada (pré-configurada pelo utilizador previamente). Em ambos os casos, o cluster tem dependências de saída em serviços fora dessa rede virtual (o serviço não tem dependências de entrada). Para fins operacionais e de gerenciamento, os nós em um cluster AKS precisam acessar determinadas portas e FQDNs (nomes de domínio totalmente qualificados) que descrevem essas dependências de saída. Isso é necessário para várias funções, incluindo, mas não limitado a, nós que se comunicam com o servidor de API do Kubernetes. Eles baixam e instalam os principais componentes de cluster do Kubernetes e as atualizações de segurança do nó, ou extraem imagens de contêiner do sistema base do Microsoft Container Registry (MCR) e assim por diante. Essas dependências de saída são quase inteiramente definidas com FQDNs, que não têm endereços estáticos por trás. A falta de endereços estáticos significa que os Grupos de Segurança de Rede não podem ser usados para bloquear o tráfego de saída de um cluster AKS. Por esse motivo, por padrão, os clusters AKS têm acesso irrestrito à Internet de saída (saída). Esse nível de acesso à rede permite que nós e serviços executados acessem recursos externos conforme necessário.

No entanto, em um ambiente de produção, as comunicações com um cluster Kubernetes devem ser protegidas para evitar a exfiltração de dados e outras vulnerabilidades. Todo o tráfego de entrada e saída da rede deve ser monitorado e controlado com base em um conjunto de regras de segurança. Se você quiser fazer isso, terá que restringir o tráfego de saída, mas um número limitado de portas e endereços deve permanecer acessível para manter as tarefas de manutenção de cluster saudáveis e satisfazer as dependências de saída mencionadas anteriormente.

A solução mais simples usa um dispositivo de firewall que pode controlar o tráfego de saída com base em nomes de domínio. Um firewall normalmente estabelece uma barreira entre uma rede confiável e uma rede não confiável, como a Internet. O Firewall do Azure, por exemplo, pode restringir o tráfego HTTP e HTTPS de saída com base no FQDN do destino, oferecendo controle de tráfego de saída refinado, mas, ao mesmo tempo, permite que você forneça acesso aos FQDNs que abrangem as dependências de saída de um cluster AKS (algo que os NSGs não podem fazer). Da mesma forma, você pode controlar o tráfego de entrada e melhorar a segurança habilitando a filtragem baseada em inteligência de ameaças em um Firewall do Azure implantado em uma rede de perímetro compartilhada. Essa filtragem pode fornecer alertas e negar tráfego de e para endereços IP e domínios mal-intencionados conhecidos.

Veja o seguinte vídeo de Abhinav Sriram para uma rápida visão geral de como isso funciona na prática em um ambiente de exemplo:

Você pode baixar um arquivo zip do Centro de Download da Microsoft que contém um arquivo de script bash e um arquivo yaml para configurar automaticamente o ambiente de exemplo usado no vídeo. Ele configura o Firewall do Azure para proteger o tráfego de entrada e saída. Os guias a seguir percorrem cada etapa do script com mais detalhes para que você possa configurar uma configuração personalizada.

O diagrama a seguir mostra o ambiente de exemplo do vídeo que o script e o guia configuram:

Diagram showing A K S cluster with Azure Firewall for ingress egress filtering.

Há uma diferença entre o script e o guia a seguir. O script usa identidades gerenciadas, mas o guia usa uma entidade de serviço. Isso mostra duas maneiras diferentes de criar uma identidade para gerenciar e criar recursos de cluster.

Restringir o tráfego de saída usando o Firewall do Azure

Definir configuração através de variáveis de ambiente

Defina um conjunto de variáveis de ambiente a serem usadas em criações de recursos.

PREFIX="aks-egress"
RG="${PREFIX}-rg"
LOC="eastus"
PLUGIN=azure
AKSNAME="${PREFIX}"
VNET_NAME="${PREFIX}-vnet"
AKSSUBNET_NAME="aks-subnet"
# DO NOT CHANGE FWSUBNET_NAME - This is currently a requirement for Azure Firewall.
FWSUBNET_NAME="AzureFirewallSubnet"
FWNAME="${PREFIX}-fw"
FWPUBLICIP_NAME="${PREFIX}-fwpublicip"
FWIPCONFIG_NAME="${PREFIX}-fwconfig"
FWROUTE_TABLE_NAME="${PREFIX}-fwrt"
FWROUTE_NAME="${PREFIX}-fwrn"
FWROUTE_NAME_INTERNET="${PREFIX}-fwinternet"

Criar uma rede virtual com várias sub-redes

Crie uma rede virtual com duas sub-redes separadas, uma para o cluster e outra para o firewall. Opcionalmente, você também pode criar um para entrada de serviço interno.

Empty network topology

Crie um grupo de recursos para armazenar todos os recursos.

# Create Resource Group

az group create --name $RG --location $LOC

Crie uma rede virtual com duas sub-redes para hospedar o cluster AKS e o Firewall do Azure. Cada um tem a sua própria sub-rede. Comecemos pela rede AKS.

# Dedicated virtual network with AKS subnet

az network vnet create \
    --resource-group $RG \
    --name $VNET_NAME \
    --location $LOC \
    --address-prefixes 10.42.0.0/16 \
    --subnet-name $AKSSUBNET_NAME \
    --subnet-prefix 10.42.1.0/24

# Dedicated subnet for Azure Firewall (Firewall name cannot be changed)

az network vnet subnet create \
    --resource-group $RG \
    --vnet-name $VNET_NAME \
    --name $FWSUBNET_NAME \
    --address-prefix 10.42.2.0/24

Criar e configurar um Firewall do Azure com um UDR

As regras de entrada e saída do Firewall do Azure devem ser configuradas. O principal objetivo do firewall é permitir que as organizações configurem regras granulares de entrada e saída de tráfego dentro e fora do Cluster AKS.

Firewall and UDR

Importante

Se o cluster ou aplicativo criar um grande número de conexões de saída direcionadas para o mesmo ou pequeno subconjunto de destinos, talvez seja necessário mais IPs de front-end de firewall para evitar o máximo de portas por IP de frontend. Para obter mais informações sobre como criar um firewall do Azure com vários IPs, consulte aqui

Crie um recurso IP público de SKU padrão que é usado como o endereço de front-end do Firewall do Azure.

az network public-ip create -g $RG -n $FWPUBLICIP_NAME -l $LOC --sku "Standard"

Registre a extensão cli de visualização para criar um Firewall do Azure.

# Install Azure Firewall preview CLI extension

az extension add --name azure-firewall

# Deploy Azure Firewall

az network firewall create -g $RG -n $FWNAME -l $LOC --enable-dns-proxy true

O endereço IP criado anteriormente agora pode ser atribuído ao front-end do firewall.

Nota

A configuração do endereço IP público para o Firewall do Azure pode levar alguns minutos. Para aproveitar o FQDN em regras de rede, precisamos de proxy DNS habilitado, quando habilitado, o firewall escutará na porta 53 e encaminhará solicitações DNS para o servidor DNS especificado anteriormente. Isso permitirá que o firewall traduza esse FQDN automaticamente.

# Configure Firewall IP Config

az network firewall ip-config create -g $RG -f $FWNAME -n $FWIPCONFIG_NAME --public-ip-address $FWPUBLICIP_NAME --vnet-name $VNET_NAME

Quando o comando anterior tiver êxito, salve o endereço IP do frontend do firewall para configuração posterior.

# Capture Firewall IP Address for Later Use

FWPUBLIC_IP=$(az network public-ip show -g $RG -n $FWPUBLICIP_NAME --query "ipAddress" -o tsv)
FWPRIVATE_IP=$(az network firewall show -g $RG -n $FWNAME --query "ipConfigurations[0].privateIPAddress" -o tsv)

Criar um UDR com um salto para o Firewall do Azure

O Azure roteia automaticamente o tráfego entre sub-redes do Azure, redes virtuais e redes locais. Se você quiser alterar qualquer roteamento padrão do Azure, faça isso criando uma tabela de rotas.

Crie uma tabela de rotas vazia para ser associada a uma determinada sub-rede. A tabela de rotas definirá o próximo salto como o Firewall do Azure criado anteriormente. Cada sub-rede pode ter zero ou uma tabela de rotas associada a si.

# Create UDR and add a route for Azure Firewall

az network route-table create -g $RG -l $LOC --name $FWROUTE_TABLE_NAME
az network route-table route create -g $RG --name $FWROUTE_NAME --route-table-name $FWROUTE_TABLE_NAME --address-prefix 0.0.0.0/0 --next-hop-type VirtualAppliance --next-hop-ip-address $FWPRIVATE_IP
az network route-table route create -g $RG --name $FWROUTE_NAME_INTERNET --route-table-name $FWROUTE_TABLE_NAME --address-prefix $FWPUBLIC_IP/32 --next-hop-type Internet

Consulte a documentação da tabela de rotas de rede virtual sobre como você pode substituir as rotas padrão do sistema do Azure ou adicionar mais rotas à tabela de rotas de uma sub-rede.

Adicionando regras de firewall

Nota

Para aplicativos fora dos namespaces kube-system ou gatekeeper-system que precisam falar com o servidor de API, é necessária uma regra de rede adicional para permitir a comunicação TCP com a porta 443 para o IP do servidor de API, além de adicionar a regra de aplicativo para fqdn-tag AzureKubernetesService.

Você pode usar as três regras de rede a seguir para configurar seu firewall. Talvez seja necessário adaptar essas regras com base em sua implantação. A primeira regra permite o acesso à porta 9000 via TCP. A segunda regra permite o acesso às portas 1194 e 123 via UDP. Ambas as regras permitem apenas o tráfego destinado ao CIDR da Região do Azure que estamos a utilizar, neste caso o Leste dos EUA.

Finalmente, adicionamos uma terceira regra de rede abrindo a porta 123 a um FQDN do servidor de tempo da Internet (por exemplo:ntp.ubuntu.com) via UDP. Adicionar um FQDN como uma regra de rede é um dos recursos específicos do Firewall do Azure, e você precisa adaptá-lo ao usar suas próprias opções.

Depois de definir as regras de rede, também adicionaremos uma regra de aplicativo usando o AzureKubernetesService que cobre os FQDNs necessários acessíveis através das portas TCP 443 e 80. Além disso, talvez seja necessário configurar mais regras de rede e aplicativos com base em sua implantação. Para obter mais informações, consulte Rede de saída e regras FQDN para clusters do Serviço Kubernetes do Azure (AKS).

Adicionar regras de rede FW

az network firewall network-rule create -g $RG -f $FWNAME --collection-name 'aksfwnr' -n 'apiudp' --protocols 'UDP' --source-addresses '*' --destination-addresses "AzureCloud.$LOC" --destination-ports 1194 --action allow --priority 100
az network firewall network-rule create -g $RG -f $FWNAME --collection-name 'aksfwnr' -n 'apitcp' --protocols 'TCP' --source-addresses '*' --destination-addresses "AzureCloud.$LOC" --destination-ports 9000
az network firewall network-rule create -g $RG -f $FWNAME --collection-name 'aksfwnr' -n 'time' --protocols 'UDP' --source-addresses '*' --destination-fqdns 'ntp.ubuntu.com' --destination-ports 123

Adicionar regras de aplicativo FW

az network firewall application-rule create -g $RG -f $FWNAME --collection-name 'aksfwar' -n 'fqdn' --source-addresses '*' --protocols 'http=80' 'https=443' --fqdn-tags "AzureKubernetesService" --action allow --priority 100

Consulte a documentação do Firewall do Azure para saber mais sobre o serviço Firewall do Azure.

Associar a tabela de rotas ao AKS

Para associar o cluster ao firewall, a sub-rede dedicada para a sub-rede do cluster deve fazer referência à tabela de rotas criada anteriormente. A associação pode ser feita emitindo um comando para a rede virtual que contém o cluster e o firewall para atualizar a tabela de rotas da sub-rede do cluster.

# Associate route table with next hop to Firewall to the AKS subnet

az network vnet subnet update -g $RG --vnet-name $VNET_NAME --name $AKSSUBNET_NAME --route-table $FWROUTE_TABLE_NAME

Implantar o AKS com o tipo de UDR de saída na rede existente

Agora, um cluster AKS pode ser implantado na rede virtual existente. Você também usa o tipo de saída, esse recurso garante que qualquer tráfego de saída seja forçado através do firewall e que não existam outros caminhos de saída (por padrão, o tipo userDefinedRoutingde saída do Balanceador de Carga pode ser usado).

aks-deploy

A sub-rede de destino a ser implantada é definida com a variável de ambiente, $SUBNETID. Não definimos a $SUBNETID variável nas etapas anteriores. Para definir o valor para o ID da sub-rede, você pode usar o seguinte comando:

SUBNETID=$(az network vnet subnet show -g $RG --vnet-name $VNET_NAME --name $AKSSUBNET_NAME --query id -o tsv)

Você define o tipo de saída para usar o UDR que já existe na sub-rede. Essa configuração permite que o AKS ignore a instalação e o provisionamento de IP para o balanceador de carga.

Importante

Para obter mais informações sobre UDR de tipo de saída, incluindo limitações, consulte UDR de tipo de saída de saída.

Gorjeta

Recursos adicionais podem ser adicionados à implantação do cluster, como o Cluster Privado ou a alteração da SKU do sistema operacional.

O recurso AKS para intervalos de IP autorizados pelo servidor API pode ser adicionado para limitar o acesso do servidor API apenas ao ponto de extremidade público do firewall. O recurso de intervalos de IP autorizados é indicado no diagrama como opcional. Ao habilitar o recurso de intervalo de IP autorizado para limitar o acesso ao servidor de API, suas ferramentas de desenvolvedor devem usar uma jumpbox da rede virtual do firewall ou você deve adicionar todos os pontos de extremidade do desenvolvedor ao intervalo de IP autorizado.

az aks create -g $RG -n $AKSNAME -l $LOC \
  --node-count 3 \
  --network-plugin azure \
  --outbound-type userDefinedRouting \
  --vnet-subnet-id $SUBNETID \
  --api-server-authorized-ip-ranges $FWPUBLIC_IP

Nota

Para criar e usar sua própria VNet e tabela de rotas com kubenet plug-in de rede, você precisa usar uma identidade gerenciada atribuída pelo usuário. Para a identidade gerenciada atribuída pelo sistema, não podemos obter a ID de identidade antes de criar o cluster, o que causa atraso para que a atribuição de função entre em vigor.

Para criar e usar sua própria VNet e tabela de rotas com azure plug-in de rede, há suporte para identidades gerenciadas atribuídas pelo sistema e pelo usuário.

Habilitar o acesso do desenvolvedor ao servidor de API

Se você usou intervalos de IP autorizados para o cluster na etapa anterior, você deve adicionar seus endereços IP de ferramentas de desenvolvedor à lista de clusters AKS de intervalos IP aprovados para acessar o servidor de API a partir daí. Outra opção é configurar uma jumpbox com as ferramentas necessárias dentro de uma sub-rede separada na rede virtual do Firewall.

Adicione outro endereço IP aos intervalos aprovados com o seguinte comando:

# Retrieve your IP address
CURRENT_IP=$(dig @resolver1.opendns.com ANY myip.opendns.com +short)

# Add to AKS approved list
az aks update -g $RG -n $AKSNAME --api-server-authorized-ip-ranges $CURRENT_IP/32

Use o comando az aks get-credentials para configurar kubectl para se conectar ao cluster Kubernetes recém-criado.

az aks get-credentials -g $RG -n $AKSNAME

Restringir o tráfego de entrada usando o Firewall do Azure

Agora você pode começar a expor serviços e implantar aplicativos nesse cluster. Neste exemplo, expomos um serviço público, mas você também pode optar por expor um serviço interno por meio do balanceador de carga interno.

Public Service DNAT

Implante o aplicativo de aplicativo de votação do Azure copiando o yaml a seguir para um arquivo chamado example.yaml.

# voting-storage-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: voting-storage
spec:
  replicas: 1
  selector:
    matchLabels:
      app: voting-storage
  template:
    metadata:
      labels:
        app: voting-storage
    spec:
      containers:
      - name: voting-storage
        image: mcr.microsoft.com/azuredocs/voting/storage:2.0
        args: ["--ignore-db-dir=lost+found"]
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 250m
            memory: 256Mi
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_ROOT_PASSWORD
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_USER
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_PASSWORD
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_DATABASE
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
---
# voting-storage-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: voting-storage-secret
type: Opaque
data:
  MYSQL_USER: ZGJ1c2Vy
  MYSQL_PASSWORD: UGFzc3dvcmQxMg==
  MYSQL_DATABASE: YXp1cmV2b3Rl
  MYSQL_ROOT_PASSWORD: UGFzc3dvcmQxMg==
---
# voting-storage-pv-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
# voting-storage-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: voting-storage
  labels:
    app: voting-storage
spec:
  ports:
  - port: 3306
    name: mysql
  selector:
    app: voting-storage
---
# voting-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: voting-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: voting-app
  template:
    metadata:
      labels:
        app: voting-app
    spec:
      containers:
      - name: voting-app
        image: mcr.microsoft.com/azuredocs/voting/app:2.0
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          name: http
        env:
        - name: MYSQL_HOST
          value: "voting-storage"
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_USER
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_PASSWORD
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_DATABASE
        - name: ANALYTICS_HOST
          value: "voting-analytics"
---
# voting-app-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: voting-app
  labels:
    app: voting-app
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
    name: http
  selector:
    app: voting-app
---
# voting-analytics-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: voting-analytics
spec:
  replicas: 1
  selector:
    matchLabels:
      app: voting-analytics
      version: "2.0"
  template:
    metadata:
      labels:
        app: voting-analytics
        version: "2.0"
    spec:
      containers:
      - name: voting-analytics
        image: mcr.microsoft.com/azuredocs/voting/analytics:2.0
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          name: http
        env:
        - name: MYSQL_HOST
          value: "voting-storage"
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_USER
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_PASSWORD
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: voting-storage-secret
              key: MYSQL_DATABASE
---
# voting-analytics-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: voting-analytics
  labels:
    app: voting-analytics
spec:
  ports:
  - port: 8080
    name: http
  selector:
    app: voting-analytics

Implante o serviço executando:

kubectl apply -f example.yaml

Adicionar uma regra DNAT ao Firewall do Azure

Importante

Ao usar o Firewall do Azure para restringir o tráfego de saída e criar uma rota definida pelo usuário (UDR) para forçar todo o tráfego de saída, certifique-se de criar uma regra DNAT apropriada no Firewall para permitir corretamente o tráfego de entrada. Usar o Firewall do Azure com um UDR interrompe a configuração de entrada devido ao roteamento assimétrico. (O problema ocorre se a sub-rede AKS tiver uma rota padrão que vá para o endereço IP privado do firewall, mas você estiver usando um balanceador de carga público - ingress ou serviço Kubernetes do tipo: LoadBalancer). Nesse caso, o tráfego do balanceador de carga de entrada é recebido por meio de seu endereço IP público, mas o caminho de retorno passa pelo endereço IP privado do firewall. Como o firewall tem monitoração de estado, ele descarta o pacote que retorna porque o firewall não está ciente de uma sessão estabelecida. Para saber como integrar o Firewall do Azure ao seu balanceador de carga de entrada ou serviço, consulte Integrar o Firewall do Azure ao Balanceador de Carga Padrão do Azure.

Para configurar a conectividade de entrada, uma regra DNAT deve ser gravada no Firewall do Azure. Para testar a conectividade com o cluster, é definida uma regra para que o endereço IP público front-end do firewall seja encaminhado para o IP interno exposto pelo serviço interno.

O endereço de destino pode ser personalizado, pois é a porta no firewall a ser acessada. O endereço traduzido deve ser o endereço IP do balanceador de carga interno. A porta traduzida deve ser a porta exposta para o serviço Kubernetes.

Você precisa especificar o endereço IP interno atribuído ao balanceador de carga criado pelo serviço Kubernetes. Recupere o endereço executando:

kubectl get services

O endereço IP necessário está listado na coluna EXTERNAL-IP, semelhante à seguinte.

NAME               TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes         ClusterIP      10.41.0.1       <none>        443/TCP        10h
voting-analytics   ClusterIP      10.41.88.129    <none>        8080/TCP       9m
voting-app         LoadBalancer   10.41.185.82    20.39.18.6    80:32718/TCP   9m
voting-storage     ClusterIP      10.41.221.201   <none>        3306/TCP       9m

Obtenha o IP do serviço executando:

SERVICE_IP=$(kubectl get svc voting-app -o jsonpath='{.status.loadBalancer.ingress[*].ip}')

Adicione a regra NAT executando:

az network firewall nat-rule create --collection-name exampleset --destination-addresses $FWPUBLIC_IP --destination-ports 80 --firewall-name $FWNAME --name inboundrule --protocols Any --resource-group $RG --source-addresses '*' --translated-port 80 --action Dnat --priority 100 --translated-address $SERVICE_IP

Validar a conectividade

Navegue até o endereço IP de front-end do Firewall do Azure em um navegador para validar a conectividade.

Você deve ver o aplicativo de votação AKS. Neste exemplo, o IP público do Firewall era 52.253.228.132.

Screenshot shows the A K S Voting App with buttons for Cats, Dogs, and Reset, and totals.

Clean up resources (Limpar recursos)

Para limpar os recursos do Azure, exclua o grupo de recursos AKS.

az group delete -g $RG

Próximos passos