diff --git a/README.md b/README.md index 602c456..76a20ff 100644 --- a/README.md +++ b/README.md @@ -1228,6 +1228,7 @@ erzwingen, HTTP/2 und Websockets einschalten und den empfohlenen | `proxy default --domain X` | HTTPS über die blanke IP ermöglichen (spiegelt X in einen Standard-Server) | | `proxy default --off` | diese Spiegelung wieder entfernen | | `proxy admin --domain X` / `--root` | Adminoberfläche erreichbar machen – per Name oder über die Wurzel | +| `proxy check` | antwortet hinter jedem Pfad etwas? (aus dem Proxy-Container geprüft) | | `proxy reload` | nginx die Konfiguration neu einlesen lassen – ohne Unterbrechung | | `proxy restart` | den Proxy-Manager-Container neu starten | @@ -1677,6 +1678,75 @@ bei Variante A. Ohne Angabe zeigt `proxy admin` den Stand. In der Oberfläche liegt beides unter **Proxy-Hosts → Adminoberfläche erreichbar machen**. +### 10.12 Weitere Dienste dazunehmen – Beispiel Grafana + +Mit einem einzigen freien Port lassen sich mehrere Dienste über Pfade +auseinanderhalten: + +``` + https://172.0.250.82/ Adminoberfläche des Proxy Managers + https://172.0.250.82/cmk/ Checkmk + https://172.0.250.82/grafana/ Grafana +``` + +Ob ein Dienst das mitmacht, hängt an ihm selbst – nicht am Proxy. Er muss +wissen, dass er unter einem Präfix liegt, und ihn in jede Adresse einsetzen, +die er erzeugt. Checkmk kann das von Haus aus (der Site-Pfad *ist* der +Präfix), die Oberfläche des Proxy Managers kann es gar nicht +([Kapitel 10.11](#1011-die-adminoberfläche-des-proxy-managers-erreichbar-machen)), +**Grafana kann es, wenn man es ihm sagt**: + +```yaml +# docker-compose.yml, Dienst grafana +environment: + GF_SERVER_ROOT_URL: "https://172.0.250.82/grafana/" + GF_SERVER_SERVE_FROM_SUB_PATH: "true" +``` + +Gleichwertig in der `grafana.ini`: + +```ini +[server] +root_url = https://172.0.250.82/grafana/ +serve_from_sub_path = true +``` + +Damit setzt Grafana ein `` in seine Seiten, und alle +Dateien werden relativ dazu geladen. Ohne die beiden Zeilen leitet es auf +`/login` um – also auf die Wurzel, wo etwas ganz anderes liegt. Gemessen: + +| Grafana-Einstellung | `/grafana/login` | Dateien der Seite | +|---|---|---| +| ohne `serve_from_sub_path` | 302 auf `/login` | gehen an die Wurzel | +| mit `root_url` + `serve_from_sub_path` | 200 | `/grafana/public/…` → 200 | + +Auf der Proxy-Seite ist es dann ein Pfad mehr: + +```bash +./scripts/cmk-manage.sh proxy edit --id 1 --location /grafana=127.0.0.1:3000 +./scripts/cmk-manage.sh proxy default --domain --restart # falls eingerichtet +./scripts/cmk-manage.sh proxy check +``` + +`proxy check` fragt jedes Ziel **aus dem Proxy-Container heraus** ab – von dort +baut nginx die Verbindung auf, nicht von deinem Arbeitsplatz: + +``` +Proxy-Host 1 stack.local + PFAD ZIEL ANTWORT + / http://127.0.0.1:81 HTTP 200 + /grafana http://127.0.0.1:3000 HTTP 302 + /cmk http://127.0.0.1:9999 nicht erreichbar +``` + +Genau so findet man einen Tippfehler in der Zieladresse, bevor der Browser +`502 Bad Gateway` zeigt. In der Oberfläche liegt das unter **Proxy-Hosts → +Ziele prüfen**. + +> Port **3000 bleibt in der Firewall zu**. Grafana wird ausschließlich über den +> Proxy erreicht, genau wie Checkmk auf 5000. `firewall close` bietet einen +> versehentlich geöffneten Port wieder an. + --- ## 11. Proxy Manager lokal einrichten (Terminal-Browser) diff --git a/README_en.md b/README_en.md index 55e567a..926b404 100644 --- a/README_en.md +++ b/README_en.md @@ -1227,6 +1227,7 @@ HTTPS, enables HTTP/2 and websockets, and sets the recommended | `proxy default --domain X` | enable HTTPS on the bare IP (mirrors X into a default server) | | `proxy default --off` | remove that mirror again | | `proxy admin --domain X` / `--root` | make the admin interface reachable – by name or via the root | +| `proxy check` | does something answer behind each path? (checked from inside the proxy container) | | `proxy reload` | make nginx re-read its configuration – without interruption | | `proxy restart` | restart the Proxy Manager container | @@ -1668,6 +1669,73 @@ option A. Without arguments `proxy admin` shows the current state. The GUI has both under **Proxy-Hosts → Adminoberfläche erreichbar machen**. +### 10.12 Adding more services – Grafana as an example + +A single free port is enough to keep several services apart by path: + +``` + https://172.0.250.82/ Proxy Manager admin interface + https://172.0.250.82/cmk/ Checkmk + https://172.0.250.82/grafana/ Grafana +``` + +Whether a service plays along is up to the service, not the proxy. It has to +know that it lives under a prefix and put that prefix into every address it +generates. Checkmk does this natively (the site path *is* the prefix), the +Proxy Manager's interface cannot do it at all +([chapter 10.11](#1011-making-the-proxy-managers-admin-interface-reachable)), +and **Grafana can, once you tell it**: + +```yaml +# docker-compose.yml, service grafana +environment: + GF_SERVER_ROOT_URL: "https://172.0.250.82/grafana/" + GF_SERVER_SERVE_FROM_SUB_PATH: "true" +``` + +Equivalent in `grafana.ini`: + +```ini +[server] +root_url = https://172.0.250.82/grafana/ +serve_from_sub_path = true +``` + +With that Grafana puts a `` into its pages and loads +every file relative to it. Without those two lines it redirects to `/login` – +the root, where something else lives entirely. Measured: + +| Grafana setting | `/grafana/login` | page assets | +|---|---|---| +| without `serve_from_sub_path` | 302 to `/login` | go to the root | +| with `root_url` + `serve_from_sub_path` | 200 | `/grafana/public/…` → 200 | + +On the proxy side it is one more path: + +```bash +./scripts/cmk-manage.sh proxy edit --id 1 --location /grafana=127.0.0.1:3000 +./scripts/cmk-manage.sh proxy default --domain --restart # if set up +./scripts/cmk-manage.sh proxy check +``` + +`proxy check` queries every target **from inside the proxy container** – that +is where nginx opens the connection from, not your workstation: + +``` +Proxy-Host 1 stack.local + PFAD ZIEL ANTWORT + / http://127.0.0.1:81 HTTP 200 + /grafana http://127.0.0.1:3000 HTTP 302 + /cmk http://127.0.0.1:9999 nicht erreichbar +``` + +That is how you find a typo in a target address before the browser shows +`502 Bad Gateway`. The GUI has it under **Proxy-Hosts → Ziele prüfen**. + +> Port **3000 stays closed in the firewall**. Grafana is reached exclusively +> through the proxy, just like Checkmk on 5000. `firewall close` offers an +> accidentally opened port for closing again. + --- ## 11. Configuring the Proxy Manager locally (terminal browser) diff --git a/scripts/cmk-manage.sh b/scripts/cmk-manage.sh index 200c96e..c7de3dd 100755 --- a/scripts/cmk-manage.sh +++ b/scripts/cmk-manage.sh @@ -3940,6 +3940,23 @@ npm_reload_hint() { # restart-flag fi } +# Ein Ziel aus Sicht des Proxy-Containers pruefen - von dort aus baut +# nginx die Verbindung auf, nicht von diesem Rechner. +proxy_check_one() { # pfad schema host port + local path="$1" sch="$2" host="$3" port="$4" code + code="$($RUNTIME exec "$NPM_CONTAINER_NAME" \ + curl -s -o /dev/null -k --max-time 5 -w '%{http_code}' \ + "$sch://$host:$port/" 2>/dev/null)" || code=000 + case "${code:-000}" in + 000) printf ' %-9s %-34s %b\n' "$path" "$sch://$host:$port" \ + "${C_RED}nicht erreichbar${C_RST}" + return 1 ;; + *) printf ' %-9s %-34s %b\n' "$path" "$sch://$host:$port" \ + "${C_GRN}HTTP $code${C_RST}" + return 0 ;; + esac +} + cmd_proxy() { local sub="${1:-list}"; [ $# -gt 0 ] && shift || true @@ -4025,6 +4042,69 @@ cmd_proxy() { echo return 0 ;; + check|test) + local pid="" dom="" + while [ $# -gt 0 ]; do + case "$1" in + --id) pid="$2"; shift 2 ;; + --domain) dom="$2"; shift 2 ;; + *) die "Unbekannte Option fuer 'proxy check': $1" ;; + esac + done + npm_require_running || return 1 + NPM_TOKEN="$(npm_api_token)" || return 1 + + local all i + all="$(npm_api GET /api/nginx/proxy-hosts)" + local -a ids=() + if [ -n "$pid" ]; then + ids=("$pid") + elif [ -n "$dom" ]; then + for i in $(json_ids "$all"); do + if ( set +o pipefail; npm_api GET "/api/nginx/proxy-hosts/$i" | grep -qF "\"$dom\"" ); then + ids=("$i"); break + fi + done + [ "${#ids[@]}" -gt 0 ] || die "Kein Proxy-Host fuer '$dom' gefunden." + else + mapfile -t ids < <(json_ids "$all") + fi + if [ "${#ids[@]}" -eq 0 ]; then + warn "Es gibt keine Proxy-Hosts."; return 0 + fi + + local fehler=0 o d sch host port el + for i in "${ids[@]}"; do + o="$(npm_api GET "/api/nginx/proxy-hosts/$i")" + [ -n "$(json_num "$o" id)" ] || { warn "Proxy-Host $i gibt es nicht."; continue; } + d="$(printf '%s' "$o" | sed -n 's/.*"domain_names"[[:space:]]*:[[:space:]]*\[[[:space:]]*"\([^"]*\)".*/\1/p')" + printf '\n%sProxy-Host %s %s%s\n' "$C_BLD" "$i" "${d:-?}" "$C_RST" + printf ' %-9s %-34s %s\n' "PFAD" "ZIEL" "ANTWORT" + + sch="$(json_str "$o" forward_scheme)"; [ -n "$sch" ] || sch=http + host="$(json_str "$o" forward_host)"; port="$(json_num "$o" forward_port)" + proxy_check_one "/" "$sch" "$host" "$port" || fehler=$((fehler+1)) + + while IFS= read -r el; do + [ -n "$el" ] || continue + sch="$(json_str "$el" forward_scheme)"; [ -n "$sch" ] || sch=http + host="$(json_str "$el" forward_host)"; port="$(json_num "$el" forward_port)" + proxy_check_one "$(json_str "$el" path)" "$sch" "$host" "$port" || fehler=$((fehler+1)) + done < <(json_split_array "$(json_raw "$o" locations)") + done + + echo + if [ "$fehler" -eq 0 ]; then + ok "Alle Ziele antworten." + echo; return 0 + fi + err "$fehler Ziel(e) antworten nicht - dort meldet nginx 502 Bad Gateway." + printf ' Haeufigste Ursachen: Tippfehler in der Adresse (127.0.0.1 statt + 172.0.0.1), der Dienst laeuft nicht, oder er lauscht nur auf einer + anderen Schnittstelle. + Aendern mit: %s proxy edit --id N --location /pfad=host:port\n\n' "$SELF" + return 1 ;; + admin|adminui) local dom="" root="" off="" pid="" host="" path="" cert=self while [ $# -gt 0 ]; do @@ -4744,7 +4824,7 @@ EOF *) die "Unbekanntes Unterkommando 'proxy $sub'. Moeglich: status | list | show | certs | add | edit | del | default - | admin | reload | restart" ;; + | admin | check | reload | restart" ;; esac } @@ -5976,6 +6056,7 @@ Ziel : ${PROXY_FORWARD_HOST:-127.0.0.1}:${PROXY_FORWARD_PORT:-5000}" \ show "Einzelheiten eines Proxy-Hosts (mit Pfaden)" \ certs "Vorhandene Zertifikate anzeigen" \ ipzugang "HTTPS ueber die blanke IP (ohne Namen) ermoeglichen" \ + pruefen "Ziele pruefen - antwortet hinter jedem Pfad etwas?" \ adminui "Adminoberflaeche erreichbar machen (Name oder Wurzel)" \ neustart "Proxy Manager neu starten / nginx neu laden" \ del "Proxy-Host loeschen" \ @@ -5995,6 +6076,9 @@ Ziel : ${PROXY_FORWARD_HOST:-127.0.0.1}:${PROXY_FORWARD_PORT:-5000}" \ certs) local f2; f2="$(mktemp)"; ( NO_COLOR=1 cmd_proxy certs ) >"$f2" 2>&1 || true ui_text "Zertifikate" "$f2"; rm -f "$f2" ;; ipzugang) gui_proxy_default || true ;; + pruefen) local fp; fp="$(mktemp)" + ( NO_COLOR=1 cmd_proxy check ) >"$fp" 2>&1 || true + ui_text "Ziele der Proxy-Hosts" "$fp"; rm -f "$fp" ;; adminui) gui_proxy_admin || true ;; neustart) gui_proxy_restart || true ;; del) gui_proxy_del || true ;;