Desarrolladores → Guías

Certificados wildcard con DNS-01 y certbot

El desafío DNS-01 demuestra que usted controla un dominio mediante un registro TXT en lugar de una página web. Es la única forma de obtener un certificado wildcard y funciona con servidores que no son accesibles desde internet. Este hook permite que certbot publique ese registro mediante la API de NameOcean.

NameOcean emite por sí mismo un certificado wildcard gratuito para cualquier dominio en NameOcean DNS, lo renueva y le proporciona un enlace de descarga (SSL, o POST /ssl/example.com). Utilice esta guía cuando su servidor deba obtener y renovar su propio certificado.

Qué necesita

  • El DNS del dominio en NameOcean (servidores de nombres ns1.nameocean.net a ns4.nameocean.net).
  • certbot, dig (dnsutils o bind-utils) y la CLI de nameocean en el servidor.
  • Una clave de API con dns:read y dns:write. Restrínjala a la dirección IP del servidor si es posible.

1. Instalar el hook

root@server
# curl -fsSL https://nameocean.net/install.sh | sh
# curl -fsSL https://nameocean.net/developers/guides/letsencrypt-dns-01/nameocean-hook.sh \
    -o /etc/letsencrypt/nameocean-hook.sh
# chmod 700 /etc/letsencrypt/nameocean-hook.sh
# echo 'NAMEOCEAN_API_KEY=no_live_…' > /etc/letsencrypt/nameocean.env
# chmod 600 /etc/letsencrypt/nameocean.env

La clave se guarda en un archivo que solo root puede leer; la configuración de renovación que guarda certbot nunca la contiene.

2. Obtener el certificado

root@server
# certbot certonly --manual --preferred-challenges dns \
    --manual-auth-hook "/etc/letsencrypt/nameocean-hook.sh auth" \
    --manual-cleanup-hook "/etc/letsencrypt/nameocean-hook.sh cleanup" \
    -d example.com -d "*.example.com"

Para cada nombre, el hook localiza la zona a la que pertenece entre sus zonas de NameOcean, añade _acme-challenge como registro TXT y espera hasta que los cuatro servidores de nombres respondan con él, normalmente en menos de diez segundos. Tras la verificación, el paso de limpieza elimina exactamente el registro que añadió. example.com y *.example.com usan ambos _acme-challenge.example.com; los dos valores se publican uno junto al otro.

3. Renovaciones

certbot guarda los hooks junto con el certificado, por lo que su temporizador lo renueva de la misma manera. Pruébelo una vez:

root@server
# certbot renew --dry-run

Cada registro que el hook añade y elimina aparece en Panel → Actividad junto con la clave que lo hizo, y envía los eventos de webhook dns.record.created y dns.record.deleted.

El hook

Léalo antes de ejecutarlo como root. Solo invoca la CLI de nameocean y dig.

/etc/letsencrypt/nameocean-hook.sh
#!/bin/sh
# certbot DNS-01 hooks for NameOcean DNS:
#   --manual-auth-hook "/etc/letsencrypt/nameocean-hook.sh auth"
#   --manual-cleanup-hook "/etc/letsencrypt/nameocean-hook.sh cleanup"
# Needs the nameocean CLI and an API key with dns:write, in /etc/letsencrypt/nameocean.env:
#   NAMEOCEAN_API_KEY=no_live_...
set -e
[ -f /etc/letsencrypt/nameocean.env ] && . /etc/letsencrypt/nameocean.env && export NAMEOCEAN_API_KEY

# the zone CERTBOT_DOMAIN is in: the longest of your zones it ends with
zone="$NAMEOCEAN_ZONE"
if [ -z "$zone" ]; then
  for z in $(nameocean dns ls --json | sed -n 's/.*"name": *"\([^"]*\)".*/\1/p'); do
    case "$CERTBOT_DOMAIN" in "$z" | *".$z") [ ${#z} -gt ${#zone} ] && zone="$z" ;; esac
  done
fi
[ -n "$zone" ] || { echo "no NameOcean DNS zone for $CERTBOT_DOMAIN" >&2; exit 1; }
name="_acme-challenge"
[ "$CERTBOT_DOMAIN" = "$zone" ] || name="_acme-challenge.${CERTBOT_DOMAIN%.$zone}"

case "$1" in
auth)
  id=$(nameocean dns add "$zone" TXT "$name" "$CERTBOT_VALIDATION" --ttl 5m --json \
    | sed -n 's/.*"id": *\([0-9][0-9]*\).*/\1/p' | head -n 1)
  [ -n "$id" ] || { echo "could not add the TXT record" >&2; exit 1; }
  echo "$id" # certbot passes this to the cleanup hook as CERTBOT_AUTH_OUTPUT
  # wait until all four nameservers answer with it (usually a few seconds)
  for i in $(seq 1 60); do
    n=0
    for ns in ns1 ns2 ns3 ns4; do
      dig +short TXT "$name.$zone" "@$ns.nameocean.net" | grep -q -- "$CERTBOT_VALIDATION" && n=$((n + 1))
    done
    [ "$n" -eq 4 ] && exit 0
    sleep 2
  done
  echo "the TXT record is not on every nameserver after 2 minutes" >&2
  exit 1
  ;;
cleanup)
  [ -z "$CERTBOT_AUTH_OUTPUT" ] || nameocean dns rm "$zone" TXT "$CERTBOT_AUTH_OUTPUT"
  ;;
*)
  echo "usage: $0 auth|cleanup" >&2
  exit 2
  ;;
esac

Sin la CLI

El hook realiza dos llamadas a la API que puede hacer desde cualquier cliente ACME o lenguaje:

curl
$ curl -X POST https://nameocean.net/api/v1/dns/example.com/records \
    -H "Authorization: Bearer $NAMEOCEAN_API_KEY" -H "Content-Type: application/json" \
    -d '{"type": "TXT", "hostname": "_acme-challenge", "text": "<validation>", "ttl": "5m"}'
{"id": 4190, "type": "TXT", "hostname": "_acme-challenge", …}
$ curl -X DELETE https://nameocean.net/api/v1/dns/example.com/records/TXT/4190 \
    -H "Authorization: Bearer $NAMEOCEAN_API_KEY"