// DevOps

Certificados SSL a través de DNS: automatización de la emisión de Let’s Encrypt

Publicado el 04.10.2025

Introducción

Let’s Encrypt — el estándar para obtener certificados TLS gratuitos. La mayoría de las veces los certificados se emiten mediante el método HTTP-01, que requiere un servidor web accesible en el puerto 80. Sin embargo, para servicios internos o certificados wildcard (por ejemplo, *.example.com) es más cómodo usar DNS-01, que valida la propiedad del dominio mediante registros TXT en DNS y no requiere puertos abiertos.

En el artículo se tratan:

  • Emisión de certificados a través de la API de Cloudflare,
  • Emisión de certificados a través de Amazon Route 53 (AWS),
  • Integración con los servidores web Nginx, HAProxy y Traefik,
  • Automatización de la renovación de certificados.

Nota: Las instrucciones son válidas para Certbot 2.x, acme.sh 3.x, Nginx 1.18+, HAProxy 2.4+, Traefik 2.x en Linux (Ubuntu/Debian). Para otros SO o versiones de las herramientas pueden ser necesarios ajustes.


Cloudflare

1. Creación del token API

  1. Inicie sesión en Cloudflare → API TokensCreate Token.
  2. Use la plantilla Edit zone DNS con los permisos:
    • Zone.DNS:Edit
    • Zone.Zone:Read
  3. Limite el token a una zona concreta.
  4. Guarde el token en un lugar seguro (secretos de CI/CD, variables de entorno).

⚠️ La fuga del token otorga control total sobre la zona DNS.

2. Instalación de Certbot y el plugin

Ubuntu/Debian:

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

CentOS/Fedora:

sudo dnf install -y certbot python3-certbot-dns-cloudflare

A través de snap:

sudo snap install --classic certbot
sudo snap install certbot-dns-cloudflare

3. Configuración del token

Archivo /etc/letsencrypt/cloudflare.ini:

dns_cloudflare_api_token = YOUR_CF_API_TOKEN

Permisos:

sudo chown root:root /etc/letsencrypt/cloudflare.ini
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

4. Emisión del certificado

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d example.com -d '*.example.com' \
  --non-interactive

AWS Route 53

1. Configuración de permisos IAM

Cree una política con permisos mínimos para una zona concreta:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets",
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets"
      ],
      "Resource": "arn:aws:route53:::hostedzone/<YOUR_HOSTED_ZONE_ID>"
    }
  ]
}

Configure el perfil de AWS CLI:

aws configure --profile certbot

Proporcione AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY y la región (por ejemplo, us-east-1).

2. Instalación del plugin

Ubuntu/Debian:

sudo apt-get install -y certbot python3-certbot-dns-route53

CentOS/Fedora:

sudo dnf install -y certbot python3-certbot-dns-route53

3. Emisión del certificado

AWS_PROFILE=certbot \
sudo certbot certonly \
  --dns-route53 \
  -d example.com -d '*.example.com' \
  --non-interactive

Nota: se pueden usar variables de entorno en lugar del perfil (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY).


Integración con servidores web

Nginx

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

Hook para recargar /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh:

#!/bin/sh
if nginx -t; then
    systemctl reload nginx
else
    echo "Nginx configuration test failed"
    exit 1
fi
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

HAProxy

Concatenación del certificado y la clave:

cat /etc/letsencrypt/live/example.com/fullchain.pem \
    /etc/letsencrypt/live/example.com/privkey.pem \
    | tee /etc/haproxy/certs/example.com.pem >/dev/null

Fragmento de la configuración:

frontend https-in
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
    default_backend web-backend

Traefik

Archivo /etc/traefik/dynamic.yml:

tls:
  certificates:
    - certFile: /etc/letsencrypt/live/example.com/fullchain.pem
      keyFile: /etc/letsencrypt/live/example.com/privkey.pem

traefik.yml:

providers:
  file:
    filename: /etc/traefik/dynamic.yml

Si Traefik se ejecuta en Docker:

-v /etc/letsencrypt:/etc/letsencrypt:ro

Automatización de la renovación

Certbot

Tarea cron:

sudo crontab -e
0 0,12 * * * certbot renew --quiet --deploy-hook /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Registros de errores: /var/log/letsencrypt/letsencrypt.log.

acme.sh

Al instalar acme.sh, este añade su propia tarea cron. Verificación:

crontab -l | grep acme.sh

Alternativa: acme.sh

Instalación:

curl https://get.acme.sh | sh

Cloudflare:

export CF_Token="YOUR_CF_API_TOKEN"
~/.acme.sh/acme.sh --issue --dns dns_cf -d example.com -d '*.example.com'

AWS Route 53:

export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_REGION="us-east-1"
~/.acme.sh/acme.sh --issue --dns dns_aws -d example.com -d '*.example.com'

Instalación del certificado:

~/.acme.sh/acme.sh --install-cert -d example.com \
  --key-file       /etc/ssl/example.com/privkey.pem \
  --fullchain-file /etc/ssl/example.com/fullchain.pem \
  --reloadcmd     "systemctl reload nginx"

Certbot vs acme.sh

CriterioCertbotacme.sh
LenguajePythonPOSIX sh
InstalaciónPaquetes del SO o snapUn script `curlsh`
Soporte DNSPlugins oficiales~40 proveedores incorporados
ActualizacionesA través del gestor de paquetesActualización automática integrada
Hooks y despliegueHooks limitadosConvenientes --reloadcmd, --deploy
DependenciasPython, paquetes certbot-*curl, socat, openssl
RecursosMás pesado (Python)Más ligero, minimalista
PopularidadEstándar de facto, más documentaciónMás conveniente para CI/CD

Cuando elegir Certbot:

  • si se necesita soporte oficial de Let’s Encrypt,
  • si es cómodo instalar paquetes mediante apt/yum,
  • si la prioridad es un ecosistema estándar.

Cuando elegir acme.sh:

  • si se dispone de un entorno mínimo sin Python,
  • si la emisión debe integrarse en un pipeline de CI/CD,
  • si el proveedor DNS está soportado directamente en acme.sh.

Limitaciones y riesgos

  • La fuga de un token API = control total sobre la zona DNS.
  • Límites de Let’s Encrypt: hasta 50 certificados por semana por dominio.
  • Propagación DNS: retrasos de segundos a minutos.
  • Errores en la renovación: hooks incorrectos pueden llevar a la expiración.

Consejo: añada monitorización (Zabbix, Prometheus, Nagios) para controlar la validez de los certificados.


Conclusión

La validación DNS permite:

  • automatizar la emisión y renovación de SSL,
  • usar certificados wildcard,
  • no depender de puertos abiertos.

Cloudflare y AWS Route 53 ofrecen APIs para automatizar por completo. La integración con Nginx, HAProxy y Traefik requiere solo unas pocas líneas de configuración. La renovación automática y la monitorización son pasos obligatorios para una explotación fiable.

// Reviews

Reseñas relacionadas

apande

apande

Configuración de Nginx y OpenCart

07.09.2024 · ★ 5/5

Un comprador muy poderoso
kireevk

kireevk

Diagnóstico de Nginx Proxy Manager en un contenedor Docker y solución del problema

15.03.2024 · ★ 5/5

Comprador acostumbrado

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema