O requisito de segurança é muito comum em arquiteturas corporativas, mas é difícil determinar se uma aplicação está segura ou qual é o mínimo necessário. É mais uma jornada do que uma ação específica. Para começar a nossa jornada, vamos criar nosso AKS e ACR totalmente privados dentro de uma rede virtual do Azure.
Escrevi um artigo sobre o AKS, no qual eu falo sobre os conceitos principais e mostro como criar um cluster com um node pool Windows. A ideia foi explorar um cenário comum para aplicações legadas. Se você é novo no AKS, vale a pena conferir.
É muito comum utilizar redes virtuais privadas (VNET) restringindo o acesso à internet. Esse tipo de arquitetura é muito mais segura e desejado pela maioria dos projetos. Porém, para isso, precisamos entender claramente quais redes/sub-redes precisamos e onde cada recurso será criado.
Vamos entender a arquitetura, olhando para o desenho abaixo:

Temos duas VNETs, uma que o próprio AKS criou quando escolhemos o modo de rede kubernet e a outra VNET que criamos manualmente para colocar a VM. Na VNET do AKS, criei uma subnet nova para o “private endpoint” do ACR.
Existem dois pontos de atenção:
- Ao provisionar o AKS através do portal do Azure, o plugin de rede padrão é o “Kubenet”, o que significa que não é possível escolher uma Virtual Network (VNET) pré-existente. Se você precisa usar uma VNET pré-existente, precisará optar pelo modo de rede CNI no portal do Azure ou usar o Azure CLI para criar a instância do AKS. Com o Azure CLI, você poderá escolher o plugin “Kubenet” e também uma VNET pré-existente. saiba mais
- O AKS cria seus recursos internos dentro de outro grupo de recursos, e é nele que está a VNET em questão, assim como outros recursos. Caso queira criar o AKS pelo CLI, dê uma olhada neste artigo: AKS com Windows — Microsoft Community Hub
Criando o AKS pelo portal do Azure
Para criar um cluster AKS pelo portal do Azure, no primeiro passo não é necessário selecionar nenhuma configuração especial para proteger o cluster. A configuração especial necessária para isso está na etapa quatro, na aba Network. Nesta seção, podemos escolher a opção de rede “Kubenet” ou “CNI”, conforme mencionado acima. Além disso, existe a opção “Enable private cluster” que deve ser marcada se desejarmos criar um cluster privado.

Um cluster privado usa um endereço IP interno para garantir que o tráfego de rede entre o servidor API e os node pools permaneça apenas em uma rede privada. Saiba mais
Apenas para facilitar o processo, vamos até a aba “Integrations” e provisionamos o ACR, o único detalhe está na camada que precisa ser Premium para posteriormente configurarmos o acesso privado.

Criação do ACR no momento da criação do AKS
Agora temos o AKS com a VNET criada, podemos confirmar no menu network e também podemos verificar que o cluster está privado.

Configurando o ACR como privado
Após o ACR ser provisionado vamos no menu network e desabilitaremos o acesso público ao ACR conforme a imagem abaixo. Deixaremos os serviços do Azure liberados.

Em seguida, na aba “Private Endpoint”, vamos criar um “Private Endpoint” para a subnet do ACR na VNET do AKS. Vale lembrar que estamos utilizando a VNET criada pelo AKS no RG MC_AKS_PRIVADO_AKSPRV01_eastus. Antes de prosseguir, é necessário criar a subnet manualmente na VNET.
Para criar a subnet, acesse a VNET no RG MC_AKS_PRIVADO_AKSPRV01_eastus e clique no menu Subnets para criar uma nova

Utilize os pontos de extremidade privados para se conectar de forma privada a um serviço ou recurso. É importante lembrar que o ponto de extremidade privado deve estar na mesma região da sua rede virtual, mas pode estar em uma região diferente do recurso ao qual você está se conectando por meio do link privado. Saiba mais
Dê uma olhada no Resource Group criado pelo AKS MC_AKS_PRIVADO_AKSPRV01_eastus nele temos a VNET e vários outros recursos importantes como NSGs, DNS etc.

CSV com os recursos do Resource Group
"08e2c034-1768-463f-8444-1122ee88c2ff","Public IP address","East US"
"aks-agentpool-17649214-nsg","Network security group","East US"
"aks-agentpool-17649214-routetable","Route table","East US"
"aks-agentpool-45541675-vmss","Virtual machine scale set","East US"
"aks-VNET-17649214","Virtual network","East US"
"AKSPRV01-agentpool","Managed Identity","East US"
"d6ce2cd6-9633-40f1-b530-9e27554dde80.privatelink.eastus.azmk8s.io","Private DNS zone","Global"
"kube-apiserver","Private endpoint","East US"
"kube-apiserver.nic.a93724b9-006b-4f14-8bbd-a0bb576abfd6","Network Interface","East US"
"kubernetes","Load balancer","East US"
"kubernetes-aafae011874a74f61a036d25d76ed247","Public IP address","East US"
"omsagent-aksprv01","Managed Identity","East US"
"privatelink.azurecr.io","Private DNS zone","Global"
Máquina Virtual com suporte a Nested Virtualization
Para testar a conectividade com o AKS e o ACR, vamos precisar de uma máquina JUMP, que é uma VM criada para acessar recursos protegidos por VNETs.
É importante escolher o tipo correto de VM, pois ela será utilizada para processos de conteinerização e é provável que seja necessário usar o Docker. Por isso, é recomendado escolher a família correta de máquinas virtuais. No meu caso, utilizei a máquina Standard D2s v3, com 2 vCPUs e 8 GiB de memória
Para entender melhor essa questão de nested virtualization dê uma olhada nesse artigo Saiba mais

Configurando a Máquina Virtual
- Crie a VNET para a máquina virtual. Eu chamei de AKS_PRIVADO-VNET mas recomendo dar um nome melhor
- Crie uma subnet VM
- Crie a máquina virtual Windows (Windows 11 Enterprise) Standard D2s v3 (2 VCPUS, 8 GiB memory) ou Linux se preferir
- Instale o docker desktop a partir daqui
- Instale a atualização do kernel linux daqui # no meu caso, baseado em x64;
Devemos instalar uma distribuição Linux no WSL. Para isto podemos utilizar os seguintes comandos:
Comando | Descrição |
---|---|
wsl -l -v | Lista as distribuições instaladas no WSL |
wsl –list –online | Lista as distribuições disponíveis para instalação |
wsl –install -d Ubuntu | Instala uma distribuição no WSL |
wsl –set-default-version 2 | Define a versão padrão do WSL |
OBS: Ao criar uma máquina virtual no Portal do Azure, é importante estar atento ao campo ‘Security Type’. O modo ‘Trusted’ pode gerar restrições de segurança que podem impedir a instalação do Docker Desktop. Recomenda-se avaliar cuidadosamente as configurações de segurança antes de prosseguir com a criação da VM, a fim de garantir uma instalação sem problemas.

Instalar o Azure CLI
Primeiro instale a CLI do Azure clicando Instalar a CLI do Azure para Windows | Microsoft Learn
Utilizarei diversos comandos da CLI do Azure, inclusive para instalar a CLI do AKS e para efetuar logon no Azure. Para isto utilize os seguintes comandos:
Fazer login na conta do Azure
az login
Para instalar a CLI do AKS usamos o comando
az aks install-cli
Voce também pode baixar o exe diretamente dessa url Baixar apenas kubectl
VNET da VM Peering com a VNET do AKS
Como vamos realizar nossos testes a partir de uma VM que está em uma VNET diferente da VNET do AKS, é necessário configurar o Peering. Essa configuração permitirá que as sub-redes da VNET A, existentes antes do Peering, possam se comunicar com as sub-redes da VNET B. Saiba mais

Meus Resource group AKS_PRIVADO

Exportei o CSV com os recursos do RG
"acrprv01","Container registry","East US"
"AKS_PRIVADO-VNET","Virtual network","East US"
"AKSPRV01","Kubernetes service","East US"
"jumpprv01","Virtual machine","East US"
"jumpprv01-ip","Public IP address","East US"
"jumpprv01-nsg","Network security group","East US"
"jumpprv01950_z1","Network Interface","East US"
"jumpprv01_OsDisk_1_7e010fb8246a4ea596830d75eb08f346","Disk","East US"
"pe_aks_VNET","Private endpoint","East US"
"pe_aks_VNET-nic","Network Interface","East US"
privatelink.azurecr.io
Para realizar nossos testes, usaremos DNS em vez de IPs. Por isso, é necessário associar as Zonas de DNS Privado criadas para resolver os nomes de domínio azurecr.io e azmk8s.io em IPs na VNET da VM.
Esses recursos foram criados automaticamente e já estão associados à VNET do AKS.

Associação do Private Dns Zone azurecr.io com VNET do AKS e da VM
Para realizar a associação, basta acessar a Zona DNS Privada, selecionar o menu ‘Link de rede virtual’ e clicar em ‘Adicionar’. Em seguida, selecione a VNET da VM.

d6ce2cd6–9633–40f1-b530–9e27554dde80.privatelink.eastus.azmk8s.io
Tela do Azure Private Dns Zone azmk8s.io
Associação do azmk8s.io com VNET do AKS e da VM
Para realizar a associação, basta acessar a Zona DNS Privada, selecionar o menu ‘Link de rede virtual’ e clicar em ‘Adicionar’. Em seguida, selecione a VNET da VM.
Tela do Azure Associação do azmk8s.io com VNET do AKS e da VM
Recapitulando pontos chaves da configuração do ambiente
- Definir que o Cluster de AKS será privado
- Contratar o ACR na tier premium
- Desabilitar o acesso público no ACR
- Criar um Private Endpoint no ACR para a VNET do AKS de preferência em uma subnet própria.
- Criar a VM com suporte a Nested Virtualization em um VNET separada e fazer a configuração dos CLIs
- Fazer o Peering entre as VMS
- Associar os private DNS zones com as VNETs envolvidas
Depois disso estamos com o Ambiente pronto e nosso próximo passo é fazer um deploy no AKS.
Deploy no AKS usando imagem compilada com Docker
Conectando no AKS
az aks get-credentials --resource-group AKS_PRIVADO --name AKSPRV01
Precisamos conseguir listar os nodes do AKS para saber que a comunicação com o AKS funcionou corretamente
kubectl get nodes
Vou subir uma aplicação .net core então será necessário ter o SDK do .net core instalada na máquina também essa aplicação tem esse dockerFile
# Stage 1
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /build
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app
# Stage 2
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "Sample.Api.dll"]
Tem uma cópia da API nesse git, clone esse projeto vá até a pasta dele e rode o comando abaixo para gerar uma imagem
docker build -t sampleapi:dev01 .
Depois vamos gerar uma tag
docker tag sampleapi:dev01 acrprv01.azurecr.io/sampleapi:latest
Precisamos fazer o Login no ACR
az acr login -n acrprv01.azurecr.io
E agora podemos subir a imagem no ACR
docker push acrprv01.azurecr.io/sampleapi:latest
Feito isso já podemos gerar nossos arquivos de manifestos para fazer o deploy no AKS vou usar um comando que gerar o arquivo pronto apontando para a imagem.
kubectl create deploy sampleapi --image=acrprv01.azurecr.io/sampleapi:latest --dry-run=client -o yaml > sampleapi.yaml
Aplica o Deploy
kubectl create -f .\\sampleapi.yaml
Gerar arquivo de manifesto para o serviço por enquanto vou usar um ip público mas isso não é o final
kubectl expose -f sampleapi.yaml --name=sampleapisvc --type=LoadBalancer --port=80 --target-port=80 --dry-run=client -o yaml > svcsampleapi.yaml
aplica o Deploy
kubectl create -f .\\svcsampleapi.yaml
Se tudo deu certo você precisa ver os pods rodando e o serviço criado com o IP externo que você vai usar para acessar a aplicação
ao rodar o comando baixo
kubectl get pods
precisamos ver algo com isso
NAME READY STATUS RESTARTS AGE
sampleapi-b87d78d46-wdts5 1/1 Running 0 30m
e para descobrir o IP público usamos
kubectl get services
e precisamos de algo como isso
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 18h
sampleapisvc LoadBalancer 10.0.68.139 20.232.252.49 80:32246/TCP 26m
Por fim, conseguimos acessar a aplicação pelo IP público. No entanto, é importante lembrar que isso não é ideal. O recomendado é criar uma camada adicional de segurança em frente ao AKS e mantê-lo sem acesso público. Para isso, podemos utilizar, por exemplo, o Application Gateway. Ele terá um IP público, mas também estará em uma VNET que pode se comunicar com a VNET do AKS, mantendo tudo seguro.

Compilar com o ACR
Caso você não queira instalar o Docker em sua máquina de JUMP, é possível usar o próprio ACR para compilar suas imagens. Para isso, será necessário configurar um agente e utilizá-lo para a compilação das imagens.
Configurando um Agent
Começamos pegando o id da subnet
$subnetId= az network vnet subnet list --resource-group AKS_PRIVADO --vnet-name AKS_PRIVADO-VNET --query "[0].id" --output tsv
Depois criando o agent
az acr agentpool create --registry acrprv01 --name myagentpool --tier S1 --subnet-id "/subscriptions/60701f8d-0759-4bb3-8d6b-1f1810aef353/resourceGroups/AKS_PRIVADO/providers/Microsoft.Network/virtualNetworks/AKS_PRIVADO-VNET/subnets/default"
E agora rodando ACR buid com o agent criado
az acr build -t sampleapiacr --agent-pool myagentpool --registry acrprv01 .
Podemos ver o agentes criados pelo portal do Azure

Podemos listar as imagens do ACR
az acr repository list -n acrprv01
[
"sampleapi",
"sampleapiacr"
]
AGIC (Application Gateway Ingress Controller)
Fiz uma alteração interessante: ao invés de utilizar o LoadBalancer e expor um IP público para acessar a aplicação no AKS, criei um Ingress Controller para o Application Gateway, deixando tudo privado de ponta a ponta e ganhando todas as funcionalidades de um Gateway de camada 7 como roteamento com base em atributos adicionais de uma solicitação HTTP, por exemplo, caminho de URI ou cabeçalhos de host. Dessa forma, podemos remover o serviço sampleapisvc usando o comando abaixo.
kubectl delete service sampleapisvc
Deletado o serviço vamos começar as configurações no AKS em Settings/network habilitando o Application gateway:

Habilitar Ingress no APPGW
Para fazer um deploy de ingress privado devemos logo depois de integrar o AKS com o APGW criar um IP em Frontend IP configurations.

Ao clicar nesse item será necessário atribuir um IP manualmente marcando a opção Choose a specific private IP address.

Basicamente eu escolhi um IP da subnet do gateway que no meu caso foi essa rede 10.225.0.0/16 então botei o 10.225.0.10.
Para criar um ingress, é necessário associá-lo a um serviço do tipo ClusterIP. Essa associação pode ser realizada por meio da linha de comando (CLI)
kubectl expose deployment sampleapi --type=ClusterIP --port=80 --target-port=80
Vale ressaltar que ‘sample api’ refere-se ao deployment da aplicação que se deseja acessar.
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 3h1m
sampleapi ClusterIP 10.0.135.190 <none> 80/TCP 18m
Em seguida, criei o manifesto ‘deploymentingress.yaml’, apontando-o para o serviço do tipo ClusterIP ‘sampleapi’.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sampleapi
annotations:
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/use-private-ip: "true"
spec:
rules:
- http:
paths:
- path: /
backend:
service:
name: sampleapi
port:
number: 80
pathType: Exact
aplicar o deploy
kubectl apply -f .\\deploymentingress.yaml
Observe que agora o ingress ficou atrelado ao ip privado do gateway
kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
aspnetappv2 <none> \* 10.225.0.15 80 42m
O serviço pode ser acessado de dentro da VNET ou de outras VNETs que possuam peering com a VNET do AKS. Para expor o serviço de forma segura, uma boa opção é utilizar um Azure API Management em uma VNET com peering na VNET do AKS, mantendo assim o AKS totalmente privado e protegido.
Link: https://techcommunity.microsoft.com/t5/desenvolvedores-br/aks-cluster-privado-de-ponta-a-ponta/ba-p/3772687
No Responses