BLOG | NGINX

Habilitación de la gestión de certificados y DNS de autoservicio en Kubernetes

Miniatura de Jason Schmidt
Jason Schmidt
Publicado el 1 de noviembre de 2022

El objetivo final del desarrollo de aplicación es, por supuesto, exponer aplicaciones en Internet. Para un desarrollador, Kubernetes simplifica este proceso hasta cierto punto al proporcionar el controlador de Ingress como mecanismo para enrutar solicitudes a la aplicación. Pero no todo es tan autoservicio como probablemente quisieras: aún necesitas un registro en el Sistema de nombres de dominio (DNS) para asignar el nombre de dominio de la aplicación a la dirección IP del controlador de Ingress y un certificado TLS para proteger las conexiones usando HTTPS. En la mayoría de las organizaciones, usted no es propietario de DNS o TLS y, por lo tanto, debe coordinarse con el grupo operativo (o los grupos) que sí lo tienen.

Las cosas no son necesariamente más fáciles para los operadores. En la mayoría de las organizaciones, la necesidad de actualizar registros DNS es tan poco frecuente que los procedimientos (tanto las reglas comerciales como los pasos técnicos reales) tienden a ser escasos o inexistentes. Esto significa que cuando necesitas agregar un registro DNS primero debes buscar la documentación, preguntar a un colega o (en el peor de los casos) averiguarlo. También debe asegurarse de cumplir con todas las normas de seguridad corporativas y de que el ingreso esté etiquetado correctamente para los firewalls.

Afortunadamente, existe una manera de hacer la vida más fácil tanto a los desarrolladores como a los operadores. En esta publicación, mostramos cómo los operadores pueden configurar una implementación de Kubernetes para permitir el autoservicio para que los desarrolladores actualicen registros DNS y generen certificados TLS en un entorno de Kubernetes. Al desarrollar la infraestructura con anticipación, puede asegurarse de que se cumplan todos los requisitos comerciales y técnicos necesarios.

Descripción general y requisitos previos

Con la solución implementada, todo lo que un desarrollador necesita hacer para exponer una aplicación a Internet es crear un controlador de Ingress siguiendo una plantilla proporcionada que incluye un nombre de dominio completo (FQDN) dentro de un dominio administrado por la instalación de Kubernetes. Kubernetes usa la plantilla para asignar una dirección IP para el controlador de Ingress, crear el registro DNS A para asignar el FQDN a la dirección IP y generar certificados TLS para el FQDN y agregarlos al controlador de Ingress. La limpieza es igual de sencilla: cuando se elimina el Ingress, se limpian los registros DNS.

La solución aprovecha las siguientes tecnologías (proporcionamos instrucciones de instalación y configuración a continuación):

Antes de configurar la solución, necesita:

  • Una instalación en la nube de Kubernetes con un objeto de salida ( LoadBalancer ). La solución utiliza Linode, pero también funcionan otros proveedores de nube.
  • Un nombre de dominio alojado con Cloudflare , que elegimos porque es uno de los proveedores de DNS compatibles con cert-manager y admite ExternalDNS ( en versión beta al momento de escribir este artículo). Recomendamos encarecidamente que el dominio no se utilice para producción ni para ningún otro propósito crítico.
  • Acceso a la API de Cloudflare, que está incluida en el nivel gratuito.
  • Helm para instalar e implementar Kubernetes.
  • kubectl como interfaz de línea de comandos para Kubernetes.
  • Opcionalmente, K9s , una interfaz de usuario tangible (TUI) bien construida que proporciona una forma más estructurada de interactuar con Kubernetes.

También asumimos que tienes un conocimiento básico de Kubernetes (cómo aplicar un manifiesto, usar un gráfico de Helm y emitir comandos kubectl para ver la salida y solucionar problemas). Comprender los conceptos básicos de Let's Encrypt es útil, pero no obligatorio; para obtener una descripción general, consulte nuestro blog . Tampoco es necesario que sepas cómo funciona cert-manager , pero si te interesa saber cómo funciona (y los certificados en general) con NGINX Ingress Controller, consulta mi publicación reciente, Automatizar la gestión de certificados en un entorno de Kubernetes .

Hemos probado la solución tanto en macOS como en Linux. No hemos realizado pruebas en el Subsistema de Windows para Linux versión 2 (WSL2), pero no prevemos ningún problema.

Nota:  La solución está pensada como una muestra de prueba de concepto y no para su uso en producción. En particular, no incorpora todas las mejores prácticas de operación y seguridad. Para obtener información sobre estos temas, consulte la documentación de cert-manager y ExternalDNS .

Implementación de la solución

Siga los pasos de estas secciones para implementar la solución:

Descargar software
  1. Descargue su token de API de Cloudflare .
  2. Clonar el repositorio del controlador de ingreso NGINX:

    $ 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.
  3. Verifique que pueda conectarse al clúster de 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'.

Implementar el controlador de ingreso NGINX

  1. Usando Helm, implemente el controlador de ingreso NGINX. Tenga en cuenta que estamos agregando tres opciones de configuración no estándar :

    • controller.enableCustomResources – Le indica a Helm que instale las definiciones de recursos personalizados (CRD) necesarias para crear los recursos personalizados NGINX VirtualServer y VirtualServerRoute.
    • controller.enableCertManager : configura el controlador de ingreso NGINX para comunicarse con los componentes del administrador de certificados .
    • controller.enableExternalDNS : configura el controlador de ingreso para comunicarse con componentes ExternalDNS.
    $ 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.
  2. Verifique que el controlador de ingreso NGINX se esté ejecutando y anote el valor en el campo IP EXTERNA : es la dirección IP del controlador de ingreso NGINX (aquí, www.xxx.yyy.zzz ). La salida se distribuye en dos líneas para facilitar la legibilidad.

    $ 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

Implementar cert-manager

En la solución, cert-manager utiliza el tipo de desafío DNS-01 al obtener un certificado TLS, lo que requiere que se proporcione el token de API de Cloudflare durante la creación del recurso ClusterIssuer . En la solución, el token de API se proporciona como un secreto de Kubernetes.

  1. Usando Helm, implemente 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!
  2. Implemente el token de API de Cloudflare como un secreto de Kubernetes, sustituyéndolo por <su-token-API>:

    $ 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
  3. Cree un objeto ClusterIssuer, especificando Cloudflare-api-token-secret (definido en el paso anterior) como el lugar para recuperar el token. Si lo desea, puede reemplazar example-issuer en el campo metadata.name (y example-issuer-account-key en el campo spec.acme.privateKeySecretRef.name ) con un nombre diferente.

    $ 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
  4. Verifique que ClusterIssuer esté implementado y listo (el valor en el campo READY es True ).

    $ kubectl get clusterissuerNAME             READY   AGE
    example-issuer   True    3h9m

Implementar ExternalDNS

Al igual que cert-manager, el proyecto ExternalDNS requiere un token de API de Cloudflare para administrar DNS. Se puede usar el mismo token para ambos proyectos, pero no es obligatorio.

  1. Cree los CRD de ExternalDNS para el controlador de ingreso NGINX para permitir la integración entre los proyectos.

    $ kubectl create -f ./kubernetes-ingress/deployments/common/crds/externaldns.nginx.org_dnsendpoints.yamlcustomresourcedefinition.apiextensions.k8s.io/dnsendpoints.externaldns.nginx.org created
  2. Cree el servicio DNS externo ( external-dns ). Como el manifiesto es bastante largo, aquí lo dividiremos en dos partes. La primera parte configura cuentas, roles y permisos:

    • Crea un objeto ServiceAccount llamado external-dns para administrar todas las operaciones de escritura y actualización para administrar DNS.
    • Crea un objeto ClusterRole (también llamado external-dns ) que define los permisos necesarios.
    • Vincula el ClusterRole a la ServiceAccount.
    $ 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

    La segunda parte del manifiesto crea la implementación de ExternalDNS:

    • Crea un filtro de dominio que limita el alcance de posibles daños causados por ExternalDNS mientras administra dominios. Por ejemplo, puede especificar los nombres de dominio de los entornos de prueba para evitar cambios en los entornos de producción. En este ejemplo, establecemos domain-filter en example.com .
    • Establece la variable de entorno CF_API_TOKEN en su token de API de Cloudflare. Para <su-token-API>, sustituya el token real o un secreto que contenga el token. En el último caso, también es necesario proyectar el secreto en el contenedor mediante una variable de entorno.
    • Establece la variable de entorno FREE_TIER en "verdadero" (apropiado a menos que tenga una suscripción paga de Cloudflare).
    $  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

Implementar la aplicación de muestra

Utilice la aplicación de muestra estándar NGINX Ingress Controller llamada Cafe para fines de prueba.

  1. Implementar la aplicación 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
  2. Implementar el controlador de ingreso NGINX para la aplicación Cafe. Tenga en cuenta la siguiente configuración:

    • amable: VirtualServer : utilizamos el recurso personalizado NGINX VirtualServer, no el recurso estándar Kubernetes Ingress.
    • spec.host – Reemplace cafe.example.com con el nombre del host que está implementando. El host debe estar dentro del dominio que se administra con ExternalDNS.
    • spec.tls.cert-manager.cluster-issuer – Si ha estado utilizando los valores especificados en esta publicación, este es example-issuer . Si es necesario, sustituya el nombre que eligió en el Paso 3 de Implementar cert-manager .
    • spec.externalDNS.enable – El valor true le indica a ExternalDNS que cree un registro DNS A.

    Tenga en cuenta que el tiempo que tarda en completarse este paso depende en gran medida del proveedor de DNS, ya que Kubernetes interactúa con la API de DNS del proveedor.

    $ 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

Validar la solución

  1. Verifique el registro A del DNS, en particular que en el bloque SECCIÓN DE RESPUESTA el FQDN (aquí, cafe.example.com ) esté asignado a la dirección IP correcta ( 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
  2. Compruebe que el certificado sea válido (el valor en el campo READY es True ).

    $ kubectl get certificatesNAME          READY   SECRET        AGE
    cafe-secret   True    cafe-secret   8m51s
  3. Verifique que pueda acceder a la aplicación.

    $ 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

¿Qué sucede cuando un desarrollador implementa el controlador de ingreso NGINX?

Una vez que se implementa la solución, ocurren muchas cosas bajo tierra. El diagrama muestra lo que sucede cuando un desarrollador implementa el controlador de ingreso NGINX con un recurso personalizado NGINX VirtualServer. Téngase en cuenta que se omiten algunos detalles operativos.

  1. El desarrollador implementa un recurso VirtualServer utilizando NGINX CRD
  2. Kubernetes crea el servidor virtual mediante el controlador de ingreso NGINX
  3. El controlador de ingreso de NGINX llama a ExternalDNS para crear un registro DNS A
  4. ExternalDNS crea el registro A en DNS
  5. El controlador de ingreso de NGINX llama a cert-manager para solicitar un certificado TLS
  6. cert-manager agrega un registro DNS para usar durante el desafío DNS-01
  7. cert-manager contacta a Let’s Encrypt para completar el desafío
  8. Let’s Encrypt valida el desafío contra DNS
  9. Let’s Encrypt emite el certificado TLS
  10. cert-manager proporciona el certificado TLS al controlador de ingreso NGINX
  11. El controlador de ingreso NGINX enruta las solicitudes externas protegidas por TLS a los pods de la aplicación

Solución de problemas

Dada la complejidad de Kubernetes junto con los componentes que utilizamos, es difícil proporcionar una guía completa de solución de problemas. Dicho esto, hay algunas sugerencias básicas para ayudarle a determinar el problema.

  • Utilice los comandos kubectl get y kubectl describe para validar la configuración de los objetos implementados.
  • Utilice el kubectl registros <componente> Comando para ver los archivos de registro de los distintos componentes implementados.
  • Utilice K9s para inspeccionar la instalación; el software resalta los problemas en amarillo o rojo (dependiendo de la gravedad) y proporciona una interfaz para acceder a registros y detalles sobre los objetos.

Si aún tienes problemas, encuéntranos en el Slack de la comunidad NGINX y solicita ayuda. Tenemos una comunidad vibrante y siempre estamos felices de resolver los problemas.

Para probar el controlador de ingreso NGINX basado en NGINX Plus, comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso .


"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.