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" 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.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" 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 created
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 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 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
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 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 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: coffee
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 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 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
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.