Certificados SSL gratuitos y automáticos en Kubernetes K3s usando Cert Manager
Obtenga certificados SSL gratuitos y automáticos usando el administrador de certificados y Let's Encrypt
En el escenario actual, los certificados SSL son la parte más importante de la implementación de una aplicación en Internet. Son solo los atributos más importantes los que determinan si sus sitios web son seguros o no.
Este símbolo transmite a su cliente que el sitio web que está visitando es seguro, protegido y verificado. Entonces, ¿cómo logra realmente HTTPS en su sitio web?
HTTP + SSL = HTTPS.
Obtener un certificado SSL no es tan fácil. Además, también es caro. En el mundo actual, donde Kubernetes está predominantemente en todas partes.
Y millones de recursos de Ingress en Kubernetes, se vuelve realmente difícil obtener una cantidad tan grande de certificados, monitorearlos y rotarlos cada vez.
Esto sería un Dolor de Cabeza para nosotros los DevOps. ¿Qué sucede si les digo que existe una herramienta que podría obtener certificados SSL gratuitos y rotarlos automáticamente cuando caduquen?
Aquí viene Cert-Manager
Cert-Manager automatiza el aprovisionamiento de certificados dentro de los clústeres de Kubernetes. Proporciona un conjunto de recursos personalizados para emitir certificados y adjuntarlos a los servicios.
Uno de los casos de uso más comunes es proteger las aplicaciones web y las API con certificados SSL de Let's Encrypt . Aquí se explica cómo agregar Cert-Manager a su clúster, configurar un emisor de certificados de Let's Encrypt y adquirir un certificado para pods expuestos a través de un Ingress .
¿De qué se trata toda la historia?
Comprender los conceptos de cert-manager Comprender varios desafíos de ACME (DNS01, HTTP01) Crear un certificado de Let's Encrypt Staging y finalmente obtenga el certificado de Let's Encrypt Production.
Crear certificado con cert-manager
Cert Manager admite la solicitud de certificados de servidores ACME (Entorno de gestión de certificados automatizado). Un ejemplo de estos servidores ACME es Let's Encrypt. El protocolo ACME admite varios mecanismos.
- HTTP01
- DNS01
HTTP01 es relativamente más fácil en comparación con el mecanismo DNS01. Pero el mecanismo DNS01 es más seguro que HTTP01.
En el desafío DNS01 , la propiedad de su dominio se prueba cuando el administrador de certificados puede crear con éxito un registro TXT en su zona alojada.
En HTTP01, la propiedad de su dominio se prueba cuando el administrador de certificados puede garantizar con éxito que un archivo está presente en el dominio en una ruta determinada.
Let's Encrypt tiene 2 URL de servidor ACME
Ambiente Staging acme-staging-v02.api.letsencrypt.org/direct..
Producción: acme-v02.api.letsencrypt.org/directory
Hay algunos límites de tasa restringidos en el servidor de producción (Ej: Certificados por dominio registrado -> 50 por semana, etc.).
Por lo tanto, siempre se recomienda realizar cualquier tipo de prueba en la URL de prueba "staging" y luego pasar al entorno de producción.
Muy bien, suficiente de la charla. Pasemos ahora a la acción.
¡Suena bien! ¿Qué necesito?
Deberías cumplir con estos pre-requisitos:
- Tener un clúster de kubernetes funcional con nginx como ingress-controller
- Tener helm instalado ò instalar con los manifest de la pagina oficial de cert-manager
- Tener un dominio que apunte hacia el clúster , estoy usando nip.io
- Descarga mi Repo github
- Ganas de aprender de rompercosas y desafiar al SSL 🦸♀️
¡Empecemos!
Primero necesitamos añadir el repositorio de helm para cert-manager:
helm repo add jetstack https://charts.jetstack.io
Siempre validen la version del helm chart en su web oficial
$ helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.7.1 \
Instalando con kubectl
$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.7.1/cert-manager.crds.yaml
Dale unos segundos de margen para que termine y deberías ver un mensaje de SUCCESS con éxito.
Recuerden que necesitamos tener instalado nginx controller lo puede instalar desde su pagina oficial Website
Pero Les traigo una Documentación que realiza Gisela Torres , muy bien explicado con relación al ingress-controller con Nginx ademas les explica sobre el dominio "temporal" nip.io
Configurar un Ingress Controller con Nginx, Helm y nip.io
Acá la documentación
Luego que revisen los pasos del ingress-controller son muy simples.
Ahora si vamos a lo que venimos!!!
Primer Paso realicen un chequeo si tienen el ingress.controller nginx
root@localhost:~# k get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller-admission ClusterIP 10.43.147.229 <none> 443/TCP 119m
ingress-nginx-controller LoadBalancer 10.43.194.181 45.79.215.27 80:30173/TCP,443:31766/TCP 119m
Si vieron la documentación voy a tomar en cuenta el External-IP para la configuracion de mi dominio nip.io y el cert-manager
Ahora vamos a crear el ClusterIssuer
Podemos usar tanto un Issuer como un ClusterIssuer (namespace vs cluster-scoped). Es exactamente lo mismo, solo cambia el scope. El Issuer/ClusterIssuer representa la CA de la que queremos obtener el nuevo certificado, en este caso usaremos LetsEncrypt.
Fíjate a continuación que el objeto ClusterIssuer realmente es muy simple. Tanto el nombre como el nombre del secret pueden ser lo que tú quieras:
Recuerden dejo el repo en github
Les voy a mostrar la configuracion tanto para ambiente de prueba como la de producción
cluster-issuer-staging.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
namespace: default
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: <YOUR_EMAIL>
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- selector: {}
http01:
ingress:
class: nginx
cluster-issuer-production.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-production
namespace: default
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <YOUR_EMAIL>
privateKeySecretRef:
name: letsencrypt-production
solvers:
- selector: {}
http01:
ingress:
class: nginx
📌 Importante: El campo email, debe ser un email real no un example@example.com, porque sino les dará error
Aplicamos con:
kubectl apply -f cluster-issuer-staging.yaml
y validamos si se aplico y en que estado se encuentra:
root@localhost:~# k get ClusterIssuer -A
NAME READY AGE
letsencrypt-production True 111m
letsencrypt-staging True 111m
Si aplicas ambos, asegura que todos tengan status TRUE
Ahora vamos a crear el certificado
certificate-staging.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ssl-cert-staging
namespace: default
spec:
secretName: ssl-cert-staging
issuerRef:
name: letsencrypt-staging
kind: ClusterIssuer
commonName: <YOUR_IP>.nip.io
dnsNames:
- <YOUR_IP>.nip.io
certificate-production.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ssl-cert-production
namespace: default
spec:
secretName: ssl-cert-production
issuerRef:
name: letsencrypt-production
kind: ClusterIssuer
commonName: <YOUR_IP>.nip.io
dnsNames:
- <YOUR_IP>.nip.io
Aca dependiendo cual certificado voy aplicar asegura que no tenga ambos el mismo el dominio por ejemplo en staging podemos usar otro un traefi.me o algo o dominio ya personalizando
para el caso de este Hand-On estoy trabajando con el del produccion
kubectl apply -f certificate-production.yaml
Asi que reviso el status
root@localhost:~# k get Certificate -A
NAMESPACE NAME READY SECRET AGE
default ssl-cert-production True ssl-cert-production 110m
Todo esta muy bien,!! yes, por suerte, pero todo puede pasar, así que siempre tengan a la mano
Kubectl describe xxxxx es un gran aliado
Ya tenemos los 2 primeros pasos cubiertos
Vamos a levantar una aplicación bastante basic un nginx
Vamos a hacer un deploy muy simple con un nginx, lo expondremos como servicio y después crearemos un ingress. Así pues, vamos allá:
kubectl create deployment nginx --image nginx:alpine
kubectl expose deployment nginx --port 80 --target-port 80
Ahora crearemos un nuevo objeto ingress para hacer accesible el servicio a través del ingress controller. Recuerda reemplazar a continuación las ocurrencias del host con tu propio dominio:
ingress.nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
#cert-manager.io/cluster-issuer: letsencrypt-production
kubernetes.io/ingress.class: "nginx"
labels:
app: nginx
name: nginx
namespace: default
spec:
rules:
- host: <YOUR_IP>.nip.io
http:
paths:
- backend:
service:
name: nginx
port:
number: 80
path: /
pathType: Prefix
#Descomenta esto despues que este aplicado el certificado
# tls:
# - hosts:
# - <YOUR_IP>.nip.io
# secretName: ssl-cert-production
Aplicamos el nuevo manifest:
kubectl apply -f ingress-nginx.yaml
Si ahora intentas acceder al host que has definido en el ingress verás un mensaje de error conforme tu certificado no es válido
Ahora para poder solventarlo tenemos que apuntar nuestro ingress al certificado que previamente y toca modificar el ingress-nginx.yaml
cert-manager.io/cluster-issuer: letsencrypt-production
y quedaria de esta manera el ingress-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
kubernetes.io/ingress.class: "nginx"
labels:
app: nginx
name: nginx
namespace: default
spec:
rules:
- host: <YOUR_IP>.nip.io
http:
paths:
- backend:
service:
name: nginx
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- <YOUR_IP>.nip.io
secretName: ssl-cert-production
Aplicamos de nuevo manifest:
kubectl apply -f ingress-nginx.yaml
Aplica el ingress de nuevo (o guarda los cambios si estás haciendo un edit) y revisa el estado del objeto «cert». Deberías ver algo así:
root@localhost:~# kubectl get cert -n default
NAME READY SECRET AGE
ssl-cert-production False ssl-cert-production 119m
Luego de un minuto pasa a True
root@localhost:~# kubectl get cert -n default
NAME READY SECRET AGE
ssl-cert-production True ssl-cert-production 119m
Es importante si pasa mas de 3 min y mantiene el FALSE hay algo mal, recuerden Kubectl describe es el mejor aliado
… EUREKA ¡hemos conseguido el certificado!
Haz un test refrescando la página o abriendo una nueva pestaña y deberías ver el mensaje de bienvenida de Nginx. Si inspeccionas el certificado, verás que efectivamente éste ha sido expedido por LetsEncrypt:
root@localhost:~# curl -kv https://45.79.215.27.nip.io
* Trying 45.79.215.27:443...
* Connected to 45.79.215.27.nip.io (45.79.215.27) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
Que genialidad
Que pasa si tenemos problemas con los Certificados Troubleshooting
Si presenta problemas con cert-manager, su mejor amigo es kubectl describe, esto le brindará información sobre los recursos, así como sobre eventos recientes. No se recomienda utilizar los registros, ya que son bastante detallados y solo deben consultarse si los siguientes pasos no brindan ayuda.
Documentación Adicional que recomiendo:
Configurar un Ingress Controller con Nginx, Helm y nip.io
Linode cuenta con un cupon de descuento de 100$ asi que podemos hacer mucho labs alli linode.com/es
Y viendo que no rompimos nada dejo esto por acá
Que vimos hoy:
- Como instalar cert-manager
- Como configurar un nuevo ClusterIssuer
- Como expedir de forma sencilla un certificado
Vieron que es un proceso simple, ¿verdad?
También hay otras formas de validar DNS pero son temas mas avanzados y esas aguas hay que llevarlos con calma, lo vemos luego
Recuerden seguirme en mis redes sociales busquen como @295devops
y toda consulta en linkedin
Y para no olvidar le dejo unos pasos en vídeo por si no me entendieron en toda la historia xD