Zwangsproxy: lokale Anfragen daran vorbei, und ein Pruefpunkt dafuer

Steht in der Umgebung ein HTTP-Proxy und nimmt no_proxy die lokalen
Adressen nicht aus, schickt curl selbst eine Anfrage an 127.0.0.1
dorthin. Der Proxy kennt die Adresse nicht, und die Meldung landet an
einer Stelle, die nichts dafuer kann: "Anmeldung am Proxy Manager
fehlgeschlagen". Danach sucht man an den Zugangsdaten statt am Netz.

Zwei Teile:

1. Die eigenen Aufrufe gehen jetzt grundsaetzlich am Proxy vorbei, wenn
   das Ziel auf diesem Rechner liegt - erkannt an der Adresse
   (127.0.0.0/8, localhost, ::1, eigene IP). Anfragen ins Internet
   (Registry, Downloadportal) bleiben unberuehrt. Schlaegt die Anmeldung
   trotzdem fehl und steht ein Proxy in der Umgebung, nennt die Meldung
   diese Moeglichkeit jetzt ausdruecklich.

2. "noproxy" prueft und richtet ein. Erkannt wird nicht geraten, sondern
   an dem, was curl selbst sagt ("Uses proxy env variable http_proxy") -
   dafuer muss auf der Zieladresse nichts laufen. "--fix" schreibt
   /etc/profile.d/cmk-noproxy.sh, "--fix --user" die ~/.bashrc.
   Menuepunkt unter Einstellungen > Runtime und Zeitzone.

Beim Schreiben des Eintrags war der erste Entwurf falsch: er las nur
no_proxy in Kleinschreibung und haette ein vorhandenes NO_PROXY
weggeworfen. Jetzt werden beide Schreibweisen eingesammelt, Vorhandenes
steht vorn, unsere Adressen kommen dahinter, Doppelte fallen heraus.
Durchgespielt fuer: beide leer, nur klein, nur gross, beide verschieden,
127.0.0.1 schon vorhanden, Liste bereits vollstaendig.

Nachgemessen und vermerkt: no_proxy/NO_PROXY und https_proxy/HTTPS_PROXY
gelten bei curl in beiden Schreibweisen, HTTP_PROXY in Grossschreibung
dagegen nicht - das ignoriert curl absichtlich. Ist nur die gesetzt,
sagt der Bericht das dazu, statt einen Proxy zu melden, den curl gar
nicht benutzt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-20 23:11:20 +02:00
co-authored by Claude Opus 5
parent 91455f3f41
commit 467796e23a
3 changed files with 425 additions and 4 deletions
+282 -4
View File
@@ -64,6 +64,9 @@ KOMMANDOS - Stack
- SSH niemals, fremde Freigaben bleiben unberuehrt.
Der Adminport des Proxy Managers wird mit
angeboten; --keep-admin laesst ihn offen
noproxy [--fix] pruefen, ob lokale Adressen am HTTP-Proxy vorbeigehen
(Zwangsproxy: sonst laufen selbst Anfragen an
127.0.0.1 dorthin - sichtbar als "API-Fehler")
selftest Skripte auf bekannte Fallstricke pruefen
tui-check Testzeilen fuer die Rahmendarstellung ausgeben
(hilft bei q/x/l/m statt Linien, etwa in PuTTY)
@@ -978,6 +981,35 @@ _curl_cfg() { # user pass -> Konfigurationszeile auf stdout
printf 'user = "%s:%s"\n' "$u" "$p"
}
# ---------------------------------------------------------------------
# Lokale Anfragen am HTTP-Proxy vorbei
#
# In Umgebungen mit Zwangsproxy stehen http_proxy/https_proxy in der
# Umgebung, und wenn no_proxy die lokalen Adressen nicht ausnimmt,
# schickt curl selbst eine Anfrage an 127.0.0.1 an den Proxy. Der kennt
# die Adresse nicht und antwortet mit einem Fehler - hier sichtbar als
# "API-Fehler" beim Anmelden am Proxy Manager.
#
# Fuer Ziele auf diesem Rechner wird der Proxy deshalb ausdruecklich
# abgeschaltet. Anfragen ins Internet (Registry, Downloadportal) bleiben
# unberuehrt - die brauchen ihn ja.
# ---------------------------------------------------------------------
url_is_local() { # url
local h="${1#*://}"; h="${h%%/*}"; h="${h%%\?*}"
h="${h##*@}"; h="${h%%:*}"
case "$h" in
localhost|127.0.0.1|::1|\[::1\]|0.0.0.0) return 0 ;;
127.*) return 0 ;;
esac
[ -n "$h" ] && [ "$h" = "$(primary_ip 2>/dev/null)" ] && return 0
return 1
}
# Argumente fuer curl, damit eine lokale Anfrage nicht ueber den Proxy geht
curl_local_args() { # url
if url_is_local "$1"; then printf -- '--noproxy\n*\n'; fi
}
curl_auth() { # user pass curl-argumente...
local u="$1" p="$2"; shift 2
if [ -n "$u" ]; then
@@ -992,6 +1024,7 @@ http_code() { # url [user] [pass] [extra-header]
local url="$1" u="${2:-}" p="${3:-}" hdr="${4:-}"
local -a a=(-sS -o /dev/null -w '%{http_code}' --max-time 30)
if [ -n "$hdr" ]; then a+=(-H "$hdr"); fi
local np; while IFS= read -r np; do [ -n "$np" ] && a+=("$np"); done < <(curl_local_args "$url")
local code
code="$(curl_auth "$u" "$p" "${a[@]}" "$url" 2>/dev/null)" || code=000
printf '%s\n' "${code:-000}"
@@ -3728,15 +3761,25 @@ EOF
return 1
fi
local body
body="$(curl -sS --max-time 20 -X POST "$base/api/tokens" \
body="$(curl -sS --max-time 20 --noproxy '*' -X POST "$base/api/tokens" \
-H 'Content-Type: application/json' \
-d "{\"identity\":\"$NPM_ADMIN_EMAIL\",\"secret\":\"$NPM_ADMIN_PASSWORD\"}" 2>/dev/null)" || true
local tok; tok="$(json_str "$body" token)"
if [ -z "$tok" ]; then
err "Anmeldung am Proxy Manager fehlgeschlagen."
local m; m="$(json_str "$body" message)"
[ -n "$m" ] && printf ' Antwort: %s\n' "$m" >&2
if [ -n "$m" ]; then printf ' Antwort: %s\n' "$m" >&2; fi
printf ' Geprueft wurde: %s\n' "$base/api/tokens" >&2
# In Kundennetzen mit Zwangsproxy ist das die haeufigste Ursache
# fuer eine Fehlermeldung, die nach einem API-Problem aussieht.
if [ -n "${http_proxy:-}${HTTP_PROXY:-}${https_proxy:-}${HTTPS_PROXY:-}" ]; then
printf '
In dieser Umgebung steht ein HTTP-Proxy. Nimmt no_proxy die lokalen
Adressen nicht aus, geht selbst diese Anfrage an ihn - dann meldet
er den Fehler, nicht der Proxy Manager. Nachsehen mit:
%s noproxy
' "$SELF" >&2
fi
return 1
fi
printf '%s' "$tok"
@@ -3750,7 +3793,7 @@ npm_api() {
local method="$1" path="$2" data="${3:-}"
local base; base="$(npm_api_base)"
local -a a=(-sS --max-time 60 -X "$method" -H "Authorization: Bearer $NPM_TOKEN"
-w '\n%{http_code}')
--noproxy '*' -w '\n%{http_code}')
[ -n "$data" ] && a+=(-H 'Content-Type: application/json' -d "$data")
local out; out="$(curl "${a[@]}" "$base$path" 2>/dev/null)" || out=$'\n000'
NPM_CODE="${out##*$'\n'}"
@@ -3807,7 +3850,7 @@ npm_cert_selfsigned() { # domain
fi
log "Lade Schluessel und Zertifikat hoch (ID $id) ..." >&2
local up
up="$(curl -sS --max-time 60 -X POST "$(npm_api_base)/api/nginx/certificates/$id/upload" \
up="$(curl -sS --max-time 60 --noproxy '*' -X POST "$(npm_api_base)/api/nginx/certificates/$id/upload" \
-H "Authorization: Bearer $NPM_TOKEN" \
-F "certificate=@$dir/fullchain.pem" \
-F "certificate_key=@$dir/privkey.pem" 2>/dev/null)" || true
@@ -6366,6 +6409,7 @@ gui_settings_category() {
done < <(settings_defs)
# Zusatzaktionen, die inhaltlich zur Kategorie gehoeren
case "$cat" in
runtime) items+=("__net__" ">> Lokale Verbindungen pruefen (Zwangsproxy)") ;;
image) items+=("__get__" ">> Image jetzt holen (Registry oder Downloadportal)")
items+=("__wizard__" ">> Assistent: Edition, Version, Bezugsweg")
items+=("__test__" ">> Zugangsdaten jetzt pruefen (Registry + Downloadportal)") ;;
@@ -6391,6 +6435,7 @@ gui_settings_category() {
__fwcheck__) gui_firewall || true; continue ;;
__fwapply__) gui_firewall_apply || true; continue ;;
__fwclose__) gui_firewall_close || true; continue ;;
__net__) gui_noproxy || true; continue ;;
__npm__) local f; f="$(mktemp)"
( NO_COLOR=1 cmd_npm_enable; NO_COLOR=1 cmd_npm_port ) >"$f" 2>&1 || true
ui_text "NGINX Proxy Manager" "$f"; rm -f "$f"; continue ;;
@@ -7131,6 +7176,45 @@ Bitte als root erneut aufrufen."
}
# Nicht mehr benoetigte Ports wieder schliessen
# Zwangsproxy: geht 127.0.0.1 daran vorbei?
gui_noproxy() {
local f rc=0
f="$(mktemp)"
( NO_COLOR=1 cmd_noproxy ) >"$f" 2>&1 || rc=$?
ui_text "Lokale Verbindungen und HTTP-Proxy" "$f"
rm -f "$f"
[ "$rc" -eq 0 ] && return 0
ui_yesno "HTTP-Proxy" "Lokale Adressen laufen ueber den HTTP-Proxy.
Die Ausnahme jetzt dauerhaft eintragen?" || return 0
local sel
sel="$(ui_menu "Wohin eintragen?" \
"Fuer alle Benutzer braucht es root-Rechte. Dienste (systemd, cron)
lesen weder das eine noch das andere - dort gehoert die Ausnahme in
die Unit bzw. nach /etc/environment." \
system "Fuer alle: /etc/profile.d/cmk-noproxy.sh" \
user "Nur fuer mich: ~/.bashrc" \
back "zurueck")" || return 0
case "$sel" in back) return 0 ;; esac
local -a a=(noproxy --fix)
if [ "$sel" = user ]; then
a+=(--user)
local f2; f2="$(mktemp)"
( NO_COLOR=1 cmd_noproxy --fix --user ) >"$f2" 2>&1 || true
ui_text "Eingetragen" "$f2"; rm -f "$f2"
elif [ "$(id -u)" -eq 0 ]; then
ui_run "Eintragen" "$SELF" ${SELF_ENV_ARR[@]+"${SELF_ENV_ARR[@]}"} "${a[@]}" || true
elif command -v sudo >/dev/null 2>&1; then
gui_run_interactive sudo -- "$SELF" ${SELF_ENV_ARR[@]+"${SELF_ENV_ARR[@]}"} "${a[@]}"
else
ui_msg "Rechte" "Dafuer werden root-Rechte benoetigt."
fi
return 0
}
gui_firewall_close() {
local tmp rc=0 hat="" adminfall=""
tmp="$(mktemp)"
@@ -8017,6 +8101,199 @@ cmd_firewall() {
return 1
}
# =====================================================================
# Zwangsproxy: lokale Adressen ausnehmen
#
# In Kundennetzen steht oft ein HTTP-Proxy in der Umgebung. Nimmt
# no_proxy die lokalen Adressen nicht aus, schickt curl selbst eine
# Anfrage an 127.0.0.1 dorthin. Der Proxy kennt die Adresse nicht, und
# am Ende steht ein "API-Fehler" beim Anmelden am Proxy Manager - eine
# Meldung, die in die voellig falsche Richtung zeigt.
#
# Dieses Werkzeug umgeht das fuer seine eigenen Aufrufe bereits
# (curl --noproxy). Fuer alles andere auf dem Rechner - eigene
# curl-Tests, der Terminal-Browser, andere Skripte - muss no_proxy
# stimmen. Genau das prueft und richtet dieser Befehl ein.
# =====================================================================
NOPROXY_FILE="/etc/profile.d/cmk-noproxy.sh"
# Welche Adressen gehoeren in no_proxy?
noproxy_list() {
local ip host
ip="$(primary_ip 2>/dev/null)"
host="$(hostname 2>/dev/null)"
local l="localhost,127.0.0.1,::1"
if [ -n "$host" ]; then l="$l,$host"; fi
if [ -n "$ip" ]; then l="$l,$ip"; fi
printf '%s' "$l"
}
# Wuerde curl fuer diese Adresse den Proxy benutzen?
noproxy_would_use_proxy() { # url
local out
out="$(curl -sS -o /dev/null -v --max-time 3 "$1" 2>&1 || true)"
case "$out" in
*"Uses proxy env variable http_proxy"*|*"Uses proxy env variable https_proxy"*) return 0 ;;
esac
return 1
}
cmd_noproxy() {
local fix="" scope=system
while [ $# -gt 0 ]; do
case "$1" in
--fix) fix=1; shift ;;
--user) scope=user; shift ;;
--system) scope=system; shift ;;
--help|-h)
cat <<EOF
Pruefen, ob lokale Adressen am HTTP-Proxy vorbeigehen.
$SELF noproxy nachsehen
sudo $SELF noproxy --fix dauerhaft eintragen ($NOPROXY_FILE)
$SELF noproxy --fix --user nur fuer den eigenen Benutzer (~/.bashrc)
Steht in der Umgebung ein HTTP-Proxy und nimmt no_proxy die lokalen
Adressen nicht aus, laufen selbst Anfragen an 127.0.0.1 ueber ihn. Der
Proxy kennt die Adresse nicht - sichtbar wird das als "API-Fehler" beim
Anmelden am Proxy Manager, also an einer Stelle, die nichts dafuer kann.
EOF
return 0 ;;
*) die "Unbekannte Option fuer 'noproxy': $1" ;;
esac
done
local url="http://127.0.0.1:${NPM_ADMIN_PORT:-81}/"
printf '\n%sHTTP-Proxy in der Umgebung%s\n' "$C_BLD" "$C_RST"
# HTTP_PROXY in Grossschreibung beachtet curl absichtlich NICHT - eine
# Kopfzeile "Proxy:" wuerde sie in CGI-Umgebungen sonst setzen koennen.
# no_proxy, NO_PROXY, https_proxy und HTTPS_PROXY gelten dagegen alle.
local v any=""
for v in http_proxy HTTP_PROXY https_proxy HTTPS_PROXY; do
local val; eval "val=\"\${$v:-}\""
[ -n "$val" ] || continue
if [ "$v" = HTTP_PROXY ]; then
printf ' %-12s %s (curl ignoriert diese Schreibweise)\n' "$v" "$val"
else
printf ' %-12s %s\n' "$v" "$val"
any=1
fi
done
for v in no_proxy NO_PROXY; do
local val; eval "val=\"\${$v:-}\""
printf ' %-12s %s\n' "$v" "${val:-<leer>}"
done
if [ -z "$any" ]; then
echo
ok "Es ist kein HTTP-Proxy gesetzt - lokale Anfragen gehen direkt."
printf ' Auf Rechnern mit Zwangsproxy sieht das anders aus; dort hilft:
%s noproxy --fix\n\n' "$SELF"
return 0
fi
printf '\n%sGeht %s daran vorbei?%s\n' "$C_BLD" "$url" "$C_RST"
if noproxy_would_use_proxy "$url"; then
test_result fail "Nein - curl schickt auch diese Anfrage an den Proxy."
printf ' Daher kommen Meldungen wie "API-Fehler" beim Anmelden am
Proxy Manager: nicht der Proxy Manager antwortet nicht, sondern
der HTTP-Proxy weiss mit 127.0.0.1 nichts anzufangen.\n'
else
test_result ok "Ja - lokale Anfragen gehen direkt."
if [ -z "$fix" ]; then
printf '\n Die eigenen Aufrufe dieses Werkzeugs gehen ohnehin daran vorbei
(curl --noproxy). Fuer eigene Tests und andere Programme ist es
damit ebenfalls in Ordnung.\n\n'
return 0
fi
fi
local liste; liste="$(noproxy_list)"
printf '\n%sEinzutragen%s\n' "$C_BLD" "$C_RST"
printf ' no_proxy=%s\n' "$liste"
if [ -z "$fix" ]; then
printf '\n Dauerhaft setzen:
sudo %s noproxy --fix fuer alle (%s)
%s noproxy --fix --user nur fuer diesen Benutzer (~/.bashrc)\n\n' \
"$SELF" "$NOPROXY_FILE" "$SELF"
return 1
fi
local target
if [ "$scope" = user ]; then
target="$HOME/.bashrc"
else
target="$NOPROXY_FILE"
if [ "$(id -u)" -ne 0 ]; then
die "Zum Schreiben von $target werden root-Rechte benoetigt.
Entweder mit sudo, oder nur fuer diesen Benutzer:
$SELF noproxy --fix --user"
fi
fi
if ( set +o pipefail; grep -q 'cmk-manage.sh noproxy' "$target" 2>/dev/null ); then
ok "In $target steht der Eintrag bereits."
log "Wirksam wird er in neuen Sitzungen - oder sofort mit:
export no_proxy='$liste' NO_PROXY='$liste'"
return 0
fi
# Wichtig: was schon dasteht, bleibt - und zwar VORNE. Beide
# Schreibweisen werden eingesammelt, denn manche Systeme setzen nur
# NO_PROXY; wer nur no_proxy liest, wirft das Grosse weg. Doppelte
# Eintraege fallen dabei heraus.
local tmp; tmp="$(mktemp)"
{
printf '\n# Lokale Adressen nicht ueber den HTTP-Proxy leiten.\n'
printf '# Erzeugt von cmk-manage.sh noproxy --fix\n'
printf '# Vorhandene Eintraege bleiben erhalten und stehen vorn.\n'
printf '_cmk_local="%s"\n' "$liste"
printf '_cmk_all="${no_proxy:-}"\n'
printf 'if [ -n "${NO_PROXY:-}" ]; then _cmk_all="${_cmk_all:+${_cmk_all},}${NO_PROXY}"; fi\n'
printf '_cmk_all="${_cmk_all:+${_cmk_all},}${_cmk_local}"\n'
printf '_cmk_out=""\n'
printf 'for _cmk_e in $(printf "%%s" "$_cmk_all" | tr "," " "); do\n'
printf ' case ",${_cmk_out}," in\n'
printf ' *",${_cmk_e},"*) ;;\n'
printf ' *) _cmk_out="${_cmk_out:+${_cmk_out},}${_cmk_e}" ;;\n'
printf ' esac\n'
printf 'done\n'
printf 'no_proxy="$_cmk_out"\n'
printf 'NO_PROXY="$_cmk_out"\n'
printf 'export no_proxy NO_PROXY\n'
printf 'unset _cmk_local _cmk_all _cmk_out _cmk_e\n'
} >"$tmp"
if [ "$scope" = user ]; then
cat "$tmp" >> "$target"
else
{ [ -f "$target" ] && cat "$target"; printf '#!/bin/sh\n'; } >/dev/null 2>&1
{ printf '#!/bin/sh\n'; cat "$tmp"; } > "$tmp.f"
install -m 0644 "$tmp.f" "$target"
rm -f "$tmp.f"
fi
rm -f "$tmp"
ok "Eingetragen in $target"
log "Vorhandene Eintraege bleiben erhalten und stehen vorn - ergaenzt wird
nur, was fehlt (beide Schreibweisen, no_proxy und NO_PROXY)."
log "Wirksam in neuen Sitzungen. Sofort:
export no_proxy='$liste' NO_PROXY='$liste'"
cat <<EOF
${C_BLD}Was damit NICHT erledigt ist${C_RST}
Dienste lesen keine Profildateien. Laeuft der Stack ueber systemd und
steht dort ein Proxy, gehoert die Ausnahme zusaetzlich nach
/etc/environment oder in eine Unit-Datei:
Environment=no_proxy=$liste
EOF
return 0
}
# =====================================================================
# Selbstpruefung
#
@@ -8212,6 +8489,7 @@ main() {
selftest) cmd_selftest ;;
tui-check) cmd_tui_check ;;
firewall) cmd_firewall "$@" ;;
noproxy) cmd_noproxy "$@" ;;
help|-h|--help) usage ;;
*) err "Unbekanntes Kommando: $cmd"; echo; usage; exit 1 ;;
esac