diff --git a/installer.sh b/installer.sh index d0c40a7..9210510 100644 --- a/installer.sh +++ b/installer.sh @@ -579,241 +579,7 @@ chown www-data:www-data /var/lib/mailwolt/wizard chmod 775 /var/lib/mailwolt/wizard step "Hilfsskripte & Konfiguration installieren" "5 Sek" -cat > /usr/local/sbin/mailwolt-apply-domains <<'HELPER' -#!/usr/bin/env bash -set -euo pipefail - -UI_HOST=""; WEBMAIL_HOST=""; MAIL_HOST=""; SSL_AUTO=0 -while [[ $# -gt 0 ]]; do - case "$1" in - --ui-host) UI_HOST="$2"; shift 2 ;; - --webmail-host) WEBMAIL_HOST="$2"; shift 2 ;; - --mail-host) MAIL_HOST="$2"; shift 2 ;; - --ssl-auto) SSL_AUTO="$2"; shift 2 ;; - *) shift ;; - esac -done - -PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') -PHP_FPM_SOCK="/run/php/php${PHPV}-fpm.sock" -[ -S "$PHP_FPM_SOCK" ] || PHP_FPM_SOCK="/run/php/php-fpm.sock" - -APP_DIR="/var/www/mailwolt" -NGINX_SITE="/etc/nginx/sites-available/mailwolt.conf" -ACME_ROOT="/var/www/letsencrypt" -mkdir -p "${ACME_ROOT}/.well-known/acme-challenge" - -# --- Phase 1: HTTP-only Vhosts mit ACME-Challenge --- -cat > "${NGINX_SITE}" </dev/null | grep -q 'inet6' -} - -cert_needs_action() { - local domain="$1" - local cert="/etc/letsencrypt/live/${domain}/fullchain.pem" - [ ! -f "${cert}" ] && return 0 - # 0 = gültig für >10 Tage → überspringen; 1 = läuft ab → erneuern - openssl x509 -checkend 864000 -noout -in "${cert}" 2>/dev/null && return 1 - return 0 -} - -certbot_safe() { - local domain="$1" - local has_aaaa - has_aaaa=$(dig +short AAAA "${domain}" 2>/dev/null | head -1) - if [ -n "${has_aaaa}" ] && ! has_global_ipv6; then - echo "[!] ${domain}: AAAA-Record vorhanden aber kein IPv6 auf diesem Server — Let's Encrypt würde fehlschlagen. Self-signed wird verwendet." >&2 - return 1 - fi - certbot certonly --webroot \ - -w "${ACME_ROOT}" \ - -d "${domain}" \ - --non-interactive --agree-tos \ - --email "webmaster@${domain}" \ - --no-eff-email -} - -if [ "${SSL_AUTO}" = "1" ]; then - for DOMAIN in "${UI_HOST}" "${WEBMAIL_HOST}"; do - [ -z "${DOMAIN}" ] && continue - if cert_needs_action "${DOMAIN}"; then - certbot_safe "${DOMAIN}" || true - fi - done -fi - -# --- Phase 3: Finale Vhosts --- -# Nur HTTPS wenn LE-Cert tatsächlich vorhanden, sonst HTTP-only (kein self-signed Fallback) -UI_HAS_CERT=0 -WM_HAS_CERT=0 -[ -f "/etc/letsencrypt/live/${UI_HOST}/fullchain.pem" ] && UI_HAS_CERT=1 -[ -f "/etc/letsencrypt/live/${WEBMAIL_HOST}/fullchain.pem" ] && WM_HAS_CERT=1 -( - -if [ "${UI_HAS_CERT}" = "1" ] || [ "${WM_HAS_CERT}" = "1" ]; then - # Mindestens ein Cert vorhanden → HTTP-Redirect Block - cat < "${NGINX_SITE}" - -# State-Dateien schreiben — korrekt pro Domain, nicht pauschal "done" -STATE_DIR="/var/lib/mailwolt/wizard" -if [ -d "${STATE_DIR}" ]; then - # UI: done nur wenn LE-Cert tatsächlich vorhanden, sonst error - if [ -f "${STATE_DIR}/ui" ]; then - if [ "${UI_HAS_CERT}" = "1" ]; then - printf "done" > "${STATE_DIR}/ui" - else - printf "error" > "${STATE_DIR}/ui" - fi - fi - # Webmail: done nur wenn LE-Cert tatsächlich vorhanden, sonst error - if [ -f "${STATE_DIR}/webmail" ]; then - if [ "${WM_HAS_CERT}" = "1" ]; then - printf "done" > "${STATE_DIR}/webmail" - else - printf "error" > "${STATE_DIR}/webmail" - fi - fi - # Mail-Domain: kein certbot im Wizard für MX → skip (kein Fehler, nur nicht zutreffend) - [ -f "${STATE_DIR}/mail" ] && printf "skip" > "${STATE_DIR}/mail" - - # done-Signal: immer schreiben damit der 2s-Poll stoppt - if [ "${UI_HAS_CERT}" = "1" ] || [ "${WM_HAS_CERT}" = "1" ]; then - # Erst done schreiben, dann 6s warten bevor nginx auf HTTPS wechselt — - # so hat der Browser Zeit den "Zum Login"-Button zu rendern bevor der Switch passiert - printf "1" > "${STATE_DIR}/done" - sleep 6 - else - printf "0" > "${STATE_DIR}/done" - fi -fi - -nginx -t && systemctl reload nginx -HELPER -chmod 755 /usr/local/sbin/mailwolt-apply-domains +install -m 755 "${APP_DIR}/scripts/mailwolt-apply-domains" /usr/local/sbin/mailwolt-apply-domains # ===== mailwolt-update installieren ===== install -m 755 "${APP_DIR}/update.sh" /usr/local/sbin/mailwolt-update diff --git a/scripts/mailwolt-apply-domains b/scripts/mailwolt-apply-domains new file mode 100755 index 0000000..c00ba00 --- /dev/null +++ b/scripts/mailwolt-apply-domains @@ -0,0 +1,232 @@ +#!/usr/bin/env bash +set -euo pipefail + +UI_HOST=""; WEBMAIL_HOST=""; MAIL_HOST=""; SSL_AUTO=0 +while [[ $# -gt 0 ]]; do + case "$1" in + --ui-host) UI_HOST="$2"; shift 2 ;; + --webmail-host) WEBMAIL_HOST="$2"; shift 2 ;; + --mail-host) MAIL_HOST="$2"; shift 2 ;; + --ssl-auto) SSL_AUTO="$2"; shift 2 ;; + *) shift ;; + esac +done + +PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') +PHP_FPM_SOCK="/run/php/php${PHPV}-fpm.sock" +[ -S "$PHP_FPM_SOCK" ] || PHP_FPM_SOCK="/run/php/php-fpm.sock" + +APP_DIR="/var/www/mailwolt" +NGINX_SITE="/etc/nginx/sites-available/mailwolt.conf" +ACME_ROOT="/var/www/letsencrypt" +mkdir -p "${ACME_ROOT}/.well-known/acme-challenge" + +# --- Phase 1: HTTP-only Vhosts mit ACME-Challenge --- +cat > "${NGINX_SITE}" </dev/null | grep -q 'inet6' +} + +cert_needs_action() { + local domain="$1" + local cert="/etc/letsencrypt/live/${domain}/fullchain.pem" + [ ! -f "${cert}" ] && return 0 + # 0 = gültig für >10 Tage → überspringen; 1 = läuft ab → erneuern + openssl x509 -checkend 864000 -noout -in "${cert}" 2>/dev/null && return 1 + return 0 +} + +certbot_safe() { + local domain="$1" + local has_aaaa + has_aaaa=$(dig +short AAAA "${domain}" 2>/dev/null | head -1) + if [ -n "${has_aaaa}" ] && ! has_global_ipv6; then + echo "[!] ${domain}: AAAA-Record vorhanden aber kein IPv6 auf diesem Server — Let's Encrypt würde fehlschlagen. Self-signed wird verwendet." >&2 + return 1 + fi + certbot certonly --webroot \ + -w "${ACME_ROOT}" \ + -d "${domain}" \ + --non-interactive --agree-tos \ + --email "webmaster@${domain}" \ + --no-eff-email +} + +if [ "${SSL_AUTO}" = "1" ]; then + for DOMAIN in "${UI_HOST}" "${WEBMAIL_HOST}"; do + [ -z "${DOMAIN}" ] && continue + if cert_needs_action "${DOMAIN}"; then + certbot_safe "${DOMAIN}" || true + fi + done +fi + +# --- Phase 3: Finale Vhosts --- +# Nur HTTPS wenn LE-Cert tatsächlich vorhanden, sonst HTTP-only (kein self-signed Fallback) +UI_HAS_CERT=0 +WM_HAS_CERT=0 +[ -f "/etc/letsencrypt/live/${UI_HOST}/fullchain.pem" ] && UI_HAS_CERT=1 +[ -f "/etc/letsencrypt/live/${WEBMAIL_HOST}/fullchain.pem" ] && WM_HAS_CERT=1 +( + +if [ "${UI_HAS_CERT}" = "1" ] || [ "${WM_HAS_CERT}" = "1" ]; then + # Mindestens ein Cert vorhanden → HTTP-Redirect Block + cat < "${NGINX_SITE}" + +# State-Dateien schreiben — korrekt pro Domain, nicht pauschal "done" +STATE_DIR="/var/lib/mailwolt/wizard" +if [ -d "${STATE_DIR}" ]; then + # UI: done nur wenn LE-Cert tatsächlich vorhanden, sonst error + if [ -f "${STATE_DIR}/ui" ]; then + if [ "${UI_HAS_CERT}" = "1" ]; then + printf "done" > "${STATE_DIR}/ui" + else + printf "error" > "${STATE_DIR}/ui" + fi + fi + # Webmail: done nur wenn LE-Cert tatsächlich vorhanden, sonst error + if [ -f "${STATE_DIR}/webmail" ]; then + if [ "${WM_HAS_CERT}" = "1" ]; then + printf "done" > "${STATE_DIR}/webmail" + else + printf "error" > "${STATE_DIR}/webmail" + fi + fi + # Mail-Domain: kein certbot im Wizard für MX → skip (kein Fehler, nur nicht zutreffend) + [ -f "${STATE_DIR}/mail" ] && printf "skip" > "${STATE_DIR}/mail" + + # done-Signal: immer schreiben damit der 2s-Poll stoppt + if [ "${UI_HAS_CERT}" = "1" ] || [ "${WM_HAS_CERT}" = "1" ]; then + # Erst done schreiben, dann 6s warten bevor nginx auf HTTPS wechselt — + # so hat der Browser Zeit den "Zum Login"-Button zu rendern bevor der Switch passiert + printf "1" > "${STATE_DIR}/done" + sleep 6 + else + printf "0" > "${STATE_DIR}/done" + fi +fi + +nginx -t && systemctl reload nginx