Configurar o DNS do Azure e o TLS com a implementação da API do Gateway de Roteamento de Aplicativos

Com a API do Gateway de Roteamento de Aplicativos, os usuários podem expor facilmente aplicativos HTTPS no AKS com seus próprios certificados Azure Key Vault, incluindo publicação automática de Nome de Domínio. O operador de Roteamento de Aplicativos integra-se com o DNS do Azure e o Azure Key Vault e reconcilia um SecretProviderClass, um segredo do Kubernetes para certificados TLS, o campo ouvinte certificateRefs e a implantação separada external-dns para que você não precise gerenciar esses recursos manualmente.

Este artigo mostra como:

  • Provisione os recursos pré-requisitos do Azure (zona DNS do Azure, Azure Key Vault, identidade gerenciada atribuída ao usuário, atribuições de funções e credenciais de identidade federada).
  • Configure um ouvinte Gateway para encerrar o TLS usando um certificado armazenado no Azure Key Vault por meio das opções de TLS do ouvinte kubernetes.azure.com/tls-cert-keyvault-uri e kubernetes.azure.com/tls-cert-service-account.
  • Use os recursos personalizados ClusterExternalDNS e ExternalDNS para publicar registros DNS no DNS do Azure com base nos nomes de host dos seus recursos Gateway, HTTPRoute e GRPCRoute.

Como funciona a integração

O operador de Roteamento de Aplicativos expõe duas integrações para automatizar os recursos que você criaria manualmente para colocar um Gateway recurso online com um domínio personalizado e uma terminação TLS.

Integração do TLS

Quando um recurso Gateway usa uma GatewayClass gerenciada — seja approuting-istio (da implementação da API Application Routing Gateway) ou istio (do complemento Istio service mesh) — e um ouvinte carrega as duas opções TLS a seguir, o operador de Roteamento de Aplicativos reconcilia os recursos necessários para encerrar o TLS usando um certificado armazenado no Azure Key Vault:

Chave de opção TLS Valor
kubernetes.azure.com/tls-cert-keyvault-uri O URI do certificado Azure Key Vault do qual o certificado TLS será originado. Use um URI sem versão (por exemplo, https://<vault>.vault.azure.net/certificates/<cert>) para que o operador detecte automaticamente as rotações de certificados no Azure Key Vault.
kubernetes.azure.com/tls-cert-service-account O nome de um ServiceAccount do Kubernetes no mesmo namespace que o Gateway. A ServiceAccount deve estar vinculada a uma identidade gerenciada atribuída pelo usuário por meio da Identidade de Carga de Trabalho do Microsoft Entra, e essa identidade gerenciada deve ter a função Key Vault Secrets User no Azure Key Vault de destino.

Para cada listener que tem ambas as opções de TLS, o operador:

  1. Provisiona um SecretProviderClass chamado kv-gw-cert-<gateway-name>-<listener-name> no namespace do Gateway, configurado para obter o certificado do Azure Key Vault usando a autenticação de identidade de carga de trabalho.
  2. Desencadeia o provedor do Azure Key Vault para o Driver CSI do repositório de segredos para sincronizar o certificado como um kubernetes.io/tls Secret do Kubernetes com o mesmo nome no namespace do Gateway.
  3. Corrige o campo tls.certificateRefs do ouvinte para fazer referência ao Segredo do Kubernetes sincronizado.

Integração do DNS

O operador de Roteamento de Aplicativos gerencia uma external-dns instância para você por meio de dois recursos personalizados:

Recurso Personalizado Scope
ClusterExternalDNS (clusterexternaldnses.approuting.kubernetes.azure.com) No escopo do cluster. Monitora recursos Gateway, HTTPRoute e GRPCRoute em todos os namespaces do cluster.
ExternalDNS (externaldnses.approuting.kubernetes.azure.com) No escopo do namespace. Observa apenas Gateway, HTTPRoutee GRPCRoute recursos no mesmo namespace que o recurso personalizado.

Ambos os recursos personalizados aceitam seletores opcionais filters para restringir ainda mais quais recursos a instância gerenciada external-dns observa dentro de seu escopo:

Filtro O que restringe
filters.gatewayLabels Restringe quais Gateway recursos o controlador observa.
filters.routeAndIngressLabels Restringe quais recursos HTTPRoute e Ingress o controlador observa.

Para cada recurso personalizado, o operador de Roteamento de Aplicativos:

  1. Implanta uma instância gerenciada external-dns, configurada para obter registros dos recursos HTTPRoute e GRPCRoute, e visa às zonas do DNS do Azure especificadas.
  2. Autentica-se no DNS do Azure usando a Identidade de Carga de Trabalho do Microsoft Entra, por meio da ServiceAccount referenciada no campo identity do recurso personalizado.
  3. Publica registros A em cada uma das zonas do DNS do Azure listadas para cada nome de host HTTPRoute ou GRPCRoute vinculado a um recurso Gateway gerenciado no escopo.

Prerequisites

  • Um cluster do AKS com ambos os seguintes recursos habilitados:

    • O complemento de Roteamento de Aplicativos (--enable-app-routing). Esse complemento implanta o operador de Roteamento de Aplicativos no cluster, que é o componente que reconcilia as integrações DNS e TLS documentadas neste artigo. Em um cluster existente, você também pode habilitar esse recurso usando az aks approuting enable.
    • Uma implementação gerenciada da API de Gateway com a qual o operador possa se integrar. Escolha uma das opções a seguir (as duas opções são mutuamente exclusivas e não podem ser habilitadas ao mesmo tempo):
      • A implementação da API App Routing Gateway no Istio (--enable-app-routing-istio), que fornece a approuting-istio GatewayClass. Em um cluster existente, você também pode habilitá-lo usando az aks approuting gateway istio enable. Use gatewayClassName: approuting-istio em seus Gateway recursos.
      • O complemento Istio service mesh, que fornece a istio GatewayClass. Use esta opção quando quiser que as integrações de DNS e TLS nos recursos Gateway sejam gerenciadas pelo complemento da malha de serviço Istio e para definir gatewayClassName: istio nesses recursos.

    Para usar essas integrações, habilite o complemento de Roteamento de Aplicativos (usando az aks approuting enable) e uma das implementações de API de Gateway gerenciadas listadas anteriormente.

  • A instalação da API Managed Gateway habilitada no cluster.

  • O recurso Identidade de Carga de Trabalho do Microsoft Entra habilitado no cluster, juntamente com o emissor OIDC. Você pode habilitar ambos os recursos usando as flags --enable-oidc-issuer e --enable-workload-identity no az aks create ou no az aks update.

  • O complemento provedor do Azure Key Vault para o Secrets Store CSI Driver está habilitado no cluster. Você pode habilitá-lo usando o az aks enable-addons comando com --addons azure-keyvault-secrets-provider, ou passando --enable-kv para az aks approuting enable ou az aks approuting update.

  • CLI do Azure versão 2.86.0 ou superior Execute az --version para localizar sua azure-cli versão e execute az upgrade para atualizar.

  • RBAC (controle de acesso baseado em função) do Azure suficiente em sua própria identidade para criar atribuições de função e credenciais de identidade federadas. A Owner função ou uma combinação de Role Based Controle de Acesso Administrator e Managed Identity Contributor é suficiente.

Note

Os sinalizadores --attach-kv e --attach-zones em az aks approuting update (e os subcomandos az aks approuting zone) foram projetados para a experiência baseada em NGINX herdada, na qual a identidade gerenciada atribuída pelo usuário do próprio complemento de Roteamento de Aplicativos recebe acesso ao RBAC do Azure para um único Azure Key Vault e a uma única zona DNS. Eles não são usados pela integração da API do Gateway documentada neste artigo. A nova experiência usa a Identidade de Carga de Trabalho do Microsoft Entra em vez da identidade gerenciada do complemento, portanto, você precisa criar sua própria identidade gerenciada atribuída pelo usuário, conceder a ela as funções apropriadas do DNS do Azure e do Azure Key Vault e criar credenciais de identidade federadas que a vinculem às ServiceAccounts do Kubernetes referenciadas nas opções de TLS em seu ouvinte em Gateway e nos recursos personalizados ExternalDNS/ClusterExternalDNS.

Defina as variáveis de ambiente a seguir. O passo a passo os reutiliza em todos os comandos subsequentes:

export RESOURCE_GROUP=<resource-group-name>
export CLUSTER=<cluster-name>
export LOCATION=<azure-region>

Obter credenciais do cluster para kubectl:

az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER

Criar a infraestrutura de Azure

Criar a zona DNS do Azure

Se você já tiver uma zona do DNS do Azure para a qual deseja que o operador de Roteamento de Aplicativos gerencie os registros, poderá ignorar esta etapa e atribuir o valor de ZONE_NAME ao nome da zona existente.

export ZONE_NAME=<dns-zone-name>
az network dns zone create --resource-group $RESOURCE_GROUP --name $ZONE_NAME
export ZONE_ID=$(az network dns zone show --resource-group $RESOURCE_GROUP --name $ZONE_NAME --query id -o tsv)

Criar o Azure Key Vault e o certificado

Crie o Azure Key Vault que armazena o certificado TLS. Configure o cofre para usar o RBAC do Azure para autorização, que é o modelo de permissão recomendado:

export KV_NAME=<key-vault-name>
az keyvault create \
  --name $KV_NAME \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION \
  --enable-rbac-authorization true

Note

Para criar o certificado na próxima etapa, sua própria identidade do Azure precisa da função Key Vault Certificates Officer (ou Key Vault Administrator) no cofre. Atribua esta função ao cofre antes de continuar.

Crie um certificado curinga autoassinado no Azure Key Vault. Para implantações de produção, importe um certificado assinado pela AC (autoridade de certificação) usando az keyvault certificate import em vez disso.

cat > cert-policy.json <<EOF
{
  "issuerParameters": { "name": "Self" },
  "keyProperties": { "exportable": true, "keyType": "RSA", "keySize": 2048, "reuseKey": false },
  "secretProperties": { "contentType": "application/x-pkcs12" },
  "x509CertificateProperties": {
    "subject": "CN=*.${ZONE_NAME}",
    "subjectAlternativeNames": { "dnsNames": ["*.${ZONE_NAME}", "${ZONE_NAME}"] },
    "validityInMonths": 12,
    "keyUsage": ["digitalSignature", "keyEncipherment"]
  }
}
EOF

az keyvault certificate create \
  --vault-name $KV_NAME \
  --name approuting-demo-cert \
  --policy @cert-policy.json

Capture o URI do certificado sem versão. O operador de Roteamento de Aplicativos usa esse URI para configurar o SecretProviderClass. Um URI sem versão garante que o operador obtenha novas versões do certificado no Azure Key Vault quando houver a rotação do certificado.

export CERT_URI=$(az keyvault certificate show \
  --vault-name $KV_NAME \
  --name approuting-demo-cert \
  --query id -o tsv | sed 's|/[^/]*$||')
echo "Cert URI: $CERT_URI"

Criar a identidade gerenciada atribuída pelo usuário e conceder funções do Azure RBAC

Crie uma identidade gerenciada atribuída pelo usuário para que a implantação de external-dns do operador de Roteamento de Aplicativos e a sincronização de TLS do ouvinte do gateway usem essa identidade para se autenticar no DNS do Azure e no Azure Key Vault.

export UAMI_NAME=<managed-identity-name>
az identity create --resource-group $RESOURCE_GROUP --name $UAMI_NAME --location $LOCATION
export UAMI_CLIENT_ID=$(az identity show --resource-group $RESOURCE_GROUP --name $UAMI_NAME --query clientId -o tsv)
export UAMI_PRINCIPAL_ID=$(az identity show --resource-group $RESOURCE_GROUP --name $UAMI_NAME --query principalId -o tsv)

Atribua à identidade gerenciada a função DNS Zone Contributor na zona do DNS do Azure de destino e a função Key Vault Secrets User no Azure Key Vault de destino:

az role assignment create \
  --assignee-object-id $UAMI_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "DNS Zone Contributor" \
  --scope $ZONE_ID

az role assignment create \
  --assignee-object-id $UAMI_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets User" \
  --scope $(az keyvault show --name $KV_NAME --query id -o tsv)

Criar os namespaces, ServiceAccounts e credenciais de identidade federadas

As integrações de TLS e DNS do operador de Roteamento de Aplicativos se autenticam no Azure por meio de uma ServiceAccount do Kubernetes vinculada à identidade gerenciada atribuída pelo usuário por meio de uma FIC (credencial de identidade federada). Cada par (namespace, ServiceAccount) que precisa se autenticar requer uma FIC.

Capture a URL do emissor OIDC do cluster:

export OIDC_ISSUER=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query oidcIssuerProfile.issuerUrl -o tsv)

Para cada namespace em que você planeja implantar um Gateway recurso que usa a integração TLS ou um ExternalDNS recurso, crie o namespace, uma credencial de identidade federada para o ServiceAccount e o próprio ServiceAccount. O exemplo a seguir cria dois namespaces, app-a e app-b, cada um deles com uma ServiceAccount chamada approuting-demo-sa:

export SA_NAME=approuting-demo-sa
for ns in app-a app-b; do
  kubectl create namespace $ns

  az identity federated-credential create \
    --identity-name $UAMI_NAME \
    --resource-group $RESOURCE_GROUP \
    --name approuting-demo-fic-$ns \
    --issuer $OIDC_ISSUER \
    --subject "system:serviceaccount:$ns:$SA_NAME" \
    --audiences "api://AzureADTokenExchange"

  kubectl apply -n $ns -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: $SA_NAME
  annotations:
    azure.workload.identity/client-id: $UAMI_CLIENT_ID
  labels:
    azure.workload.identity/use: "true"
EOF
done

A anotação azure.workload.identity/client-id associa a ServiceAccount à identidade gerenciada, e o rótulo azure.workload.identity/use: "true" instrui o webhook da Identidade de Carga de Trabalho do Microsoft Entra a projetar um token federado em pods que consomem a ServiceAccount. Ambos são necessários para que as integrações TLS e DNS do operador de Roteamento de Aplicativos sejam autenticadas para Azure com êxito.

Configurar a terminação do TLS em um Gateway

Implante uma carga de trabalho de exemplo httpbin em cada namespace:

for ns in app-a app-b; do
  kubectl apply -n $ns -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/httpbin/httpbin.yaml
done

Crie um recurso Gateway em cada namespace com um listener HTTPS que faça referência ao certificado do Azure Key Vault nas opções de TLS. Cada Gateway um usa seu próprio sub-host da zona DNS do Azure (por exemplo, a.<zone> e b.<zone>):

Note

Os exemplos neste artigo usam gatewayClassName: approuting-istio, a GatewayClass fornecida pela implementação da API Gateway de Roteamento de Aplicativos. Se você habilitou o complemento Istio service mesh, defina gatewayClassName: istio em seus recursos Gateway. As integrações DNS e TLS se comportam de forma idêntica para ambas as GatewayClasses gerenciadas.

for pair in "app-a:a" "app-b:b"; do
  ns=${pair%%:*}
  sub=${pair##*:}
  fqdn=${sub}.${ZONE_NAME}
  kubectl apply -n $ns -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: ${sub}-gateway
  labels:
    app: approuting-demo
    zone: ${sub}
spec:
  gatewayClassName: approuting-istio
  listeners:
  - name: https
    hostname: $fqdn
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      options:
        kubernetes.azure.com/tls-cert-keyvault-uri: $CERT_URI
        kubernetes.azure.com/tls-cert-service-account: $SA_NAME
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: ${sub}-route
spec:
  parentRefs:
  - name: ${sub}-gateway
  hostnames: ["$fqdn"]
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /get
    backendRefs:
    - name: httpbin
      port: 8000
EOF
done

Aguarde até que cada Gateway atinja a condição Programmed:

kubectl wait -n app-a --for=condition=programmed gateway a-gateway --timeout=300s
kubectl wait -n app-b --for=condition=programmed gateway b-gateway --timeout=300s

Verifique se o operador de Roteamento de Aplicativos criou um SecretProviderClass e se o provedor do Azure Key Vault para o Driver CSI do repositório de segredos sincronizou o certificado em um segredo kubernetes.io/tls em cada namespace:

kubectl get secretproviderclass,secret -n app-a
kubectl get secretproviderclass,secret -n app-b

Saída de exemplo para um namespace:

NAME                                                                        AGE
secretproviderclass.secrets-store.csi.x-k8s.io/kv-gw-cert-a-gateway-https   2m

NAME                                TYPE                DATA   AGE
secret/kv-gw-cert-a-gateway-https   kubernetes.io/tls   2      2m

Configurar registros DNS do Azure usando ClusterExternalDNS

Implante uma instância external-dns com escopo de cluster que publica registros A para recursos Gateway em qualquer namespace aplicando um recurso personalizado ClusterExternalDNS.

kubectl apply -f - <<EOF
apiVersion: approuting.kubernetes.azure.com/v1alpha1
kind: ClusterExternalDNS
metadata:
  name: demo-cluster-dns
spec:
  resourceName: demo-cluster-dns
  resourceNamespace: app-a
  dnsZoneResourceIDs:
  - $ZONE_ID
  resourceTypes:
  - gateway
  identity:
    type: workloadIdentity
    serviceAccount: $SA_NAME
EOF

O resourceNamespace campo especifica o namespace em que o operador de Roteamento de Aplicativos implanta a instância gerenciada external-dns . A ServiceAccount referenciada por identity.serviceAccount deve existir nesse namespace.

Após cerca de um minuto, dois registros A aparecem na zona DNS do Azure - um para cadaGateway:

az network dns record-set a list --resource-group $RESOURCE_GROUP --zone-name $ZONE_NAME -o table
Name    ResourceGroup              Ttl    Type    AutoRegistered    Metadata
------  -------------------------  -----  ------  ----------------  --------
a       <your-rg>                  300    A       False
b       <your-rg>                  300    A       False

Configurar registros de DNS do Azure usando um ExternalDNS com escopo de namespace

Para publicar registros apenas para um subconjunto de recursos Gateway, use o recurso personalizado ExternalDNS com escopo de namespace. Ao contrário de ClusterExternalDNS, a variante com escopo no namespace observa apenas os recursos Gateway, HTTPRoute e GRPCRoute no mesmo namespace que o recurso personalizado. Assim como com ClusterExternalDNS, você pode opcionalmente restringir ainda mais o escopo usando os seletores filters.gatewayLabels e filters.routeAndIngressLabels.

Primeiro, exclua o ClusterExternalDNS da etapa anterior:

kubectl delete clusterexternaldns demo-cluster-dns

Implante um novo Gateway em app-a com o rótulo zone: c e um HTTPRoute correspondente:

kubectl apply -n app-a -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: c-gateway
  labels:
    app: approuting-demo
    zone: c
spec:
  gatewayClassName: approuting-istio
  listeners:
  - name: https
    hostname: c.${ZONE_NAME}
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      options:
        kubernetes.azure.com/tls-cert-keyvault-uri: $CERT_URI
        kubernetes.azure.com/tls-cert-service-account: $SA_NAME
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: c-route
spec:
  parentRefs:
  - name: c-gateway
  hostnames: ["c.${ZONE_NAME}"]
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /get
    backendRefs:
    - name: httpbin
      port: 8000
EOF

kubectl wait -n app-a --for=condition=programmed gateway c-gateway --timeout=300s

Aplique um recurso ExternalDNS em app-a com escopo de namespace com um filtro de rótulo para zone=c:

kubectl apply -n app-a -f - <<EOF
apiVersion: approuting.kubernetes.azure.com/v1alpha1
kind: ExternalDNS
metadata:
  name: demo-ns-dns
spec:
  resourceName: demo-ns-dns
  dnsZoneResourceIDs:
  - $ZONE_ID
  resourceTypes:
  - gateway
  identity:
    type: workloadIdentity
    serviceAccount: $SA_NAME
  filters:
    gatewayLabels: "zone=c"
EOF

Duas regras de escopo se aplicam:

  • O escopo do espaço de nomes de ExternalDNS exclui b-gateway porque ele reside no espaço de nomes app-b.
  • O filtro de rótulo zone=c exclui a-gateway porque está em app-a, mas é rotulado como zone=a.

O operador de Roteamento de Aplicativos publica um novo registro A para c.${ZONE_NAME}:

az network dns record-set a list --resource-group $RESOURCE_GROUP --zone-name $ZONE_NAME -o table

Verificar o tráfego HTTPS encerrado pelo TLS

Resolva o Gatewaynome do host por meio do nameserver autoritativo da zona DNS do Azure e envie uma solicitação HTTPS:

NS=$(az network dns zone show --resource-group $RESOURCE_GROUP --name $ZONE_NAME --query 'nameServers[0]' -o tsv | sed 's/\.$//')
GATEWAY_IP=$(dig +short @${NS} a.${ZONE_NAME} | tail -1)
curl -k -I --resolve "a.${ZONE_NAME}:443:${GATEWAY_IP}" "https://a.${ZONE_NAME}/get"

Você deve ver uma HTTP/2 200 resposta. O certificado TLS que o gateway apresenta é aquele sincronizado de Azure Key Vault. Se você importou um certificado assinado por AC, substitua -k--cacert <path-to-ca-chain> para validar a cadeia de certificados.

Note

O exemplo usa curl --resolve para ignorar a resolução DNS local e direcionar a solicitação para o IP externo do gateway. Esse método é útil para teste antes de delegar a zona DNS a um registrador. Para uso em produção, configure o registrador de domínios para delegar a zona aos nameservers de DNS do Azure retornados por az network dns zone show --query 'nameServers'.

Limitações

  • As integrações de DNS e TLS se aplicam somente aos Gateway recursos que usam um GatewayClass gerenciado: approuting-istio (a implementação da API do Gateway de Roteamento de Aplicativos) ou istio (o complemento da malha de serviço istio). Gateway recursos que usam qualquer outra GatewayClass não são compatíveis.
  • Um recurso personalizado ClusterExternalDNS ou ExternalDNS pode fazer referência a até sete zonas do DNS do Azure por meio de dnsZoneResourceIDs. Todas as zonas referenciadas em um único recurso personalizado devem estar na mesma assinatura do Azure e no mesmo grupo de recursos. Eles também devem ser todos do mesmo tipo (público ou privado).
  • A instância gerenciada external-dns não exclui automaticamente os registros DNS quando você exclui o ClusterExternalDNS recurso ou ExternalDNS personalizado. Para remover registros órfãos, exclua-os diretamente da zona DNS do Azure depois de excluir o recurso personalizado.
  • Atualmente, não há suporte para a reconciliação de registros DNS dos TLSRoute recursos. A instância gerenciada external-dns só origina registros de Gateway, HTTPRoutee GRPCRoute recursos.

Próximas Etapas