From 52d656138564ee73960af0e187d9e8e567d43c68 Mon Sep 17 00:00:00 2001 From: chk Date: Sat, 21 Mar 2026 11:09:44 +0100 Subject: [PATCH] Login --- .forwarding.conf.swp | 0 .gitignore | 10 + __nginx.conf__ohne_Portainer_aber_lief | 375 -------- __split_subpages_nginx.__conf__ | 31 - auth/node_modules/.bin/mime | 9 +- auth/node_modules/.bin/node-gyp-build | 85 +- .../node_modules/.bin/node-gyp-build-optional | 8 +- auth/node_modules/.bin/node-gyp-build-test | 20 +- auth/users.json | 3 +- connect-proxies.sh | 283 ++++++ docker-compose.yaml | 74 +- dokumentation/2026_03_21___q1_Auth.txt | 813 ++++++++++++++++++ dokumentation/2026_03_21___q2_WS.txt | 0 forwarding.conf | 40 + letsEncrypt.sh | 40 +- letsEncrypt_crontab.txt | 0 letsEncrypt_init.sh | 12 - .../conf/renewal/server.schooltech.ch.conf | 4 +- nginx.conf | 425 +-------- ..._vibeCode_zerhauen => nginx.conf_PortalSSH | 777 +++++++++-------- nginxPages/10-server-schooltech.conf | 2 + public/app.js | 8 +- 22 files changed, 1819 insertions(+), 1200 deletions(-) create mode 100644 .forwarding.conf.swp create mode 100755 .gitignore delete mode 100644 __nginx.conf__ohne_Portainer_aber_lief delete mode 100644 __split_subpages_nginx.__conf__ mode change 120000 => 100755 auth/node_modules/.bin/mime mode change 120000 => 100755 auth/node_modules/.bin/node-gyp-build mode change 120000 => 100755 auth/node_modules/.bin/node-gyp-build-optional mode change 120000 => 100755 auth/node_modules/.bin/node-gyp-build-test create mode 100755 connect-proxies.sh mode change 100755 => 100644 docker-compose.yaml create mode 100755 dokumentation/2026_03_21___q1_Auth.txt create mode 100755 dokumentation/2026_03_21___q2_WS.txt create mode 100644 forwarding.conf mode change 100755 => 100644 letsEncrypt_crontab.txt delete mode 100755 letsEncrypt_init.sh rename _nginx.conf_vibeCode_zerhauen => nginx.conf_PortalSSH (69%) diff --git a/.forwarding.conf.swp b/.forwarding.conf.swp new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..37c6b03 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/__nginx.conf__ohne_Portainer_aber_lief b/__nginx.conf__ohne_Portainer_aber_lief deleted file mode 100644 index 135a91e..0000000 --- a/__nginx.conf__ohne_Portainer_aber_lief +++ /dev/null @@ -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; - } -} diff --git a/__split_subpages_nginx.__conf__ b/__split_subpages_nginx.__conf__ deleted file mode 100644 index 960b0bd..0000000 --- a/__split_subpages_nginx.__conf__ +++ /dev/null @@ -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; -} - diff --git a/auth/node_modules/.bin/mime b/auth/node_modules/.bin/mime deleted file mode 120000 index fbb7ee0..0000000 --- a/auth/node_modules/.bin/mime +++ /dev/null @@ -1 +0,0 @@ -../mime/cli.js \ No newline at end of file diff --git a/auth/node_modules/.bin/mime b/auth/node_modules/.bin/mime new file mode 100755 index 0000000..20b1ffe --- /dev/null +++ b/auth/node_modules/.bin/mime @@ -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'); + diff --git a/auth/node_modules/.bin/node-gyp-build b/auth/node_modules/.bin/node-gyp-build deleted file mode 120000 index 671c6eb..0000000 --- a/auth/node_modules/.bin/node-gyp-build +++ /dev/null @@ -1 +0,0 @@ -../node-gyp-build/bin.js \ No newline at end of file diff --git a/auth/node_modules/.bin/node-gyp-build b/auth/node_modules/.bin/node-gyp-build new file mode 100755 index 0000000..c778e0a --- /dev/null +++ b/auth/node_modules/.bin/node-gyp-build @@ -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 + } +} diff --git a/auth/node_modules/.bin/node-gyp-build-optional b/auth/node_modules/.bin/node-gyp-build-optional deleted file mode 120000 index 46d347e..0000000 --- a/auth/node_modules/.bin/node-gyp-build-optional +++ /dev/null @@ -1 +0,0 @@ -../node-gyp-build/optional.js \ No newline at end of file diff --git a/auth/node_modules/.bin/node-gyp-build-optional b/auth/node_modules/.bin/node-gyp-build-optional new file mode 100755 index 0000000..8daa04a --- /dev/null +++ b/auth/node_modules/.bin/node-gyp-build-optional @@ -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) diff --git a/auth/node_modules/.bin/node-gyp-build-test b/auth/node_modules/.bin/node-gyp-build-test deleted file mode 120000 index d11de1b..0000000 --- a/auth/node_modules/.bin/node-gyp-build-test +++ /dev/null @@ -1 +0,0 @@ -../node-gyp-build/build-test.js \ No newline at end of file diff --git a/auth/node_modules/.bin/node-gyp-build-test b/auth/node_modules/.bin/node-gyp-build-test new file mode 100755 index 0000000..b6622a5 --- /dev/null +++ b/auth/node_modules/.bin/node-gyp-build-test @@ -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('./')() diff --git a/auth/users.json b/auth/users.json index 89f12db..fed6288 100755 --- a/auth/users.json +++ b/auth/users.json @@ -1,3 +1,4 @@ { - "admin": "$2b$12$a6.TdWrJruzs1qfwfMBNsuJApkRwUiDG5IZfbtclNPhYEAsIJGMHC" + "admin": "$2b$12$a6.TdWrJruzs1qfwfMBNsuJApkRwUiDG5IZfbtclNPhYEAsIJGMHC", + "abc": "$2b$12$HurtBQkjJyXOV2qFpWkFv.wAIRs6B01sSSScaHMdUrbuBWE0aoyIa" } \ No newline at end of file diff --git a/connect-proxies.sh b/connect-proxies.sh new file mode 100755 index 0000000..f6066a1 --- /dev/null +++ b/connect-proxies.sh @@ -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" </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" <>> 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" <Service temporarily unavailable

\$server_name nicht erreichbar

DNS-Auflösung fehlgeschlagen. Bitte später erneut versuchen.

"; + } +} +NGINX + else + # 443 Proxy – DNS ok/unknown: Laufzeit-Auflösung + freundlicher 503 bei Downstreams + cat > "$HTTPS_OUT" <>> 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 "Service temporarily unavailable

$server_name nicht erreichbar

Der Dienst ist momentan nicht verfügbar. Bitte später erneut versuchen.

"; + } + +} +NGINX + fi + # <<< END NEW + fi + + # 80->443 Redirect-Server nur, wenn gewünscht + if [ "$HTTP_BEHAVIOR" = "redirect" ]; then + cat > "$HTTP_REDIRECT_OUT" <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." \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml old mode 100755 new mode 100644 index 9f2e4dd..7097aea --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,34 +1,86 @@ 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: 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 - - /home/chk/Documents/AppServerPortalUI/certs:/etc/ssl:ro + - /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: - - "5080:80" + - "80:80" - "443:443" networks: - - default + - appRobotNet restart: unless-stopped command: ["nginx", "-g", "daemon off;"] - - AppServerAuth: - image: node:18 - container_name: appServer_Auth + + appServer_LetsEncryptFetcher: + image: certbot/certbot + container_name: appServer_LetsEncryptFetcher 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 command: sh -c "npm install && node auth.js" ports: - "10300:3000" # optional, für Tests networks: - default + - appRobotNet 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: default: - \ No newline at end of file + appRobotNet: diff --git a/dokumentation/2026_03_21___q1_Auth.txt b/dokumentation/2026_03_21___q1_Auth.txt new file mode 100755 index 0000000..5243979 --- /dev/null +++ b/dokumentation/2026_03_21___q1_Auth.txt @@ -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" </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" <>> 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" <Service temporarily unavailable

\$server_name nicht erreichbar

DNS-Auflösung fehlgeschlagen. Bitte später erneut versuchen.

"; + } +} +NGINX + else + # 443 Proxy – DNS ok/unknown: Laufzeit-Auflösung + freundlicher 503 bei Downstreams + cat > "$HTTPS_OUT" <>> 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 "Service temporarily unavailable

$server_name nicht erreichbar

Der Dienst ist momentan nicht verfügbar. Bitte später erneut versuchen.

"; + } + +} +NGINX + fi + # <<< END NEW + fi + + # 80->443 Redirect-Server nur, wenn gewünscht + if [ "$HTTP_BEHAVIOR" = "redirect" ]; then + cat > "$HTTP_REDIRECT_OUT" <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 + + + + + + Service Portal + + + + + + +
+

server.schooltech.ch - Service Portal

+ + + + + + +
+ + + + + + + +// ==== 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? + + diff --git a/dokumentation/2026_03_21___q2_WS.txt b/dokumentation/2026_03_21___q2_WS.txt new file mode 100755 index 0000000..e69de29 diff --git a/forwarding.conf b/forwarding.conf new file mode 100644 index 0000000..660f56d --- /dev/null +++ b/forwarding.conf @@ -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 diff --git a/letsEncrypt.sh b/letsEncrypt.sh index 2bf15f3..248218d 100755 --- a/letsEncrypt.sh +++ b/letsEncrypt.sh @@ -1 +1,39 @@ -docker exec -it appServer_LetsEncryptFetcher certbot renew --dry-run \ No newline at end of file +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 diff --git a/letsEncrypt_crontab.txt b/letsEncrypt_crontab.txt old mode 100755 new mode 100644 diff --git a/letsEncrypt_init.sh b/letsEncrypt_init.sh deleted file mode 100755 index 20cdb95..0000000 --- a/letsEncrypt_init.sh +++ /dev/null @@ -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 \ No newline at end of file diff --git a/letsencrypt/conf/renewal/server.schooltech.ch.conf b/letsencrypt/conf/renewal/server.schooltech.ch.conf index 56518ff..e5228cb 100644 --- a/letsencrypt/conf/renewal/server.schooltech.ch.conf +++ b/letsencrypt/conf/renewal/server.schooltech.ch.conf @@ -16,6 +16,6 @@ server.schooltech.ch = /var/www/certbot xyz.server.schooltech.ch = /var/www/certbot controller.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] -ari_retry_after = 2026-02-14T07:10:40 +ari_retry_after = 2026-02-19T08:25:19 diff --git a/nginx.conf b/nginx.conf index 98366a4..0e7b8f5 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,432 +1,29 @@ -error_log /var/log/nginx/error.log info; - -resolver 127.0.0.11 valid=30s; -resolver_timeout 5s; - -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; -} - - - - -# ------------------------------------------------------------ -# Default HTTP -> HTTPS redirect -# ------------------------------------------------------------ +# /etc/nginx/conf.d/default.conf 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; + default_type "text/plain; charset=utf-8"; } - 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; - 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; - - + # PortalUI root 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 + # === API forwarding for Auth-Service === location /api/ { - proxy_pass http://appServer_Auth:3000/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; - - proxy_buffering off; - proxy_request_buffering off; } - # ------------------------------------------------------------ - # 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 ' - - - Dienst offline - - -

Ein Dienst ist momentan nicht erreichbar

-

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

- - '; - } - - # ---- 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 ---- + # === SPA routing (Portal UI) === location / { - auth_request /nginxauth; - - 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; + try_files $uri $uri/ /index.html; } - - # /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 ' - - - Dienst (Simulation) offline - - -

Ein Dienst ist momentan nicht erreichbar

-

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

- - '; - } - - # ---- 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 ' - - - Dienst (Guacamole) offline - - -

Ein Dienst ist momentan nicht erreichbar

-

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

- - '; - } - - 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 ' - - - Dienst (Portainer) offline - - -

Ein Dienst ist momentan nicht erreichbar

-

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

- - '; - } - - 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 - } -} +} \ No newline at end of file diff --git a/_nginx.conf_vibeCode_zerhauen b/nginx.conf_PortalSSH similarity index 69% rename from _nginx.conf_vibeCode_zerhauen rename to nginx.conf_PortalSSH index 146e954..98366a4 100755 --- a/_nginx.conf_vibeCode_zerhauen +++ b/nginx.conf_PortalSSH @@ -1,345 +1,432 @@ -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; - - 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; - - # 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; - } - - - 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; - } -} +error_log /var/log/nginx/error.log info; + +resolver 127.0.0.11 valid=30s; +resolver_timeout 5s; + +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; + 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; + 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://appServer_Auth: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; + + proxy_buffering off; + proxy_request_buffering off; + } + + # ------------------------------------------------------------ + # 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 ' + + + Dienst offline + + +

Ein Dienst ist momentan nicht erreichbar

+

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

+ + '; + } + + # ---- 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 / { + auth_request /nginxauth; + + 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 ' + + + Dienst (Simulation) offline + + +

Ein Dienst ist momentan nicht erreichbar

+

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

+ + '; + } + + # ---- 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 ' + + + Dienst (Guacamole) offline + + +

Ein Dienst ist momentan nicht erreichbar

+

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

+ + '; + } + + 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 ' + + + Dienst (Portainer) offline + + +

Ein Dienst ist momentan nicht erreichbar

+

Bitte Seite neu laden - Verbindung wird automatisch erneut versucht.

+ + '; + } + + 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 + } +} diff --git a/nginxPages/10-server-schooltech.conf b/nginxPages/10-server-schooltech.conf index c000cd2..2563ea7 100644 --- a/nginxPages/10-server-schooltech.conf +++ b/nginxPages/10-server-schooltech.conf @@ -10,6 +10,8 @@ server { root /usr/share/nginx/html; index index.html; + auth_request /nginxauth; + # UI / SPA location / { try_files $uri $uri/ /index.html; diff --git a/public/app.js b/public/app.js index f0a027b..716ce30 100755 --- a/public/app.js +++ b/public/app.js @@ -2,10 +2,10 @@ // 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://simulation3a29.server.schooltech.ch/" }, - { id: "portainer", name: "Portainer", url: "https://portainer.server.schooltech.ch/" } + { id: "abc", name: "Control GamePad", url: "https://tccontrol.server.schooltech.ch/" }, + { id: "xyz", name: "Guacamole", url: "https://rp5guac.server.schooltech.ch/" }, + { id: "sim", name: "Simulation", url: "https://tcSimulation.server.schooltech.ch/" }, + { id: "portainer", name: "Portainer", url: "https://rp5portainer.server.schooltech.ch/" } ]; // DOM-Elemente