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
parent
814776d1ff
commit
eb16f7d6ad
253
installer.sh
253
installer.sh
|
|
@ -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" "2–5 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)" "1–2 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)" "1–3 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"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue