Laudende Default seite entfernen

main
boksbc 2025-10-17 00:41:49 +02:00
parent 3c6185e291
commit ce31a51b18
4 changed files with 387 additions and 113 deletions

View File

@ -2,17 +2,18 @@
set -euo pipefail
source ./lib.sh
# -----------------------------------------------------------------------------
# ────────────────────────────────────────────────────────────────────────────
# 21-le-deploy-hook.sh
# - Legt /etc/mailwolt/installer.env (falls fehlt) an
# - Erzeugt LE-Deploy-Hooks:
# * 50-mailwolt-symlinks.sh → verlinkt LE-Zerts nach /etc/ssl/{ui,webmail,mail}
# * 60-mailwolt-tlsa.sh → schreibt TLSA (3 1 1) für MX nach jedem Renew
# -----------------------------------------------------------------------------
# • 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)
# ────────────────────────────────────────────────────────────────────────────
# 1) Sicherstellen, dass die Hosts persistent verfügbar sind
# 0) Hostnamen persistent speichern (für spätere Deploys)
install -d -m 0755 /etc/mailwolt
if [[ ! -f /etc/mailwolt/installer.env ]]; then
install -d -m 0755 /etc/mailwolt
cat >/etc/mailwolt/installer.env <<EOF
UI_HOST=${UI_HOST}
WEBMAIL_HOST=${WEBMAIL_HOST}
@ -21,91 +22,113 @@ EOF
echo "[+] /etc/mailwolt/installer.env erstellt."
fi
# 2) Deploy-Hooks-Verzeichnis anlegen
# 1) Deploy-Hooks-Verzeichnis anlegen
install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
# 3) Hook: LE-Zertifikate nach /etc/ssl/* verlinken und Nginx reloaden
cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<'HOOK'
# ────────────────────────────────────────────────────────────────────────────
# 2) 50-mailwolt-symlinks.sh
# ────────────────────────────────────────────────────────────────────────────
cat >/etc/letsencrypt/renewal-hooks/deploy/50-mailwolt-symlinks.sh <<HOOK
#!/usr/bin/env bash
set -euo pipefail
# Env aus dem Installer laden (falls vorhanden), aber unbound vermeiden
set +u
[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
set -u
UI_LE="/etc/letsencrypt/live/${UI_HOST}"
WEBMAIL_LE="/etc/letsencrypt/live/${WEBMAIL_HOST}"
MX_LE="/etc/letsencrypt/live/${MAIL_HOSTNAME}"
UI_SSL_DIR="/etc/ssl/ui"
WEBMAIL_SSL_DIR="/etc/ssl/webmail"
MAIL_SSL_DIR="/etc/ssl/mail"
# Falls Variablen nicht gesetzt sind → leere Defaults (vermeidet unbound)
UI_HOST="${UI_HOST:-}"
WEBMAIL_HOST="${WEBMAIL_HOST:-}"
MX_HOST="${MAIL_HOSTNAME:-}"
UI_LE="/etc/letsencrypt/live/${UI_HOST}"
WEBMAIL_LE="/etc/letsencrypt/live/${WEBMAIL_HOST}"
MX_LE="/etc/letsencrypt/live/${MX_HOST}"
# Zielverzeichnisse anlegen (einmalig)
install -d -m 0755 "\$UI_SSL_DIR" "\$WEBMAIL_SSL_DIR" "\$MAIL_SSL_DIR"
link_if() {
local le_base="$1" target_dir="$2"
local cert="${le_base}/fullchain.pem"
local key="${le_base}/privkey.pem"
if [[ -f "$cert" && -f "$key" ]]; then
install -d -m 0755 "$target_dir"
ln -sf "$cert" "${target_dir}/fullchain.pem"
ln -sf "$key" "${target_dir}/privkey.pem"
echo "[+] Linked ${target_dir} -> ${le_base}"
fi
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
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 linken, wenn Hostnamen vorhanden sind
[[ -n "$UI_HOST" ]] && link_if "$UI_LE" "$UI_SSL_DIR"
[[ -n "$WEBMAIL_HOST" ]] && link_if "$WEBMAIL_LE" "$WEBMAIL_SSL_DIR"
[[ -n "$MX_HOST" ]] && link_if "$MX_LE" "$MAIL_SSL_DIR"
# 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"
# sinnvolle Rechte (Key nur für root lesbar, Chain world-readable)
chmod 640 "${UI_SSL_DIR}/privkey.pem" 2>/dev/null || true
chmod 640 "${WEBMAIL_SSL_DIR}/privkey.pem" 2>/dev/null || true
chmod 640 "${MAIL_SSL_DIR}/privkey.pem" 2>/dev/null || true
chmod 644 "${UI_SSL_DIR}/fullchain.pem" 2>/dev/null || true
chmod 644 "${WEBMAIL_SSL_DIR}/fullchain.pem" 2>/dev/null || true
chmod 644 "${MAIL_SSL_DIR}/fullchain.pem" 2>/dev/null || true
# Nur Nginx neu laden Postfix/Dovecot startet später im Installer
systemctl reload nginx || true
# 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
# 4) Hook: TLSA (3 1 1) für MX nach jedem Renew/Issue generieren
# ────────────────────────────────────────────────────────────────────────────
# 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
# MAIL_HOSTNAME kommt von certbot via Environment nicht automatisch,
# daher direkt aus installer.env lesen, falls gesetzt.
# 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"
[[ -n "$MX_HOST" ]] || exit 0
# Nur reagieren, wenn das MX-Zert in diesem Run drin war
# Nur reagieren, wenn MX-Zertifikat betroffen war
case " ${RENEWED_DOMAINS:-} " in
*" ${MX_HOST} "*) ;; # ok
*" ${MX_HOST} "*) ;;
*) exit 0 ;;
esac
CERT="${RENEWED_LINEAGE}/fullchain.pem"
if [[ -s "$CERT" ]]; then
HASH="$(openssl x509 -in "$CERT" -noout -pubkey \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 | sed 's/^.*= //')"
TLSA_LINE="_25._tcp.${MX_HOST}. IN TLSA 3 1 1 ${HASH}"
install -d -m 0755 /etc/mailwolt/dns
echo "${TLSA_LINE}" > "/etc/mailwolt/dns/${MX_HOST}.tlsa.txt"
echo "[TLSA] ${TLSA_LINE}"
[ -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
chmod +x /etc/letsencrypt/renewal-hooks/deploy/60-mailwolt-tlsa.sh
# ────────────────────────────────────────────────────────────────────────────
echo "[✓] Deploy-Hooks installiert."

View File

@ -50,7 +50,8 @@ if [[ "$BASE_DOMAIN" != "example.com" ]]; then
issue "$WEBMAIL_HOST"
issue "$MAIL_HOSTNAME"
systemctl reload nginx || true
run-parts /etc/letsencrypt/renewal-hooks/deploy || true
systemctl reload nginx || true
# TLSA direkt einmal schreiben (Hook macht es bei Renewals sowieso)
MX_CERT="/etc/letsencrypt/live/${MAIL_HOSTNAME}/fullchain.pem"

View File

@ -3,12 +3,31 @@ set -euo pipefail
source ./lib.sh
log "MOTD installieren …"
install -d /usr/local/bin
cat >/usr/local/bin/mw-motd <<'SH'
#!/usr/bin/env bash
set -euo pipefail
NC="\033[0m"; CY="\033[1;36m"; GR="\033[1;32m"; YE="\033[1;33m"; RD="\033[1;31m"; GY="\033[0;90m"
printf "\033[1;36m"
# Farben
NC="\033[0m"; CY="\033[1;36m"; GR="\033[1;32m"; YE="\033[1;33m"; RD="\033[1;31m"; GY="\033[0;90m"; WH="\033[1;37m"
# Installer-Variablen (optional)
UI_HOST=""; WEBMAIL_HOST=""; MAIL_HOSTNAME=""; LE_EMAIL=""; PROXY_MODE=""; NPM_IP=""
if [ -r /etc/mailwolt/installer.env ]; then
# shellcheck disable=SC1091
. /etc/mailwolt/installer.env || true
fi
# Aus .env (falls vorhanden)
if [ -r /var/www/mailwolt/.env ]; then
LE_EMAIL="${LE_EMAIL:-$(grep -E '^LE_EMAIL=' /var/www/mailwolt/.env 2>/dev/null | sed 's/^LE_EMAIL=//')}"
PROXY_MODE="${PROXY_MODE:-$(grep -E '^PROXY_MODE=' /var/www/mailwolt/.env 2>/dev/null | sed 's/^PROXY_MODE=//')}"
NPM_IP="${NPM_IP:-$(grep -E '^NPM_IP=' /var/www/mailwolt/.env 2>/dev/null | sed 's/^NPM_IP=//')}"
fi
# ASCII-Header
printf "${CY}"
cat <<'ASCII'
:::: :::: ::: ::::::::::: ::: ::: ::: :::::::: ::: :::::::::::
+:+:+: :+:+:+ :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
@ -18,25 +37,101 @@ cat <<'ASCII'
#+# #+# #+# #+# #+# #+# #+#+# #+#+# #+# #+# #+# #+#
### ### ### ### ########### ########## ### ### ######## ########## ###
ASCII
printf "\033[0m\n"
printf "${NC}\n"
# Systemdaten
now="$(date '+%Y-%m-%d %H:%M:%S %Z')"
fqdn="$(hostname -f 2>/dev/null || hostname)"
ip_int="$(hostname -I 2>/dev/null | awk '{print $1}')"
ip_ext=""; command -v curl >/dev/null 2>&1 && ip_ext="$(curl -s --max-time 1 https://ifconfig.me || true)"
ip_ext=""; command -v curl >/dev/null 2>&1 && ip_ext="$(curl -fsS --max-time 1 https://ifconfig.me 2>/dev/null || true)"
upt="$(uptime -p 2>/dev/null || true)"
cores="$(nproc 2>/dev/null || echo -n '?')"
load="$(awk '{print $1" / "$2" / "$3}' /proc/loadavg 2>/dev/null)"
svc(){ systemctl is-active --quiet "$1" && echo -e "${GR}OK${NC}" || echo -e "${RD}FAIL${NC}"; }
# RAM/SWAP (MiB)
mem_total="$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null)"
mem_avail="$(awk '/MemAvailable/ {print int($2/1024)}' /proc/meminfo 2>/dev/null)"
mem_used=$(( (mem_total-mem_avail) ))
swap_total="$(awk '/SwapTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null)"
swap_free="$(awk '/SwapFree/ {print int($2/1024)}' /proc/meminfo 2>/dev/null)"
swap_used=$(( (swap_total-swap_free) ))
# Disks (/, /var)
disk_line(){
local mnt="$1"
df -hP "$mnt" 2>/dev/null | awk 'NR==2{printf "%s/%s (%s used)", $3,$2,$5}'
}
disk_root="$(disk_line /)"
disk_var="$(disk_line /var)"
svc_state(){
local unit="$1"
if systemctl is-active --quiet "$unit"; then
printf "${GR}OK${NC}"
else
printf "${RD}FAIL${NC}"
fi
}
# Zeilen
printf "${CY}Information as of:${NC} ${YE}%s${NC}\n" "$now"
printf "${GY}FQDN :${NC} %s\n" "$fqdn"
if [ -n "$ip_ext" ]; then printf "${GY}IP :${NC} %s ${GY}(ext:${NC} %s${GY})${NC}\n" "${ip_int:-?}" "$ip_ext"; else printf "${GY}IP :${NC} %s\n" "${ip_int:-?}"; fi
if [ -n "$ip_ext" ]; then
printf "${GY}IP :${NC} %s ${GY}(ext:${NC} %s${GY})${NC}\n" "${ip_int:-?}" "$ip_ext"
else
printf "${GY}IP :${NC} %s\n" "${ip_int:-?}"
fi
printf "${GY}Uptime :${NC} %s\n" "${upt:-?}"
printf "${GY}Cores :${NC} %s\n" "$cores"
printf "${GY}Load :${NC} %s (1/5/15)\n" "${load:-?}"
printf "${GY}Svc :${NC} postfix: $(svc postfix) dovecot: $(svc dovecot) nginx: $(svc nginx) mariadb: $(svc mariadb) redis: $(svc redis)\n"
printf "${GY}CPU :${NC} %s cores, load %s (1/5/15)\n" "$cores" "${load:-?}"
printf "${GY}RAM :${NC} %s MiB used / %s MiB total\n" "${mem_used:-?}" "${mem_total:-?}"
printf "${GY}SWAP :${NC} %s MiB used / %s MiB total\n" "${swap_used:-?}" "${swap_total:-?}"
printf "${GY}Disk / :${NC} %s\n" "${disk_root:-?}"
printf "${GY}Disk/var:${NC} %s\n" "${disk_var:-?}"
# App/Installer Infos
[ -n "$LE_EMAIL" ] && printf "${GY}LE E-Mail:${NC} %s\n" "$LE_EMAIL"
[ -n "$UI_HOST" ] && printf "${GY}UI Host :${NC} %s\n" "$UI_HOST"
[ -n "$WEBMAIL_HOST" ] && printf "${GY}Webmail :${NC} %s\n" "$WEBMAIL_HOST"
[ -n "$MAIL_HOSTNAME" ]&& printf "${GY}MX Host :${NC} %s\n" "$MAIL_HOSTNAME"
if [ -n "${PROXY_MODE:-}" ]; then
if [ "$PROXY_MODE" = "1" ]; then
printf "${GY}Proxy :${NC} ja (NPM: %s)\n" "${NPM_IP:-unbekannt}"
else
printf "${GY}Proxy :${NC} nein\n"
fi
fi
# Services
printf "${WH}\nServices:${NC}\n"
printf " nginx … %b\n" "$(svc_state nginx)"
printf " mariadb … %b\n" "$(svc_state mariadb)"
printf " redis-server … %b\n" "$(svc_state redis-server)"
printf " postfix … %b\n" "$(svc_state postfix)"
printf " dovecot … %b\n" "$(svc_state dovecot)"
# App Units (optional)
printf " mailwolt-ws … %b\n" "$(svc_state mailwolt-ws)"
printf " mailwolt-queue… %b\n" "$(svc_state mailwolt-queue)"
printf " mailwolt-schedule … %b\n" "$(svc_state mailwolt-schedule)"
# Zertifikate (kurzer Hinweis, optional)
show_cert_hint(){
local name="$1" path="$2"
if [ -r "$path" ]; then
local exp
exp="$(openssl x509 -in "$path" -noout -enddate 2>/dev/null | sed 's/notAfter=//')"
[ -n "$exp" ] && printf "${GY}%s cert:${NC} expires %s\n" "$name" "$exp"
fi
}
show_cert_hint "UI" "/etc/ssl/ui/fullchain.pem"
show_cert_hint "Webmail" "/etc/ssl/webmail/fullchain.pem"
show_cert_hint "MX" "/etc/ssl/mail/fullchain.pem"
echo
SH
chmod +x /usr/local/bin/mw-motd
# update-motd integration
if [[ -d /etc/update-motd.d ]]; then
cat >/etc/update-motd.d/10-mailwolt <<'SH'
#!/usr/bin/env bash
@ -45,8 +140,64 @@ SH
chmod +x /etc/update-motd.d/10-mailwolt
[[ -f /etc/update-motd.d/50-motd-news ]] && chmod -x /etc/update-motd.d/50-motd-news || true
else
# Fallback für Systeme ohne dynamic MOTD
cat >/etc/profile.d/10-mailwolt-motd.sh <<'SH'
case "$-" in *i*) /usr/local/bin/mw-motd ;; esac
SH
fi
: > /etc/motd 2>/dev/null || true
# altes /etc/motd leeren (sonst doppelt)
: > /etc/motd 2>/dev/null || true
##!/usr/bin/env bash
#set -euo pipefail
#source ./lib.sh
#
#log "MOTD installieren …"
#install -d /usr/local/bin
#cat >/usr/local/bin/mw-motd <<'SH'
##!/usr/bin/env bash
#set -euo pipefail
#NC="\033[0m"; CY="\033[1;36m"; GR="\033[1;32m"; YE="\033[1;33m"; RD="\033[1;31m"; GY="\033[0;90m"
#printf "\033[1;36m"
#cat <<'ASCII'
#:::: :::: ::: ::::::::::: ::: ::: ::: :::::::: ::: :::::::::::
#+:+:+: :+:+:+ :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
#+:+ +:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+
#+#+ +:+ +#+ +#++:++#++: +#+ +#+ +#+ +:+ +#+ +#+ +:+ +#+ +#+
#+#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+ +#+ +#+ +#+ +#+ +#+
##+# #+# #+# #+# #+# #+# #+#+# #+#+# #+# #+# #+# #+#
#### ### ### ### ########### ########## ### ### ######## ########## ###
#ASCII
#printf "\033[0m\n"
#now="$(date '+%Y-%m-%d %H:%M:%S %Z')"
#fqdn="$(hostname -f 2>/dev/null || hostname)"
#ip_int="$(hostname -I 2>/dev/null | awk '{print $1}')"
#ip_ext=""; command -v curl >/dev/null 2>&1 && ip_ext="$(curl -s --max-time 1 https://ifconfig.me || true)"
#upt="$(uptime -p 2>/dev/null || true)"
#cores="$(nproc 2>/dev/null || echo -n '?')"
#load="$(awk '{print $1" / "$2" / "$3}' /proc/loadavg 2>/dev/null)"
#svc(){ systemctl is-active --quiet "$1" && echo -e "${GR}OK${NC}" || echo -e "${RD}FAIL${NC}"; }
#printf "${CY}Information as of:${NC} ${YE}%s${NC}\n" "$now"
#printf "${GY}FQDN :${NC} %s\n" "$fqdn"
#if [ -n "$ip_ext" ]; then printf "${GY}IP :${NC} %s ${GY}(ext:${NC} %s${GY})${NC}\n" "${ip_int:-?}" "$ip_ext"; else printf "${GY}IP :${NC} %s\n" "${ip_int:-?}"; fi
#printf "${GY}Uptime :${NC} %s\n" "${upt:-?}"
#printf "${GY}Cores :${NC} %s\n" "$cores"
#printf "${GY}Load :${NC} %s (1/5/15)\n" "${load:-?}"
#printf "${GY}Svc :${NC} postfix: $(svc postfix) dovecot: $(svc dovecot) nginx: $(svc nginx) mariadb: $(svc mariadb) redis: $(svc redis)\n"
#SH
#chmod +x /usr/local/bin/mw-motd
#
#if [[ -d /etc/update-motd.d ]]; then
# cat >/etc/update-motd.d/10-mailwolt <<'SH'
##!/usr/bin/env bash
#/usr/local/bin/mw-motd
#SH
# chmod +x /etc/update-motd.d/10-mailwolt
# [[ -f /etc/update-motd.d/50-motd-news ]] && chmod -x /etc/update-motd.d/50-motd-news || true
#else
# cat >/etc/profile.d/10-mailwolt-motd.sh <<'SH'
#case "$-" in *i*) /usr/local/bin/mw-motd ;; esac
#SH
#fi
#: > /etc/motd 2>/dev/null || true

View File

@ -2,82 +2,181 @@
set -euo pipefail
source ./lib.sh
log() { printf "\033[1;32m[+]\033[0m %s\n" "$*"; }
ok() { printf " [\033[1;32mOK\033[0m]\n"; }
fail() { printf " [\033[1;31mFAIL\033[0m]\n"; }
# ─────────────────────────────────────────────────────────────
# Schöner Abschluss-Summary mit Farben, Diensten & Smoke-Test
# ─────────────────────────────────────────────────────────────
# Farben & Symbole
BOLD="\033[1m"; DIM="\033[2m"; NC="\033[0m"
GREEN="\033[1;32m"; RED="\033[1;31m"; YELLOW="\033[1;33m"; CYAN="\033[1;36m"; GREY="\033[0;90m"
OKS="${GREEN}OK${NC}"; FAILS="${RED}FAIL${NC}"; WARN="${YELLOW}!${NC}"
PLUS="${GREEN}[+]${NC}"
bar(){ printf "${CYAN}%s${NC}\n" "──────────────────────────────────────────────────────────────────────────────"; }
log(){ printf "${GREEN}[+]${NC} %s\n" "$*"; }
ok() { printf " [${OKS}]\n"; }
fail(){ printf " [${FAILS}]\n"; }
# Evtl. persistente Variablen laden (falls vom Installer geschrieben)
set +u
[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env
set -u
# Defaults / Umgebung
APP_USER="${APP_USER:-mailwolt}"
APP_GROUP="${APP_GROUP:-www-data}"
APP_DIR="${APP_DIR:-/var/www/${APP_USER}}"
BASE_DOMAIN="${BASE_DOMAIN:-example.com}"
UI_HOST="${UI_HOST:-}"
WEBMAIL_HOST="${WEBMAIL_HOST:-}"
MAIL_HOSTNAME="${MAIL_HOSTNAME:-}"
APP_ENV="${APP_ENV:-production}"
PROXY_MODE="${PROXY_MODE:-0}"
NPM_IP="${NPM_IP:-}"
LE_EMAIL="${LE_EMAIL:-admin@${BASE_DOMAIN}}"
ACME_WEBROOT="/var/www/letsencrypt"
# Zert-Pfade (werden idR via Hook symlinked)
UI_CERT="/etc/ssl/ui/fullchain.pem"
UI_KEY="/etc/ssl/ui/privkey.pem"
SCHEME="http"
WEBMAIL_CERT="/etc/ssl/webmail/fullchain.pem"
MAIL_CERT="/etc/ssl/mail/fullchain.pem"
SERVER_PUBLIC_IPV4="$(detect_ip)"
# IPs
SERVER_PUBLIC_IPV4="${SERVER_PUBLIC_IPV4:-$(detect_ip)}"
SERVER_PUBLIC_IPV6="${SERVER_PUBLIC_IPV6:-$(detect_ipv6)}"
# Scheme/URLs ableiten
SCHEME="http"
[[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SCHEME="https"
UI_HOST="${UI_HOST:-}"
APP_URL="${APP_URL:-${SCHEME}://${SERVER_PUBLIC_IPV4}}"
if [[ -n "$UI_HOST" ]]; then
APP_URL="${SCHEME}://${UI_HOST}"
fi
APP_URL="${SCHEME}://${UI_HOST:-$SERVER_PUBLIC_IPV4}"
WEBMAIL_URL="${SCHEME}://${WEBMAIL_HOST:-$SERVER_PUBLIC_IPV4}"
MAIL_HOSTNAME="${MAIL_HOSTNAME:-${SERVER_PUBLIC_IPV4}}"
# Erkennen, ob die Zert-Symlinks auf LE zeigen (nur kosmetisch)
real_target() { readlink -f -- "$1" 2>/dev/null || true; }
UI_CERT_TARGET="$(real_target "$UI_CERT")"
WEBMAIL_CERT_TARGET="$(real_target "$WEBMAIL_CERT")"
MAIL_CERT_TARGET="$(real_target "$MAIL_CERT")"
is_le(){ [[ "$1" == /etc/letsencrypt/live/*/fullchain.pem ]]; }
UI_LE=$([[ -n "$UI_CERT_TARGET" ]] && is_le "$UI_CERT_TARGET" && echo "LE" || echo "self-signed/none")
WEBMAIL_LE=$([[ -n "$WEBMAIL_CERT_TARGET" ]] && is_le "$WEBMAIL_CERT_TARGET" && echo "LE" || echo "self-signed/none")
MAIL_LE=$([[ -n "$MAIL_CERT_TARGET" ]] && is_le "$MAIL_CERT_TARGET" && echo "LE" || echo "self-signed/none")
echo
echo "──────────────────────────────────────────────────────────────────────────────"
echo "✔ MailWolt Bootstrap fertig"
echo "──────────────────────────────────────────────────────────────────────────────"
printf " Aufruf UI: %s\n" "${APP_URL}"
printf " App Root: %s\n" "${APP_DIR}"
printf " Nginx Site: %s\n" "/etc/nginx/sites-available/${APP_USER}.conf"
printf " Mail-FQDN: %s\n" "${MAIL_HOSTNAME}"
bar
printf " %s %s\n" "✔ MailWolt Bootstrap fertig" ""
bar
# Kopf-Infos
printf " %-14s %s\n" "Aufruf UI:" "${APP_URL}"
printf " %-14s %s\n" "Webmail:" "${WEBMAIL_URL}"
printf " %-14s %s\n" "App Root:" "${APP_DIR}"
printf " %-14s %s\n" "Mail-FQDN:" "${MAIL_HOSTNAME:-$SERVER_PUBLIC_IPV4}"
printf " %-14s %s\n" "BASE_DOMAIN:" "${BASE_DOMAIN}"
printf " %-14s %s\n" "LE-Email:" "${LE_EMAIL}"
printf " %-14s %s\n" "APP_ENV:" "${APP_ENV}"
[[ -v PROXY_MODE ]] && printf " %-14s %s\n" "Proxy-Mode:" "$([[ "$PROXY_MODE" = "1" ]] && echo "ja (NPM: ${NPM_IP:-unbekannt})" || echo "nein")"printf " %-14s %s\n" "Server IPv4:" "${SERVER_PUBLIC_IPV4}"
printf " %-14s %s\n" "Server IPv6:" "${SERVER_PUBLIC_IPV6:-}"
printf " %-14s %s\n" "ACME Webroot:" "${ACME_WEBROOT}"
echo
printf " %-14s UI=%s, Webmail=%s, MX=%s\n" "Zertifikate:" "$UI_LE" "$WEBMAIL_LE" "$MAIL_LE"
echo
echo " Anmeldung: Keine vordefinierten Admin-Daten."
echo " Bitte zuerst registrieren (Erst-User wird Admin, danach"
echo " wird die Registrierung automatisch gesperrt)."
echo
# -------- Services ----------
printf "Services:\n"
# Dienste-Status
bar
echo " Services"
bar
OK_LIST=()
FAIL_LIST=()
svc(){
local name="$1"
printf " • %-10s … " "$name"
if systemctl is-active --quiet "$name"; then ok; else fail; fi
local unit="$1" label="${2:-$1}"
printf " • %-18s … " "$label"
if systemctl is-active --quiet "$unit"; then
ok
OK_LIST+=("$label")
else
fail
FAIL_LIST+=("$label")
fi
}
# Kern-Services
svc nginx
svc mariadb
svc redis-server
svc postfix
svc dovecot
svc "${APP_USER}-ws" || true
svc "${APP_USER}-schedule" || true
svc "${APP_USER}-queue" || true
# App-Worker (tolerant)
svc "${APP_USER}-ws" "mailwolt-ws" || true
svc "${APP_USER}-schedule" "mailwolt-schedule" || true
svc "${APP_USER}-queue" "mailwolt-queue" || true
# Kurze Zusammenfassung
echo
if ((${#OK_LIST[@]})); then
printf " ${GREEN}OK:${NC} %s\n" "$(IFS=', '; echo "${OK_LIST[*]}")"
fi
if ((${#FAIL_LIST[@]})); then
printf " ${RED}FAIL:${NC} %s\n" "$(IFS=', '; echo "${FAIL_LIST[*]}")"
echo " ${YELLOW}Hinweis:${NC} Details mit: journalctl -u <dienst> -b --no-pager"
fi
echo
# -------- Ports / Smoke Test ----------
echo "──────────────────────────────────────────────────────────────────────────────"
# Smoke-Test
bar
echo " Smoke-Test (SMTP/IMAP/POP3 mit/ohne TLS)"
echo "──────────────────────────────────────────────────────────────────────────────"
bar
check_port(){
local label="$1" cmd="$2"
printf "[%-3s] %-35s … " "$label" "$3"
local tag="$1" cmd="$2" desc="$3"
printf " [%-3s] %-35s … " "$tag" "$desc"
if timeout 8s bash -lc "$cmd" >/dev/null 2>&1; then ok; else fail; fi
}
# ein kurzes Delay, damit frisch gestartete Dienste lauschen
sleep 6 || true
# SMTP family
check_port "25" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 25' "SMTP (EHLO)"
check_port "465" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:465 -quiet -ign_eof' "SMTPS (TLS + EHLO)"
check_port "587" 'printf "EHLO x\r\nSTARTTLS\r\nQUIT\r\n" | openssl s_client -starttls smtp -connect 127.0.0.1:587 -quiet -ign_eof' "Submission (STARTTLS)"
# SMTP
check_port "25" 'printf "EHLO x\r\nQUIT\r\n" | nc -w 3 127.0.0.1 25' \
"SMTP (EHLO)"
check_port "465" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:465 -quiet -ign_eof' \
"SMTPS (TLS + EHLO)"
check_port "587" 'printf "EHLO x\r\nSTARTTLS\r\nQUIT\r\n" | openssl s_client -starttls smtp -connect 127.0.0.1:587 -quiet -ign_eof' \
"Submission (STARTTLS)"
# POP/IMAP
check_port "110" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 110' "POP3 (QUIT)"
check_port "995" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:995 -quiet -ign_eof' "POP3S (TLS + QUIT)"
check_port "143" 'printf ". CAPABILITY\r\n. LOGOUT\r\n" | nc -w 3 127.0.0.1 143' "IMAP (CAPABILITY/LOGOUT)"
check_port "993" 'printf ". CAPABILITY\r\n. LOGOUT\r\n" | openssl s_client -connect 127.0.0.1:993 -quiet -ign_eof' "IMAPS (TLS + CAPABILITY/LOGOUT)"
check_port "110" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 110' \
"POP3 (QUIT)"
check_port "995" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:995 -quiet -ign_eof' \
"POP3S (TLS + QUIT)"
check_port "143" 'printf ". CAPABILITY\r\n. LOGOUT\r\n" | nc -w 3 127.0.0.1 143' \
"IMAP (CAPABILITY/LOGOUT)"
check_port "993" 'printf ". CAPABILITY\r\n. LOGOUT\r\n" | openssl s_client -connect 127.0.0.1:993 -quiet -ign_eof' \
"IMAPS (TLS + CAPABILITY/LOGOUT)"
echo
echo
# Nützliche Hinweise am Ende
if [[ "$UI_LE" != "LE" || "$WEBMAIL_LE" != "LE" ]]; then
echo -e " ${YELLOW}Hinweis:${NC} UI/Webmail verwenden noch kein Let's-Encrypt-Zertifikat."
echo -e " Prüfe Symlinks unter /etc/ssl/{ui,webmail} und den LE-Hook (21/75-Skripte)."
echo
fi
if [[ "$PROXY_MODE" = "1" ]]; then
echo -e " ${GREY}Proxy-Hinweis:${NC} App erwartet TLS am Proxy (keine https-Redirects im Backend)."
echo
fi