Entwickler → Anleitungen

Wildcard-Zertifikate mit DNS-01 und certbot

Bei der DNS-01-Challenge weisen Sie die Kontrolle über eine Domain mit einem TXT-Eintrag statt einer Webseite nach. Nur so erhalten Sie ein Wildcard-Zertifikat, und es funktioniert auch für Server, die aus dem Internet nicht erreichbar sind. Mit diesem Hook veröffentlicht certbot diesen Eintrag über die NameOcean API.

NameOcean stellt selbst für jede Domain auf NameOcean DNS ein kostenloses Wildcard-Zertifikat aus, verlängert es und stellt Ihnen einen Download-Link bereit (SSL oder POST /ssl/example.com). Nutzen Sie diese Anleitung, wenn Ihr Server sein eigenes Zertifikat beziehen und verlängern soll.

Was Sie brauchen

  • Das DNS der Domain liegt bei NameOcean (Nameserver ns1.nameocean.net bis ns4.nameocean.net).
  • certbot, dig (dnsutils oder bind-utils) und die nameocean CLI auf dem Server.
  • Ein API-Schlüssel mit dns:read und dns:write. Beschränken Sie ihn nach Möglichkeit auf die IP-Adresse des Servers.

1. Hook installieren

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

Der Schlüssel liegt in einer Datei, die nur root lesen kann; die gespeicherten Verlängerungseinstellungen von certbot enthalten ihn nie.

2. Zertifikat beziehen

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"

Für jeden Namen ermittelt der Hook die zugehörige Zone unter Ihren NameOcean-Zonen, legt _acme-challenge als TXT-Eintrag an und wartet, bis alle vier Nameserver ihn ausliefern – meist innerhalb von zehn Sekunden. Nach der Prüfung löscht der Cleanup-Schritt genau den Eintrag, den er angelegt hat. example.com und *.example.com verwenden beide _acme-challenge.example.com; die beiden Werte werden nebeneinander veröffentlicht.

3. Verlängerungen

certbot speichert die Hooks zusammen mit dem Zertifikat, sodass sein Timer es auf dieselbe Weise verlängert. Testen Sie das einmal:

root@server
# certbot renew --dry-run

Jeder Eintrag, den der Hook anlegt und löscht, erscheint unter Dashboard → Aktivität mit dem verwendeten Schlüssel und löst die Webhook-Ereignisse dns.record.created und dns.record.deleted aus.

Der Hook

Lesen Sie ihn, bevor Sie ihn als root ausführen. Er ruft nur die nameocean CLI und dig auf.

/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

Ohne die CLI

Der Hook führt zwei API-Aufrufe aus, die Sie aus jedem ACME-Client und jeder Sprache heraus nutzen können:

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"