🔐 SSL/TLS con Let's Encrypt y Cloudflare
Certificados SSL gratuitos con renovación automática usando DNS-01 challenge (funciona con proxy naranja de Cloudflare).
Índice
- Instalar Certbot
- Configurar DNS-01 con Cloudflare
- Emitir Certificados
- Renovación Automática
- Certificados Wildcard
- Configuración de Cloudflare
- Comandos Útiles
- Troubleshooting
Instalar Certbot
Instalación básica
Verificar instalación
Configurar DNS-01 con Cloudflare
¿Por qué DNS-01?
Ventajas sobre HTTP-01:
- ✅ Funciona con proxy naranja de Cloudflare
- ✅ No depende de /.well-known/acme-challenge/
- ✅ Permite certificados wildcard (*.ejemplo.com)
- ✅ No requiere configurar reglas especiales en Cloudflare
Funcionamiento: 1. Certbot pide un certificado a Let's Encrypt 2. Let's Encrypt solicita crear un registro TXT en el DNS 3. Certbot usa la API de Cloudflare para crear el registro 4. Let's Encrypt verifica el registro TXT 5. Certificado emitido ✅
Instalar plugin de Cloudflare
Crear token de API en Cloudflare
Solo necesitas hacer esto UNA VEZ:
- Ve a Cloudflare Dashboard
- Click en tu perfil (esquina superior derecha)
- My Profile → API Tokens → Create Token
- Usa la plantilla "Edit zone DNS"
- Configura:
- Permissions:
Zone → DNS → Edit - Zone Resources:
Specific zone → tu-dominio.com - Continue to summary → Create Token
- Copia el token (solo se muestra una vez)
Guardar credenciales en el VPS
# Crear directorio seguro
sudo install -d -m 0700 /root/.secrets
# Crear archivo de credenciales
sudo bash -c 'cat > /root/.secrets/cf.ini <<EOF
dns_cloudflare_api_token = TU_TOKEN_AQUI
EOF'
# Asegurar permisos
sudo chmod 600 /root/.secrets/cf.ini
⚠️ IMPORTANTE: Si alguna vez muestras/pegas el token en claro, revócalo inmediatamente y crea uno nuevo.
Verificar archivo
Debería mostrar:
Emitir Certificados
Primer certificado (ejemplo: labs.josejordan.dev)
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cf.ini \
--cert-name labs.josejordan.dev \
-d labs.josejordan.dev
Durante el proceso: - Email: Introduce tu email para notificaciones de renovación - Términos de servicio: Acepta (Y) - Newsletter EFF: Opcional (N)
Si ya tienes un certificado HTTP-01 y quieres migrarlo
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cf.ini \
--cert-name labs.josejordan.dev \
-d labs.josejordan.dev \
--force-renewal
El flag --force-renewal fuerza la renovación inmediata.
Verificar que usa DNS-01
sudo grep -E 'authenticator|dns_cloudflare_credentials' \
/etc/letsencrypt/renewal/labs.josejordan.dev.conf
Resultado esperado:
Ubicación de certificados
Los certificados se guardan en:
/etc/letsencrypt/live/labs.josejordan.dev/
├── fullchain.pem # Certificado + cadena (usa este en Nginx)
├── privkey.pem # Clave privada (usa este en Nginx)
├── cert.pem # Solo certificado
└── chain.pem # Solo cadena
Usar en Nginx
server {
listen 443 ssl http2;
server_name labs.josejordan.dev;
ssl_certificate /etc/letsencrypt/live/labs.josejordan.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/labs.josejordan.dev/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ... resto de configuración
}
Renovación Automática
Certificados de Let's Encrypt
- Validez: 90 días
- Renovación automática: Cada 60 días (con margen de 30 días)
- Timer:
certbot.timerse encarga automáticamente
Verificar timer de renovación
# Ver estado del timer
sudo systemctl status certbot.timer
# Ver próxima ejecución
sudo systemctl list-timers | grep certbot
Resultado esperado:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Wed 2025-10-30 12:00:00 UTC 10h left - - certbot.timer certbot.service
Probar renovación (dry-run)
Esto NO renueva los certificados, solo verifica que funcionaría:
Si todo está bien, deberías ver:
Renovar manualmente (si es necesario)
Ver logs de renovación
# Ver logs de certbot
sudo journalctl -u certbot -f
# Ver logs de Let's Encrypt
sudo tail -f /var/log/letsencrypt/letsencrypt.log
Certificados Wildcard
¿Qué es un certificado wildcard?
Un solo certificado válido para todos los subdominios:
- labs.josejordan.dev ✅
- api.josejordan.dev ✅
- blog.josejordan.dev ✅
- cualquier-cosa.josejordan.dev ✅
Solo funciona con DNS-01 challenge.
Emitir certificado wildcard
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cf.ini \
--cert-name wildcard.josejordan.dev \
-d '*.josejordan.dev' -d josejordan.dev \
--keep-until-expiring
Nota: Incluye -d josejordan.dev para que el dominio raíz también funcione.
Usar wildcard en Nginx
En cada virtual host que quieras usar el wildcard:
server {
listen 443 ssl http2;
server_name api.josejordan.dev;
# Usar certificado wildcard
ssl_certificate /etc/letsencrypt/live/wildcard.josejordan.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/wildcard.josejordan.dev/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# ... resto de configuración
}
Ventajas y desventajas
✅ Ventajas: - Un solo certificado para todos los subdominios - Más fácil de gestionar - Menos renovaciones
❌ Desventajas:
- Si se compromete, afecta a todos los subdominios
- No incluye subdominios de subdominios (sub.labs.josejordan.dev)
Recomendación: Usar certificados individuales para producción, wildcard para staging/desarrollo.
Configuración de Cloudflare
Modo SSL/TLS
Ve a: SSL/TLS → Overview
Selecciona: Full (strict)
Modos disponibles:
- Off: Sin SSL (no recomendado)
- Flexible: Cloudflare ↔ Visitante HTTPS, Cloudflare ↔ Servidor HTTP (inseguro)
- Full: Cloudflare ↔ Servidor HTTPS, pero no valida certificado
- Full (strict): ✅ HTTPS end-to-end con validación (recomendado)
Opciones adicionales (recomendadas)
SSL/TLS → Edge Certificates: - ✅ Always Use HTTPS: ON (fuerza HTTPS) - ✅ HTTP Strict Transport Security (HSTS): Activar cuando todo funcione - ✅ Minimum TLS Version: TLS 1.2 o superior - ✅ Opportunistic Encryption: ON - ✅ TLS 1.3: ON - ✅ Automatic HTTPS Rewrites: ON
Speed → Optimization: - ✅ HTTP/2: ON - ✅ HTTP/3 (with QUIC): ON - ✅ Brotli: ON
Cache Rules para subdominios dinámicos
Si tu subdominio sirve contenido dinámico (APIs, apps), configura bypass de caché.
Ve a: Caching → Cache Rules → Create Rule
Configuración:
- Rule name: Bypass cache - labs.josejordan.dev
- When incoming requests match:
- Field: Hostname
- Operator: equals
- Value: labs.josejordan.dev
- Then:
- Cache eligibility: Bypass cache
- Save
Repite para cada subdominio dinámico.
Workers: Evitar comodines
⚠️ IMPORTANTE: No uses rutas de Worker tipo *.josejordan.dev/*
Esto intercepta todos los subdominios y puede romper tu configuración.
✅ Correcto:
❌ Incorrecto:
Purgar caché de un subdominio
Si haces cambios y no se reflejan:
Ve a: Caching → Configuration → Purge Cache
- Selecciona Custom Purge
- Purge by: Hostname
- Introduce: labs.josejordan.dev
- Purge
Comandos Útiles
Ver certificados instalados
# Listar todos los certificados
sudo certbot certificates
# Ver un certificado específico
sudo certbot certificates --cert-name labs.josejordan.dev
Renovar certificados
# Renovar todos los certificados que estén próximos a expirar
sudo certbot renew
# Forzar renovación de un certificado específico
sudo certbot renew --cert-name labs.josejordan.dev --force-renewal
# Dry run (prueba sin renovar)
sudo certbot renew --dry-run
Revocar certificado
Eliminar certificado
Ver configuración de renovación
Testing SSL
# Verificar certificado desde el VPS
curl -vI https://labs.josejordan.dev 2>&1 | grep -i "ssl\|certificate"
# Ver fecha de expiración
echo | openssl s_client -servername labs.josejordan.dev -connect labs.josejordan.dev:443 2>/dev/null | openssl x509 -noout -dates
# Test completo SSL
openssl s_client -connect labs.josejordan.dev:443 -servername labs.josejordan.dev
Testing SSL online
Troubleshooting
Error: "Timeout during connect"
Problema: Certbot no puede conectar con Cloudflare API.
Solución: 1. Verificar que el token es correcto:
2. Verificar permisos del token en Cloudflare 3. Verificar que el VPS puede acceder a Internet:Error: "The client lacks sufficient authorization"
Problema: El token no tiene permisos suficientes.
Solución:
1. Ir a Cloudflare → API Tokens
2. Verificar que el token tiene permiso Zone → DNS → Edit
3. Verificar que aplica a la zona correcta
4. Si es necesario, crear un nuevo token
Error: "Domain not found"
Problema: El dominio no existe en Cloudflare.
Solución: 1. Verificar que el dominio está en Cloudflare 2. Verificar que escribiste bien el dominio 3. Esperar propagación DNS si acabas de añadir el dominio
Renovación falla
# Ver logs
sudo tail -f /var/log/letsencrypt/letsencrypt.log
# Probar renovación manualmente
sudo certbot renew --cert-name labs.josejordan.dev --force-renewal
# Verificar timer
sudo systemctl status certbot.timer
Nginx no encuentra los certificados
# Verificar que existen
ls -la /etc/letsencrypt/live/labs.josejordan.dev/
# Verificar permisos
sudo chmod 755 /etc/letsencrypt/live/
sudo chmod 755 /etc/letsencrypt/archive/
# Verificar sintaxis Nginx
sudo nginx -t
Certificado expirado
Si olvidaste renovar y el certificado expiró:
# Renovar inmediatamente
sudo certbot renew --cert-name labs.josejordan.dev --force-renewal
# Recargar Nginx
sudo systemctl reload nginx
# Verificar timer para el futuro
sudo systemctl status certbot.timer
Checklist SSL/TLS
- [x] Certbot instalado
- [x] Plugin dns-cloudflare instalado
- [x] Token de API de Cloudflare creado y guardado
- [x] Certificados emitidos con DNS-01
- [x] Nginx configurado con SSL
- [x] Cloudflare en modo "Full (strict)"
- [x] Renovación automática verificada (certbot.timer)
- [x] Cache Rules configuradas para subdominios dinámicos
- [x] Workers sin rutas comodín
Flujo Completo de Trabajo
Para cada nuevo subdominio
-
DNS en Cloudflare
-
Emitir certificado
-
Configurar Nginx
-
Configurar bypass cache (si es dinámico)
-
Probar
⬅️ Anterior: Nginx | Volver al índice | ➡️ Siguiente: Subdominios