--- title: "Protocolo ACME: cómo automatizar certificados TLS" description: "Entiende cómo funciona el protocolo ACME, sus desafíos HTTP-01, DNS-01 y TLS-ALPN-01, compara clientes y automatiza la emisión y renovación con Certbot." category: "SSL/TLS y Certificados" language: es source: https://cubepath.com/es/docs/ssl-tls-y-certificados/protocolo-acme-y-automatizacion-de-certificados updated: 2026-09-25 --- # Protocolo ACME: cómo automatizar certificados TLS ACME (Automatic Certificate Management Environment, RFC 8555) es el protocolo que permite a un programa pedir, validar, renovar y revocar certificados TLS sin intervención humana. Lo usan Let's Encrypt, ZeroSSL, Google Trust Services y otras CA, y está integrado en herramientas como Certbot, Caddy o Traefik. En esta guía verás cómo funciona el protocolo, qué desafío elegir en cada caso y cómo montar en Ubuntu 24.04 una emisión y renovación automática con Certbot que puedas verificar. ## Requisitos previos Para la parte práctica necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, y un usuario no root con privilegios `sudo`. - Un dominio con un registro DNS `A` (y `AAAA` si usas IPv6) que apunte a `your_server_ip`. - Nginx instalado (`sudo apt install nginx`) y el puerto 80 accesible desde Internet, además del 443. Sustituye `your_domain` por tu dominio en todos los comandos. ## Cómo funciona ACME Una emisión ACME sigue siempre los mismos pasos entre el cliente (Certbot, acme.sh...) y el servidor de la CA: 1. **Directorio.** El cliente descarga un documento JSON con las URL de cada operación. 2. **Cuenta.** El cliente genera un par de claves de cuenta y se registra. Todas las peticiones posteriores van firmadas con esa clave. 3. **Pedido (order).** El cliente pide un certificado para una lista de nombres. 4. **Autorizaciones y desafíos.** Por cada nombre, la CA propone uno o varios desafíos para demostrar el control del dominio. 5. **Validación.** El cliente publica la respuesta y avisa a la CA, que la comprueba desde varios puntos de Internet. 6. **Finalización.** Con todos los nombres validados, el cliente envía una CSR y descarga el certificado con su cadena. Puedes ver el directorio de Let's Encrypt tú mismo: ```bash curl -s https://acme-v02.api.letsencrypt.org/directory ``` ```output { "keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change", "meta": { ... }, "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct", "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce", "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order", "renewalInfo": "https://acme-v02.api.letsencrypt.org/acme/renewal-info", "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert" } ``` Let's Encrypt ofrece además un entorno de pruebas (staging) en `https://acme-staging-v02.api.letsencrypt.org/directory`. Emite certificados que no son de confianza pero tiene límites mucho más altos, así que es donde debes probar cualquier configuración nueva. ## Tipos de desafío La elección del desafío determina qué necesita el servidor y qué tipo de certificado puedes obtener: | Desafío | Cómo se valida | Requisitos | Comodines | Uso típico | |---|---|---|---|---| | HTTP-01 | La CA descarga `http://your_domain/.well-known/acme-challenge/` | Puerto 80 accesible desde Internet | No | Servidores web públicos | | DNS-01 | La CA consulta el registro TXT `_acme-challenge.your_domain` | API del proveedor DNS | Sí | Comodines, servidores internos, varios servidores | | TLS-ALPN-01 | La CA conecta al puerto 443 con el protocolo ALPN `acme-tls/1` | Puerto 443 y un cliente que lo soporte | No | Proxies como Caddy o Traefik sin puerto 80 | Algunas consecuencias prácticas: - HTTP-01 sigue las redirecciones, así que puedes redirigir todo a HTTPS siempre que `/.well-known/acme-challenge/` responda. - Con DNS-01 el servidor no tiene que ser accesible desde Internet, lo que lo hace útil para servicios internos con nombre público. - Si hay varios servidores detrás de un balanceador, HTTP-01 exige que cualquiera de ellos pueda servir el token; DNS-01 evita el problema. ## Comparativa de clientes ACME | Cliente | Lenguaje | Puntos fuertes | A tener en cuenta | |---|---|---|---| | Certbot | Python | Cliente de referencia de la EFF, plugins para Nginx, Apache y muchos DNS, temporizador incluido | Instalación recomendada con snap | | acme.sh | Shell | Sin dependencias, más de 150 APIs DNS, instalación por usuario | Su CA por defecto es ZeroSSL; usa `--server letsencrypt` si prefieres Let's Encrypt | | lego | Go | Binario único, muchos proveedores DNS, fácil de usar en contenedores | La renovación la programas tú (cron o temporizador) | | Caddy / Traefik | Go | ACME integrado en el servidor web o proxy, sin configuración aparte | Los certificados los gestiona el propio proceso | Si ya usas Nginx o Apache en un servidor, Certbot es la opción más directa y la que usa el resto de esta guía. ## Paso 1: Instalar Certbot Instala Certbot con snap, que es el método que mantiene el proyecto, y crea un enlace para poder ejecutarlo como `certbot`: ```bash sudo apt remove certbot sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/local/bin/certbot certbot --version ``` El último comando debe mostrar una línea `certbot X.Y.Z` con la versión instalada. ## Paso 2: Preparar el desafío HTTP-01 con webroot El método `webroot` hace que Certbot escriba el token en un directorio que Nginx ya sirve, sin detener el servidor ni tocar su configuración. Crea un directorio dedicado: ```bash sudo mkdir -p /var/www/acme ``` Edita el bloque `server` del puerto 80 de tu sitio: ```bash sudo nano /etc/nginx/sites-available/your_domain ``` Sirve los tokens desde ese directorio y redirige todo lo demás a HTTPS: ```nginx server { listen 80; listen [::]:80; server_name your_domain www.your_domain; location /.well-known/acme-challenge/ { root /var/www/acme; } location / { return 301 https://$host$request_uri; } } ``` Activa el sitio si aún no lo está, recarga y comprueba que Nginx sirve un archivo de prueba en esa ruta: ```bash sudo ln -sf /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx sudo mkdir -p /var/www/acme/.well-known/acme-challenge echo ok | sudo tee /var/www/acme/.well-known/acme-challenge/test curl http://your_domain/.well-known/acme-challenge/test ``` ```output ok ``` Borra el archivo de prueba: ```bash sudo rm /var/www/acme/.well-known/acme-challenge/test ``` ## Paso 3: Probar contra staging y emitir el certificado Simula la emisión contra el entorno de staging con `--dry-run`. Si algo falla, no consumirás los límites de producción: ```bash sudo certbot certonly --webroot -w /var/www/acme -d your_domain -d www.your_domain --dry-run ``` ```output The dry run was successful. ``` Emite ahora el certificado real. La primera ejecución crea la cuenta ACME y te pide un correo de contacto: ```bash sudo certbot certonly --webroot -w /var/www/acme -d your_domain -d www.your_domain ``` ```output Successfully received certificate. Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Key is saved at: /etc/letsencrypt/live/your_domain/privkey.pem ``` Certbot guarda la clave de la cuenta en `/etc/letsencrypt/accounts/` y los parámetros de esta emisión (método, directorio webroot, nombres) en `/etc/letsencrypt/renewal/your_domain.conf`. Las renovaciones reutilizan ese archivo. Añade el bloque HTTPS a Nginx apuntando a `fullchain.pem` y `privkey.pem`: ```nginx server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name your_domain www.your_domain; ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem; root /var/www/html; } ``` ```bash sudo nginx -t && sudo systemctl reload nginx ``` ## Paso 4: Automatizar la renovación Los certificados de Let's Encrypt duran 90 días y la tendencia del sector es reducir aún más su vida útil, así que la renovación debe ser totalmente automática. El snap de Certbot instala un temporizador de systemd que ejecuta `certbot renew` dos veces al día: ```bash systemctl list-timers snap.certbot.renew.timer ``` `certbot renew` solo actúa sobre los certificados que están a menos de 30 días de caducar, por lo que ejecutarlo a menudo es seguro. Falta un detalle: Nginx no carga el certificado nuevo hasta que se recarga. Crea un hook de despliegue, que Certbot ejecuta únicamente tras una renovación correcta: ```bash sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh ``` ```bash #!/usr/bin/env bash set -euo pipefail systemctl reload nginx ``` ```bash sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh ``` Simula una renovación completa, hooks incluidos: ```bash sudo certbot renew --dry-run ``` ```output Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/your_domain/fullchain.pem (success) ``` Let's Encrypt ya no envía correos de aviso de caducidad, así que conviene vigilar la fecha por tu cuenta, por ejemplo desde tu sistema de monitorización. Para consultarla a mano: ```bash sudo certbot certificates ``` ## Límites de emisión Let's Encrypt aplica límites para proteger su infraestructura. Los que más afectan en la práctica son: - Un número máximo de certificados nuevos por dominio registrado cada semana (50 en el momento de escribir esta guía). - Un máximo de 5 certificados por semana con exactamente el mismo conjunto de nombres. - Un límite de validaciones fallidas por nombre y cuenta cada hora. Las renovaciones de un certificado existente tienen un trato más permisivo, pero repetir emisiones de prueba en producción es la forma más habitual de bloquearse. Usa siempre `--dry-run` para probar y evita `--force-renewal` salvo que realmente necesites reemitir. Los valores actuales están en la documentación de límites de Let's Encrypt. ## Solución de problemas **`Invalid response from http://your_domain/.well-known/acme-challenge/...: 404`.** Nginx no sirve el directorio webroot para ese nombre. Revisa que el `server_name` del bloque del puerto 80 incluye todos los nombres pedidos y repite la prueba del paso 2. **`Timeout during connect (likely firewall problem)`.** La CA no llega al puerto 80. Comprueba el cortafuegos (`sudo ufw status`, y `sudo ufw allow 'Nginx Full'` si hace falta) y que el registro `AAAA`, si existe, apunta a una IPv6 que el servidor atiende: Let's Encrypt prefiere IPv6 cuando hay registro `AAAA`. **`DNS problem: NXDOMAIN looking up A for your_domain`.** El registro DNS no existe o aún no se ha propagado. Compruébalo con `dig +short A your_domain`. **Para ver el detalle de cualquier error**, consulta el registro de Certbot: ```bash sudo tail -n 50 /var/log/letsencrypt/letsencrypt.log ``` ## Conclusión Has visto las fases del protocolo ACME, cuándo usar HTTP-01, DNS-01 o TLS-ALPN-01, y has montado con Certbot una emisión por webroot con renovación automática y recarga de Nginx verificada con `--dry-run`. Como siguientes pasos, puedes pasar a DNS-01 para obtener certificados comodín, añadir una alerta de caducidad en tu monitorización o probar un servidor con ACME integrado como Caddy para servicios nuevos.