Cuando necesitas emitir un certificado Let’s Encrypt para un dominio (y especialmente un wildcard tipo *.tudominio.com), el método recomendado es el desafío DNS-01. Este desafío consiste en publicar temporalmente un registro TXT en el DNS con un token que valida que eres el propietario del dominio.
En este post vamos a automatizar el proceso usando Certbot en modo manual, pero con hooks que se encargan de:
- Crear el TXT
_acme-challengevía Plesk API (agent.php) antes de validar (--manual-auth-hook) - Eliminarlo al finalizar (
--manual-cleanup-hook) - Esperar propagación DNS antes de que Certbot continúe, evitando fallos por “aún no se ve el TXT”.
La ventaja de este método es que no dependes de plugins específicos, puedes usarlo con DNS gestionado desde Plesk y sirve para wildcard.
Requisitos previos
- Tener certbot instalado en el servidor donde emitirás el certificado.
- Tener acceso admin al Plesk que gestiona la zona DNS del dominio.
- Tener instaladas herramientas básicas:
curlxmllint(normalmente enlibxml2-utils)awk,sed,trnslookup(paquetednsutilsen Debian/Ubuntu)
Preparación
- Crea el dominio (Si no existe):
sudo mkdir -p /etc/letsencrypt
- Crea el script auth hook:
- Guárdalo como:
/etc/letsencrypt/plesk_dns_hook.sh
- y dale permisos de ejecución.
sudo nano /etc/letsencrypt/plesk_dns_hook.shsudo chmod +x /etc/letsencrypt/plesk_dns_hook.sh
- Script:
/etc/letsencrypt/plesk_dns_hook.sh
- Guárdalo como:
#!/bin/bash
# /etc/letsencrypt/plesk_dns_hook.sh
# Llamar como:
# certbot certonly --manual --preferred-challenges dns --manual-auth-hook /etc/letsencrypt/plesk_dns_hook.sh --manual-cleanup-hook /etc/letsencrypt/plesk_dns_cleanup.sh -d '*.occentus.net'
echo $CERTBOT_DOMAIN
echo $CERTBOT_VALIDATION
#sleep 20000
set -e
HTTP_AUTH_LOGIN="admin"
HTTP_AUTH_PASSWD="PASSWORD"
domain_plesk="plesk.occentus.net"
domain="occentus.net"
site_id="99"
# Certbot proporciona estas:
# CERTBOT_DOMAIN, CERTBOT_VALIDATION
# Obtener IDs de registros TXT previos para _acme-challenge
ids=$(curl -ks --connect-timeout 5 --max-time 10 -X POST https://$domain_plesk:8443/enterprise/control/agent.php \
-H "HTTP_AUTH_LOGIN: $HTTP_AUTH_LOGIN" \
-H "HTTP_AUTH_PASSWD: $HTTP_AUTH_PASSWD" \
-d "<?xml version=\"1.0\"?>
<packet>
<dns>
<get_rec>
<filter>
<site-id>$site_id</site-id>
</filter>
</get_rec>
</dns>
</packet>" \
| xmllint --format - \
| awk "/<id>/ { id=\$0 } /<host>_acme-challenge.${CERTBOT_DOMAIN}.<\/host>/ { print id }" \
| tr ' ' '\n' | sed '/^$/d' | sed 's/<id>//g' | sed 's#</id>##g')
# Borrar cada ID encontrado
for id in $ids; do
curl -ks --connect-timeout 5 --max-time 10 -X POST https://$domain_plesk:8443/enterprise/control/agent.php \
-H "HTTP_AUTH_LOGIN: $HTTP_AUTH_LOGIN" \
-H "HTTP_AUTH_PASSWD: $HTTP_AUTH_PASSWD" \
-d "<?xml version=\"1.0\"?>
<packet>
<dns>
<del_rec>
<filter>
<id>$id</id>
</filter>
</del_rec>
</dns>
</packet>"
done
# Añadir el nuevo registro TXT
curl -ks --connect-timeout 5 --max-time 10 -X POST https://$domain_plesk:8443/enterprise/control/agent.php \
-H "HTTP_AUTH_LOGIN: $HTTP_AUTH_LOGIN" \
-H "HTTP_AUTH_PASSWD: $HTTP_AUTH_PASSWD" \
-d "<?xml version=\"1.0\"?>
<packet version=\"1.6.3.0\">
<dns>
<add_rec>
<site-id>$site_id</site-id>
<type>TXT</type>
<host>_acme-challenge</host>
<value>$CERTBOT_VALIDATION</value>
</add_rec>
</dns>
</packet>"
# Esperar a que el registro TXT esté disponible en DNS público (máx. 60 minutos)
echo "Esperando propagación DNS para _acme-challenge.${CERTBOT_DOMAIN}..."
for i in {1..120}; do
if nslookup -q=TXT "_acme-challenge.${CERTBOT_DOMAIN}" | grep -q "$CERTBOT_VALIDATION"; then
echo "✔ Registro DNS propagado correctamente."
break
fi
echo "⏳ Intento $i: Registro aún no visible. Esperando 60 segundos..."
sleep 60
done
# Si después de 120 minutos no aparece, abortar
if ! nslookup -q=TXT "_acme-challenge.${CERTBOT_DOMAIN}" | grep -q "$CERTBOT_VALIDATION"; then
echo "❌ ERROR: El registro TXT no se ha propagado tras 120 minutos."
exit 1
fi
- Crea el script cleanup hook:
- Guárdalo como:
/etc/letsencrypt/plesk_dns_cleanup.sh
- y dale permisos de ejecución.
sudo nano /etc/letsencrypt/plesk_dns_cleanup.shsudo chmod +x /etc/letsencrypt/plesk_dns_cleanup.sh
- Script:
/etc/letsencrypt/plesk_dns_cleanup.sh
- Guárdalo como:
#!/bin/bash
# /etc/letsencrypt/plesk_dns_cleanup.sh
set -e
HTTP_AUTH_LOGIN="admin"
HTTP_AUTH_PASSWD="PASSWORD"
domain_plesk="plesk.occentus.net"
site_id="99"
# Obtener IDs del TXT a borrar
ids=$(curl -ks --connect-timeout 5 --max-time 10 -X POST https://$domain_plesk:8443/enterprise/control/agent.php \
-H "HTTP_AUTH_LOGIN: $HTTP_AUTH_LOGIN" \
-H "HTTP_AUTH_PASSWD: $HTTP_AUTH_PASSWD" \
-d "<?xml version=\"1.0\"?>
<packet>
<dns>
<get_rec>
<filter>
<site-id>$site_id</site-id>
</filter>
</get_rec>
</dns>
</packet>" \
| xmllint --format - \
| awk "/<id>/ { id=\$0 } /<host>_acme-challenge.${CERTBOT_DOMAIN}.<\/host>/ { print id }" \
| tr ' ' '\n' | sed '/^$/d' | sed 's/<id>//g' | sed 's#</id>##g')
#echo "$ids"
#sleep 30
for id in $ids; do
curl -ks --connect-timeout 5 --max-time 10 -X POST https://$domain_plesk:8443/enterprise/control/agent.php \
-H "HTTP_AUTH_LOGIN: $HTTP_AUTH_LOGIN" \
-H "HTTP_AUTH_PASSWD: $HTTP_AUTH_PASSWD" \
-d "<?xml version=\"1.0\"?>
<packet>
<dns>
<del_rec>
<filter>
<id>$id</id>
</filter>
</del_rec>
</dns>
</packet>"
done
Configuración a revisar en los scripts
Antes de ejecutar, ajusta estas variables en ambos scripts:
HTTP_AUTH_LOGIN/ HTTP_AUTH_PASSWD: credenciales para autenticar contra Plesk (según tengas configurada la API).domain_plesk: hostname del Plesk, por ejemplo plesk.midominio.comsite_id: el ID del sitio/zona DNS en Plesk donde están los registros del dominio
Por limitación de Plesk, debe usarse la cuenta admin de Plesk para realizar estos cambios en los DNS.
Emitir el certificado con Certbot
Ejemplo para wildcard:
sudo certbot certonly \
--manual \
--preferred-challenges dns \
--manual-auth-hook /etc/letsencrypt/plesk_dns_hook.sh \
--manual-cleanup-hook /etc/letsencrypt/plesk_dns_cleanup.sh \
-d '*.occentus.net'
Si además quieres el dominio raíz (recomendado en muchos casos), añade también:
-d 'occentus.net'
Quedaría:
sudo certbot certonly \
--manual \
--preferred-challenges dns \
--manual-auth-hook /etc/letsencrypt/plesk_dns_hook.sh \
--manual-cleanup-hook /etc/letsencrypt/plesk_dns_cleanup.sh \
-d 'occentus.net' \
-d '*.occentus.net'
Qué ocurre durante la ejecución
- Certbot inicia el desafío DNS-01 y exporta variables como:
CERTBOT_DOMAINCERTBOT_VALIDATION
- Se ejecuta el
manual-auth-hook:- borra TXT antiguos
_acme-challenge.<dominio> - crea el TXT nuevo con el token
- espera hasta que
nslookuplo vea públicamente
- borra TXT antiguos
- Let’s Encrypt valida el dominio.
- Se ejecuta el
manual-cleanup-hooky se eliminan los TXT creados.
Solución de problemas
- No encuentra
xmllint- Instala
libxml2-utils.
- Instala
- No encuentra
nslookup- Instala
dnsutils.
- Instala
- La propagación tarda demasiado
- Puede depender del TTL y de los servidores autoritativos. El script espera hasta 120 intentos de 60s (2 horas).
- El TXT no coincide
- Revisa si hay múltiples valores TXT y si el DNS está sirviendo cache antiguo.
- Errores de autenticación en Plesk
- Verifica credenciales y que la API por
agent.phpesté accesible desde tu servidor.
- Verifica credenciales y que la API por