30 Jul Cómo Resolver Errores de Renovación SSL con Certbot: El Problema de los Certificados Huérfanos
Como ingeniero de infraestructura, uno de los problemas más frustrantes que puedes encontrar es cuando tu script de renovación automática de certificados SSL falla inesperadamente. Hoy te voy a contar sobre un error común que me encontré recientemente y cómo resolverlo de manera definitiva.
El Problema: Timeout Durante la Renovación
Todo comenzó con este error en mi script de renovación que deje en el post de este mismo blog (“Como instalar y configurar Certbot para NGINX en Amazon Linux 2023“) :
Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems: Domain: dev.domain.com Type: connection Detail: Fetching http://dev.domain.com/.well-known/acme-challenge/: Timeout during connect (likely firewall problem)
Al principio pensé en las causas típicas:
- Problemas de firewall
- Security Groups mal configurados en AWS
- DNS mal configurado
- Nginx caído
Pero después de verificar todo esto, el problema persistía. ¿La causa real? Certificados huérfanos.
¿Qué son los Certificados Huérfanos?
Un certificado huérfano es un certificado SSL que existe en el sistema de Let’s Encrypt pero ya no tiene una configuración correspondiente en tu servidor web. Esto suele ocurrir cuando:
- Eliminas un dominio de tu configuración de nginx
- Migras servicios a otros servidores
- Cambias la arquitectura de tu aplicación
- Removes subdominios que ya no usas
En mi caso, tenía un certificado para dev.wms-alphas.com que ya no existía en mi configuración de nginx, pero certbot seguía intentando renovarlo automáticamente.
Diagnóstico Paso a Paso
1. Verificar Certificados Existentes
# Listar todos los certificados sudo certbot certificates # Ver detalles específicos sudo certbot certificates --cert-name dev.domain.com
2. Verificar Configuración de Nginx
# Listar sitios habilitados ls -la /etc/nginx/sites-enabled/ # Buscar configuraciones de dominio grep -r "server_name" /etc/nginx/sites-enabled/ # Verificar dominio específico grep -r "dev.domain.com" /etc/nginx/sites-enabled/
3. Identificar el Desajuste
Si el comando certbot certificates muestra un certificado pero grep no encuentra configuración en nginx, tienes un certificado huérfano.
La Solución: Limpieza de Certificados
Opción 1: Eliminar Certificados No Utilizados (Recomendado)
# Eliminar el certificado huérfano sudo certbot delete --cert-name dev.domain.com # Verificar que se eliminó correctamente sudo certbot certificates # Probar renovación sudo certbot renew --dry-run
Opción 2: Crear Configuración Temporal
Si planeas usar el dominio en el futuro:
# /etc/nginx/sites-available/dev.domain.com
server {
listen 80;
server_name dev.domain.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
allow all;
}
location / {
return 503; # Service unavailable
}
}
Script de Renovación Mejorado
Aquí tienes un script optimizado que evita estos problemas en el futuro:
#!/bin/bash
# updatessl.sh - Versión mejorada con validaciones
echo "=== SSL Certificate Renewal Script ==="
# Verificar configuración de nginx
if ! sudo nginx -t; then
echo "❌ ERROR: Invalid nginx configuration"
exit 1
fi
# Verificar certificados vs configuración
echo "🔍 Checking for orphaned certificates..."
for cert_conf in /etc/letsencrypt/renewal/*.conf; do
if [ -f "$cert_conf" ]; then
cert_name=$(basename "$cert_conf" .conf)
domains=$(grep "^domains = " "$cert_conf" | cut -d' ' -f3- | tr ',' ' ')
for domain in $domains; do
if ! grep -r "$domain" /etc/nginx/sites-enabled/ > /dev/null; then
echo "⚠️ WARNING: Domain $domain has no nginx configuration"
echo " Consider running: sudo certbot delete --cert-name $cert_name"
fi
done
fi
done
# Proceder con renovación
echo "🔄 Starting certificate renewal..."
sudo systemctl stop nginx
if sudo certbot renew --force-renewal; then
echo "✅ Certificate renewal successful"
else
echo "❌ Certificate renewal failed"
sudo systemctl start nginx
exit 1
fi
# Reiniciar nginx
if sudo systemctl start nginx; then
echo "✅ Nginx restarted successfully"
sudo systemctl status nginx --no-pager -l
else
echo "❌ Failed to start nginx"
sudo journalctl -xeu nginx.service --no-pager
exit 1
fi
echo "🎉 SSL renewal completed successfully!"
Automatización con AWS
Para entornos de producción, considera automatizar esto con Infrastructure as Code:
# Lambda function para renovación automática
import boto3
import json
def lambda_handler(event, context):
ssm = boto3.client('ssm')
commands = [
"sudo certbot certificates",
"sudo certbot renew --quiet --no-self-upgrade",
"sudo systemctl reload nginx"
]
response = ssm.send_command(
InstanceIds=[event['instance_id']],
DocumentName='AWS-RunShellScript',
Parameters={'commands': commands},
TimeoutSeconds=300
)
return {
'statusCode': 200,
'body': json.dumps({
'message': 'SSL renewal initiated',
'command_id': response['Command']['CommandId']
})
}
Mejores Prácticas
- Auditoría Regular: Revisa periódicamente tus certificados vs configuración de nginx
- Monitoreo: Implementa alertas para fallos de renovación
- Documentación: Mantén un registro de qué dominios están activos
- Automatización: Usa Infrastructure as Code para gestionar certificados
- Testing: Siempre usa
--dry-runantes de renovaciones forzadas
Conclusión
Los certificados huérfanos son un problema común pero fácil de resolver una vez que sabes qué buscar. La clave está en mantener sincronizada tu configuración de certificados con tu configuración de servidor web.
Este tipo de problemas son perfectos ejemplos de por qué la infraestructura debe tratarse como código: versionada, documentada y auditable.
Enlaces Útiles
Happy Hacking!