Saltar a contenido

🔐 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

Instalación básica

sudo apt install certbot python3-certbot-nginx -y

Verificar instalación

certbot --version

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

sudo apt-get update
sudo apt-get install -y python3-certbot-dns-cloudflare

Crear token de API en Cloudflare

Solo necesitas hacer esto UNA VEZ:

  1. Ve a Cloudflare Dashboard
  2. Click en tu perfil (esquina superior derecha)
  3. My ProfileAPI TokensCreate Token
  4. Usa la plantilla "Edit zone DNS"
  5. Configura:
  6. Permissions: Zone → DNS → Edit
  7. Zone Resources: Specific zone → tu-dominio.com
  8. Continue to summaryCreate Token
  9. 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

sudo cat /root/.secrets/cf.ini

Debería mostrar:

dns_cloudflare_api_token = tu_token_aqui


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:

authenticator = dns-cloudflare
dns_cloudflare_credentials = /root/.secrets/cf.ini

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.timer se 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:

sudo certbot renew --dry-run

Si todo está bien, deberías ver:

Congratulations, all simulated renewations succeeded

Renovar manualmente (si es necesario)

sudo certbot renew
sudo systemctl reload nginx

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:

josejordan.dev/*
www.josejordan.dev/*

❌ Incorrecto:

*.josejordan.dev/*    # ¡Intercepta TODOS los subdominios!

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

sudo certbot revoke --cert-path /etc/letsencrypt/live/labs.josejordan.dev/cert.pem

Eliminar certificado

sudo certbot delete --cert-name labs.josejordan.dev

Ver configuración de renovación

sudo cat /etc/letsencrypt/renewal/labs.josejordan.dev.conf

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:

sudo cat /root/.secrets/cf.ini
2. Verificar permisos del token en Cloudflare 3. Verificar que el VPS puede acceder a Internet:
curl -I https://api.cloudflare.com

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

  1. DNS en Cloudflare

    Crear registro A: subdominio.ejemplo.com → IP del VPS
    

  2. Emitir certificado

    sudo certbot certonly \
      --dns-cloudflare \
      --dns-cloudflare-credentials /root/.secrets/cf.ini \
      --cert-name subdominio.ejemplo.com \
      -d subdominio.ejemplo.com
    

  3. Configurar Nginx

    sudo nano /etc/nginx/sites-available/subdominio
    # Añadir configuración con SSL
    sudo ln -s /etc/nginx/sites-available/subdominio /etc/nginx/sites-enabled/
    sudo nginx -t && sudo systemctl reload nginx
    

  4. Configurar bypass cache (si es dinámico)

    Cloudflare → Cache Rules → Bypass para el subdominio
    

  5. Probar

    curl -I https://subdominio.ejemplo.com
    


⬅️ Anterior: Nginx | Volver al índice | ➡️ Siguiente: Subdominios