En este artículo hablaré sobre cómo automatizar el pedido y la renovación de certificados de Let's Encrypt (y no solo) para Ingress en Kubernetes utilizando el complemento cert-manager. Pero comenzaré con una breve introducción a la esencia del problema.Un poco de programa educativo
El protocolo HTTP, desarrollado a principios de los años 90 del siglo pasado, entró en nuestra vida cotidiana con tanta fuerza que es difícil imaginar al menos un día sin usarlo. Sin embargo, por sí solo, ni siquiera proporciona un nivel mínimo de seguridad al intercambiar información entre un usuario y un servidor web. HTTPS ("S" - seguro) viene al rescate: utilizando el paquete de los datos transmitidos en SSL / TLS, este protocolo ha demostrado su eficacia en la protección de la información contra la intercepción y es promovido activamente por la industria.Por ejemplo, Google se ha adherido a 2014Posición "HTTPS en todas partes" e incluso reduce la prioridad de los sitios sin ella en los resultados de búsqueda. Esta "propaganda" tampoco pasa por alto a los consumidores comunes: los navegadores modernos advierten a sus usuarios sobre la presencia y la corrección de los certificados SSL para los sitios visitados.

El costo de un certificado para un sitio personal comienza desde decenas de dólares. No siempre comprarlo está justificado y es apropiado. Afortunadamente, desde finales de 2015, hay disponible una alternativa gratuita en forma de certificados Let's Encrypt (LE). Este proyecto sin fines de lucro fue creado por entusiastas de Mozilla para cubrir la mayoría de los sitios web con encriptación.La autoridad de certificación emite certificados validados por el dominio(el más simple entre los disponibles en el mercado) con un período de validez de 90 días, y durante muchos años ha sido posible emitir el llamado certificado comodín para varios subdominios.Para obtener el certificado, el sitio utiliza los algoritmos descritos en el protocolo del Entorno de administración de certificados automatizado (ACME), creado específicamente para Let's Encrypt. Al usarlo, la confirmación de la propiedad del dominio se lleva a cabo mediante solicitudes mediante la colocación de un código HTTP específico (llamado HTTP-01 ) o la instalación de registros DNS (DNS-01) ; a continuación se brindará más información sobre ellos.Gerente de certificacion
Cert-manager es un proyecto especial para Kubernetes, que es un conjunto de CustomResourceDefinitions (de ahí la limitación de la versión mínima admitida de K8s - v1.12) para la configuración de CA (autoridades de certificación) y el pedido directo de certificados. Instalar CRD en un clúster es trivial y se reduce a usar un archivo YAML:kubectl create ns cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.13.0/cert-manager.yaml
( También existe la posibilidad de instalar usando Helm.)Para iniciar el procedimiento de pedido, los recursos de las autoridades de certificación (CA) deben declararse en el clúster: Issuer
o ClusterIssuer
, que se utilizan para firmar el CSR (solicitudes de emisión de certificados). La diferencia entre el primer recurso y el segundo está en el alcance:Issuer
se puede usar dentro del mismo espacio de nombres,ClusterIssuer
es un objeto de clúster global.
Practica con cert-manager
No 1. Certificado autofirmado
Comencemos con el caso más simple: pedir un certificado autofirmado. Esta opción es bastante común, por ejemplo, para entornos de prueba dinámicos para desarrolladores o en el caso de utilizar un equilibrador externo que finalice el tráfico SSL.El recurso Issuer
se verá así:apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: selfsigned
spec:
selfSigned: {}
Y para emitir un certificado, es necesario describir el recurso Certificate
, dónde se indica cómo emitirlo (consulte la sección a issuerRef
continuación) y dónde se encuentra la clave privada (campo secretName
). Después de eso, Ingress tendrá que referirse a esta clave (ver sección tls
c spec
):---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: selfsigned-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: selfsigned
commonName: "yet-another.website"
dnsNames:
- "yet-another.website"
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: app
spec:
tls:
- hosts:
- "yet-another.website"
secretName: tls-secret
rules:
- host: "yet-another.website"
http:
paths:
- path: /
backend:
serviceName: app
servicePort: 8080
Unos segundos después de agregar estos recursos al clúster, se emitirá el certificado. Puede ver el informe correspondiente en la salida del comando:kubectl -n app describe certificate selfsigned-crt
...
Normal GeneratedKey 5s cert-manager Generated a new private key
Normal Requested 5s cert-manager Created new CertificateRequest resource "selfsigned-crt-4198958557"
Normal Issued 5s cert-manager Certificate issued successfully
Si observa el recurso secreto en sí, entonces contiene:- clave privada
tls.key
, - certificado raíz
ca.crt
, - Nuestro certificado autofirmado
tls.crt
.
El contenido de estos archivos se puede ver usando la utilidad openssl
, por ejemplo, así:kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:01:59 2020 GMT
notAfter=May 10 21:01:59 2020 GMT
issuer=O = cert-manager, CN = yet-another.website
Vale la pena señalar que, en el caso general, los Issuer
clientes conectados no confiarán en el certificado emitido con este . La razón es simple: no tiene una CA (consulte la nota en el sitio web del proyecto ) . Para evitar esto, debe especificar en la Certificate
ruta al archivo secreto donde está contenido ca.crt
. Puede ser una organización de CA corporativa: para firmar los certificados emitidos para Ingress con una clave que ya se utiliza para las necesidades de otros servicios de servidor / sistemas de información.No 2. Cifremos el certificado con validación HTTP
Para emitir certificados LE, como se mencionó anteriormente, hay dos tipos de confirmación de propiedad de dominio disponibles : HTTP-01 y DNS-01.El primer enfoque (HTTP-01) es lanzar un pequeño servidor web con una implementación separada, que enviará a Internet a través del enlace <YOUR_DOMAIN > /. Conocidos / acme-challenge / <TOKEN> algunos datos solicitados al servidor de certificación. Por lo tanto, este método implica la disponibilidad de Ingress desde el exterior en el puerto 80 y la resolución del registro DNS del dominio en direcciones IP públicas.La segunda opción para verificar el certificado emitido (DNS-01) proviene dede tener una API para el servidor DNS que aloja los registros de dominio. El emisor, utilizando los tokens indicados, crea registros TXT en el dominio, que el servidor ACME luego recibe durante la confirmación. Entre los proveedores de DNS oficialmente compatibles están CloudFlare, AWS Route53, Google CloudDNS y otros, incluida su propia implementación ( acme-dns ).Nota : Let's Encrypt tiene límites bastante estrictos en las solicitudes a los servidores ACME. Para no entrar en una prohibición larga, se recomienda utilizar el tipo de certificado de ensayo de letencrypt para la depuración (la diferencia es solo en el servidor ACME).Entonces, describimos los recursos:apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: le-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: letsencrypt
commonName: yet-another.website
dnsNames:
- yet-another.website
Tenga en cuenta que la dirección del servidor provisional se especifica como server
y acme
(c Issuer
) . Reemplazarlo con una batalla será posible más tarde. Aplicando esta configuración, rastreamos toda la ruta del pedido:- La creación
Certificate
generó un nuevo recurso CertificateRequest
:
kubectl -n app describe certificate le-crt
...
Created new CertificateRequest resource "le-crt-1127528680"
- En su descripción, una marca en la creación
Order
:
kubectl -n app describe certificaterequests le-crt-1127528680
…
Created Order resource app/le-crt-1127528680-1805948596
- Se
Order
describe con qué parámetros de los pases de prueba y cuál es su estado actual es. Dicha verificación es realizada por el recurso Challenge
:
kubectl -n app describe order le-crt-1127528680-1805948596
…
Created Challenge resource "le-crt-1127528680-1805948596-1231544594" for domain "yet-another.website"
- Finalmente, los detalles de este recurso contienen información sobre el estado del escaneo en sí:
kubectl -n app describe challenges le-crt-1127528680-1805948596-1231544594
...
Reason: Successfully authorized domain
...
Normal Started 2m45s cert-manager Challenge scheduled for processing
Normal Presented 2m45s cert-manager Presented challenge using http-01 challenge mechanism
Normal DomainVerified 2m22s cert-manager Domain "yet-another.website" verified with "http-01" validation
Si se han cumplido todas las condiciones (es decir, se puede acceder al dominio desde el exterior, no hay prohibición de la LE ...): en menos de un minuto, se emitirá el certificado. Si tiene éxito, describe certificate le-crt
aparecerá un registro en la salida Certificate issued successfully
.Ahora puede cambiar de forma segura la dirección del servidor para combatir ( https://acme-v02.api.letsencrypt.org/directory
) y volver a ordenar los certificados reales ya firmados Fake LE Intermediate X1
, pero no Let's Encrypt Authority X3
.Para hacer esto, primero debe eliminar el recurso Certificate
: de lo contrario, no se activarán los procedimientos de pedido, porque el certificado ya existe y es relevante. Eliminar un secreto llevará a su devolución inmediata con un mensaje a describe certificate
: Normal PrivateKeyLost 44s cert-manager Lost private key for CertificateRequest "le-crt-613810377", deleting old resource
Queda por aplicar el manifiesto de "combate" para Issuer
el ya descrito anteriormente Certificate
(no ha cambiado):apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
Después de recibir el mensaje Certificate issued successfully
, describe
verifíquelo:kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -issuer
notBefore=Feb 10 21:11:48 2020 GMT
notAfter=May 10 21:11:48 2020 GMT
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
Numero 3. Comodín LE con validación de DNS
Complicaremos la tarea escribiendo un certificado de inmediato en todos los subdominios del sitio y utilizando esta vez la verificación de DNS (a través de CloudFlare).Primero, obtenga el token en el panel de control de CloudFlare para trabajar a través de la API:- Perfil → Tokens API → Crear token.
- Establezca los permisos de la siguiente manera:
- Permisos:
- Zone - DNS - Editar
- Zona - Zona - Leer
- Recursos de zona:
- Copie el token recibido después de guardar (por ejemplo :)
y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
.
Cree un secreto en el que se almacenará este token y consúltelo en el emisor:apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token
type: Opaque
stringData:
api-token: y_JNkgQwkroIsflbbYqYmBooyspN6BskXZpsiH4M
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt
solvers:
- dns01:
cloudflare:
email: my-cloudflare-acc@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: le-crt
spec:
secretName: tls-secret
issuerRef:
kind: Issuer
name: letsencrypt
commonName: yet-another.website
dnsNames:
- "yet-another.website"
- "*.yet-another.website"
(¡No olvide usar la puesta en escena si está experimentando!)Pasemos por el proceso de confirmar la propiedad del dominio:kubectl -n app describe challenges.acme.cert-manager.io le-crt-613810377-1285319347-3806582233
...
Status:
Presented: true
Processing: true
Reason: Waiting for dns-01 challenge propagation: DNS record for "yet-another.website" not yet propagated
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 54s cert-manager Challenge scheduled for processing
Normal Presented 53s cert-manager Presented challenge using dns-01 challenge mechanism
Aparecerá un registro TXT en el panel:
... y después de un tiempo el estado cambiará a:Domain "yet-another.website" verified with "dns-01" validation
Asegúrese de que el certificado sea válido para cualquier subdominio:kubectl -n app get secret tls-secret -ojson | jq -r '.data."tls.crt"' | base64 -d | openssl x509 -dates -noout -text |grep DNS:
DNS:*.yet-another.website, DNS:yet-another.website
La validación por DNS, por regla general, no ocurre rápidamente, ya que la mayoría de los proveedores de DNS tienen un período de actualización de datos que muestra cuánto tiempo transcurre desde el momento en que el registro DNS se cambia a la actualización real de todos los servidores DNS del proveedor. Sin embargo, el estándar ACME también proporciona una combinación de dos opciones de verificación, que pueden usarse para acelerar la recepción de un certificado para el dominio principal. En este caso, la descripción Issuer
será la siguiente:apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: letsencrypt
spec:
acme:
server: https:
privateKeySecretRef:
name: letsencrypt
solvers:
- selector:
dnsNames:
- "*.yet-another.website"
dns01:
cloudflare:
email: my-cloudflare-acc@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
- selector:
dnsNames:
- "yet-another.website"
http01:
ingress:
class: nginx
Si aplica esta configuración, se crearán dos recursos Challenge
:kubectl -n app describe orders le-crt-613810377-1285319347
…
Normal Created 3m29s cert-manager Created Challenge resource "le-crt-613810377-1285319347-3996324737" for domain "yet-another.website"
Normal Created 3m29s cert-manager Created Challenge resource "le-crt-613810377-1285319347-1443470517" for domain "yet-another.website"
Número 4. Uso de anotaciones especiales de entrada
Además de la ruta directa para crear certificados en cert-manager, es posible usar un componente llamado ingress-shim y no crear recursos explícitamente Certificate
. La idea es que con la ayuda de anotaciones especiales de Ingress, el certificado se ordenará automáticamente utilizando el indicado en ellos Issuer
. El resultado es aproximadamente el siguiente recurso de Ingress:apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- "yet-another.website"
secretName: tls-secret
rules:
- host: "yet-another.website"
http:
paths:
- path: /
backend:
serviceName: app
servicePort: 8080
Para un funcionamiento correcto, solo la presencia del Emisor es suficiente aquí, es decir, crear una entidad menos.Además, hay una anotación obsoleta kube-lego - kubernetes.io/tls-acme: "true"
, - que requiere un trabajo Issuer
predeterminado al instalar cert-manager usando los parámetros de Helm (o en las opciones de inicio del contenedor del administrador).Nosotros en la compañía no utilizamos estas opciones y no podemos aconsejarlas debido a la opacidad de los enfoques utilizados para pedir certificados SSL (y al mismo tiempo a los diversos problemas que surgen ), pero aún así decidimos mencionar en el artículo para obtener una imagen más completa.En lugar de una conclusión
Mediante simples manipulaciones con CRD, aprendimos cómo escribir certificados SSL renovables, autofirmados y gratuitos del proyecto Let's Encrypt para sitios de dominio lanzados como parte de Ingresss en clústeres de Kubernetes.El artículo proporciona ejemplos para resolver los problemas más comunes en nuestra práctica. Sin embargo, las funciones de cert-manager no se limitan a las características descritas anteriormente. En el sitio web de la utilidad, puede encontrar ejemplos de cómo trabajar con otros servicios, por ejemplo, un paquete con Vault o el uso de centros emisores externos (emisores).PD
Lea también en nuestro blog: