From 216e46311bfd72c959b9dbd3688affba65dd9be6 Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 24 Apr 2026 15:11:39 +0200 Subject: [PATCH] chore: mailwolt-installer in eigenes Repo ausgelagert Verschoben nach https://git.nexlab.at/boban/mailwolt-installer.git Co-Authored-By: Claude Sonnet 4.6 --- mailwolt-installer/.idea/.gitignore | 8 - .../.idea/copilot.data.migration.agent.xml | 6 - .../.idea/copilot.data.migration.ask.xml | 6 - .../copilot.data.migration.ask2agent.xml | 6 - .../.idea/copilot.data.migration.edit.xml | 6 - .../.idea/mailwolt-installer.iml | 8 - mailwolt-installer/.idea/misc.xml | 6 - mailwolt-installer/.idea/modules.xml | 8 - mailwolt-installer/.idea/php.xml | 19 - mailwolt-installer/.idea/vcs.xml | 6 - .../config/nginx/site.conf.tmpl | 17 - mailwolt-installer/install.sh | 909 ------------------ mailwolt-installer/scripts/10-provision.sh | 125 --- mailwolt-installer/scripts/20-ssl.sh | 60 -- .../scripts/21-le-deploy-hook.sh | 588 ----------- mailwolt-installer/scripts/22-dkim-helper.sh | 42 - mailwolt-installer/scripts/30-db.sh | 14 - mailwolt-installer/scripts/40-postfix.sh | 133 --- mailwolt-installer/scripts/50-dovecot.sh | 247 ----- .../scripts/60-rspamd-opendkim.sh | 319 ------ mailwolt-installer/scripts/61-opendmarc.sh | 63 -- mailwolt-installer/scripts/62-clamav.sh | 59 -- mailwolt-installer/scripts/63-fail2ban.sh | 230 ----- .../scripts/64-apply-milters.sh | 24 - mailwolt-installer/scripts/70-nginx.sh | 528 ---------- mailwolt-installer/scripts/75-le-issue.sh | 121 --- mailwolt-installer/scripts/80-app.sh | 343 ------- .../scripts/88-update-wrapper.sh | 264 ----- mailwolt-installer/scripts/90-services.sh | 134 --- mailwolt-installer/scripts/92-sudoers-npm.sh | 33 - mailwolt-installer/scripts/93-backup-tools.sh | 284 ------ mailwolt-installer/scripts/95-monit.sh | 47 - .../scripts/95-woltguard.bak.sh | 491 ---------- mailwolt-installer/scripts/95-woltguard.sh | 439 --------- mailwolt-installer/scripts/98-motd.sh | 150 --- mailwolt-installer/scripts/99-summary.sh | 212 ---- mailwolt-installer/scripts/bootstrap.sh | 633 ------------ mailwolt-installer/scripts/install-wrapper.sh | 46 - mailwolt-installer/scripts/lib.sh | 189 ---- mailwolt-installer/scripts/phase2-go-live.sh | 102 -- mailwolt-installer/scripts/update.sh | 261 ----- mailwolt-installer/tools/push.sh | 33 - mailwolt-installer/tools/release.sh | 80 -- 43 files changed, 7299 deletions(-) delete mode 100644 mailwolt-installer/.idea/.gitignore delete mode 100644 mailwolt-installer/.idea/copilot.data.migration.agent.xml delete mode 100644 mailwolt-installer/.idea/copilot.data.migration.ask.xml delete mode 100644 mailwolt-installer/.idea/copilot.data.migration.ask2agent.xml delete mode 100644 mailwolt-installer/.idea/copilot.data.migration.edit.xml delete mode 100644 mailwolt-installer/.idea/mailwolt-installer.iml delete mode 100644 mailwolt-installer/.idea/misc.xml delete mode 100644 mailwolt-installer/.idea/modules.xml delete mode 100644 mailwolt-installer/.idea/php.xml delete mode 100644 mailwolt-installer/.idea/vcs.xml delete mode 100644 mailwolt-installer/config/nginx/site.conf.tmpl delete mode 100755 mailwolt-installer/install.sh delete mode 100644 mailwolt-installer/scripts/10-provision.sh delete mode 100644 mailwolt-installer/scripts/20-ssl.sh delete mode 100644 mailwolt-installer/scripts/21-le-deploy-hook.sh delete mode 100644 mailwolt-installer/scripts/22-dkim-helper.sh delete mode 100644 mailwolt-installer/scripts/30-db.sh delete mode 100644 mailwolt-installer/scripts/40-postfix.sh delete mode 100644 mailwolt-installer/scripts/50-dovecot.sh delete mode 100644 mailwolt-installer/scripts/60-rspamd-opendkim.sh delete mode 100644 mailwolt-installer/scripts/61-opendmarc.sh delete mode 100644 mailwolt-installer/scripts/62-clamav.sh delete mode 100644 mailwolt-installer/scripts/63-fail2ban.sh delete mode 100644 mailwolt-installer/scripts/64-apply-milters.sh delete mode 100644 mailwolt-installer/scripts/70-nginx.sh delete mode 100644 mailwolt-installer/scripts/75-le-issue.sh delete mode 100644 mailwolt-installer/scripts/80-app.sh delete mode 100644 mailwolt-installer/scripts/88-update-wrapper.sh delete mode 100644 mailwolt-installer/scripts/90-services.sh delete mode 100644 mailwolt-installer/scripts/92-sudoers-npm.sh delete mode 100644 mailwolt-installer/scripts/93-backup-tools.sh delete mode 100644 mailwolt-installer/scripts/95-monit.sh delete mode 100644 mailwolt-installer/scripts/95-woltguard.bak.sh delete mode 100644 mailwolt-installer/scripts/95-woltguard.sh delete mode 100644 mailwolt-installer/scripts/98-motd.sh delete mode 100644 mailwolt-installer/scripts/99-summary.sh delete mode 100644 mailwolt-installer/scripts/bootstrap.sh delete mode 100644 mailwolt-installer/scripts/install-wrapper.sh delete mode 100644 mailwolt-installer/scripts/lib.sh delete mode 100755 mailwolt-installer/scripts/phase2-go-live.sh delete mode 100644 mailwolt-installer/scripts/update.sh delete mode 100644 mailwolt-installer/tools/push.sh delete mode 100644 mailwolt-installer/tools/release.sh diff --git a/mailwolt-installer/.idea/.gitignore b/mailwolt-installer/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/mailwolt-installer/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/mailwolt-installer/.idea/copilot.data.migration.agent.xml b/mailwolt-installer/.idea/copilot.data.migration.agent.xml deleted file mode 100644 index 4ea72a9..0000000 --- a/mailwolt-installer/.idea/copilot.data.migration.agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/copilot.data.migration.ask.xml b/mailwolt-installer/.idea/copilot.data.migration.ask.xml deleted file mode 100644 index 7ef04e2..0000000 --- a/mailwolt-installer/.idea/copilot.data.migration.ask.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/copilot.data.migration.ask2agent.xml b/mailwolt-installer/.idea/copilot.data.migration.ask2agent.xml deleted file mode 100644 index 1f2ea11..0000000 --- a/mailwolt-installer/.idea/copilot.data.migration.ask2agent.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/copilot.data.migration.edit.xml b/mailwolt-installer/.idea/copilot.data.migration.edit.xml deleted file mode 100644 index 8648f94..0000000 --- a/mailwolt-installer/.idea/copilot.data.migration.edit.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/mailwolt-installer.iml b/mailwolt-installer/.idea/mailwolt-installer.iml deleted file mode 100644 index c956989..0000000 --- a/mailwolt-installer/.idea/mailwolt-installer.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/misc.xml b/mailwolt-installer/.idea/misc.xml deleted file mode 100644 index 3ce3588..0000000 --- a/mailwolt-installer/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/modules.xml b/mailwolt-installer/.idea/modules.xml deleted file mode 100644 index 2492f5c..0000000 --- a/mailwolt-installer/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/php.xml b/mailwolt-installer/.idea/php.xml deleted file mode 100644 index f324872..0000000 --- a/mailwolt-installer/.idea/php.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/mailwolt-installer/.idea/vcs.xml b/mailwolt-installer/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/mailwolt-installer/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/mailwolt-installer/config/nginx/site.conf.tmpl b/mailwolt-installer/config/nginx/site.conf.tmpl deleted file mode 100644 index 935dfe0..0000000 --- a/mailwolt-installer/config/nginx/site.conf.tmpl +++ /dev/null @@ -1,17 +0,0 @@ -# ===================== HTTP (Port 80) ===================== -server { - listen 80 default_server; - listen [::]:80 default_server; - server_name _; - - # ACME HTTP-01 - location ^~ /.well-known/acme-challenge/ { - root /var/www/letsencrypt; - allow all; - } - - __HTTP_BODY__ -} - -# ===================== HTTPS (Port 443) ==================== -__SSL_SERVER_BLOCK__ \ No newline at end of file diff --git a/mailwolt-installer/install.sh b/mailwolt-installer/install.sh deleted file mode 100755 index d1e7429..0000000 --- a/mailwolt-installer/install.sh +++ /dev/null @@ -1,909 +0,0 @@ -#!/usr/bin/env bash -( -export HISTFILE= -set +o history -set -euo pipefail - -############################################## -# MailWolt # -# Bootstrap Installer v1.0 # -############################################## - -# ===== CLI-Flags (-dev / -stag) ===== -APP_ENV="${APP_ENV:-production}" -APP_DEBUG="${APP_DEBUG:-false}" - -DEV_MODE=0 -STAG_MODE=0 - -while [[ $# -gt 0 ]]; do - case "$1" in - -dev) - DEV_MODE=1 - APP_ENV="local" - APP_DEBUG="true" - ;; - -stag|-staging) - STAG_MODE=1 - APP_ENV="staging" - APP_DEBUG="false" - ;; - esac - shift -done - -# ===== Branding & Pfade ===== -APP_NAME="${APP_NAME:-MailWolt}" - -APP_USER="${APP_USER:-mailwolt}" -APP_GROUP="${APP_GROUP:-www-data}" - -APP_DIR="/var/www/${APP_USER}" - -ADMIN_USER="${APP_USER}" -ADMIN_EMAIL="admin@localhost" -ADMIN_PASS="ChangeMe" - -CONF_BASE="/etc/${APP_USER}" -CERT_DIR="${CONF_BASE}/ssl" -CERT="${CERT_DIR}/cert.pem" -KEY="${CERT_DIR}/key.pem" - -NGINX_SITE="/etc/nginx/sites-available/${APP_USER}.conf" -NGINX_SITE_LINK="/etc/nginx/sites-enabled/${APP_USER}.conf" - -DB_NAME="${DB_NAME:-${APP_USER}}" -DB_USER="${DB_USER:-${APP_USER}}" -DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}" - -GIT_REPO="${GIT_REPO:-http://10.10.20.81:3000/boban/mailwolt.git}" -GIT_BRANCH="${GIT_BRANCH:-main}" - -NODE_SETUP="${NODE_SETUP:-deb}" - -# ===== Styling ===== -GREEN="\033[1;32m"; YELLOW="\033[1;33m"; RED="\033[1;31m"; CYAN="\033[1;36m"; GREY="\033[0;90m"; NC="\033[0m" -BAR="──────────────────────────────────────────────────────────────────────────────" - -header() { - echo -e "${CYAN}${BAR}${NC}" - echo -e "${CYAN} 888b d888 d8b 888 888 888 888 888 ${NC}" - echo -e "${CYAN} 8888b d8888 Y8P 888 888 o 888 888 888 ${NC}" - echo -e "${CYAN} 88888b.d88888 888 888 d8b 888 888 888 ${NC}" - echo -e "${CYAN} 888Y88888P888 8888b. 888 888 888 d888b 888 .d88b. 888 888888 ${NC}" - echo -e "${CYAN} 888 Y888P 888 '88b 888 888 888d88888b888 d88''88b 888 888 ${NC}" - echo -e "${CYAN} 888 Y8P 888 .d888888 888 888 88888P Y88888 888 888 888 888 ${NC}" - echo -e "${CYAN} 888 ' 888 888 888 888 888 8888P Y8888 Y88..88P 888 Y88b. ${NC}" - echo -e "${CYAN} 888 888 'Y888888 888 888 888P Y888 'Y88P' 888 'Y888 ${NC}" - echo -e "${CYAN}${BAR}${NC}" - echo -} - -print_bootstrap_summary() { - local ip="$1" - local admin_user="$2" - local admin_pass="$3" - local GREEN="\033[1;32m" - local CYAN="\033[1;36m" - local GREY="\033[0;90m" - local YELLOW="\033[1;33m" - local RED="\033[1;31m" - local NC="\033[0m" - local BAR="${BAR:-──────────────────────────────────────────────────────────────────────────────}" - local scheme="http" - if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then scheme="https"; fi - - echo - echo -e "${GREEN}${BAR}${NC}" - echo -e "${GREEN} ✔ ${APP_NAME} Bootstrap erfolgreich abgeschlossen${NC}" - echo -e "${GREEN}${BAR}${NC}" - echo -e " Bootstrap-Login (nur für ERSTEN Login & Wizard):" - echo -e " User: ${YELLOW}${admin_user}${NC}" - echo -e " Passwort: ${RED}${admin_pass}${NC}" - echo - echo -e " Aufruf: ${CYAN}${scheme}://${ip}${NC}" - echo -e " Laravel Root: ${GREY}${APP_DIR}${NC}" - echo -e " Nginx Site: ${GREY}${NGINX_SITE}${NC}" - echo -e " Self-signed Cert: ${GREY}${CERT_DIR}/{cert.pem,key.pem}${NC}" - echo -e " Postfix/Dovecot Ports aktiv: ${GREY}25, 465, 587, 110, 995, 143, 993${NC}" - echo -e " Rspamd/OpenDKIM: ${GREY}aktiv (DKIM-Keys später im Wizard)${NC}" - echo -e " Monit (Watchdog): ${GREY}installiert, NICHT aktiviert${NC}" - echo -e "${GREEN}${BAR}${NC}" - echo -} - -log() { echo -e "${GREEN}[+]${NC} $*"; } -warn() { echo -e "${YELLOW}[!]${NC} $*"; } -err() { echo -e "${RED}[x]${NC} $*"; } -require_root() { [ "$(id -u)" -eq 0 ] || { err "Bitte als root ausführen."; exit 1; }; } - -detect_ip() { - local ip - ip="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')" || true - [[ -n "${ip:-}" ]] || ip="$(hostname -I 2>/dev/null | awk '{print $1}')" - [[ -n "${ip:-}" ]] || { err "Konnte Server-IP nicht ermitteln."; exit 1; } - echo "$ip" -} - -gen() { head -c 512 /dev/urandom | tr -dc 'A-Za-z0-9' | head -c "${1:-28}" || true; } -pw() { gen 28; } -short() { gen 16; } - -# ===== Start ===== -require_root -header - -SERVER_IP="$(detect_ip)" -MAIL_HOSTNAME="${MAIL_HOSTNAME:-"bootstrap.local"}" -TZ="${TZ:-""}" - -echo -e "${GREY}Server-IP erkannt: ${SERVER_IP}${NC}" -[ -n "$TZ" ] && { ln -fs "/usr/share/zoneinfo/${TZ}" /etc/localtime || true; } - -log "Paketquellen aktualisieren…" -export DEBIAN_FRONTEND=noninteractive -apt-get update -y - -# ---- MariaDB-Workaround ---- -log "MariaDB-Workaround vorbereiten…" -mkdir -p /etc/mysql /etc/mysql/mariadb.conf.d -[ -f /etc/mysql/mariadb.cnf ] || echo '!include /etc/mysql/mariadb.conf.d/*.cnf' > /etc/mysql/mariadb.cnf - -# ---- Basis-Pakete installieren ---- -log "Pakete installieren… (dies kann einige Minuten dauern)" -export DEBIAN_FRONTEND=noninteractive -apt-get -y -o Dpkg::Options::="--force-confdef" \ - -o Dpkg::Options::="--force-confold" install \ - postfix postfix-mysql \ - dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql \ - mariadb-server mariadb-client \ - redis-server \ - rspamd \ - opendkim opendkim-tools \ - nginx \ - php php-fpm php-cli php-mbstring php-xml php-curl php-zip php-mysql php-redis php-gd unzip curl \ - composer git \ - certbot python3-certbot-nginx \ - fail2ban \ - ca-certificates rsyslog sudo openssl netcat-openbsd monit acl - -NGINX_HTTP2_SUPPORTED=0 -if nginx -V 2>&1 | grep -q http_v2; then - NGINX_HTTP2_SUPPORTED=1 - log "Nginx: HTTP/2-Unterstützung vorhanden ✅" -else - warn "Nginx: HTTP/2-Modul nicht gefunden – wechsle auf 'nginx-full'…" - apt-get install -y nginx-full || true - systemctl restart nginx || true - if nginx -V 2>&1 | grep -q http_v2; then - NGINX_HTTP2_SUPPORTED=1 - log "Nginx: HTTP/2 jetzt verfügbar ✅" - else - warn "HTTP/2 weiterhin nicht verfügbar (verwende SSL ohne http2)." - fi -fi - -if [ "$NGINX_HTTP2_SUPPORTED" = "1" ]; then - NGINX_HTTP2_SUFFIX=" http2" -else - NGINX_HTTP2_SUFFIX="" -fi - -# ===== Verzeichnisse / User ===== -log "Verzeichnisse und Benutzer anlegen…" -mkdir -p "${CERT_DIR}" /etc/postfix/sql /etc/dovecot/conf.d /etc/rspamd/local.d /var/mail/vhosts -id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail -chown -R vmail:vmail /var/mail - -id "$APP_USER" >/dev/null 2>&1 || adduser --disabled-password --gecos "" "$APP_USER" -usermod -a -G "$APP_GROUP" "$APP_USER" - -# ===== Self-signed TLS (SAN = IP) ===== -OSSL_CFG="${CERT_DIR}/openssl.cnf" -if [ ! -s "$CERT" ] || [ ! -s "$KEY" ]; then - log "Erzeuge Self-Signed TLS Zertifikat (SAN=IP:${SERVER_IP})…" - install -d -m 0750 -o root -g "${APP_USER}" "${CERT_DIR}" - cat > "$OSSL_CFG" </dev/null 2>&1; then - setfacl -m u:${DEV_USER}:x "${CONF_BASE}" "${CERT_DIR}" || true - setfacl -m u:${DEV_USER}:r "$CERT" "$KEY" || true -fi - -# ===== MariaDB ===== -log "MariaDB vorbereiten…" -systemctl enable --now mariadb -mysql -uroot < /etc/postfix/sql/mysql-virtual-mailbox-maps.cf < /etc/postfix/sql/mysql-virtual-alias-maps.cf < /etc/dovecot/dovecot.conf <<'CONF' -!include_try /etc/dovecot/conf.d/*.conf -CONF - -cat > /etc/dovecot/conf.d/10-mail.conf <<'CONF' -protocols = imap pop3 lmtp -mail_location = maildir:/var/mail/vhosts/%d/%n -namespace inbox { inbox = yes } -mail_privileged_group = mail -CONF - -cat > /etc/dovecot/conf.d/10-auth.conf <<'CONF' -disable_plaintext_auth = yes -auth_mechanisms = plain login -!include_try auth-sql.conf.ext -CONF - -cat > /etc/dovecot/dovecot-sql.conf.ext < /etc/dovecot/conf.d/auth-sql.conf.ext <<'CONF' -passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } -userdb { driver = static args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n } -CONF -sudo chown root:dovecot /etc/dovecot/conf.d/auth-sql.conf.ext -sudo chmod 640 /etc/dovecot/conf.d/auth-sql.conf.ext - -cat > /etc/dovecot/conf.d/10-master.conf <<'CONF' -service lmtp { - unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix } -} -service auth { - unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } -} -service imap-login { - inet_listener imap { port = 143 } - inet_listener imaps { port = 993 ssl = yes } -} -service pop3-login { - inet_listener pop3 { port = 110 } - inet_listener pop3s { port = 995 ssl = yes } -} -CONF - -cat > /etc/dovecot/conf.d/10-ssl.conf < /etc/rspamd/local.d/worker-controller.inc <<'CONF' -password = "admin"; -bind_socket = "127.0.0.1:11334"; -CONF -systemctl enable --now rspamd || true - -cat > /etc/opendkim.conf <<'CONF' -Syslog yes -UMask 002 -Mode sv -Socket inet:8891@127.0.0.1 -Canonicalization relaxed/simple -On-BadSignature accept -On-Default accept -On-KeyNotFound accept -On-NoSignature accept -LogWhy yes -OversignHeaders From -# KeyTable / SigningTable werden nach dem Wizard gesetzt -CONF -systemctl enable --now opendkim || true - -# ===== Redis ===== -log "Redis absichern (Passwort setzen & nur localhost)…" -REDIS_CONF="/etc/redis/redis.conf" -REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}" -sed -i 's/^\s*#\?\s*bind .*/bind 127.0.0.1/' "$REDIS_CONF" -sed -i 's/^\s*#\?\s*protected-mode .*/protected-mode yes/' "$REDIS_CONF" -if grep -qE '^\s*#?\s*requirepass ' "$REDIS_CONF"; then - sed -i "s/^\s*#\?\s*requirepass .*/requirepass ${REDIS_PASS}/" "$REDIS_CONF" -else - printf "\nrequirepass %s\n" "${REDIS_PASS}" >> "$REDIS_CONF" -fi -systemctl enable --now redis-server -systemctl restart redis-server - -# ===== Nginx ===== -log "Nginx konfigurieren…" -rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default || true - -detect_php_fpm_sock() { - for v in 8.3 8.2 8.1 8.0 7.4; do s="/run/php/php${v}-fpm.sock"; [ -S "$s" ] && { echo "$s"; return; }; done - [ -S "/run/php/php-fpm.sock" ] && { echo "/run/php/php-fpm.sock"; return; } - echo "127.0.0.1:9000" -} -PHP_FPM_SOCK="$(detect_php_fpm_sock)" -install -d -m 0755 /var/www/letsencrypt - -if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then - cat > "${NGINX_SITE}" < "${NGINX_SITE}" </dev/null || true)" ]; then - sudo -u "$APP_USER" -H bash -lc "cd /var/www && COMPOSER_ALLOW_SUPERUSER=0 composer create-project laravel/laravel ${APP_USER} --no-interaction" - fi -else - if [ ! -d "${APP_DIR}/.git" ]; then - sudo -u "$APP_USER" -H bash -lc "git clone --depth=1 -b ${GIT_BRANCH} ${GIT_REPO} ${APP_DIR}" - else - sudo -u "$APP_USER" -H bash -lc " - set -e - cd ${APP_DIR} - git checkout ${GIT_BRANCH} 2>/dev/null || git checkout -B ${GIT_BRANCH} - git fetch --depth=1 origin ${GIT_BRANCH} - if git merge-base --is-ancestor HEAD origin/${GIT_BRANCH}; then - git pull --ff-only - else - echo '[i] Non-fast-forward erkannt – setze hart auf origin/${GIT_BRANCH}.' >&2 - git reset --hard origin/${GIT_BRANCH} - git clean -fd - fi - " - fi - if [ -f "${APP_DIR}/composer.json" ]; then - if [ "${DEV_MODE}" = "1" ]; then - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-interaction --prefer-dist" - else - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --no-dev" - fi - fi -fi - -# ===== Node / Frontend ===== -if [ -f "${APP_DIR}/package.json" ]; then - log "Node/NPM installieren…" - if command -v node >/dev/null 2>&1; then - NODE_MAJ=$(node -v | sed 's/^v//' | cut -d. -f1) - NODE_MIN=$(node -v | sed 's/^v//' | cut -d. -f2) - if [ "$NODE_MAJ" -lt 20 ] || { [ "$NODE_MAJ" -eq 20 ] && [ "$NODE_MIN" -lt 19 ]; }; then - curl -fsSL https://deb.nodesource.com/setup_22.x | bash - - apt-get install -y nodejs - fi - else - curl -fsSL https://deb.nodesource.com/setup_22.x | bash - - apt-get install -y nodejs - fi - - # .env anlegen & APP_KEY - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && cp -n .env.example .env || true" - if ! grep -q '^APP_KEY=' "${APP_DIR}/.env"; then echo "APP_KEY=" >> "${APP_DIR}/.env"; fi - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan key:generate --force || true" -fi - -# ===== .env füllen ===== -ENV_FILE="${APP_DIR}/.env" -upsert_env () { - local key="$1" val="$2" - local esc_key esc_val - esc_key="$(printf '%s' "$key" | sed -e 's/[.[\*^$(){}+?|/]/\\&/g')" - esc_val="$(printf '%s' "$val" | sed -e 's/[&/]/\\&/g')" - if grep -qE "^[#[:space:]]*${esc_key}=" "$ENV_FILE"; then - sed -Ei "s|^[#[:space:]]*${esc_key}=.*|${key}=${esc_val}|g" "$ENV_FILE" - else - printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE" - fi -} - -if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then - upsert_env APP_URL "\"https://\${APP_HOST}\"" -else - upsert_env APP_URL "\"http://\${APP_HOST}\"" -fi - -upsert_env APP_HOST "${SERVER_IP}" -upsert_env APP_ADMIN_USER "${ADMIN_USER}" -upsert_env APP_ADMIN_EMAIL "${ADMIN_EMAIL}" -upsert_env APP_ADMIN_PASS "${ADMIN_PASS}" -upsert_env APP_NAME "${APP_NAME}" -upsert_env APP_ENV "${APP_ENV}" -upsert_env APP_DEBUG "${APP_DEBUG}" - -upsert_env DB_CONNECTION "mysql" -upsert_env DB_HOST "127.0.0.1" -upsert_env DB_PORT "3306" -upsert_env DB_DATABASE "${DB_NAME}" -upsert_env DB_USERNAME "${DB_USER}" -upsert_env DB_PASSWORD "${DB_PASS}" - -# -------- WICHTIG: Cache-Store auf REDIS setzen (verhindert DB-Tabellenfehler) -------- -upsert_env CACHE_SETTINGS_STORE "redis" -upsert_env CACHE_STORE "redis" # <- HIER geändert (vorher: database) -upsert_env CACHE_DRIVER "redis" -upsert_env CACHE_PREFIX "${APP_USER}_cache" - -upsert_env SESSION_DRIVER "redis" -upsert_env REDIS_CLIENT "phpredis" -upsert_env REDIS_HOST "127.0.0.1" -upsert_env REDIS_PORT "6379" -upsert_env REDIS_PASSWORD "${REDIS_PASS}" -upsert_env REDIS_DB "0" -upsert_env REDIS_CACHE_DB "1" -upsert_env REDIS_CACHE_CONNECTION "cache" -upsert_env REDIS_CACHE_LOCK_CONNECTION "default" - -# Reverb / Vite -upsert_env BROADCAST_DRIVER "reverb" -upsert_env QUEUE_CONNECTION "redis" -upsert_env REVERB_APP_ID "${APP_USER}" -upsert_env REVERB_APP_KEY "${APP_USER}-yhp47tbt1aebhr1fgvgj" -upsert_env REVERB_APP_SECRET "${APP_USER}-ulrdt9agwzkqwqsunbnb" -upsert_env REVERB_HOST "127.0.0.1" -upsert_env REVERB_PORT "443" -upsert_env REVERB_SCHEME "https" -upsert_env REVERB_PATH "/ws" -upsert_env VITE_REVERB_APP_KEY "\${REVERB_APP_KEY}" -upsert_env VITE_REVERB_PORT "\${REVERB_PORT}" -upsert_env VITE_REVERB_SCHEME "\${REVERB_SCHEME}" -upsert_env VITE_REVERB_PATH "\${REVERB_PATH}" - -if [ "${DEV_MODE}" = "1" ]; then - sed -i '/^# --- MailWolt DEV/,/^# --- \/MailWolt DEV/d' "${ENV_FILE}" - cat >> "${ENV_FILE}" < "${APP_DIR}/vite.config.js" <<'JS' -import { defineConfig, loadEnv } from 'vite' -import laravel from 'laravel-vite-plugin' -import tailwindcss from '@tailwindcss/vite' -export default ({ mode }) => { - const env = loadEnv(mode, process.cwd(), '') - const host = env.VITE_DEV_HOST || '127.0.0.1' - const port = Number(env.VITE_DEV_PORT || 5173) - const origin = env.VITE_DEV_ORIGIN || env.APP_URL || 'https://localhost' - const hmrHost = env.VITE_HMR_HOST || (new URL(origin)).hostname - return defineConfig({ - plugins: [laravel({ input: ['resources/css/app.css','resources/js/app.js'], refresh: true }), tailwindcss()], - server: { host, port, https:false, strictPort:true, - hmr:{ protocol: env.VITE_HMR_PROTOCOL || 'wss', host: hmrHost, clientPort:Number(env.VITE_HMR_CLIENT_PORT||443) }, - origin - } - }) -} -JS - chown "${APP_USER}:${APP_GROUP}" "${APP_DIR}/vite.config.js" -fi - -# ===== Frontend Build ===== -if [ -f "${APP_DIR}/package.json" ]; then - log "Frontend Build…" - if [ -f "${APP_DIR}/package-lock.json" ] || [ -f "${APP_DIR}/npm-shrinkwrap.json" ]; then - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm ci --no-audit --no-fund || npm install" - else - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm install" - fi - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm run build || true" - rm -f ${APP_DIR}/bootstrap/cache/*.php -fi - -# ===== Rechte / PHP-FPM ===== -APP_PW="${APP_PW:-changeme}" -if ! id -u "$APP_USER" >/dev/null 2>&1; then - adduser --disabled-password --gecos "" "$APP_USER" - echo "${APP_USER}:${APP_PW}" | chpasswd -fi -usermod -a -G "$APP_GROUP" "$APP_USER" - -# Sichert, dass alle nötigen Ordner existieren (idempotent) -install -d -m 0775 "${APP_DIR}/storage" \ - "${APP_DIR}/storage/framework" \ - "${APP_DIR}/storage/framework/cache" \ - "${APP_DIR}/storage/framework/cache/data" \ - "${APP_DIR}/storage/framework/sessions" \ - "${APP_DIR}/storage/framework/views" \ - "${APP_DIR}/bootstrap/cache" - -# Besitz & Rechte -chown -R "$APP_USER":"$APP_GROUP" "$APP_DIR" -find "$APP_DIR" -type d -exec chmod 775 {} \; -find "$APP_DIR" -type f -exec chmod 664 {} \; - -[ -f "$APP_DIR/artisan" ] && chmod 755 "$APP_DIR/artisan" -[ -d "$APP_DIR/vendor/bin" ] && chmod -R 755 "$APP_DIR/vendor/bin" -[ -d "$APP_DIR/node_modules/.bin" ] && chmod -R 755 "$APP_DIR/node_modules/.bin" -[ -f "$APP_DIR/node_modules/vite/bin/vite.js" ] && chmod 755 "$APP_DIR/node_modules/vite/bin/vite.js" -find "$APP_DIR" -type f -name "*.sh" -exec chmod 755 {} \; - -chmod -R 775 "$APP_DIR"/storage "$APP_DIR"/bootstrap/cache - -if command -v setfacl >/dev/null 2>&1; then - setfacl -R -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true - setfacl -dR -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true -fi - -grep -q 'umask 002' /home/${APP_USER}/.profile 2>/dev/null || echo 'umask 002' >> /home/${APP_USER}/.profile -grep -q 'umask 002' /home/${APP_USER}/.bashrc 2>/dev/null || echo 'umask 002' >> /home/${APP_USER}/.bashrc -sudo -u "$APP_USER" -H bash -lc "npm config set umask 0002" >/dev/null 2>&1 || true - -PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') -FPM_POOL="/etc/php/${PHPV}/fpm/pool.d/www.conf" -if [ -f "$FPM_POOL" ]; then - sed -i 's/^;*listen\.owner.*/listen.owner = www-data/' "$FPM_POOL" - sed -i 's/^;*listen\.group.*/listen.group = www-data/' "$FPM_POOL" - sed -i 's/^;*listen\.mode.*/listen.mode = 0660/' "$FPM_POOL" - systemctl restart php${PHPV}-fpm || true -fi - -IDE_USER="${SUDO_USER:-}" -if [ -n "$IDE_USER" ] && id "$IDE_USER" >/dev/null 2>&1 && command -v setfacl >/dev/null 2>&1; then - usermod -a -G "$APP_GROUP" "$IDE_USER" || true - setfacl -R -m u:${IDE_USER}:rwX "$APP_DIR" - setfacl -dR -m u:${IDE_USER}:rwX "$APP_DIR" - echo -e "${GREEN}[i]${NC} Benutzer '${IDE_USER}' wurde für Schreibzugriff freigeschaltet (ACL + Gruppe ${APP_GROUP})." -fi - -# Webstack neu laden -systemctl reload nginx || true -systemctl restart php*-fpm || true - -# ===== Reverb systemd ===== -cat > /etc/systemd/system/mailwolt-ws.service </dev/null | grep -qE '(^| )reverb:start( |$)'"; then - systemctl enable --now mailwolt-ws -else - systemctl disable --now mailwolt-ws >/dev/null 2>&1 || true -fi - -# ===== Monit ===== -log "Monit konfigurieren & starten…" -cat > /etc/monit/monitrc <<'EOF' -set daemon 60 -set logfile syslog facility log_daemon - -check process postfix with pidfile /var/spool/postfix/pid/master.pid - start program = "/bin/systemctl start postfix" - stop program = "/bin/systemctl stop postfix" - if failed port 25 protocol smtp then restart - if failed port 465 type tcp ssl then restart - if failed port 587 type tcp then restart - -check process dovecot with pidfile /var/run/dovecot/master.pid - start program = "/bin/systemctl start dovecot" - stop program = "/bin/systemctl stop dovecot" - if failed port 143 type tcp then restart - if failed port 993 type tcp ssl then restart - if failed port 110 type tcp then restart - if failed port 995 type tcp ssl then restart - -check process mariadb with pidfile /var/run/mysqld/mysqld.pid - start program = "/bin/systemctl start mariadb" - stop program = "/bin/systemctl stop mariadb" - if failed port 3306 type tcp then restart - -check process redis-server with pidfile /run/redis/redis-server.pid - start program = "/bin/systemctl start redis-server" - stop program = "/bin/systemctl stop redis-server" - if failed port 6379 type tcp then restart - -check process rspamd with pidfile /run/rspamd/rspamd.pid - start program = "/bin/systemctl start rspamd" - stop program = "/bin/systemctl stop rspamd" - if failed port 11332 type tcp then restart - -check process opendkim with pidfile /run/opendkim/opendkim.pid - start program = "/bin/systemctl start opendkim" - stop program = "/bin/systemctl stop opendkim" - if failed port 8891 type tcp then restart - -check process nginx with pidfile /run/nginx.pid - start program = "/bin/systemctl start nginx" - stop program = "/bin/systemctl stop nginx" - if failed port 80 type tcp then restart - if failed port 443 type tcp ssl then restart - -check process mailwolt-ws matching "reverb:start" - start program = "/bin/systemctl start mailwolt-ws" - stop program = "/bin/systemctl stop mailwolt-ws" - if failed host 127.0.0.1 port 8080 type tcp for 2 cycles then restart - if 5 restarts within 5 cycles then timeout -EOF -chmod 600 /etc/monit/monitrc -monit -t && systemctl enable --now monit -monit reload -monit summary || true - -# ===== Healthchecks ===== -GREEN="\033[1;32m"; RED="\033[1;31m"; GREY="\033[0;90m"; NC="\033[0m" -ok(){ echo -e " [${GREEN}OK${NC}]"; } -fail(){ echo -e " [${RED}FAIL${NC}]"; } - -echo "[+] Quick-Healthchecks…" -printf " • MariaDB … " ; mysqladmin ping --silent >/dev/null 2>&1 && ok || fail -printf " • Redis … " ; if command -v redis-cli >/dev/null 2>&1; then - if [ -n "${REDIS_PASS:-}" ] && [ "${REDIS_PASS}" != "null" ]; then - redis-cli -a "${REDIS_PASS}" ping 2>/dev/null | grep -q PONG && ok || fail - else - redis-cli ping 2>/dev/null | grep -q PONG && ok || fail - fi -else fail; fi -printf " • PHP-FPM … " ; if [[ "$PHP_FPM_SOCK" == 127.0.0.1:9000 ]]; then ss -ltn | grep -q ":9000 " && ok || fail; else [ -S "$PHP_FPM_SOCK" ] && ok || fail; fi -printf " • App … " ; if command -v curl >/dev/null 2>&1; then - if [ -s "${CERT}" ] && [ -s "${KEY}" ]; then curl -skI "https://127.0.0.1" >/dev/null 2>&1 && ok || fail; else curl -sI "http://127.0.0.1" >/dev/null 2>&1 && ok || fail; fi -else echo -e " ${GREY}(curl fehlt)${NC}"; fi -check_port(){ local label="$1" cmd="$2"; printf " • %-5s … " "$label"; timeout 8s bash -lc "$cmd" >/dev/null 2>&1 && ok || fail; } -check_port "25" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 25' -check_port "465" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:465 -quiet -ign_eof' -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' -check_port "110" 'printf "QUIT\r\n" | nc -w 3 127.0.0.1 110' -check_port "995" 'printf "QUIT\r\n" | openssl s_client -connect 127.0.0.1:995 -quiet -ign_eof' -check_port "143" 'printf ". LOGOUT\r\n" | nc -w 3 127.0.0.1 143' -check_port "993" 'printf ". LOGOUT\r\n" | openssl s_client -connect 127.0.0.1:993 -quiet -ign_eof' - -print_bootstrap_summary "$SERVER_IP" "$ADMIN_USER" "$ADMIN_PASS" - -# ===== MOTD ===== -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 '?')" -mhz="$(LC_ALL=C lscpu 2>/dev/null | awk -F: '/MHz/{gsub(/ /,"",$2); printf("%.0f MHz",$2); exit}')" -[ -z "$mhz" ] && mhz="$(awk -F: '/cpu MHz/{printf("%.0f MHz",$2); exit}' /proc/cpuinfo 2>/dev/null)" -load="$(awk '{print $1" / "$2" / "$3}' /proc/loadavg 2>/dev/null)" -mem_total="$(awk '/MemTotal/{printf "%.2f GB",$2/1024/1024}' /proc/meminfo)" -mem_free="$(awk '/MemAvailable/{printf "%.2f GB",$2/1024/1024}' /proc/meminfo)" -svc_status(){ 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}(external:${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}Core(s) :${NC} %s core(s) at ${CY}%s${NC}\n" "$cores" "${mhz:-?}" -printf "${GY}Load :${NC} %s (1 / 5 / 15)\n" "${load:-?}" -printf "${GY}Memory :${NC} ${RD}%s${NC} ${GY}(free)${NC} / ${CY}%s${NC} ${GY}(total)${NC}\n" "${mem_free:-?}" "${mem_total:-?}" -echo -printf "${GY}Services :${NC} postfix: $(svc_status postfix) dovecot: $(svc_status dovecot) nginx: $(svc_status nginx) mariadb: $(svc_status mariadb) redis: $(svc_status 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 - [ -f /etc/update-motd.d/80-livepatch ] && chmod -x /etc/update-motd.d/80-livepatch || 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 - -) \ No newline at end of file diff --git a/mailwolt-installer/scripts/10-provision.sh b/mailwolt-installer/scripts/10-provision.sh deleted file mode 100644 index 1697460..0000000 --- a/mailwolt-installer/scripts/10-provision.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -if [ -r /etc/mailwolt/installer.env ]; then - . /etc/mailwolt/installer.env -fi - -REDIS_PASS="${REDIS_PASS:-}" - -SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -find "$SCRIPTS_DIR/.." -type f -name "*.sh" -exec sed -i 's/\r$//' {} \; || true - -log "Pakete installieren …" -export DEBIAN_FRONTEND=noninteractive -apt-get update -y -# Minimal aber vollständig -apt-get -y -o Dpkg::Options::="--force-confdef" \ - -o Dpkg::Options::="--force-confold" install \ - postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql \ - mariadb-server mariadb-client redis-server rspamd opendkim opendkim-tools opendmarc clamav \ - clamav-daemon nginx php php-fpm php-cli php-mbstring php-xml php-curl php-zip php-mysql \ - php-redis php-gd unzip curl composer git certbot python3-certbot-nginx fail2ban ca-certificates \ - rsyslog sudo openssl monit acl netcat-openbsd jq sqlite3 - -# <<< Apache konsequent entfernen >>> -systemctl disable --now apache2 >/dev/null 2>&1 || true -apt-get -y purge 'apache2*' >/dev/null 2>&1 || true -apt-get -y autoremove >/dev/null 2>&1 || true - -log "Systemuser/Dirs …" -id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail -id "$APP_USER" >/dev/null 2>&1 || adduser --disabled-password --gecos "" "$APP_USER" -# Systemuser/Dirs … -id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail -id "$APP_USER" >/dev/null 2>&1 || adduser --disabled-password --gecos "" "$APP_USER" - -# --- FIX: Gruppen und Berechtigungen für Maildir und Dovecot-Zugriff --- -# vmail soll primär der Gruppe "mail" angehören, zusätzlich dovecot -usermod -g mail -a -G dovecot vmail || true - -# App-User in relevante Gruppen -usermod -a -G "$APP_GROUP" "$APP_USER" || true -usermod -a -G mail,dovecot "$APP_USER" || true - -# Maildir-Baum für Gruppe mail lesbar -chgrp -R mail /var/mail/vhosts || true -chmod -R g+rx /var/mail/vhosts || true - -# ACLs setzen, damit neue Verzeichnisse automatisch passende Rechte bekommen -setfacl -R -m g:mail:rx /var/mail/vhosts || true -setfacl -dR -m g:mail:rx /var/mail/vhosts || true -usermod -a -G "$APP_GROUP" "$APP_USER" || true -install -d -m 0755 -o root -g root /var/www -install -d -m 0775 -o "$APP_USER" -g "$APP_GROUP" "$APP_DIR" - -SUDOERS_DKIM="/etc/sudoers.d/mailwolt-dkim" -cat > "${SUDOERS_DKIM}" <<'EOF' -Defaults!/usr/local/sbin/mailwolt-install-dkim !requiretty -Defaults!/usr/local/sbin/mailwolt-remove-dkim !requiretty -Defaults!/usr/bin/systemctl !requiretty -Defaults!/usr/bin/test !requiretty - -www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install-dkim * -www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-remove-dkim * -www-data ALL=(root) NOPASSWD: /usr/bin/systemctl reload opendkim -www-data ALL=(root) NOPASSWD: /usr/bin/test * - -mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install-dkim * -mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-remove-dkim * -mailwolt ALL=(root) NOPASSWD: /usr/bin/systemctl reload opendkim -mailwolt ALL=(root) NOPASSWD: /usr/bin/test * -EOF -chown root:root "${SUDOERS_DKIM}" -chmod 440 "${SUDOERS_DKIM}" - -if ! visudo -c -f "${SUDOERS_DKIM}" >/dev/null 2>&1; then - echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_DKIM} – entferne Datei." - rm -f "${SUDOERS_DKIM}" -fi - -SUDOERS_DOVEADM="/etc/sudoers.d/mailwolt-doveadm" -cat > "${SUDOERS_DOVEADM}" <<'EOF' -Cmnd_Alias MW_DOVEADM_STATUS = /usr/bin/doveadm -f tab mailbox status -u * messages INBOX, \ - /usr/bin/doveadm mailbox status -u * messages INBOX -www-data ALL=(vmail) NOPASSWD: MW_DOVEADM_STATUS -mailwolt ALL=(vmail) NOPASSWD: MW_DOVEADM_STATUS -EOF -chown root:root "${SUDOERS_DOVEADM}" -chmod 440 "${SUDOERS_DOVEADM}" -visudo -c -f "${SUDOERS_DOVEADM}" || rm -f "${SUDOERS_DOVEADM}" - -log "MariaDB include-fix …" -mkdir -p /etc/mysql/mariadb.conf.d -[[ -f /etc/mysql/mariadb.cnf ]] || echo '!include /etc/mysql/mariadb.conf.d/*.cnf' > /etc/mysql/mariadb.cnf - -log "Redis absichern …" -if [[ -z "${REDIS_PASS:-}" || "${REDIS_PASS}" == "changeme" ]]; then - REDIS_PASS="$(openssl rand -hex 16)" - export REDIS_PASS - log "Neues Redis-Passwort generiert." -fi -# Aktiven Redis-Config-Pfad aus systemd holen (Fallback: Standard) -REDIS_CONF="$(systemctl show -p ExecStart redis-server \ - | sed -n 's/^ExecStart=.*redis-server[[:space:]]\+\([^[:space:]]\+\).*/\1/p')" -REDIS_CONF="${REDIS_CONF:-/etc/redis/redis.conf}" - -# Bind + protected-mode hart setzen -sed -i 's/^[[:space:]]*#\?[[:space:]]*bind .*/bind 127.0.0.1/' "$REDIS_CONF" -sed -i 's/^[[:space:]]*#\?[[:space:]]*protected-mode .*/protected-mode yes/' "$REDIS_CONF" - -# Vorherige requirepass-Zeilen entfernen (kommentiert/unkommentiert), dann neu schreiben -sed -i '/^[[:space:]]*#\?[[:space:]]*requirepass[[:space:]]\+/d' "$REDIS_CONF" -printf '\nrequirepass %s\n' "${REDIS_PASS}" >> "$REDIS_CONF" - -# Dienst aktivieren & neu starten -systemctl enable --now redis-server -systemctl restart redis-server || true - -# Sanity-Check (kein harter Exit, nur Log) -if redis-cli -a "${REDIS_PASS}" ping 2>/dev/null | grep -q PONG; then - log "Redis mit Passwort OK." -else - warn "Redis PING mit Passwort fehlgeschlagen – bitte /etc/redis/redis.conf prüfen." -fi \ No newline at end of file diff --git a/mailwolt-installer/scripts/20-ssl.sh b/mailwolt-installer/scripts/20-ssl.sh deleted file mode 100644 index 1dceed2..0000000 --- a/mailwolt-installer/scripts/20-ssl.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -CONF_BASE="/etc/${APP_USER}" -CERT_DIR="${CONF_BASE}/ssl" -UI_SSL_DIR="/etc/ssl/ui"; WEBMAIL_SSL_DIR="/etc/ssl/webmail"; MAIL_SSL_DIR="/etc/ssl/mail" -UI_CERT="${UI_SSL_DIR}/fullchain.pem"; UI_KEY="${UI_SSL_DIR}/privkey.pem" -WEBMAIL_CERT="${WEBMAIL_SSL_DIR}/fullchain.pem"; WEBMAIL_KEY="${WEBMAIL_SSL_DIR}/privkey.pem" -MAIL_CERT="${MAIL_SSL_DIR}/fullchain.pem"; MAIL_KEY="${MAIL_SSL_DIR}/privkey.pem" - -install -d -m 0750 "$CERT_DIR" -CERT="${CERT_DIR}/cert.pem"; KEY="${CERT_DIR}/key.pem" - -if [[ ! -s "$CERT" || ! -s "$KEY" ]]; then - log "Self-signed Zertifikat erzeugen …" - OSSL_CFG="${CERT_DIR}/openssl.cnf" - cat > "$OSSL_CFG" <&2 -fi - -# Optional: kurze Info, wohin verlinkt wurde -echo "[i] Mail TLS: $MAIL_CERT -> $CERT ; $MAIL_KEY -> $KEY" \ No newline at end of file diff --git a/mailwolt-installer/scripts/21-le-deploy-hook.sh b/mailwolt-installer/scripts/21-le-deploy-hook.sh deleted file mode 100644 index fb37804..0000000 --- a/mailwolt-installer/scripts/21-le-deploy-hook.sh +++ /dev/null @@ -1,588 +0,0 @@ -#!/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 - . /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." \ No newline at end of file diff --git a/mailwolt-installer/scripts/22-dkim-helper.sh b/mailwolt-installer/scripts/22-dkim-helper.sh deleted file mode 100644 index 2ef8c7d..0000000 --- a/mailwolt-installer/scripts/22-dkim-helper.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Installiere DKIM-Helper …" - -install -d -m 0755 /usr/local/sbin - -cat >/usr/local/sbin/mailwolt-install-dkim <<'EOF' -#!/usr/bin/env bash -set -euo pipefail - -DOMAIN="$1" # z.B. sysmail.toastra.com -SELECTOR="${2:-mwl1}" - -[[ -n "$DOMAIN" ]] || { echo "Usage: $0 [selector]"; exit 2; } - -KEYDIR="/etc/opendkim/keys/${DOMAIN}" -PRIV="${KEYDIR}/${SELECTOR}.private" -TXT="${KEYDIR}/${SELECTOR}.txt" - -install -d -m 0750 -o opendkim -g opendkim "$KEYDIR" - -if [[ ! -s "$PRIV" ]]; then - opendkim-genkey -b 2048 -s "$SELECTOR" -d "$DOMAIN" -D "$KEYDIR" - chown opendkim:opendkim "$PRIV" - chmod 600 "$PRIV" -fi - -grep -q "^${SELECTOR}\._domainkey\.${DOMAIN} " /etc/opendkim/KeyTable 2>/dev/null \ - || echo "${SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${SELECTOR}:${PRIV}" >> /etc/opendkim/KeyTable - -grep -q "^\*@${DOMAIN} " /etc/opendkim/SigningTable 2>/dev/null \ - || echo "*@${DOMAIN} ${SELECTOR}._domainkey.${DOMAIN}" >> /etc/opendkim/SigningTable - -install -d -m 0755 /etc/mailwolt/dns -[[ -s "$TXT" ]] && cp -f "$TXT" "/etc/mailwolt/dns/dkim-${DOMAIN}.txt" || true - -systemctl restart opendkim -EOF - -log "[✓] DKIM-Helper installiert: /usr/local/sbin/mailwolt-install-dkim" \ No newline at end of file diff --git a/mailwolt-installer/scripts/30-db.sh b/mailwolt-installer/scripts/30-db.sh deleted file mode 100644 index 9781562..0000000 --- a/mailwolt-installer/scripts/30-db.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "MariaDB vorbereiten …" -systemctl enable --now mariadb -mysql -uroot < /etc/postfix/sql/mysql-virtual-domains.cf < /etc/postfix/sql/mysql-virtual-mailbox-maps.cf < /etc/postfix/sql/mysql-virtual-alias-maps.cf </dev/null 2>&1 || true diff --git a/mailwolt-installer/scripts/50-dovecot.sh b/mailwolt-installer/scripts/50-dovecot.sh deleted file mode 100644 index bd2922f..0000000 --- a/mailwolt-installer/scripts/50-dovecot.sh +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -MAIL_SSL_DIR="/etc/ssl/mail" -MAIL_CERT="${MAIL_SSL_DIR}/fullchain.pem" -MAIL_KEY="${MAIL_SSL_DIR}/privkey.pem" - -log "Dovecot konfigurieren …" - -# ────────────────────────────────────────────────────────────────────────────── -# 1) vmail-Benutzer/Gruppe & Mailspool vorbereiten (DYNAMIC UID!) -# ────────────────────────────────────────────────────────────────────────────── - -# Sicherstellen, dass die Gruppe 'mail' existiert (auf Debian/Ubuntu idR vorhanden) -getent group mail >/dev/null || groupadd -g 8 mail || true - -# vmail anlegen, wenn er fehlt. Bevorzugt UID 109, falls frei – sonst automatisch. -if ! getent passwd vmail >/dev/null; then - if ! getent passwd 109 >/dev/null; then - useradd -u 109 -g mail -d /var/mail -M -s /usr/sbin/nologin vmail - else - useradd -g mail -d /var/mail -M -s /usr/sbin/nologin vmail - fi -fi - -# Tatsächliche vmail-UID ermitteln (wird unten in die Dovecot-Config geschrieben) -VMAIL_UID="$(id -u vmail)" - -# Mailspool-Basis -install -d -m 0770 -o vmail -g mail /var/mail/vhosts - -# ────────────────────────────────────────────────────────────────────────────── -# 2) Dovecot Grundgerüst -# ────────────────────────────────────────────────────────────────────────────── - -# Hauptdatei -install -d -m 0755 /etc/dovecot/conf.d -cat > /etc/dovecot/dovecot.conf <<'CONF' -!include_try /etc/dovecot/conf.d/*.conf -CONF - -# Mail-Location & Namespace + UID-Grenzen -cat > /etc/dovecot/conf.d/10-mail.conf < /etc/dovecot/conf.d/15-mailboxes.conf <<'CONF' -namespace inbox { - inbox = yes - - mailbox Drafts { - special_use = \Drafts - auto = subscribe - } - mailbox Junk { - special_use = \Junk - auto = subscribe - } - mailbox Trash { - special_use = \Trash - auto = subscribe - } - mailbox Sent { - special_use = \Sent - auto = subscribe - } - mailbox Archive { - special_use = \Archive - auto = create - } -} -CONF - -# Auth -cat > /etc/dovecot/conf.d/10-auth.conf <<'CONF' -disable_plaintext_auth = yes -auth_mechanisms = plain login -!include_try auth-sql.conf.ext - -auth_cache_size = 10M -auth_cache_ttl = 1 hour -CONF - -# SQL-Anbindung (Passwörter aus App-DB) -cat > /etc/dovecot/dovecot-sql.conf.ext < /etc/dovecot/conf.d/auth-sql.conf.ext <<'CONF' -passdb { - driver = sql - args = /etc/dovecot/dovecot-sql.conf.ext -} -userdb { - driver = static - args = uid=vmail gid=mail home=/var/mail/vhosts/%d/%n -} -CONF -chown root:dovecot /etc/dovecot/conf.d/auth-sql.conf.ext -chmod 640 /etc/dovecot/conf.d/auth-sql.conf.ext - -# ────────────────────────────────────────────────────────────────────────────── -# 3) IMAP Optimierung (iOS/IDLE-freundlich) -# ────────────────────────────────────────────────────────────────────────────── - -cat > /etc/dovecot/conf.d/20-imap.conf <<'CONF' -# IMAP-spezifische Einstellungen - -imap_idle_notify_interval = 2 mins -imap_hibernate_timeout = 0 - -protocol imap { - mail_max_userip_connections = 20 - imap_logout_format = in=%i out=%o deleted=%{deleted} expunged=%{expunged} -} -CONF - -# ────────────────────────────────────────────────────────────────────────────── -# 4) Master Services (LMTP, AUTH, IMAP, POP3, STATS) -# ────────────────────────────────────────────────────────────────────────────── - -cat > /etc/dovecot/conf.d/10-master.conf <<'CONF' -service lmtp { - unix_listener /var/spool/postfix/private/dovecot-lmtp { - mode = 0600 - user = postfix - group = postfix - } -} -service auth { - unix_listener /var/spool/postfix/private/auth { - mode = 0660 - user = postfix - group = postfix - } - unix_listener auth-userdb { - mode = 0660 - user = vmail - group = mail - } - process_limit = 1 -} -service imap-login { - inet_listener imap { - port = 143 - } - inet_listener imaps { - port = 993 - ssl = yes - } - process_limit = 128 - process_min_avail = 10 - service_count = 0 - vsz_limit = 512M -} -service pop3-login { - inet_listener pop3 { - port = 110 - } - inet_listener pop3s { - port = 995 - ssl = yes - } - process_limit = 50 - service_count = 0 -} -CONF - -# --- Dovecot: doveadm-server für App-Zugriff --- -cat >/etc/dovecot/conf.d/99-mailwolt-perms.conf <<'CONF' -service auth { - unix_listener auth-userdb { - mode = 0660 - user = vmail - group = mail - } -} - -service stats { - unix_listener stats-reader { - mode = 0660 - user = vmail - group = mail - } - unix_listener stats-writer { - mode = 0660 - user = vmail - group = mail - } -} -CONF - -# ────────────────────────────────────────────────────────────────────────────── -# 5) SSL-Konfiguration (ohne DH-Param-Erzeugung) -# ────────────────────────────────────────────────────────────────────────────── - -DOVECOT_SSL_CONF="/etc/dovecot/conf.d/10-ssl.conf" -touch "$DOVECOT_SSL_CONF" -grep -q '^ssl\s*=' "$DOVECOT_SSL_CONF" 2>/dev/null || echo "ssl = required" >> "$DOVECOT_SSL_CONF" -if grep -q '^\s*ssl_cert\s*=' "$DOVECOT_SSL_CONF"; then - sed -i "s|^\s*ssl_cert\s*=.*|ssl_cert = <${MAIL_CERT}|" "$DOVECOT_SSL_CONF" -else - echo "ssl_cert = <${MAIL_CERT}" >> "$DOVECOT_SSL_CONF" -fi -if grep -q '^\s*ssl_key\s*=' "$DOVECOT_SSL_CONF"; then - sed -i "s|^\s*ssl_key\s*=.*|ssl_key = <${MAIL_KEY}|" "$DOVECOT_SSL_CONF" -else - echo "ssl_key = <${MAIL_KEY}" >> "$DOVECOT_SSL_CONF" -fi -grep -q '^ssl_min_protocol' "$DOVECOT_SSL_CONF" || echo "ssl_min_protocol = TLSv1.2" >> "$DOVECOT_SSL_CONF" -grep -q '^ssl_prefer_server_ciphers' "$DOVECOT_SSL_CONF" || echo "ssl_prefer_server_ciphers = yes" >> "$DOVECOT_SSL_CONF" - -grep -q '^ssl_dh' "$DOVECOT_SSL_CONF" || echo "ssl_dh = > "$DOVECOT_SSL_CONF" - -# ────────────────────────────────────────────────────────────────────────────── -# 6) Verzeichnisse & Rechte prüfen -# ────────────────────────────────────────────────────────────────────────────── - -mkdir -p /var/spool/postfix/private -chown root:root /var/spool/postfix -chmod 0755 /var/spool/postfix -chown postfix:postfix /var/spool/postfix/private -chmod 0755 /var/spool/postfix/private - - -# ────────────────────────────────────────────────────────────────────────────── -# 7) Abschluss -# ────────────────────────────────────────────────────────────────────────────── - -log "Dovecot-Konfiguration abgeschlossen." diff --git a/mailwolt-installer/scripts/60-rspamd-opendkim.sh b/mailwolt-installer/scripts/60-rspamd-opendkim.sh deleted file mode 100644 index 9d7166a..0000000 --- a/mailwolt-installer/scripts/60-rspamd-opendkim.sh +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Rspamd + OpenDKIM einrichten …" - -# ────────────────────────────────────────────────────────────── -# ENV laden -# ────────────────────────────────────────────────────────────── -set +u -[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env -set -u - -BASE_DOMAIN="${BASE_DOMAIN:-example.com}" -SYSMAIL_DOMAIN="${SYSMAIL_DOMAIN:-sysmail.${BASE_DOMAIN}}" # z.B. sysmail.example.com -DKIM_ENABLE="${DKIM_ENABLE:-1}" # 1=OpenDKIM aktiv -DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}" # z.B. mwl1 -DKIM_GENERATE="${DKIM_GENERATE:-0}" # 1=Key generieren, falls fehlt -RSPAMD_CONTROLLER_PASSWORD="${RSPAMD_CONTROLLER_PASSWORD:-admin}" - -# ────────────────────────────────────────────────────────────── -# Rspamd (Controller + Milter) -# ────────────────────────────────────────────────────────────── -install -d -m 0750 /etc/rspamd/local.d - -if command -v rspamadm >/dev/null 2>&1; then - RSPAMD_HASH="$(rspamadm pw -p "${RSPAMD_CONTROLLER_PASSWORD}")" -else - RSPAMD_HASH="${RSPAMD_CONTROLLER_PASSWORD}" -fi - -cat >/etc/rspamd/local.d/worker-controller.inc </etc/rspamd/local.d/statistic.conf </etc/rspamd/local.d/worker-proxy.inc <<'CONF' -worker "proxy" { - bind_socket = "127.0.0.1:11332"; - milter = yes; - timeout = 120s; - - upstream "scan" { - default = yes; - self_scan = yes; - servers = "127.0.0.1:11333"; - } -} -CONF - -cat >/etc/rspamd/local.d/worker-normal.inc <<'CONF' -worker "normal" { - bind_socket = "127.0.0.1:11333"; -} -CONF - -cat >/etc/rspamd/local.d/milter_headers.conf <<'CONF' -use = ["authentication-results"]; -header = "Authentication-Results"; -CONF - -cat >/etc/rspamd/local.d/options.inc <<'CONF' -dns { - servers = ["9.9.9.9:53", "1.1.1.1:53"]; - timeout = 5s; - retransmits = 2; -} -CONF - -# ────────────────────────────────────────────────────────────── -# Rspamd Redis-Konfiguration -# ────────────────────────────────────────────────────────────── -log "Rspamd Redis konfigurieren …" - -: "${REDIS_PASS:=}" - -cat >/etc/rspamd/local.d/redis.conf </dev/null 2>&1; then - if [[ -n "${REDIS_PASS}" ]]; then - if redis-cli -h 127.0.0.1 -p 6379 -a "${REDIS_PASS}" ping >/dev/null 2>&1; then - log "[✓] Redis erreichbar und Passwort akzeptiert." - else - log "[!] Warnung: Redis antwortet nicht oder Passwort falsch." - fi - else - if redis-cli -h 127.0.0.1 -p 6379 ping >/dev/null 2>&1; then - log "[✓] Redis erreichbar (ohne Passwort)." - else - log "[!] Warnung: Redis antwortet nicht." - fi - fi -fi - -systemctl enable --now rspamd || true - -# ────────────────────────────────────────────────────────────── -# OpenDKIM – nur wenn DKIM_ENABLE=1 -# ────────────────────────────────────────────────────────────── - -if [[ "${DKIM_ENABLE}" != "1" ]]; then - log "DKIM_ENABLE=0 → OpenDKIM wird übersprungen." - /usr/sbin/postconf -e "milter_default_action = accept" - /usr/sbin/postconf -e "milter_protocol = 6" - /usr/sbin/postconf -e "smtpd_milters = inet:127.0.0.1:11332" - /usr/sbin/postconf -e "non_smtpd_milters = inet:127.0.0.1:11332" - exit 0 -fi - - -install -d -m 0755 /etc/opendkim -install -d -m 0750 /etc/opendkim/keys -chown -R opendkim:opendkim /etc/opendkim -chmod 750 /etc/opendkim/keys - -# TrustedHosts -cat >/etc/opendkim/TrustedHosts <<'CONF' -127.0.0.1 -::1 -localhost -CONF -chown opendkim:opendkim /etc/opendkim/TrustedHosts -chmod 640 /etc/opendkim/TrustedHosts - -# ── Key-Verzeichnis für SYSMAIL_DOMAIN vorbereiten ─────────────────────────── -KEY_DIR="/etc/opendkim/keys/${SYSMAIL_DOMAIN}" -KEY_PRIV="${KEY_DIR}/${DKIM_SELECTOR}.private" -KEY_DNSTXT="${KEY_DIR}/${DKIM_SELECTOR}.txt" -install -d -m 0750 -o opendkim -g opendkim "${KEY_DIR}" - -# ── Key optional generieren (nur wenn gewünscht) ───────────────────────────── -if [[ ! -s "${KEY_PRIV}" && "${DKIM_GENERATE}" = "1" ]]; then - if command -v opendkim-genkey >/dev/null 2>&1; then - opendkim-genkey -b 2048 -s "${DKIM_SELECTOR}" -d "${SYSMAIL_DOMAIN}" -D "${KEY_DIR}" - chown opendkim:opendkim "${KEY_DIR}/${DKIM_SELECTOR}.private" || true - chmod 600 "${KEY_DIR}/${DKIM_SELECTOR}.private" || true - else - echo "[!] opendkim-genkey fehlt – kann DKIM-Key nicht generieren." - fi -fi - -# ── Key-/SigningTable nur anlegen, nicht leeren ─────────────────────────────── -touch /etc/opendkim/KeyTable /etc/opendkim/SigningTable -chown opendkim:opendkim /etc/opendkim/KeyTable /etc/opendkim/SigningTable -chmod 640 /etc/opendkim/KeyTable /etc/opendkim/SigningTable - -if [[ -s "${KEY_PRIV}" && "${BASE_DOMAIN}" != "example.com" ]]; then - LINE_KT="${DKIM_SELECTOR}._domainkey.${SYSMAIL_DOMAIN} ${SYSMAIL_DOMAIN}:${DKIM_SELECTOR}:${KEY_PRIV}" - LINE_ST="*@${SYSMAIL_DOMAIN} ${DKIM_SELECTOR}._domainkey.${SYSMAIL_DOMAIN}" - grep -Fqx "$LINE_KT" /etc/opendkim/KeyTable || echo "$LINE_KT" >> /etc/opendkim/KeyTable - grep -Fqx "$LINE_ST" /etc/opendkim/SigningTable || echo "$LINE_ST" >> /etc/opendkim/SigningTable -else - echo "[i] Kein Private Key unter ${KEY_PRIV} – App-Helper trägt später ein." -fi - -# ── Hauptkonfiguration ─────────────────────────────────────────────────────── -cat >/etc/opendkim.conf <<'CONF' -Syslog yes -UMask 002 -Mode sv -Socket inet:8891@127.0.0.1 -PidFile /run/opendkim/opendkim.pid -Canonicalization relaxed/simple - -On-BadSignature accept -On-Default accept -On-KeyNotFound accept -On-NoSignature accept - -LogWhy yes -OversignHeaders From - -KeyTable /etc/opendkim/KeyTable -SigningTable refile:/etc/opendkim/SigningTable -ExternalIgnoreList /etc/opendkim/TrustedHosts -InternalHosts /etc/opendkim/TrustedHosts - -UserID opendkim:opendkim -AutoRestart yes -AutoRestartRate 10/1h -Background yes -DNSTimeout 5 -SignatureAlgorithm rsa-sha256 -SyslogSuccess yes -CONF - -# ── systemd Drop-in: /run/opendkim sicherstellen ───────────────────────────── -install -d -m 0755 /etc/systemd/system/opendkim.service.d -cat >/etc/systemd/system/opendkim.service.d/override.conf <<'EOF' -[Service] -RuntimeDirectory=opendkim -RuntimeDirectoryMode=0755 -EOF - -install -d -o opendkim -g opendkim -m 0755 /run/opendkim - -# ────────────────────────────────────────────────────────────── -# Root-Helper: DKIM installieren / entfernen + sudoers-Regel -# ────────────────────────────────────────────────────────────── -install -d -m 0750 /usr/local/sbin - -# --- mailwolt-install-dkim ------------------------------------ -cat > /usr/local/sbin/mailwolt-install-dkim <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail - -DOMAIN="$1" -SELECTOR="$2" -SRC_PRIV="$3" -SRC_TXT="${4:-}" - -OKDIR="/etc/opendkim" -KEYDIR="${OKDIR}/keys/${DOMAIN}" -KEYPRI="${KEYDIR}/${SELECTOR}.private" - -install -d -m 0750 -o opendkim -g opendkim "${KEYDIR}" -install -m 0600 -o opendkim -g opendkim "${SRC_PRIV}" "${KEYPRI}" - -KT="${OKDIR}/KeyTable" -ST="${OKDIR}/SigningTable" -touch "$KT" "$ST" -chown opendkim:opendkim "$KT" "$ST" -chmod 0640 "$KT" "$ST" - -LINE_KT="${SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${SELECTOR}:${KEYPRI}" -LINE_ST="*@${DOMAIN} ${SELECTOR}._domainkey.${DOMAIN}" - -grep -Fqx "$LINE_KT" "$KT" || echo "$LINE_KT" >> "$KT" -grep -Fqx "$LINE_ST" "$ST" || echo "$LINE_ST" >> "$ST" - -if [[ -n "${SRC_TXT}" && -s "${SRC_TXT}" ]]; then - install -d -m 0755 /etc/mailwolt/dns - cp -f "${SRC_TXT}" "/etc/mailwolt/dns/dkim-${DOMAIN}.txt" -fi - -systemctl is-active --quiet opendkim && systemctl reload opendkim || true -echo "OK" -EOSH -chmod 0750 /usr/local/sbin/mailwolt-install-dkim -chown root:root /usr/local/sbin/mailwolt-install-dkim - -# --- 2) mailwolt-remove-dkim ---------------------------------- -cat >/usr/local/sbin/mailwolt-remove-dkim <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail - -DOMAIN="$1" # z.B. kunden.tld oder sysmail.example.com -SELECTOR="$2" # z.B. mwl1 - -OKDIR="/etc/opendkim" -KEYDIR="${OKDIR}/keys/${DOMAIN}" -KEYPRI="${KEYDIR}/${SELECTOR}.private" -KT="${OKDIR}/KeyTable" -ST="${OKDIR}/SigningTable" - -# Key-Datei löschen (falls vorhanden) -[[ -f "${KEYPRI}" ]] && rm -f "${KEYPRI}" - -# Zeilen aus KeyTable und SigningTable entfernen -if [[ -f "$KT" ]]; then - tmp="$(mktemp)"; grep -v -F "${SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${SELECTOR}:" "$KT" >"$tmp" && mv "$tmp" "$KT" - chown opendkim:opendkim "$KT"; chmod 0640 "$KT" -fi -if [[ -f "$ST" ]]; then - tmp="$(mktemp)"; grep -v -F "*@${DOMAIN} ${SELECTOR}._domainkey.${DOMAIN}" "$ST" >"$tmp" && mv "$tmp" "$ST" - chown opendkim:opendkim "$ST"; chmod 0640 "$ST" -fi - -# Verzeichnis ggf. aufräumen -rmdir "${KEYDIR}" 2>/dev/null || true - -# Dienst neu laden, falls aktiv -if systemctl is-active --quiet opendkim; then - systemctl reload opendkim || true -fi - -echo "OK" -EOSH -chown root:root /usr/local/sbin/mailwolt-remove-dkim -chmod 0750 /usr/local/sbin/mailwolt-remove-dkim - -# ── Dienst + Postfix-Milter aktivieren ───────────────────────── -systemctl daemon-reload -systemctl enable opendkim || true - -touch /run/mailwolt.need-apply-milters || true - -chgrp _rspamd /etc/rspamd/local.d/*.inc /etc/rspamd/local.d/*.conf || true -chmod 0640 /etc/rspamd/local.d/*.inc /etc/rspamd/local.d/*.conf || true - -#/usr/sbin/postconf -e "smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891" -#/usr/sbin/postconf -e "non_smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891" - -log "[✓] Rspamd + OpenDKIM eingerichtet (läuft; signiert, sobald Keys vorhanden sind)." diff --git a/mailwolt-installer/scripts/61-opendmarc.sh b/mailwolt-installer/scripts/61-opendmarc.sh deleted file mode 100644 index 5f48521..0000000 --- a/mailwolt-installer/scripts/61-opendmarc.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "OpenDMARC installieren/konfigurieren …" - -# Flags laden -set +u -[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env -set -u -OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}" - -# Paket sicherstellen -if ! dpkg -s opendmarc >/dev/null 2>&1; then - apt-get update -qq - apt-get install -y opendmarc -fi - -# Config-Verzeichnisse -install -d -m 0755 /etc/opendmarc -install -d -m 0755 /run/opendmarc - -# IgnoreHosts -cat >/etc/opendmarc/ignore.hosts <<'EOF' -127.0.0.1 -::1 -localhost -EOF -chmod 0644 /etc/opendmarc/ignore.hosts - -# Hauptkonfiguration -cat >/etc/opendmarc.conf <<'EOF' -AuthservID mailwolt -TrustedAuthservIDs mailwolt -IgnoreHosts /etc/opendmarc/ignore.hosts -Syslog true -SoftwareHeader true -Socket local:/run/opendmarc/opendmarc.sock -RejectFailures false -EOF -chmod 0644 /etc/opendmarc.conf - -# systemd Drop-in für RuntimeDirectory (robust nach Reboot) -install -d -m 0755 /etc/systemd/system/opendmarc.service.d -cat >/etc/systemd/system/opendmarc.service.d/override.conf <<'EOF' -[Service] -RuntimeDirectory=opendmarc -RuntimeDirectoryMode=0755 -EOF - -systemctl daemon-reload - -# Dienst nach Flag -if [[ "$OPENDMARC_ENABLE" = "1" ]]; then - systemctl enable --now opendmarc -else - systemctl disable --now opendmarc || true -fi - -# Postfix-Milter-Kette konsistent setzen (Rspamd + OpenDKIM + optional OpenDMARC) -touch /run/mailwolt.need-apply-milters || true - -log "[✓] OpenDMARC (ENABLE=${OPENDMARC_ENABLE}) bereit." \ No newline at end of file diff --git a/mailwolt-installer/scripts/62-clamav.sh b/mailwolt-installer/scripts/62-clamav.sh deleted file mode 100644 index fd3cb07..0000000 --- a/mailwolt-installer/scripts/62-clamav.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "ClamAV (clamav-daemon) installieren/konfigurieren …" - -# Flags laden -set +u -[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env -set -u -CLAMAV_ENABLE="${CLAMAV_ENABLE:-0}" - -# Pakete -if ! dpkg -s clamav-daemon >/dev/null 2>&1; then - apt-get update -qq - apt-get install -y clamav clamav-daemon -fi - -# Signaturen aktualisieren (erst Freshclam starten) -systemctl stop clamav-freshclam 2>/dev/null || true -freshclam || true -systemctl start clamav-freshclam || true - -# clamd LocalSocket setzen -sed -i 's|^#\?LocalSocket .*|LocalSocket /run/clamav/clamd.ctl|' /etc/clamav/clamd.conf || true -install -d -m 0755 /run/clamav -chown clamav:clamav /run/clamav - -# Dienst nach Flag -if [[ "$CLAMAV_ENABLE" = "1" ]]; then - systemctl enable --now clamav-daemon -else - systemctl disable --now clamav-daemon || true -fi - -# Rspamd-Integration (nur wenn aktiv) -AV_CONF="/etc/rspamd/local.d/antivirus.conf" -if [[ "$CLAMAV_ENABLE" = "1" ]]; then - cat >"$AV_CONF" <<'EOF' -clamav { - symbol = "CLAM_VIRUS"; - type = "clamav"; - servers = "/run/clamav/clamd.ctl"; - scan_mime_parts = true; - scan_text_mime = true; - max_size = 50mb; - log_clean = false; - action = "reject"; -} -EOF - chown root:_rspamd "$AV_CONF" || true - chmod 0640 "$AV_CONF" || true - systemctl reload rspamd || systemctl restart rspamd -else - rm -f "$AV_CONF" || true - systemctl reload rspamd || true -fi - -log "[✓] ClamAV (ENABLE=${CLAMAV_ENABLE}) konfiguriert." \ No newline at end of file diff --git a/mailwolt-installer/scripts/63-fail2ban.sh b/mailwolt-installer/scripts/63-fail2ban.sh deleted file mode 100644 index 454b24f..0000000 --- a/mailwolt-installer/scripts/63-fail2ban.sh +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Fail2Ban installieren/konfigurieren …" - -# Flags laden -set +u -[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env -set -u -FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" - -# Paket -if ! dpkg -s fail2ban >/dev/null 2>&1; then - apt-get update -qq - apt-get install -y fail2ban sqlite3 -fi - -install -d -m 0755 /etc/fail2ban/jail.d - -# --------------------------------------------------------------- -# Basis-Jails (praxisnah) -# --------------------------------------------------------------- -cat >/etc/fail2ban/jail.d/mailwolt.conf <<'EOF' -[sshd] -enabled = true -port = ssh -logpath = /var/log/auth.log - -[postfix] -enabled = true -logpath = /var/log/mail.log -port = smtp,ssmtp,submission,465 - -[dovecot] -enabled = true -logpath = /var/log/mail.log -port = pop3,pop3s,imap,imaps,submission,465,587,993 - -[rspamd-controller] -enabled = true -port = 11334 -filter = rspamd -logpath = /var/log/rspamd/rspamd.log -maxretry = 5 -EOF - -# einfacher Filter für Rspamd-Controller -if [ ! -f /etc/fail2ban/filter.d/rspamd.conf ]; then - cat >/etc/fail2ban/filter.d/rspamd.conf <<'EOF' -[Definition] -failregex = .*Authentication failed for user.* from -ignoreregex = -EOF -fi - -# --------------------------------------------------------------- -# Fail2Ban-Backend auf SQLite umstellen -# --------------------------------------------------------------- -log "SQLite-Backend aktivieren …" - -cat >/etc/fail2ban/fail2ban.local <<'EOF' -[Definition] -loglevel = INFO -logtarget = /var/log/fail2ban.log -dbfile = /var/lib/fail2ban/fail2ban.sqlite3 -dbpurgeage = 86400 -EOF - -# Datenbankverzeichnis sicherstellen -install -d -o fail2ban -g fail2ban -m 0750 /var/lib/fail2ban - -# Falls DB nicht existiert, Dummy anlegen (wird vom Dienst erweitert) -if [ ! -f /var/lib/fail2ban/fail2ban.sqlite3 ]; then - sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "VACUUM;" -fi -chown fail2ban:fail2ban /var/lib/fail2ban/fail2ban.sqlite3 -chmod 0640 /var/lib/fail2ban/fail2ban.sqlite3 - -# --------------------------------------------------------------- -# sudoers für Web-UI -# --------------------------------------------------------------- -# Fail2Ban Blacklist-Jail -cat >/etc/fail2ban/jail.d/mailwolt-blacklist.local <<'EOF' -[mailwolt-blacklist] -enabled = true -filter = none -port = anyport -bantime = -1 -findtime = 1 -maxretry = 1 -EOF - -cat >/etc/fail2ban/filter.d/none.conf <<'EOF' -[Definition] -failregex = -ignoreregex = -EOF - -chmod 0640 /etc/fail2ban/filter.d/none.conf - - -SUDOERS_F2B="/etc/sudoers.d/mailwolt-fail2ban" -cat > "${SUDOERS_F2B}" <<'EOF' -Defaults:www-data !requiretty -www-data ALL=(root) NOPASSWD: \ - /usr/bin/fail2ban-client, \ - /usr/bin/fail2ban-client ping, \ - /usr/bin/fail2ban-client status, \ - /usr/bin/fail2ban-client status *, \ - /usr/bin/fail2ban-client get *, \ - /usr/bin/fail2ban-client set * banip *, \ - /usr/bin/fail2ban-client set * unbanip *, \ - /usr/bin/fail2ban-client reload, \ - /usr/bin/journalctl, \ - /bin/journalctl, \ - /usr/bin/zgrep, \ - /bin/zgrep, \ - /usr/bin/grep, \ - /bin/grep, \ - /usr/bin/tail, \ - /bin/tail, \ - /usr/bin/sqlite3, \ - /usr/bin/tee /etc/fail2ban/jail.d/* -EOF -chown root:root "${SUDOERS_F2B}" -chmod 440 "${SUDOERS_F2B}" - -if ! visudo -c -f "${SUDOERS_F2B}" >/dev/null 2>&1; then - echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_F2B} – entferne Datei." - rm -f "${SUDOERS_F2B}" -fi - -# --------------------------------------------------------------- -# Dienst aktivieren/deaktivieren -# --------------------------------------------------------------- -if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then - systemctl enable --now fail2ban -else - systemctl disable --now fail2ban || true -fi - -log "[✓] Fail2Ban (ENABLE=${FAIL2BAN_ENABLE}) bereit." - - -##!/usr/bin/env bash -#set -euo pipefail -#source ./lib.sh -# -#log "Fail2Ban installieren/konfigurieren …" -# -## Flags laden -#set +u -#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env -#set -u -#FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" -# -## Paket -#if ! dpkg -s fail2ban >/dev/null 2>&1; then -# apt-get update -qq -# apt-get install -y fail2ban -#fi -# -#install -d -m 0755 /etc/fail2ban/jail.d -# -## Basis-Jails (praxisnah) -#cat >/etc/fail2ban/jail.d/mailwolt.conf <<'EOF' -#[DEFAULT] -#bantime = 1h -#findtime = 10m -#maxretry = 5 -#backend = auto -# -#[sshd] -#enabled = true -#port = ssh -#logpath = /var/log/auth.log -# -#[postfix] -#enabled = true -#logpath = /var/log/mail.log -#port = smtp,ssmtp,submission,465 -# -#[dovecot] -#enabled = true -#logpath = /var/log/mail.log -#port = pop3,pop3s,imap,imaps,submission,465,587,993 -# -#[rspamd-controller] -#enabled = true -#port = 11334 -#filter = rspamd -#logpath = /var/log/rspamd/rspamd.log -#maxretry = 5 -#EOF -# -## einfacher Filter für Rspamd-Controller -#if [ ! -f /etc/fail2ban/filter.d/rspamd.conf ]; then -# cat >/etc/fail2ban/filter.d/rspamd.conf <<'EOF' -#[Definition] -#failregex = .*Authentication failed for user.* from -#ignoreregex = -#EOF -#fi -# -#SUDOERS_F2B="/etc/sudoers.d/mailwolt-fail2ban" -#cat > "${SUDOERS_F2B}" <<'EOF' -#www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status * -#EOF -#chown root:root "${SUDOERS_F2B}" -#chmod 440 "${SUDOERS_F2B}" -# -#if ! visudo -c -f "${SUDOERS_F2B}" >/dev/null 2>&1; then -# echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_F2B} – entferne Datei." -# rm -f "${SUDOERS_F2B}" -#fi -# -#sudo tee /etc/sudoers.d/mailwolt-fail2ban >/dev/null <<'EOF' -#www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status * -#EOF -#sudo visudo -cf /etc/sudoers.d/mailwolt-fail2ban -# -## Dienst nach Flag -#if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then -# systemctl enable --now fail2ban -#else -# systemctl disable --now fail2ban || true -#fi -# -#log "[✓] Fail2Ban (ENABLE=${FAIL2BAN_ENABLE}) bereit." \ No newline at end of file diff --git a/mailwolt-installer/scripts/64-apply-milters.sh b/mailwolt-installer/scripts/64-apply-milters.sh deleted file mode 100644 index 0927324..0000000 --- a/mailwolt-installer/scripts/64-apply-milters.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -# nur ausführen, wenn vorherige Schritte das Flag gesetzt haben -if [[ -f /run/mailwolt.need-apply-milters ]]; then - if command -v /usr/local/sbin/mailwolt-apply-milters >/dev/null 2>&1; then - log "Setze Postfix-Milter-Kette (Rspamd/OpenDKIM[/OpenDMARC]) …" - /usr/local/sbin/mailwolt-apply-milters || true - else - # Fallback (ident wie im Tool) - /usr/sbin/postconf -e "milter_default_action = accept" - /usr/sbin/postconf -e "milter_protocol = 6" - CHAIN="inet:127.0.0.1:11333, inet:127.0.0.1:8891" - systemctl is-active --quiet opendmarc && CHAIN="$CHAIN, inet:127.0.0.1:8893" || true - /usr/sbin/postconf -e "smtpd_milters = $CHAIN" - /usr/sbin/postconf -e "non_smtpd_milters = $CHAIN" - systemctl reload postfix || true - fi - rm -f /run/mailwolt.need-apply-milters || true - log "[✓] Milter-Kette angewandt." -else - log "Milter-Kette: kein Bedarf (Flag nicht gesetzt) – überspringe." -fi \ No newline at end of file diff --git a/mailwolt-installer/scripts/70-nginx.sh b/mailwolt-installer/scripts/70-nginx.sh deleted file mode 100644 index 822f1fd..0000000 --- a/mailwolt-installer/scripts/70-nginx.sh +++ /dev/null @@ -1,528 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Nginx konfigurieren …" - -# ── Flags/Umgebung (vom Bootstrap gesetzt; hier Fallbacks) ──────────────── -DEV_MODE="${DEV_MODE:-0}" # 1 = DEV (Vite-Proxy aktiv), 0 = PROD -PROXY_MODE="${PROXY_MODE:-0}" # 1 = NPM/Proxy davor, Backend spricht nur HTTP:80 -NPM_IP="${NPM_IP:-}" # z.B. 10.10.20.20 - -# Erwartet vom Bootstrap/Installer exportiert: -: "${UI_HOST:?UI_HOST fehlt}" -: "${WEBMAIL_HOST:?WEBMAIL_HOST fehlt}" -: "${APP_DIR:?APP_DIR fehlt}" - -ACME_ROOT="/var/www/letsencrypt" -install -d -m 0755 "$ACME_ROOT" - -# Default-Sites entfernen (verhindert doppelten default_server) -rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default || true - -# HTTP/2-Unterstützung erkennen -NGINX_HTTP2_SUFFIX="" -if nginx -V 2>&1 | grep -q http_v2; then - NGINX_HTTP2_SUFFIX=" http2" -fi - -# PHP-FPM Socket/TCP finden → fastcgi_pass bauen -detect_php_fpm_sock(){ - for v in 8.3 8.2 8.1 8.0 7.4; do - s="/run/php/php${v}-fpm.sock" - [[ -S "$s" ]] && { echo "unix:${s}"; return; } - done - [[ -S "/run/php/php-fpm.sock" ]] && { echo "unix:/run/php/php-fpm.sock"; return; } - echo "127.0.0.1:9000" -} -PHP_FPM_TARGET="$(detect_php_fpm_sock)" -if [[ "$PHP_FPM_TARGET" == unix:* ]]; then - FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};" -else - FASTCGI_PASS="fastcgi_pass ${PHP_FPM_TARGET};" -fi - -# ── Builder 1: HTTP-only (Proxy-Mode: TLS endet im NPM) ─────────────────── -## $1=host, $2=outfile -#build_site_http_only(){ -# local host="$1" outfile="$2" -# -# local def="" -# [[ "${DEV_MODE}" = "1" ]] && def=" default_server" -# [[ -z "${host}" || "${host}" = "_" ]] && host="_" -# -# cat > "$outfile" <> "$outfile" <<'CONF' -# # DEV: Vite-Proxy (HMR) -# location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_set_header Host $host; } -# location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_set_header Host $host; } -# location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_set_header Host $host; } -#CONF -# fi -# -# echo "}" >> "$outfile" -#} - -#build_site_http_only(){ -# local host="$1" outfile="$2" -# -# # DEV: IP-Zugriff ohne Hostname → default_server + server_name _ -# local def="" -# if [[ "${DEV_MODE}" = "1" ]]; then -# def=" default_server" -# host="_" -# fi -# [[ -z "${host}" || "${host}" = "_" ]] && host="_" -# -# cat > "$outfile" <> "$outfile" <<'CONF' -# # DEV: Vite-Proxy (HMR) -# location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_set_header Host $host; } -# location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_set_header Host $host; } -# location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_set_header Host $host; } -#CONF -# fi -# -# echo "}" >> "$outfile" -#} - -# $1=host, $2=outfile, $3=default_flag (default|nodefault) -build_site_http_only(){ - local host="$1" outfile="$2" def_flag="${3:-default}" - - local def="" - if [[ "${DEV_MODE}" = "1" && "${def_flag}" = "default" ]]; then - def=" default_server" - fi - [[ -z "${host}" || "${host}" = "_" ]] && host="_" - - cat > "$outfile" <> "$outfile" <<'CONF' - # DEV: Vite-Proxy (HMR) - location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_set_header Host $host; } - location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_set_header Host $host; } - location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_set_header Host $host; } -CONF - fi - - echo "}" >> "$outfile" -} - -# ── Builder 2: 80→443 Redirect + 443/TLS (Live-Server) ──────────────────── -# $1=host, $2=cert_dir (/etc/ssl/ui | /etc/ssl/webmail), $3=outfile -build_site_tls(){ - local host="$1" cert_dir="$2" outfile="$3" - local cert="${cert_dir}/fullchain.pem" - local key="${cert_dir}/privkey.pem" - - cat > "$outfile" <> "$outfile" <<'CONF' - # DEV: Vite-Proxy (npm run dev) - location = /vite-hmr { - proxy_pass http://127.0.0.1:5173/vite-hmr; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - } - location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; } - location ^~ /node_modules/ { proxy_pass http://127.0.0.1:5173/node_modules/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; } - location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; } -CONF - fi - - echo "}" >> "$outfile" -} - -build_site_acme_only(){ - local host="$1" outfile="$2" - - cat > "$outfile" < "$outfile" < "$outfile" < /etc/nginx/conf.d/realip.conf </dev/null 2>&1 || true - systemctl reload nginx || true -else - die "nginx -t fehlgeschlagen – siehe /var/log/nginx/*.log" -fi diff --git a/mailwolt-installer/scripts/75-le-issue.sh b/mailwolt-installer/scripts/75-le-issue.sh deleted file mode 100644 index 02ae118..0000000 --- a/mailwolt-installer/scripts/75-le-issue.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -ACME_WEBROOT="/var/www/letsencrypt" -install -d -m 0755 "${ACME_WEBROOT}/.well-known/acme-challenge" - -# Staging optional (verbraucht kein Live-Limit) -CERTBOT_EXTRA=() -LE_STAGING="${LE_STAGING:-0}" -[[ "$LE_STAGING" = "1" ]] && CERTBOT_EXTRA+=(--test-cert) - -# Einheitliche LE-Mail (Fallback) -LE_MAIL="${LE_EMAIL:-admin@${BASE_DOMAIN}}" - -resolve_ok() { - local host="$1" - local pats=() - [[ -n "${SERVER_PUBLIC_IPV4:-}" ]] && pats+=("${SERVER_PUBLIC_IPV4//./\\.}") - [[ -n "${SERVER_PUBLIC_IPV6:-}" ]] && pats+=("${SERVER_PUBLIC_IPV6//:/\\:}") - [[ ${#pats[@]} -eq 0 ]] && return 0 - getent ahosts "$host" | awk '{print $1}' | sort -u \ - | grep -Eq "^($(IFS='|'; echo "${pats[*]}"))$" -} - -probe_http() { - local host="$1" - echo test > "${ACME_WEBROOT}/.well-known/acme-challenge/_probe" - curl -fsS --max-time 5 -4 "http://${host}/.well-known/acme-challenge/_probe" >/dev/null \ - || curl -fsS --max-time 5 -6 "http://${host}/.well-known/acme-challenge/_probe" >/dev/null -} - -issue() { - local host="${1:-}" - [[ -z "$host" ]] && return 0 - - echo "[i] Versuche LE für ${host} …" - - if ! resolve_ok "$host"; then - echo "[!] DNS zeigt (noch) nicht hierher – überspringe: ${host}" - return 0 - fi - - if ! probe_http "$host"; then - echo "[!] ACME-HTTP-Check für ${host} fehlgeschlagen (Port 80/IPv6/Firewall/Nginx prüfen)." - # wir versuchen trotzdem – Certbot meldet sich, falls es scheitert - fi - - EXTRA_ARGS=() - # Für MX den Key wiederverwenden → stabiler TLSA (3 1 1) - [[ "$host" == "${MAIL_HOSTNAME}" ]] && EXTRA_ARGS+=(--reuse-key) - - # WICHTIG: Deploy-Wrapper anhängen, damit Symlinks/Nginx gesetzt werden - certbot certonly \ - --agree-tos -m "${LE_MAIL}" --non-interactive \ - --webroot -w "${ACME_WEBROOT}" -d "${host}" \ - --deploy-hook /usr/local/sbin/mailwolt-deploy.sh \ - "${EXTRA_ARGS[@]}" "${CERTBOT_EXTRA[@]}" || true -} - -if [[ "${BASE_DOMAIN}" != "example.com" ]]; then - issue "${UI_HOST:-}" - issue "${WEBMAIL_HOST:-}" - issue "${MAIL_HOSTNAME:-}" - - # Nginx nur neu laden, wenn aktiv - if systemctl is-active --quiet nginx; then - systemctl reload nginx || true - fi -else - echo "[i] BASE_DOMAIN=example.com – LE wird übersprungen." -fi - -# ────────────────────────────────────────────────────────────────────────────── -# FIX: Validierung & Reparatur des Mail-Zertifikats -# ────────────────────────────────────────────────────────────────────────────── -MAIL_SSL_DIR="/etc/ssl/mail" -install -d -m 0755 "$MAIL_SSL_DIR" - -MAIL_CERT="${MAIL_SSL_DIR}/fullchain.pem" -MAIL_KEY="${MAIL_SSL_DIR}/privkey.pem" - -HOST="${MAIL_HOSTNAME:-}" -LE_DIR="" -[[ -n "$HOST" ]] && LE_DIR="/etc/letsencrypt/live/${HOST}" - -need_fix=0 - -# Ist der vorhandene Key gültig? (leer/nicht vorhanden/ungültig -> fix) -if [[ ! -s "$MAIL_KEY" ]] || ! openssl pkey -in "$MAIL_KEY" -noout >/dev/null 2>&1; then - need_fix=1 -fi - -# Wenn Fix nötig: aus Let's Encrypt Live kopieren -if [[ $need_fix -eq 1 ]]; then - echo "[!] Ungültiger oder fehlender Mail-Private-Key – versuche Reparatur …" - if [[ -n "$LE_DIR" && -r "${LE_DIR}/privkey.pem" && -r "${LE_DIR}/fullchain.pem" ]]; then - cp -f "${LE_DIR}/privkey.pem" "$MAIL_KEY" - cp -f "${LE_DIR}/fullchain.pem" "$MAIL_CERT" - chown root:root "$MAIL_CERT" "$MAIL_KEY" - chmod 600 "$MAIL_KEY" - chmod 644 "$MAIL_CERT" - echo "[+] Zertifikate neu kopiert aus ${LE_DIR}." - # Reload NICHT sofort – flaggen für 90-services - touch /run/mailwolt.need-dovecot-reload - else - echo "[!] Konnte ${LE_DIR}/privkey.pem oder fullchain.pem nicht lesen – bitte prüfen." - fi -else - echo "[✓] Mail-Zertifikat & -Key sind gültig." -fi - -# Optionaler Live-Check (nur wenn Host gesetzt) -if [[ -n "$HOST" ]]; then - if openssl s_client -connect "${HOST}:993" -servername "${HOST}" /dev/null \ - | grep -q "Verify return code: 0"; then - echo "[✓] TLS-Handshake erfolgreich auf imaps://${HOST}:993." - else - echo "[!] TLS-Handshake auf imaps://${HOST}:993 fehlgeschlagen (Dovecot Reload folgt in 90-services, falls Flag gesetzt)." - fi -fi \ No newline at end of file diff --git a/mailwolt-installer/scripts/80-app.sh b/mailwolt-installer/scripts/80-app.sh deleted file mode 100644 index 6fa99e4..0000000 --- a/mailwolt-installer/scripts/80-app.sh +++ /dev/null @@ -1,343 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -# --- Helper: sicherer Frontend-Build als APP_USER --------------------------- -safe_frontend_build() { - echo "[i] Frontend build …" - - # Verzeichnisse & Rechte vorbereiten (Gruppen-sticky & ACL) - install -d -m 2775 -o "$APP_USER" -g "$APP_GROUP" \ - "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.npm-cache" - - chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}" - find "${APP_DIR}" -type d -exec chmod 2775 {} \; - find "${APP_DIR}" -type f -exec chmod 664 {} \; - setfacl -R -m g:"$APP_GROUP":rwX -m d:g:"$APP_GROUP":rwX "${APP_DIR}" || true - - # Vite-/Build-Reste bereinigen (falls mal root dort gebaut hat) - rm -rf "${APP_DIR}/node_modules/.vite" "${APP_DIR}/public/build/"* 2>/dev/null || true - - # npm auf projektlokales Cache konfigurieren - sudo -u "$APP_USER" -H bash -lc "cat > ~/.npmrc <<'RC' -fund=false -audit=false -prefer-offline=true -cache=${APP_DIR}/.npm-cache -RC" - - # Node ggf. installieren - if ! command -v node >/dev/null 2>&1; then - curl -fsSL https://deb.nodesource.com/setup_22.x | bash - - apt-get install -y nodejs - fi - - # Dependencies + Build (als App-User) - if sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && (npm ci --no-audit --no-fund || npm install --no-audit --no-fund) && npm run build"; then - return 0 - fi - - echo "[!] Build fehlgeschlagen – Rechtefix + Clean + Retry …" - rm -rf "${APP_DIR}/node_modules/.vite" "${APP_DIR}/public/build/"* 2>/dev/null || true - chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}" - find "${APP_DIR}" -type d -exec chmod 2775 {} \; - find "${APP_DIR}" -type f -exec chmod 664 {} \; - - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm run build" -} - -relink_and_reload() { - if [[ -d /etc/letsencrypt/renewal-hooks/deploy ]]; then - run-parts /etc/letsencrypt/renewal-hooks/deploy || true - fi - if systemctl is-active --quiet nginx; then - systemctl reload nginx || true - fi -} - -log "App bereitstellen …" -mkdir -p "$(dirname "$APP_DIR")" -chown -R "$APP_USER":"$APP_GROUP" "$(dirname "$APP_DIR")" - -# Repo holen oder Laravel anlegen – passe GIT_REPO/GIT_BRANCH bei Bedarf an -GIT_REPO="${GIT_REPO:-https://git.nexlab.at/boban/mailwolt.git}" -GIT_BRANCH="${GIT_BRANCH:-main}" - -if [[ "${GIT_REPO}" == "https://example.com/your-repo-placeholder.git" ]]; then - [[ -d "$APP_DIR" && -n "$(ls -A "$APP_DIR" 2>/dev/null || true)" ]] || \ - sudo -u "$APP_USER" -H bash -lc "cd /var/www && composer create-project laravel/laravel ${APP_USER} --no-interaction" -else - if [[ ! -d "${APP_DIR}/.git" ]]; then - sudo -u "$APP_USER" -H bash -lc "git clone --depth=1 -b ${GIT_BRANCH} ${GIT_REPO} ${APP_DIR}" - else - sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && git fetch --depth=1 origin ${GIT_BRANCH} && git reset --hard origin/${GIT_BRANCH}" - fi - [[ -f "${APP_DIR}/composer.json" ]] && sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-interaction --prefer-dist" -fi - -ENV_FILE="${APP_DIR}/.env" -sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && cp -n .env.example .env || true" -grep -q '^APP_KEY=' "$ENV_FILE" || echo "APP_KEY=" >> "$ENV_FILE" -sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan key:generate --force || true" - -# --- App-URL/Hosts ---------------------------------------------------------- -#SERVER_PUBLIC_IPV4="${SERVER_PUBLIC_IPV4:-}" -#if [[ -z "$SERVER_PUBLIC_IPV4" ]] && command -v curl >/dev/null 2>&1; then -# SERVER_PUBLIC_IPV4="$(curl -fsS --max-time 2 https://ifconfig.me 2>/dev/null || true)" -# [[ "$SERVER_PUBLIC_IPV4" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || SERVER_PUBLIC_IPV4="" -#fi -#[[ -n "$SERVER_PUBLIC_IPV4" ]] || SERVER_PUBLIC_IPV4="$(detect_ip)" -# -#UI_CERT="/etc/ssl/ui/fullchain.pem" -#UI_KEY="/etc/ssl/ui/privkey.pem" -# -#if [[ -n "${UI_HOST:-}" ]]; then -# APP_HOST_VAL="$UI_HOST" -# APP_URL_VAL="https://${UI_HOST}" -#else -# APP_HOST_VAL="$SERVER_PUBLIC_IPV4" -# SCHEME="http" -# [[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SCHEME="https" -# APP_URL_VAL="${SCHEME}://${SERVER_PUBLIC_IPV4}" -#fi - -SERVER_PUBLIC_IPV4="${SERVER_PUBLIC_IPV4:-}" -if [[ -z "$SERVER_PUBLIC_IPV4" ]] && command -v curl >/dev/null 2>&1; then - SERVER_PUBLIC_IPV4="$(curl -fsS --max-time 2 https://ifconfig.me 2>/dev/null || true)" - [[ "$SERVER_PUBLIC_IPV4" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || SERVER_PUBLIC_IPV4="" -fi -[[ -n "$SERVER_PUBLIC_IPV4" ]] || SERVER_PUBLIC_IPV4="$(detect_ip)" - -UI_CERT="/etc/ssl/ui/fullchain.pem" -UI_KEY="/etc/ssl/ui/privkey.pem" - -# DEV-Modus: immer IP als Host, http (kein example.com / keine Fake-Domain) -if [[ "${DEV_MODE:-0}" = "1" || "${APP_ENV:-production}" = "local" ]]; then - APP_HOST_VAL="$SERVER_PUBLIC_IPV4" - APP_URL_VAL="http://${APP_HOST_VAL}" -else - # PROD/normal: wenn UI_HOST gesetzt → benutzen, sonst IP - if [[ -n "${UI_HOST:-}" ]]; then - APP_HOST_VAL="$UI_HOST" - APP_URL_VAL="https://${UI_HOST}" - else - APP_HOST_VAL="$SERVER_PUBLIC_IPV4" - SCHEME="http" - [[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SCHEME="https" - APP_URL_VAL="${SCHEME}://${APP_HOST_VAL}" - fi -fi - -SECURE=$([[ "${APP_ENV}" = "production" ]] && echo true || echo false) - -# --- .env schreiben --------------------------------------------------------- -upsert_env APP_URL "${APP_URL_VAL}" - -if [[ "${PROXY_MODE:-0}" -eq 1 ]]; then - TP_LIST="127.0.0.1,::1" - [[ -n "${NPM_IP:-}" ]] && TP_LIST="${TP_LIST},${NPM_IP}" - upsert_env TRUSTED_PROXIES "$TP_LIST" - upsert_env TRUSTED_HEADERS "x-forwarded-all" -else - upsert_env TRUSTED_PROXIES "" - upsert_env TRUSTED_HEADERS "x-forwarded-all" -fi - -upsert_env APP_HOST "${APP_HOST_VAL}" -upsert_env APP_NAME "${APP_NAME}" -upsert_env APP_ENV "${APP_ENV:-production}" -upsert_env APP_DEBUG "${APP_DEBUG:-false}" -upsert_env APP_TIMEZONE "${APP_TZ:-UTC}" - -upsert_env APP_LOCALE "${APP_LOCALE:-de}" -upsert_env APP_FALLBACK_LOCALE "en" - -upsert_env SERVER_PUBLIC_IPV4 "${SERVER_PUBLIC_IPV4}" -upsert_env SERVER_PUBLIC_IPV6 "${SERVER_PUBLIC_IPV6:-}" - -upsert_env SYSMAIL_SUB "${SYSMAIL_SUB}" -upsert_env SYSMAIL_DOMAIN "${SYSMAIL_DOMAIN}" -upsert_env DKIM_ENABLE "${DKIM_ENABLE}" -upsert_env DKIM_SELECTOR "${DKIM_SELECTOR}" -upsert_env DKIM_GENERATE "${DKIM_GENERATE}" - -upsert_env BASE_DOMAIN "${BASE_DOMAIN}" -upsert_env UI_SUB "${UI_SUB}" -upsert_env WEBMAIL_SUB "${WEBMAIL_SUB}" -upsert_env MTA_SUB "${MTA_SUB}" -upsert_env LE_EMAIL "${LE_EMAIL:-admin@${BASE_DOMAIN}}" - -upsert_env DB_CONNECTION "mysql" -upsert_env DB_HOST "127.0.0.1" -upsert_env DB_PORT "3306" -upsert_env DB_DATABASE "${DB_NAME}" -upsert_env DB_USERNAME "${DB_USER}" -upsert_env DB_PASSWORD "${DB_PASS}" - -upsert_env CACHE_SETTINGS_STORE "redis" -upsert_env CACHE_STORE "redis" -upsert_env CACHE_DRIVER "redis" -upsert_env CACHE_PREFIX "${APP_USER_PREFIX}_cache:" -upsert_env SESSION_DRIVER "redis" -upsert_env SESSION_SECURE_COOKIE "false" # wird nach SSL-Setup auf true gesetzt -upsert_env SESSION_SAMESITE "lax" -upsert_env REDIS_CLIENT "phpredis" -upsert_env REDIS_HOST "127.0.0.1" -upsert_env REDIS_PORT "6379" -upsert_env REDIS_PASSWORD "${REDIS_PASS}" -upsert_env REDIS_DB "0" -upsert_env REDIS_CACHE_DB "1" -upsert_env REDIS_CACHE_CONNECTION "cache" -upsert_env REDIS_CACHE_LOCK_CONNECTION "default" - -upsert_env BROADCAST_DRIVER "reverb" -upsert_env QUEUE_CONNECTION "redis" -upsert_env LOG_CHANNEL "daily" - -upsert_env REVERB_APP_ID "${APP_USER_PREFIX}" -grep -q '^REVERB_APP_KEY=' "$ENV_FILE" || upsert_env REVERB_APP_KEY "${APP_USER_PREFIX}_$(openssl rand -hex 16)" -grep -q '^REVERB_APP_SECRET=' "$ENV_FILE" || upsert_env REVERB_APP_SECRET "${APP_USER_PREFIX}_$(openssl rand -hex 32)" -upsert_env REVERB_HOST "\${APP_HOST}" -upsert_env REVERB_PORT "443" -upsert_env REVERB_SCHEME "https" -upsert_env REVERB_PATH "/ws" -upsert_env REVERB_SCALING_ENABLED "true" -upsert_env REVERB_SCALING_CHANNEL "reverb" - -upsert_env VITE_REVERB_APP_KEY "\${REVERB_APP_KEY}" -upsert_env VITE_REVERB_HOST "\${REVERB_HOST}" -upsert_env VITE_REVERB_PORT "\${REVERB_PORT}" -upsert_env VITE_REVERB_SCHEME "\${REVERB_SCHEME}" -upsert_env VITE_REVERB_PATH "\${REVERB_PATH}" - -upsert_env REVERB_SERVER_APP_KEY "\${REVERB_APP_KEY}" -upsert_env REVERB_SERVER_HOST "127.0.0.1" -upsert_env REVERB_SERVER_PORT "8080" -upsert_env REVERB_SERVER_PATH "" -upsert_env REVERB_SERVER_SCHEME "http" - -# --- DEV Block (optional) --------------------------------------------------- -DEV_MODE="${DEV_MODE:-0}" -if [[ "$DEV_MODE" = "1" ]]; then - sed -i '/^# --- MailWolt DEV/,/^# --- \/MailWolt DEV/d' "${ENV_FILE}" - cat >> "${ENV_FILE}" <make(Illuminate\\Contracts\\Console\\Kernel::class)->bootstrap(); - \$d = App\\Models\\Domain::firstOrCreate([\"domain\"=>\"${SYSMAIL_DOMAIN}\"],[\"is_active\"=>1,\"is_system\"=>1]); - \$r = app(App\\Services\\DkimService::class)->generateForDomain(\$d, 2048, \"${DKIM_SELECTOR}\"); - echo \$r[\"priv_path\"], \"\\n\"; - echo \$r[\"dns_txt\"], \"\\n\"; - ' - ")" - - PRIV_PATH="$(printf '%s\n' "$OUT" | sed -n '1p')" - DNS_TXT="$(printf '%s\n' "$OUT" | sed -n '2,$p')" - - if [[ -z "$PRIV_PATH" || ! -s "$PRIV_PATH" ]]; then - echo "[!] DKIM priv_path fehlt oder Datei leer: $PRIV_PATH" >&2 - exit 1 - fi - - TMP_TXT="$(mktemp /tmp/dkim_txt_XXXXXX.txt)" - printf '%s' "$DNS_TXT" >"$TMP_TXT" - - # 2) Root-Helper ausführen (hängt Key ein, pflegt Key/SigningTable, kopiert TXT) - if [[ -x /usr/local/sbin/mailwolt-install-dkim ]]; then - /usr/local/sbin/mailwolt-install-dkim "${SYSMAIL_DOMAIN}" "${DKIM_SELECTOR}" "${PRIV_PATH}" "${TMP_TXT}" - else - echo "[!] Helper /usr/local/sbin/mailwolt-install-dkim fehlt oder ist nicht ausführbar." >&2 - fi - - rm -f "$TMP_TXT" || true - - # 3) OpenDKIM neu laden - touch /run/mailwolt.need-opendkim-reload || true -else - log "DKIM übersprungen (DKIM_ENABLE=${DKIM_ENABLE}, SYSMAIL_DOMAIN='${SYSMAIL_DOMAIN}')." -fi - - -# --- TLSA aus App heraus (idempotent; läuft, wenn Zert lesbar ist) ---------- -sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan dns:tlsa:refresh || true" - -# --- Build Frontend (nur wenn nötig) ---------------------------------------- -if [[ -f "${APP_DIR}/package.json" && ! -f "${APP_DIR}/public/build/manifest.json" ]]; then - safe_frontend_build -fi - -# --- Abschluss: Caches + Rechte + Reloads ----------------------------------- -sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan optimize:clear && php artisan config:cache && php artisan optimize:clear" - -# Konsistente Rechte/ACL für das gesamte App-Verzeichnis -chown -R "$APP_USER":"$APP_GROUP" "$APP_DIR" -find "$APP_DIR" -type d -exec chmod 2775 {} \; -find "$APP_DIR" -type f -exec chmod 664 {} \; -setfacl -R -m g:"$APP_GROUP":rwX -m d:g:"$APP_GROUP":rwX "$APP_DIR" || true - -# Laravel-Write-Dirs sicherstellen (mit setgid & ACL) -install -d -m 2775 -o "$APP_USER" -g "$APP_GROUP" "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" -chgrp -R www-data "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" || true -find "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" -type d -exec chmod 2775 {} \; || true -find "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" -type f -exec chmod 0664 {} \; || true -setfacl -R -m u:www-data:rwx,u:${APP_USER}:rwx "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" || true -setfacl -dR -m u:www-data:rwx,u:${APP_USER}:rwx "$APP_DIR/storage" "$APP_DIR/bootstrap/cache" || true \ No newline at end of file diff --git a/mailwolt-installer/scripts/88-update-wrapper.sh b/mailwolt-installer/scripts/88-update-wrapper.sh deleted file mode 100644 index 6852861..0000000 --- a/mailwolt-installer/scripts/88-update-wrapper.sh +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Update-Wrapper & Sudoers …" - -WRAPPER="/usr/local/sbin/mailwolt-update" -LOGFILE="/var/log/mailwolt-update.log" -STATEDIR="/var/lib/mailwolt/update" -SUDOERS="/etc/sudoers.d/mailwolt-update" -VERSION_FILE="/var/lib/mailwolt/version" -SUDOERS_SERVICES="/etc/sudoers.d/mailwolt-services" -SUDOERS_ARTISAN="/etc/sudoers.d/mailwolt-artisan" - -# Kandidaten: wo liegt update.sh? -CANDIDATES=( - /opt/mailwolt-installer/scripts/update.sh - /mailwolt-installer/scripts/update.sh - /usr/local/lib/mailwolt/update.sh -) - -# State/Log vorbereiten -install -d -m 0755 "$(dirname "$LOGFILE")" -install -d -m 0755 "$STATEDIR" -: > "$LOGFILE" || true -chmod 0644 "$LOGFILE" - -# Wrapper erzeugen -cat > "$WRAPPER" <<'EOF' -#!/usr/bin/env bash -set -euo pipefail - -LOG="/var/log/mailwolt-update.log" -STATE_DIR="/var/lib/mailwolt/update" -APP_DIR="/var/www/mailwolt" -WEB_USER="www-data" - -CANDIDATES=( - /opt/mailwolt-installer/scripts/update.sh - /mailwolt-installer/scripts/update.sh - /usr/local/lib/mailwolt/update.sh -) - -install -d -m 0755 "$(dirname "$LOG")" "$STATE_DIR" /var/lib/mailwolt -: > "$LOG" || true -chmod 0644 "$LOG" - -echo "running" > "$STATE_DIR/state" - -{ - echo "===== $(date -Is) :: Update gestartet =====" - - # --- Update-Script finden -------------------------------------------------- - SCRIPT="" - for p in "${CANDIDATES[@]}"; do - if [[ -x "$p" ]]; then SCRIPT="$p"; break; fi - if [[ -f "$p" && -r "$p" ]]; then SCRIPT="$p"; break; fi - done - - if [[ -z "$SCRIPT" ]]; then - echo "[!] update.sh nicht gefunden (versucht: ${CANDIDATES[*]})" - rc=127 - else - echo "[i] benutze: $SCRIPT" - if [[ "$(id -u)" -ne 0 ]]; then - echo "[!] Bitte als root ausführen" - rc=1 - else - if [[ -x "$SCRIPT" ]]; then - ALLOW_DIRTY=1 "$SCRIPT" - else - ALLOW_DIRTY=1 bash "$SCRIPT" - fi - rc=$? - fi - fi - - echo "===== $(date -Is) :: Update-Script beendet (rc=$rc) =====" - - # --- Nach dem Update: Assets neu bauen & Laravel optimieren --------------- - if [ -d "$APP_DIR" ]; then - cd "$APP_DIR" || exit 1 - - echo "[i] Führe Composer aus (falls vorhanden) ..." - if [ -f composer.json ]; then - sudo -u "$WEB_USER" composer install --no-dev --prefer-dist --no-interaction -q || true - fi - - echo "[i] Baue Frontend-Assets neu ..." - if command -v npm >/dev/null 2>&1 && [ -f package.json ]; then - sudo -u "$WEB_USER" npm ci --silent || true - sudo -u "$WEB_USER" npm run build --silent || true - fi - - echo "[i] Führe Migrationen & Cache-Optimierungen durch ..." - sudo -u "$WEB_USER" php artisan migrate --force || true - sudo -u "$WEB_USER" php artisan config:cache || true - sudo -u "$WEB_USER" php artisan optimize:clear || true - sudo -u "$WEB_USER" php artisan route:cache || true - sudo -u "$WEB_USER" php artisan view:cache || true - - echo "[i] Hebe Wartungsmodus auf ..." - sudo -u "$WEB_USER" php artisan up >/dev/null 2>&1 || true - fi - - # --- Version aktualisieren ------------------------------------------------- - echo "[i] Aktualisiere Version ..." - if command -v git >/dev/null 2>&1; then - SRC="/var/www/mailwolt" - if [ ! -d "$SRC/.git" ]; then - SRC="/opt/mailwolt-installer" - fi - - git config --global --add safe.directory "$SRC" || true - - if [ -f "$SRC/.git/shallow" ]; then - git -C "$SRC" fetch --unshallow --quiet || true - fi - git -C "$SRC" fetch --tags --quiet origin || true - - raw="$(git -C "$SRC" describe --tags --always --dirty 2>/dev/null || echo "unknown")" - norm="$(printf '%s' "$raw" | sed -E 's/^[vV]//; s/-.*$//')" - - printf '%s\n' "$raw" > /var/lib/mailwolt/version_raw - printf '%s\n' "$norm" > /var/lib/mailwolt/version - chmod 0644 /var/lib/mailwolt/version_raw /var/lib/mailwolt/version - - echo "[i] Version aktualisiert: raw=$raw norm=$norm (Quelle: $SRC)" - else - echo "unknown" > /var/lib/mailwolt/version_raw - echo "0.0.0" > /var/lib/mailwolt/version - chmod 0644 /var/lib/mailwolt/version_raw /var/lib/mailwolt/version - fi - - # --- Services neu starten -------------------------------------------------- - echo "[i] Starte MailWolt-Dienste neu ..." - sudo -u "$WEB_USER" php artisan mailwolt:restart-services || true - - # --- Abschluss ------------------------------------------------------------- - printf '%s\n' "$rc" > "$STATE_DIR/rc" - echo "done" > "$STATE_DIR/state" - echo "===== $(date -Is) :: Update beendet =====" - exit "$rc" - -} | tee -a "$LOG" -EOF - -chmod 0755 "$WRAPPER" -chown root:root "$WRAPPER" - -# Sudoers: www-data (Laravel) & mailwolt dürfen den Wrapper laufen lassen -cat > "$SUDOERS" <<'EOF' -Defaults!/usr/local/sbin/mailwolt-update !requiretty -www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-update -mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-update -EOF - -chown root:root "$SUDOERS" -chmod 440 "$SUDOERS" - -if ! visudo -c -f "$SUDOERS" >/dev/null 2>&1; then - echo "[!] Ungültiger sudoers-Eintrag in $SUDOERS – entferne Datei." - rm -f "$SUDOERS" -fi - -cat > "$SUDOERS_SERVICES" <<'EOF' -Defaults!/usr/bin/systemctl !requiretty - -Cmnd_Alias MW_SERVICES = \ - /usr/bin/systemctl reload nginx.service, \ - /usr/bin/systemctl try-reload-or-restart nginx.service, \ - /usr/bin/systemctl try-reload-or-restart postfix.service, \ - /usr/bin/systemctl try-reload-or-restart dovecot.service, \ - /usr/bin/systemctl try-reload-or-restart rspamd.service, \ - /usr/bin/systemctl try-reload-or-restart opendkim.service, \ - /usr/bin/systemctl try-reload-or-restart opendmarc.service, \ - /usr/bin/systemctl try-reload-or-restart clamav-daemon.service, \ - /usr/bin/systemctl try-reload-or-restart redis-server.service - -www-data ALL=(root) NOPASSWD: MW_SERVICES -EOF - -chmod 440 "$SUDOERS_SERVICES" -chown root:root "$SUDOERS_SERVICES" - -# Prüfen, ob Syntax gültig ist -if ! visudo -c -f "$SUDOERS_SERVICES" >/dev/null 2>&1; then - echo "[!] Ungültiger sudoers-Eintrag in $SUDOERS_SERVICES – entferne Datei." - rm -f "$SUDOERS_SERVICES" -else - echo "[✓] Sudoers für Dienststeuerung angelegt: $SUDOERS_SERVICES" -fi - -# Version-File initial anlegen, falls nicht existiert -if [[ ! -f "$VERSION_FILE" ]]; then - echo "unknown" > "$VERSION_FILE" - chmod 0644 "$VERSION_FILE" -fi - -cat > "$SUDOERS_ARTISAN" <<'EOF' -# mailwolt darf artisan im App-Verzeichnis als www-data ausführen (ohne Passwort) -mailwolt ALL=(www-data) NOPASSWD: /usr/bin/php /var/www/mailwolt/artisan * -EOF - -chown root:root "$SUDOERS_ARTISAN" -chmod 440 "$SUDOERS_ARTISAN" - -if ! visudo -c -f "$SUDOERS_ARTISAN" >/dev/null 2>&1; then - echo "[!] Ungültiger sudoers-Eintrag in $SUDOERS_ARTISAN – entferne Datei." - rm -f "$SUDOERS_ARTISAN" -else - echo "[✓] Sudoers für Artisan-Kommandos angelegt: $SUDOERS_ARTISAN" -fi - -log "[✓] Update-Wrapper bereit: $WRAPPER" -log "[✓] Version wird unter $VERSION_FILE gespeichert" - -# ─── Installer-Wrapper ──────────────────────────────────────────────────────── -INSTALL_WRAPPER="/usr/local/sbin/mailwolt-install" -INSTALL_SUDOERS="/etc/sudoers.d/mailwolt-install" -INSTALL_STATE_DIR="/var/lib/mailwolt/install" -INSTALL_LOG="/var/log/mailwolt-install.log" - -# State/Log vorbereiten -install -d -m 0755 "$INSTALL_STATE_DIR" -: > "$INSTALL_LOG" || true -chmod 0644 "$INSTALL_LOG" - -# Installer-Wrapper aus scripts/install-wrapper.sh kopieren -INSTALL_SRC="" -for candidate in \ - "$(dirname "$0")/install-wrapper.sh" \ - /opt/mailwolt-installer/scripts/install-wrapper.sh \ - /var/www/mailwolt/mailwolt-installer/scripts/install-wrapper.sh; do - [[ -f "$candidate" ]] && INSTALL_SRC="$candidate" && break -done - -if [[ -n "$INSTALL_SRC" ]]; then - cp "$INSTALL_SRC" "$INSTALL_WRAPPER" - chmod 0755 "$INSTALL_WRAPPER" - chown root:root "$INSTALL_WRAPPER" - echo "[✓] Installer-Wrapper angelegt: $INSTALL_WRAPPER" -else - echo "[!] install-wrapper.sh nicht gefunden – Installer-Wrapper wird übersprungen." -fi - -# Sudoers: www-data & mailwolt dürfen den Installer-Wrapper laufen lassen -cat > "$INSTALL_SUDOERS" <<'SUDOEOF' -Defaults!/usr/local/sbin/mailwolt-install !requiretty -www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install -www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install * -mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install -mailwolt ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-install * -SUDOEOF - -chown root:root "$INSTALL_SUDOERS" -chmod 440 "$INSTALL_SUDOERS" - -if ! visudo -c -f "$INSTALL_SUDOERS" >/dev/null 2>&1; then - echo "[!] Ungültiger sudoers-Eintrag in $INSTALL_SUDOERS – entferne Datei." - rm -f "$INSTALL_SUDOERS" -else - echo "[✓] Sudoers für Installer angelegt: $INSTALL_SUDOERS" -fi \ No newline at end of file diff --git a/mailwolt-installer/scripts/90-services.sh b/mailwolt-installer/scripts/90-services.sh deleted file mode 100644 index c28b982..0000000 --- a/mailwolt-installer/scripts/90-services.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "systemd Units (Reverb / Scheduler / Queue / Mail) …" - -cat > /etc/systemd/system/${APP_USER}-ws.service < /etc/systemd/system/${APP_USER}-schedule.service < /etc/systemd/system/${APP_USER}-queue.service </dev/null 2>&1 || true -fi -systemctl enable --now ${APP_USER}-schedule -systemctl enable --now ${APP_USER}-queue - -# Mail-Dienste starten -systemctl enable --now rspamd opendkim postfix dovecot || true - -# PHP-FPM: Unit erkennen, enable + (re)load -enable_and_touch_php_fpm() { - for u in php8.3-fpm php8.2-fpm php8.1-fpm php8.0-fpm php7.4-fpm php-fpm; do - if systemctl list-unit-files | grep -q "^${u}\.service"; then - systemctl enable --now "$u" || true - systemctl reload "$u" || systemctl restart "$u" || true - echo "[i] PHP-FPM unit: $u" - return 0 - fi - done - echo "[!] Keine passende php-fpm Unit gefunden." -} -enable_and_touch_php_fpm - -# Falls in 80-app.sh DKIM installiert wurde: jetzt einmal reloaden -if [[ -e /run/mailwolt.need-opendkim-reload ]]; then - systemctl reload opendkim || true - rm -f /run/mailwolt.need-opendkim-reload || true -fi - -# Falls Zert-Fix markiert ist: Dovecot neu laden -if [[ -e /run/mailwolt.need-dovecot-reload ]]; then - systemctl reload dovecot || true - rm -f /run/mailwolt.need-dovecot-reload || true -fi - -# Falls DB-Migration schon durch: einmal reload -db_ready(){ mysql -u"${DB_USER}" -p"${DB_PASS}" -h 127.0.0.1 -D "${DB_NAME}" -e "SHOW TABLES LIKE 'migrations'\G" >/dev/null 2>&1; } -if db_ready; then - systemctl reload postfix || true -fi - -# Mini-Portcheck (hilft beim Installer-Output) -echo "Listening (25/465/587):" -ss -ltnp | awk '$4 ~ /:(25|465|587)$/ {print " " $0}' \ No newline at end of file diff --git a/mailwolt-installer/scripts/92-sudoers-npm.sh b/mailwolt-installer/scripts/92-sudoers-npm.sh deleted file mode 100644 index 6f07a93..0000000 --- a/mailwolt-installer/scripts/92-sudoers-npm.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Sudoers: npm-Build ohne Passwort für user 'mailwolt' …" - -# 1) npm-Binary ermitteln (normal: /usr/bin/npm) -NPM_BIN="$(command -v npm || true)" - -if [[ -z "$NPM_BIN" ]]; then - warn "npm wurde nicht gefunden – sudoers wird vorbereitet, aber ohne Validierung. Stelle sicher, dass Node/npm installiert ist." - # Fallback – die meisten Distros legen hier an - NPM_BIN="/usr/bin/npm" -fi - -SUDOERS_FILE="/etc/sudoers.d/mailwolt-npm" - -# 2) Sudoers-Datei schreiben -cat > "$SUDOERS_FILE" </dev/null 2>&1; then - log "[✓] sudoers OK: ${SUDOERS_FILE} erlaubt 'mailwolt' → ${NPM_BIN} ohne Passwort." -else - echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_FILE} – entferne Datei." - rm -f "$SUDOERS_FILE" -fi \ No newline at end of file diff --git a/mailwolt-installer/scripts/93-backup-tools.sh b/mailwolt-installer/scripts/93-backup-tools.sh deleted file mode 100644 index ebfd7c4..0000000 --- a/mailwolt-installer/scripts/93-backup-tools.sh +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Backup/Restore – Tools, Config & Timer (installer.env) …" - -# ───────────────────────────────────────────────────────────── -# 1) installer.env laden (ENV > installer.env > Defaults) -# ───────────────────────────────────────────────────────────── -if [[ -f /etc/mailwolt/installer.env ]]; then - # automatisch exportieren, damit ${VAR} später überall wirkt - set -a - # shellcheck disable=SC1091 - source /etc/mailwolt/installer.env - set +a -else - log "[i] /etc/mailwolt/installer.env nicht gefunden – nutze Defaults." -fi - -# ───────────────────────────────────────────────────────────── -# 2) Pfade & Defaults (werden durch ENV/installer.env überschrieben) -# ───────────────────────────────────────────────────────────── -CONF_DIR="/etc/mailwolt" -CONF_FILE="${CONF_DIR}/backup.conf" -BIN_DIR="/usr/local/sbin" -UNIT_DIR="/etc/systemd/system" - -APP_DIR="${APP_DIR:-/var/www/mailwolt}" - -# DB-Parameter aus installer.env (bzw. ENV) oder Fallbacks -DB_HOST="${DB_HOST:-127.0.0.1}" -DB_NAME="${DB_NAME:-mailwolt}" -DB_USER="${DB_USER:-mailwolt}" -DB_PASS="${DB_PASS:-}" - -# Backup-Settings aus installer.env (bzw. ENV) -BACKUP_DIR="${BACKUP_DIR:-/var/backups/mailwolt}" -BACKUP_RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}" -BACKUP_USE_ZSTD="${BACKUP_USE_ZSTD:-1}" -BACKUP_ENABLED="${BACKUP_ENABLED:-0}" # 0|1 -BACKUP_INTERVAL="${BACKUP_INTERVAL:-daily}" # daily|weekly|monthly - -install -d -m 0755 "$CONF_DIR" "$BACKUP_DIR" - - -SUDOERS_BACKUP_FILE="/etc/sudoers.d/mailwolt-backup" -# 2) Sudoers-Datei schreiben -cat > "${SUDOERS_BACKUP_FILE} " </dev/null 2>&1; then - echo "[!] Ungültiger sudoers-Eintrag in ${SUDOERS_BACKUP_FILE} – entferne Datei." - rm -f "${SUDOERS_BACKUP_FILE}" -fi - -# ───────────────────────────────────────────────────────────── -# 3) /etc/mailwolt/backup.conf (von UI/APP überschreibbar) -# ───────────────────────────────────────────────────────────── -cat > "$CONF_FILE" < "${BIN_DIR}/mailwolt-backup" <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail - -log(){ echo "[$(date -Is)] $*"; } - -# Konfiguration laden (ENV > Datei) -CONF="/etc/mailwolt/backup.conf" -[[ -f "$CONF" ]] && # shellcheck disable=SC1090 -source "$CONF" - -APP_DIR="${APP_DIR:-/var/www/mailwolt}" -BACKUP_DIR="${BACKUP_DIR:-/var/backups/mailwolt}" -RETENTION_DAYS="${RETENTION_DAYS:-7}" -USE_ZSTD="${USE_ZSTD:-1}" - -MYSQL_DB="${MYSQL_DB:-mailwolt}" -MYSQL_USER="${MYSQL_USER:-mailwolt}" -MYSQL_PASS="${MYSQL_PASS:-}" -MYSQL_HOST="${MYSQL_HOST:-127.0.0.1}" -MYSQL_PORT="${MYSQL_PORT:-3306}" - -STATE_DIR="/var/lib/mailwolt" -STATUS_FILE="${STATE_DIR}/backup.status" -install -d -m 0755 "$STATE_DIR" "$BACKUP_DIR" - -START_TS="$(date +%s)" -TS="$(date -u +%Y%m%dT%H%M%SZ)" -TMP="$(mktemp -d /tmp/mwbackup.XXXXXX)" -trap 'rm -rf "$TMP"' EXIT - -fail(){ - local msg="${1:-backup failed}" - local now="$(date -Is)" - { - echo "time=${now}" - echo "size=0" - echo "dur=$(( $(date +%s) - START_TS ))s" - echo "ok=0" - echo "error=${msg}" - } > "$STATUS_FILE" - echo "[$now] ${msg}" >&2 - exit 1 -} -trap 'fail "unexpected error (exit $?)"' ERR - -OUT="${BACKUP_DIR}/mailwolt-${TS}.tar" -log "⇒ starte Backup in $OUT …" - -# 1) DB -log " • mysqldump …" -MYSQL_PWD="$MYSQL_PASS" mysqldump \ - -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" \ - --single-transaction --routines --events --triggers \ - "$MYSQL_DB" > "$TMP/mysql.sql" - -# 2) Maildir -log " • Maildir …" -tar -C / -cf "$TMP/mail.tar" var/mail/vhosts 2>/dev/null || true - -# 3) App (ohne heavy dirs) -log " • App …" -tar -C / -cf "$TMP/app.tar" \ - --exclude='var/www/mailwolt/vendor' \ - --exclude='var/www/mailwolt/node_modules' \ - --exclude='var/www/mailwolt/public/build' \ - var/www/mailwolt - -# 4) Configs -log " • Configs …" -mkdir -p "$TMP/files" -cp -a /etc/mailwolt "$TMP/files/" 2>/dev/null || true -cp -a /etc/postfix "$TMP/files/" 2>/dev/null || true -cp -a /etc/dovecot "$TMP/files/" 2>/dev/null || true -cp -a /etc/opendkim "$TMP/files/" 2>/dev/null || true -cp -a /etc/opendmarc "$TMP/files/" 2>/dev/null || true -cp -a /etc/rspamd "$TMP/files/" 2>/dev/null || true -cp -a /etc/ssl/ui "$TMP/files/" 2>/dev/null || true -tar -C "$TMP" -cf "$TMP/files.tar" files - -# 5) Paket -log " • Archiviere …" -tar -C "$TMP" -cf "$OUT" mysql.sql mail.tar app.tar files.tar - -# 6) Komprimieren (optional) -if [[ "${USE_ZSTD:-1}" = "1" ]] && command -v zstd >/dev/null 2>&1; then - log " • komprimiere (zstd) …" - zstd -f --rm -19 "$OUT" - OUT="${OUT}.zst" -fi - -# 7) Retention -if [[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]]; then - log " • Retention: lösche älter als ${RETENTION_DAYS} Tage …" - find "$BACKUP_DIR" -type f -mtime +"$RETENTION_DAYS" -name 'mailwolt-*' -delete || true -fi - -# 8) Statusfile fürs UI -SIZE_BYTES="$(stat -c '%s' "$OUT" 2>/dev/null || echo 0)" -{ - echo "time=$(date -Is)" - echo "size=${SIZE_BYTES}" - echo "dur=$(( $(date +%s) - START_TS ))s" - echo "ok=1" - echo "file=${OUT}" -} > "$STATUS_FILE" -chmod 0644 "$STATUS_FILE" 2>/dev/null || true - -log "[✓] Backup fertig: $OUT" -EOSH -chmod 0755 "${BIN_DIR}/mailwolt-backup" - -# ───────────────────────────────────────────────────────────── -# 5) /usr/local/sbin/mailwolt-restore -# ───────────────────────────────────────────────────────────── -cat > "${BIN_DIR}/mailwolt-restore" <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail -log(){ echo "[$(date -Is)] $*"; } - -ARCHIVE="${1:-}" -[[ -n "$ARCHIVE" ]] || { echo "Usage: mailwolt-restore "; exit 1; } -[[ -f "$ARCHIVE" ]] || { echo "Backup nicht gefunden: $ARCHIVE"; exit 1; } - -TMP="$(mktemp -d /tmp/mwrestore.XXXXXX)" -trap 'rm -rf "$TMP"' EXIT - -case "$ARCHIVE" in - *.zst) zstd -d -c "$ARCHIVE" > "$TMP/backup.tar" ;; - *) cp -a "$ARCHIVE" "$TMP/backup.tar" ;; -esac - -log "⇒ entpacke …" -tar -C "$TMP" -xf "$TMP/backup.tar" - -# Reihenfolge: DB → App → Mail → Config -if [[ -f "$TMP/mysql.sql" ]]; then - log " • MySQL wiederherstellen …" - mysql < "$TMP/mysql.sql" -fi - -if [[ -f "$TMP/app.tar" ]]; then - log " • App → /var/www/mailwolt …" - tar -C / -xf "$TMP/app.tar" -fi - -if [[ -f "$TMP/mail.tar" ]]; then - log " • Maildir → /var/mail/vhosts …" - tar -C / -xf "$TMP/mail.tar" -fi - -if [[ -f "$TMP/files.tar" ]]; then - log " • Configs → /etc/* …" - tar -C / -xf "$TMP/files.tar" -fi - -log "[✓] Restore abgeschlossen." -EOSH -chmod 0755 "${BIN_DIR}/mailwolt-restore" - -log "[✓] Tools installiert: ${BIN_DIR}/mailwolt-backup, mailwolt-restore" - -# ───────────────────────────────────────────────────────────── -# 6) systemd Service + Timer (Timer default via installer.env) -# ───────────────────────────────────────────────────────────── -cat > "${UNIT_DIR}/mailwolt-backup.service" <<'EOSVC' -[Unit] -Description=MailWolt Backup - -[Service] -Type=oneshot -ExecStart=/usr/local/sbin/mailwolt-backup -Nice=10 -IOSchedulingClass=best-effort -IOSchedulingPriority=7 -EOSVC - -cat > "${UNIT_DIR}/mailwolt-backup.timer" </dev/null 2>&1 || true -fi - -log "[✓] Backup-Setup abgeschlossen." \ No newline at end of file diff --git a/mailwolt-installer/scripts/95-monit.sh b/mailwolt-installer/scripts/95-monit.sh deleted file mode 100644 index 6dca86e..0000000 --- a/mailwolt-installer/scripts/95-monit.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "Monit konfigurieren …" -cat > /etc/monit/monitrc <<'EOF' -set daemon 60 -set logfile syslog facility log_daemon - -check process postfix with pidfile /var/spool/postfix/pid/master.pid - start program = "/bin/systemctl start postfix" - stop program = "/bin/systemctl stop postfix" - if failed port 25 protocol smtp then restart - if failed port 465 type tcp ssl then restart - if failed port 587 type tcp then restart - -check process dovecot with pidfile /run/dovecot/master.pid - start program = "/bin/systemctl start dovecot" - stop program = "/bin/systemctl stop dovecot" - if failed port 143 type tcp then restart - if failed port 993 type tcp ssl then restart - -check process mariadb with pidfile /run/mysqld/mysqld.pid - start program = "/bin/systemctl start mariadb" - stop program = "/bin/systemctl stop mariadb" - if failed port 3306 type tcp then restart - -check process redis-server with pidfile /run/redis/redis-server.pid - start program = "/bin/systemctl start redis-server" - stop program = "/bin/systemctl stop redis-server" - if failed port 6379 type tcp then restart - -check process nginx with pidfile /run/nginx.pid - start program = "/bin/systemctl start nginx" - stop program = "/bin/systemctl stop nginx" - if failed port 80 type tcp then restart - if failed port 443 type tcp ssl then restart -EOF -chmod 600 /etc/monit/monitrc -monit -t && systemctl enable --now monit -monit reload || true - -log "[✓] Monit konfiguriert und gestartet" - -# ── mailwolt-update ins System kopieren ───────────────────────────── -install -m 0750 -o root -g root scripts/update.sh /usr/local/sbin/mailwolt-update -log "[✓] mailwolt-update installiert → ausführbar via 'sudo mailwolt-update'" \ No newline at end of file diff --git a/mailwolt-installer/scripts/95-woltguard.bak.sh b/mailwolt-installer/scripts/95-woltguard.bak.sh deleted file mode 100644 index d727918..0000000 --- a/mailwolt-installer/scripts/95-woltguard.bak.sh +++ /dev/null @@ -1,491 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -log "WoltGuard (Monit + Self-Heal) einrichten …" - -# ───────────────────────────────────────────────────────────── -# Env nur nachladen, wenn Flags nicht bereits exportiert sind -# ───────────────────────────────────────────────────────────── -INSTALLER_ENV="/etc/mailwolt/installer.env" -: "${CLAMAV_ENABLE:=}" ; : "${OPENDMARC_ENABLE:=}" ; : "${FAIL2BAN_ENABLE:=}" -if [[ -z "${CLAMAV_ENABLE}${OPENDMARC_ENABLE}${FAIL2BAN_ENABLE}" && -r "$INSTALLER_ENV" ]]; then - # shellcheck disable=SC1090 - . "$INSTALLER_ENV" -fi -CLAMAV_ENABLE="${CLAMAV_ENABLE:-0}" -OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-0}" -FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" - -# ───────────────────────────────────────────────────────────── -# Monit installieren & aktivieren -# ───────────────────────────────────────────────────────────── -command -v monit >/dev/null || { apt-get update -qq; apt-get install -y monit; } -systemctl enable --now monit - -# ───────────────────────────────────────────────────────────── -# Helper-Skripte (laufen später eigenständig → Env selbst laden) -# ───────────────────────────────────────────────────────────── -install -d -m 0755 /usr/local/sbin - -# Redis-Ping (nimmt REDIS_PASSWORD aus installer.env oder .env) -cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail - -INSTALLER_ENV="/etc/mailwolt/installer.env" -APP_ENV="/var/www/mailwolt/.env" - -# Defaults -REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -REDIS_PORT="${REDIS_PORT:-6379}" -REDIS_PASSWORD="${REDIS_PASSWORD:-}" -REDIS_PASS="${REDIS_PASS:-}" # Legacy - -# Installer-Env (falls vorhanden) -[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" || true - -# Falls .env existiert: Werte ergänzen, die noch leer sind -if [[ -r "$APP_ENV" ]]; then - [[ -z "${REDIS_HOST}" ]] && REDIS_HOST="$(grep -m1 -E '^REDIS_HOST=' "$APP_ENV" | cut -d= -f2- || true)" - [[ -z "${REDIS_PORT}" ]] && REDIS_PORT="$(grep -m1 -E '^REDIS_PORT=' "$APP_ENV" | cut -d= -f2- || true)" - [[ -z "${REDIS_PASSWORD}" ]] && REDIS_PASSWORD="$(grep -m1 -E '^REDIS_PASSWORD=' "$APP_ENV" | cut -d= -f2- || true)" -fi - -# Legacy-Fallback: wenn PASSWORD leer, aber PASS gesetzt → übernehmen -[[ -z "${REDIS_PASSWORD}" && -n "${REDIS_PASS}" ]] && REDIS_PASSWORD="$REDIS_PASS" - -# Quotes strippen -strip(){ printf '%s' "$1" | sed -E 's/^"(.*)"$/\1/; s/^'\''(.*)'\''$/\1/'; } -REDIS_HOST="$(strip "${REDIS_HOST:-}")" -REDIS_PORT="$(strip "${REDIS_PORT:-}")" -REDIS_PASSWORD="$(strip "${REDIS_PASSWORD:-}")" - -# redis-cli muss vorhanden sein -command -v redis-cli >/dev/null 2>&1 || exit 1 - -BASE=(timeout 2 redis-cli --no-auth-warning --raw -h "$REDIS_HOST" -p "$REDIS_PORT") -if [[ -n "$REDIS_PASSWORD" ]]; then - CMD=("${BASE[@]}" -a "$REDIS_PASSWORD" ping) -else - CMD=("${BASE[@]}" ping) -fi - -# Erfolgreich nur bei exakt "PONG" -[[ "$("${CMD[@]}" 2>/dev/null || true)" == "PONG" ]] -EOSH -chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh - -# Rspamd-Heal (setzt Laufzeitverzeichnis, leert alte Socke, restarts rspamd) -cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail - -INSTALLER_ENV="/etc/mailwolt/installer.env" -APP_ENV="/var/www/mailwolt/.env" - -REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -REDIS_PORT="${REDIS_PORT:-6379}" -REDIS_PASSWORD="${REDIS_PASSWORD:-}" - -[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" -if [[ -z "${REDIS_PASSWORD}" && -r "$APP_ENV" ]]; then - REDIS_PASSWORD="$(grep -E '^REDIS_PASSWORD=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)" -fi - -# Rspamd Runtime fixen -install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true -[[ -S /var/lib/rspamd/rspamd.sock ]] && rm -f /var/lib/rspamd/rspamd.sock || true - -# Neustart -systemctl restart rspamd - -# Mini-Healthcheck -sleep 2 -ss -tln | grep -q ':11334' || echo "[WARN] Rspamd Controller Port 11334 nicht sichtbar" - -exit 0 -EOSH -chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh - -# ───────────────────────────────────────────────────────────── -# WoltGuard Wrapper + Unit -# ───────────────────────────────────────────────────────────── -cat >/usr/local/bin/woltguard <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail -case "${1:-status}" in - start) systemctl enable --now monit ;; - stop) systemctl stop monit ;; - status) monit summary || systemctl status monit || true ;; - heal) monit reload || true; sleep 1; monit restart all || true ;; - monitor) monit monitor all || true ;; - unmonitor) monit unmonitor all || true ;; - *) echo "Usage: woltguard {start|stop|status|heal|monitor|unmonitor}"; exit 2;; -esac -EOSH -chmod 0755 /usr/local/bin/woltguard - -cat >/etc/systemd/system/woltguard.service <<'EOF' -[Unit] -Description=WoltGuard – Self-Healing Monitor for MailWolt -After=network.target -[Service] -Type=oneshot -ExecStart=/usr/local/bin/woltguard start -ExecStop=/usr/local/bin/woltguard stop -RemainAfterExit=yes -[Install] -WantedBy=multi-user.target -EOF -systemctl daemon-reload -systemctl enable --now woltguard - -# ───────────────────────────────────────────────────────────── -# Monit Basis + includes -# ───────────────────────────────────────────────────────────── -sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true -grep -q 'include /etc/monit/conf.d/*' /etc/monit/monitrc || echo 'include /etc/monit/conf.d/*' >>/etc/monit/monitrc -install -d -m 0755 /etc/monit/conf.d - -# ───────────────────────────────────────────────────────────── -# Monit Checks -# ───────────────────────────────────────────────────────────── -# 10 – Redis zuerst (abhängig für rspamd) -cat >/etc/monit/conf.d/10-redis.conf <<'EOF' -check process redis with pidfile /run/redis/redis-server.pid - start program = "/bin/systemctl start redis-server" - stop program = "/bin/systemctl stop redis-server" - if failed host 127.0.0.1 port 6379 for 2 cycles then restart - if 5 restarts within 5 cycles then alert - -check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh" - if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server" -EOF - -# 20 – Rspamd (hängt von Redis ab), robust über process-matching -cat >/etc/monit/conf.d/20-rspamd.conf <<'EOF' -check process rspamd matching "/usr/bin/rspamd" - start program = "/bin/systemctl start rspamd" - stop program = "/bin/systemctl stop rspamd" - depends on redis - if failed host 127.0.0.1 port 11333 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" - if failed host 127.0.0.1 port 11334 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" - if 5 restarts within 5 cycles then alert -EOF - -# 30 – Maildienste -cat >/etc/monit/conf.d/30-postfix.conf <<'EOF' -check process postfix with pidfile /var/spool/postfix/pid/master.pid - start program = "/bin/systemctl start postfix" - stop program = "/bin/systemctl stop postfix" - if failed port 25 protocol smtp then restart - if failed port 465 type tcpssl then restart - if failed port 587 type tcp then restart - if 5 restarts within 5 cycles then alert -EOF - -cat >/etc/monit/conf.d/30-dovecot.conf <<'EOF' -check process dovecot with pidfile /run/dovecot/master.pid - start program = "/bin/systemctl start dovecot" - stop program = "/bin/systemctl stop dovecot" - if failed port 993 type tcpssl for 2 cycles then restart - if failed port 24 protocol lmtp for 2 cycles then restart - if 5 restarts within 5 cycles then alert -EOF - -# 40 – Web/PHP -cat >/etc/monit/conf.d/40-nginx.conf <<'EOF' -check process nginx with pidfile /run/nginx.pid - start program = "/bin/systemctl start nginx" - stop program = "/bin/systemctl stop nginx" - if failed port 80 type tcp then restart - if failed port 443 type tcpssl then restart - if 5 restarts within 5 cycles then alert -EOF - -# 50 – DKIM/DMARC -cat >/etc/monit/conf.d/50-opendkim.conf <<'EOF' -check process opendkim with pidfile /run/opendkim/opendkim.pid - start program = "/bin/systemctl start opendkim" - stop program = "/bin/systemctl stop opendkim" - if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart - if 5 restarts within 5 cycles then alert -EOF - -# optional: OpenDMARC -if [[ "$OPENDMARC_ENABLE" = "1" ]]; then - cat >/etc/monit/conf.d/55-opendmarc.conf <<'EOF' -check process opendmarc with pidfile /run/opendmarc/opendmarc.pid - start program = "/bin/systemctl start opendmarc" - stop program = "/bin/systemctl stop opendmarc" - if 5 restarts within 5 cycles then alert -EOF -else - rm -f /etc/monit/conf.d/55-opendmarc.conf || true -fi - -# 60 – optional: ClamAV -if [[ "$CLAMAV_ENABLE" = "1" ]]; then - cat >/etc/monit/conf.d/60-clamav.conf <<'EOF' -check process clamd with pidfile /run/clamav/clamd.pid - start program = "/bin/systemctl start clamav-daemon" - stop program = "/bin/systemctl stop clamav-daemon" - if failed unixsocket /run/clamav/clamd.ctl for 3 cycles then restart - if 5 restarts within 5 cycles then timeout -EOF -else - rm -f /etc/monit/conf.d/60-clamav.conf || true -fi - -# 70 – Fail2Ban (optional, standardmäßig aktiv) -if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then - cat >/etc/monit/conf.d/70-fail2ban.conf <<'EOF' -check process fail2ban with pidfile /run/fail2ban/fail2ban.pid - start program = "/bin/systemctl start fail2ban" - stop program = "/bin/systemctl stop fail2ban" - if 5 restarts within 5 cycles then alert -EOF -else - rm -f /etc/monit/conf.d/70-fail2ban.conf || true -fi -# ───────────────────────────────────────────────────────────── -# Monit neu laden -# ───────────────────────────────────────────────────────────── -monit -t -systemctl reload monit || systemctl restart monit -systemctl status monit --no-pager || true -log "[✓] WoltGuard aktiv." - -##!/usr/bin/env bash -#set -euo pipefail -#source ./lib.sh -# -#log "WoltGuard (Monit + Self-Heal) einrichten …" -# -#set +u -#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env -#set -u -#CLAMAV_ENABLE="${CLAMAV_ENABLE:-0}" -#OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-0}" -#FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" -# -## Pakete sicherstellen -#command -v monit >/dev/null || { apt-get update -qq; apt-get install -y monit; } -#systemctl enable --now monit -# -## Helper-Skripte -#install -d -m 0755 /usr/local/sbin -#cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH' -##!/usr/bin/env bash -#set -euo pipefail -#PASS="" -#[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env || true -#if command -v redis-cli >/dev/null 2>&1; then -# [[ -n "${REDIS_PASS:-}" ]] \ -# && redis-cli -h 127.0.0.1 -p 6379 -a "$REDIS_PASS" ping | grep -q PONG \ -# || redis-cli -h 127.0.0.1 -p 6379 ping | grep -q PONG -#else -# exit 1 -#fi -#EOSH -#chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh -# -#cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH' -##!/usr/bin/env bash -#set -euo pipefail -# -#REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -#REDIS_PORT="${REDIS_PORT:-6379}" -#REDIS_PASSWORD="${REDIS_PASSWORD:-}" -# -#INSTALLER_ENV="/etc/mailwolt/installer.env" -#APP_ENV="/var/www/mailwolt/.env" -#REDIS_CLI="$(command -v redis-cli || true)" -#SYSTEMCTL="$(command -v systemctl || true)" -#RSPAMD_SERVICE="rspamd" -# -#if [ -r "$INSTALLER_ENV" ]; then . "$INSTALLER_ENV"; fi -#if [ -z "${REDIS_PASSWORD}" ] && [ -r "$APP_ENV" ]; then -# REDIS_PASSWORD="$(grep -E '^REDIS_PASSWORD=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)" -#fi -# -#if [ -n "$REDIS_CLI" ]; then -# echo "[INFO] Prüfe Redis Verbindung..." -# if [ -n "${REDIS_PASSWORD}" ]; then -# if ! "$REDIS_CLI" -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" ping | grep -q '^PONG$'; then -# echo "[WARN] Redis antwortet nicht oder Passwort falsch!" -# else -# echo "[OK] Redis antwortet (auth ok)." -# fi -# else -# if ! "$REDIS_CLI" -h "$REDIS_HOST" -p "$REDIS_PORT" ping | grep -q '^PONG$'; then -# echo "[WARN] Redis antwortet nicht (ohne Passwort)." -# else -# echo "[OK] Redis antwortet (kein Passwort)." -# fi -# fi -#else -# echo "[WARN] redis-cli nicht gefunden – überspringe Test." -#fi -# -#echo "[INFO] Prüfe Rspamd Socket & Verzeichnis..." -#install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true -#[ -S /var/lib/rspamd/rspamd.sock ] && rm -f /var/lib/rspamd/rspamd.sock || true -# -#echo "[INFO] Starte Rspamd neu..." -#if [ -n "$SYSTEMCTL" ]; then -# "$SYSTEMCTL" restart "$RSPAMD_SERVICE" -# echo "[OK] Rspamd erfolgreich neu gestartet." -#else -# echo "[ERROR] systemctl nicht gefunden – kein Neustart möglich." -# exit 1 -#fi -# -#echo "[INFO] Healthcheck (Port 11334)..." -#sleep 3 -#if ss -tln | grep -q ':11334'; then -# echo "[OK] Rspamd Controller läuft auf Port 11334." -#else -# echo "[WARN] Rspamd Controller Port 11334 nicht erreichbar." -#fi -# -#echo "[DONE] Mailwolt Rspamd-Heal abgeschlossen." -#exit 0 -#EOSH -#chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh -# -## WoltGuard Wrapper + Unit -#cat >/usr/local/bin/woltguard <<'EOSH' -##!/usr/bin/env bash -#set -euo pipefail -#case "${1:-status}" in -# start) systemctl enable --now monit ;; -# stop) systemctl stop monit ;; -# status) monit summary || systemctl status monit || true ;; -# heal) monit reload || true; sleep 1; monit restart all || true ;; -# monitor) monit monitor all || true ;; -# unmonitor) monit unmonitor all || true ;; -# *) echo "Usage: woltguard {start|stop|status|heal|monitor|unmonitor}"; exit 2;; -#esac -#EOSH -#chmod 0755 /usr/local/bin/woltguard -# -#cat >/etc/systemd/system/woltguard.service <<'EOF' -#[Unit] -#Description=WoltGuard – Self-Healing Monitor for MailWolt -#After=network.target -#[Service] -#Type=oneshot -#ExecStart=/usr/local/bin/woltguard start -#ExecStop=/usr/local/bin/woltguard stop -#RemainAfterExit=yes -#[Install] -#WantedBy=multi-user.target -#EOF -#systemctl daemon-reload -#systemctl enable --now woltguard -# -## Monit Basis + include -#sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true -#grep -q 'include /etc/monit/conf.d/*' /etc/monit/monitrc || echo 'include /etc/monit/conf.d/*' >>/etc/monit/monitrc -#install -d -m 0755 /etc/monit/conf.d -# -## Checks -#cat >/etc/monit/conf.d/postfix.conf <<'EOF' -#check process postfix with pidfile /var/spool/postfix/pid/master.pid -# start program = "/bin/systemctl start postfix" -# stop program = "/bin/systemctl stop postfix" -# if failed port 25 protocol smtp then restart -# if failed port 465 type tcpssl then restart -# if failed port 587 type tcp then restart -# if 5 restarts within 5 cycles then alert -#EOF -# -#cat >/etc/monit/conf.d/dovecot.conf <<'EOF' -#check process dovecot with pidfile /run/dovecot/master.pid -# start program = "/bin/systemctl start dovecot" -# stop program = "/bin/systemctl stop dovecot" -# if failed port 993 type tcpssl for 2 cycles then restart -# if failed port 24 protocol lmtp for 2 cycles then restart -# if 5 restarts within 5 cycles then alert -#EOF -# -#cat >/etc/monit/conf.d/nginx.conf <<'EOF' -#check process nginx with pidfile /run/nginx.pid -# start program = "/bin/systemctl start nginx" -# stop program = "/bin/systemctl stop nginx" -# if failed port 80 type tcp then restart -# if failed port 443 type tcpssl then restart -# if 5 restarts within 5 cycles then alert -#EOF -# -#cat >/etc/monit/conf.d/redis.conf <<'EOF' -#check process redis with pidfile /run/redis/redis-server.pid -# start program = "/bin/systemctl start redis-server" -# stop program = "/bin/systemctl stop redis-server" -# if failed host 127.0.0.1 port 6379 for 2 cycles then restart -# if 5 restarts within 5 cycles then alert -# -#check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh" -# if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server" -#EOF -# -#cat >/etc/monit/conf.d/rspamd.conf <<'EOF' -#check process rspamd with pidfile /run/rspamd/rspamd.pid -# start program = "/bin/systemctl start rspamd" -# stop program = "/bin/systemctl stop rspamd" -# if failed port 11333 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" -# if failed port 11334 for 2 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" -# if 5 restarts within 5 cycles then alert -#EOF -# -#cat >/etc/monit/conf.d/opendkim.conf <<'EOF' -#check process opendkim with pidfile /run/opendkim/opendkim.pid -# start program = "/bin/systemctl start opendkim" -# stop program = "/bin/systemctl stop opendkim" -# if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart -# if 5 restarts within 5 cycles then alert -#EOF -# -## optional: OpenDMARC -#if [[ "$OPENDMARC_ENABLE" = "1" ]]; then -# cat >/etc/monit/conf.d/opendmarc.conf <<'EOF' -#check process opendmarc with pidfile /run/opendmarc/opendmarc.pid -# start program = "/bin/systemctl start opendmarc" -# stop program = "/bin/systemctl stop opendmarc" -# if 5 restarts within 5 cycles then alert -#EOF -#else -# rm -f /etc/monit/conf.d/opendmarc.conf || true -#fi -# -## optional: ClamAV -#if [[ "$CLAMAV_ENABLE" = "1" ]]; then -# cat >/etc/monit/conf.d/clamav.conf <<'EOF' -#check process clamd with pidfile /run/clamav/clamd.pid -# start program = "/bin/systemctl start clamav-daemon" -# stop program = "/bin/systemctl stop clamav-daemon" -# if failed unixsocket /run/clamav/clamd.ctl then restart -# if 5 restarts within 5 cycles then alert -#EOF -#else -# rm -f /etc/monit/conf.d/clamav.conf || true -#fi -# -## optional: Fail2Ban -#if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then -# cat >/etc/monit/conf.d/fail2ban.conf <<'EOF' -#check process fail2ban with pidfile /run/fail2ban/fail2ban.pid -# start program = "/bin/systemctl start fail2ban" -# stop program = "/bin/systemctl stop fail2ban" -# if 5 restarts within 5 cycles then alert -#EOF -#else -# rm -f /etc/monit/conf.d/fail2ban.conf || true -#fi -# -#monit -t -#systemctl reload monit || systemctl restart monit -#systemctl status monit --no-pager || true -#log "[✓] WoltGuard aktiv." \ No newline at end of file diff --git a/mailwolt-installer/scripts/95-woltguard.sh b/mailwolt-installer/scripts/95-woltguard.sh deleted file mode 100644 index b3f31f4..0000000 --- a/mailwolt-installer/scripts/95-woltguard.sh +++ /dev/null @@ -1,439 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Flags laden (falls vorhanden) -INSTALLER_ENV="/etc/mailwolt/installer.env" -: "${CLAMAV_ENABLE:=}"; : "${OPENDMARC_ENABLE:=}"; : "${FAIL2BAN_ENABLE:=}"; : "${MONIT_HTTP:=}" -if [[ -z "${CLAMAV_ENABLE}${OPENDMARC_ENABLE}${FAIL2BAN_ENABLE}" && -r "$INSTALLER_ENV" ]]; then - . "$INSTALLER_ENV" -fi -CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}" -OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}" -FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" -MONIT_HTTP="${MONIT_HTTP:-1}" - -# ── Monit so konfigurieren, dass NUR monitrc.d/* geladen wird ──────────────── -install -d -m 0755 /etc/monit/monitrc.d -install -d -m 0755 /etc/monit/conf.d # passiver Ablageort (NICHT includiert) - -# Poll-Intervall (30s) -sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true -# alle alten include-Zeilen raus und monitrc.d setzen -sed -i 's|^#\?\s*include .*$||g' /etc/monit/monitrc -grep -q '^include /etc/monit/monitrc.d/\*' /etc/monit/monitrc \ - || echo 'include /etc/monit/monitrc.d/*' >> /etc/monit/monitrc - -# Optional: HTTP-UI nur einschalten, wenn explizit gewünscht -if [[ "$MONIT_HTTP" = "1" ]]; then - grep -q '^set httpd port 2812' /etc/monit/monitrc || cat >>/etc/monit/monitrc <<'HTTP' -set httpd port 2812 and - use address localhost - allow localhost -HTTP -fi - -# KEIN Löschen mehr der Dateien – wir verschieben je nach Status -# (vorher stand hier rm -rf /etc/monit/monitrc.d/* und rm -f /etc/monit/conf.d/*.conf) - -# ── Helper-Skripte ────────────────────────────────────────────────────────── -install -d -m 0755 /usr/local/sbin - -# Redis-Ping (Password: REDIS_PASSWORD aus installer.env oder .env) -cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail -INSTALLER_ENV="/etc/mailwolt/installer.env" -APP_ENV="/var/www/mailwolt/.env" -REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -REDIS_PORT="${REDIS_PORT:-6379}" -REDIS_PASSWORD="${REDIS_PASSWORD:-}" -REDIS_PASS="${REDIS_PASS:-}" - -[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" || true -if [[ -r "$APP_ENV" ]]; then - [[ -z "${REDIS_HOST}" ]] && REDIS_HOST="$(grep -m1 '^REDIS_HOST=' "$APP_ENV" | cut -d= -f2- || true)" - [[ -z "${REDIS_PORT}" ]] && REDIS_PORT="$(grep -m1 '^REDIS_PORT=' "$APP_ENV" | cut -d= -f2- || true)" - [[ -z "${REDIS_PASSWORD}" ]] && REDIS_PASSWORD="$(grep -m1 '^REDIS_PASSWORD=' "$APP_ENV" | cut -d= -f2- || true)" -fi -[[ -z "${REDIS_PASSWORD}" && -n "${REDIS_PASS}" ]] && REDIS_PASSWORD="$REDIS_PASS" - -strip(){ printf '%s' "$1" | sed -E 's/^"(.*)"$/\1/; s/^'"'"'(.*)'"'"'$/\1/'; } -REDIS_HOST="$(strip "${REDIS_HOST:-}")" -REDIS_PORT="$(strip "${REDIS_PORT:-}")" -REDIS_PASSWORD="$(strip "${REDIS_PASSWORD:-}")" - -command -v redis-cli >/dev/null 2>&1 || exit 1 -BASE=(timeout 2 redis-cli --no-auth-warning --raw -h "$REDIS_HOST" -p "$REDIS_PORT") -[[ -n "$REDIS_PASSWORD" ]] && CMD=("${BASE[@]}" -a "$REDIS_PASSWORD" ping) || CMD=("${BASE[@]}" ping) -[[ "$("${CMD[@]}" 2>/dev/null || true)" == "PONG" ]] -EOSH -chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh - -# Rspamd-Heal (Socke aufräumen, restart, Mini-Port-Check) -cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH' -#!/usr/bin/env bash -set -euo pipefail - -INSTALLER_ENV="/etc/mailwolt/installer.env" -APP_ENV="/var/www/mailwolt/.env" - -REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -REDIS_PORT="${REDIS_PORT:-6379}" -REDIS_PASS="${REDIS_PASS:-}" - -[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" -if [[ -z "${REDIS_PASS}" && -r "$APP_ENV" ]]; then - REDIS_PASS="$(grep -E '^REDIS_PASS=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)" -fi - -# Rspamd Runtime fixen -install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true -[[ -S /var/lib/rspamd/rspamd.sock ]] && rm -f /var/lib/rspamd/rspamd.sock || true - -echo "$(date '+%F %T') heal run" >> /var/log/rspamd-heal.log - -# Neustart -systemctl restart rspamd - -# Mini-Healthcheck -sleep 2 -ss -tln | grep -q ':11334' || echo "[WARN] Rspamd Controller Port 11334 nicht sichtbar" - -exit 0 -EOSH -chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh - -# ── Monit-Checks (nummeriert) – fixe Dienste immer aktiv ──────────────────── -# 10 – Redis -cat >/etc/monit/monitrc.d/10-redis.conf <<'EOF' -check process redis with pidfile /run/redis/redis-server.pid - start program = "/bin/systemctl start redis-server" - stop program = "/bin/systemctl stop redis-server" - if failed host 127.0.0.1 port 6379 for 2 cycles then restart - if 5 restarts within 5 cycles then alert - -check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh" - if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server" -EOF - -# 20 – Rspamd (robust via process-matching + Heal) -cat >/etc/monit/monitrc.d/20-rspamd.conf <<'EOF' -check process rspamd matching "rspamd: main process" - start program = "/bin/systemctl start rspamd" with timeout 120 seconds - stop program = "/bin/systemctl stop rspamd" - depends on redis - if failed host 127.0.0.1 port 11333 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" - if failed host 127.0.0.1 port 11334 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" - if does not exist for 2 cycles then restart - if 5 restarts within 10 cycles then unmonitor -EOF - -# 30 – Postfix -cat >/etc/monit/monitrc.d/30-postfix.conf <<'EOF' -check process postfix with pidfile /var/spool/postfix/pid/master.pid - start program = "/bin/systemctl start postfix" - stop program = "/bin/systemctl stop postfix" - if failed host 127.0.0.1 port 25 type tcp with timeout 15 seconds for 3 cycles then restart - if failed host 127.0.0.1 port 465 type tcpssl with timeout 10 seconds then restart - if failed host 127.0.0.1 port 587 type tcp with timeout 10 seconds then restart - if 5 restarts within 5 cycles then alert -EOF - -# 30 – Dovecot (IMAPS; LMTP oft Unix-Socket → kein TCP-Fehlalarm) -cat >/etc/monit/monitrc.d/30-dovecot.conf <<'EOF' -check process dovecot with pidfile /run/dovecot/master.pid - start program = "/bin/systemctl start dovecot" - stop program = "/bin/systemctl stop dovecot" - if failed port 993 type tcpssl for 3 cycles then restart - if 5 restarts within 10 cycles then alert -EOF - -# 40 – Nginx -cat >/etc/monit/monitrc.d/40-nginx.conf <<'EOF' -check process nginx with pidfile /run/nginx.pid - start program = "/bin/systemctl start nginx" - stop program = "/bin/systemctl stop nginx" - if failed port 80 type tcp then restart - if failed port 443 type tcpssl then restart - if 5 restarts within 5 cycles then alert -EOF - -# 50 – OpenDKIM -cat >/etc/monit/monitrc.d/50-opendkim.conf <<'EOF' -check process opendkim with pidfile /run/opendkim/opendkim.pid - start program = "/bin/systemctl start opendkim" - stop program = "/bin/systemctl stop opendkim" - if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart - if 5 restarts within 5 cycles then alert -EOF - -move_monit_conf() { - local name="$1" # z.B. 55-opendmarc - local enabled="$2" # "0" oder "1" - local src="/etc/monit/conf.d/${name}.conf" - local dst="/etc/monit/monitrc.d/${name}.conf" - - mkdir -p /etc/monit/conf.d /etc/monit/monitrc.d - - # Falls Datei nirgends existiert → in conf.d anlegen (lesbare Quelle) - if [[ ! -f "$src" && ! -f "$dst" ]]; then - cat >"$src" <<'EOF_PAYLOAD' -__PAYLOAD__ -EOF_PAYLOAD - fi - - if [[ "$enabled" = "1" ]]; then - # Aktiv: in monitrc.d haben - if [[ -f "$src" && ! -f "$dst" ]]; then - mv -f "$src" "$dst" - fi - else - # Inaktiv: in conf.d haben - if [[ -f "$dst" && ! -f "$src" ]]; then - mv -f "$dst" "$src" - fi - fi -} - -move_monit_conf "55-opendmarc" "${OPENDMARC_ENABLE:-0}" <<'EOF' -check process opendmarc with pidfile /run/opendmarc/opendmarc.pid - start program = "/bin/systemctl start opendmarc" - stop program = "/bin/systemctl stop opendmarc" - if 5 restarts within 5 cycles then alert -EOF - -move_monit_conf "60-clamav" "${CLAMAV_ENABLE:-0}" <<'EOF' -check process clamd matching "clamd" - start program = "/bin/systemctl start clamav-daemon" - stop program = "/bin/systemctl stop clamav-daemon" - if failed unixsocket /run/clamav/clamd.ctl for 3 cycles then restart - if 5 restarts within 10 cycles then unmonitor -EOF - -move_monit_conf "70-fail2ban" "${FAIL2BAN_ENABLE:-0}" <<'EOF' -check process fail2ban with pidfile /run/fail2ban/fail2ban.pid - start program = "/bin/systemctl start fail2ban" - stop program = "/bin/systemctl stop fail2ban" - if 5 restarts within 5 cycles then alert -EOF - -# ── Monit neu laden ───────────────────────────────────────────────────────── -monit -t -systemctl reload monit || systemctl restart monit - -# Optionaler Sichttest (CLI funktioniert auch ohne HTTP-UI) -# sleep 2 -# monit summary || true - -##!/usr/bin/env bash -#set -euo pipefail -# -## Flags laden (falls vorhanden) -#INSTALLER_ENV="/etc/mailwolt/installer.env" -#: "${CLAMAV_ENABLE:=}"; : "${OPENDMARC_ENABLE:=}"; : "${FAIL2BAN_ENABLE:=}"; : "${MONIT_HTTP:=}" -#if [[ -z "${CLAMAV_ENABLE}${OPENDMARC_ENABLE}${FAIL2BAN_ENABLE}" && -r "$INSTALLER_ENV" ]]; then -# . "$INSTALLER_ENV" -#fi -#CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}" -#OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}" -#FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" -#MONIT_HTTP="${MONIT_HTTP:-1}" -# -## ── Monit so konfigurieren, dass NUR monitrc.d/* geladen wird ──────────────── -#install -d -m 0755 /etc/monit/monitrc.d -## Poll-Intervall (30s) -#sed -i 's/^set daemon .*/set daemon 30/' /etc/monit/monitrc || true -## alle alten include-Zeilen raus und monitrc.d setzen -#sed -i 's|^#\?\s*include .*$||g' /etc/monit/monitrc -#grep -q '^include /etc/monit/monitrc.d/\*' /etc/monit/monitrc \ -# || echo 'include /etc/monit/monitrc.d/*' >> /etc/monit/monitrc -# -## Optional: HTTP-UI nur einschalten, wenn explizit gewünscht -#if [[ "$MONIT_HTTP" = "1" ]]; then -# grep -q '^set httpd port 2812' /etc/monit/monitrc || cat >>/etc/monit/monitrc <<'HTTP' -#set httpd port 2812 and -# use address localhost -# allow localhost -#HTTP -#fi -# -#sudo mkdir -p /etc/monit/monitrc.d -#sudo rm -rf /etc/monit/monitrc.d/* 2>/dev/null || true -#sudo rm -f /etc/monit/conf.d/*.conf 2>/dev/null || true -# -## ── Helper-Skripte ────────────────────────────────────────────────────────── -#install -d -m 0755 /usr/local/sbin -# -## Redis-Ping (Password: REDIS_PASSWORD aus installer.env oder .env) -#cat >/usr/local/sbin/mailwolt-redis-ping.sh <<'EOSH' -##!/usr/bin/env bash -#set -euo pipefail -#INSTALLER_ENV="/etc/mailwolt/installer.env" -#APP_ENV="/var/www/mailwolt/.env" -#REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -#REDIS_PORT="${REDIS_PORT:-6379}" -#REDIS_PASSWORD="${REDIS_PASSWORD:-}" -#REDIS_PASS="${REDIS_PASS:-}" -# -#[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" || true -#if [[ -r "$APP_ENV" ]]; then -# [[ -z "${REDIS_HOST}" ]] && REDIS_HOST="$(grep -m1 '^REDIS_HOST=' "$APP_ENV" | cut -d= -f2- || true)" -# [[ -z "${REDIS_PORT}" ]] && REDIS_PORT="$(grep -m1 '^REDIS_PORT=' "$APP_ENV" | cut -d= -f2- || true)" -# [[ -z "${REDIS_PASSWORD}" ]] && REDIS_PASSWORD="$(grep -m1 '^REDIS_PASSWORD=' "$APP_ENV" | cut -d= -f2- || true)" -#fi -#[[ -z "${REDIS_PASSWORD}" && -n "${REDIS_PASS}" ]] && REDIS_PASSWORD="$REDIS_PASS" -# -#strip(){ printf '%s' "$1" | sed -E 's/^"(.*)"$/\1/; s/^'"'"'(.*)'"'"'$/\1/'; } -#REDIS_HOST="$(strip "${REDIS_HOST:-}")" -#REDIS_PORT="$(strip "${REDIS_PORT:-}")" -#REDIS_PASSWORD="$(strip "${REDIS_PASSWORD:-}")" -# -#command -v redis-cli >/dev/null 2>&1 || exit 1 -#BASE=(timeout 2 redis-cli --no-auth-warning --raw -h "$REDIS_HOST" -p "$REDIS_PORT") -#[[ -n "$REDIS_PASSWORD" ]] && CMD=("${BASE[@]}" -a "$REDIS_PASSWORD" ping) || CMD=("${BASE[@]}" ping) -#[[ "$("${CMD[@]}" 2>/dev/null || true)" == "PONG" ]] -#EOSH -#chmod 0755 /usr/local/sbin/mailwolt-redis-ping.sh -# -## Rspamd-Heal (Socke aufräumen, restart, Mini-Port-Check) -#cat >/usr/local/sbin/mailwolt-rspamd-heal.sh <<'EOSH' -##!/usr/bin/env bash -#set -euo pipefail -# -#INSTALLER_ENV="/etc/mailwolt/installer.env" -#APP_ENV="/var/www/mailwolt/.env" -# -#REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -#REDIS_PORT="${REDIS_PORT:-6379}" -#REDIS_PASS="${REDIS_PASS:-}" -# -#[[ -r "$INSTALLER_ENV" ]] && . "$INSTALLER_ENV" -#if [[ -z "${REDIS_PASS}" && -r "$APP_ENV" ]]; then -# REDIS_PASS="$(grep -E '^REDIS_PASS=' "$APP_ENV" | head -n1 | cut -d= -f2- || true)" -#fi -# -## Rspamd Runtime fixen -#install -d -m 0755 -o _rspamd -g _rspamd /run/rspamd || true -#[[ -S /var/lib/rspamd/rspamd.sock ]] && rm -f /var/lib/rspamd/rspamd.sock || true -# -#echo "$(date '+%F %T') heal run" >> /var/log/rspamd-heal.log -# -## Neustart -#systemctl restart rspamd -# -## Mini-Healthcheck -#sleep 2 -#ss -tln | grep -q ':11334' || echo "[WARN] Rspamd Controller Port 11334 nicht sichtbar" -# -#exit 0 -#EOSH -#chmod 0755 /usr/local/sbin/mailwolt-rspamd-heal.sh -# -## ── Monit-Checks (nummeriert) ─────────────────────────────────────────────── -## 10 – Redis -#cat >/etc/monit/monitrc.d/10-redis.conf <<'EOF' -#check process redis with pidfile /run/redis/redis-server.pid -# start program = "/bin/systemctl start redis-server" -# stop program = "/bin/systemctl stop redis-server" -# if failed host 127.0.0.1 port 6379 for 2 cycles then restart -# if 5 restarts within 5 cycles then alert -# -#check program redis_ping path "/usr/local/sbin/mailwolt-redis-ping.sh" -# if status != 0 for 2 cycles then exec "/bin/systemctl restart redis-server" -#EOF -# -## 20 – Rspamd (robust via process-matching + Heal) -#cat >/etc/monit/monitrc.d/20-rspamd.conf <<'EOF' -#check process rspamd matching "rspamd: main process" -# start program = "/bin/systemctl start rspamd" with timeout 120 seconds -# stop program = "/bin/systemctl stop rspamd" -# depends on redis -# if failed host 127.0.0.1 port 11333 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" -# if failed host 127.0.0.1 port 11334 for 3 cycles then exec "/usr/local/sbin/mailwolt-rspamd-heal.sh" -# if does not exist for 2 cycles then restart -# if 5 restarts within 10 cycles then unmonitor -#EOF -# -## 30 – Postfix -#cat >/etc/monit/monitrc.d/30-postfix.conf <<'EOF' -#check process postfix with pidfile /var/spool/postfix/pid/master.pid -# start program = "/bin/systemctl start postfix" -# stop program = "/bin/systemctl stop postfix" -# if failed host 127.0.0.1 port 25 type tcp with timeout 15 seconds for 3 cycles then restart -# if failed host 127.0.0.1 port 465 type tcpssl with timeout 10 seconds then restart -# if failed host 127.0.0.1 port 587 type tcp with timeout 10 seconds then restart -# if 5 restarts within 5 cycles then alert -#EOF -# -## 30 – Dovecot (IMAPS; LMTP oft Unix-Socket → kein TCP-Fehlalarm) -#cat >/etc/monit/monitrc.d/30-dovecot.conf <<'EOF' -#check process dovecot with pidfile /run/dovecot/master.pid -# start program = "/bin/systemctl start dovecot" -# stop program = "/bin/systemctl stop dovecot" -# if failed port 993 type tcpssl for 3 cycles then restart -# if 5 restarts within 10 cycles then alert -#EOF -# -## 40 – Nginx -#cat >/etc/monit/monitrc.d/40-nginx.conf <<'EOF' -#check process nginx with pidfile /run/nginx.pid -# start program = "/bin/systemctl start nginx" -# stop program = "/bin/systemctl stop nginx" -# if failed port 80 type tcp then restart -# if failed port 443 type tcpssl then restart -# if 5 restarts within 5 cycles then alert -#EOF -# -## 50 – OpenDKIM -#cat >/etc/monit/monitrc.d/50-opendkim.conf <<'EOF' -#check process opendkim with pidfile /run/opendkim/opendkim.pid -# start program = "/bin/systemctl start opendkim" -# stop program = "/bin/systemctl stop opendkim" -# if failed host 127.0.0.1 port 8891 type tcp for 2 cycles then restart -# if 5 restarts within 5 cycles then alert -#EOF -# -## 55 – OpenDMARC (optional) -#if [[ "$OPENDMARC_ENABLE" = "1" ]]; then -# cat >/etc/monit/monitrc.d/55-opendmarc.conf <<'EOF' -#check process opendmarc with pidfile /run/opendmarc/opendmarc.pid -# start program = "/bin/systemctl start opendmarc" -# stop program = "/bin/systemctl stop opendmarc" -# if 5 restarts within 5 cycles then alert -#EOF -#else -# rm -f /etc/monit/monitrc.d/55-opendmarc.conf || true -#fi -# -## 60 – ClamAV (über Socket) -#if [[ "$CLAMAV_ENABLE" = "1" ]]; then -# cat >/etc/monit/monitrc.d/60-clamav.conf <<'EOF' -#check process clamd matching "clamd" -# start program = "/bin/systemctl start clamav-daemon" -# stop program = "/bin/systemctl stop clamav-daemon" -# if failed unixsocket /run/clamav/clamd.ctl for 3 cycles then restart -# if 5 restarts within 10 cycles then unmonitor -#EOF -#else -# rm -f /etc/monit/monitrc.d/60-clamav.conf || true -#fi -# -## 70 – Fail2Ban (optional) -#if [[ "$FAIL2BAN_ENABLE" = "1" ]]; then -# cat >/etc/monit/monitrc.d/70-fail2ban.conf <<'EOF' -#check process fail2ban with pidfile /run/fail2ban/fail2ban.pid -# start program = "/bin/systemctl start fail2ban" -# stop program = "/bin/systemctl stop fail2ban" -# if 5 restarts within 5 cycles then alert -#EOF -#else -# rm -f /etc/monit/monitrc.d/70-fail2ban.conf || true -#fi -# -## ── Monit neu laden ───────────────────────────────────────────────────────── -#monit -t -#systemctl reload monit || systemctl restart monit -# -## Optionaler Sichttest (CLI funktioniert auch ohne HTTP-UI) -##sleep 2 -##monit summary || true \ No newline at end of file diff --git a/mailwolt-installer/scripts/98-motd.sh b/mailwolt-installer/scripts/98-motd.sh deleted file mode 100644 index f5a7736..0000000 --- a/mailwolt-installer/scripts/98-motd.sh +++ /dev/null @@ -1,150 +0,0 @@ -#!/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 -# MOTD – MailWolt -# bewusst KEIN "set -e" und KEIN pipefail; MOTD darf nie hart abbrechen -set -u - -# ---------- Farben ---------- -NC="\033[0m"; WH="\033[1;37m"; CY="\033[1;36m"; GY="\033[0;90m" -GR="\033[1;32m"; YE="\033[1;33m"; RD="\033[1;31m" - -# ---------- Breite / Zentrierung ---------- -W=110 -term_cols=$(tput cols 2>/dev/null || echo $W) -[ "$term_cols" -gt "$W" ] && pad=$(( (term_cols - W)/2 )) || pad=0 -sp(){ [ "$1" -gt 0 ] && printf "%${1}s" " " || true; } -center() { local s="$1"; local n=$(( (W - ${#s})/2 )); sp $((pad+n)); printf "%s\n" "$s"; } -rule(){ sp "$pad"; printf "%0.s=" $(seq 1 "$W"); printf "\n"; } -title(){ sp "$pad"; local t="$1"; local lf=$(( (W - ${#t} - 2)/2 )); local rf=$(( W - ${#t} - 2 - lf )); \ - printf "%s" "$(printf '─%.0s' $(seq 1 $lf))"; printf " %s " "$t"; printf "%s\n" "$(printf '─%.0s' $(seq 1 $rf))"; } -kv(){ sp "$pad"; printf "%-12s: %s\n" "$1" "$2"; } - -# ---------- Installer-/App-Variablen ---------- -UI_HOST=""; WEBMAIL_HOST=""; MAIL_HOSTNAME="" -[ -r /etc/mailwolt/installer.env ] && . /etc/mailwolt/installer.env || true - -# ---------- Systemdaten ---------- -now="$(date '+%Y-%m-%d %H:%M:%S %Z' 2>/dev/null || echo '-')" -upt="$(uptime -p 2>/dev/null || echo '-')" -cores="$(nproc 2>/dev/null || echo 1)" -load_raw="$(awk '{printf "%s / %s / %s",$1,$2,$3}' /proc/loadavg 2>/dev/null || echo '0.00 / 0.00 / 0.00')" -load1="$(awk '{print $1}' /proc/loadavg 2>/dev/null || echo 0)" - -# RAM/SWAP -mem_total="$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)" -mem_avail="$(awk '/MemAvailable/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)" -mem_used=$(( mem_total - mem_avail )) -swap_total="$(awk '/SwapTotal/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)" -swap_free="$(awk '/SwapFree/ {print int($2/1024)}' /proc/meminfo 2>/dev/null || echo 0)" -swap_used=$(( swap_total - swap_free )) - -pct(){ local u="$1" t="$2"; [ "$t" -gt 0 ] || { echo 0; return; }; awk -v u="$u" -v t="$t" 'BEGIN{printf "%d",(u*100)/t}' ; } -ram_pct=$(pct "$mem_used" "$mem_total") -swap_pct=$(pct "$swap_used" "$swap_total") - -# Disks -df_line(){ df -hP "$1" 2>/dev/null | awk 'NR==2{printf "%s / %s (%s)",$3,$2,$5}'; } -df_pct(){ df -P "$1" 2>/dev/null | awk 'NR==2{gsub("%","",$5);print $5+0}'; } -disk_root="$(df_line /)"; pct_root="$(df_pct /)" -disk_var="$(df_line /var 2>/dev/null)"; [ -n "$disk_var" ] || disk_var="-" -pct_var="$(df_pct /var 2>/dev/null)"; [ -n "$pct_var" ] || pct_var=0 - -# IPs (int/ext) -ipv4_int="$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i!~/:/){print $i;exit}}')" -ipv6_int="$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i~/:/){print $i;exit}}')" -ipv4_ext="$(curl -4fsS --max-time 1 https://ifconfig.me 2>/dev/null || true)" -ipv6_ext="$(curl -6fsS --max-time 1 https://ifconfig.me 2>/dev/null || true)" - -# ---------- Status-Farben ---------- -mark(){ # value thresholdY thresholdR - local v="$1" y="$2" r="$3" - if [ "$v" -ge "$r" ]; then printf "${RD}[HIGH]${NC}" - elif [ "$v" -ge "$y" ]; then printf "${YE}[WARN]${NC}" - else printf "${GR}[OK]${NC}" - fi -} -# Load/CPU-Schwellen (pro Core) -load_pct=$(awk -v l="$load1" -v c="$cores" 'BEGIN{if(c<1)c=1; printf "%d", (l/c)*100}') -m_load="$(mark "$load_pct" 70 100)" -m_ram="$(mark "$ram_pct" 75 90)" -m_swap="$(mark "$swap_pct" 10 50)" -m_root="$(mark "$pct_root" 75 90)" -m_var="$(mark "$pct_var" 75 90)" - -# ---------- Header ---------- -rule -center "" -center ":::: :::: ::: ::::::::::: ::: ::: ::: :::::::: ::: :::::::::::" -center ":+:+:+ :+:+:+ :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: " -center ":+: +:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ " -center "+#+ +:+ +#+ +#++:++#++: +#+ +#+ +#+ +:+ +#+ +#+ +:+ +#+ +#+ " -center "+#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+ +#+ +#+ +#+ +#+ +#+ " -center "#+# #+# #+# #+# #+# #+# #+#+# #+#+# #+# #+# #+# #+# " -center "### ### ### ### ########### ########## ### ### ######## ########## ### " -center "" -rule - -# ---------- System ---------- -kv "Date / Time" "${YE}${now}${NC}" -sp "$pad"; printf "%-12s: int %-40s ext %s\n" "IPv4" "${ipv4_int:--}" "${ipv4_ext:--}" -sp "$pad"; printf "%-12s: int %-40s ext %s\n" "IPv6" "${ipv6_int:--}" "${ipv6_ext:--}" -kv "Uptime" "$upt" -sp "$pad"; printf "%-12s: %s cores, load %s %b\n" "CPU" "$cores" "$load_raw" "$m_load" -sp "$pad"; printf "%-12s: %s MiB / %s MiB (%d%%) %b %-5s %s MiB / %s MiB (%d%%) %b\n" \ - "RAM" "$mem_used" "$mem_total" "$ram_pct" "$m_ram" "SWAP:" "$swap_used" "$swap_total" "$swap_pct" "$m_swap" -sp "$pad"; printf "%-12s: / %s %b %-5s %s %b\n" \ - "Disk" "$disk_root" "$m_root" "/var:" "$disk_var" "$m_var" -echo - -# ---------- Domains ---------- -title "Domains" -[ -n "${UI_HOST:-}" ] && kv "UI" "${UI_HOST}" -[ -n "${WEBMAIL_HOST:-}" ] && kv "Webmail" "${WEBMAIL_HOST}" -[ -n "${MAIL_HOSTNAME:-}" ]&& kv "MX" "${MAIL_HOSTNAME}" -echo - -# ---------- Services (4 Spalten, bündig) ---------- -title "Services" -svc_state(){ systemctl is-active --quiet "$1" && printf "${GR}[OK]${NC}" || printf "${RD}[FAIL]${NC}"; } -SVC=( nginx mariadb redis-server postfix dovecot rspamd opendkim opendmarc clamav-daemon fail2ban mailwolt-ws mailwolt-queue mailwolt-schedule ) - -i=0; line="" -for s in "${SVC[@]}"; do - st="$(svc_state "$s")" - seg="$(printf "%-18s %-7s" "$s" "$st")" - line="$line$seg" - i=$((i+1)) - if [ $((i%4)) -eq 0 ]; then sp "$pad"; echo "$line"; line=""; else line="$line "; fi -done -[ -n "$line" ] && { sp "$pad"; echo "$line"; } -echo - -exit 0 -SH - -chmod 755 /usr/local/bin/mw-motd - -# update-motd Hook -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 - # 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 -log "[✓] MOTD installiert." \ No newline at end of file diff --git a/mailwolt-installer/scripts/99-summary.sh b/mailwolt-installer/scripts/99-summary.sh deleted file mode 100644 index bf08778..0000000 --- a/mailwolt-installer/scripts/99-summary.sh +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -source ./lib.sh - -# ───────────────────────────────────────────────────────────── -# MailWolt – Abschluss / Summary (Dienste, Zertifikate, Smoke-Test) -# ───────────────────────────────────────────────────────────── - -# Farben & Deko -NC="\033[0m"; BOLD="\033[1m"; DIM="\033[2m" -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}" -bar(){ printf "${CYAN}%s${NC}\n" "──────────────────────────────────────────────────────────────────────────────"; } -ok(){ printf " [${OKS}]\n"; } -fail(){ printf " [${FAILS}]\n"; } - -# Installer-Variablen laden (falls vorhanden) -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:-}" # leer = nicht anzeigen; "1"=Proxy, "dev"=Dev, sonst "nein" -NPM_IP="${NPM_IP:-}" - -LE_EMAIL="${LE_EMAIL:-admin@${BASE_DOMAIN}}" -ACME_WEBROOT="/var/www/letsencrypt" - -# Zert-Pfade (werden via Hook nach /etc/ssl/* verlinkt) -UI_CERT="/etc/ssl/ui/fullchain.pem" -UI_KEY="/etc/ssl/ui/privkey.pem" -WEBMAIL_CERT="/etc/ssl/webmail/fullchain.pem" -MAIL_CERT="/etc/ssl/mail/fullchain.pem" - -# IPs (aus lib.sh) -SERVER_PUBLIC_IPV4="${SERVER_PUBLIC_IPV4:-$(detect_ip)}" -SERVER_PUBLIC_IPV6="${SERVER_PUBLIC_IPV6:-$(detect_ipv6)}" - -# URLs (https nur, wenn UI-Cert+Key vorhanden) -SCHEME="http" -[[ -s "$UI_CERT" && -s "$UI_KEY" ]] && SCHEME="https" -APP_URL="${SCHEME}://${UI_HOST:-$SERVER_PUBLIC_IPV4}" -WEBMAIL_URL="${SCHEME}://${WEBMAIL_HOST:-$SERVER_PUBLIC_IPV4}" - -# Ziel eines Symlinks auflösen -real_target(){ readlink -f -- "$1" 2>/dev/null || true; } - -# "LE" werten, wenn live/* ODER archive/* (auch fullchainN.pem) getroffen wird -is_le_path(){ - local p="$1" - [[ "$p" == /etc/letsencrypt/live/*/fullchain.pem || "$p" == /etc/letsencrypt/archive/*/fullchain*.pem ]] -} - -UI_CERT_TARGET="$(real_target "$UI_CERT")" -WEBMAIL_CERT_TARGET="$(real_target "$WEBMAIL_CERT")" -MAIL_CERT_TARGET="$(real_target "$MAIL_CERT")" - -is_le_path() { - case "$1" in - /etc/letsencrypt/live/*) return 0 ;; - *) return 1 ;; - esac -} - -# robust gegen set -u: immer ${var:-} -UI_LE="self-signed/none" -if [ -s "${UI_CERT:-}" ] && [ -n "${UI_CERT_TARGET:-}" ] && is_le_path "${UI_CERT_TARGET:-}"; then - UI_LE="LE" -fi - -WEBMAIL_LE="self-signed/none" -if [ -s "${WEBMAIL_CERT:-}" ] && [ -n "${WEBMAIL_CERT_TARGET:-}" ] && is_le_path "${WEBMAIL_CERT_TARGET:-}"; then - WEBMAIL_LE="LE" -fi - -MAIL_LE="self-signed/none" -if [ -s "${MAIL_CERT:-}" ] && [ -n "${MAIL_CERT_TARGET:-}" ] && is_le_path "${MAIL_CERT_TARGET:-}"; then - MAIL_LE="LE" -fi - -echo -bar -printf " %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}" -# Proxy-Block nur anzeigen, wenn Variable gesetzt ist -if [[ -n "$PROXY_MODE" ]]; then - if [[ "$PROXY_MODE" == "1" ]]; then - printf " %-14s %s\n" "Proxy-Mode:" "ja (NPM: ${NPM_IP:-unbekannt})" - elif [[ "$PROXY_MODE" == "dev" ]]; then - printf " %-14s %s\n" "Proxy-Mode:" "Entwicklungsmodus" - else - printf " %-14s %s\n" "Proxy-Mode:" "nein" - fi -fi -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 - -# ── Dienste ──────────────────────────────────────────────────────────────── -bar -echo " Services" -bar - -OK_LIST=() -FAIL_LIST=() - -svc(){ - 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 -# App-Worker (tolerant) -svc "${APP_USER}-ws" "mailwolt-ws" || true -svc "${APP_USER}-schedule" "mailwolt-schedule" || true -svc "${APP_USER}-queue" "mailwolt-queue" || true - -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 -b --no-pager" -fi -echo - -# ── Smoke-Test ───────────────────────────────────────────────────────────── -bar -echo " Smoke-Test (SMTP/IMAP/POP3 mit/ohne TLS)" -bar - -check_port(){ - 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 -} - -# kleines Delay nach Erststart -sleep 6 || true - -# 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)" - -echo - -# Hinweise nur ausgeben, wenn wirklich kein LE für UI/Webmail -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 - -# Proxy-Info (optional) -if [[ "$PROXY_MODE" == "1" ]]; then - echo -e " ${GREY}Proxy-Hinweis:${NC} App erwartet TLS am Proxy (Backend ohne https-Redirects)." - echo -fi \ No newline at end of file diff --git a/mailwolt-installer/scripts/bootstrap.sh b/mailwolt-installer/scripts/bootstrap.sh deleted file mode 100644 index 2bfaf20..0000000 --- a/mailwolt-installer/scripts/bootstrap.sh +++ /dev/null @@ -1,633 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# --- Farbschema für whiptail (libnewt) – hohe Lesbarkeit (dunkler Input, schwarze Schrift) --- -export NEWT_COLORS=' -root=,blue -border=black,lightgray -window=black,lightgray -textbox=black,lightgray -label=black,lightgray -entry=black,cyan -button=black,cyan -actlistbox=black,cyan -actsellistbox=black,cyan -' - -# optionales Backtitle (erscheint oben) -export DIALOGOPTS="--backtitle MailWolt Setup" - -# ────────────────────────────────────────────────────────────── -# MailWolt – Interaktiver Bootstrap (whiptail + Fallback) -# ────────────────────────────────────────────────────────────── - -DEV_MODE=0 -PROXY_MODE=0 -NPM_IP="" -while [[ $# -gt 0 ]]; do - case "$1" in - -dev) DEV_MODE=1 ;; - -proxy) PROXY_MODE=1; NPM_IP="${2:-}"; shift ;; - esac - shift -done - -APP_ENV="${APP_ENV:-$([[ $DEV_MODE -eq 1 ]] && echo local || echo production)}" -APP_DEBUG="${APP_DEBUG:-$([[ $DEV_MODE -eq 1 ]] && echo true || echo false)}" -export DEV_MODE PROXY_MODE NPM_IP APP_ENV APP_DEBUG - -DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}" -REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}" -export DB_PASS REDIS_PASS - -cd "$(dirname "$0")" -source ./lib.sh -require_root -header - -# ── Defaults ────────────────────────────────────────────────── -APP_NAME="${APP_NAME:-MailWolt}" -APP_USER="${APP_USER:-mailwolt}" -APP_GROUP="${APP_GROUP:-www-data}" -APP_USER_PREFIX="${APP_USER_PREFIX:-mw}" -APP_DIR="${APP_DIR:-/var/www/${APP_USER}}" - -BASE_DOMAIN="${BASE_DOMAIN:-example.com}" -UI_SUB="${UI_SUB:-ui}" -WEBMAIL_SUB="${WEBMAIL_SUB:-webmail}" -MTA_SUB="${MTA_SUB:-mx}" - -DB_NAME="${DB_NAME:-${APP_USER}}" -DB_USER="${DB_USER:-${APP_USER}}" - -SERVER_PUBLIC_IPV4="$(detect_ip)" -SERVER_PUBLIC_IPV6="$(detect_ipv6)" -DEFAULT_TZ="$(detect_timezone)" -DEFAULT_LOCALE="$(guess_locale_from_tz "$DEFAULT_TZ")" - -echo -e "${GREY}Erkannte IP (v4): ${SERVER_PUBLIC_IPV4} v6: ${SERVER_PUBLIC_IPV6:-–}${NC}" - -# ── Helpers ─────────────────────────────────────────────────── -have_whiptail(){ command -v whiptail >/dev/null 2>&1; } - -#valid_fqdn(){ -# [[ "$1" =~ ^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+[a-z]{2,}$ ]] -#} - -# ── Host-Validierung & DEV-Erkennung ──────────────────────────────────────── -valid_fqdn_prod(){ [[ "$1" =~ ^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+[a-z]{2,}$ ]]; } -valid_host_dev(){ - # erlaubt: single-label (ui, webmail), FQDNs, IPv4 - [[ "$1" =~ ^([a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9-]+)*$ ]] || [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] -} -is_local_like(){ - local h="$(echo "$1" | tr '[:upper:]' '[:lower:]')" - [[ "$h" =~ \.local$ || "$h" =~ \.loc$ || "$h" =~ \.dev$ || "$h" =~ \.test$ || "$h" = "localhost" ]] && return 0 - [[ "$h" =~ ^10\. || "$h" =~ ^192\.168\. || "$h" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. || "$h" =~ ^127\. ]] && return 0 - return 1 -} -normalize_host(){ - # $1=input $2=default (nutzt DEV_MODE für die passende Prüflogik) - local inp="$1" def="$2" - if [[ "${DEV_MODE}" = "1" ]]; then - valid_host_dev "$inp" && { echo "$inp"; return; } - else - valid_fqdn_prod "$inp" && { echo "$inp"; return; } - fi - echo "$def" -} - -ask_tty_domain(){ - local label="$1" example="$2" def="$3" outvar="$4" inp - echo -e "${CYAN}${label}${NC}" - echo -e " z.B. ${YELLOW}${example}${NC}" - echo -e " Default: ${GREY}${def}${NC}" - read -r -p " Eingabe (Enter=Default): " inp || true - inp="${inp:-$def}" - if ! valid_fqdn "$inp"; then - echo -e "${YELLOW}[!] Ungültiger FQDN, nehme Default: ${def}${NC}" - inp="$def" - fi - eval "$outvar='$inp'" -} - -# ── Interaktive Eingaben (whiptail oder Fallback) ───────────── -MTA_DEFAULT="${MTA_SUB}.${BASE_DOMAIN}" -UI_DEFAULT="${UI_SUB}.${BASE_DOMAIN}" -WEBMAIL_DEFAULT="${WEBMAIL_SUB}.${BASE_DOMAIN}" - -CLAMAV_ENABLE=1 -OPENDMARC_ENABLE=1 -FAIL2BAN_ENABLE=1 - -if command -v whiptail >/dev/null 2>&1; then - TITLE="MailWolt Setup" - - # Hinweise zu erlaubten DEV-Hosts - MSG_SUFFIX="\n\nHinweis: Im DEV-Modus sind auch single-label Hosts (z.B. ui, webmail), *.local/*.dev und IPs erlaubt." - - _mta_in="$(whiptail --title "$TITLE" --inputbox "Mailserver-Host (MX)\nBeispiele: mx.domain.tld | mx.local | 10.0.0.10${MSG_SUFFIX}" 13 70 "$MTA_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1 - _ui_in="$(whiptail --title "$TITLE" --inputbox "UI / Admin-Panel Host\nBeispiele: ui.domain.tld | ui.local | 10.0.0.10${MSG_SUFFIX}" 13 70 "$UI_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1 - _wm_in="$(whiptail --title "$TITLE" --inputbox "Webmail Host\nBeispiele: webmail.domain.tld | web.local | 10.0.0.10${MSG_SUFFIX}" 13 70 "$WEBMAIL_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1 - - # ZUERST provisorisch prüfen, ob „lokal“ → DEV erzwingen - if is_local_like "$_mta_in" || is_local_like "$_ui_in" || is_local_like "$_wm_in"; then - DEV_MODE=1; APP_ENV="local"; APP_DEBUG="true" - fi - export DEV_MODE APP_ENV APP_DEBUG - - # Jetzt mit passender Logik normalisieren - MTA_FQDN="$(normalize_host "$_mta_in" "$MTA_DEFAULT")" - UI_FQDN="$(normalize_host "$_ui_in" "$UI_DEFAULT")" - WEBMAIL_FQDN="$(normalize_host "$_wm_in" "$WEBMAIL_DEFAULT")" - - CHOICES="$(whiptail --title "$TITLE" --checklist "Optionale Dienste aktivieren" 15 70 6 \ - "ClamAV" "Virenscan (clamd/clamav-daemon)" ON \ - "OpenDMARC" "DMARC-Auswertung" ON \ - "Fail2Ban" "Brute-Force-Schutz" ON \ - 3>&1 1>&2 2>&3)" || true - CLAMAV_ENABLE=0; [[ "$CHOICES" == *"ClamAV"* ]] && CLAMAV_ENABLE=1 - OPENDMARC_ENABLE=0; [[ "$CHOICES" == *"OpenDMARC"* ]] && OPENDMARC_ENABLE=1 - FAIL2BAN_ENABLE=0; [[ "$CHOICES" == *"Fail2Ban"* ]] && FAIL2BAN_ENABLE=1 - -else - echo -e "${GREY}[i] whiptail nicht gefunden – TTY-Fallback.${NC}\n" - read -r -p "Mailserver-Host (MX) [${MTA_DEFAULT}]: " _mta_in; _mta_in="${_mta_in:-$MTA_DEFAULT}" - read -r -p "UI / Admin-Panel Host [${UI_DEFAULT}]: " _ui_in; _ui_in="${_ui_in:-$UI_DEFAULT}" - read -r -p "Webmail Host [${WEBMAIL_DEFAULT}]: " _wm_in; _wm_in="${_wm_in:-$WEBMAIL_DEFAULT}" - - if is_local_like "$_mta_in" || is_local_like "$_ui_in" || is_local_like "$_wm_in"; then - DEV_MODE=1; APP_ENV="local"; APP_DEBUG="true" - fi - export DEV_MODE APP_ENV APP_DEBUG - - MTA_FQDN="$(normalize_host "$_mta_in" "$MTA_DEFAULT")" - UI_FQDN="$(normalize_host "$_ui_in" "$UI_DEFAULT")" - WEBMAIL_FQDN="$(normalize_host "$_wm_in" "$WEBMAIL_DEFAULT")" - - read -r -p "ClamAV aktivieren? (1/0, Enter=1): " CLAMAV_ENABLE; CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}" - read -r -p "OpenDMARC aktivieren? (1/0, Enter=1): " OPENDMARC_ENABLE; OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}" - read -r -p "Fail2Ban aktivieren? (1/0, Enter=1): " FAIL2BAN_ENABLE; FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" -fi - -#if have_whiptail; then -# TITLE="MailWolt Setup" -# -# MTA_FQDN="$(whiptail --title "$TITLE" --inputbox "Mailserver-FQDN (MX)\nBeispiel: mx.domain.tld" 11 70 "$MTA_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1 -# valid_fqdn "$MTA_FQDN" || MTA_FQDN="$MTA_DEFAULT" -# -# UI_FQDN="$(whiptail --title "$TITLE" --inputbox "UI / Admin-Panel FQDN\nBeispiel: ui.domain.tld" 11 70 "$UI_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1 -# valid_fqdn "$UI_FQDN" || UI_FQDN="$UI_DEFAULT" -# -# WEBMAIL_FQDN="$(whiptail --title "$TITLE" --inputbox "Webmail FQDN\nBeispiel: webmail.domain.tld" 11 70 "$WEBMAIL_DEFAULT" 3>&1 1>&2 2>&3)" || exit 1 -# valid_fqdn "$WEBMAIL_FQDN" || WEBMAIL_FQDN="$WEBMAIL_DEFAULT" -# -# CHOICES="$(whiptail --title "$TITLE" --checklist "Optionale Dienste aktivieren" 15 70 6 \ -# "ClamAV" "Virenscan (clamd/clamav-daemon)" ON \ -# "OpenDMARC" "DMARC-Auswertung" ON \ -# "Fail2Ban" "Brute-Force-Schutz" ON \ -# 3>&1 1>&2 2>&3)" || true -# -# CLAMAV_ENABLE=0; [[ "$CHOICES" == *"ClamAV"* ]] && CLAMAV_ENABLE=1 -# OPENDMARC_ENABLE=0; [[ "$CHOICES" == *"OpenDMARC"* ]] && OPENDMARC_ENABLE=1 -# FAIL2BAN_ENABLE=0; [[ "$CHOICES" == *"Fail2Ban"* ]] && FAIL2BAN_ENABLE=1 -# -# whiptail --title "$TITLE" --msgbox "Zusammenfassung: -# -#MX : $MTA_FQDN -#UI : $UI_FQDN -#Webmail : $WEBMAIL_FQDN -# -#ClamAV : $([[ $CLAMAV_ENABLE -eq 1 ]] && echo Aktiv || echo Deaktiv) -#OpenDMARC : $([[ $OPENDMARC_ENABLE -eq 1 ]] && echo Aktiv || echo Deaktiv) -#Fail2Ban : $([[ $FAIL2BAN_ENABLE -eq 1 ]] && echo Aktiv || echo Deaktiv) -#" 16 70 -# -#else -# echo -e "${GREY}[i] whiptail nicht gefunden – nutze TTY-Prompts.${NC}\n" -# ask_tty_domain "Mailserver-FQDN (MX)" "mx.domain.tld" "$MTA_DEFAULT" MTA_FQDN -# ask_tty_domain "UI / Admin-Panel FQDN" "ui.domain.tld" "$UI_DEFAULT" UI_FQDN -# ask_tty_domain "Webmail FQDN" "webmail.domain.tld" "$WEBMAIL_DEFAULT" WEBMAIL_FQDN -# -# read -r -p "ClamAV aktivieren? (1/0, Enter=1): " CLAMAV_ENABLE; CLAMAV_ENABLE="${CLAMAV_ENABLE:-1}" -# read -r -p "OpenDMARC aktivieren? (1/0, Enter=1): " OPENDMARC_ENABLE; OPENDMARC_ENABLE="${OPENDMARC_ENABLE:-1}" -# read -r -p "Fail2Ban aktivieren? (1/0, Enter=1): " FAIL2BAN_ENABLE; FAIL2BAN_ENABLE="${FAIL2BAN_ENABLE:-1}" -#fi - -# ── Defaults/Kompatibilität ────────────────────────────────── -MTA_FQDN="${MTA_FQDN:-${MTA_DEFAULT}}" -UI_FQDN="${UI_FQDN:-${UI_DEFAULT}}" -WEBMAIL_FQDN="${WEBMAIL_FQDN:-${WEBMAIL_DEFAULT}}" -DKIM_ENABLE="${DKIM_ENABLE:-1}" -DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}" -DKIM_GENERATE="${DKIM_GENERATE:-1}" - -# BASE_DOMAIN/Subs aus FQDNs ableiten -if [[ "$MTA_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then MTA_SUB="${BASH_REMATCH[1]}"; BASE_DOMAIN="${BASH_REMATCH[2]}"; fi -if [[ "$UI_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then UI_SUB="${BASH_REMATCH[1]}"; fi -if [[ "$WEBMAIL_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then WEBMAIL_SUB="${BASH_REMATCH[1]}"; fi - -SYSMAIL_SUB="${SYSMAIL_SUB:-sysmail}" -SYSMAIL_DOMAIN="${SYSMAIL_SUB}.${BASE_DOMAIN}" - -MAIL_HOSTNAME="${MTA_FQDN}" -UI_HOST="${UI_FQDN}" -WEBMAIL_HOST="${WEBMAIL_FQDN}" - -APP_TZ="${APP_TZ:-$DEFAULT_TZ}" -APP_LOCALE="${APP_LOCALE:-$DEFAULT_LOCALE}" - -# ── Export & persist ───────────────────────────────────────── -export APP_NAME APP_USER APP_GROUP APP_USER_PREFIX APP_DIR -export BASE_DOMAIN UI_SUB WEBMAIL_SUB MTA_SUB -export SYSMAIL_SUB SYSMAIL_DOMAIN DKIM_ENABLE DKIM_SELECTOR DKIM_GENERATE -export UI_HOST WEBMAIL_HOST MAIL_HOSTNAME -export DB_NAME DB_USER -export SERVER_PUBLIC_IPV4 SERVER_PUBLIC_IPV6 APP_TZ APP_LOCALE -export CLAMAV_ENABLE OPENDMARC_ENABLE FAIL2BAN_ENABLE - -install -d -m 0755 /etc/mailwolt -cat >/etc/mailwolt/installer.env <>> Running ${STEP}.sh" - bash "./${STEP}.sh" -done - -##!/usr/bin/env bash -#set -euo pipefail -# -## --- Flags / Modi --- -#DEV_MODE=0 -#PROXY_MODE=0 -#NPM_IP="" -# -#while [[ $# -gt 0 ]]; do -# case "$1" in -# -dev) DEV_MODE=1 ;; -# -proxy) PROXY_MODE=1; NPM_IP="${2:-}"; shift ;; -# esac -# shift -#done -# -#APP_ENV="${APP_ENV:-$([[ $DEV_MODE -eq 1 ]] && echo local || echo production)}" -#APP_DEBUG="${APP_DEBUG:-$([[ $DEV_MODE -eq 1 ]] && echo true || echo false)}" -#export DEV_MODE PROXY_MODE NPM_IP APP_ENV APP_DEBUG -# -#DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}" -#REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}" -# -#export DB_PASS REDIS_PASS -# -#cd "$(dirname "$0")" -#source ./lib.sh -#require_root -#header -# -## ── Defaults ──────────────────────────────────────────────────────────────── -#APP_NAME="${APP_NAME:-MailWolt}" -#APP_USER="${APP_USER:-mailwolt}" -#APP_GROUP="${APP_GROUP:-www-data}" -#APP_USER_PREFIX="${APP_USER_PREFIX:-mw}" -#APP_DIR="${APP_DIR:-/var/www/${APP_USER}}" -# -#BASE_DOMAIN="${BASE_DOMAIN:-example.com}" -#UI_SUB="${UI_SUB:-ui}" -#WEBMAIL_SUB="${WEBMAIL_SUB:-webmail}" -#MTA_SUB="${MTA_SUB:-mx}" -# -#DB_NAME="${DB_NAME:-${APP_USER}}" -#DB_USER="${DB_USER:-${APP_USER}}" -# -#SERVER_PUBLIC_IPV4="$(detect_ip)" -#SERVER_PUBLIC_IPV6="$(detect_ipv6)" -#DEFAULT_TZ="$(detect_timezone)" -#DEFAULT_LOCALE="$(guess_locale_from_tz "$DEFAULT_TZ")" -# -#echo -e "${GREY}Erkannte IP (v4): ${SERVER_PUBLIC_IPV4} v6: ${SERVER_PUBLIC_IPV6:-–}${NC}" -# -## ── Schöne, farbige Abfragen ──────────────────────────────────────────────── -#echo -e "${CYAN}" -#echo "──────────────────────────────────────────────" -#echo -e " 📧 MailWolt Setup – Domain Konfiguration" -#echo "──────────────────────────────────────────────" -#echo -e "${NC}" -# -#MTA_DEFAULT="${MTA_SUB}.${BASE_DOMAIN}" -#UI_DEFAULT="${UI_SUB}.${BASE_DOMAIN}" -#WEBMAIL_DEFAULT="${WEBMAIL_SUB}.${BASE_DOMAIN}" -# -#ask_domain() { -# local __outvar="$1" label="$2" example="$3" defval="$4" input="" -# echo -e "${GREEN}[?]${NC} ${label}" -# echo -e " z.B. ${YELLOW}${example}${NC}" -# echo -e " Default: ${CYAN}${defval}${NC}" -# echo -ne " → Eingabe: ${CYAN}" -# read -r input -# echo -e "${NC}" -# if [[ -z "$input" ]]; then -# eval "$__outvar='$defval'" -# else -# eval "$__outvar='$input'" -# fi -#} -# -#ask_toggle() { -# local __outvar="$1" label="$2" defval="${3:-1}" input="" -# echo -ne "${GREEN}[?]${NC} ${label} (${CYAN}1${NC}=Ja / ${YELLOW}0${NC}=Nein) [Enter=${defval}]: " -# read -r input -# input="${input:-$defval}" -# case "$input" in -# 1|0) ;; -# *) echo -e "${YELLOW}Ungültig, nehme Default=${defval}.${NC}"; input="$defval" ;; -# esac -# eval "$__outvar='$input'" -#} -# -#ask_domain "MTA_FQDN" "Mailserver-FQDN (MX)" "mx.domain.tld" "$MTA_DEFAULT" -#ask_domain "UI_FQDN" "UI / Admin-Panel" "ui.domain.tld" "$UI_DEFAULT" -#ask_domain "WEBMAIL_FQDN" "Webmail-FQDN" "webmail.domain.tld" "$WEBMAIL_DEFAULT" -# -#echo -e "${CYAN}" -#echo "──────────────────────────────────────────────" -#echo -e " 🛡 Optionale Dienste" -#echo "──────────────────────────────────────────────" -#echo -e "${NC}" -# -#ask_toggle "CLAMAV_ENABLE" "ClamAV Virenscan aktivieren?" 1 -#ask_toggle "OPENDMARC_ENABLE" "OpenDMARC auswerten?" 1 -#ask_toggle "FAIL2BAN_ENABLE" "Fail2Ban aktivieren?" 1 -#echo -# -## Defaults, wenn Enter gedrückt (Abwärtskompatibilität) -#MTA_FQDN="${MTA_FQDN:-${MTA_SUB}.${BASE_DOMAIN}}" -#UI_FQDN="${UI_FQDN:-${UI_SUB}.${BASE_DOMAIN}}" -#WEBMAIL_FQDN="${WEBMAIL_FQDN:-${WEBMAIL_SUB}.${BASE_DOMAIN}}" -#DKIM_ENABLE="${DKIM_ENABLE:-1}" -#DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}" -#DKIM_GENERATE="${DKIM_GENERATE:-1}" -# -## BASE_DOMAIN und Sub-Labels aus MTA/UI/WEBMAIL ableiten (robust) -#if [[ "$MTA_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then -# MTA_SUB="${BASH_REMATCH[1]}" -# BASE_DOMAIN="${BASH_REMATCH[2]}" -#fi -#if [[ "$UI_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then -# UI_SUB="${BASH_REMATCH[1]}" -#fi -#if [[ "$WEBMAIL_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then -# WEBMAIL_SUB="${BASH_REMATCH[1]}" -#fi -# -#SYSMAIL_SUB="${SYSMAIL_SUB:-sysmail}" -#SYSMAIL_DOMAIN="${SYSMAIL_SUB}.${BASE_DOMAIN}" -## Kanonische Host-Variablen (NIE wieder zusammenbauen – nimm die FQDNs) -#MAIL_HOSTNAME="${MTA_FQDN}" -#UI_HOST="${UI_FQDN}" -#WEBMAIL_HOST="${WEBMAIL_FQDN}" -# -## Zeitzone/Locale sinnvoll setzen -#APP_TZ="${APP_TZ:-$DEFAULT_TZ}" -#APP_LOCALE="${APP_LOCALE:-$DEFAULT_LOCALE}" -# -## ── Variablen exportieren ─────────────────────────────────────────────────── -#export APP_NAME APP_USER APP_GROUP APP_USER_PREFIX APP_DIR -#export BASE_DOMAIN UI_SUB WEBMAIL_SUB MTA_SUB -#export SYSMAIL_SUB SYSMAIL_DOMAIN DKIM_ENABLE DKIM_SELECTOR DKIM_GENERATE -#export UI_HOST WEBMAIL_HOST MAIL_HOSTNAME -#export DB_NAME DB_USER -#export SERVER_PUBLIC_IPV4 SERVER_PUBLIC_IPV6 APP_TZ APP_LOCALE -#export CLAMAV_ENABLE OPENDMARC_ENABLE FAIL2BAN_ENABLE -# -#install -d -m 0755 /etc/mailwolt -#cat >/etc/mailwolt/installer.env <>> Running ${STEP}.sh" -# bash "./${STEP}.sh" -#done -###!/usr/bin/env bash -##set -euo pipefail -## -### --- Flags / Modi --- -##DEV_MODE=0 -##PROXY_MODE=0 -##NPM_IP="" -## -##while [[ $# -gt 0 ]]; do -## case "$1" in -## -dev) DEV_MODE=1 ;; -## -proxy) PROXY_MODE=1; NPM_IP="${2:-}"; shift ;; -## esac -## shift -##done -## -##APP_ENV="${APP_ENV:-$([[ $DEV_MODE -eq 1 ]] && echo local || echo production)}" -##APP_DEBUG="${APP_DEBUG:-$([[ $DEV_MODE -eq 1 ]] && echo true || echo false)}" -##export DEV_MODE PROXY_MODE NPM_IP APP_ENV APP_DEBUG -## -##DB_PASS="${DB_PASS:-$(openssl rand -hex 16)}" -##REDIS_PASS="${REDIS_PASS:-$(openssl rand -hex 16)}" -## -##export DB_PASS REDIS_PASS -## -##cd "$(dirname "$0")" -##source ./lib.sh -##require_root -##header -## -### ── Defaults ──────────────────────────────────────────────────────────────── -##APP_NAME="${APP_NAME:-MailWolt}" -##APP_USER="${APP_USER:-mailwolt}" -##APP_GROUP="${APP_GROUP:-www-data}" -##APP_USER_PREFIX="${APP_USER_PREFIX:-mw}" -##APP_DIR="${APP_DIR:-/var/www/${APP_USER}}" -## -##BASE_DOMAIN="${BASE_DOMAIN:-example.com}" -##UI_SUB="${UI_SUB:-ui}" -##WEBMAIL_SUB="${WEBMAIL_SUB:-webmail}" -##MTA_SUB="${MTA_SUB:-mx}" -## -##DB_NAME="${DB_NAME:-${APP_USER}}" -##DB_USER="${DB_USER:-${APP_USER}}" -## -##SERVER_PUBLIC_IPV4="$(detect_ip)" -##SERVER_PUBLIC_IPV6="$(detect_ipv6)" -##DEFAULT_TZ="$(detect_timezone)" -##DEFAULT_LOCALE="$(guess_locale_from_tz "$DEFAULT_TZ")" -## -##echo -e "${GREY}Erkannte IP (v4): ${SERVER_PUBLIC_IPV4} v6: ${SERVER_PUBLIC_IPV6:-–}${NC}" -## -### ── FQDNs abfragen ─────────────────────────────────────────────────────────── -##read -r -p "Mailserver FQDN (MX, z.B. mx.domain.tld) [Enter=${MTA_SUB}.${BASE_DOMAIN}]: " MTA_FQDN -##read -r -p "UI / Admin-Panel FQDN (z.B. ui.domain.tld) [Enter=${UI_SUB}.${BASE_DOMAIN}]: " UI_FQDN -##read -r -p "Webmail FQDN (z.B. webmail.domain.tld) [Enter=${WEBMAIL_SUB}.${BASE_DOMAIN}]: " WEBMAIL_FQDN -## -### Defaults, wenn Enter gedrückt -##MTA_FQDN="${MTA_FQDN:-${MTA_SUB}.${BASE_DOMAIN}}" -##UI_FQDN="${UI_FQDN:-${UI_SUB}.${BASE_DOMAIN}}" -##WEBMAIL_FQDN="${WEBMAIL_FQDN:-${WEBMAIL_SUB}.${BASE_DOMAIN}}" -##DKIM_ENABLE="${DKIM_ENABLE:-1}" -##DKIM_SELECTOR="${DKIM_SELECTOR:-mwl1}" -##DKIM_GENERATE="${DKIM_GENERATE:-1}" -## -### BASE_DOMAIN und Sub-Labels aus MTA/UI/WEBMAIL ableiten (robust) -##if [[ "$MTA_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then -## MTA_SUB="${BASH_REMATCH[1]}" -## BASE_DOMAIN="${BASH_REMATCH[2]}" -##fi -##if [[ "$UI_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then -## UI_SUB="${BASH_REMATCH[1]}" -##fi -##if [[ "$WEBMAIL_FQDN" =~ ^([^.]+)\.(.+)$ ]]; then -## WEBMAIL_SUB="${BASH_REMATCH[1]}" -##fi -## -##SYSMAIL_SUB="${SYSMAIL_SUB:-sysmail}" -##SYSMAIL_DOMAIN="${SYSMAIL_SUB}.${BASE_DOMAIN}" -### Kanonische Host-Variablen (NIE wieder zusammenbauen – nimm die FQDNs) -##MAIL_HOSTNAME="${MTA_FQDN}" -##UI_HOST="${UI_FQDN}" -##WEBMAIL_HOST="${WEBMAIL_FQDN}" -## -### Zeitzone/Locale sinnvoll setzen (könntest du auch noch abfragen) -##APP_TZ="${APP_TZ:-$DEFAULT_TZ}" -##APP_LOCALE="${APP_LOCALE:-$DEFAULT_LOCALE}" -## -### ── Variablen exportieren ─────────────────────────────────────────────────── -##export APP_NAME APP_USER APP_GROUP APP_USER_PREFIX APP_DIR -##export BASE_DOMAIN UI_SUB WEBMAIL_SUB MTA_SUB -##export SYSMAIL_SUB SYSMAIL_DOMAIN DKIM_ENABLE DKIM_SELECTOR DKIM_GENERATE -##export UI_HOST WEBMAIL_HOST MAIL_HOSTNAME -##export DB_NAME DB_USER -##export SERVER_PUBLIC_IPV4 SERVER_PUBLIC_IPV6 APP_TZ APP_LOCALE -## -##install -d -m 0755 /etc/mailwolt -##cat >/etc/mailwolt/installer.env <>> Running ${STEP}.sh" -## bash "./${STEP}.sh" -##done \ No newline at end of file diff --git a/mailwolt-installer/scripts/install-wrapper.sh b/mailwolt-installer/scripts/install-wrapper.sh deleted file mode 100644 index 9d8040c..0000000 --- a/mailwolt-installer/scripts/install-wrapper.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# Mailwolt Installer-Wrapper -# Deploy to: /usr/local/sbin/mailwolt-install -# Permissions: chmod 0755, chown root:root -set -euo pipefail - -LOG="/var/log/mailwolt-install.log" -STATE_DIR="/var/lib/mailwolt/install" -INSTALLER_SCRIPT="/var/www/mailwolt/mailwolt-installer/install.sh" -APP_DIR="/var/www/mailwolt" - -install -d -m 0755 "$STATE_DIR" /var/lib/mailwolt /var/lib/mailwolt/wizard -chown www-data:www-data /var/lib/mailwolt/wizard -: > "$LOG" -chmod 0644 "$LOG" - -echo "running" > "$STATE_DIR/state" -: > "$STATE_DIR/rc" - -{ - echo "===== $(date -Is) :: Installation gestartet =====" - - if [[ "$(id -u)" -ne 0 ]]; then - echo "[!] Muss als root laufen" - printf '1\n' > "$STATE_DIR/rc" - echo "done" > "$STATE_DIR/state" - exit 1 - fi - - # Komponente aus $1, falls übergeben (z.B. "nginx", "postfix", "dovecot", "all") - COMPONENT="${1:-all}" - echo "[i] Komponente: $COMPONENT" - - RC=0 - if [[ -f "$INSTALLER_SCRIPT" ]]; then - APP_DIR="$APP_DIR" COMPONENT="$COMPONENT" bash "$INSTALLER_SCRIPT" || RC=$? - else - echo "[!] installer script nicht gefunden: $INSTALLER_SCRIPT" - RC=127 - fi - - echo "===== $(date -Is) :: Installation beendet (rc=$RC) =====" - printf '%s\n' "$RC" > "$STATE_DIR/rc" - echo "done" > "$STATE_DIR/state" - exit "$RC" -} 2>&1 | tee -a "$LOG" diff --git a/mailwolt-installer/scripts/lib.sh b/mailwolt-installer/scripts/lib.sh deleted file mode 100644 index cfa30c4..0000000 --- a/mailwolt-installer/scripts/lib.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -if [ -f /etc/mailwolt/installer.env ]; then - set -a - . /etc/mailwolt/installer.env - set +a -fi -# ── Styling ──────────────────────────────────────────────────────────────── -GREEN="$(printf '\033[1;32m')"; YELLOW="$(printf '\033[1;33m')" -RED="$(printf '\033[1;31m')"; CYAN="$(printf '\033[1;36m')" -GREY="$(printf '\033[0;90m')"; NC="$(printf '\033[0m')" -BAR="──────────────────────────────────────────────────────────────────────────────" -log(){ echo -e "${GREEN}[+]${NC} $*"; } -warn(){ echo -e "${YELLOW}[!]${NC} $*"; } -err(){ echo -e "${RED}[x]${NC} $*"; } -die(){ err "$*"; exit 1; } -require_root(){ [[ "$(id -u)" -eq 0 ]] || die "Bitte als root ausführen."; } - -# --- Defaults, nur wenn noch nicht gesetzt --------------------------------- -: "${APP_USER:=mailwolt}" -: "${APP_GROUP:=www-data}" -: "${APP_DIR:=/var/www/${APP_USER}}" - -: "${APP_NAME:=MailWolt}" - -: "${BASE_DOMAIN:=example.com}" -: "${UI_SUB:=ui}" -: "${WEBMAIL_SUB:=webmail}" -: "${MTA_SUB:=mx}" - -# DB / Redis (werden später durch .env überschrieben) -: "${DB_NAME:=${APP_USER}}" -: "${DB_USER:=${APP_USER}}" -: "${DB_PASS:=}" -: "${REDIS_PASS:=}" - -# Stabile Zert-Pfade (UI/WEBMAIL/MX → symlinked via 20-ssl.sh) -: "${MAIL_SSL_DIR:=/etc/ssl/mail}" -: "${UI_SSL_DIR:=/etc/ssl/ui}" -: "${WEBMAIL_SSL_DIR:=/etc/ssl/webmail}" -: "${UI_CERT:=${UI_SSL_DIR}/fullchain.pem}" -: "${UI_KEY:=${UI_SSL_DIR}/privkey.pem}" - -# Optional: E-Mail für LE -: "${LE_EMAIL:=admin@${BASE_DOMAIN}}" - -load_env_file(){ - local f="$1" - [[ -f "$f" ]] || return 0 - while IFS='=' read -r k v; do - [[ "$k" =~ ^[A-Z0-9_]+$ ]] || continue - export "$k=$v" - done < <(grep -E '^[A-Z0-9_]+=' "$f") -} - -header(){ echo -e "${CYAN}${BAR}${NC} -${CYAN} :::: :::: ::: ::::::::::: ::: ::: ::: :::::::: ::: ::::::::::: -${CYAN} +:+:+: :+:+:+ :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: -${CYAN} +:+ +:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ -${CYAN} +#+ +:+ +#+ +#++:++#++: +#+ +#+ +#+ +:+ +#+ +#+ +:+ +#+ +#+ -${CYAN} +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+#+ +#+ +#+ +#+ +#+ +#+ -${CYAN} #+# #+# #+# #+# #+# #+# #+#+# #+#+# #+# #+# #+# #+# -${CYAN} ### ### ### ### ########### ########## ### ### ######## ########## ### -${CYAN} ${CYAN}${BAR}${NC}\n"; } - -#header(){ echo -e "${CYAN}${BAR}${NC} -#${CYAN} 888b d888 d8b 888 888 888 888 888 -#${CYAN} 8888b d8888 Y8P 888 888 o 888 888 888 -#${CYAN} 88888b.d88888 888 888 d8b 888 888 888 -#${CYAN} 888Y88888P888 8888b. 888 888 888 d888b 888 .d88b. 888 888888 -#${CYAN} 888 Y888P 888 '88b 888 888 888d88888b888 d88''88b 888 888 -#${CYAN} 888 Y8P 888 .d888888 888 888 88888P Y88888 888 888 888 888 -#${CYAN} 888 ' 888 888 888 888 888 8888P Y8888 Y88..88P 888 Y88b. -#${CYAN} 888 888 'Y888888 888 888 888P Y888 'Y88P' 888 'Y888 -#${CYAN}${BAR}${NC}\n"; } - -detect_ip(){ - local ip - ip="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}')" || true - [[ -n "${ip:-}" ]] || ip="$(hostname -I 2>/dev/null | awk '{print $1}')" - [[ -n "${ip:-}" ]] || die "Konnte Server-IP nicht ermitteln." - echo "$ip" -} -detect_ipv4() { - local ext="" - if command -v curl >/dev/null 2>&1; then - ext="$(curl -fsS --max-time 2 https://ifconfig.me 2>/dev/null || true)" - [[ "$ext" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || ext="" - fi - echo "$ext" -} -detect_ipv6(){ - local ip6 - ip6="$(ip -6 addr show scope global 2>/dev/null | awk '/inet6/{print $2}' | cut -d/ -f1 | head -n1)" || true - [[ -n "${ip6:-}" ]] || ip6="$(hostname -I 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i ~ /:/){print $i; exit}}')" || true - echo "${ip6:-}" -} -detect_timezone(){ - local tz - if command -v timedatectl >/dev/null 2>&1; then - tz="$(timedatectl show -p Timezone --value 2>/dev/null | tr -d '[:space:]')" || true - [[ -n "${tz:-}" && "$tz" == */* ]] && { echo "$tz"; return; } - fi - [[ -r /etc/timezone ]] && { tz="$(sed -n '1p' /etc/timezone | tr -d '[:space:]')" || true; [[ "$tz" == */* ]] && { echo "$tz"; return; }; } - if [[ -L /etc/localtime ]]; then - tz="$(readlink -f /etc/localtime 2>/dev/null || true)"; tz="${tz#/usr/share/zoneinfo/}" - [[ "$tz" == */* ]] && { echo "$tz"; return; } - fi - if command -v curl >/dev/null 2>&1; then - tz="$(curl -fsSL --max-time 3 https://ipapi.co/timezone 2>/dev/null || true)"; [[ "$tz" == */* ]] && { echo "$tz"; return; } - fi - echo "UTC" -} -guess_locale_from_tz(){ case "${1:-UTC}" in - Europe/Berlin|Europe/Vienna|Europe/Zurich|Europe/Luxembourg|Europe/Brussels|Europe/Amsterdam) echo "de";; - *) echo "en";; esac; } - -resolve_ok(){ local host="$1"; getent ahosts "$host" | awk '{print $1}' | sort -u | grep -q -F "${SERVER_PUBLIC_IPV4:-}" ; } -join_host(){ local sub="$1" base="$2"; [[ -z "$sub" ]] && echo "$base" || echo "$sub.$base"; } - -# dns_preflight HOST [HOST2 ...] -# Prüft: A-Record → SERVER_PUBLIC_IPV4, MX (nur wenn HOST == MAIL_HOSTNAME), PTR. -# Gibt strukturierte Zeilen aus: OK|WARN|FAIL -# Rückgabe 0 = alles OK; 1 = mind. ein FAIL. -dns_preflight(){ - local overall=0 - local server_ip="${SERVER_PUBLIC_IPV4:-}" - - _dns_line(){ local level="$1" host="$2" check="$3" detail="$4" - case "$level" in - OK) echo -e "${GREEN}[DNS OK ]${NC} ${host} ${GREY}${check}${NC} → ${detail}" ;; - WARN) echo -e "${YELLOW}[DNS WARN]${NC} ${host} ${GREY}${check}${NC} → ${detail}" ;; - FAIL) echo -e "${RED}[DNS FAIL]${NC} ${host} ${GREY}${check}${NC} → ${detail}"; overall=1 ;; - esac - } - - for host in "$@"; do - [[ -z "$host" || "$host" == "example.com" ]] && continue - - # A-Record - local a_ip - a_ip="$(dig +short A "$host" @1.1.1.1 2>/dev/null | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n1)" - if [[ -z "$a_ip" ]]; then - _dns_line FAIL "$host" "A-Record" "kein Eintrag gefunden" - elif [[ -n "$server_ip" && "$a_ip" != "$server_ip" ]]; then - _dns_line FAIL "$host" "A-Record" "${a_ip} ≠ ${server_ip} (Server-IP)" - else - _dns_line OK "$host" "A-Record" "${a_ip}" - fi - - # MX (nur für MAIL_HOSTNAME) - if [[ "$host" == "${MAIL_HOSTNAME:-}" ]]; then - local mx - mx="$(dig +short MX "$host" @1.1.1.1 2>/dev/null | awk '{print $2}' | head -n1)" - if [[ -z "$mx" ]]; then - _dns_line WARN "$host" "MX-Record" "kein Eintrag – ausgehende Mail ggf. eingeschränkt" - else - _dns_line OK "$host" "MX-Record" "${mx}" - fi - fi - - # PTR (nur wenn IP bekannt) - if [[ -n "$server_ip" && -n "$a_ip" && "$a_ip" == "$server_ip" ]]; then - local ptr - ptr="$(dig +short -x "$server_ip" @1.1.1.1 2>/dev/null | head -n1 | sed 's/\.$//')" - if [[ -z "$ptr" ]]; then - _dns_line WARN "$host" "PTR-Record" "kein Reverse-DNS – kann Spam-Score erhöhen" - elif [[ "$ptr" != "$host" && "$ptr" != "${MAIL_HOSTNAME:-}" ]]; then - _dns_line WARN "$host" "PTR-Record" "${ptr} (zeigt nicht auf ${host})" - else - _dns_line OK "$host" "PTR-Record" "${ptr}" - fi - fi - done - - return $overall -} - -upsert_env(){ # upsert in $ENV_FILE - local k="$1" v="$2" ek ev - ek="$(printf '%s' "$k" | sed -e 's/[.[\*^$(){}+?|/]/\\&/g')" - ev="$(printf '%s' "$v" | sed -e 's/[&/]/\\&/g')" - if grep -qE "^[#[:space:]]*${ek}=" "$ENV_FILE" 2>/dev/null; then - sed -Ei "s|^[#[:space:]]*${ek}=.*|${k}=${ev}|g" "$ENV_FILE" - else - printf '%s=%s\n' "$k" "$v" >> "$ENV_FILE" - fi -} diff --git a/mailwolt-installer/scripts/phase2-go-live.sh b/mailwolt-installer/scripts/phase2-go-live.sh deleted file mode 100755 index af9692c..0000000 --- a/mailwolt-installer/scripts/phase2-go-live.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash -# Phase 2: Go-Live — DNS-Check → LE-Zertifikate → Nginx → Postfix/Dovecot aktualisieren -# Muss als root ausgeführt werden, NACHDEM die DNS-Einträge gesetzt wurden. -# Liest Konfiguration aus /etc/mailwolt/installer.env (durch Phase 1 geschrieben). -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/lib.sh" - -require_root - -ENV_FILE="/etc/mailwolt/installer.env" -[[ -f "$ENV_FILE" ]] || die "Phase 1 noch nicht abgeschlossen – ${ENV_FILE} fehlt." - -set -a; . "$ENV_FILE"; set +a - -# ── Pflichtfelder ───────────────────────────────────────────────────────────── -: "${BASE_DOMAIN:?BASE_DOMAIN fehlt in ${ENV_FILE}}" -: "${UI_HOST:?UI_HOST fehlt in ${ENV_FILE}}" -: "${WEBMAIL_HOST:?WEBMAIL_HOST fehlt in ${ENV_FILE}}" -: "${MAIL_HOSTNAME:?MAIL_HOSTNAME fehlt in ${ENV_FILE}}" -: "${SERVER_PUBLIC_IPV4:?SERVER_PUBLIC_IPV4 fehlt in ${ENV_FILE}}" - -header -echo -e "${CYAN} Phase 2 – Go-Live${NC}" -echo -e "${CYAN}${BAR}${NC}" -echo - -# ── Schritt 1: DNS Preflight ────────────────────────────────────────────────── -log "DNS-Vorab-Check …" - -SKIP_DNS="${SKIP_DNS:-0}" -dns_ok=0 - -if [[ "$SKIP_DNS" != "1" ]]; then - if dns_preflight "$UI_HOST" "$WEBMAIL_HOST" "$MAIL_HOSTNAME"; then - dns_ok=1 - else - warn "Einige DNS-Einträge zeigen noch nicht auf diesen Server." - echo - echo -e " Möglichkeiten:" - echo -e " a) DNS reparieren und dieses Skript erneut ausführen." - echo -e " b) Trotzdem fortfahren: SKIP_DNS=1 bash phase2-go-live.sh" - echo - read -rp "Trotzdem fortfahren? [j/N] " _ans - [[ "${_ans,,}" == "j" ]] || die "Abgebrochen." - dns_ok=1 - fi -else - warn "DNS-Check übersprungen (SKIP_DNS=1)." - dns_ok=1 -fi - -echo - -# ── Schritt 2: Let's Encrypt Zertifikate ───────────────────────────────────── -log "Let's Encrypt Zertifikate ausstellen …" -bash "${SCRIPT_DIR}/75-le-issue.sh" -echo - -# ── Schritt 3: Nginx-Konfiguration neu schreiben (TLS) ─────────────────────── -log "Nginx-Konfiguration aktualisieren (TLS) …" -# Nginx-Builder aus 70-nginx.sh wiederverwenden -source "${SCRIPT_DIR}/70-nginx.sh" || true # sourcing setzt Variablen und führt aus -echo - -# ── Schritt 4: Postfix hostname + TLS-Zertifikate aktualisieren ────────────── -log "Postfix: myhostname = ${MAIL_HOSTNAME} …" -postconf -e "myhostname = ${MAIL_HOSTNAME}" -postconf -e "myorigin = \$myhostname" -postconf -e "smtpd_tls_cert_file = /etc/ssl/mail/fullchain.pem" -postconf -e "smtpd_tls_key_file = /etc/ssl/mail/privkey.pem" -systemctl reload postfix || true - -# ── Schritt 5: Dovecot TLS ─────────────────────────────────────────────────── -log "Dovecot: TLS-Zertifikate aktualisieren …" -if [[ -f /etc/dovecot/conf.d/10-ssl.conf ]]; then - sed -i "s|^ssl_cert =.*|ssl_cert = /dev/null || systemctl restart "$u" - return 0 - fi - done -} - -artisan_down(){ - as_app "cd ${APP_DIR} && php artisan down --retry=10 --render=errors.503" 2>/dev/null || true - echo "[i] Wartungsmodus aktiviert" -} - -artisan_up(){ - as_app "cd ${APP_DIR} && php artisan up" 2>/dev/null || true - echo "[i] Wartungsmodus beendet" -} - -git_safe(){ - # Falls nötig: Repo als safe markieren (z.B. wenn Root den Wrapper aufruft) - as_app "git -C ${APP_DIR} config --global --add safe.directory ${APP_DIR} >/dev/null 2>&1 || true" -} - -git_dirty_check(){ - if [[ "$ALLOW_DIRTY" != "1" ]]; then - local dirty - dirty="$(as_app "git -C ${APP_DIR} status --porcelain")" - if [[ -n "$dirty" ]]; then - echo "[!] Arbeitsbaum hat uncommitted Änderungen. Abbruch (ALLOW_DIRTY=1 zum Überschreiben)." - exit 2 - fi - fi -} - -get_version(){ - as_app "cd ${APP_DIR} && (git describe --tags --always 2>/dev/null || git rev-parse --short=7 HEAD)" -} - -write_build_info(){ - local ver="$1" rev="$2" - install -d -m 0755 /etc/mailwolt || true - printf "version=%s\nrev=%s\nupdated=%s\n" "$ver" "$rev" "$(date -Is)" > /etc/mailwolt/build.info || true -} - -# --- Frontend build (quiet & robust) ------------------------------------------ -frontend_build_quiet() { - local LOG="/var/log/mailwolt-frontend-build.log" - local NPM_ENV="CI=1 NPM_CONFIG_FUND=false NPM_CONFIG_AUDIT=false npm_config_loglevel=warn" - - # Logfile vorbereiten - install -d -m 0755 "$(dirname "$LOG")" - : > "$LOG" || true - chmod 0644 "$LOG" - - echo "[i] Frontend: vorbereiten …" - # Build-Ziele & Temp besitzbar machen - as_app "mkdir -p '${APP_DIR}/public/build' '${APP_DIR}/node_modules' '${APP_DIR}/.vite' '${APP_DIR}/.npm-cache'" - chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" "${APP_DIR}/.npm-cache" || true - chmod -R g+rwX "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" || true - - # evtl. störrische Reste entfernen (vermeidet EACCES beim Unlink/Rimraf) - rm -rf "${APP_DIR}/node_modules/.vite" "${APP_DIR}/public/build/"* 2>/dev/null || true - - # npm Config (kein Fund/Audit, eigener Cache unter App-User) - as_app "printf 'fund=false\naudit=false\ncache=${APP_DIR}/.npm-cache\n' > ~/.npmrc" >>"$LOG" 2>&1 || true - - echo "[i] Frontend: Dependencies … (Details: $LOG)" - if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm ci --no-audit --no-fund --no-progress" >>"$LOG" 2>&1; then - if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm install --no-audit --no-fund --no-progress" >>"$LOG" 2>&1; then - echo "[!] npm install fehlgeschlagen. Letzte 60 Zeilen:" - tail -n 60 "$LOG" || true - return 1 - fi - fi - - echo "[i] Frontend: Build … (Details: $LOG)" - if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm run build --silent --loglevel=warn" >>"$LOG" 2>&1; then - echo "[!] Build fehlgeschlagen. Letzte 80 Zeilen:" - tail -n 80 "$LOG" || true - return 1 - fi - - # Nach dem Build noch einmal Rechte begradigen - chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}/public/build" || true - find "${APP_DIR}/public/build" -type d -exec chmod 2775 {} \; 2>/dev/null || true - find "${APP_DIR}/public/build" -type f -exec chmod 0664 {} \; 2>/dev/null || true - - echo "[✓] Frontend gebaut." -} - -# -------- Guards -------------------------------------------------------------- -[[ "$(id -u)" -eq 0 ]] || { echo "[!] Bitte als root ausführen"; exit 1; } -[[ -d "$APP_DIR/.git" ]] || { echo "[!] $APP_DIR scheint kein Git-Repo zu sein"; exit 1; } - -git_safe -git_dirty_check - -# -------- Git: neuen Stand holen --------------------------------------------- -echo "[i] Prüfe Repository …" -OLD_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")" -OLD_VER="$(get_version)" -NEW_REV="$OLD_REV" - -if [[ "$MODE" = "tags" ]]; then - # → Neueste Tags holen - as_app "git -C ${APP_DIR} fetch --quiet origin && git -C ${APP_DIR} fetch --tags --quiet origin || true" - LATEST_TAG="$(as_app "git -C ${APP_DIR} tag --list | sort -V | tail -n1")" - if [[ -z "$LATEST_TAG" ]]; then - echo "[!] Keine Tags gefunden – falle auf origin/${BRANCH} zurück" - as_app "git -C ${APP_DIR} checkout -q ${BRANCH} && git -C ${APP_DIR} pull --ff-only origin ${BRANCH}" - else - TARGET_REV="$(as_app "git -C ${APP_DIR} rev-list -n1 ${LATEST_TAG}")" - if [[ "$TARGET_REV" = "$OLD_REV" ]]; then - echo "[✓] Bereits auf neuestem Release (${LATEST_TAG}) – nichts zu tun." - write_build_info "$(get_version)" "$OLD_REV" - exit 0 - fi - echo "[i] Checkout auf Release ${LATEST_TAG} (${TARGET_REV:0:7}) …" - as_app "git -C ${APP_DIR} checkout -q ${LATEST_TAG}" - fi - NEW_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")" -else - # Rolling: branch folgen - as_app "git -C ${APP_DIR} fetch --quiet origin ${BRANCH}" - BEHIND="$(as_app "git -C ${APP_DIR} rev-list --count HEAD..origin/${BRANCH} || echo 0")" - if [[ "$BEHIND" -eq 0 ]]; then - echo "[✓] Branch origin/${BRANCH} ist bereits aktuell – nichts zu tun." - write_build_info "$(get_version)" "$OLD_REV" - exit 0 - fi - echo "[i] Es gibt ${BEHIND} neue Commit(s) – ziehe Änderungen …" - as_app "git -C ${APP_DIR} checkout -q ${BRANCH} && git -C ${APP_DIR} pull --ff-only origin ${BRANCH}" - NEW_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")" -fi - -# -------- Änderungstypen ermitteln ------------------------------------------- -CHANGED_FILES="$(as_app "git -C ${APP_DIR} diff --name-only ${OLD_REV}..${NEW_REV}")" - -NEED_COMPOSER=0 -NEED_MIGRATIONS=0 -NEED_FRONTEND=0 -NEED_PHP_RESTART=0 - -echo "$CHANGED_FILES" | grep -qE '(^|/)composer\.(json|lock)$' && NEED_COMPOSER=1 -echo "$CHANGED_FILES" | grep -qE '^database/migrations/' && NEED_MIGRATIONS=1 -echo "$CHANGED_FILES" | grep -qE '^(package(-lock)?\.json|vite\.config(\.ts|\.js)?|resources/|public/.*\.(js|css))' && NEED_FRONTEND=1 -echo "$CHANGED_FILES" | grep -qE '^(app/|routes/|config/|resources/views/)' && NEED_PHP_RESTART=1 - -echo "[i] Zusammenfassung:" -echo " Composer : $([[ $NEED_COMPOSER -eq 1 ]] && echo JA || echo nein)" -echo " Migrations : $([[ $NEED_MIGRATIONS -eq 1 ]] && echo JA || echo nein)" -echo " Frontend : $([[ $NEED_FRONTEND -eq 1 ]] && echo JA || echo nein)" -echo " PHP restart : $([[ $NEED_PHP_RESTART -eq 1 ]] && echo JA || echo nein)" - -# Wenn gar nichts relevantes geändert wurde → sauber beenden -if [[ $NEED_COMPOSER -eq 0 && $NEED_MIGRATIONS -eq 0 && $NEED_FRONTEND -eq 0 && $NEED_PHP_RESTART -eq 0 ]]; then - echo "[✓] Code-Stand aktualisiert, aber keine Build/Runtime-Änderungen – keine Neustarts nötig." - NEW_VER="$(get_version)" - write_build_info "$NEW_VER" "$NEW_REV" - echo "[i] Version: ${OLD_VER} → ${NEW_VER}" - exit 0 -fi - -# -------- Composer (ohne Downtime) ------------------------------------------- -if [[ $NEED_COMPOSER -eq 1 ]]; then - echo "[i] Composer …" - as_app "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev" -fi - -# -------- Frontend (ohne Downtime — Assets sind statisch) -------------------- -if [[ $NEED_FRONTEND -eq 1 ]]; then - echo "[i] Frontend-Änderungen erkannt – baue Assets …" - if ! frontend_build_quiet; then - echo "[!] Frontend-Build ist fehlgeschlagen (siehe /var/log/mailwolt-frontend-build.log)." - exit 1 - fi -fi - -# -------- Wartungsmodus: Migrations + Cache + PHP-FPM reload ----------------- -# Trap stellt sicher dass artisan up auch bei Fehler läuft -MAINTENANCE_ACTIVE=0 -cleanup_maintenance(){ - if [[ "$MAINTENANCE_ACTIVE" -eq 1 ]]; then - artisan_up - fi -} -trap cleanup_maintenance EXIT INT TERM - -if [[ $NEED_MIGRATIONS -eq 1 || $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 ]]; then - artisan_down - MAINTENANCE_ACTIVE=1 -fi - -if [[ $NEED_MIGRATIONS -eq 1 ]]; then - echo "[i] DB-Migrationen …" - as_app "cd ${APP_DIR} && php artisan migrate --force" -fi - -if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then - echo "[i] Cache/Optimierungen …" - as_app "cd ${APP_DIR} && php artisan optimize:clear || true" - as_app "cd ${APP_DIR} && php artisan config:cache || true" - as_app "cd ${APP_DIR} && php artisan route:cache || true" - as_app "cd ${APP_DIR} && php artisan queue:restart || true" -fi - -# -------- Dienste neu laden (gezielt) ---------------------------------------- -echo "[i] Dienste neu laden …" -if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then - restart_php_fpm - restart_if_exists "${APP_USER}-queue" - restart_if_exists "${APP_USER}-schedule" - restart_if_exists "${APP_USER}-ws" -fi -if [[ $NEED_FRONTEND -eq 1 || $NEED_PHP_RESTART -eq 1 ]]; then - reload_if_active nginx -fi - -# -------- Wartungsmodus beenden ---------------------------------------------- -if [[ "$MAINTENANCE_ACTIVE" -eq 1 ]]; then - artisan_up - MAINTENANCE_ACTIVE=0 -fi - -# -------- Build-Info ablegen -------------------------------------------------- -NEW_VER="$(get_version)" -write_build_info "$NEW_VER" "$NEW_REV" - -echo "[✓] Update abgeschlossen: ${OLD_REV:0:7} → ${NEW_REV:0:7} (Version: ${NEW_VER})" \ No newline at end of file diff --git a/mailwolt-installer/tools/push.sh b/mailwolt-installer/tools/push.sh deleted file mode 100644 index 67456ba..0000000 --- a/mailwolt-installer/tools/push.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -MSG="" -BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo main)" -REMOTE="${REMOTE:-origin}" - -usage(){ echo "Usage: $0 -m \"commit message\" [-b branch]"; exit 1; } - -while getopts ":m:b:" opt; do - case "$opt" in - m) MSG="$OPTARG" ;; - b) BRANCH="$OPTARG" ;; - *) usage ;; - esac -done -shift $((OPTIND-1)) - -[[ -n "$MSG" ]] || usage -git rev-parse --git-dir >/dev/null 2>&1 || { echo "[x] kein Git-Repo"; exit 1; } - -echo "[i] Add/Commit → $BRANCH" -git add -A -# Kein Commit, wenn nichts geändert ist -if ! git diff --cached --quiet; then - git commit -m "$MSG" -else - echo "[i] Nichts zu committen (Index leer) – pushe nur." -fi - -echo "[i] Push → $REMOTE/$BRANCH" -git push "$REMOTE" "$BRANCH" -echo "[✓] Done." \ No newline at end of file diff --git a/mailwolt-installer/tools/release.sh b/mailwolt-installer/tools/release.sh deleted file mode 100644 index 75869cb..0000000 --- a/mailwolt-installer/tools/release.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -REMOTE="${REMOTE:-origin}" -BRANCH="${BRANCH:-main}" -ALLOW_DIRTY=0 -VERSION="" -MSG="" - -usage(){ - echo "Usage: $0 [-m \"release notes\"] [-b branch] [--allow-dirty]" - exit 1 -} - -# Args -[[ $# -ge 1 ]] || usage -VERSION="$1"; shift || true -[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "[x] Ungültige Version: $VERSION (erwarte SemVer X.Y.Z)"; exit 1; } - -while [[ $# -gt 0 ]]; do - case "$1" in - -m) MSG="$2"; shift 2 ;; - -b) BRANCH="$2"; shift 2 ;; - --allow-dirty) ALLOW_DIRTY=1; shift ;; - *) usage ;; - esac -done - -git rev-parse --git-dir >/dev/null 2>&1 || { echo "[x] kein Git-Repo"; exit 1; } - -# Clean tree? -if [[ $ALLOW_DIRTY -eq 0 ]]; then - if ! git diff --quiet || ! git diff --cached --quiet; then - echo "[x] Arbeitsbaum nicht sauber. Committe oder nutze --allow-dirty." - exit 1 - fi -fi - -# Aktuellen Stand holen -git fetch --quiet "$REMOTE" --tags -# Branch check-out/sync -git checkout -q "$BRANCH" -git pull --ff-only "$REMOTE" "$BRANCH" - -TAG="v${VERSION}" -if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then - echo "[x] Tag ${TAG} existiert bereits." - exit 1 -fi - -LAST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)" -if [[ -n "$LAST_TAG" ]]; then - CHANGELOG="$(git log --pretty=format:'- %s (%h)' "${LAST_TAG}..HEAD" || true)" -else - CHANGELOG="$(git log --pretty=format:'- %s (%h)' || true)" -fi -[[ -n "$CHANGELOG" ]] || CHANGELOG="- initial release content" - -# VERSION bumpen -echo -n "$VERSION" > VERSION -git add VERSION -git commit -m "chore(release): v${VERSION}" - -# Tag-Message bauen -if [[ -z "$MSG" ]]; then -read -r -d '' MSG <