Habilitando o gerenciamento de DNS e certificados por autoatendimento no Kubernetes

NGINX | 01 de novembro de 2022

O objetivo final do desenvolvimento de aplicações é, naturalmente, disponibilizá-las na Internet. Para quem desenvolve, o Kubernetes simplifica esse processo até certo ponto, fornecendo o controlador Ingress como mecanismo para encaminhar solicitações à aplicação. No entanto, nem tudo funciona de forma tão autônoma quanto você provavelmente gostaria: ainda é necessário ter um registro no Sistema de Nomes de Domínio (DNS) para associar o nome de domínio da aplicação ao endereço IP do controlador Ingress, além de um certificado TLS para proteger as conexões via HTTPS. Na maioria das organizações, você não gerencia o DNS nem o TLS diretamente e, por isso, precisa coordenar essas tarefas com a equipe operacional (ou as equipes!) responsável por eles.

As coisas não são necessariamente mais fáceis para os operadores. Na maioria das organizações, a necessidade de atualizar registros DNS é tão rara que os procedimentos — tanto as regras de negócios quanto as etapas técnicas propriamente ditas — tendem a ser escassos ou inexistentes. Isso significa que, quando você precisa adicionar um registro DNS, primeiro precisa encontrar a documentação, perguntar a um colega ou, na pior das hipóteses, descobrir por conta própria como fazer isso. Você também precisa garantir a conformidade com todas as regras de segurança corporativa e verificar se o Ingress está devidamente identificado para os firewalls.

Felizmente, há uma maneira de facilitar a vida tanto dos desenvolvedores quanto dos operadores. Neste post, mostramos como os operadores podem configurar uma implantação do Kubernetes para permitir que os desenvolvedores atualizem registros DNS e gerem certificados TLS por conta própria em um ambiente Kubernetes. Ao preparar a infraestrutura com antecedência, você pode garantir que todos os requisitos técnicos e de negócios necessários sejam atendidos.

Visão geral e pré-requisitos

Com a solução implementada, tudo o que um desenvolvedor precisa fazer para expor uma aplicação à Internet é criar um controlador de Ingress seguindo um modelo fornecido que inclua um nome de domínio totalmente qualificado (FQDN) em um domínio gerenciado pela instalação do Kubernetes. O Kubernetes usa o modelo para alocar um endereço IP para o controlador de Ingress, criar o registro DNS A para associar o FQDN ao endereço IP, gerar certificados TLS para o FQDN e adicioná-los ao controlador de Ingress. A limpeza é igualmente simples: quando o Ingress é removido, os registros DNS são excluídos.

A solução utiliza as seguintes tecnologias (fornecemos abaixo as instruções de instalação e configuração):

Antes de configurar a solução, você precisa de:

  • Uma instalação do Kubernetes na nuvem com um objeto de saída (LoadBalancer). A solução usa a Linode, mas também funciona com outros provedores de nuvem.
  • Um nome de domínio hospedado na Cloudflare, que escolhemos por ser um dos provedores de DNS compatíveis com o cert-manager e por oferecer suporte ao ExternalDNS (em versão beta no momento da redação deste artigo). Recomendamos enfaticamente que o domínio não seja usado em produção nem para qualquer outra finalidade crítica.
  • Acesso à API da Cloudflare, incluído no plano gratuito.
  • Helm para instalar e implantar o Kubernetes.
  • kubectl como interface de linha de comando do Kubernetes.
  • Opcionalmente, K9s, uma interface de usuário em modo texto (TUI) bem desenvolvida que oferece uma maneira mais estruturada de interagir com o Kubernetes.

Também pressupomos que você tenha conhecimentos básicos sobre o Kubernetes (como aplicar um manifesto, usar um chart do Helm e executar comandos kubectl para visualizar a saída e solucionar problemas). Conhecer os conceitos básicos do Let’s Encrypt é útil, mas não obrigatório; para ter uma visão geral, consulte nosso blog. Você também não precisa saber como o cert-manager funciona, mas, se quiser entender como ele (e os certificados em geral) funciona com o NGINX Ingress Controller, confira meu post recente, Automatização do gerenciamento de certificados em um ambiente Kubernetes.

Testamos a solução tanto no macOS quanto no Linux. Não a testamos no Subsistema do Windows para Linux versão 2 (WSL2), mas não prevemos nenhum problema.

Observação: A solução destina-se a servir como uma amostra de prova de conceito, e não para uso em produção. Em particular, ela não incorpora todas as práticas recomendadas de operação e segurança. Para obter informações sobre esses tópicos, consulte a documentação do cert-manager e do ExternalDNS.

Implantação da solução

Siga as etapas destas seções para implantar a solução:

Baixar software

  1. Baixe seu token de API da Cloudflare.
  2. Clone o repositório do NGINX Ingress Controller:
$ git clone https://github.com/nginxinc/kubernetes-ingress.gitCloning into 'kubernetes-ingress'...
remote: Enumerating objects: 45176, done.
remote: Counting objects: 100% (373/373), done.
remote: Compressing objects: 100% (274/274), done.
remote: Total 45176 (delta 173), reused 219 (delta 79), pack-reused 44803
Receiving objects: 100% (45176/45176), 60.45 MiB | 26.81 MiB/s, done.
Resolving deltas: 100% (26592/26592), done.
  1. Verifique se você consegue se conectar ao cluster do Kubernetes.
$ kubectl cluster-infoKubernetes control plane is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443
KubeDNS is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
 
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Implantar o NGINX Ingress Controller

  1. Usando o Helm, implante o NGINX Ingress Controller. Observe que estamos adicionando três opções de configuração não padrão:
$ helm install nginx-kic nginx-stable/nginx-ingress --namespace nginx-ingress --set controller.enableCustomResources=true --create-namespace --set controller.enableCertManager=true --set controller.enableExternalDNS=trueNAME: nginx-kic
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.
  • controller.enableCustomResources – Instrui o Helm a instalar as definições de recursos personalizados (CRDs) usadas para criar os recursos personalizados VirtualServer e VirtualServerRoute do NGINX.
  • controller.enableCertManager – Configura o NGINX Ingress Controller para se comunicar com os componentes do cert-manager.
  • controller.enableExternalDNS – Configura o Ingress Controller para se comunicar com os componentes do ExternalDNS.
  1. Verifique se o NGINX Ingress Controller está em execução e anote o valor no campo EXTERNAL-IP – esse é o endereço IP do NGINX Ingress Controller (neste exemplo, www.xxx.yyy.zzz). A saída está distribuída em duas linhas para facilitar a leitura.
$ kubectl get services --namespace nginx-ingressNAME                      TYPE           CLUSTER-IP      ...
nginx-kic-nginx-ingress   LoadBalancer   10.128.152.88   ... 

   ... EXTERNAL-IP       PORT(S)                      AGE
   ... www.xxx.yyy.zzz   80:32457/TCP,443:31971/TCP   3h8m

Implantar o cert-manager

Na solução, o cert-manager usa o tipo de desafio DNS-01 ao obter um certificado TLS, o que exige que o token da API da Cloudflare seja fornecido durante a criação do recurso ClusterIssuer. Na solução, o token da API é fornecido como um Secret do Kubernetes.

  1. Usando o Helm, implante o cert-manager:
$ helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.9.1 --set installCRDs=trueNAME: cert-manager
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.9.1 has been deployed successfully!
  1. Implante o token da API da Cloudflare como um Secret do Kubernetes, substituindo-o por <your-API-token>:
$ kubectl apply -f - <<EOFapiVersion: v1 kind: Secret metadata: name: Cloudflare-api-token-secret namespace: cert-manager type: Opaque stringData: api-token: "<your-API-token>" EOF
secret/Cloudflare-api-token-secret created
  1. Crie um objeto ClusterIssuer, especificando Cloudflare-api-token-secret (definido na etapa anterior) como o local de onde o token será obtido. Se desejar, você pode substituir example-issuer no campo metadata.name (e example-issuer-account-key no campo spec.acme.privateKeySecretRef.name) por outro nome.
$ kubectl apply -f - <<EOFapiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: example-issuer namespace: cert-manager spec: acme: email: example@example.com server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: example-issuer-account-key solvers: - dns01: Cloudflare: apiTokenSecretRef: name: Cloudflare-api-token-secret key: api-token EOF
clusterissuer.cert-manager.io/example-issuer created
  1. Verifique se o ClusterIssuer está implantado e pronto (o valor no campo READY é True).
$ kubectl get clusterissuerNAME             READY   AGE
example-issuer   True    3h9m

Implantar o ExternalDNS

Assim como o cert-manager, o projeto ExternalDNS requer um token de API da Cloudflare para gerenciar o DNS. O mesmo token pode ser usado nos dois projetos, mas isso não é obrigatório.

  1. Crie as CRDs do ExternalDNS para o NGINX Ingress Controller a fim de habilitar a integração entre os projetos.
$ kubectl create -f ./kubernetes-ingress/deployments/common/crds/externaldns.nginx.org_dnsendpoints.yamlcustomresourcedefinition.apiextensions.k8s.io/dnsendpoints.externaldns.nginx.org created
  1. Crie o serviço de DNS externo (external-dns). Como o manifesto é bastante extenso, vamos dividi-lo em duas partes. A primeira parte configura contas, funções e permissões:
$ kubectl apply -f - <<EOFapiVersion: v1 kind: ServiceAccount metadata: name: external-dns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: external-dns rules: - apiGroups: [""] resources: ["services","endpoints","pods"] verbs: ["get","watch","list"] - apiGroups: ["extensions","networking.k8s.io"] resources: ["ingresses"] verbs: ["get","watch","list"] - apiGroups: ["externaldns.nginx.org"] resources: ["dnsendpoints"] verbs: ["get","watch","list"] - apiGroups: ["externaldns.nginx.org"] resources: ["dnsendpoints/status"] verbs: ["update"] - apiGroups: [""] resources: ["nodes"] verbs: ["list","watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: external-dns-viewer roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: external-dns subjects: - kind: ServiceAccount name: external-dns namespace: default EOF
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
  1. A segunda parte do manifesto cria a implantação do ExternalDNS:
$  kubectl apply -f - <<EOF --- apiVersion: apps/v1 kind: Deployment metadata: name: external-dns spec: strategy: type: Recreate selector: matchLabels: app: external-dns template: metadata: labels: app: external-dns spec: serviceAccountName: external-dns containers: - name: external-dns image: k8s.gcr.io/external-dns/external-dns:v0.12.0 args: - --source=service - --source=ingress - --source=crd - --crd-source-apiversion=externaldns.nginx.org/v1 - --crd-source-kind=DNSEndpoint - --domain-filter=example.com - --provider=Cloudflare env: - name: CF_API_TOKEN value: "<your-API-token>" - name: FREE_TIER value: "true" EOF
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
deployment.apps/external-dns created
  • Cria um objeto ServiceAccount chamado external-dns para gerenciar todas as operações de gravação e atualização relacionadas ao DNS.
  • Cria um objeto ClusterRole (também chamado de external-dns) que define as permissões necessárias.
  • Vincula o ClusterRole à ServiceAccount.
  • Cria um filtro de domínio, que limita o alcance de possíveis danos causados pelo ExternalDNS ao gerenciar domínios. Por exemplo, você pode especificar os nomes de domínio dos ambientes de staging para evitar alterações nos ambientes de produção. Neste exemplo, definimos domain-filter como example.com.
  • Define a variável de ambiente CF_API_TOKEN como seu token de API da Cloudflare. No lugar de <your-API-token>, insira o token real ou um Secret que contenha o token. Neste último caso, você também precisa projetar o Secret no contêiner usando uma variável de ambiente.
  • Define a variável de ambiente FREE_TIER como "true" (adequado, a menos que você tenha uma assinatura paga da Cloudflare).

Implantar o aplicativo de exemplo

Use o aplicativo de exemplo padrão do NGINX Ingress Controller chamado Cafe para fins de teste.

  1. Implante a aplicação Cafe.
$ kubectl apply -f ./kubernetes-ingress/examples/ingress-resources/complete-example/cafe.yamldeployment.apps/coffee created
service/coffee-svc created
deployment.apps/tea created
service/tea-svc created
  1. Implante o NGINX Ingress Controller para a aplicação Cafe. Observe as seguintes configurações: o tempo necessário para concluir esta etapa depende muito do provedor de DNS, pois o Kubernetes interage com a API de DNS do provedor.
$ kubectl apply -f - <<EOFapiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: cafe spec: host: cafe.example.com tls: secret: cafe-secret cert-manager: cluster-issuer: example-issuer externalDNS: enable: true upstreams: - name: tea service: tea-svc port: 80 - name: coffee service: coffee-svc port: 80 routes: - path: /tea action: pass: tea - path: /coffee action: pass: coffee EOF
virtualserver.k8s.nginx.org/cafe created
  • kind: VirtualServer – Estamos usando o recurso personalizado VirtualServer do NGINX, e não o recurso Ingress padrão do Kubernetes.
  • spec.host – Substitua cafe.example.com pelo nome do host que você está implantando. O host deve estar dentro do domínio gerenciado pelo ExternalDNS.
  • spec.tls.cert-manager.cluster-issuer – Se você estiver usando os valores especificados nesta publicação, este será o example-issuer. Se necessário, substitua-o pelo nome que você escolheu na Etapa 3 de Implantar o cert‑manager.
  • spec.externalDNS.enable – O valor true instrui o ExternalDNS a criar um registro DNS A.

Validar a solução

  1. Verifique o registro DNS A — em especial, confirme se, no bloco ANSWER SECTION, o FQDN (neste caso, cafe.example.com) está associado ao endereço IP correto (www.xxx.yyy.zzz).
$ dig cafe.example.com 
; <<>> DiG 9.10.6 <<>> cafe.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22633
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;cafe.example.com.		IN	A
 
;; ANSWER SECTION:
cafe.example.com.	279	IN	A	www.xxx.yyy.zzz
 
;; Query time: 1 msec
;; SERVER: 2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359#53(2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359)
;; WHEN: Day Mon DD hh:mm:ss TZ YYYY
;; MSG SIZE  rcvd: 67
  1. Verifique se o certificado é válido (o valor no campo READY é True).
$ kubectl get certificatesNAME          READY   SECRET        AGE
cafe-secret   True    cafe-secret   8m51s
  1. Verifique se você consegue acessar a aplicação.
$ curl https://cafe.example.com/coffeeServer address: 10.2.2.4:8080
Server name: coffee-7c86d7d67c-lsfs6
Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
URI: /coffee
Request ID: 91077575f19e6e735a91b9d06e9684cd
$ curl https://cafe.example.com/tea
Server address: 10.2.2.5:8080
Server name: tea-5c457db9-ztpns
Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
URI: /tea
Request ID: 2164c245a495d22c11e900aa0103b00f

O que acontece quando um desenvolvedor implanta o NGINX Ingress Controller

Muita coisa acontece nos bastidores depois que a solução é implementada. O diagrama mostra o que acontece quando um desenvolvedor implanta o NGINX Ingress Controller com um recurso personalizado NGINX VirtualServer. Observe que alguns detalhes operacionais foram omitidos.

O que acontece quando um desenvolvedor implanta o NGINX Ingress Controller com um recurso personalizado NGINX VirtualServer: (1) O desenvolvedor implanta um recurso VirtualServer usando a CRD do NGINX (2) O Kubernetes cria o VirtualServer usando o NGINX Ingress Controller (3) O NGINX Ingress Controller chama o ExternalDNS para criar um registro DNS do tipo A (4) O ExternalDNS cria o registro A no DNS (5) O NGINX Ingress Controller chama o cert-manager para solicitar um certificado TLS (6) O cert-manager adiciona um registro DNS para uso durante o desafio DNS-01 (7) O cert-manager entra em contato com o Let’s Encrypt para concluir o desafio (8) O Let’s Encrypt valida o desafio no DNS (9) O Let’s Encrypt emite o certificado TLS (10) O cert-manager fornece o certificado TLS ao NGINX Ingress Controller (11) O NGINX Ingress Controller encaminha solicitações externas protegidas por TLS para os pods da aplicação

  1. O desenvolvedor implanta um recurso VirtualServer usando a CRD do NGINX
  2. O Kubernetes cria o VirtualServer usando o NGINX Ingress Controller
  3. O NGINX Ingress Controller chama o ExternalDNS para criar um registro DNS A record
  4. O ExternalDNS cria o registro A no DNS
  5. O NGINX Ingress Controller chama o cert-manager para solicitar um certificado TLS
  6. O cert-manager adiciona um registro DNS para uso durante o desafio DNS-01
  7. O cert-manager entra em contato com o Let’s Encrypt para concluir o desafio
  8. O Let’s Encrypt valida o desafio por meio do DNS
  9. A Let’s Encrypt emite o certificado TLS
  10. O cert-manager fornece o certificado TLS ao NGINX Ingress Controller
  11. O NGINX Ingress Controller encaminha solicitações externas protegidas por TLS para os pods da aplicação

Solução de problemas

Dada a complexidade do Kubernetes e dos componentes que estamos usando, é difícil fornecer um guia abrangente de solução de problemas. Ainda assim, há algumas sugestões básicas que podem ajudar você a identificar o problema.

  • Use os comandos kubectl get e kubectl describe para validar a configuração dos objetos implantados.
  • Use o comando kubectl logs <component> para visualizar os arquivos de log dos diversos componentes implantados.
  • Use o K9s para inspecionar a instalação; o software destaca os problemas em amarelo ou vermelho (dependendo da gravidade) e fornece uma interface para acessar logs e detalhes sobre os objetos.

Se você ainda estiver enfrentando problemas, fale conosco no Slack da comunidade NGINX e peça ajuda! Temos uma comunidade vibrante e estamos sempre dispostos a ajudar a resolver problemas.

Para experimentar o NGINX Ingress Controller baseado no NGINX Plus, comece hoje mesmo sua avaliação gratuita de 30 dias ou entre em contato conosco para discutir seus casos de uso.


Compartilhar
Tags: DNS, F5 NGINX, Tech

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
NGINX | 10/10/2024

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade

Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
NGINX | 17/06/2024

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa

Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
NGINX | 14/07/2021

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor

Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
NGINX | 02/04/2019

Distribuição segura de chaves privadas SSL com o NGINX

Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
NGINX | 24/05/2018

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX

O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
NGINX | 19/04/2016

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus

Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.