380 lines
11 KiB
Bash
380 lines
11 KiB
Bash
#!/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" <<CONF
|
||
## --- ${host} : HTTP (kein Redirect, kein TLS; läuft hinter Reverse-Proxy) ---
|
||
#server {
|
||
# listen 80;
|
||
# listen [::]:80;
|
||
# server_name ${host};
|
||
#
|
||
# # ACME HTTP-01 (optional; meist übernimmt das der Proxy)
|
||
# location ^~ /.well-known/acme-challenge/ {
|
||
# root ${ACME_ROOT};
|
||
# allow all;
|
||
# }
|
||
#
|
||
# root ${APP_DIR}/public;
|
||
# index index.php index.html;
|
||
#
|
||
# access_log /var/log/nginx/${host}_access.log;
|
||
# error_log /var/log/nginx/${host}_error.log;
|
||
#
|
||
# client_max_body_size 25m;
|
||
#
|
||
# location / { try_files \$uri \$uri/ /index.php?\$query_string; }
|
||
#
|
||
# location ~ \.php\$ {
|
||
# include snippets/fastcgi-php.conf;
|
||
# ${FASTCGI_PASS}
|
||
# }
|
||
#
|
||
# location ^~ /livewire/ { try_files \$uri /index.php?\$query_string; }
|
||
# location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)\$ { expires 30d; access_log off; }
|
||
#
|
||
# # WebSocket: Laravel Reverb (Backend intern HTTP)
|
||
# location /ws/ {
|
||
# proxy_http_version 1.1;
|
||
# proxy_set_header Upgrade \$http_upgrade;
|
||
# proxy_set_header Connection "Upgrade";
|
||
# proxy_set_header Host \$host;
|
||
# proxy_read_timeout 60s;
|
||
# proxy_send_timeout 60s;
|
||
# proxy_pass http://127.0.0.1:8080/;
|
||
# }
|
||
#
|
||
# # Reverb HTTP API
|
||
# location /apps/ {
|
||
# proxy_http_version 1.1;
|
||
# proxy_set_header Host \$host;
|
||
# proxy_read_timeout 60s;
|
||
# proxy_send_timeout 60s;
|
||
# proxy_pass http://127.0.0.1:8080/apps/;
|
||
# }
|
||
#CONF
|
||
#
|
||
# if [[ "${DEV_MODE}" = "1" ]]; then
|
||
# cat >> "$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" <<CONF
|
||
# --- ${host} : HTTP (kein Redirect, kein TLS; läuft hinter Reverse-Proxy/DEV) ---
|
||
server {
|
||
listen 80${def};
|
||
listen [::]:80${def};
|
||
server_name ${host};
|
||
|
||
# ACME HTTP-01 (optional; meist übernimmt das der Proxy)
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
root ${ACME_ROOT};
|
||
allow all;
|
||
}
|
||
|
||
root ${APP_DIR}/public;
|
||
index index.php index.html;
|
||
|
||
access_log /var/log/nginx/${host/_/__}_access.log;
|
||
error_log /var/log/nginx/${host/_/__}_error.log;
|
||
|
||
client_max_body_size 25m;
|
||
|
||
location / { try_files \$uri \$uri/ /index.php?\$query_string; }
|
||
|
||
location ~ \.php\$ {
|
||
include snippets/fastcgi-php.conf;
|
||
${FASTCGI_PASS}
|
||
}
|
||
|
||
location ^~ /livewire/ { try_files \$uri /index.php?\$query_string; }
|
||
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)\$ { expires 30d; access_log off; }
|
||
|
||
# WebSocket: Laravel Reverb
|
||
location /ws/ {
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade \$http_upgrade;
|
||
proxy_set_header Connection "Upgrade";
|
||
proxy_set_header Host \$host;
|
||
proxy_read_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_pass http://127.0.0.1:8080/;
|
||
}
|
||
|
||
# Reverb HTTP API
|
||
location /apps/ {
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host \$host;
|
||
proxy_read_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_pass http://127.0.0.1:8080/apps/;
|
||
}
|
||
CONF
|
||
|
||
if [[ "${DEV_MODE}" = "1" ]]; then
|
||
cat >> "$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" <<CONF
|
||
# --- ${host} : HTTP (ACME + Redirect) ---
|
||
server {
|
||
listen 80;
|
||
listen [::]:80;
|
||
server_name ${host};
|
||
|
||
# ACME HTTP-01 auf Port 80
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
root ${ACME_ROOT};
|
||
allow all;
|
||
}
|
||
|
||
return 301 https://\$host\$request_uri;
|
||
}
|
||
|
||
# --- ${host} : HTTPS ---
|
||
server {
|
||
listen 443 ssl${NGINX_HTTP2_SUFFIX};
|
||
listen [::]:443 ssl${NGINX_HTTP2_SUFFIX};
|
||
server_name ${host};
|
||
|
||
ssl_certificate ${cert};
|
||
ssl_certificate_key ${key};
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
|
||
# WICHTIG: ACME auch auf 443, sonst 404 bei Redirects
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
root ${ACME_ROOT};
|
||
allow all;
|
||
}
|
||
|
||
root ${APP_DIR}/public;
|
||
index index.php index.html;
|
||
|
||
access_log /var/log/nginx/${host}_ssl_access.log;
|
||
error_log /var/log/nginx/${host}_ssl_error.log;
|
||
|
||
client_max_body_size 25m;
|
||
|
||
location / { try_files \$uri \$uri/ /index.php?\$query_string; }
|
||
|
||
location ~ \.php\$ {
|
||
include snippets/fastcgi-php.conf;
|
||
${FASTCGI_PASS}
|
||
}
|
||
|
||
location ^~ /livewire/ { try_files \$uri /index.php?\$query_string; }
|
||
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg)\$ { expires 30d; access_log off; }
|
||
|
||
# WebSocket: Laravel Reverb (Backend intern HTTP)
|
||
location /ws/ {
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade \$http_upgrade;
|
||
proxy_set_header Connection "Upgrade";
|
||
proxy_set_header Host \$host;
|
||
proxy_read_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_pass http://127.0.0.1:8080/;
|
||
}
|
||
|
||
# Reverb HTTP API
|
||
location /apps/ {
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host \$host;
|
||
proxy_read_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_pass http://127.0.0.1:8080/apps/;
|
||
}
|
||
CONF
|
||
|
||
if [[ "${DEV_MODE}" = "1" ]]; then
|
||
cat >> "$outfile" <<'CONF'
|
||
# DEV: Vite-Proxy
|
||
location ^~ /@vite/ { proxy_pass http://127.0.0.1:5173/@vite/; 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_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }
|
||
location ^~ /resources/ { proxy_pass http://127.0.0.1:5173/resources/; 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" <<CONF
|
||
# --- ${host} : ACME-only (80 + 443), KEIN App-Root ---
|
||
server {
|
||
listen 80;
|
||
listen [::]:80;
|
||
server_name ${host};
|
||
|
||
# HTTP-01 Challenge exakt ausliefern
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
root ${ACME_ROOT};
|
||
default_type "text/plain";
|
||
try_files \$uri =404;
|
||
}
|
||
|
||
# Alles andere → nach https
|
||
location / { return 301 https://\$host\$request_uri; }
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl${NGINX_HTTP2_SUFFIX};
|
||
listen [::]:443 ssl${NGINX_HTTP2_SUFFIX};
|
||
server_name ${host};
|
||
|
||
ssl_certificate /etc/ssl/mail/fullchain.pem;
|
||
ssl_certificate_key /etc/ssl/mail/privkey.pem;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
|
||
# Auch via https die Challenge bedienen (falls Redirects gefolgt werden)
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
root ${ACME_ROOT};
|
||
default_type "text/plain";
|
||
try_files \$uri =404;
|
||
}
|
||
|
||
# Sonst nichts preisgeben
|
||
location / { return 444; }
|
||
}
|
||
CONF
|
||
}
|
||
|
||
# ── Sites erzeugen ─────────────────────────────────────────────────────────
|
||
MX_SITE="/etc/nginx/sites-available/mx-mailwolt.conf"
|
||
UI_SITE="/etc/nginx/sites-available/ui-mailwolt.conf"
|
||
WEBMAIL_SITE="/etc/nginx/sites-available/webmail-mailwolt.conf"
|
||
|
||
# UI & Webmail wie gehabt …
|
||
#if [[ "${PROXY_MODE:-0}" -eq 1 ]]; then
|
||
# build_site_http_only "$UI_HOST" "$UI_SITE"
|
||
# build_site_http_only "$WEBMAIL_HOST" "$WEBMAIL_SITE"
|
||
#else
|
||
# build_site_tls "$UI_HOST" "/etc/ssl/ui" "$UI_SITE"
|
||
# build_site_tls "$WEBMAIL_HOST" "/etc/ssl/webmail" "$WEBMAIL_SITE"
|
||
#fi
|
||
|
||
# UI & Webmail …
|
||
if [[ "${DEV_MODE}" = "1" ]]; then
|
||
# DEV: per IP erreichbar → Catch-All („_“) und HTTP-only
|
||
build_site_http_only "_" "$UI_SITE"
|
||
build_site_http_only "_" "$WEBMAIL_SITE"
|
||
else
|
||
if [[ "${PROXY_MODE:-0}" -eq 1 ]]; then
|
||
build_site_http_only "$UI_HOST" "$UI_SITE"
|
||
build_site_http_only "$WEBMAIL_HOST" "$WEBMAIL_SITE"
|
||
else
|
||
build_site_tls "$UI_HOST" "/etc/ssl/ui" "$UI_SITE"
|
||
build_site_tls "$WEBMAIL_HOST" "/etc/ssl/webmail" "$WEBMAIL_SITE"
|
||
fi
|
||
fi
|
||
|
||
# MX: **immer** ACME-only (kein Laravel dahinter)
|
||
build_site_acme_only "${MAIL_HOSTNAME}" "$MX_SITE"
|
||
|
||
ln -sf "$UI_SITE" /etc/nginx/sites-enabled/ui-mailwolt.conf
|
||
ln -sf "$WEBMAIL_SITE" /etc/nginx/sites-enabled/webmail-mailwolt.conf
|
||
ln -sf "$MX_SITE" /etc/nginx/sites-enabled/mx-mailwolt.conf
|
||
|
||
# ── Real-IP nur, wenn Proxy davor ──────────────────────────────────────────
|
||
if [[ "${PROXY_MODE}" -eq 1 && -n "${NPM_IP}" ]]; then
|
||
cat > /etc/nginx/conf.d/realip.conf <<NGX
|
||
real_ip_header X-Forwarded-For;
|
||
set_real_ip_from ${NPM_IP};
|
||
real_ip_recursive on;
|
||
NGX
|
||
else
|
||
rm -f /etc/nginx/conf.d/realip.conf || true
|
||
fi
|
||
|
||
# ── Test & reload ──────────────────────────────────────────────────────────
|
||
if nginx -t; then
|
||
systemctl enable --now nginx >/dev/null 2>&1 || true
|
||
systemctl reload nginx || true
|
||
else
|
||
die "nginx -t fehlgeschlagen – siehe /var/log/nginx/*.log"
|
||
fi
|