Docker: interner nginx statt socat (Host-Header -> localhost)
Ursache des 404 bei Zugriff über IP/Proxy: .NET-HttpListener bedient den Prefix http://localhost:8077/ nur bei Host: localhost. socat (L4) reicht den originalen Host-Header durch -> 404. Jetzt sitzt ein winziger nginx im Container davor, der den Host-Header auf localhost umschreibt (Upstream [::1]:8077, IPv4 als Fallback). App-Code bleibt unverändert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+5
-3
@@ -5,10 +5,11 @@
|
|||||||
# Env-Variablen bzw. das Entrypoint-Script - ohne Codeaenderung.
|
# Env-Variablen bzw. das Entrypoint-Script - ohne Codeaenderung.
|
||||||
FROM mcr.microsoft.com/powershell:7.4-debian-12
|
FROM mcr.microsoft.com/powershell:7.4-debian-12
|
||||||
|
|
||||||
# socat = Bridge 0.0.0.0:BRIDGE_PORT -> 127.0.0.1:APP_PORT (App bindet nur loopback).
|
# nginx = interner Reverse-Proxy, der den Host-Header auf "localhost" umschreibt
|
||||||
|
# (HttpListener-Prefix http://localhost:8077/ bedient nur Host: localhost).
|
||||||
# git = fuer das optionale Policy-Git-Snapshot-Feature.
|
# git = fuer das optionale Policy-Git-Snapshot-Feature.
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends socat git ca-certificates \
|
&& apt-get install -y --no-install-recommends nginx git ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& pwsh -NoProfile -Command "Install-Module Microsoft.Graph.Authentication -Scope AllUsers -Force"
|
&& pwsh -NoProfile -Command "Install-Module Microsoft.Graph.Authentication -Scope AllUsers -Force"
|
||||||
|
|
||||||
@@ -19,7 +20,8 @@ COPY Start.ps1 ./
|
|||||||
COPY src/ ./src/
|
COPY src/ ./src/
|
||||||
COPY www/ ./www/
|
COPY www/ ./www/
|
||||||
|
|
||||||
# Entrypoint (neu, nur fuer den Container).
|
# Interner nginx-Reverse-Proxy (Host-Rewrite) + Entrypoint (neu, nur fuer Container).
|
||||||
|
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||||
COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -9,9 +9,11 @@ Entrypoint-Script.
|
|||||||
> veröffentlichten Port (`8080`) an. HTTPS macht der Proxy.
|
> veröffentlichten Port (`8080`) an. HTTPS macht der Proxy.
|
||||||
|
|
||||||
## Wie es funktioniert
|
## Wie es funktioniert
|
||||||
- Die App bindet unverändert auf `http://localhost:8077/` (nur containerintern).
|
- Die App bindet unverändert auf `http://localhost:8077/` (nur containerintern) und
|
||||||
- Ein **socat-Bridge** im Container leitet `0.0.0.0:8080 → 127.0.0.1:8077` — dadurch ist
|
bedient — bedingt durch .NET-`HttpListener` — **nur Requests mit `Host: localhost`**.
|
||||||
die App von außen erreichbar, ohne den App-Code anzufassen.
|
- Ein **interner nginx** (Port 8080) leitet auf die App weiter und schreibt dabei den
|
||||||
|
**`Host`-Header auf `localhost`** um. Ohne dieses Rewrite käme bei Zugriff über IP/Proxy
|
||||||
|
ein `404`. Der App-Code bleibt unangetastet.
|
||||||
- Settings/Policy-Exporte liegen im Volume `/data` (`XDG_CONFIG_HOME=/data`, wird von
|
- Settings/Policy-Exporte liegen im Volume `/data` (`XDG_CONFIG_HOME=/data`, wird von
|
||||||
`Get-AppDataDir` auf Linux automatisch genutzt).
|
`Get-AppDataDir` auf Linux automatisch genutzt).
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8080:8080" # <docker-host>:8080 -> vom Reverse-Proxy anziehen
|
- "8080:8080" # <docker-host>:8080 -> vom Reverse-Proxy anziehen
|
||||||
volumes:
|
volumes:
|
||||||
- im-data:/data # settings.json + policy-exports persistent
|
- /volume2/docker/intuneManager/data:/data # settings.json + policy-exports persistent
|
||||||
environment:
|
environment:
|
||||||
- XDG_CONFIG_HOME=/data
|
- XDG_CONFIG_HOME=/data
|
||||||
- APP_PORT=8077
|
- APP_PORT=8077
|
||||||
@@ -19,3 +19,4 @@ services:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
im-data:
|
im-data:
|
||||||
|
ls
|
||||||
+12
-18
@@ -1,36 +1,30 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Startet die App unveraendert (bindet auf localhost:APP_PORT) und leitet externe
|
# Startet die App unveraendert (bindet auf localhost:APP_PORT) und stellt einen
|
||||||
# Verbindungen per socat-Bridge dorthin - so bleibt der App-Code unangetastet.
|
# internen nginx davor, der den Host-Header auf "localhost" umschreibt.
|
||||||
#
|
#
|
||||||
# HINWEIS: .NET-HttpListener bindet den Prefix "localhost" je nach System auf IPv4
|
# WARUM nginx statt socat: .NET-HttpListener bedient den Prefix
|
||||||
# ODER IPv6. Deshalb ermitteln wir zur Laufzeit, welche Loopback-Familie antwortet,
|
# http://localhost:APP_PORT/ NUR bei Host: localhost. socat (reiner TCP-Proxy)
|
||||||
# und richten socat darauf aus (sonst "Connection refused").
|
# reicht den originalen Host-Header durch -> von aussen 404. nginx schreibt den
|
||||||
|
# Host-Header um -> jeder Zugriffsweg funktioniert, ohne den App-Code zu aendern.
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
: "${APP_PORT:=8077}"
|
: "${APP_PORT:=8077}"
|
||||||
: "${BRIDGE_PORT:=8080}"
|
|
||||||
|
|
||||||
# App im Hintergrund starten. -NoBrowser: im Container kein Desktop/open.
|
# App im Hintergrund starten. -NoBrowser: im Container kein Desktop/open.
|
||||||
pwsh -NoProfile -File /app/Start.ps1 -NoBrowser -Port "${APP_PORT}" &
|
pwsh -NoProfile -File /app/Start.ps1 -NoBrowser -Port "${APP_PORT}" &
|
||||||
APP_PID=$!
|
APP_PID=$!
|
||||||
|
|
||||||
# Warten bis der Listener antwortet und dabei IPv4 vs. IPv6 erkennen.
|
# Best-effort warten, bis der App-Listener antwortet (IPv6 oder IPv4).
|
||||||
# Default = IPv6 (haeufigster Fall im Container); IPv4 wird bevorzugt, wenn erreichbar.
|
# nginx wuerde sonst kurz 502 liefern, bis die App oben ist.
|
||||||
SOCAT_TARGET="TCP6:[::1]:${APP_PORT}"
|
|
||||||
for i in $(seq 1 30); do
|
for i in $(seq 1 30); do
|
||||||
if (exec 3<>"/dev/tcp/127.0.0.1/${APP_PORT}") 2>/dev/null; then
|
if (exec 3<>"/dev/tcp/::1/${APP_PORT}") 2>/dev/null; then exec 3>&-; break; fi
|
||||||
exec 3>&-; SOCAT_TARGET="TCP4:127.0.0.1:${APP_PORT}"; break
|
if (exec 3<>"/dev/tcp/127.0.0.1/${APP_PORT}") 2>/dev/null; then exec 3>&-; break; fi
|
||||||
fi
|
|
||||||
if (exec 3<>"/dev/tcp/::1/${APP_PORT}") 2>/dev/null; then
|
|
||||||
exec 3>&-; SOCAT_TARGET="TCP6:[::1]:${APP_PORT}"; break
|
|
||||||
fi
|
|
||||||
# App unerwartet beendet? Dann mit deren Exit-Code aussteigen.
|
|
||||||
kill -0 "$APP_PID" 2>/dev/null || { echo "[entrypoint] App-Prozess beendet - Abbruch."; wait "$APP_PID"; exit $?; }
|
kill -0 "$APP_PID" 2>/dev/null || { echo "[entrypoint] App-Prozess beendet - Abbruch."; wait "$APP_PID"; exit $?; }
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "[entrypoint] Bridge: 0.0.0.0:${BRIDGE_PORT} -> ${SOCAT_TARGET}"
|
echo "[entrypoint] Starte nginx (Host-Rewrite -> localhost) auf :8080"
|
||||||
socat "TCP-LISTEN:${BRIDGE_PORT},fork,reuseaddr" "${SOCAT_TARGET}" &
|
nginx -g 'daemon off;' &
|
||||||
|
|
||||||
# Am Leben des App-Prozesses haengen (Container endet, wenn die App endet).
|
# Am Leben des App-Prozesses haengen (Container endet, wenn die App endet).
|
||||||
wait "$APP_PID"
|
wait "$APP_PID"
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Interner Reverse-Proxy im Container.
|
||||||
|
# Zweck: den Host-Header auf "localhost" umschreiben. Die App bindet den
|
||||||
|
# HttpListener-Prefix http://localhost:8077/ und bedient NUR Requests mit
|
||||||
|
# Host: localhost - ohne dieses Rewrite kaeme von aussen 404.
|
||||||
|
worker_processes 1;
|
||||||
|
events { worker_connections 1024; }
|
||||||
|
|
||||||
|
http {
|
||||||
|
access_log /dev/stdout;
|
||||||
|
error_log /dev/stderr;
|
||||||
|
|
||||||
|
# App-Listener: bevorzugt IPv6-Loopback (so bindet HttpListener "localhost"
|
||||||
|
# im Container), IPv4 als Fallback.
|
||||||
|
upstream imapp {
|
||||||
|
server [::1]:8077;
|
||||||
|
server 127.0.0.1:8077 backup;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
listen [::]:8080;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://imapp;
|
||||||
|
proxy_set_header Host localhost; # <- der entscheidende Punkt
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_read_timeout 300s; # Device-Code-Login/Graph koennen dauern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user