Los certificados SSL/TLS válidos son un requisito fundamental del panorama de aplicación moderno. Lamentablemente, la gestión de las renovaciones de certificados (o cert) suele ser una ocurrencia posterior al implementar una aplicación. Los certificados tienen una vida útil limitada, que va desde aproximadamente 13 meses para los certificados de DigiCert hasta 90 días para los certificados Let's Encrypt . Para mantener un acceso seguro, estos certificados deben renovarse o volver a emitirse antes de su vencimiento. Dada la importante carga de trabajo de la mayoría de los equipos de operaciones, a veces la renovación de certificados pasa desapercibida, lo que genera un lío a medida que los certificados se acercan a su fecha de vencimiento (o peor aún, la superan).
No tiene por qué ser así. Con un poco de planificación y preparación, la gestión de certificados se puede automatizar y agilizar. Aquí, veremos una solución para Kubernetes utilizando tres tecnologías:
En este blog, aprenderá a simplificar la gestión de certificados al proporcionar certificados únicos, renovados y actualizados automáticamente a sus puntos finales.
Antes de entrar en detalles técnicos, necesitamos definir algo de terminología. El término “certificado TLS” se refiere a dos componentes necesarios para habilitar conexiones HTTPS en nuestro controlador Ingress:
Tanto el certificado como la clave privada son emitidos por Let's Encrypt . Para obtener una explicación completa de cómo funcionan los certificados TLS, consulte la publicación de DigiCert Cómo funcionan los certificados TLS/SSL .
En Kubernetes, estos dos componentes se almacenan como secretos . Las cargas de trabajo de Kubernetes, como el controlador de ingreso NGINX y cert-manager , pueden escribir y leer estos secretos, que también pueden ser administrados por usuarios que tienen acceso a la instalación de Kubernetes.
El proyecto cert-manager es un controlador de certificados que funciona con Kubernetes y OpenShift. Cuando se implementa en Kubernetes, cert-manager emitirá automáticamente los certificados requeridos por los controladores de Ingress y garantizará que sean válidos y actualizados. Además, rastreará las fechas de vencimiento de los certificados e intentará la renovación en un intervalo de tiempo configurado. Aunque funciona con numerosos emisores públicos y privados, mostraremos su integración con Let's Encrypt.
Al utilizar Let’s Encrypt, toda la gestión de certificados se realiza de forma automática. Si bien esto proporciona mucha comodidad, también presenta un problema: ¿Cómo garantiza el servicio que usted es el propietario del nombre de dominio completo (FQDN) en cuestión?
Este problema se resuelve mediante un desafío , que requiere que usted responda una solicitud de verificación que sólo alguien con acceso a los registros DNS del dominio específico puede proporcionar. Los desafíos toman una de dos formas:
HTTP-01 es la forma más sencilla de generar un certificado, ya que no requiere acceso directo al proveedor de DNS. Este tipo de desafío siempre se realiza a través del puerto 80 (HTTP). Tenga en cuenta que al utilizar desafíos HTTP-01, cert-manager utilizará el controlador de Ingress para servir el token de desafío.
Un controlador de ingreso es un servicio especializado para Kubernetes que trae tráfico desde fuera del clúster, equilibra la carga hacia los pods internos (un grupo de uno o más contenedores) y administra el tráfico de salida. Además, el controlador de Ingress se controla a través de la API de Kubernetes y supervisará y actualizará la configuración de equilibrio de carga a medida que se agregan, eliminan o fallan los pods.
Para obtener más información sobre los controladores Ingress, lea los siguientes blogs:
En los ejemplos siguientes, utilizaremos el controlador de ingreso NGINX, desarrollado y mantenido por F5 NGINX.
Estos ejemplos suponen que tiene una instalación de Kubernetes en funcionamiento con la que puede realizar pruebas y que la instalación puede asignar una dirección IP externa (objeto Kubernetes LoadBalancer). Además, se supone que puede recibir tráfico tanto en el puerto 80 como en el puerto 443 (si utiliza el desafío HTTP-01) o únicamente en el puerto 443 (si utiliza el desafío DNS-01). Estos ejemplos se ilustran utilizando Mac OS X, pero también se pueden utilizar en Linux o WSL.
También necesitará un proveedor de DNS y un FQDN para el cual pueda ajustar el registro A. Si está utilizando el desafío HTTP-01, solo necesita la capacidad de agregar un registro A (o que se agregue uno por usted). Si está utilizando el desafío DNS-01, necesitará acceso a la API de un proveedor de DNS compatible o un proveedor de webhook compatible .
La forma más fácil de implementar es a través de Helm . Esta implementación le permite utilizar tanto Kubernetes Ingress como el CRD del servidor virtual NGINX.
$ helm repo add nginx-stable https://helm.nginx.com/stable  "nginx-stable" has been added to your repositories $ helm repo update  Hang tight while we grab the latest from your chart repositories...
  ...Successfully got an update from the "nginx-stable" chart repository
  Update Complete. ⎈Happy Helming!⎈ $ helm install nginx-kic nginx-stable/nginx-ingress \  --namespace nginx-ingress  --set controller.enableCustomResources=true \ 
  --create-namespace  --set controller.enableCertManager=true 
  NAME: nginx-kic
  LAST DEPLOYED: Thu Sep  1 15:58:15 2022
  NAMESPACE: nginx-ingress
  STATUS: deployed
  REVISION: 1
  TEST SUITE: None
  NOTES:
  The NGINX Ingress Controller has been installed. $ kubectl get deployments --namespace nginx-ingress  NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
  nginx-kic-nginx-ingress   1/1     1            1           23s
  $ kubectl get services --namespace nginx-ingress
  NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
  nginx-kic-nginx-ingress   LoadBalancer   10.128.60.190   www.xxx.yyy.zzz   80:31526/TCP,443:32058/TCP   30s El proceso aquí dependerá de su proveedor de DNS. Este nombre DNS deberá poder resolverse desde los servidores Let's Encrypt, lo que puede requerir que espere a que el registro se propague antes de que funcione. Para obtener más información sobre esto, consulte el artículo de SiteGround ¿Qué es la propagación de DNS y por qué tarda tanto?
Una vez que pueda resolver el FQDN elegido, estará listo para pasar al siguiente paso.
$ host cert.example.com  cert.example.com has address www.xxx.yyy.zzzEl siguiente paso es implementar la versión más reciente de cert-manager. Nuevamente, utilizaremos Helm para nuestra implementación.
$ helm repo add jetstack https://charts.jetstack.io  "jetstack" has been added to your repositories $ helm repo update  Hang tight while we grab the latest from your chart repositories...
  ...Successfully got an update from the "nginx-stable" chart repository
  ...Successfully got an update from the "jetstack" chart repository
  Update Complete. ⎈Happy Helming!⎈ $ helm install cert-manager jetstack/cert-manager \  --namespace cert-manager --create-namespace \
  --version v1.9.1  --set installCRDs=true 
  NAME: cert-manager
  LAST DEPLOYED: Thu Sep  1 16:01:52 2022 
  NAMESPACE: cert-manager
  STATUS: deployed
  REVISION: 1 
  TEST SUITE: None
  NOTES:
  cert-manager v1.9.1 has been deployed successfully!
  In order to begin issuing certificates, you will need to set up a ClusterIssuer
  or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
  More information on the different types of issuers and how to configure them
  can be found in our documentation:
  https://cert-manager.io/docs/configuration/
  For information on how to configure cert-manager to automatically provision
  Certificates for Ingress resources, take a look at the `ingress-shim`
  documentation:
  https://cert-manager.io/docs/usage/ingress/$ kubectl get deployments --namespace cert-manager  NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
  cert-manager              1/1     1            1           4m30s
  cert-manager-cainjector   1/1     1            1           4m30s
  cert-manager-webhook      1/1     1            1           4m30s Utilizaremos el ejemplo de NGINX Cafe para proporcionar nuestra implementación y servicios de backend. Este es un ejemplo común en la documentación de NGINX. No implementaremos Ingress como parte de este ejemplo.
$ git clone https://github.com/nginxinc/kubernetes-ingress.git  Cloning into 'kubernetes-ingress'...
  remote: Enumerating objects: 44979, done.
  remote: Counting objects: 100% (172/172), done.
  remote: Compressing objects: 100% (108/108), done.
  remote: Total 44979 (delta 87), reused 120 (delta 63), pack-reused 44807
  Receiving objects: 100% (44979/44979), 60.27 MiB | 27.33 MiB/s, done.
  Resolving deltas: 100% (26508/26508), done. $ cd ./kubernetes-ingress/examples/ingress-resources/complete-example $ kubectl apply -f ./cafe.yaml
  deployment.apps/coffee created
  service/coffee-svc created
  deployment.apps/tea created
  service/tea-svc createdkubectl get. Debe asegurarse de que los Pods se muestren como LISTOS y los Servicios se muestren como en ejecución . El siguiente ejemplo muestra una muestra representativa de lo que estás buscando. Tenga en cuenta que el servicio Kubernetes es un servicio del sistema que se ejecuta en el mismo espacio de nombres (predeterminado) que el ejemplo NGINX Cafe.$ kubectl get deployments,services  --namespace default  NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
  deployment.apps/coffee   2/2     2            2           69s
  deployment.apps/tea      3/3     3            3           68s
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
  service/coffee-svc   ClusterIP   10.128.154.225   <none>        80/TCP    68s
  service/kubernetes   ClusterIP   10.128.0.1       <none>        443/TCP   29m
	service/tea-svc      ClusterIP   10.128.96.145    <none>        80/TCP    68s Dentro de cert-manager, se puede usar ClusterIssuer para emitir certificados. Se trata de un objeto de ámbito de clúster al que puede hacer referencia cualquier espacio de nombres y que puede utilizar cualquier solicitud de certificado con la autoridad emisora de certificados definida. En este ejemplo, cualquier solicitud de certificado para certificados Let's Encrypt puede ser manejada por este ClusterIssuer.
Implemente el ClusterIssuer para el tipo de desafío que haya seleccionado. Si bien está fuera del alcance de esta publicación, existen opciones de configuración avanzadas que le permiten especificar múltiples resolvers (seleccionados en función de los campos selectores) en su ClusterIssuer.
El protocolo del entorno de administración automatizada de certificados (ACME) se utiliza para determinar si usted es el propietario de un nombre de dominio y, por lo tanto, se le puede emitir un certificado Let’s Encrypt. Para este desafío, estos son los parámetros que se deben pasar:
Este ejemplo muestra cómo configurar un ClusterIssuer para utilizar el desafío HTTP-01 para demostrar la propiedad del dominio y recibir un certificado.
$ cat << EOF | kubectl apply -f   apiVersion: cert-manager.io/v1
  kind: ClusterIssuer
  metadata:
    name: prod-issuer
  spec:
    acme:
      email: example@example.com
      server: https://acme-v02.api.letsencrypt.org/directory
      privateKeySecretRef:
        name: prod-issuer-account-key
      solvers:
      - http01:
         ingress:
           class: nginx
  EOF
  clusterissuer.cert-manager.io/prod-issuer created $ kubectl get clusterissuer  NAME          READY   AGE
	prod-issuer   True    34s Este ejemplo muestra cómo configurar un ClusterIssuer para utilizar el desafío DNS-01 para autenticar la propiedad de su dominio. Dependiendo de su proveedor de DNS, probablemente necesitará usar un secreto de Kubernetes para almacenar su token. Este ejemplo utiliza Cloudflare . Tenga en cuenta el uso del espacio de nombres. La aplicación cert-manager, que se implementa en el espacio de nombres cert-manager, necesita tener acceso al Secreto.
Para este ejemplo, necesitará un token de API de Cloudflare , que puede crear desde su cuenta. Esto deberá colocarse en la línea a continuación. Si no utiliza Cloudflare, deberá seguir la documentación de su proveedor .
$ cat << EOF | kubectl apply -f   apiVersion: v1
  kind: Secret
  metadata:
    name: cloudflare-api-token-secret
    namespace: cert-manager
  type: Opaque
  stringData:
    api-token: <API Token> 
  EOF $ cat << EOF | kubectl apply -f   apiVersion: cert-manager.io/v1
  kind: ClusterIssuer
  metadata:
    name: prod-issuer
  spec:
    acme:
      email: example@example.com
      server: https://acme-v02.api.letsencrypt.org/directory
      privateKeySecretRef:
        name: prod-issuer-account-key
      solvers:
        - dns01:
            cloudflare:
              apiTokenSecretRef:
                name: cloudflare-api-token-secret
                key: api-token
  EOF $ kubectl get clusterissuer  NAME          READY   AGE
	prod-issuer   True    31m Este es el punto hacia el cual hemos estado trabajando: la implementación del recurso Ingress para nuestra aplicación. Esto dirigirá el tráfico a la aplicación NGINX Cafe que implementamos anteriormente.
Si está utilizando el recurso Ingress estándar de Kubernetes, utilizará el siguiente YAML de implementación para configurar Ingress y solicitar un certificado.
apiVersion: networking.k8s.io/v1   kind: Ingress 
  metadata: 
    name: cafe-ingress 
    annotations: 
      cert-manager.io/cluster-issuer: prod-issuer 
      acme.cert-manager.io/http01-edit-in-place: "true" 
  spec: 
    ingressClassName: nginx 
    tls: 
    - hosts: 
      - cert.example.com 
      secretName: cafe-secret 
    rules: 
    - host: cert.example.com 
      http: 
        paths: 
        - path: /tea 
          pathType: Prefix 
          backend: 
            service: 
              name: tea-svc 
              port: 
                number: 80 
        - path: /coffee 
          pathType: Prefix 
          backend: 
            service: 
              name: coffee-svc 
              port: 
number: 80 Vale la pena revisar algunas partes clave del manifiesto:
metadata.annotations , donde establecemos acme.cert-manager.io/http01-edit-in-place en “verdadero”. Este valor es obligatorio y ajusta la forma en que se atiende el desafío. Para obtener más información, consulte el documento Anotaciones admitidas . Esto también se puede manejar mediante una configuración maestro/súbdito .spec.ingressClassName hace referencia al controlador de ingreso NGINX que instalamos y utilizaremos.spec.tls.secret almacena la clave del certificado que se devuelve cuando Let's Encrypt emite el certificado. cert.example.com se especifica para spec.tls.hosts y spec.rules.host . Este es el nombre de host para el cual nuestro ClusterIssuer emitió el certificado.spec.rules.http define las rutas y los servicios backend que atenderán las solicitudes en esas rutas. Por ejemplo, el tráfico a /tea se dirigirá al puerto 80 en tea-svc .spec.rules.host y spec.tls.hosts , pero debe revisar todos los parámetros en la configuración. $  kubectl apply -f ./cafe-virtual-server.yaml  virtualserver.k8s.nginx.org/cafe created $ kubectl get certificates  NAME                                      READY   SECRET        AGE
  certificate.cert-manager.io/cafe-secret   True    cafe-secret   37m Si está utilizando los CRD de NGINX, deberá utilizar el siguiente YAML de implementación para configurar su Ingress.
  apiVersion: k8s.nginx.org/v1 
  kind: VirtualServer 
  metadata: 
    name: cafe 
  spec: 
    host: cert.example.com 
    tls: 
      secret: cafe-secret 
      cert-manager: 
        cluster-issuer: prod-issuer 
    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: coffeeUna vez más, vale la pena revisar algunas partes clave del manifiesto:
spec.tls.secret almacena la clave del certificado que se devuelve cuando Let's Encrypt emite el certificado. cert.example.com está especificado para spec.host . Este es el nombre de host para el cual nuestro ClusterIssuer emitió el certificado.spec.upstreams apuntan a nuestros servicios de backend, incluidos los puertos.spec.routes define tanto la ruta como la acción a tomar cuando se alcanzan esas rutas.de spec.host , pero debe revisar todos los parámetros en la configuración.  $  kubectl apply -f ./cafe-virtual-server.yaml  virtualserver.k8s.nginx.org/cafe created$ kubectl get VirtualServers  NAME   STATE   HOST                    IP             PORTS      AGE
  cafe   Valid   cert.example.com   www.xxx.yyy.zzz   [80,443]   51m Puede ver el certificado a través de la API de Kubernetes. Esto le mostrará detalles sobre el certificado, incluyendo su tamaño y la clave privada asociada.
$ kubectl describe secret cafe-secret  Name:         cafe-secret
  Namespace:    default
  Labels:       <none>
  Annotations:  cert-manager.io/alt-names: cert.example.com
                cert-manager.io/certificate-name: cafe-secret
                cert-manager.io/common-name: cert.example.com
                cert-manager.io/ip-sans:
                cert-manager.io/issuer-group:
                cert-manager.io/issuer-kind: ClusterIssuer
                cert-manager.io/issuer-name: prod-issuer
                cert-manager.io/uri-sans:Type:  kubernetes.io/tlsData
  ====
  tls.crt:  5607 bytes
  tls.key:  1675 bytes Si desea ver el certificado y la clave reales, puede hacerlo ejecutando el siguiente comando. (Nota: Esto ilustra una debilidad de los secretos de Kubernetes. Es decir, pueden ser leídos por cualquier persona con los permisos de acceso necesarios).
$ kubectl get secret cafe-secret -o yamlPruebe los certificados. Puedes utilizar cualquier método que desees aquí. El siguiente ejemplo utiliza cURL . El éxito se indica mediante un bloque similar al mostrado anteriormente, que incluye el nombre del servidor, su dirección interna, la fecha, la URI (ruta) elegida (café o té) y el ID de la solicitud. Los fallos se presentarán como códigos de error HTTP, probablemente 400 o 301.
$ curl https://cert.example.com/tea
  Server address: 10.2.0.6:8080
  Server name: tea-5c457db9-l4pvq
  Date: 02/Sep/2022:15:21:06 +0000
  URI: /tea
  Request ID: d736db9f696423c6212ffc70cd7ebecf
  $ curl https://cert.example.com/coffee
  Server address: 10.2.2.6:8080
  Server name: coffee-7c86d7d67c-kjddk
  Date: 02/Sep/2022:15:21:10 +0000
  URI: /coffee
Request ID: 4ea3aa1c87d2f1d80a706dde91f31d54 Al principio, prometimos que este enfoque eliminaría la necesidad de gestionar renovaciones de certificados. Sin embargo, todavía tenemos que explicar cómo hacerlo. ¿Por qué?Porque esta es una parte fundamental e integrada de cert-manager. En este proceso automático, cuando cert-manager se da cuenta de que un certificado no está presente, está vencido, está a 15 días de su vencimiento o si el usuario solicita un nuevo certificado a través de la CLI, entonces se solicita automáticamente un nuevo certificado . No hay nada más fácil que eso.
Si es suscriptor de NGINX Plus, la única diferencia para usted será la instalación del controlador de ingreso NGINX. Consulte la sección Instalación de Helm de los documentos de NGINX para obtener instrucciones sobre cómo modificar el comando Helm indicado anteriormente para lograr esto.
Esto depende en gran medida de su caso de uso.
El método de desafío HTTP-01 requiere que el puerto 80 esté abierto a Internet y que el registro DNS A se haya configurado correctamente para la dirección IP del controlador de Ingress. Este enfoque no requiere acceso al proveedor de DNS excepto para crear el registro A.
El método de desafío DNS-01 se puede utilizar cuando no se puede exponer el puerto 80 a Internet y solo requiere que el administrador de certificados tenga acceso de salida al proveedor de DNS. Sin embargo, este método requiere que usted tenga acceso a la API de su proveedor de DNS, aunque el nivel de acceso requerido varía según el proveedor específico.
Dado que Kubernetes es tan complejo, es difícil proporcionar información específica para la resolución de problemas. Si tiene algún problema, nos gustaría invitarlo a que nos pregunte en Slack de la comunidad NGINX (los suscriptores de NGINX Plus pueden usar sus opciones de soporte normales).
Comience solicitando su prueba gratuita de 30 días de NGINX Ingress Controller con NGINX App Protect WAF y DoS, y descargue NGINX Service Mesh siempre gratuito.
"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.