#!/usr/bin/env bash set -euo pipefail source ./lib.sh log "Let's Encrypt Deploy-Hooks und Wrapper anlegen …" # ------------------------------------------------------------------- # 2) POSIX-kompatibler Deploy-Wrapper (von Certbot aufgerufen) # ------------------------------------------------------------------- cat >/usr/local/sbin/mailwolt-deploy.sh <<'WRAP' #!/bin/sh # POSIX-safe Certbot deploy-hook (ohne bashisms) set -eu # Installer-ENV laden (liefert UI_HOST/WEBMAIL_HOST/MAIL_HOSTNAME etc.) if [ -r /etc/mailwolt/installer.env ]; then # shellcheck disable=SC1091 . /etc/mailwolt/installer.env fi UI_HOST="${UI_HOST:-}" WEBMAIL_HOST="${WEBMAIL_HOST:-}" MAIL_HOSTNAME="${MAIL_HOSTNAME:-}" ACME_BASE="/etc/letsencrypt/live" copy_cert() { le_base="$1" # z.B. /etc/letsencrypt/live/ui.example.com target_dir="$2" # z.B. /etc/ssl/ui cert="${le_base}/fullchain.pem" key="${le_base}/privkey.pem" [ -s "$cert" ] || { echo "[deploy] missing $cert"; return 1; } [ -s "$key" ] || { echo "[deploy] missing $key"; return 1; } mkdir -p "$target_dir" # echte Dateien (keine Symlinks), feste Rechte install -m 0644 "$cert" "${target_dir}/fullchain.pem" install -m 0600 "$key" "${target_dir}/privkey.pem" echo "[+] Copied ${target_dir}/fullchain.pem und privkey.pem ← ${le_base}" } reload_services() { kind="$1" # ui | mail if command -v systemctl >/dev/null 2>&1; then if [ "$kind" = "mail" ]; then systemctl reload postfix 2>/dev/null || true systemctl reload dovecot 2>/dev/null || true else systemctl reload nginx 2>/dev/null || true fi fi } # Certbot-Kontext LINEAGE="${RENEWED_LINEAGE:-}" HOST="" if [ -n "$LINEAGE" ]; then HOST="$(basename "$LINEAGE")" fi did_any=0 maybe_copy_for_host() { host="$1" dir="$2" [ -n "$host" ] || return 0 # Fall A: Certbot liefert RENEWED_DOMAINS (Space-getrennt) if [ -n "${RENEWED_DOMAINS:-}" ]; then case " ${RENEWED_DOMAINS} " in *" ${host} "*) copy_cert "${ACME_BASE}/${host}" "${dir}" && did_any=1 ;; esac return 0 fi # Fall B: Erst-issue / kein RENEWED_DOMAINS → über LINEAGE matchen if [ -n "$HOST" ] && [ "$HOST" = "$host" ]; then copy_cert "${ACME_BASE}/${host}" "${dir}" && did_any=1 fi } # Gezieltes Kopieren maybe_copy_for_host "$UI_HOST" "/etc/ssl/ui" maybe_copy_for_host "$WEBMAIL_HOST" "/etc/ssl/webmail" maybe_copy_for_host "$MAIL_HOSTNAME" "/etc/ssl/mail" # Fallback (Erstlauf): kopiere vorhandene Lineages if [ "$did_any" -eq 0 ]; then [ -n "$UI_HOST" ] && [ -d "${ACME_BASE}/${UI_HOST}" ] && copy_cert "${ACME_BASE}/${UI_HOST}" "/etc/ssl/ui" [ -n "$WEBMAIL_HOST" ] && [ -d "${ACME_BASE}/${WEBMAIL_HOST}" ] && copy_cert "${ACME_BASE}/${WEBMAIL_HOST}" "/etc/ssl/webmail" [ -n "$MAIL_HOSTNAME" ] && [ -d "${ACME_BASE}/${MAIL_HOSTNAME}" ] && copy_cert "${ACME_BASE}/${MAIL_HOSTNAME}" "/etc/ssl/mail" fi # TLSA-Refresh (tolerant falls App noch nicht ready) if command -v php >/dev/null 2>&1 && [ -f /var/www/mailwolt/artisan ]; then (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true fi # Services neu laden if [ -n "$HOST" ]; then if [ -n "$MAIL_HOSTNAME" ] && [ "$HOST" = "$MAIL_HOSTNAME" ]; then reload_services mail else reload_services ui fi else reload_services ui fi exit 0 WRAP chmod +x /usr/local/sbin/mailwolt-deploy.sh # ------------------------------------------------------------------- # 3) Certbot deploy-hook, der den Wrapper aufruft # ------------------------------------------------------------------- install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh <<'HOOK' #!/bin/sh exec /usr/local/sbin/mailwolt-deploy.sh HOOK chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh log "[✓] MailWolt Deploy-Hook eingerichtet" ##!/usr/bin/env bash #set -euo pipefail #source ./lib.sh # ## Persistente Installer-Variablen (werden vom Wrapper gelesen) #install -d -m 0755 /etc/mailwolt #cat >/etc/mailwolt/installer.env </usr/local/sbin/mw-deploy.sh <<'WRAP' ##!/usr/bin/env bash #set -euo pipefail # ## Installer-Variablen laden #set +u #[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env #set -u # #UI_HOST="${UI_HOST:-}" #WEBMAIL_HOST="${WEBMAIL_HOST:-}" #MAIL_HOSTNAME="${MAIL_HOSTNAME:-}" # ## --- Kopieren statt Symlinks (damit Laravel lesen kann) --------------------- #copy_cert() { # local le_base="$1" target_dir="$2" # local cert="${le_base}/fullchain.pem" # local key="${le_base}/privkey.pem" # # [[ -s "$cert" && -s "$key" ]] || return 0 # # install -d -m 0755 "$target_dir" # # # Vorhandene Symlinks entfernen, sonst kopierst du in die LE-Datei hinein # [ -L "${target_dir}/fullchain.pem" ] && rm -f "${target_dir}/fullchain.pem" # [ -L "${target_dir}/privkey.pem" ] && rm -f "${target_dir}/privkey.pem" # # # Echte Dateien ablegen # install -m 0644 "$cert" "${target_dir}/fullchain.pem" # install -m 0600 "$key" "${target_dir}/privkey.pem" # # echo "[+] Copied ${target_dir}/fullchain.pem und privkey.pem ← ${le_base}" #} # ## Nur Domains bearbeiten, die in diesem Lauf betroffen sind. ## Bei manchen Distros ist RENEWED_DOMAINS auf Erst-issue leer -> Fallback nutzen. #RDOMS=" ${RENEWED_DOMAINS:-} " #did_any=0 # #maybe_copy_for() { # local host="$1" dir="$2" # [[ -z "$host" ]] && return 0 # if [[ "$RDOMS" == *" ${host} "* ]]; then # copy_cert "/etc/letsencrypt/live/${host}" "${dir}" # did_any=1 # fi #} # ## 1) Normalfall: nur die vom Certbot gemeldeten Hosts kopieren #maybe_copy_for "$UI_HOST" "/etc/ssl/ui" #maybe_copy_for "$WEBMAIL_HOST" "/etc/ssl/webmail" #maybe_copy_for "$MAIL_HOSTNAME" "/etc/ssl/mail" # ## 2) Fallback: Beim Erstlauf/Edge-Cases alles kopieren, was bereits existiert #if [[ "$did_any" -eq 0 ]]; then # [[ -n "$UI_HOST" && -d "/etc/letsencrypt/live/${UI_HOST}" ]] && copy_cert "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui" # [[ -n "$WEBMAIL_HOST" && -d "/etc/letsencrypt/live/${WEBMAIL_HOST}" ]] && copy_cert "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail" # [[ -n "$MAIL_HOSTNAME" && -d "/etc/letsencrypt/live/${MAIL_HOSTNAME}"]] && copy_cert "/etc/letsencrypt/live/${MAIL_HOSTNAME}"/etc/ssl/mail #fi # ## Optional: TLSA via Laravel (tolerant, falls App noch nicht gebaut) #if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ] && [ -f /var/www/mailwolt/artisan ]; then # (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true #fi # ## Nginx nur neu laden, wenn aktiv #if systemctl is-active --quiet nginx; then # systemctl reload nginx || true #fi #WRAP #chmod +x /usr/local/sbin/mw-deploy.sh # ## 2) Certbot-Deploy-Hook: ruft den Wrapper bei jeder erfolgreichen Ausstellung/Renew auf #install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy #cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh <<'HOOK' ##!/usr/bin/env bash #exec /usr/local/sbin/mw-deploy.sh #HOOK #chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-certs.sh # #log "[✓] MailWolt Deploy-Hook eingerichtet" ##!/usr/bin/env bash #set -euo pipefail #source ./lib.sh # ## Persistente Installer-Variablen (werden vom Wrapper gelesen) #install -d -m 0755 /etc/mailwolt #cat >/etc/mailwolt/installer.env </usr/local/sbin/mw-deploy.sh <<'WRAP' ##!/usr/bin/env bash #set -euo pipefail # ## Installer-Variablen laden #set +u #[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env #set -u # #UI_HOST="${UI_HOST:-}" #WEBMAIL_HOST="${WEBMAIL_HOST:-}" #MAIL_HOSTNAME="${MAIL_HOSTNAME:-}" # ## --- Kopieren statt Symlinks (damit Laravel lesen kann) --------------------- #copy_cert() { # local le_base="$1" target_dir="$2" # local cert="${le_base}/fullchain.pem" # local key="${le_base}/privkey.pem" # # [[ -s "$cert" && -s "$key" ]] || return 0 # # # Zielordner sicherstellen # install -d -m 0755 "$target_dir" # # # Falls vorher Symlinks existieren → entfernen, sonst würde "install" das Ziel des Links überschreiben # [ -L "${target_dir}/fullchain.pem" ] && rm -f "${target_dir}/fullchain.pem" # [ -L "${target_dir}/privkey.pem" ] && rm -f "${target_dir}/privkey.pem" # # # KOPIEREN mit sauberen Rechten (Chain world-readable, Key nur root) # install -m 0644 "$cert" "${target_dir}/fullchain.pem" # install -m 0600 "$key" "${target_dir}/privkey.pem" # # echo "[+] Copied ${target_dir}/fullchain.pem und privkey.pem ← ${le_base}" #} # ## Nur für Domains arbeiten, die in diesem Lauf betroffen sind #RDOMS=" ${RENEWED_DOMAINS:-} " # ## UI #if [[ -n "$UI_HOST" && "$RDOMS" == *" ${UI_HOST} "* ]]; then # copy_cert "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui" #fi ## Webmail #if [[ -n "$WEBMAIL_HOST" && "$RDOMS" == *" ${WEBMAIL_HOST} "* ]]; then # copy_cert "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail" #fi ## MX #if [[ -n "$MAIL_HOSTNAME" && "$RDOMS" == *" ${MAIL_HOSTNAME} "* ]]; then # copy_cert "/etc/letsencrypt/live/${MAIL_HOSTNAME}" "/etc/ssl/mail" #fi # ## Optional: TLSA via Laravel (still tolerant, falls App noch nicht gebaut) #if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ] && [ -f /var/www/mailwolt/artisan ]; then # (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true #fi # ## Nginx nur neu laden, wenn aktiv #if systemctl is-active --quiet nginx; then # systemctl reload nginx || true #fi #WRAP #chmod +x /usr/local/sbin/mw-deploy.sh # ## 2) Certbot-Deploy-Hook: ruft den Wrapper bei jeder erfolgreichen Ausstellung/Renew auf #install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy #cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK' ##!/usr/bin/env bash #exec /usr/local/sbin/mw-deploy.sh #HOOK #chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh # #log "[✓] MailWolt Deploy-Hook eingerichtet" ##!/usr/bin/env bash #set -euo pipefail #source ./lib.sh # #install -d -m 0755 /etc/mailwolt #cat >/etc/mailwolt/installer.env </usr/local/sbin/mw-deploy.sh <<'WRAP' ##!/usr/bin/env bash #set -euo pipefail # ## Installer-Variablen laden (UI_HOST, WEBMAIL_HOST, MAIL_HOSTNAME, optional LE_EMAIL etc.) #set +u #[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env #set -u # #UI_HOST="${UI_HOST:-}" #WEBMAIL_HOST="${WEBMAIL_HOST:-}" #MAIL_HOSTNAME="${MAIL_HOSTNAME:-}" # #link_if() { # local le_base="$1" target_dir="$2" # local cert="${le_base}/fullchain.pem" # local key="${le_base}/privkey.pem" # [[ -s "$cert" && -s "$key" ]] || return 0 # install -d -m 0755 "$target_dir" # ln -sf "$cert" "${target_dir}/fullchain.pem" # ln -sf "$key" "${target_dir}/privkey.pem" # chmod 644 "${target_dir}/fullchain.pem" 2>/dev/null || true # chmod 600 "${target_dir}/privkey.pem" 2>/dev/null || true # echo "[+] Linked ${target_dir} -> ${le_base}" #} # ## Nur für Domains arbeiten, die im aktuellen Lauf erneuert/ausgestellt wurden #RDOMS=" ${RENEWED_DOMAINS:-} " # ## UI #if [[ -n "$UI_HOST" && "$RDOMS" == *" ${UI_HOST} "* ]]; then # link_if "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui" #fi ## Webmail #if [[ -n "$WEBMAIL_HOST" && "$RDOMS" == *" ${WEBMAIL_HOST} "* ]]; then # link_if "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail" #fi ## MX #if [[ -n "$MAIL_HOSTNAME" && "$RDOMS" == *" ${MAIL_HOSTNAME} "* ]]; then # link_if "/etc/letsencrypt/live/${MAIL_HOSTNAME}" "/etc/ssl/mail" #fi # ## Optional: TLSA via Laravel, falls App schon vorhanden (sonst still überspringen) #if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ]; then # (cd /var/www/mailwolt && php artisan dns:tlsa:refresh) || true #fi # ## Nginx nur neu laden, wenn aktiv #if systemctl is-active --quiet nginx; then # systemctl reload nginx || true #fi #WRAP #chmod +x /usr/local/sbin/mw-deploy.sh # ## 2) Certbot-Deploy-Hooks einrichten (ruft nur den Wrapper auf) #install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy #cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK' ##!/usr/bin/env bash #exec /usr/local/sbin/mw-deploy.sh #HOOK #chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh # #log "[✓] MailWolt Deploy-Hook eingerichtet" ##!/usr/bin/env bash #set -euo pipefail #source ./lib.sh # #log "Let's Encrypt Deploy-Hooks und Wrapper anlegen …" # ## 1) Wrapper-Skript, das Symlinks setzt und Nginx reloaded #cat >/usr/local/sbin/mw-deploy.sh <<'WRAP' ##!/usr/bin/env bash #set -euo pipefail # #link_if() { # local le_base="$1" target_dir="$2" # local cert="${le_base}/fullchain.pem" # local key="${le_base}/privkey.pem" # [[ -s "$cert" && -s "$key" ]] || return 0 # install -d -m 0755 "$target_dir" # ln -sf "$cert" "${target_dir}/fullchain.pem" # ln -sf "$key" "${target_dir}/privkey.pem" # chmod 644 "${target_dir}/fullchain.pem" 2>/dev/null || true # chmod 600 "${target_dir}/privkey.pem" 2>/dev/null || true # echo "[+] Linked ${target_dir} -> ${le_base}" #} # #UI_HOST="${UI_HOST:-}" #WEBMAIL_HOST="${WEBMAIL_HOST:-}" #MAIL_HOSTNAME="${MAIL_HOSTNAME:-}" # #[[ -n "$UI_HOST" ]] && link_if "/etc/letsencrypt/live/${UI_HOST}" "/etc/ssl/ui" #[[ -n "$WEBMAIL_HOST" ]] && link_if "/etc/letsencrypt/live/${WEBMAIL_HOST}" "/etc/ssl/webmail" #[[ -n "$MAIL_HOSTNAME" ]] && link_if "/etc/letsencrypt/live/${MAIL_HOSTNAME}" "/etc/ssl/mail" # #if systemctl is-active --quiet nginx; then # systemctl reload nginx || true #fi #WRAP # #chmod +x /usr/local/sbin/mw-deploy.sh # ## 2) Certbot Deploy-Hook-Verzeichnis + Symlink für Renewals #install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy #cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK' ##!/usr/bin/env bash #exec /usr/local/sbin/mw-deploy.sh #HOOK #chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh # #log "[✓] MailWolt Deploy-Hook eingerichtet" # ###!/usr/bin/env bash ##set -euo pipefail ##source ./lib.sh ## ### ──────────────────────────────────────────────────────────────────────────── ### 21-le-deploy-hook.sh ### • legt /etc/mailwolt/installer.env an (falls fehlt) ### • erzeugt Deploy-Hooks: ### - 50-mailwolt-symlinks.sh → verlinkt LE-Zerts nach /etc/ssl/{ui,webmail,mail} ### - 60-mailwolt-tlsa.sh → aktualisiert TLSA (3 1 1) für MX bei jedem Renew ### • KEIN Reload von Postfix/Dovecot (kommt später im Installer) ### ──────────────────────────────────────────────────────────────────────────── ## ### 0) Hostnamen persistent speichern (für spätere Deploys) ##install -d -m 0755 /etc/mailwolt ##if [[ ! -f /etc/mailwolt/installer.env ]]; then ## cat >/etc/mailwolt/installer.env </etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh </dev/null || true ## chmod 600 "\${target_dir}/privkey.pem" 2>/dev/null || true ## echo "[+] Linked \${target_dir} -> \${le_base}" ##} ## ### Verlinken (nur wenn Host konfiguriert) ##[[ -n "${UI_HOST}" ]] && link_if "\$UI_LE" "\$UI_SSL_DIR" ##[[ -n "${WEBMAIL_HOST}" ]] && link_if "\$WEBMAIL_LE" "\$WEBMAIL_SSL_DIR" ##[[ -n "${MAIL_HOSTNAME}" ]] && link_if "\$MX_LE" "\$MAIL_SSL_DIR" ## ### Nur reloaden, wenn Nginx aktiv ist (Installer startet ihn später erst) ##if systemctl is-active --quiet nginx; then ## systemctl reload nginx || true ##fi ##HOOK ##chmod +x /etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh ## ### ──────────────────────────────────────────────────────────────────────────── ### 3) 60-mailwolt-tlsa.sh ### → nutzt Laravel, falls vorhanden; sonst Fallback mit OpenSSL. ### → schreibt nur, wenn sich der Hash geändert hat (idempotent) ### ──────────────────────────────────────────────────────────────────────────── ##cat >/etc/letsencrypt/renewal-hooks/deploy/60-mailwolt-tlsa.sh <<'HOOK' ###!/usr/bin/env bash ##set -euo pipefail ## ### installer.env lesen ##set +u ##[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env ##set -u ## ##APP_ENV_VAL="${APP_ENV:-production}" ##BASE_DOMAIN_VAL="${BASE_DOMAIN:-example.com}" ## ##case "$APP_ENV_VAL" in ## local|dev|development) exit 0 ;; ##esac ##[ "$BASE_DOMAIN_VAL" = "example.com" ] && exit 0 ## ##MX_HOST="${MAIL_HOSTNAME:-}" ##SERVICE="_25._tcp" ##DNS_DIR="/etc/mailwolt/dns" ##OUT_FILE="${DNS_DIR}/${MX_HOST}.tlsa.txt" ## ### Nur reagieren, wenn MX-Zertifikat betroffen war ##case " ${RENEWED_DOMAINS:-} " in ## *" ${MX_HOST} "*) ;; ## *) exit 0 ;; ##esac ## ##CERT="${RENEWED_LINEAGE}/fullchain.pem" ##[ -s "$CERT" ] || exit 0 ## ### Wenn Laravel vorhanden ist → interner Command (DB + Datei idempotent) ##if command -v php >/dev/null 2>&1 && [ -d /var/www/mailwolt ]; then ## cd /var/www/mailwolt || exit 0 ## php artisan dns:tlsa:refresh || true ## exit 0 ##fi ## ### Fallback: nur Datei aktualisieren, wenn Hash sich ändert ##HASH="$(openssl x509 -in "$CERT" -noout -pubkey \ ## | openssl pkey -pubin -outform DER \ ## | openssl dgst -sha256 | sed 's/^.*= //')" ##NEW_LINE="${SERVICE}.${MX_HOST}. IN TLSA 3 1 1 ${HASH}" ## ##mkdir -p "$DNS_DIR" ## ##if [ -r "$OUT_FILE" ] && grep -q "IN TLSA" "$OUT_FILE"; then ## if grep -q "$HASH" "$OUT_FILE"; then ## echo "[TLSA] Unverändert – kein Update nötig." ## exit 0 ## fi ##fi ## ##echo "$NEW_LINE" > "$OUT_FILE" ##echo "[TLSA] Aktualisiert: $NEW_LINE" ##HOOK ##chmod +x /etc/letsencrypt/renewal-hooks/deploy/60-mailwolt-tlsa.sh ## ### ──────────────────────────────────────────────────────────────────────────── ##echo "[✓] Deploy-Hooks installiert."