## 1. Cert-Manager: Gestión automática de certificados TLS en ## Kubernetes [`cert-manager`](https://cert-manager.io/) 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 (`Issuer` y `ClusterIssuer`) 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 `Ingress` para 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 ```yaml # namespace.yaml apiVersion: v1 kind: Namespace metadata: name: cert-manager ``` ```bash kubectl apply -f namespace.yaml ``` --- ### Paso 2: Instalar cert-manager desde el repositorio oficial ```bash 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 ```yaml # 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 ``` ```yaml # 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 `ingressClassName` coincida con el del Ingress Controller instalado (como `nginx`, `traefik`, `haproxy`). ```bash 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: ```yaml # kustomization.yaml namespace: cert-manager resources: - clusterissuer-prod.yaml - clusterissuer-staging.yaml ``` Y aplicarlo con: ```bash 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 `DaemonSet` en los nodos del clú ster. * Expone los puertos 80 y 443 mediante un `Service` tipo `NodePort` o `LoadBalancer` (cuando se usa con MetalLB). * Requiere una configuración RBAC, una `IngressClass`, y un `ConfigMap` para 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 `DaemonSet` para 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 ```bash 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`** ```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: ```yaml 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 a `192.168.200.10`. **Notas:** - Si dependes de la IP real del cliente en logs/reglas, usa `externalTrafficPolicy: Local` en 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 `Service` tipo `NodePort` o `LoadBalancer`. * Lee su configuración desde un `ConfigMap` que 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` ```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 ```bash 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: ```bash # 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: ```bash 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 en `values.yaml`. ### Archivo `ingressclass.yaml` ```yaml apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: haproxy spec: controller: haproxy.org/ingress-controller ``` ```bash kubectl apply -f ingressclass.yaml ``` ### Fragmento del archivo `values.yaml` ```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 `StorageClass` llamada `nfs-temp`, cuya configuración está explicada en la guía de almacenamiento. ### Estructura de manifiestos (namespace: `vpn`) #### 1️⃣ namespace.yaml ```yaml apiVersion: v1 kind: Namespace metadata: name: vpn ``` #### 2️⃣ pvc.yaml ```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 ```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 ```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 ```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) ```yaml 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 ```bash 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](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.