282 lines
8.3 KiB
Bash
Executable File
282 lines
8.3 KiB
Bash
Executable File
#!/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
|
||
|
||
# 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'
|
||
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." |