15 KiB
1. Cert-Manager: Gestión automática de certificados TLS en
Kubernetes
cert-manager es un componente esencial en entornos Kubernetes modernos. Se encarga de emitir y renovar automáticamente certificados TLS, eliminando la necesidad de gestionarlos manualmente. Esto es especialmente útil cuando los servicios se exponen a través de un Ingress Controller y se quiere TLS mediante Let's Encrypt.
¿Cómo funciona?
- Se integra con el API de Kubernetes.
- Usa recursos personalizados (
IssueryClusterIssuer) para definir cómo se emitirán los certificados. - Funciona con ACME (como Let's Encrypt) para obtener certificados automáticamente.
- Trabaja junto con los objetos
Ingresspara asegurar las rutas públicas de nuestros servicios. - Puede usar solvers
http01(habitualmente expuestos por el Ingress Controller) para verificar la propiedad del dominio.
2. Despliegue de cert-manager
Paso 1: Crear el namespace
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
kubectl apply -f namespace.yaml
Paso 2: Instalar cert-manager desde el repositorio oficial
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
Esto instalará los CRDs necesarios, así como los controladores en el namespace cert-manager.
3. Configurar los emisores de certificados
Issuer vs ClusterIssuer
Issuer: solo disponible en un namespace concreto.ClusterIssuer: disponible en todo el clú ster.
Aquí usamos ClusterIssuer para que cualquier Ingress de cualquier namespace pueda solicitar certificados.
Paso 3: Crear los ClusterIssuers
# clusterissuer-staging.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
email: xavor@hotmail.es
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
ingressClassName: traefik
# clusterissuer-prod.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
email: xavor@hotmail.es
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
ingressClassName: traefik
⚠️ Asegúrate de que
ingressClassNamecoincida con el del Ingress Controller instalado (comonginx,traefik,haproxy).
kubectl apply -f clusterissuer-staging.yaml
kubectl apply -f clusterissuer-prod.yaml
4. Uso de kustomize para aplicar en bloque
Puedes usar kustomize para aplicar todo desde un solo punto:
# kustomization.yaml
namespace: cert-manager
resources:
- clusterissuer-prod.yaml
- clusterissuer-staging.yaml
Y aplicarlo con:
kubectl apply -k .
5. Ingress-NGINX: Controlador de entrada HTTP/S para Kubernetes
Ingress-NGINX es uno de los controladores de entrada (Ingress Controller) más usados en Kubernetes. Se encarga de recibir peticiones HTTP y HTTPS desde el exterior del clú ster y redirigirlas al servicio correspondiente dentro del clú ster.
Es altamente configurable y permite gestionar el enrutamiento, certificados TLS, cabeceras, tiempos de espera, redirecciones, etc.
Arquitectura básica
- Se despliega como un
DaemonSeten los nodos del clú ster. - Expone los puertos 80 y 443 mediante un
ServicetipoNodePortoLoadBalancer(cuando se usa con MetalLB). - Requiere una configuración RBAC, una
IngressClass, y unConfigMappara parámetros adicionales.
Archivos usados en este despliegue
Estructura del repositorio:
.
├── configmap/configmap.yaml # Configuración del controlador
├── deployments/deployment.yaml # Despliegue como DaemonSet
├── ingressclass/ingressclass.yaml # Define la clase Ingress "nginx"
├── namespace.yaml # Namespace "ingress-nginx"
├── rbac/ # Roles y bindings necesarios
└── services/service.yaml # Service tipo NodePort
ℹ️ Este despliegue usa un
DaemonSetpara que el Ingress Controller se ejecute en todos los nodos. Esto es ideal cuando se usa con MetalLB para exponer los puertos 80/443 desde cada nodo.
Comando de despliegue
kubectl apply -k .
Esto desplegará el controlador en el namespace ingress-nginx y lo dejará listo para enrutar peticiones entrantes hacia los servicios del clúster.
Separacion del trafico en dos redes
Para separar el tráfico externo (Internet) del interno (VPN), el controlador ingress-nginx se publica con dos Services de tipo LoadBalancer apuntando al mismo DaemonSet de NGINX:
Estructura (repo kubernetes/ingress-nginx/):
configmap/configmap.yaml
deployments/deployment.yaml # DaemonSet del controller
ingressclass/ingressclass.yaml # IngressClass: nginx (controller: k8s.io/ingress-nginx)
namespace.yaml
rbac/*
services/
├─ service.yaml # LB público (IP en red 192.168.0.0/24)
└─ service-200.yaml # LB VPN (IP en red 192.168.200.0/24)
service.yaml→ IP pública (ej.:192.168.0.100). Aquí entra ACME/Let’s Encrypt y tráfico externo normal.service-200.yaml→ IP VPN (ej.:192.168.200.10). Solo accesible desde la VPN/CoreDNS interno.
Ambos Services seleccionan los mismos Pods del controller (
selector: app.kubernetes.io/name: ingress-nginx). Kubernetes permite múltiples Services por el mismo backend.
Ejemplo de services/service-200.yaml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-controller-200
namespace: ingress-nginx
spec:
type: LoadBalancer
loadBalancerIP: 192.168.200.10
# Preserva la IP de cliente en NGINX y evita hairpinning innecesario.
externalTrafficPolicy: Local
selector:
app.kubernetes.io/name: ingress-nginx
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
Uso desde los Ingress de las apps:
- Todos los Ingress usan la misma
ingressClassName: nginx. - El aislamiento se consigue con la lista blanca de orígenes, por ejemplo:
metadata: annotations: nginx.ingress.kubernetes.io/whitelist-source-range: "192.168.200.0/24,10.244.0.0/16,192.168.4.0/24" - ACME/Let’s Encrypt validará por el LB público (
service.yaml). - El LB VPN (
service-200.yaml) sirve a clientes internos (WireGuard) y al CoreDNS interno, que resuelve los FQDN a192.168.200.10.
Notas:
- Si dependes de la IP real del cliente en logs/reglas, usa
externalTrafficPolicy: Localen ambos Services (o al menos en el interno). Ajusta el firewall si procede.
6. Ingress-Traefik: Controlador ligero, moderno y compatible con CRDs
Traefik es un Ingress Controller muy versátil y fácil de extender. Además del soporte nativo para Ingress, también soporta su propio sistema basado en CRDs (IngressRoute, Middleware, etc.) y se integra muy bien con cert-manager.
Arquitectura básica
- Se despliega como un
Deployment, normalmente con un solo pod. - Expone los puertos HTTP/HTTPS mediante un
ServicetipoNodePortoLoadBalancer. - Lee su configuración desde un
ConfigMapque define el comportamiento del proxy.
Archivos usados en este despliegue
Estructura del repositorio:
.
├── configmaps/configmap.yaml # Configuración traefik.yml
├── deployments/deployment.yaml # Deployment con volumen del configmap
├── ingressclass.yaml # IngressClass "traefik"
├── namespace.yaml # Namespace "traefik"
├── rbac/ # Roles y ServiceAccount
└── services/service.yaml # Service tipo NodePort
Fragmento clave del deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: traefik
namespace: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik
containers:
- name: traefik
image: traefik:v3.0
args:
- --configFile=/config/traefik.yml
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: traefik-config
Comando de despliegue
kubectl apply -k .
Esto desplegará el controlador en el namespace traefik y lo dejará operativo como proxy HTTP/HTTPS para tus servicios.
7. Ingress-HAProxy: Controlador HTTP/S y TCP/UDP con soporte avanzado
HAProxy Ingress Controller es una alternativa robusta y flexible a NGINX y Traefik. Permite manejar no solo tráfico HTTP/S, sino también servicios TCP y UDP mediante recursos adicionales como TCPIngress y UDPIngress.
Instalación con Helm
Para instalarlo correctamente, es necesario tener Helm instalado:
# Agregar el repositorio oficial de HAProxy Ingress
helm repo add haproxy-ingress https://haproxy-ingress.github.io/charts
helm repo update
Luego, se puede instalar el controlador con los siguientes archivos:
helm install haproxy haproxy-ingress/haproxy-ingress \
--namespace haproxy-controller \
--create-namespace \
--values values.yaml
⚠️ A veces es necesario definir manualmente la
IngressClass, aunque se haya especificado envalues.yaml.
Archivo ingressclass.yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: haproxy
spec:
controller: haproxy.org/ingress-controller
kubectl apply -f ingressclass.yaml
Fragmento del archivo values.yaml
controller:
name: haproxy
ingressClass:
create: true
name: haproxy
default: false
service:
enabled: true
type: LoadBalancer
loadBalancerIP: 192.168.1.100
ports:
http:
port: 80
https:
port: 443
metrics:
enabled: true
extraArgs:
watch-ingress-classes: "true"
watch-ingress-without-class: "true"
disable-tcp-services: "false"
disable-udp-services: "false"
createCustomResources: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Esto genera un Service tipo LoadBalancer que puede integrarse con MetalLB para asignar una IP fija (en este caso, 192.168.1.100).
8. Ejemplo completo: VPN WireGuard con panel web y tráfico UDP con HAProxy
Este ejemplo demuestra el despliegue de un servicio WireGuard VPN con un panel de administración web (wg-easy), certificado TLS automático, y exposición de tráfico UDP mediante UDPIngress de HAProxy. El panel web podría estar expuesto con cualquier Ingress Controller, pero el tráfico UDP requiere el uso de HAProxy o un mecanismo equivalente.
La parte de almacenamiento (PVC) usa una
StorageClassllamadanfs-temp, cuya configuración está explicada en la guía de almacenamiento.
Estructura de manifiestos (namespace: vpn)
1️⃣ namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: vpn
2️⃣ pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wg-easy-config
namespace: vpn
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-temp
resources:
requests:
storage: 1Gi
3️⃣ deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wg-easy
namespace: vpn
spec:
replicas: 1
selector:
matchLabels:
app: wg-easy
template:
metadata:
labels:
app: wg-easy
spec:
containers:
- name: wg-easy
image: ghcr.io/wg-easy/wg-easy
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_MODULE"]
env:
- name: WG_HOST
value: "c2et.com"
- name: PASSWORD_HASH
value: "$2y$10$PqXdePb8yMvOsEgiuhS4KON8Thy541uAPOjV6nwczNaCcy9bdA9Jq"
- name: WG_PORT
value: "51820"
- name: WG_DEFAULT_ADDRESS
value: "192.168.200.x"
- name: WG_DEFAULT_DNS
value: "192.168.0.80"
ports:
- containerPort: 51820
protocol: UDP
- containerPort: 51821
protocol: TCP
volumeMounts:
- mountPath: /etc/wireguard
name: config
volumes:
- name: config
persistentVolumeClaim:
claimName: wg-easy-config
4️⃣ service.yaml
apiVersion: v1
kind: Service
metadata:
name: wg-easy
namespace: vpn
spec:
selector:
app: wg-easy
ports:
- name: vpn
port: 51820
protocol: UDP
targetPort: 51820
- name: panel
port: 51821
protocol: TCP
targetPort: 51821
5️⃣ udp-ingress.yaml
apiVersion: configuration.haproxy.org/v1alpha1
kind: UDPIngress
metadata:
name: wg-easy-udp
namespace: vpn
spec:
rules:
- port: 51820
backend:
serviceName: wg-easy
servicePort: 51820
6️⃣ ingress.yaml (panel web con TLS)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wg-easy-panel
namespace: vpn
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
haproxy-ingress.github.io/ssl-redirect: "true"
spec:
ingressClassName: haproxy
tls:
- hosts:
- wireguard.manabo.org
secretName: wg-easy-tls
rules:
- host: wireguard.manabo.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wg-easy
port:
number: 51821
✅ Aplicación en orden
kubectl apply -f namespace.yaml
kubectl apply -f pvc.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f udp-ingress.yaml
kubectl apply -f ingress.yaml
🧪 Comprobaciones
| Componente | Verificación |
|---|---|
| Web panel | https://wireguard.manabo.org |
| VPN UDP | Conexión desde cliente a c2et.com:51820 |
| Persistencia | Archivos de peers en /etc/wireguard se mantienen tras reinicio |
Próxima sección: ejemplos comunes para exponer servicios con Ingress y cert-manager.