Refactor: Installer-Output auf saubere Schritt-Anzeige umgestellt

- Nur Hauptschritte mit Zeitschätzung sichtbar (~Pakete 2-5 Min, etc.)
- Alle verbose Ausgaben (apt/composer/npm/git) gehen in /var/log/mailwolt-install.log
- Bei Fehler: letzte 20 Log-Zeilen werden angezeigt + Log-Pfad
- quietly()/try_quiet() Helper für stille Ausführung
- Smoke-Test zeigt nur OK/⚠ pro Port ohne verbose openssl-Output
- Node/npm wird nach Git-Clone installiert (package.json bereits vorhanden)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
boban 2026-04-24 13:27:31 +02:00
parent 814776d1ff
commit eb16f7d6ad
1 changed files with 134 additions and 119 deletions

View File

@ -44,6 +44,10 @@ NODE_SETUP="${NODE_SETUP:-deb}"
GREEN="\033[1;32m"; YELLOW="\033[1;33m"; RED="\033[1;31m"; CYAN="\033[1;36m"; GREY="\033[0;90m"; NC="\033[0m" GREEN="\033[1;32m"; YELLOW="\033[1;33m"; RED="\033[1;31m"; CYAN="\033[1;36m"; GREY="\033[0;90m"; NC="\033[0m"
BAR="──────────────────────────────────────────────────────────────────────────────" BAR="──────────────────────────────────────────────────────────────────────────────"
# ===== Install-Log =====
LOG_FILE="/var/log/mailwolt-install.log"
> "$LOG_FILE"
header() { header() {
echo -e "${CYAN}${BAR}${NC}" echo -e "${CYAN}${BAR}${NC}"
echo -e "${CYAN} 888b d888 d8b 888 888 888 888 888 ${NC}" echo -e "${CYAN} 888b d888 d8b 888 888 888 888 888 ${NC}"
@ -81,9 +85,30 @@ footer_ok() {
echo echo
} }
log() { echo -e "${GREEN}[+]${NC} $*"; } _STEP_T=0
warn() { echo -e "${YELLOW}[!]${NC} $*"; } step() {
err() { echo -e "${RED}[x]${NC} $*"; } local msg="$1" dur="${2:-}"
[ -n "$dur" ] && dur=" ${GREY}(~${dur})${NC}" || dur=""
printf "\n${CYAN}${NC} %-42s%b\n" "$msg" "$dur"
_STEP_T=$SECONDS
}
ok() {
local t=$(( SECONDS - _STEP_T ))
[ $t -gt 1 ] && printf " ${GREEN}${NC} ${GREY}%ds${NC}\n" $t || printf " ${GREEN}${NC}\n"
}
warn() { printf " ${YELLOW}${NC} %s\n" "$*"; }
err() { printf " ${RED}${NC} %s\n" "$*"; }
quietly() {
if ! "$@" >> "$LOG_FILE" 2>&1; then
printf "\n ${RED}${NC} Fehlgeschlagen. Letzte Log-Zeilen:\n\n"
tail -20 "$LOG_FILE" | sed 's/^/ /'
printf "\n ${GREY}Vollständiges Log: %s${NC}\n\n" "$LOG_FILE"
exit 1
fi
}
try_quiet() { "$@" >> "$LOG_FILE" 2>&1 || true; }
log() { :; } # Kompatibilität — stille Ausgabe
require_root() { [ "$(id -u)" -eq 0 ] || { err "Bitte als root ausführen."; exit 1; }; } require_root() { [ "$(id -u)" -eq 0 ] || { err "Bitte als root ausführen."; exit 1; }; }
# ===== IP ermitteln ===== # ===== IP ermitteln =====
@ -118,22 +143,19 @@ APP_PW="${APP_PW:-$(pw)}"
MAIL_HOSTNAME="${MAIL_HOSTNAME:-"bootstrap.local"}" # Wizard setzt später FQDN MAIL_HOSTNAME="${MAIL_HOSTNAME:-"bootstrap.local"}" # Wizard setzt später FQDN
TZ="${TZ:-""}" # leer; Wizard setzt final TZ="${TZ:-""}" # leer; Wizard setzt final
echo -e "${GREY}Server-IP erkannt: ${SERVER_IP}${NC}" echo -e "\n ${GREY}Server-IP: ${SERVER_IP} Log: ${LOG_FILE}${NC}"
[ -n "$TZ" ] && { ln -fs "/usr/share/zoneinfo/${TZ}" /etc/localtime || true; } [ -n "$TZ" ] && { ln -fs "/usr/share/zoneinfo/${TZ}" /etc/localtime || true; }
log "Paketquellen aktualisieren…" step "Paketquellen aktualisieren" "10 Sek"
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt-get update -y quietly apt-get update -y
ok
# ---- MariaDB-Workaround (fix für mariadb-common prompt) ----
log "MariaDB-Workaround vorbereiten…"
mkdir -p /etc/mysql /etc/mysql/mariadb.conf.d 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 [ -f /etc/mysql/mariadb.cnf ] || echo '!include /etc/mysql/mariadb.conf.d/*.cnf' > /etc/mysql/mariadb.cnf
# ---- Basis-Pakete installieren ---- step "Pakete installieren" "25 Min"
log "Pakete installieren… (dies kann einige Minuten dauern)" quietly apt-get -y -o Dpkg::Options::="--force-confdef" \
#apt-get install -y \
apt-get -y -o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" install \ -o Dpkg::Options::="--force-confold" install \
postfix postfix-mysql \ postfix postfix-mysql \
dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql \ dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql \
@ -147,15 +169,15 @@ apt-get -y -o Dpkg::Options::="--force-confdef" \
certbot python3-certbot-nginx \ certbot python3-certbot-nginx \
fail2ban \ fail2ban \
ca-certificates rsyslog sudo openssl netcat-openbsd monit git ca-certificates rsyslog sudo openssl netcat-openbsd monit git
ok
# ===== Verzeichnisse / User ===== step "Benutzer & Verzeichnisse anlegen" "5 Sek"
log "Verzeichnisse und Benutzer anlegen…"
mkdir -p ${CERT_DIR} /etc/postfix /etc/dovecot/conf.d /etc/rspamd/local.d /var/mail/vhosts mkdir -p ${CERT_DIR} /etc/postfix /etc/dovecot/conf.d /etc/rspamd/local.d /var/mail/vhosts
id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail quietly bash -c "id vmail >/dev/null 2>&1 || adduser --system --group --home /var/mail vmail"
chown -R vmail:vmail /var/mail chown -R vmail:vmail /var/mail
quietly bash -c "id '$APP_USER' >/dev/null 2>&1 || adduser --disabled-password --gecos '' '$APP_USER'"
id "$APP_USER" >/dev/null 2>&1 || adduser --disabled-password --gecos "" "$APP_USER" quietly usermod -a -G www-data "$APP_USER"
usermod -a -G www-data "$APP_USER" ok
# ===== Self-signed TLS (SAN = IP) ===== # ===== Self-signed TLS (SAN = IP) =====
CERT="${CERT_DIR}/cert.pem" CERT="${CERT_DIR}/cert.pem"
@ -163,7 +185,7 @@ KEY="${CERT_DIR}/key.pem"
OSSL_CFG="${CERT_DIR}/openssl.cnf" OSSL_CFG="${CERT_DIR}/openssl.cnf"
if [ ! -s "$CERT" ] || [ ! -s "$KEY" ]; then if [ ! -s "$CERT" ] || [ ! -s "$KEY" ]; then
log "Erzeuge Self-Signed TLS Zertifikat (SAN=IP:${SERVER_IP})…" step "Self-Signed Zertifikat erstellen" "5 Sek"
cat > "$OSSL_CFG" <<CFG cat > "$OSSL_CFG" <<CFG
[req] [req]
default_bits = 2048 default_bits = 2048
@ -183,24 +205,26 @@ subjectAltName = @alt_names
[alt_names] [alt_names]
IP.1 = ${SERVER_IP} IP.1 = ${SERVER_IP}
CFG CFG
openssl req -x509 -newkey rsa:2048 -days 825 -nodes \ quietly openssl req -x509 -newkey rsa:2048 -days 825 -nodes \
-keyout "$KEY" -out "$CERT" -config "$OSSL_CFG" -keyout "$KEY" -out "$CERT" -config "$OSSL_CFG"
chmod 600 "$KEY"; chmod 644 "$CERT" chmod 600 "$KEY"; chmod 644 "$CERT"
ok
fi fi
# ===== MariaDB vorbereiten ===== # ===== MariaDB vorbereiten =====
log "MariaDB vorbereiten…" step "Datenbank einrichten" "10 Sek"
systemctl enable --now mariadb quietly systemctl enable --now mariadb
DB_PASS="$(pw)" DB_PASS="$(pw)"
mysql -uroot <<SQL quietly mysql -uroot <<SQL
CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}'; CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost'; GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES; FLUSH PRIVILEGES;
SQL SQL
ok
# ===== Postfix konfigurieren (25/465/587) ===== # ===== Postfix konfigurieren (25/465/587) =====
log "Postfix konfigurieren…" step "Mailserver konfigurieren (Postfix / Dovecot / Rspamd)" "15 Sek"
postconf -e "myhostname = ${MAIL_HOSTNAME}" postconf -e "myhostname = ${MAIL_HOSTNAME}"
postconf -e "myorigin = \$myhostname" postconf -e "myorigin = \$myhostname"
postconf -e "mydestination = " postconf -e "mydestination = "
@ -279,10 +303,9 @@ CONF
chown root:postfix /etc/postfix/sql/mysql-virtual-alias-maps.cf chown root:postfix /etc/postfix/sql/mysql-virtual-alias-maps.cf
chmod 640 /etc/postfix/sql/mysql-virtual-alias-maps.cf chmod 640 /etc/postfix/sql/mysql-virtual-alias-maps.cf
systemctl enable --now postfix || true try_quiet systemctl enable --now postfix
# ===== Dovecot konfigurieren (IMAP/POP3 + SSL) ===== # ===== Dovecot konfigurieren (IMAP/POP3 + SSL) =====
log "Dovecot konfigurieren…"
cat > /etc/dovecot/dovecot.conf <<'CONF' cat > /etc/dovecot/dovecot.conf <<'CONF'
!include_try /etc/dovecot/conf.d/*.conf !include_try /etc/dovecot/conf.d/*.conf
CONF CONF
@ -358,15 +381,14 @@ ssl_cert = <${CERT}
ssl_key = <${KEY} ssl_key = <${KEY}
CONF CONF
systemctl enable --now dovecot || true try_quiet systemctl enable --now dovecot
# ===== Rspamd & OpenDKIM ===== # ===== Rspamd & OpenDKIM =====
log "Rspamd + OpenDKIM aktivieren…"
cat > /etc/rspamd/local.d/worker-controller.inc <<'CONF' cat > /etc/rspamd/local.d/worker-controller.inc <<'CONF'
password = "admin"; password = "admin";
bind_socket = "127.0.0.1:11334"; bind_socket = "127.0.0.1:11334";
CONF CONF
systemctl enable --now rspamd || true try_quiet systemctl enable --now rspamd
cat > /etc/opendkim.conf <<'CONF' cat > /etc/opendkim.conf <<'CONF'
Syslog yes Syslog yes
@ -382,13 +404,12 @@ LogWhy yes
OversignHeaders From OversignHeaders From
# KeyTable / SigningTable später im Wizard # KeyTable / SigningTable später im Wizard
CONF CONF
systemctl enable --now opendkim || true try_quiet systemctl enable --now opendkim
try_quiet systemctl enable --now redis-server
# ===== Redis ===== ok
systemctl enable --now redis-server || true
# ===== Nginx: Laravel vHost (80/443) ===== # ===== Nginx: Laravel vHost (80/443) =====
log "Nginx konfigurieren…" step "Webserver konfigurieren (Nginx)" "5 Sek"
rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default || true rm -f /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default || true
PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
@ -455,33 +476,23 @@ server {
} }
CONF CONF
ln -sf ${NGINX_SITE} ${NGINX_SITE_LINK} ln -sf ${NGINX_SITE} ${NGINX_SITE_LINK}
nginx -t && systemctl enable --now nginx || true try_quiet nginx -t
try_quiet systemctl enable --now nginx
ok
# ===== Node/NPM installieren (für Vite/Tailwind Build) ===== step "Projekt herunterladen (Git)" "15 Sek"
log "Node/NPM installieren…"
if [ "$NODE_SETUP" = "nodesource" ]; then
curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh
bash /tmp/nodesource_setup.sh
rm -f /tmp/nodesource_setup.sh
apt-get install -y nodejs
else
apt-get install -y nodejs npm
fi
# ===== Projekt aus Git holen =====
log "Projekt aus Git klonen…"
mkdir -p "$(dirname "$APP_DIR")" mkdir -p "$(dirname "$APP_DIR")"
chown "$APP_USER":$APP_GROUP "$(dirname "$APP_DIR")" chown "$APP_USER":$APP_GROUP "$(dirname "$APP_DIR")"
if [ ! -d "${APP_DIR}/.git" ]; then if [ ! -d "${APP_DIR}/.git" ]; then
rm -rf "${APP_DIR}" rm -rf "${APP_DIR}"
sudo -u "$APP_USER" -H bash -lc "git clone --depth=1 -b ${GIT_BRANCH} ${GIT_REPO} ${APP_DIR}" quietly sudo -u "$APP_USER" -H bash -lc "git clone --depth=1 -b ${GIT_BRANCH} ${GIT_REPO} ${APP_DIR}"
else else
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && git fetch --depth=1 origin ${GIT_BRANCH} && git checkout ${GIT_BRANCH} && git pull --ff-only" quietly sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && git fetch --depth=1 origin ${GIT_BRANCH} && git checkout ${GIT_BRANCH} && git pull --ff-only"
fi fi
ok
# ===== .env erstellen und befüllen ===== # ===== .env erstellen und befüllen =====
log ".env konfigurieren…"
APP_URL="http://${SERVER_IP}" APP_URL="http://${SERVER_IP}"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && cp -n .env.example .env || true" sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && cp -n .env.example .env || true"
@ -534,34 +545,43 @@ grep -q '^BOOTSTRAP_ADMIN_PASSWORD_HASH=' "${APP_DIR}/.env" \
|| echo "BOOTSTRAP_ADMIN_PASSWORD_HASH=${BOOTSTRAP_HASH}" >> "${APP_DIR}/.env" || echo "BOOTSTRAP_ADMIN_PASSWORD_HASH=${BOOTSTRAP_HASH}" >> "${APP_DIR}/.env"
sed -i "s|^BOOTSTRAP_ADMIN_PASSWORD_HASH=.*|BOOTSTRAP_ADMIN_PASSWORD_HASH=${BOOTSTRAP_HASH}|g" "${APP_DIR}/.env" sed -i "s|^BOOTSTRAP_ADMIN_PASSWORD_HASH=.*|BOOTSTRAP_ADMIN_PASSWORD_HASH=${BOOTSTRAP_HASH}|g" "${APP_DIR}/.env"
# ===== Composer Dependencies ===== step "PHP-Abhängigkeiten installieren (Composer)" "12 Min"
log "Composer install…" quietly sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-dev --optimize-autoloader --no-interaction"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && composer install --no-dev --optimize-autoloader --no-interaction" ok
# ===== App-Key, Migrations & Caches ===== step "Datenbank migrieren" "15 Sek"
log "App-Key generieren, Datenbank migrieren, Caches bauen…" quietly sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan key:generate --force"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan key:generate --force" quietly sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan migrate --force"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan migrate --force" try_quiet sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan storage:link --force"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan storage:link --force || true" try_quiet sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan config:cache"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan config:cache" || true try_quiet sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan route:cache"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan route:cache" || true try_quiet sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan view:cache"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && php artisan view:cache" || true ok
# ===== Frontend Build =====
if [ -f "${APP_DIR}/package.json" ]; then if [ -f "${APP_DIR}/package.json" ]; then
log "Frontend bauen…" step "Frontend bauen (npm)" "13 Min"
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm ci --no-audit --no-fund || npm install" # Node/npm falls noch nicht installiert
sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm run build" || warn "npm run build fehlgeschlagen manuell nachholen: cd ${APP_DIR} && npm run build" if ! command -v node >/dev/null 2>&1; then
if [ "$NODE_SETUP" = "nodesource" ]; then
quietly bash -c "curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh && bash /tmp/nodesource_setup.sh && rm -f /tmp/nodesource_setup.sh"
quietly apt-get install -y nodejs
else
quietly apt-get install -y nodejs npm
fi
fi
quietly sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm ci --no-audit --no-fund || npm install"
if ! sudo -u "$APP_USER" -H bash -lc "cd ${APP_DIR} && npm run build" >> "$LOG_FILE" 2>&1; then
warn "npm run build fehlgeschlagen — manuell nachholen: cd ${APP_DIR} && npm run build"
else
ok
fi
fi fi
# ===== Wizard State-Verzeichnis =====
mkdir -p /var/lib/mailwolt/wizard mkdir -p /var/lib/mailwolt/wizard
chown www-data:www-data /var/lib/mailwolt/wizard chown www-data:www-data /var/lib/mailwolt/wizard
chmod 775 /var/lib/mailwolt/wizard chmod 775 /var/lib/mailwolt/wizard
# ===== mailwolt-apply-domains Helper ===== step "Hilfsskripte & Konfiguration installieren" "5 Sek"
log "mailwolt-apply-domains Helper installieren…"
cat > /usr/local/sbin/mailwolt-apply-domains <<'HELPER' cat > /usr/local/sbin/mailwolt-apply-domains <<'HELPER'
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
@ -758,67 +778,51 @@ GIT_TAG="$(sudo -u "$APP_USER" -H bash -lc "git -C ${APP_DIR} describe --tags --
if [ -n "$GIT_TAG" ]; then if [ -n "$GIT_TAG" ]; then
echo "${GIT_TAG#v}" > /var/lib/mailwolt/version echo "${GIT_TAG#v}" > /var/lib/mailwolt/version
echo "$GIT_TAG" > /var/lib/mailwolt/version_raw echo "$GIT_TAG" > /var/lib/mailwolt/version_raw
log "Version: ${GIT_TAG}"
else else
warn "Kein Git-Tag gefunden — Version-Datei wird nicht geschrieben" warn "Kein Git-Tag gefunden — Version-Datei wird nicht geschrieben"
fi fi
ok
# ===== App-User/Gruppen & Rechte (am ENDE ausführen) ===== step "Berechtigungen setzen & Dienste starten" "10 Sek"
# User anlegen (nur falls noch nicht vorhanden) + Passwort setzen + Gruppe
if ! id -u "$APP_USER" >/dev/null 2>&1; then if ! id -u "$APP_USER" >/dev/null 2>&1; then
adduser --disabled-password --gecos "" "$APP_USER" quietly adduser --disabled-password --gecos "" "$APP_USER"
fi fi
echo "${APP_USER}:${APP_PW}" | chpasswd echo "${APP_USER}:${APP_PW}" | quietly chpasswd
usermod -a -G "$APP_GROUP" "$APP_USER" quietly usermod -a -G "$APP_GROUP" "$APP_USER"
# Besitz & Rechte
chown -R "$APP_USER":"$APP_GROUP" "$APP_DIR" chown -R "$APP_USER":"$APP_GROUP" "$APP_DIR"
find "$APP_DIR" -type d -exec chmod 775 {} \; find "$APP_DIR" -type d -exec chmod 775 {} \; 2>>"$LOG_FILE"
find "$APP_DIR" -type f -exec chmod 664 {} \; find "$APP_DIR" -type f -exec chmod 664 {} \; 2>>"$LOG_FILE"
chmod -R 775 "$APP_DIR"/storage "$APP_DIR"/bootstrap/cache chmod -R 775 "$APP_DIR"/storage "$APP_DIR"/bootstrap/cache
if command -v setfacl >/dev/null 2>&1; then if command -v setfacl >/dev/null 2>&1; then
setfacl -R -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX \ try_quiet setfacl -R -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache"
"${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true try_quiet setfacl -dR -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache"
setfacl -dR -m u:${APP_USER}:rwX,g:${APP_GROUP}:rwX \
"${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache" || true
fi 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}/.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 grep -q 'umask 002' /home/${APP_USER}/.bashrc 2>/dev/null || echo 'umask 002' >> /home/${APP_USER}/.bashrc
try_quiet sudo -u "$APP_USER" -H bash -lc "npm config set umask 0002"
# 7) npm respektiert umask zur Sicherheit direkt setzen (für APP_USER)
sudo -u "$APP_USER" -H bash -lc "npm config set umask 0002" >/dev/null 2>&1 || true
# PHP-FPM-Socket group-writable machen
FPM_POOL="/etc/php/${PHPV}/fpm/pool.d/www.conf" FPM_POOL="/etc/php/${PHPV}/fpm/pool.d/www.conf"
if [ -f "$FPM_POOL" ]; then if [ -f "$FPM_POOL" ]; then
sed -i 's/^;*listen\.owner.*/listen.owner = www-data/' "$FPM_POOL" 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\.group.*/listen.group = www-data/' "$FPM_POOL"
sed -i 's/^;*listen\.mode.*/listen.mode = 0660/' "$FPM_POOL" sed -i 's/^;*listen\.mode.*/listen.mode = 0660/' "$FPM_POOL"
systemctl restart php${PHPV}-fpm || true try_quiet systemctl restart php${PHPV}-fpm
fi fi
# 9) Optional: deinem Shell-/IDE-User ebenfalls Schreibrechte geben
IDE_USER="${SUDO_USER:-}" IDE_USER="${SUDO_USER:-}"
if [ -n "$IDE_USER" ] && id "$IDE_USER" >/dev/null 2>&1 && command -v setfacl >/dev/null 2>&1; then 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 try_quiet usermod -a -G "$APP_GROUP" "$IDE_USER"
setfacl -R -m u:${IDE_USER}:rwX "$APP_DIR" try_quiet setfacl -R -m u:${IDE_USER}:rwX "$APP_DIR"
setfacl -dR -m u:${IDE_USER}:rwX "$APP_DIR" try_quiet setfacl -dR -m u:${IDE_USER}:rwX "$APP_DIR"
echo -e "${YELLOW}[i]${NC} Benutzer '${IDE_USER}' wurde für Schreibzugriff freigeschaltet (ACL + Gruppe ${APP_GROUP})."
fi fi
# Webstack neu laden try_quiet systemctl reload nginx
systemctl reload nginx || true try_quiet systemctl restart php*-fpm
systemctl restart php*-fpm || true ok
# Hinweis zur neuen Gruppenzugehörigkeit # ===== Monit =====
echo -e "${YELLOW}[i]${NC} SHELL: Du kannst dich nun als Benutzer '${APP_USER}' mit dem Passwort '${APP_PW}' anmelden."
echo -e "${YELLOW}[i]${NC} Hinweis: Nach dem ersten Login solltest du das Passwort mit 'passwd ${APP_USER}' ändern."
echo -e "${YELLOW}[i]${NC} Damit die Gruppenrechte (${APP_GROUP}) aktiv werden, bitte einmal ab- und wieder anmelden."
# ===== Monit (Watchdog) installiert, aber NICHT aktiviert =====
log "Monit (Watchdog) installieren (deaktiviert)"
cat > /etc/monit/monitrc <<'EOF' cat > /etc/monit/monitrc <<'EOF'
set daemon 60 set daemon 60
set logfile syslog facility log_daemon set logfile syslog facility log_daemon
@ -862,26 +866,37 @@ check process nginx with pidfile /run/nginx.pid
if failed port 443 type tcp ssl then restart if failed port 443 type tcp ssl then restart
EOF EOF
chmod 600 /etc/monit/monitrc chmod 600 /etc/monit/monitrc
systemctl enable --now monit || true try_quiet systemctl enable --now monit
# ===== Smoke-Test (alle Ports, mit Timeouts) ===== # ===== Smoke-Test =====
log "Smoke-Test (Ports & Banner):" step "Port-Check" "30 Sek"
set +e set +e
printf "[25] " && timeout 6s bash -lc 'printf "EHLO localhost\r\nQUIT\r\n" | nc -v -w 4 127.0.0.1 25 2>&1' || true _smoke_ok=0; _smoke_fail=0
printf "[465] " && timeout 6s openssl s_client -connect 127.0.0.1:465 -brief -quiet </dev/null || echo "[465] Verbindung fehlgeschlagen" smoke_port() {
printf "[587] " && timeout 6s openssl s_client -starttls smtp -connect 127.0.0.1:587 -brief -quiet </dev/null || echo "[587] Verbindung fehlgeschlagen" local port="$1" result
printf "[110] " && timeout 6s bash -lc 'printf "QUIT\r\n" | nc -v -w 4 127.0.0.1 110 2>&1' || true result=$(eval "$2" 2>&1)
printf "[995] " && timeout 6s openssl s_client -connect 127.0.0.1:995 -brief -quiet </dev/null || echo "[995] Verbindung fehlgeschlagen" if echo "$result" | grep -qiE '(220|OK|\+OK|CAPABILITY)'; then
printf "[143] " && timeout 6s bash -lc 'printf ". CAPABILITY\r\n. LOGOUT\r\n" | nc -v -w 4 127.0.0.1 143 2>&1' || true printf " ${GREEN}${NC} Port %-5s erreichbar\n" "$port"
printf "[993] " && timeout 6s openssl s_client -connect 127.0.0.1:993 -brief -quiet </dev/null || echo "[993] Verbindung fehlgeschlagen" (( _smoke_ok++ )) || true
else
printf " ${YELLOW}${NC} Port %-5s nicht erreichbar\n" "$port"
(( _smoke_fail++ )) || true
fi
}
smoke_port 25 "timeout 5s bash -c 'printf \"EHLO localhost\r\nQUIT\r\n\" | nc -w3 127.0.0.1 25'"
smoke_port 465 "timeout 5s openssl s_client -connect 127.0.0.1:465 -brief -quiet </dev/null"
smoke_port 587 "timeout 5s openssl s_client -starttls smtp -connect 127.0.0.1:587 -brief -quiet </dev/null"
smoke_port 143 "timeout 5s bash -c 'printf \". CAPABILITY\r\n. LOGOUT\r\n\" | nc -w3 127.0.0.1 143'"
smoke_port 993 "timeout 5s openssl s_client -connect 127.0.0.1:993 -brief -quiet </dev/null"
smoke_port 110 "timeout 5s bash -c 'printf \"QUIT\r\n\" | nc -w3 127.0.0.1 110'"
smoke_port 995 "timeout 5s openssl s_client -connect 127.0.0.1:995 -brief -quiet </dev/null"
set -e set -e
echo echo
echo "==============================================================" echo -e " ${GREY}Bootstrap-Login (nur für ERSTEN Login & Wizard):${NC}"
echo " Bootstrap-Login (nur für ERSTEN Login & Wizard):" echo -e " ${CYAN}User: ${NC}${BOOTSTRAP_USER}"
echo " User: ${BOOTSTRAP_USER}" echo -e " ${CYAN}Passwort: ${NC}${BOOTSTRAP_PASS}"
echo " Passwort: ${BOOTSTRAP_PASS}" echo -e " ${GREY}Log: ${LOG_FILE}${NC}"
echo "=============================================================="
echo echo
footer_ok "$SERVER_IP" footer_ok "$SERVER_IP"