Configurar o DNS do Azure e o TLS com a implementação da API do Application Routing Gateway

Com a API do Gateway de Roteamento de Aplicações, os utilizadores podem facilmente expor aplicações HTTPS no AKS com os seus próprios certificados Azure Key Vault, incluindo a publicação automática de Nomes de Domínio. O operador de Encaminhamento de Aplicações integra-se com o DNS do Azure e o Azure Key Vault e coordena um SecretProviderClass, um Segredo do Kubernetes para certificados TLS, o campo certificateRefs do escutador e o Deployment external-dns separado, para que não tenha de gerir manualmente estes recursos.

Este artigo mostra-lhe como:

  • Provisionar os recursos Azure pré-requisitos (zona DNS do Azure, Azure Key Vault, identidade gerida atribuída pelo utilizador, atribuição de funções e credenciais de identidade federada).
  • Configure um serviço de escuta Gateway para terminar TLS com um certificado armazenado no Azure Key Vault através das opções TLS do serviço de escuta kubernetes.azure.com/tls-cert-keyvault-uri e kubernetes.azure.com/tls-cert-service-account.
  • Utilize os recursos personalizados ClusterExternalDNS e ExternalDNS para publicar registos DNS no DNS do Azure com base nos nomes de anfitrião dos seus recursos Gateway, HTTPRoute e GRPCRoute.

Como funciona a integração

O operador de Encaminhamento de Aplicações expõe duas integrações para automatizar os recursos que de outra forma criaria manualmente para colocar um Gateway recurso online com um domínio personalizado e terminação TLS.

Integração com TLS

Quando um recurso Gateway usa uma GatewayClass gerida — seja approuting-istio (da implementação da API Gateway do Encaminhamento de Aplicações) ou istio (do suplemento de malha de serviço Istio) — e um listener inclui as duas opções de TLS seguintes, o operador de Encaminhamento de Aplicações efetua a reconciliação dos recursos necessários para efetuar a terminação de TLS com um certificado armazenado no Azure Key Vault:

Chave de opção TLS Valor
kubernetes.azure.com/tls-cert-keyvault-uri O URI do certificado do Azure Key Vault para obter o certificado TLS. Use um URI não versionado (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 uma ServiceAccount do Kubernetes no mesmo espaço de nomes que o Gateway. A ServiceAccount tem de estar vinculada a uma identidade gerida atribuída pelo utilizador através do Microsoft Entra Workload Identity, e essa identidade gerida tem de ter a função Key Vault Secrets User no Azure Key Vault de destino.

Para cada escutador que inclui ambas as opções de TLS, o operador:

  1. Provisiona um(a) SecretProviderClass denominado(a) kv-gw-cert-<gateway-name>-<listener-name> no espaço de nomes de Gateway, configurado(a) para obter o certificado do Azure Key Vault através da autenticação de identidade de carga de trabalho.
  2. Faz com que o fornecedor Azure Key Vault para o driver CSI da Secrets Store sincronize o certificado como um kubernetes.io/tls Kubernetes Secret com o mesmo nome no Gatewaynamespace de .
  3. Atualiza o campo do tls.certificateRefs ouvinte para referenciar o Kubernetes Secret sincronizado.

Integração DNS

O operador de Roteamento de Aplicações gere uma external-dns instância para si através de dois recursos personalizados:

Recurso personalizado Scope
ClusterExternalDNS (clusterexternaldnses.approuting.kubernetes.azure.com) Âmbito do cluster. Observa os recursos Gateway, HTTPRoute e GRPCRoute em todos os espaços de nomes do cluster.
ExternalDNS (externaldnses.approuting.kubernetes.azure.com) Ao nível do espaço de nomes. Monitoriza apenas os recursos Gateway, HTTPRoute e GRPCRoute no mesmo espaço de nomes que o recurso personalizado.

Ambos os recursos personalizados aceitam seletores opcionais filters para restringir ainda mais quais os recursos que a instância gerida external-dns observa dentro do seu âmbito:

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

Para cada recurso personalizado, o operador de Roteamento de Aplicações:

  1. Implementa uma instância gerida external-dns, configurada para obter registos a partir dos recursos HTTPRoute e GRPCRoute, e tem como destino as zonas de DNS do Azure especificadas.
  2. Autentica-se no DNS do Azure utilizando o Microsoft Entra Workload Identity, através da conta de serviço referenciada no campo identity do recurso personalizado.
  3. Publica registos A em cada uma das zonas de DNS do Azure indicadas para cada nome de anfitrião HTTPRoute ou GRPCRoute associado a um recurso gerido Gateway no âmbito.

Prerequisites

  • Um cluster AKS com ambas as seguintes funcionalidades ativadas:

    • O complemento de Encaminhamento de Aplicações (--enable-app-routing). Este complemento implementa o operador de Roteamento de Aplicações no cluster, que é o componente que concilia as integrações DNS e TLS documentadas neste artigo. Num cluster existente, também pode ativar esta funcionalidade usando az aks approuting enable.
    • Uma implementação gerida de API Gateway com a qual o operador se possa integrar. Escolha uma das seguintes opções (as duas opções são mutuamente exclusivas e não podem ser ativadas ao mesmo tempo):

    Para utilizar estas integrações, ative tanto o complemento de Ruteamento de Aplicações (usando az aks approuting enable) como uma das implementações geridas da API de Gateway mencionadas anteriormente.

  • Instalação da API de Gateway Gerido ativada no cluster.

  • A funcionalidade Microsoft Entra Workload Identity ativada no cluster, juntamente com o emissor OIDC. Pode ativar ambas as funcionalidades utilizando as opções --enable-oidc-issuer e --enable-workload-identity em az aks create ou az aks update.

  • O suplemento fornecedor do Azure Key Vault para o Secrets Store CSI Driver ativado no cluster. Pode ativá-lo utilizando o comando az aks enable-addons com --addons azure-keyvault-secrets-provider, ou ao passar --enable-kv para az aks approuting enable ou az aks approuting update.

  • Versão da CLI 2.86.0 do Azure ou superior. Corre az --version para encontrar a tua azure-cli versão e corre az upgrade para atualizar.

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

Note

As opções --attach-kv e --attach-zones em az aks approuting update (e os subcomandos az aks approuting zone) foram concebidas para a experiência legada baseada em NGINX, em que à identidade gerida atribuída pelo utilizador do próprio suplemento Application Routing é concedido acesso do Azure RBAC a um único Azure Key Vault e a uma única zona DNS. Eles não são usados pela integração da API Gateway documentada neste artigo. A nova experiência é impulsionada pelo Microsoft Entra Workload Identity em vez da identidade gerida do add-on, por isso precisa de criar a sua própria identidade gerida atribuída pelo utilizador, conceder-lhe os papéis DNS do Azure e Azure Key Vault apropriados, e criar credenciais de identidade federadas que a vinculem às Kubernetes ServiceAccounts que refere no seu Gateway Opções TLS para ouvintes e os seus ExternalDNS/ClusterExternalDNS recursos personalizados.

Defina as seguintes variáveis de ambiente. O guia passo a passo reutiliza-os em cada comando subsequente:

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 do Azure

Criar a zona DNS do Azure

Se já tens uma zona DNS do Azure onde queres que o operador de Application Routing gere registos, podes saltar este passo e atribuir o valor de ZONE_NAME ao nome da tua 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)

Crie o Azure Key Vault e o certificado

Crie o Azure Key Vault que armazena o certificado TLS. Configure o cofre para usar o Azure RBAC para autorização, que é o modelo de permissões 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 no passo seguinte, a sua própria identidade do Azure precisa da função Key Vault Certificates Officer (ou Key Vault Administrator) no cofre. Atribua esta função no cofre antes de continuar.

Crie um certificado universal autoassinado no Azure Key Vault. Para implementações em produção, importa um certificado assinado pela autoridade certificadora (CA) 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

Capturar o URI do certificado não versionado. O operador de encaminhamento de aplicações utiliza este URI para configurar o SecretProviderClass. Um URI não versionado garante que o operador deteta novas versões do certificado no Azure Key Vault à medida que o certificado é renovado.

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"

Crie a identidade gerida atribuída pelo utilizador e conceda funções Azure RBAC

Crie uma identidade gerida atribuída pelo utilizador que a implementação do external-dns operador de Ruteamento de Aplicações e a sincronização TLS do ouvinte do gateway usem para 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 gerida a DNS Zone Contributor função na zona de DNS do Azure de destino e a Key Vault Secrets User função 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)

Crie os namespaces, ServiceAccounts e credenciais de identidade federada

As integrações TLS e DNS do operador de Encaminhamento de Aplicações autenticam-se ambas no Azure através de uma ServiceAccount do Kubernetes associada à identidade gerida atribuída pelo utilizador através da utilização de uma credencial de identidade federada (FIC). Cada par (namespace, ServiceAccount) que precisa de se autenticar requer um FIC.

Obtenha o 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 onde planeia implementar um Gateway recurso que use a integração TLS ou um ExternalDNS recurso, crie o namespace, uma credencial federada de identidade para a ServiceAccount e a própria ServiceAccount. O exemplo seguinte cria dois namespaces, app-a e app-b, cada um com uma ServiceAccount nomeada 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 gerida, e o rótulo azure.workload.identity/use: "true" instrui o webhook do Microsoft Entra Workload Identity a injetar um token federado nos pods que utilizam a ServiceAccount. Ambos são necessários para que as integrações TLS e DNS do operador de Encaminhamento de Aplicações autentiquem com sucesso no Azure.

Configurar a terminação TLS num Gateway

Implemente 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 escutador HTTPS que faça referência ao certificado do Azure Key Vault nas opções de TLS. Cada um Gateway usa o seu próprio sub-hospedeiro da zona DNS do Azure (por exemplo, a.<zone> e b.<zone>):

Note

Os exemplos neste artigo utilizam gatewayClassName: approuting-istio, a GatewayClass fornecida pela implementação da API do Gateway de Roteamento de Aplicações. Se, em vez disso, ativaste a extensão de malha de serviço Istio, define gatewayClassName: istio nos teus recursos Gateway. As integrações DNS e TLS comportam-se de forma idêntica para ambas as GatewayClass geridas.

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

Espere que cada um 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 Encaminhamento de Aplicações criou um SecretProviderClass e que o fornecedor do Azure Key Vault para o Driver CSI Secrets Store sincronizou o certificado num kubernetes.io/tls Secret em cada namespace:

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

Exemplo de saída 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

Configure registos DNS do Azure usando ClusterExternalDNS

Implemente uma instância de âmbito de cluster de external-dns que publique registos A para recursos Gateway em qualquer espaço de nomes, 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 onde o operador de Roteamento de Aplicações implementa a instância gerida external-dns . A ServiceAccount referenciada por identity.serviceAccount deve existir nesse espaço de nomes.

Após cerca de um minuto, dois registos A aparecem na zona DNS do Azure - um para cada Gateway:

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

Configure os registos do DNS do Azure utilizando um ExternalDNS com âmbito delimitado ao namespace

Para publicar registos apenas para um subconjunto de recursos Gateway, utilize o recurso personalizado ExternalDNS com âmbito de espaço de nomes. Ao contrário de ClusterExternalDNS, a variante com escopo de espaço de nomes apenas observa Gateway, HTTPRoute, e GRPCRoute recursos no mesmo espaço de nomes que o recurso personalizado. Tal como com ClusterExternalDNS, pode, opcionalmente, restringir ainda mais o âmbito utilizando os seletores filters.gatewayLabels e filters.routeAndIngressLabels.

Primeiro, apague os ClusterExternalDNS do passo anterior:

kubectl delete clusterexternaldns demo-cluster-dns

Implemente um novo Gateway em app-a com o rótulo zone: c e o 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

Aplicar um recurso ExternalDNS com âmbito de espaço de nomes em app-a com um filtro de rótulos 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

Aplicam-se duas regras de âmbito de abrangência:

  • O âmbito do namespace de ExternalDNS exclui b-gateway porque está no app-b namespace.
  • O filtro de etiquetas zone=c exclui a-gateway porque está em app-a, mas tem o rótulo zone=a.

O operador de encaminhamento de aplicações publica um novo registo A para c.${ZONE_NAME}:

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

Verificar tráfego HTTPS com terminação TLS

Resolva o nome do anfitrião de Gateway através do servidor de nomes autoritativo da zona DNS do Azure e envie um pedido 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ê verá uma HTTP/2 200 resposta. O certificado TLS que o gateway apresenta é o sincronizado do Azure Key Vault. Se importou um certificado assinado pela CA, substitua -k por --cacert <path-to-ca-chain> para validar a cadeia de certificados.

Note

O exemplo serve curl --resolve para contornar a resolução DNS local e direcionar o pedido para o IP externo do gateway. Este método é útil para testar antes de delegar a zona DNS a um registrador. Para uso em produção, configure o seu registo de domínio para delegar a zona aos servidores de nomes do DNS do Azure devolvidos por az network dns zone show --query 'nameServers'.

Limitações

  • As integrações de DNS e TLS aplicam-se apenas aos recursos Gateway que utilizam uma GatewayClass gerida: approuting-istio (a implementação da API Application Routing Gateway) ou istio (o suplemento do service mesh Istio). Gateway recursos que utilizam qualquer outra GatewayClass não são suportados.
  • Um recurso personalizado ClusterExternalDNS ou ExternalDNS pode referenciar até sete zonas de DNS do Azure através de dnsZoneResourceIDs. Todas as zonas referenciadas num único recurso personalizado devem estar na mesma subscrição e grupo de recursos do Azure. Devem também ser todos do mesmo tipo (público ou privado).
  • A instância gerida external-dns não apaga automaticamente os registos DNS quando apagas o ClusterExternalDNS ou ExternalDNS o recurso personalizado. Para remover registos órfãos, apague-os diretamente da zona DNS do Azure depois de eliminar o recurso personalizado.
  • A reconciliação de registos DNS a partir de TLSRoute recursos não é atualmente suportada. A instância gerida external-dns apenas obtém registos de Gateway, HTTPRoute, e GRPCRoute recursos.

Passos seguintes