Mehrere Dienste hinter einem Port: Pfade statt Domainnamen
Hintergrund aus der Praxis: nach aussen ist nur ein Port frei, auf dem
Server laufen aber Checkmk und Grafana und noch ein Dienst. Die Frage
war, ob sich statt eines Domainnamens auch /irgendeinname nutzen laesst.
Ja - der Proxy Manager kennt dafuer "Custom locations", und die API
nimmt sie als locations-Array entgegen. Geprueft am Schema von v2.15.1
(components/proxy-host-object.json) und an der Vorlage _location.conf,
aus der nginx den location-Block erzeugt.
proxy add --domain 10.0.0.5 --forward-port 5000 --cert self \
--location /grafana=127.0.0.1:3000 \
--location /sonstwas=127.0.0.1:8080
--location ist mehrfach angebbar, versteht /pfad=host:port, wahlweise
mit Schema (https://) und mit einem Pfad, der beim Ziel vorangestellt
wird (/alt=host:3000/basis). Ungueltige Angaben werden abgewiesen.
Ebenfalls geprueft: als --domain ist eine IP-Adresse zulaessig, ein
DNS-Name ist nicht noetig (das Muster im Schema laesst alles ausser
einer Handvoll Sonderzeichen zu).
Neu dazu "proxy show --domain X | --id N", das den Eintrag samt allen
Pfaden auflistet - noetig, weil --update den Eintrag vollstaendig
ersetzt und man die bestehenden Pfade wieder mitgeben muss.
Im Assistenten fragt die Oberflaeche nach dem Zertifikat, ob weitere
Dienste unter demselben Namen liegen sollen, und nimmt dann Pfad und
Ziel im Wechsel auf, bis ein Feld leer bleibt.
Dokumentiert als Abschnitt 10.3 in beiden Sprachfassungen, mit dem
Fallstrick, der sonst Stunden kostet: ein Dienst hinter einem Unterpfad
muss seinen Basispfad kennen. Fuer Grafana sind das root_url und
serve_from_sub_path; ohne die bleibt die Seite weiss, weil das
JavaScript unter /public statt /grafana/public gesucht wird. Checkmk
ist nicht betroffen, es liegt ohnehin unter /<site>/.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+129
-7
@@ -180,7 +180,12 @@ KOMMANDOS - Proxy-Hosts (ueber die API des Proxy Managers)
|
||||
--cert self|letsencrypt|none (Vorgabe self)
|
||||
--update vorhandenen Host ersetzen
|
||||
--advanced TEXT eigener nginx-Abschnitt
|
||||
--location /P=HOST:PORT weiterer Pfad auf demselben Namen,
|
||||
mehrfach angebbar. Damit lassen sich mehrere
|
||||
Dienste hinter EINEM Port unterscheiden:
|
||||
--location /grafana=127.0.0.1:3000
|
||||
--no-force-ssl --no-http2 --no-websockets --no-block-exploits
|
||||
proxy show --domain X | --id N Einzelheiten samt Pfaden anzeigen
|
||||
proxy del --id N Proxy-Host loeschen
|
||||
|
||||
KOMMANDOS - Zertifikat
|
||||
@@ -3182,6 +3187,36 @@ EOF
|
||||
printf '%s' "$id"
|
||||
}
|
||||
|
||||
# Einen --location-Eintrag in ein JSON-Objekt umsetzen.
|
||||
# /grafana=127.0.0.1:3000
|
||||
# /grafana=http://127.0.0.1:3000
|
||||
# /alt=127.0.0.1:3000/basis (Pfad, der beim Ziel vorangestellt wird)
|
||||
proxy_location_json() { # spec -> JSON-Objekt auf stdout
|
||||
local spec="$1" path target scheme=http host port fwd=""
|
||||
case "$spec" in
|
||||
*=*) path="${spec%%=*}"; target="${spec#*=}" ;;
|
||||
*) err "Ungueltige Angabe: '$spec'"
|
||||
printf ' Erwartet: /pfad=host:port z.B. /grafana=127.0.0.1:3000\n' >&2
|
||||
return 1 ;;
|
||||
esac
|
||||
case "$path" in /*) : ;; *) path="/$path" ;; esac
|
||||
case "$target" in
|
||||
http://*) scheme=http; target="${target#http://}" ;;
|
||||
https://*) scheme=https; target="${target#https://}" ;;
|
||||
esac
|
||||
host="${target%%:*}"
|
||||
local rest="${target#*:}"
|
||||
if [ "$rest" = "$target" ]; then
|
||||
err "Im Ziel '$target' fehlt der Port (erwartet host:port)."; return 1
|
||||
fi
|
||||
port="${rest%%/*}"
|
||||
case "$rest" in */*) fwd="/${rest#*/}" ;; esac
|
||||
valid_port "$port" || { err "'$port' ist keine gueltige Portnummer."; return 1; }
|
||||
[ -n "$host" ] || { err "Im Ziel '$target' fehlt der Host."; return 1; }
|
||||
printf '{"path":"%s","forward_scheme":"%s","forward_host":"%s","forward_port":%s,"forward_path":"%s","advanced_config":""}' \
|
||||
"$path" "$scheme" "$host" "$port" "$fwd"
|
||||
}
|
||||
|
||||
# --- Proxy-Hosts ---------------------------------------------------------
|
||||
cmd_proxy() {
|
||||
local sub="${1:-list}"; [ $# -gt 0 ] && shift || true
|
||||
@@ -3234,6 +3269,39 @@ cmd_proxy() {
|
||||
[ "$n" -eq 0 ] && echo "(noch kein Zertifikat vorhanden)"
|
||||
return 0 ;;
|
||||
|
||||
show)
|
||||
local pid="" dom=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in --id) pid="$2"; shift 2 ;; --domain) dom="$2"; shift 2 ;; *) pid="$1"; shift ;; esac
|
||||
done
|
||||
npm_require_running || return 1
|
||||
NPM_TOKEN="$(npm_api_token)" || return 1
|
||||
if [ -z "$pid" ]; then
|
||||
[ -n "$dom" ] || die "--id oder --domain angeben."
|
||||
local all; all="$(npm_api GET /api/nginx/proxy-hosts)"
|
||||
local i
|
||||
for i in $(json_ids "$all"); do
|
||||
if npm_api GET "/api/nginx/proxy-hosts/$i" | grep -qF "\"$dom\""; then pid="$i"; break; fi
|
||||
done
|
||||
[ -n "$pid" ] || die "Kein Proxy-Host fuer '$dom' gefunden."
|
||||
fi
|
||||
local o; o="$(npm_api GET "/api/nginx/proxy-hosts/$pid")"
|
||||
printf '\n%sProxy-Host %s%s\n' "$C_BLD" "$pid" "$C_RST"
|
||||
printf ' Domains : %s\n' "$(printf '%s' "$o" | sed -n 's/.*"domain_names"[[:space:]]*:[[:space:]]*\[\([^]]*\)\].*/\1/p' | tr -d '"' || true)"
|
||||
printf ' Ziel : %s://%s:%s\n' "$(json_str "$o" forward_scheme)" "$(json_str "$o" forward_host)" "$(json_num "$o" forward_port)"
|
||||
printf ' Zertifikat : %s\n' "$(json_num "$o" certificate_id)"
|
||||
printf '\n %sZusaetzliche Pfade%s\n' "$C_BLD" "$C_RST"
|
||||
local n
|
||||
n="$(printf '%s' "$o" | grep -o '"path":"[^"]*"' | wc -l || true)"
|
||||
if [ "${n:-0}" -eq 0 ]; then
|
||||
printf ' (keine - alles geht an das Ziel oben)\n'
|
||||
else
|
||||
printf '%s' "$o" | tr '}' '\n' \
|
||||
| sed -n 's/.*"path":"\([^"]*\)".*"forward_host":"\([^"]*\)","forward_port":\([0-9]*\).*/ \1 -> \2:\3/p'
|
||||
fi
|
||||
echo
|
||||
return 0 ;;
|
||||
|
||||
del|delete|rm)
|
||||
local pid=""
|
||||
while [ $# -gt 0 ]; do
|
||||
@@ -3252,7 +3320,7 @@ cmd_proxy() {
|
||||
local domain="${PROXY_DOMAIN:-}" fhost="${PROXY_FORWARD_HOST:-127.0.0.1}"
|
||||
local fport="${PROXY_FORWARD_PORT:-5000}" scheme=http cert=self
|
||||
local force_ssl=true http2=true ws=true block=true update="" adv=""
|
||||
local adv_set=""
|
||||
local adv_set="" locs=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--domain) domain="$2"; shift 2 ;;
|
||||
@@ -3261,6 +3329,10 @@ cmd_proxy() {
|
||||
--scheme) scheme="$2"; shift 2 ;;
|
||||
--cert) cert="$2"; shift 2 ;;
|
||||
--advanced) adv="$2"; adv_set=1; shift 2 ;;
|
||||
--location) local lj
|
||||
lj="$(proxy_location_json "$2")" || return 1
|
||||
if [ -n "$locs" ]; then locs="$locs,$lj"; else locs="$lj"; fi
|
||||
shift 2 ;;
|
||||
--update) update=1; shift ;;
|
||||
--no-force-ssl) force_ssl=false; shift ;;
|
||||
--no-http2) http2=false; shift ;;
|
||||
@@ -3298,7 +3370,7 @@ cmd_proxy() {
|
||||
esac
|
||||
|
||||
local payload
|
||||
payload="{\"domain_names\":[\"$domain\"],\"forward_scheme\":\"$scheme\",\"forward_host\":\"$fhost\",\"forward_port\":$fport,\"certificate_id\":$certid,\"ssl_forced\":$force_ssl,\"http2_support\":$http2,\"block_exploits\":$block,\"allow_websocket_upgrade\":$ws,\"caching_enabled\":false,\"advanced_config\":\"$adv\",\"enabled\":true,\"meta\":{}}"
|
||||
payload="{\"domain_names\":[\"$domain\"],\"forward_scheme\":\"$scheme\",\"forward_host\":\"$fhost\",\"forward_port\":$fport,\"certificate_id\":$certid,\"ssl_forced\":$force_ssl,\"http2_support\":$http2,\"block_exploits\":$block,\"allow_websocket_upgrade\":$ws,\"caching_enabled\":false,\"advanced_config\":\"$adv\",\"locations\":[$locs],\"enabled\":true,\"meta\":{}}"
|
||||
|
||||
local body
|
||||
if [ -n "$exid" ]; then
|
||||
@@ -3316,6 +3388,10 @@ cmd_proxy() {
|
||||
return 1
|
||||
fi
|
||||
ok "Proxy-Host $newid angelegt: $domain -> $scheme://$fhost:$fport"
|
||||
if [ -n "$locs" ]; then
|
||||
printf ' Zusaetzliche Pfade:\n'
|
||||
printf '%s' "$locs" | tr ',' '\n' | sed -n 's/.*"path":"\([^"]*\)".*"forward_host":"\([^"]*\)","forward_port":\([0-9]*\).*/ \1 -> \2:\3/p'
|
||||
fi
|
||||
cat <<EOF
|
||||
|
||||
${C_BLD}Erreichbar${C_RST}
|
||||
@@ -3329,7 +3405,7 @@ EOF
|
||||
return 0 ;;
|
||||
|
||||
*) die "Unbekanntes Unterkommando 'proxy $sub'.
|
||||
Moeglich: status | list | certs | add | del" ;;
|
||||
Moeglich: status | list | show | certs | add | del" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -4363,6 +4439,7 @@ Konto: ${NPM_ADMIN_EMAIL:-<nicht gesetzt>}
|
||||
Ziel : ${PROXY_FORWARD_HOST:-127.0.0.1}:${PROXY_FORWARD_PORT:-5000}" \
|
||||
anlegen "Proxy-Host anlegen (Assistent)" \
|
||||
list "Vorhandene Proxy-Hosts anzeigen" \
|
||||
show "Einzelheiten eines Proxy-Hosts (mit Pfaden)" \
|
||||
certs "Vorhandene Zertifikate anzeigen" \
|
||||
del "Proxy-Host loeschen" \
|
||||
status "Anmeldung pruefen" \
|
||||
@@ -4371,6 +4448,11 @@ Ziel : ${PROXY_FORWARD_HOST:-127.0.0.1}:${PROXY_FORWARD_PORT:-5000}" \
|
||||
anlegen) gui_proxy_add || true ;;
|
||||
list) local f; f="$(mktemp)"; NO_COLOR=1 cmd_proxy list >"$f" 2>&1 || true
|
||||
ui_text "Proxy-Hosts" "$f"; rm -f "$f" ;;
|
||||
show) local d3; d3="$(ui_input "Einzelheiten" "Name oder ID des Proxy-Hosts:" "${PROXY_DOMAIN:-}")" || continue
|
||||
[ -n "$d3" ] || continue
|
||||
local f3; f3="$(mktemp)"
|
||||
NO_COLOR=1 cmd_proxy show --domain "$d3" >"$f3" 2>&1 || true
|
||||
ui_text "Proxy-Host $d3" "$f3"; rm -f "$f3" ;;
|
||||
certs) local f2; f2="$(mktemp)"; NO_COLOR=1 cmd_proxy certs >"$f2" 2>&1 || true
|
||||
ui_text "Zertifikate" "$f2"; rm -f "$f2" ;;
|
||||
del) gui_proxy_del || true ;;
|
||||
@@ -4426,19 +4508,59 @@ Womit soll HTTPS abgesichert werden?" \
|
||||
letsencrypt "Let's Encrypt (Port 80 muss aus dem Internet erreichbar sein)" \
|
||||
none "Ohne HTTPS (nur Port 80)")" || return 0
|
||||
|
||||
# --- weitere Dienste unter demselben Namen, nach Pfad getrennt ----
|
||||
local -a locopt=()
|
||||
local loclist=""
|
||||
if ui_yesno "Weitere Dienste?" \
|
||||
"Sollen unter demselben Namen noch andere Dienste erreichbar sein,
|
||||
unterschieden nach dem Pfad?
|
||||
|
||||
https://$dom/ -> Checkmk (das Ziel von eben)
|
||||
https://$dom/grafana -> Grafana
|
||||
https://$dom/sonstwas -> ein weiterer Dienst
|
||||
|
||||
Das ist der Weg, wenn nur EIN Port nach aussen frei ist."; then
|
||||
while :; do
|
||||
local lpath ltarget
|
||||
lpath="$(ui_input "Pfad" \
|
||||
"Unter welchem Pfad soll der Dienst liegen?
|
||||
|
||||
Leer lassen beendet die Eingabe." "")" || break
|
||||
[ -n "$lpath" ] || break
|
||||
ltarget="$(ui_input "Ziel fuer $lpath" \
|
||||
"Wohin soll $lpath weitergeleitet werden?
|
||||
|
||||
Schreibweise: host:port z.B. 127.0.0.1:3000
|
||||
https://host:port fuer verschluesselte Ziele" "127.0.0.1:3000")" || break
|
||||
[ -n "$ltarget" ] || break
|
||||
if ! proxy_location_json "$lpath=$ltarget" >/dev/null 2>&1; then
|
||||
ui_msg "Ungueltig" "'$lpath=$ltarget' konnte nicht gelesen werden.\n\nErwartet: /pfad und host:port"
|
||||
continue
|
||||
fi
|
||||
locopt+=(--location "$lpath=$ltarget")
|
||||
loclist="$loclist
|
||||
$lpath -> $ltarget"
|
||||
ui_yesno "Noch einer?" "Bisher:$loclist
|
||||
|
||||
Einen weiteren Pfad hinzufuegen?" || break
|
||||
done
|
||||
fi
|
||||
|
||||
local upd=""
|
||||
ui_yesno "Anlegen" \
|
||||
if ui_yesno "Anlegen" \
|
||||
"$dom -> 127.0.0.1:$fport
|
||||
Zertifikat: $cert
|
||||
Zertifikat: $cert${loclist:+
|
||||
|
||||
Zusaetzliche Pfade:$loclist}
|
||||
|
||||
Einen bereits vorhandenen Eintrag fuer diesen Namen ersetzen?
|
||||
|
||||
<Ja> ersetzt ihn
|
||||
<Nein> bricht ab, falls es ihn schon gibt" && upd=--update
|
||||
<Nein> bricht ab, falls es ihn schon gibt"; then upd=--update; fi
|
||||
|
||||
ui_run "Proxy-Host anlegen" bash -c \
|
||||
"cd '$PROJECT_DIR' && NO_COLOR=1 '$SELF' -y proxy add --domain '$dom' \
|
||||
--forward-port '$fport' --cert '$cert' $upd"
|
||||
--forward-port '$fport' --cert '$cert' $upd ${locopt[*]+${locopt[*]}}"
|
||||
|
||||
# Die Vorgaben merken, damit der naechste Aufruf sie kennt
|
||||
env_set PROXY_DOMAIN "$dom" 1 2>/dev/null || true
|
||||
|
||||
Reference in New Issue
Block a user