Files
kubernetes/ingress.md
2025-08-17 12:56:18 +02:00

536 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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/Lets 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/Lets 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.