Los certificados SSL/TLS válidos son un requisito fundamental del panorama de aplicação moderno. Lamentablemente, la gestión de las renovaciones de certificados (o cert) suele ser una ocurrencia posterior al implementar una aplicação. 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" se ha agregado a sus repositorios
$ helm repo update Esperen mientras descargamos las últimas novedades de sus repositorios de charts...
...Se recibió una actualización del repositorio de charts "nginx-stable".
Actualización completada. ¡Feliz Helming!
$ helm install nginx-kic nginx-stable/nginx-ingress \ --namespace nginx-ingress --set controller.enableCustomResources=true \
--create-namespace --set controller.enableCertManager=true
NOMBRE: nginx-kic
ÚLTIMA IMPLEMENTACIÓN: Jue 1 sep 2022 15:58:15
ESPACIO DE NOMBRES: nginx-ingress
ESTADO: implementado
REVISIÓN: 1
CONJUNTO DE PRUEBAS: Ninguna
NOTAS:
Se ha instalado el controlador de ingreso NGINX.
$ kubectl getployments --namespace nginx-ingress NOMBRE LISTO ACTUALIZADO DISPONIBLE ANTIGÜEDAD
nginx-kic-nginx-ingress 1/1 1 1 23s
$ kubectl get services --namespace nginx-ingress
NOMBRE TIPO IP DEL CLÚSTER IP EXTERNA PUERTO(S) EDAD
nginx-kic-nginx-ingress Balanceador de carga 10.128.60.190 www.xxx.yyy.zzz 80:31526/TCP,443:32058/TCP 30 s
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 tiene dirección www.xxx.yyy.zzz
El 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" se ha agregado a sus repositorios
$ helm repo update Esperen mientras descargamos las últimas novedades de sus repositorios de gráficos...
...Actualización obtenida correctamente del repositorio de gráficos "nginx-stable".
...Actualización obtenida correctamente del repositorio de gráficos "jetstack".
Actualización completada. ¡Feliz Helming!
$ helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \ --version v1.9.1 --set installCRDs=true NOMBRE: cert-manager ÚLTIMA IMPLEMENTACIÓN: Jue 1 sep 2022 16:01:52 ESPACIO DE NOMBRES: cert-manager ESTADO: implementado REVISIÓN: 1 CONJUNTO DE PRUEBAS: Ninguna NOTAS: ¡cert-manager v1.9.1 se ha implementado correctamente!
Para comenzar a emitir certificados, deberá configurar un recurso ClusterIssuer o Issuer (por ejemplo, creando un emisor 'letsencrypt-staging').
Puede encontrar más información sobre los diferentes tipos de emisores y cómo configurarlos en nuestra documentación:
https://cert-manager.io/docs/configuration/
Para obtener información sobre cómo configurar cert-manager para aprovisionar automáticamente certificados para recursos de Ingress, consulte la documentación de `ingress-shim`:
https://cert-manager.io/docs/usage/ingress/
$ kubectl getployments --namespace cert-manager NOMBRE LISTO ACTUALIZADO DISPONIBLE ANTIGÜEDAD
cert-manager 1/1 1 1 4m30s
cert-manager-cainjector 1/1 1 1 4m30s
webhook del administrador de certificados 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 Clonando en 'kubernetes-ingress'...
remoto: Enumeración de objetos: 44979, listo.
remoto: Contando objetos: 100% (172/172), listo.
remoto: Comprimiendo objetos: 100% (108/108), listo.
Remoto: Total: 44979 (delta 87), reutilizados: 120 (delta 63), reutilizados en paquete: 44807
Objetos recibidos: 100% (44979/44979), 60,27 MiB | 27,33 MiB/s, listo.
Resolviendo deltas: 100% (26508/26508), listo.
$ cd ./kubernetes-ingress/ejemplos/ingress-recursos/ejemplo-completo
$ kubectl apply -f ./cafe.yaml
deployment.apps/coffee creado
service/coffee-svc creado
deployment.apps/tea creado
service/tea-svc creado
kubectl
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 getployments,services --namespace default NOMBRE LISTO ACTUALIZADO DISPONIBLE ANTIGÜEDAD
deployment.apps/coffee 2/2 2 2 69 s
deployment.apps/tea 3/3 3 3 3 68 s
NOMBRE TIPO IP DEL CLÚSTER IP EXTERNA PUERTO(S) ANTIGÜEDAD
service/coffee-svc IP del clúster 10.128.154.225 <ninguno> 80/TCP 68 s
service/kubernetes IP del clúster 10.128.0.1 <ninguno> 443/TCP 29 m
servicio/tea-svc ClusterIP 10.128.96.145 <ninguno> 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
tipo: Emisor de clúster
Metadatos:
Nombre: emisor-de-producto
Especificación:
Acme:
Correo electrónico: ejemplo@ejemplo.com
Servidor: https://acme-v02.api.letsencrypt.org/directorio
Referencia secreta de clave privada:
Nombre: clave de cuenta del emisor-de-producto
Solucionadores:
- http01:
Entrada:
Clase: nginx
EOF
Clusterissuer.cert-manager.io/emisor-de-producto creado
$ kubectl get clusterissuer NOMBRE LISTO EDAD
prod-issuer Verdadero 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 aplicação 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
tipo: Secreto
Metadatos:
Nombre: cloudflare-api-token-secret
Espacio de nombres: cert-manager
Tipo: Opaco
stringData:
api-token: <Token de API>
EOF
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
tipo: Emisor del clúster
Metadatos:
Nombre: emisor-del-producto
Especificación:
Acme:
Correo electrónico: ejemplo@ejemplo.com
Servidor: https://acme-v02.api.letsencrypt.org/directorio
Referencia secreta de clave privada:
Nombre: clave de cuenta del emisor-del-producto
Solucionadores:
- dns01:
Cloudflare:
Referencia secreta del token API:
Nombre: secreto del token de API de Cloudflare
clave: token de API
EOF
$ kubectl get clusterissuer NOMBRE LISTO EDAD
prod-issuer Verdadero 31m
Este es el punto hacia el cual hemos estado trabajando: la implementación del recurso Ingress para nuestra aplicação. Esto dirigirá el tráfico a la aplicação 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 tipo: Entrada
Metadatos:
Nombre: cafe-ingress
Anotaciones:
Cert-manager.io/cluster-issuer: prod-issuer
Acme.cert-manager.io/http01-edit-in-place: "true"
Especificación:
NombreClaseIngress: nginx
TLS:
Hosts:
Cert.example.com
SecretName: cafe-secret
Reglas:
Host: cert.example.com
http:
Rutas:
Ruta: /tea tipoDeRuta: Prefijo
backend:
servicio:
nombre: tea-svc
puerto:
número: 80
- ruta: /café
tipoDeRuta: Prefijo
backend:
servicio:
nombre: coffee-svc
puerto:
número: 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 creado
$ kubectl obtener certificados NOMBRE ...
Si está utilizando los CRD de NGINX, deberá utilizar el siguiente YAML de implementación para configurar su Ingress.
apiVersion: k8s.nginx.org/v1
tipo: Servidor Virtual
Metadatos:
Nombre: cafe
Especificaciones:
Host: cert.example.com
TLS:
Secreto: cafe-secret
Administrador de certificados:
Emisor de clúster: emisor de producción
Subprocesos:
Nombre: tea
Servicio: tea-svc
Puerto: 80
- nombre: café
servicio: coffee-svc
puerto: 80
rutas:
- ruta: /té
acción:
pase: té
- ruta: /café
acción:
pase: café
Una 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 creado
$ kubectl get VirtualServers NOMBRE ESTADO HOST IP PUERTOS EDAD
cafe Válido 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 Nombre: cafe-secret
Espacio de nombres: predeterminado
Etiquetas: <ninguna>
Anotaciones: cert-manager.io/nombres-alt: certificado.ejemplo.com
cert-manager.io/nombre-certificado: café-secreto
cert-manager.io/nombre-común: certificado.ejemplo.com
cert-manager.io/ip-sans:
cert-manager.io/grupo-emisor:
cert-manager.io/tipo-de-emisor: Emisor del clúster cert-manager.io/issuer-name : prod-issuer cert-manager.io/uri-sans : Tipo: kubernetes.io/tls Datos ==== 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 obtener secreto cafe-secret -o yaml
Pruebe 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
Dirección del servidor: 10.2.0.6:8080
Nombre del servidor: tea-5c457db9-l4pvq
Fecha: 02/sep/2022:15:21:06 +0000
URI: /tea
ID de solicitud: d736db9f696423c6212ffc70cd7ebecf
$ curl https://cert.example.com/coffee
Dirección del servidor: 10.2.2.6:8080
Nombre del servidor: coffee-7c86d7d67c-kjddk
Fecha: 02/sep/2022:15:21:10 +0000
URI: /café
ID de solicitud: 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.