This commit is contained in:
chk
2026-03-21 11:09:44 +01:00
parent fd89de5282
commit 52d6561385
22 changed files with 1819 additions and 1200 deletions

0
.forwarding.conf.swp Normal file
View File

10
.gitignore vendored Executable file
View File

@@ -0,0 +1,10 @@
# Let's Encrypt Konfigurationen und Zertifikate ignorieren
letsencrypt/
letsencrypt/conf/
letsencrypt/conf/**/*
# Falls du nur Zertifikate ignorieren willst:
*.pem
*.key
*.csr

View File

@@ -1,375 +0,0 @@
error_log /var/log/nginx/error.log info;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# ------------------------------------------------------------
# Default HTTP -> HTTPS redirect
# ------------------------------------------------------------
server {
listen 80 default_server;
server_name _;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# ------------------------------------------------------------
# #***# DEFAULT 443 SERVER (NEU)
# Verhindert, dass der erste 443-vHost andere Subdomains "abfängt"
# ------------------------------------------------------------
server {
listen 443 ssl http2 default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Einfach Verbindung schließen für unbekannte Hosts
return 444;
}
# ------------------------------------------------------------
# UI (portal) - nur für server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# API forwarding to auth (wie vorher) - nur für server.schooltech.ch
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;
}
# ------------------------------------------------------------
# Internal auth endpoint for auth_request (used by other server blocks)
# Einheitlicher nginxauth-Block: Host + URI an Auth-Service
# ------------------------------------------------------------
location = /nginxauth {
internal;
proxy_pass http://appserverauth:3000/internal/auth; #***# AUTH: proxy_pass (wichtig)
proxy_set_header Cookie $http_cookie; #***# AUTH: Cookie weitergeben
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
# Security
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
# ------------------------------------------------------------
# abc.server.schooltech.ch - Controller on ThinkCentre
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name abc.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# ---- Static assets: keine Auth, damit Browser die .js/.css korrekt erhält ----
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp)$ {
proxy_pass https://thinkcentre.local:10010;
proxy_set_header Host thinkcentre.local;
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 https;
# kein proxy_set_header Connection / Upgrade hier
}
# ---- WebSocket-Endpoint (falls z.B. /echo) - auth prüfen ----
location /echo {
auth_request /nginxauth;
proxy_pass https://thinkcentre.local:10010/echo;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_set_header Host thinkcentre.local;
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 https;
}
# ---- Hauptanwendung (HTML, API-Aufrufe) - auth prüfen ----
location / {
auth_request /nginxauth;
proxy_pass https://thinkcentre.local:10010/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_set_header Host thinkcentre.local;
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 https;
proxy_set_header Origin $http_origin;
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
}
# /nginxauth (lokal für diesen vhost, aber internal request wird an auth-service weitergeleitet)
location = /nginxauth {
internal;
proxy_pass http://appserverauth:3000/internal/auth; #***# AUTH
proxy_set_header Cookie $http_cookie; #***# AUTH
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
}
# ------------------------------------------------------------
# simulation3a29.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name simulation3a29.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# ---- Static assets: keine Auth ----
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp)$ {
proxy_pass https://thinkcentre.local:1003;
proxy_set_header Host thinkcentre.local;
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 https;
# kein proxy_set_header Connection / Upgrade hier
}
# ---- WebSocket-Endpoint ----
location /echo {
auth_request /nginxauth;
proxy_pass https://thinkcentre.local:1003/echo;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_set_header Host thinkcentre.local;
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 https;
}
# ---- Hauptanwendung ----
location / {
auth_request /nginxauth;
proxy_pass https://thinkcentre.local:1003/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_set_header Host thinkcentre.local;
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 https;
proxy_set_header Origin $http_origin;
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
}
# ------------------------------------------------------------
# location = /nginxauth (SIMULATION FIX)
# ------------------------------------------------------------
location = /nginxauth {
internal;
proxy_pass http://appserverauth:3000/internal/auth; #***# SIMULATION: fehlende proxy_pass eingefügt
proxy_set_header Cookie $http_cookie; #***# SIMULATION: Cookie weitergeben
#***# AUTH HEADER ERWEITERUNG (nur einmal)
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
}
# ------------------------------------------------------------
# xyz.server.schooltech.ch (Guacamole on ThinkCentre)
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name xyz.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
location / {
auth_request /nginxauth; # Auth prüfen
proxy_pass http://thinkcentre.local:8080/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Proxy Header (Upstream erwartet thinkcentre.local)
proxy_set_header Host thinkcentre.local; # bewusst: Upstream Host-Expectation
proxy_set_header Origin $http_origin;
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 https;
# iFrame erlauben
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors *" always;
}
location = /nginxauth {
internal;
proxy_pass http://appserverauth:3000/internal/auth; #***# XYZ: proxy_pass wie überall
proxy_set_header Cookie $http_cookie;
proxy_set_header X-Original-URI $request_uri;
#***# XYZ AUTH HOST: Original-Host weitergeben (wichtig für Redirects/Checks)
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
}
# ------------------------------------------------------------
# portainer.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name portainer.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
#***# PORTAINER: API direkt weiterleiten (kein auth_request)
location ^~ /api/ {
proxy_pass http://127.0.0.1:9000; #***# auf lokalen Portainer HTTP Backend zeigen
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#***# PORTAINER: statische Assets / locales ebenfalls ohne auth (wichtig für i18n)
location ~* \.(?:js|css|json|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ^~ /locales/ {
# explizit für i18n Pfade
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}
# Haupt-UI: auth_request greift nur hier (UI), nicht für /api/ oder Assets
location / {
auth_request /nginxauth;
proxy_pass http://127.0.0.1:9000/; #***# auf lokales Portainer HTTP Backend zeigen
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
#***# PORTAINER HOST FIX:
proxy_set_header Host $host; #***# PORTAINER HOST
proxy_set_header X-Forwarded-Host $host; #***# PORTAINER HOST
proxy_set_header X-Forwarded-Proto https; #***# PORTAINER HOST
proxy_set_header Origin $http_origin;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors *" always;
}
location = /nginxauth {
internal;
proxy_pass http://appserverauth:3000/internal/auth; #***# AUTH
proxy_set_header Cookie $http_cookie; #***# AUTH
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
}

View File

@@ -1,31 +0,0 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log info;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Upgrade map (WebSockets)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
sendfile on;
keepalive_timeout 65;
gzip on;
# Include modulare Server-Configs
include /etc/nginx/conf.d/*.conf;
}

1
auth/node_modules/.bin/mime generated vendored
View File

@@ -1 +0,0 @@
../mime/cli.js

8
auth/node_modules/.bin/mime generated vendored Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env node
var mime = require('./mime.js');
var file = process.argv[2];
var type = mime.lookup(file);
process.stdout.write(type + '\n');

View File

@@ -1 +0,0 @@
../node-gyp-build/bin.js

84
auth/node_modules/.bin/node-gyp-build generated vendored Executable file
View File

@@ -0,0 +1,84 @@
#!/usr/bin/env node
var proc = require('child_process')
var os = require('os')
var path = require('path')
if (!buildFromSource()) {
proc.exec('node-gyp-build-test', function (err, stdout, stderr) {
if (err) {
if (verbose()) console.error(stderr)
preinstall()
}
})
} else {
preinstall()
}
function build () {
var win32 = os.platform() === 'win32'
var shell = win32
var args = [win32 ? 'node-gyp.cmd' : 'node-gyp', 'rebuild']
try {
var pkg = require('node-gyp/package.json')
args = [
process.execPath,
path.join(require.resolve('node-gyp/package.json'), '..', typeof pkg.bin === 'string' ? pkg.bin : pkg.bin['node-gyp']),
'rebuild'
]
shell = false
} catch (_) {}
proc.spawn(args[0], args.slice(1), { stdio: 'inherit', shell, windowsHide: true }).on('exit', function (code) {
if (code || !process.argv[3]) process.exit(code)
exec(process.argv[3]).on('exit', function (code) {
process.exit(code)
})
})
}
function preinstall () {
if (!process.argv[2]) return build()
exec(process.argv[2]).on('exit', function (code) {
if (code) process.exit(code)
build()
})
}
function exec (cmd) {
if (process.platform !== 'win32') {
var shell = os.platform() === 'android' ? 'sh' : true
return proc.spawn(cmd, [], {
shell,
stdio: 'inherit'
})
}
return proc.spawn(cmd, [], {
windowsVerbatimArguments: true,
stdio: 'inherit',
shell: true,
windowsHide: true
})
}
function buildFromSource () {
return hasFlag('--build-from-source') || process.env.npm_config_build_from_source === 'true'
}
function verbose () {
return hasFlag('--verbose') || process.env.npm_config_loglevel === 'verbose'
}
// TODO (next major): remove in favor of env.npm_config_* which works since npm
// 0.1.8 while npm_config_argv will stop working in npm 7. See npm/rfcs#90
function hasFlag (flag) {
if (!process.env.npm_config_argv) return false
try {
return JSON.parse(process.env.npm_config_argv).original.indexOf(flag) !== -1
} catch (_) {
return false
}
}

View File

@@ -1 +0,0 @@
../node-gyp-build/optional.js

7
auth/node_modules/.bin/node-gyp-build-optional generated vendored Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env node
/*
I am only useful as an install script to make node-gyp not compile for purely optional native deps
*/
process.exit(0)

View File

@@ -1 +0,0 @@
../node-gyp-build/build-test.js

19
auth/node_modules/.bin/node-gyp-build-test generated vendored Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env node
process.env.NODE_ENV = 'test'
var path = require('path')
var test = null
try {
var pkg = require(path.join(process.cwd(), 'package.json'))
if (pkg.name && process.env[pkg.name.toUpperCase().replace(/-/g, '_')]) {
process.exit(0)
}
test = pkg.prebuild.test
} catch (err) {
// do nothing
}
if (test) require(path.join(process.cwd(), test))
else require('./')()

View File

@@ -1,3 +1,4 @@
{ {
"admin": "$2b$12$a6.TdWrJruzs1qfwfMBNsuJApkRwUiDG5IZfbtclNPhYEAsIJGMHC" "admin": "$2b$12$a6.TdWrJruzs1qfwfMBNsuJApkRwUiDG5IZfbtclNPhYEAsIJGMHC",
"abc": "$2b$12$HurtBQkjJyXOV2qFpWkFv.wAIRs6B01sSSScaHMdUrbuBWE0aoyIa"
} }

283
connect-proxies.sh Executable file
View File

@@ -0,0 +1,283 @@
#!/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)
# >>> CHANGE: Resolver NICHT hardcoden. Dynamisch aus /etc/resolv.conf ableiten, Fallback 127.0.0.11
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
# <<< END CHANGE
# 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))
# trim + CR entfernen
LINE="$(printf '%s' "$RAW" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
[ -z "$LINE" ] && continue
case "$LINE" in \#*) continue;; esac
# Spalten splitten (mindestens 2 erforderlich)
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}"
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}"
# >>> NEW: Upstream normalisieren (Trailing Slash entfernen) + DNS-Check vorbereiten
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
# <<< END NEW
if [ -f "$FULLCHAIN" ] && [ -f "$PRIVKEY" ]; then
echo "[connect-proxies] [+] $SERVER_NAME: Zertifikat OK (cert_domain=$CERT_DOMAIN). Erzeuge 443 …"
# Fall A: local (statisch, kein proxy_pass)
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;
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
# >>> NEW: Zwei Pfade DNS_OK=false => Placeholder; sonst Proxy mit Laufzeit-Resolver
if [ "$DNS_OK" = "false" ]; then
# 443 Placeholder keine Proxy-Verbindung, saubere 503
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
# 443 Proxy DNS ok/unknown: Laufzeit-Auflösung + freundlicher 503 bei Downstreams
cat > "$HTTPS_OUT" <<NGINX
# Auto-generated - 443 reverse proxy
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;
# Fehler sauber abfangen und 503 liefern (statt rohe 502/504)
proxy_intercept_errors on;
error_page 502 503 504 = @service_down;
location / {
# >>> CHANGE: variable proxy_pass -> DNS zur Laufzeit (verhindert nginx -t Crash)
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
# <<< END NEW
fi
# 80->443 Redirect-Server nur, wenn gewünscht
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;
# ACME-Ausnahme
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
# Sicherstellen, dass kein alter Redirect liegen bleibt
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."

74
docker-compose.yaml Executable file → Normal file
View File

@@ -1,34 +1,86 @@
services: services:
openssh-server:
image: ghcr.io/linuxserver/openssh-server:latest
container_name: appServer_TunnelHead
hostname: appServerTunnel
ports:
- "2255:2222" # Access to R-Tunnel from public Internet
- "1883:1883" # Forwarded to Mosqitto
- "9222:9222" # SSH to Informatikweb
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Zurich
- PASSWORD_ACCESS=false
- USER_NAME=tunnel
- SUDO_ACCESS=false
- CUSTOM_CONT_INIT=true
volumes:
- /home/chk/Documents/appServerTunnelHead/custom-cont-init.d:/custom-cont-init.d
- /home/chk/Documents/appServerTunnelHead/config:/config
networks:
- appRobotNet
restart: unless-stopped
AppServerPortalUI: AppServerPortalUI:
image: nginx:alpine image: nginx:alpine
container_name: appServer_PortalUI container_name: appServer_PortalUI
volumes: volumes:
- /home/chk/Documents/AppServerPortalUI/nginx.conf:/etc/nginx/conf.d/default.conf:ro - /home/chk/Documents/appServerPortalUI/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- /home/chk/Documents/AppServerPortalUI/public:/usr/share/nginx/html:ro - /home/chk/Documents/appServerPortalUI/public:/usr/share/nginx/html:ro
- /home/chk/Documents/AppServerPortalUI/certs:/etc/ssl:ro # Let's Encrypt mounts
- /home/chk/Documents/appServerPortalUI/letsencrypt/conf:/etc/letsencrypt:ro
- /home/chk/Documents/appServerPortalUI/letsencrypt/www:/var/www/certbot:ro
# PortForwarding-Script-etc
- /home/chk/Documents/appServerPortalUI/forwarding.conf:/etc/nginx/forwarding.conf:ro
- /home/chk/Documents/appServerPortalUI/connect-proxies.sh:/docker-entrypoint.d/40-connect-proxies.sh:ro
ports: ports:
- "5080:80" - "80:80"
- "443:443" - "443:443"
networks: networks:
- default - appRobotNet
restart: unless-stopped restart: unless-stopped
command: ["nginx", "-g", "daemon off;"] command: ["nginx", "-g", "daemon off;"]
AppServerAuth: appServer_LetsEncryptFetcher:
image: node:18 image: certbot/certbot
container_name: appServer_Auth container_name: appServer_LetsEncryptFetcher
volumes: volumes:
- /home/chk/Documents/AppServerPortalUI/auth:/usr/src/app - /home/chk/Documents/appServerPortalUI/letsencrypt/conf:/etc/letsencrypt
- /home/chk/Documents/appServerPortalUI/letsencrypt/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do sleep 3600; done'"
AppServerAuth:
image: node:24-alpine
container_name: AppServerAuth
volumes:
- /home/chk/Documents/appServerPortalUI/auth:/usr/src/app
working_dir: /usr/src/app working_dir: /usr/src/app
command: sh -c "npm install && node auth.js" command: sh -c "npm install && node auth.js"
ports: ports:
- "10300:3000" # optional, für Tests - "10300:3000" # optional, für Tests
networks: networks:
- default - default
- appRobotNet
restart: unless-stopped restart: unless-stopped
appServerGuacamole:
image: abesnier/guacamole:latest
container_name: appServer_guacamole
ports:
- "8088:8080"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- /home/chk/Documents/appServerGuacamole/config:/config/guacamole
- /home/chk/Documents/appServerGuacamole/postgres:/config/postgres
networks:
- appRobotNet
restart: unless-stopped
networks: networks:
default: default:
appRobotNet:

View File

@@ -0,0 +1,813 @@
== PortalUI mit User-Verwaltung ==
Mein Server hat u.A. zwei Docker Container: Einmal das Portal, und einmal einen Authentifikations-Dienst.
AppServerPortalUI:
image: nginx:alpine
container_name: appServer_PortalUI
volumes:
- /home/chk/Documents/appServerPortalUI/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- /home/chk/Documents/appServerPortalUI/public:/usr/share/nginx/html:ro
# Let's Encrypt mounts
- /home/chk/Documents/appServerPortalUI/letsencrypt/conf:/etc/letsencrypt:ro
- /home/chk/Documents/appServerPortalUI/letsencrypt/www:/var/www/certbot:ro
# PortForwarding-Script-etc
- /home/chk/Documents/appServerPortalUI/forwarding.conf:/etc/nginx/forwarding.conf:ro
- /home/chk/Documents/appServerPortalUI/connect-proxies.sh:/docker-entrypoint.d/40-connect-proxies.sh:ro
ports:
- "80:80"
- "443:443"
networks:
- appRobotNet
restart: unless-stopped
command: ["nginx", "-g", "daemon off;"]
AppServerAuth:
image: node:24-alpine
container_name: appServer_Auth
volumes:
- /home/chk/Documents/appServerPortalUI/auth:/usr/src/app
working_dir: /usr/src/app
command: sh -c "npm install && node auth.js"
ports:
- "10300:3000" # optional, für Tests
networks:
- default
- appRobotNet
restart: unless-stopped
== /home/chk/Documents/appServerPortalUI/nginx.conf ==
# /etc/nginx/conf.d/default.conf
# IMMER aktive HTTP-Konfiguration (Port 80)
server {
listen 80 default_server;
listen [::]:80 default_server;
# ACME HTTP-01 Challenge (Certbot)
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type "text/plain; charset=utf-8";
}
# Deine Default-Page
root /usr/share/nginx/html;
index index.html;
location = / { try_files /index.html =404; }
location / { try_files $uri $uri/ =404; }
}
== nginxPages/10-server-schooltech.conf ==
Soll die "Nutzlast" die Pages, die der User verwenden soll, weiterleiten.
hier gibt es eine ganze reihe weiterer pages, die bereitstehen.
server {
listen 443 ssl http2;
server_name server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
root /usr/share/nginx/html;
index index.html;
# UI / SPA
location / {
try_files $uri $uri/ /index.html;
}
# API forwarding (auth)
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;
}
# Internal auth endpoint for auth_request
location = /nginxauth {
internal;
proxy_pass http://appserverauth:3000/internal/auth;
proxy_set_header Cookie $http_cookie;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
== nginxPages/50-subdomains-userA.conf ==
Für jeden User gibt es eine reihe von Pages, die eben hier weitergeleitet werden sollen.
server {
listen 443 ssl http2 default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
return 444;
}
# ------------------------------------------------------------
# portainer.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name portainer.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Auth nur auf UI
location / {
auth_request /nginxauth;
proxy_pass http://portainer:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# iFrame-freundlich
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors *" always;
}
location = /nginxauth {
internal;
proxy_pass http://appserverauth:3000/internal/auth;
proxy_set_header Cookie $http_cookie;
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
}
# ------------------------------------------------------------
# abc.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name abc.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
root /usr/share/nginx/abc;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
# ------------------------------------------------------------
# xyz.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name xyz.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
root /usr/share/nginx/xyz;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
diese sind alle unter xxx.server.schooltech.ch erreichbar.
Die ganze nginxPages/50-subdomains-userA.conf werden mit einem Script erstellt:
== connect-proxies.sh ==========
#!/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)
# >>> CHANGE: Resolver NICHT hardcoden. Dynamisch aus /etc/resolv.conf ableiten, Fallback 127.0.0.11
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
# <<< END CHANGE
# 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))
# trim + CR entfernen
LINE="$(printf '%s' "$RAW" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
[ -z "$LINE" ] && continue
case "$LINE" in \#*) continue;; esac
# Spalten splitten (mindestens 2 erforderlich)
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}"
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}"
# >>> NEW: Upstream normalisieren (Trailing Slash entfernen) + DNS-Check vorbereiten
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
# <<< END NEW
if [ -f "$FULLCHAIN" ] && [ -f "$PRIVKEY" ]; then
echo "[connect-proxies] [+] $SERVER_NAME: Zertifikat OK (cert_domain=$CERT_DOMAIN). Erzeuge 443 …"
# Fall A: local (statisch, kein proxy_pass)
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;
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 / {
try_files \$uri \$uri/ /index.html;
}
}
NGINX
else
# >>> NEW: Zwei Pfade DNS_OK=false => Placeholder; sonst Proxy mit Laufzeit-Resolver
if [ "$DNS_OK" = "false" ]; then
# 443 Placeholder keine Proxy-Verbindung, saubere 503
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
# 443 Proxy DNS ok/unknown: Laufzeit-Auflösung + freundlicher 503 bei Downstreams
cat > "$HTTPS_OUT" <<NGINX
# Auto-generated - 443 reverse proxy
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;
# Fehler sauber abfangen und 503 liefern (statt rohe 502/504)
proxy_intercept_errors on;
error_page 502 503 504 = @service_down;
location / {
# >>> CHANGE: variable proxy_pass -> DNS zur Laufzeit (verhindert nginx -t Crash)
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
# <<< END NEW
fi
# 80->443 Redirect-Server nur, wenn gewünscht
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;
# ACME-Ausnahme
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
# Sicherstellen, dass kein alter Redirect liegen bleibt
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."
=====
wobei die daten dann im forwarding.conf stehen:
# 444 → 8121 (WS/WSS)
# fluidncRedWs.server.schooltech.ch http://appServer_TunnelHead:8121 redirect true false fluidncRedWs.server.schooltech.ch 444
server.schooltech.ch local static false false
tcPortainer.server.schooltech.ch http://thinkcentre.local:9000 redirect true false
tcGuac.server.schooltech.ch http://thinkcentre.local:9000 redirect true false
#inf InformatiWeb ist per Tunnel angeschlossen. Soll auf 97xx Ports gehen
infPortainer.server.schooltech.ch http://appServer_TunnelHead:9903 redirect true false
infGuac.server.schooltech.ch http://appServer_TunnelHead:9980 redirect true false
#RP5 ist "Lokal" der Server
rp5Guac.server.schooltech.ch http://appServer_guacamole:8080 redirect true false
rp5Portainer.server.schooltech.ch http://portainer:9000 redirect true false
=== Das PortalUI stellt neben den Weiterleitungen auch noch eine Navigations-Page
zur Verfügung: index.html mit app.js
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<title>Service Portal</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header id="header">
<div class="logo">schooltech</div>
<nav id="services"></nav>
<div class="user">
<button id="login-btn">Login</button>
</div>
</header>
<main>
<h1>server.schooltech.ch - Service Portal</h1>
<!-- iFrame für Services -->
<iframe id="service-frame" style="width:100%;height:80vh;display:none;"></iframe>
<!-- Login Modal -->
<div id="login-modal" style="display:none;">
<label>User: <input type="text" id="username"/></label>
<label>Pass: <input type="password" id="password"/></label>
<button id="login-submit">Login</button>
<div id="login-msg"></div>
</div>
</main>
<script src="app.js"></script>
</body>
</html>
// ==== FRONTEND app.js ====
// Service-Liste
const services = [
{ id: "abc", name: "Control GamePad", url: "https://abc.server.schooltech.ch/" },
{ id: "xyz", name: "Guacamole", url: "https://xyz.server.schooltech.ch/" },
{ id: "sim", name: "Simulation", url: "https://simulation.server.schooltech.ch/" },
{ id: "portainer", name: "Portainer", url: "https://portainer.server.schooltech.ch/" }
];
// DOM-Elemente
const iframe = document.getElementById("service-frame");
const loginModal = document.getElementById("login-modal");
const loginBtn = document.getElementById("login-btn");
const loginSubmit = document.getElementById("login-submit");
const loginMsg = document.getElementById("login-msg");
const nav = document.getElementById("services");
const usernameInput = document.getElementById("username");
const passwordInput = document.getElementById("password");
let loggedIn = false;
// ===========================
// Login anzeigen
// ===========================
function switchToLogin() {
loginBtn.textContent = "Login";
loginBtn.onclick = () => {
loginModal.style.display = "block";
};
}
// ===========================
// Logout anzeigen
// ===========================
function switchToLogout() {
loginBtn.textContent = "Logout";
loginBtn.onclick = async () => {
try {
await fetch("/api/logout", { method: "POST" });
} catch (e) {
console.warn("Logout request failed:", e);
}
performLocalLogout();
};
}
// ===========================
// Lokales Logout
// ===========================
function performLocalLogout() {
loggedIn = false;
iframe.src = "";
iframe.style.display = "none";
nav.innerHTML = "";
loginModal.style.display = "block";
switchToLogin();
}
// ===========================
// Login-Logik
// ===========================
async function doLogin() {
const user = usernameInput.value;
const pass = passwordInput.value;
try {
const res = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ user, pass })
});
if (res.ok) {
loggedIn = true;
loginModal.style.display = "none";
loginMsg.textContent = "";
setupServiceButtons();
switchToLogout();
} else {
loginMsg.textContent = "Login fehlgeschlagen";
}
} catch (e) {
loginMsg.textContent = "Fehler: " + e.message;
}
}
loginSubmit.onclick = doLogin;
// Enter-Taste Login
[usernameInput, passwordInput].forEach(input => {
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
doLogin();
}
});
});
// ===========================
// Buttons erzeugen
// ===========================
function setupServiceButtons() {
nav.innerHTML = "";
services.forEach(svc => {
const btn = document.createElement("button");
btn.textContent = svc.name;
btn.onclick = async () => {
try {
await fetch('/api/event', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event: 'open',
service: svc.id,
url: svc.url
})
});
} catch(e) {
console.error('Event log failed', e);
}
openService(svc);
};
nav.appendChild(btn);
});
}
// ===========================
// Service öffnen
// ===========================
function openService(svc) {
iframe.src = svc.url;
iframe.style.display = "block";
window.scrollTo(0,0);
}
// ===========================
// Session Status beim Laden prüfen
// ===========================
(async function checkStatus() {
try {
const r = await fetch('/api/status');
if (r.ok) {
loggedIn = true;
setupServiceButtons();
switchToLogout();
} else {
switchToLogin();
}
} catch (e) {
switchToLogin();
}
})();
app.js muss sicherlich noch ausgebaut werden. Hier sind nur ein Bruchteil der relevanten
Pages verlinkt. Aber das kann später erfolgen.
== auth/auth.js
import express from "express";
import cookieParser from "cookie-parser";
import bcrypt from "bcrypt";
import fs from "fs";
import crypto from "crypto";
const USERS = JSON.parse(fs.readFileSync("./users.json"));
const SESSIONS = {}; // in-memory session store
const app = express();
app.use(express.json());
app.use(cookieParser());
app.post("/api/login", async (req,res)=>{
const { user, pass } = req.body;
console.log(`Auth-Service login attempt for ${user}`);
const hash = USERS[user];
if(!hash) return res.status(401).send({ ok:false });
const valid = await bcrypt.compare(pass, hash);
if(!valid) return res.status(401).send({ ok:false });
// create secure random session
const sessionID = crypto.randomBytes(32).toString("hex");
SESSIONS[sessionID] = {
user,
created: Date.now()
};
res.cookie("SESSIONID", sessionID, {
httpOnly: true,
secure: true,
domain: ".server.schooltech.ch",
sameSite: "None",
path: "/"
});
res.status(200).send({ ok:true });
});
// Logout endpoint
app.post("/api/logout", (req, res) => {
const sid = req.cookies.SESSIONID;
if (sid && SESSIONS[sid]) {
delete SESSIONS[sid];
}
// Cookie löschen
res.clearCookie("SESSIONID", {
httpOnly: true,
secure: true,
domain: ".server.schooltech.ch",
sameSite: "None",
path: "/"
});
return res.status(200).send({ ok: true });
});
// Event logging endpoint for frontend button presses
app.post('/api/event', (req,res)=>{
const svc = req.body.service || req.body.action || 'unknown';
const user = req.cookies.SESSIONID || 'anonymous';
console.log(`Event: user=${user} service=${svc} payload=${JSON.stringify(req.body)}`);
res.status(200).send({ ok:true });
});
// Optional für Nginx auth_request
app.get("/internal/auth", (req,res)=>{
const sid = req.cookies.SESSIONID;
if (sid && SESSIONS[sid]) {
return res.sendStatus(200);
}
return res.sendStatus(401);
});
// Status endpoint (unter /api so dass Nginx /api/ auf appserverauth proxyt)
app.get("/api/status", (req, res) => {
const sid = req.cookies.SESSIONID;
if (sid && SESSIONS[sid]) {
return res.status(200).send({ ok: true, user: SESSIONS[sid].user });
}
return res.status(401).send({ ok: false });
});
app.listen(3000, ()=>console.log("Auth-Service läuft auf 3000"));
==============
==============
Das läuft alles. Das ist leicht erweiterbar. Und soll eigentlich nicht geändert werden.
Aber ich brauche Authentifikation. User sollen sich einmal einloggen, wenn sei auf
die portalUI Index.html kommen. Das ist ja unter server.schooltech.ch erreichbar.
Danach soll für alle weiteren xyzABC.server.schooltech.ch Pages die Identifikation
akzeptiert werden.
Soweit ich das sehe, kann ich eine cookie basierte Identifikation auf server.schooltech.ch
laufen lassen, und dann später genau diese cookies in den einzelnen Pages überprüfen.
Fragen:
1) ist der aufbau für dich einigermassen klar? Stell Fragen falls etwas unklar ist
2) Wie kann ich die Page server.schooltech.ch Passwort-Schützen? Was muss ich einstellen,
dass per default nur die "Login" Option kommt, und sonst keine Links angezeigt werden?

View File

40
forwarding.conf Normal file
View File

@@ -0,0 +1,40 @@
# server_name upstream_url http_behavior websockets verify_upstream_tls [cert_domain]
#fluidncRed.server.schooltech.ch http://appServer_TunnelHead:8120 redirect true false
# 444 → 8121 (WS/WSS)
# fluidncRedWs.server.schooltech.ch http://appServer_TunnelHead:8121 redirect true false fluidncRedWs.server.schooltech.ch 444
#server.schooltech.ch local static false false
server.schooltech.ch local redirect false false server.schooltech.ch 443
tcPortainer.server.schooltech.ch http://thinkcentre.local:9000 redirect true false
tcGuac.server.schooltech.ch http://thinkcentre.local:9000 redirect true false
#inf InformatiWeb ist per Tunnel angeschlossen. Soll auf 97xx Ports gehen
infPortainer.server.schooltech.ch http://appServer_TunnelHead:9903 redirect true false
infGuac.server.schooltech.ch http://appServer_TunnelHead:9980 redirect true false
#RP5 ist "Lokal" der Server
rp5Guac.server.schooltech.ch http://appServer_guacamole:8080 redirect true false
rp5Portainer.server.schooltech.ch http://portainer:9000 redirect true false
#RP3 ist Raspi für die Scara-Robots, per Tunnel angeschlossen. Er hat 81xx Ports am TunnelHead
rp3Portainer.server.schooltech.ch http://appServer_TunnelHead:8100 redirect true false
rp3Guac.server.schooltech.ch http://appServer_TunnelHead:8180 redirect true false
fluidncRed.server.schooltech.ch http://appServer_TunnelHead:8120 redirect true false
fluidncWhite.server.schooltech.ch https://appServer_TunnelHead:8104 redirect true false
# ThinkCentre ist ein MiniPC der neben dem einen Roboter steht. Hier sind die 97xx Ports zugewiesen
tcGuac.server.schooltech.ch http://appServer_TunnelHead:9780 redirect false false
tcPortainer.server.schooltech.ch http://appServer_TunnelHead:9703 redirect false false
tcSimulation.server.schooltech.ch https://appServer_TunnelHead:9712 redirect true false
#tcVideocontroller.server.schooltech.ch https://tcvideo:9443 redirect true false
robotHoming.server.schooltech.ch https://appServer_TunnelHead:9793 redirect false false
tcControl.server.schooltech.ch https://appServer_TunnelHead:9710 redirect true false
# Beispiel mit abweichendem Zertifikats-Ordner (Lineage-Suffix)
# tcGuac.server.schooltech.ch https://guac:8443 redirect true false server.schooltech.ch-0002
# Beispiel für WS auf port+1 (zwei Einträge, einer nur für WS-Endpunkt)
# wsApp.server.schooltech.ch https://wsapp:443 redirect true false

View File

@@ -1 +1,39 @@
docker exec -it appServer_LetsEncryptFetcher certbot renew --dry-run for d in server.schooltech.ch \
tcControl.server.schooltech.ch \
tcGuac.server.schooltech.ch \
tcPortainer.server.schooltech.ch \
tcSimulation.server.schooltech.ch \
tcVideocontroller.server.schooltech.ch \
infGuac.server.schooltech.ch \
infPortainer.server.schooltech.ch \
infSimulation.server.schooltech.ch \
rp5Portainer.server.schooltech.ch \
rp5Guac.server.schooltech.ch \
rp5Simulation.server.schooltech.ch \
rp5Control.server.schooltech.ch \
rp3Portainer.server.schooltech.ch \
rp3Guac.server.schooltech.ch \
fluidncWhite.server.schooltech.ch \
scaraGreen.server.schooltech.ch \
scaraRed.server.schooltech.ch \
scaraGray.server.schooltech.ch \
scaraSilver.server.schooltech.ch \
scaraBlack.server.schooltech.ch \
scaraBlue.server.schooltech.ch \
scaraWhite.server.schooltech.ch \
scaraPortainer.server.schooltech.ch \
scaraSimu.server.schooltech.ch \
fluidncRed.server.schooltech.ch \
robotHoming.server.schooltech.ch \
robotControl.server.schooltech.ch \
robotVideo.server.schooltech.ch
do
docker exec -it appServer_LetsEncryptFetcher certbot certonly \
--webroot -w /var/www/certbot \
--cert-name "$d" \
-d "$d" \
--email admin@server.schooltech.ch \
--agree-tos --no-eff-email \
--keep-until-expiring
done

0
letsEncrypt_crontab.txt Executable file → Normal file
View File

View File

@@ -1,12 +0,0 @@
docker exec -it appServer_LetsEncryptFetcher certbot certonly \
--webroot \
-w /var/www/certbot \
-d server.schooltech.ch \
-d abc.server.schooltech.ch \
-d xyz.server.schooltech.ch \
-d portainer.server.schooltech.ch \
-d simulation3a29.server.schooltech.ch \
-d controller.server.schooltech.ch \
--email admin@server.schooltech.ch \
--agree-tos \
--no-eff-email

View File

@@ -16,6 +16,6 @@ server.schooltech.ch = /var/www/certbot
xyz.server.schooltech.ch = /var/www/certbot xyz.server.schooltech.ch = /var/www/certbot
controller.server.schooltech.ch = /var/www/certbot controller.server.schooltech.ch = /var/www/certbot
portainer.server.schooltech.ch = /var/www/certbot portainer.server.schooltech.ch = /var/www/certbot
simulation3a29.server.schooltech.ch = /var/www/certbot simulation.server.schooltech.ch = /var/www/certbot
[acme_renewal_info] [acme_renewal_info]
ari_retry_after = 2026-02-14T07:10:40 ari_retry_after = 2026-02-19T08:25:19

View File

@@ -1,432 +1,29 @@
error_log /var/log/nginx/error.log info; # /etc/nginx/conf.d/default.conf
resolver 127.0.0.11 valid=30s;
resolver_timeout 5s;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# ------------------------------------------------------------
# Default HTTP -> HTTPS redirect
# ------------------------------------------------------------
server { server {
listen 80 default_server; listen 80 default_server;
server_name _; listen [::]:80 default_server;
location /.well-known/acme-challenge/ { # ACME HTTP-01 Challenge (Certbot)
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot; root /var/www/certbot;
default_type "text/plain; charset=utf-8";
} }
location / { # PortalUI root
return 301 https://$host$request_uri;
}
}
# ------------------------------------------------------------
# #***# DEFAULT 443 SERVER (NEU)
# Verhindert, dass der erste 443-vHost andere Subdomains "abfängt"
# ------------------------------------------------------------
server {
listen 443 ssl http2 default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Einfach Verbindung schließen für unbekannte Hosts
return 444;
}
# ------------------------------------------------------------
# UI (portal) - nur für server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
location / { # === API forwarding for Auth-Service ===
try_files $uri $uri/ /index.html;
}
# API forwarding to auth (wie vorher) - nur für server.schooltech.ch
location /api/ { location /api/ {
proxy_pass http://appServer_Auth:3000/api/; proxy_pass http://appserverauth:3000/api/;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
} }
# ------------------------------------------------------------ # === SPA routing (Portal UI) ===
# Internal auth endpoint for auth_request (used by other server blocks)
# Einheitlicher nginxauth-Block: Host + URI an Auth-Service
# ------------------------------------------------------------
location = /nginxauth {
internal;
proxy_pass http://appServer_Auth:3000/internal/auth; #***# AUTH: proxy_pass (wichtig)
proxy_set_header Cookie $http_cookie; #***# AUTH: Cookie weitergeben
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
# Security
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# SSL
tcp_nopush on;
tcp_nodelay on;
}
# ------------------------------------------------------------
# abc.server.schooltech.ch - Controller on ThinkCentre
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name abc.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
set $serverBackendControl "appRobot_Control:10010";
set $auth_backend "appServer_Auth:3000";
location @fallback {
default_type text/html;
return 200 '
<html>
<head>
<title>Dienst offline</title>
</head>
<body style="font-family:sans-serif;text-align:center;padding-top:10%">
<h1>Ein Dienst ist momentan nicht erreichbar</h1>
<p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
</body>
</html>';
}
# ---- Static assets: keine Auth, damit Browser die .js/.css korrekt erhält ----
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp)$ {
proxy_pass https://$serverBackendControl;
proxy_set_header Host thinkcentre.local;
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 https;
# kein proxy_set_header Connection / Upgrade hier
}
# ---- WebSocket-Endpoint (falls z.B. /echo) - auth prüfen ----
location /echo {
auth_request /nginxauth;
proxy_pass https://$serverBackendControl/echo;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_set_header Host thinkcentre.local;
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 https;
}
# ---- Hauptanwendung (HTML, API-Aufrufe) - auth prüfen ----
location / { location / {
auth_request /nginxauth; try_files $uri $uri/ /index.html;
proxy_pass https://$serverBackendControl/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_set_header Host thinkcentre.local;
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 https;
proxy_set_header Origin $http_origin;
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
} }
}
# /nginxauth (lokal für diesen vhost, aber internal request wird an auth-service weitergeleitet)
location = /nginxauth {
internal;
proxy_pass http://$auth_backend/internal/auth; #***# AUTH
proxy_set_header Cookie $http_cookie; #***# AUTH
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
# ------------------------------------------------------------
# simulation3a29.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name simulation3a29.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
set $serverBackendSimulation "appRobot_Simulation:1003";
set $auth_backend "appServer_Auth:3000";
location @fallback {
default_type text/html;
return 200 '
<html>
<head>
<title>Dienst (Simulation) offline</title>
</head>
<body style="font-family:sans-serif;text-align:center;padding-top:10%">
<h1>Ein Dienst ist momentan nicht erreichbar</h1>
<p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
</body>
</html>';
}
# ---- Static assets: keine Auth ----
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp|stl)$ {
proxy_pass https://$serverBackendSimulation;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_set_header Host thinkcentre.local;
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 https;
# kein proxy_set_header Connection / Upgrade hier
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
# ---- WebSocket-Endpoint ----
location /echo {
auth_request /nginxauth;
proxy_pass https://$serverBackendSimulation/echo;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host thinkcentre.local;
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 https;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
# ---- Hauptanwendung ----
location / {
auth_request /nginxauth;
proxy_pass https://$serverBackendSimulation/;
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host thinkcentre.local;
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 https;
proxy_set_header Origin $http_origin;
proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
# ------------------------------------------------------------
# location = /nginxauth (SIMULATION FIX)
# ------------------------------------------------------------
location = /nginxauth {
internal;
proxy_pass http://$auth_backend/internal/auth; #***# AUTH
proxy_set_header Cookie $http_cookie; #***# AUTH
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
}
}
# ------------------------------------------------------------
# xyz.server.schooltech.ch (Guacamole )
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name xyz.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
set $serverBackendGuacamole "appServer_guacamole:8080";
location @fallback {
default_type text/html;
return 200 '
<html>
<head>
<title>Dienst (Guacamole) offline</title>
</head>
<body style="font-family:sans-serif;text-align:center;padding-top:10%">
<h1>Ein Dienst ist momentan nicht erreichbar</h1>
<p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
</body>
</html>';
}
location / {
proxy_pass http://$serverBackendGuacamole;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
}
## ------------------------------------------------------------
# portainer.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name portainer.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
set $auth_backend "appServer_Auth:3000";
location @fallback {
default_type text/html;
return 200 '
<html>
<head>
<title>Dienst (Portainer) offline</title>
</head>
<body style="font-family:sans-serif;text-align:center;padding-top:10%">
<h1>Ein Dienst ist momentan nicht erreichbar</h1>
<p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
</body>
</html>';
}
location / {
proxy_pass http://portainer:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
proxy_set_header Connection $http_connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WICHTIG FÜR IFRAME
proxy_hide_header X-Frame-Options;
proxy_hide_header Content-Security-Policy;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback; # <-- Fallback
}
location = /nginxauth {
internal;
proxy_pass http://$auth_backend/internal/auth;
proxy_set_header Cookie $http_cookie;
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback; # <-- Fallback
}
}

View File

@@ -1,345 +1,432 @@
error_log /var/log/nginx/error.log info; error_log /var/log/nginx/error.log info;
map $http_upgrade $connection_upgrade { resolver 127.0.0.11 valid=30s;
default upgrade; resolver_timeout 5s;
'' close;
} map $http_upgrade $connection_upgrade {
default upgrade;
# ------------------------------------------------------------ '' close;
# Default HTTP -> HTTPS redirect }
# ------------------------------------------------------------
server {
listen 80 default_server;
server_name _;
# ------------------------------------------------------------
location /.well-known/acme-challenge/ { # Default HTTP -> HTTPS redirect
root /var/www/certbot; # ------------------------------------------------------------
} server {
listen 80 default_server;
location / { server_name _;
return 301 https://$host$request_uri;
} location /.well-known/acme-challenge/ {
} root /var/www/certbot;
}
# ------------------------------------------------------------
# #***# DEFAULT 443 SERVER (NEU) location / {
# Verhindert, dass der erste 443-vHost andere Subdomains "abfängt" return 301 https://$host$request_uri;
# ------------------------------------------------------------ }
server { }
listen 443 ssl http2 default_server;
server_name _; # ------------------------------------------------------------
# #***# DEFAULT 443 SERVER (NEU)
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem; # Verhindert, dass der erste 443-vHost andere Subdomains "abfängt"
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem; # ------------------------------------------------------------
ssl_protocols TLSv1.2 TLSv1.3; server {
listen 443 ssl http2 default_server;
# Einfach Verbindung schließen für unbekannte Hosts server_name _;
return 444;
} ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
# ------------------------------------------------------------ ssl_protocols TLSv1.2 TLSv1.3;
# UI (portal) - nur für server.schooltech.ch ssl_prefer_server_ciphers on;
# ------------------------------------------------------------
server { # Einfach Verbindung schließen für unbekannte Hosts
listen 443 ssl http2; return 444;
server_name server.schooltech.ch; }
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem; # ------------------------------------------------------------
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem; # UI (portal) - nur für server.schooltech.ch
ssl_protocols TLSv1.2 TLSv1.3; # ------------------------------------------------------------
ssl_prefer_server_ciphers on; server {
listen 443 ssl http2;
root /usr/share/nginx/html; server_name server.schooltech.ch;
index index.html;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
location / { ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
try_files $uri $uri/ /index.html; ssl_protocols TLSv1.2 TLSv1.3;
} ssl_prefer_server_ciphers on;
# API forwarding to auth (wie vorher) - nur für server.schooltech.ch
location /api/ { root /usr/share/nginx/html;
proxy_pass http://appserverauth:3000/api/; index index.html;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; try_files $uri $uri/ /index.html;
proxy_set_header X-Forwarded-Proto $scheme; }
}
# API forwarding to auth (wie vorher) - nur für server.schooltech.ch
# ------------------------------------------------------------ location /api/ {
# Internal auth endpoint for auth_request (used by other server blocks) proxy_pass http://appServer_Auth:3000/api/;
# Einheitlicher nginxauth-Block: Host + URI an Auth-Service proxy_set_header Host $host;
# ------------------------------------------------------------ proxy_set_header X-Real-IP $remote_addr;
location = /nginxauth { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
internal; proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://appserverauth:3000/internal/auth; #***# AUTH: proxy_pass (wichtig)
proxy_set_header Cookie $http_cookie; #***# AUTH: Cookie weitergeben proxy_buffering off;
proxy_request_buffering off;
#***# AUTH HEADER ERWEITERUNG }
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host; # ------------------------------------------------------------
proxy_set_header X-Forwarded-Host $host; # Internal auth endpoint for auth_request (used by other server blocks)
} # Einheitlicher nginxauth-Block: Host + URI an Auth-Service
# ------------------------------------------------------------
# Security location = /nginxauth {
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; internal;
} proxy_pass http://appServer_Auth:3000/internal/auth; #***# AUTH: proxy_pass (wichtig)
proxy_set_header Cookie $http_cookie; #***# AUTH: Cookie weitergeben
# ------------------------------------------------------------
# abc.server.schooltech.ch - Controller on ThinkCentre #***# AUTH HEADER ERWEITERUNG
# ------------------------------------------------------------ proxy_set_header X-Original-URI $request_uri;
server { proxy_set_header X-Original-Host $host;
listen 443 ssl http2; proxy_set_header X-Forwarded-Host $host;
server_name abc.server.schooltech.ch; }
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem; # Security
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on; # SSL
tcp_nopush on;
# ---- Static assets: keine Auth, damit Browser die .js/.css korrekt erhält ---- tcp_nodelay on;
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp)$ { }
proxy_pass https://thinkcentre.local:10010;
proxy_set_header Host thinkcentre.local; # ------------------------------------------------------------
proxy_set_header X-Real-IP $remote_addr; # abc.server.schooltech.ch - Controller on ThinkCentre
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # ------------------------------------------------------------
proxy_set_header X-Forwarded-Proto https; server {
# kein proxy_set_header Connection / Upgrade hier listen 443 ssl http2;
} server_name abc.server.schooltech.ch;
# ---- WebSocket-Endpoint (falls z.B. /echo) - auth prüfen ---- ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
location /echo { ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
auth_request /nginxauth; ssl_protocols TLSv1.2 TLSv1.3;
proxy_pass https://thinkcentre.local:10010/echo; ssl_prefer_server_ciphers on;
proxy_http_version 1.1; set $serverBackendControl "appRobot_Control:10010";
proxy_set_header Upgrade $http_upgrade; set $auth_backend "appServer_Auth:3000";
proxy_set_header Connection $connection_upgrade;
proxy_ssl_server_name on; location @fallback {
proxy_ssl_name thinkcentre.local; default_type text/html;
proxy_ssl_verify off; return 200 '
<html>
proxy_set_header Host thinkcentre.local; <head>
proxy_set_header X-Real-IP $remote_addr; <title>Dienst offline</title>
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; </head>
proxy_set_header X-Forwarded-Proto https; <body style="font-family:sans-serif;text-align:center;padding-top:10%">
} <h1>Ein Dienst ist momentan nicht erreichbar</h1>
<p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
# ---- Hauptanwendung (HTML, API-Aufrufe) - auth prüfen ---- </body>
location / { </html>';
auth_request /nginxauth; }
proxy_pass https://thinkcentre.local:10010/; # ---- Static assets: keine Auth, damit Browser die .js/.css korrekt erhält ----
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp)$ {
proxy_http_version 1.1; proxy_pass https://$serverBackendControl;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Host thinkcentre.local;
proxy_set_header Connection $connection_upgrade; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on; proxy_set_header X-Forwarded-Proto https;
proxy_ssl_name thinkcentre.local; # kein proxy_set_header Connection / Upgrade hier
proxy_ssl_verify off; }
proxy_set_header Host thinkcentre.local; # ---- WebSocket-Endpoint (falls z.B. /echo) - auth prüfen ----
proxy_set_header X-Real-IP $remote_addr; location /echo {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; auth_request /nginxauth;
proxy_set_header X-Forwarded-Proto https; proxy_pass https://$serverBackendControl/echo;
proxy_set_header Origin $http_origin;
proxy_http_version 1.1;
proxy_hide_header X-Frame-Options; proxy_set_header Upgrade $http_upgrade;
add_header X-Frame-Options "ALLOWALL" always; proxy_set_header Connection $connection_upgrade;
proxy_hide_header Content-Security-Policy; proxy_ssl_server_name on;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always; proxy_ssl_name thinkcentre.local;
} proxy_ssl_verify off;
# /nginxauth (lokal für diesen vhost, aber internal request wird an auth-service weitergeleitet) proxy_set_header Host thinkcentre.local;
location = /nginxauth { proxy_set_header X-Real-IP $remote_addr;
internal; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://appserverauth:3000/internal/auth; #***# AUTH proxy_set_header X-Forwarded-Proto https;
proxy_set_header Cookie $http_cookie; #***# AUTH }
#***# AUTH HEADER ERWEITERUNG # ---- Hauptanwendung (HTML, API-Aufrufe) - auth prüfen ----
proxy_set_header X-Original-URI $request_uri; location / {
proxy_set_header X-Original-Host $host; auth_request /nginxauth;
proxy_set_header X-Forwarded-Host $host;
} proxy_pass https://$serverBackendControl/;
}
proxy_http_version 1.1;
# ------------------------------------------------------------ proxy_set_header Upgrade $http_upgrade;
# simulation3a29.server.schooltech.ch proxy_set_header Connection $connection_upgrade;
# ------------------------------------------------------------
server { proxy_ssl_server_name on;
listen 443 ssl http2; proxy_ssl_name thinkcentre.local;
server_name simulation3a29.server.schooltech.ch; proxy_ssl_verify off;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem; proxy_set_header Host thinkcentre.local;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem; proxy_set_header X-Real-IP $remote_addr;
ssl_protocols TLSv1.2 TLSv1.3; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
ssl_prefer_server_ciphers on; proxy_set_header X-Forwarded-Proto https;
proxy_set_header Origin $http_origin;
# ---- Static assets: keine Auth ----
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp)$ { proxy_hide_header X-Frame-Options;
proxy_pass https://thinkcentre.local:1003; add_header X-Frame-Options "ALLOWALL" always;
proxy_set_header Host thinkcentre.local;
proxy_set_header X-Real-IP $remote_addr; proxy_hide_header Content-Security-Policy;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
proxy_set_header X-Forwarded-Proto https; }
# kein proxy_set_header Connection / Upgrade hier
} # /nginxauth (lokal für diesen vhost, aber internal request wird an auth-service weitergeleitet)
location = /nginxauth {
# ---- WebSocket-Endpoint ---- internal;
location /echo { proxy_pass http://$auth_backend/internal/auth; #***# AUTH
auth_request /nginxauth; proxy_set_header Cookie $http_cookie; #***# AUTH
proxy_pass https://thinkcentre.local:1003/echo;
#***# AUTH HEADER ERWEITERUNG
proxy_http_version 1.1; proxy_set_header X-Original-URI $request_uri;
proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Original-Host $host;
proxy_set_header Connection $connection_upgrade; proxy_set_header X-Forwarded-Host $host;
}
proxy_ssl_server_name on;
proxy_ssl_name thinkcentre.local; proxy_intercept_errors on;
proxy_ssl_verify off; error_page 502 503 504 = @fallback;
}
proxy_set_header Host thinkcentre.local;
proxy_set_header X-Real-IP $remote_addr; # ------------------------------------------------------------
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # simulation3a29.server.schooltech.ch
proxy_set_header X-Forwarded-Proto https; # ------------------------------------------------------------
} server {
listen 443 ssl http2;
# ---- Hauptanwendung ---- server_name simulation3a29.server.schooltech.ch;
location / {
auth_request /nginxauth; ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
proxy_pass https://thinkcentre.local:1003/; ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; set $serverBackendSimulation "appRobot_Simulation:1003";
proxy_set_header Connection $connection_upgrade; set $auth_backend "appServer_Auth:3000";
proxy_ssl_server_name on; location @fallback {
proxy_ssl_name thinkcentre.local; default_type text/html;
proxy_ssl_verify off; return 200 '
<html>
proxy_set_header Host thinkcentre.local; <head>
proxy_set_header X-Real-IP $remote_addr; <title>Dienst (Simulation) offline</title>
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; </head>
proxy_set_header X-Forwarded-Proto https; <body style="font-family:sans-serif;text-align:center;padding-top:10%">
proxy_set_header Origin $http_origin; <h1>Ein Dienst ist momentan nicht erreichbar</h1>
<p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
proxy_hide_header X-Frame-Options; </body>
add_header X-Frame-Options "ALLOWALL" always; </html>';
}
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always; # ---- Static assets: keine Auth ----
} location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp|stl)$ {
proxy_pass https://$serverBackendSimulation;
# ------------------------------------------------------------
# location = /nginxauth (SIMULATION FIX) proxy_ssl_server_name on;
# ------------------------------------------------------------ proxy_ssl_name thinkcentre.local;
location = /nginxauth { proxy_ssl_verify off;
internal;
proxy_pass http://appserverauth:3000/internal/auth; #***# SIMULATION: fehlende proxy_pass eingefügt proxy_set_header Host thinkcentre.local;
proxy_set_header Cookie $http_cookie; #***# SIMULATION: Cookie weitergeben proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#***# AUTH HEADER ERWEITERUNG (nur einmal) proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Original-URI $request_uri; # kein proxy_set_header Connection / Upgrade hier
proxy_set_header X-Original-Host $host; proxy_intercept_errors on;
proxy_set_header X-Forwarded-Host $host; error_page 502 503 504 = @fallback;
} }
}
# ---- WebSocket-Endpoint ----
# ------------------------------------------------------------ location /echo {
# xyz.server.schooltech.ch (Guacamole on ThinkCentre) auth_request /nginxauth;
# ------------------------------------------------------------ proxy_pass https://$serverBackendSimulation/echo;
server {
listen 443 ssl http2; proxy_ssl_server_name on;
server_name xyz.server.schooltech.ch; proxy_ssl_name thinkcentre.local;
proxy_ssl_verify off;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem; proxy_http_version 1.1;
ssl_protocols TLSv1.2 TLSv1.3; proxy_set_header Upgrade $http_upgrade;
ssl_prefer_server_ciphers on; proxy_set_header Connection $connection_upgrade;
location / { proxy_set_header Host thinkcentre.local;
auth_request /nginxauth; # Auth prüfen proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://thinkcentre.local:8080/; proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1; proxy_intercept_errors on;
proxy_set_header Upgrade $http_upgrade; error_page 502 503 504 = @fallback;
proxy_set_header Connection $connection_upgrade; }
# Proxy Header (Upstream erwartet thinkcentre.local) # ---- Hauptanwendung ----
proxy_set_header Host thinkcentre.local; # bewusst: Upstream Host-Expectation location / {
proxy_set_header Origin $http_origin; auth_request /nginxauth;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass https://$serverBackendSimulation/;
proxy_set_header X-Forwarded-Proto https;
proxy_ssl_server_name on;
# iFrame erlauben proxy_ssl_name thinkcentre.local;
proxy_hide_header X-Frame-Options; proxy_ssl_verify off;
add_header X-Frame-Options "ALLOWALL" always;
proxy_http_version 1.1;
proxy_hide_header Content-Security-Policy; proxy_set_header Upgrade $http_upgrade;
add_header Content-Security-Policy "frame-ancestors *" always; proxy_set_header Connection $connection_upgrade;
}
proxy_set_header Host thinkcentre.local;
location = /nginxauth { proxy_set_header X-Real-IP $remote_addr;
internal; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://appserverauth:3000/internal/auth; #***# XYZ: proxy_pass wie überall proxy_set_header X-Forwarded-Proto https;
proxy_set_header Cookie $http_cookie; proxy_set_header Origin $http_origin;
proxy_set_header X-Original-URI $request_uri; proxy_hide_header X-Frame-Options;
add_header X-Frame-Options "ALLOWALL" always;
#***# XYZ AUTH HOST: Original-Host weitergeben (wichtig für Redirects/Checks)
proxy_set_header X-Original-Host $host; proxy_hide_header Content-Security-Policy;
proxy_set_header X-Forwarded-Host $host; add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
}
} proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
# ------------------------------------------------------------ }
# portainer.server.schooltech.ch
# ------------------------------------------------------------ # ------------------------------------------------------------
server { # location = /nginxauth (SIMULATION FIX)
listen 443 ssl http2; # ------------------------------------------------------------
server_name portainer.server.schooltech.ch; location = /nginxauth {
internal;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem; proxy_pass http://$auth_backend/internal/auth; #***# AUTH
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem; proxy_set_header Cookie $http_cookie; #***# AUTH
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on; #***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
location / { proxy_set_header X-Original-Host $host;
auth_request /nginxauth; proxy_set_header X-Forwarded-Host $host;
}
proxy_pass http://portainer:9000; }
proxy_http_version 1.1;
# ------------------------------------------------------------
proxy_set_header Upgrade $http_upgrade; # xyz.server.schooltech.ch (Guacamole )
proxy_set_header Connection "upgrade"; # ------------------------------------------------------------
server {
proxy_set_header Host $host; listen 443 ssl http2;
proxy_set_header X-Forwarded-Proto https; server_name xyz.server.schooltech.ch;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
# WICHTIG FÜR IFRAME ssl_protocols TLSv1.2 TLSv1.3;
proxy_hide_header X-Frame-Options; ssl_prefer_server_ciphers on;
proxy_hide_header Content-Security-Policy;
set $serverBackendGuacamole "appServer_guacamole:8080";
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always; location @fallback {
} default_type text/html;
return 200 '
<html>
location = /nginxauth { <head>
internal; <title>Dienst (Guacamole) offline</title>
proxy_pass http://appserverauth:3000/internal/auth; #***# AUTH </head>
proxy_set_header Cookie $http_cookie; #***# AUTH <body style="font-family:sans-serif;text-align:center;padding-top:10%">
<h1>Ein Dienst ist momentan nicht erreichbar</h1>
#***# AUTH HEADER ERWEITERUNG <p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
proxy_set_header X-Original-URI $request_uri; </body>
proxy_set_header X-Original-Host $host; </html>';
proxy_set_header X-Forwarded-Host $host; }
}
} location / {
proxy_pass http://$serverBackendGuacamole;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_request_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
}
## ------------------------------------------------------------
# portainer.server.schooltech.ch
# ------------------------------------------------------------
server {
listen 443 ssl http2;
server_name portainer.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
set $auth_backend "appServer_Auth:3000";
location @fallback {
default_type text/html;
return 200 '
<html>
<head>
<title>Dienst (Portainer) offline</title>
</head>
<body style="font-family:sans-serif;text-align:center;padding-top:10%">
<h1>Ein Dienst ist momentan nicht erreichbar</h1>
<p>Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.</p>
</body>
</html>';
}
location / {
proxy_pass http://portainer:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
proxy_set_header Connection $http_connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WICHTIG FÜR IFRAME
proxy_hide_header X-Frame-Options;
proxy_hide_header Content-Security-Policy;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors https://server.schooltech.ch" always;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback; # <-- Fallback
}
location = /nginxauth {
internal;
proxy_pass http://$auth_backend/internal/auth;
proxy_set_header Cookie $http_cookie;
#***# AUTH HEADER ERWEITERUNG
proxy_set_header X-Original-URI $request_uri;
proxy_set_header X-Original-Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback; # <-- Fallback
}
}

View File

@@ -10,6 +10,8 @@ server {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
auth_request /nginxauth;
# UI / SPA # UI / SPA
location / { location / {
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;

View File

@@ -2,10 +2,10 @@
// Service-Liste // Service-Liste
const services = [ const services = [
{ id: "abc", name: "Control GamePad", url: "https://abc.server.schooltech.ch/" }, { id: "abc", name: "Control GamePad", url: "https://tccontrol.server.schooltech.ch/" },
{ id: "xyz", name: "Guacamole", url: "https://xyz.server.schooltech.ch/" }, { id: "xyz", name: "Guacamole", url: "https://rp5guac.server.schooltech.ch/" },
{ id: "sim", name: "Simulation", url: "https://simulation3a29.server.schooltech.ch/" }, { id: "sim", name: "Simulation", url: "https://tcSimulation.server.schooltech.ch/" },
{ id: "portainer", name: "Portainer", url: "https://portainer.server.schooltech.ch/" } { id: "portainer", name: "Portainer", url: "https://rp5portainer.server.schooltech.ch/" }
]; ];
// DOM-Elemente // DOM-Elemente