#!/bin/bash
# h42 cert – Let's-Encrypt-Zertifikat über dehydrated holen und in die tägliche Erneuerung aufnehmen.
#   h42 cert add <fqdn> [--challenge http|dns] [--force]   eintragen und sofort ausstellen
#                                                          (--force: neu ausstellen, obwohl noch gültig)
#   h42 cert del <fqdn> [--purge]   austragen, wird nicht mehr erneuert (--purge: Zertifikat auch löschen)
#   h42 cert exists <fqdn> [--purge]   Exit 0: gäbe für del etwas zu tun (eingetragen; mit --purge auch: Zertifikat
#                                      liegt noch da), 3: nichts vorhanden
#   h42 cert list                   Zertifikate mit Challenge und Ablaufdatum
# Challenge (Default: CERT_CHALLENGE in /etc/h42/h42.conf, ab Werk http):
#   http  HTTP-01: der default_server auf Port 80 (hub_h42_net.conf) beantwortet sie für jeden Namen –
#         nötig ist nur ein öffentlicher DNS-Eintrag auf hov4 (h42 dns add).
#   dns   DNS-01: TXT _acme-challenge.<fqdn> per nsupdate auf ns1 (Hook /usr/lib/h42/hooks/dehydrated),
#         geht auch für Namen, die aus dem Internet nicht erreichbar sind (z. B. Zugriff "lan").
# Die Wahl steht je Zertifikat in <certs>/<fqdn>/config und gilt so auch für die Erneuerung.
set -euo pipefail
. "${H42_LIB:-/usr/lib/h42}/common.sh"
need_root

hook=$H42_LIB/hooks/dehydrated

challenge_of() {  # challenge_of <fqdn>: http|dns
  if grep -qsx 'CHALLENGETYPE=dns-01' "$DEHYDRATED_CERTS/$1/config"; then echo dns; else echo http; fi
}

set_challenge() {  # set_challenge <fqdn> http|dns: Zertifikats-Config für die Erneuerung schreiben
  local conf=$DEHYDRATED_CERTS/$1/config
  if [ -e "$conf" ]; then
    sed -i '/^# h42 cert:/d; /^CHALLENGETYPE=/d; /^HOOK=/d' "$conf"
    [ -s "$conf" ] || rm -f "$conf"
  fi
  if [ "$2" = dns ]; then
    install -d -m 700 "$DEHYDRATED_CERTS/$1"
    printf '# h42 cert: DNS-01 über nsupdate auf %s\nCHALLENGETYPE=dns-01\nHOOK=%s\n' "$NS_SERVER" "$hook" >> "$conf"
  fi
}

case ${1:-} in
  add)
    fqdn=${2:?fqdn fehlt}; challenge=${CERT_CHALLENGE:-http}; force=
    shift 2
    while [ $# -gt 0 ]; do
      case $1 in
        --challenge) challenge=${2:-}; shift 2 || usage ;;
        --challenge=*) challenge=${1#*=}; shift ;;
        --force) force=-x; shift ;;
        *) usage ;;
      esac
    done
    case $challenge in
      http) args=(--challenge http-01) ;;
      dns)  args=(--challenge dns-01 --hook "$hook") ;;
      *) die "unbekannte Challenge: $challenge (http|dns)" ;;
    esac
    if [ -e "$DEHYDRATED_CERTS/$fqdn/cert.pem" ]; then
      echo "Zertifikat für $fqdn ist schon vorhanden, gültig bis $(openssl x509 -noout -enddate -in "$DEHYDRATED_CERTS/$fqdn/cert.pem" | cut -d= -f2)"
      old_challenge=$(challenge_of "$fqdn")
      [ "$old_challenge" = "$challenge" ] || echo "Challenge wechselt von $old_challenge auf $challenge"
      if [ -n "$force" ]; then echo "--force: wird neu ausgestellt"; else echo "dehydrated stellt es nur neu aus, wenn es bald abläuft"; fi
    fi
    set_challenge "$fqdn" "$challenge"
    if grep -qsxF "$fqdn" "$DEHYDRATED_DOMAINS"; then
      echo "$fqdn steht schon in $DEHYDRATED_DOMAINS"
    else
      echo "$fqdn" >> "$DEHYDRATED_DOMAINS"
      echo "$fqdn in $DEHYDRATED_DOMAINS eingetragen"
    fi
    dehydrated -c -d "$fqdn" "${args[@]}" $force
    ls -l "$DEHYDRATED_CERTS/$fqdn/fullchain.pem"
    ;;
  del)
    fqdn=${2:?fqdn fehlt}
    case ${3:-} in ""|--purge) ;; *) usage ;; esac
    if grep -qsxF "$fqdn" "$DEHYDRATED_DOMAINS"; then
      sed -i "/^$(printf '%s' "$fqdn" | sed 's/\./\\./g')\$/d" "$DEHYDRATED_DOMAINS"
      echo "$fqdn aus $DEHYDRATED_DOMAINS entfernt"
    else
      echo "$fqdn steht nicht in $DEHYDRATED_DOMAINS"
    fi
    if [ ! -d "$DEHYDRATED_CERTS/$fqdn" ]; then
      echo "kein Zertifikat vorhanden: $DEHYDRATED_CERTS/$fqdn"
    elif [ "${3:-}" = --purge ]; then
      rm -r "$DEHYDRATED_CERTS/$fqdn"
      echo "Zertifikat gelöscht: $DEHYDRATED_CERTS/$fqdn"
    else
      echo "Zertifikat bleibt liegen: $DEHYDRATED_CERTS/$fqdn"
    fi
    ;;
  exists)
    fqdn=${2:?fqdn fehlt}
    case ${3:-} in ""|--purge) ;; *) usage ;; esac
    grep -qsxF "$fqdn" "$DEHYDRATED_DOMAINS" && exit 0
    if [ "${3:-}" = --purge ] && [ -d "$DEHYDRATED_CERTS/$fqdn" ]; then exit 0; fi
    exit 3
    ;;
  list)
    for c in "$DEHYDRATED_CERTS"/*/cert.pem; do
      [ -e "$c" ] || continue
      d=$(basename "$(dirname "$c")")
      printf '%-28s %-4s %s\n' "$d" "$(challenge_of "$d")" "$(openssl x509 -noout -enddate -in "$c" | cut -d= -f2)"
    done
    ;;
  *) usage ;;
esac
