Files
appServerPortalUI/connect-proxies.sh
2026-06-07 08:09:04 +02:00

301 lines
9.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
# /docker-entrypoint.d/40-connect-proxies.sh
# Generiert pro Zeile in /etc/nginx/forwarding.conf:
# - HTTPS vHost (Proxy oder local static) NUR wenn Zertifikate existieren
# - optional 80->443 Redirect-Server (mit ACME-Ausnahme) bei http_behavior=redirect
# Läuft automatisch beim Containerstart (nginx:alpine EntryPoint).
set -eu
CONF_DIR="/etc/nginx/conf.d"
LIVE_DIR="/etc/letsencrypt/live"
FWD_FILE="/etc/nginx/forwarding.conf"
HTTPS_SUFFIX="-https.generated.conf"
HTTP_REDIRECT_SUFFIX="-http-redirect.generated.conf"
GLOBALS_FILE="$CONF_DIR/_globals.generated.conf"
echo "[connect-proxies] start …"
# 0) Forwarding-Datei vorhanden?
if [ ! -f "$FWD_FILE" ]; then
echo "[connect-proxies] WARN: $FWD_FILE fehlt keine Proxies zu generieren."
exit 0
fi
# 1) Globale HTTP-Kontext-Map + Resolver (idempotent)
RESOLVERS="$(awk '/^nameserver/{print $2}' /etc/resolv.conf | xargs || true)"
if [ -n "${RESOLVERS:-}" ]; then
RESOLVER_LINE="resolver $RESOLVERS ipv6=off valid=30s;"
else
RESOLVER_LINE="resolver 127.0.0.11 ipv6=off valid=30s;"
fi
cat > "$GLOBALS_FILE" <<NGINX
# Automatisch generiert nicht editieren
map \$http_upgrade \$connection_upgrade {
default upgrade;
'' close;
}
$RESOLVER_LINE
NGINX
# 1b) Default-Server für 443: weist unbekanntes/leeres SNI sauber ab,
# statt es an den alphabetisch ersten vHost zu "vererben" (Anti-Leak).
# Greift auch dann, wenn einer Domain das Zertifikat fehlt.
DEFAULT_443_FILE="$CONF_DIR/00-default-server.generated.conf"
cat > "$DEFAULT_443_FILE" <<'NGINX'
# Automatisch generiert nicht editieren
# Fängt alle HTTPS-Verbindungen ab, deren SNI auf keinen echten vHost passt,
# und lehnt den TLS-Handshake ab. So landet eine unbekannte/zertifikatslose
# Domain NIE still beim ersten vHost. Braucht per ssl_reject_handshake
# bewusst KEIN eigenes Zertifikat.
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
ssl_reject_handshake on;
}
NGINX
# 2) Alte generierte Confs entfernen
rm -f "$CONF_DIR/"*"$HTTPS_SUFFIX" 2>/dev/null || true
rm -f "$CONF_DIR/"*"$HTTP_REDIRECT_SUFFIX" 2>/dev/null || true
# 3) Zeilen verarbeiten
LINE_NO=0
while IFS= read -r RAW || [ -n "$RAW" ]; do
LINE_NO=$((LINE_NO+1))
LINE="$(printf '%s' "$RAW" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
[ -z "$LINE" ] && continue
case "$LINE" in \#*) continue;; esac
set -- $LINE
SERVER_NAME="${1:-}"
UPSTREAM_URL="${2:-}"
HTTP_BEHAVIOR="${3:-redirect}"
WEBSOCKETS="${4:-false}"
VERIFY_TLS="${5:-false}"
CERT_DOMAIN="${6:-$SERVER_NAME}"
LISTEN_PORT="${7:-443}"
AUTH_REQUIRED="${8:-false}"
if [ -z "$SERVER_NAME" ] || [ -z "$UPSTREAM_URL" ]; then
echo "[connect-proxies] WARN(Line $LINE_NO): unvollständig -> $LINE"
continue
fi
FULLCHAIN="$LIVE_DIR/$CERT_DOMAIN/fullchain.pem"
PRIVKEY="$LIVE_DIR/$CERT_DOMAIN/privkey.pem"
HTTPS_OUT="$CONF_DIR/${SERVER_NAME}-p${LISTEN_PORT}${HTTPS_SUFFIX}"
HTTP_REDIRECT_OUT="$CONF_DIR/${SERVER_NAME}${HTTP_REDIRECT_SUFFIX}"
SANITIZED_UPSTREAM="${UPSTREAM_URL%/}"
DNS_OK="true"
SCHEME=""; HOST=""; PORT=""
if [ "$SANITIZED_UPSTREAM" != "local" ]; then
case "$SANITIZED_UPSTREAM" in
http://*) URI="${SANITIZED_UPSTREAM#http://}"; SCHEME="http"; DEFAULT_PORT="80";;
https://*) URI="${SANITIZED_UPSTREAM#https://}"; SCHEME="https"; DEFAULT_PORT="443";;
*)
echo "[connect-proxies] WARN(Line $LINE_NO): $SERVER_NAME upstream_url ungültig: '$UPSTREAM_URL' überspringe."
continue;;
esac
HOSTPORT="${URI%%/*}"
HOST="${HOSTPORT%%:*}"
PORT="${HOSTPORT#*:}"; [ "$PORT" = "$HOSTPORT" ] && PORT="$DEFAULT_PORT"
if command -v getent >/dev/null 2>&1; then
if ! getent hosts "$HOST" >/dev/null 2>&1; then
DNS_OK="false"
echo "[connect-proxies] [-] $SERVER_NAME: DNS nicht auflösbar ($HOST) erzeuge 503-Placeholder statt Proxy."
fi
else
DNS_OK="unknown"
fi
fi
if [ -f "$FULLCHAIN" ] && [ -f "$PRIVKEY" ]; then
echo "[connect-proxies] [+] $SERVER_NAME: Zertifikat OK (cert_domain=$CERT_DOMAIN). Erzeuge 443 …"
# Auth-Block vorbereiten
AUTH_BLOCK=""
if [ "$AUTH_REQUIRED" = "true" ]; then
AUTH_BLOCK="
auth_request /auth;
location = /auth {
proxy_pass http://appserverauth:3000/internal/auth;
proxy_pass_request_body off;
proxy_set_header Content-Length \"\";
proxy_set_header X-Original-URI \$request_uri;
proxy_set_header Host server.schooltech.ch;
proxy_set_header Cookie \$http_cookie;
}"
fi
# Fall A: local (statisch, kein Proxy)
if [ "$SANITIZED_UPSTREAM" = "local" ]; then
cat > "$HTTPS_OUT" <<NGINX
# Auto-generated - 443 static site
server {
listen ${LISTEN_PORT} ssl http2;
listen [::]:${LISTEN_PORT} ssl http2;
server_name $SERVER_NAME;
$AUTH_BLOCK
ssl_certificate $FULLCHAIN;
ssl_certificate_key $PRIVKEY;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
root /usr/share/nginx/html;
index index.html;
location /api/ {
proxy_pass http://appserverauth:3000/api/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
location / {
try_files \$uri \$uri/ /index.html;
}
}
NGINX
else
# Proxy-Fall: DNS OK?
if [ "$DNS_OK" = "false" ]; then
cat > "$HTTPS_OUT" <<NGINX
# Auto-generated - 443 placeholder (DNS failed)
server {
listen ${LISTEN_PORT} ssl http2;
listen [::]:${LISTEN_PORT} ssl http2;
server_name $SERVER_NAME;
ssl_certificate $FULLCHAIN;
ssl_certificate_key $PRIVKEY;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
default_type text/html;
return 503 "<!doctype html><html><head><meta charset='utf-8'><title>Service temporarily unavailable</title></head><body style='font-family:sans-serif;margin:3rem'><h1>\$server_name nicht erreichbar</h1><p>DNS-Auflösung fehlgeschlagen. Bitte später erneut versuchen.</p></body></html>";
}
}
NGINX
else
# Proxy mit Laufzeit-Auflösung
cat > "$HTTPS_OUT" <<NGINX
# Auto-generated - 443 reverse proxy
server {
listen ${LISTEN_PORT} ssl http2;
listen [::]:${LISTEN_PORT} ssl http2;
server_name $SERVER_NAME;
$AUTH_BLOCK
ssl_certificate $FULLCHAIN;
ssl_certificate_key $PRIVKEY;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
proxy_intercept_errors on;
error_page 502 503 504 = @service_down;
location / {
set \$target $SANITIZED_UPSTREAM;
proxy_pass \$target;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_http_version 1.1;
NGINX
if [ "$WEBSOCKETS" = "true" ]; then
cat >> "$HTTPS_OUT" <<'NGINX'
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
NGINX
else
cat >> "$HTTPS_OUT" <<'NGINX'
proxy_set_header Upgrade "";
proxy_set_header Connection close;
NGINX
fi
case "$SANITIZED_UPSTREAM" in
https://*)
if [ "$VERIFY_TLS" = "true" ]; then
cat >> "$HTTPS_OUT" <<'NGINX'
proxy_ssl_verify on;
proxy_ssl_server_name on;
NGINX
else
cat >> "$HTTPS_OUT" <<'NGINX'
proxy_ssl_verify off;
proxy_ssl_server_name on;
NGINX
fi
;;
esac
cat >> "$HTTPS_OUT" <<'NGINX'
proxy_buffering off;
proxy_request_buffering off;
client_max_body_size 50m;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
location @service_down {
default_type text/html;
return 503 "<!doctype html><html><head><meta charset='utf-8'><title>Service temporarily unavailable</title></head><body style='font-family:sans-serif;margin:3rem'><h1>$server_name nicht erreichbar</h1><p>Der Dienst ist momentan nicht verfügbar. Bitte später erneut versuchen.</p></body></html>";
}
}
NGINX
fi
fi
# 80->443 Redirect-Server
if [ "$HTTP_BEHAVIOR" = "redirect" ]; then
cat > "$HTTP_REDIRECT_OUT" <<NGINX
# Auto-generated 80->443 redirect for $SERVER_NAME
server {
listen 80;
listen [::]:80;
server_name $SERVER_NAME;
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type "text/plain; charset=utf-8";
}
location / {
return 301 https://\$host\$request_uri;
}
}
NGINX
else
rm -f "$HTTP_REDIRECT_OUT" 2>/dev/null || true
fi
else
echo "[connect-proxies] [-] $SERVER_NAME: keine Zertifikate (cert_domain=$CERT_DOMAIN). Entferne evtl. alte Confs."
rm -f "$HTTPS_OUT" "$HTTP_REDIRECT_OUT" 2>/dev/null || true
fi
done < "$FWD_FILE"
echo "[connect-proxies] nginx -t …"
nginx -t
echo "[connect-proxies] done."