"ports": wer belegt 80/443/5000 - und lokales Checkmk stilllegen

Auf einem Rechner, auf dem Checkmk vorher direkt installiert war, haelt
die alte Instanz Port 5000 weiter besetzt, ein Apache aus der
Grundinstallation gern 80 und 443. Der Container startet dann nicht, und
die Suche ist muehsam: "ss" zeigt den Prozessnamen nur mit root-Rechten.

"ports" beantwortet beides. Den Prozess findet es ueber ss, netstat oder
lsof; die zugehoerige systemd-Unit liest es aus /proc/<pid>/cgroup - die
Datei ist ohne root lesbar, damit steht auch ohne sudo da, welcher
Dienst es ist. Nachgestellt mit eigenen Lauschern auf 5000 und 8000:
Programm und Scope werden korrekt genannt.

"ports --free" schlaegt vor, was sich stilllegen laesst, "--apply" fuehrt
es nach Rueckfrage aus:

  omd stop alt
  omd config alt set AUTOSTART off
  systemctl disable --now httpd.service

Geloescht wird NICHTS. "omd stop" haelt die Site nur an, AUTOSTART=off
verhindert den Start nach einem Neustart; /omd/sites bleibt unangetastet
und laesst sich weiterhin importieren - genau darum ging es.

Drei Sicherungen: gestoppt wird nur, was laeuft; ein .scope (etwa eine
Login-Sitzung) wird nie angeboten, weil sich daran nichts abschalten
laesst; und Container-Dienste bleiben aussen vor, sonst legte man sich
den eigenen Stack still.

Beim Testen ein Abbruch gefunden: fehlt die site.conf, liefert sed 2 und
unter "set -e" war mitten in der Ausgabe Schluss - die Liste der Sites
erschien gar nicht erst.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-21 09:13:21 +02:00
co-authored by Claude Opus 5
parent 58308264c6
commit ee9ca6db33
3 changed files with 353 additions and 0 deletions
+228
View File
@@ -64,6 +64,8 @@ KOMMANDOS - Stack
- SSH niemals, fremde Freigaben bleiben unberuehrt.
Der Adminport des Proxy Managers wird mit
angeboten; --keep-admin laesst ihn offen
ports [--free] wer belegt 80/443/5000/8000? Direkt installiertes
Checkmk stilllegen, ohne Daten zu loeschen
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")
@@ -7327,6 +7329,43 @@ gui_cert_self() {
}
# Bericht anzeigen; fehlt etwas, das Uebernehmen anbieten
# Wer belegt die Ports - und Angebot, es stillzulegen
gui_ports() {
local f rc=0
f="$(mktemp)"
( NO_COLOR=1 cmd_ports ) >"$f" 2>&1 || rc=$?
ui_text "Belegte Ports" "$f"; rm -f "$f"
local vorschlag rc2=0
vorschlag="$(mktemp)"
( NO_COLOR=1 cmd_ports --free ) >"$vorschlag" 2>&1 || rc2=$?
if ! ( set +o pipefail; grep -q '^ omd \|^ systemctl ' "$vorschlag" ); then
rm -f "$vorschlag"; return 0
fi
ui_text "Was stillgelegt werden koennte" "$vorschlag"; rm -f "$vorschlag"
ui_yesno "Stilllegen" "Die gezeigten Dienste jetzt stilllegen?
Es wird NICHTS geloescht. Eine direkt installierte Site wird nur
gestoppt und vom automatischen Start genommen - ihre Daten bleiben
und lassen sich weiterhin importieren." || return 0
local -a a=(ports --free --apply --yes)
if [ "$(id -u)" -eq 0 ]; then
ui_run "Stilllegen" "$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."
return 0
fi
f="$(mktemp)"
( NO_COLOR=1 cmd_ports ) >"$f" 2>&1 || true
ui_text "Belegte Ports - Stand danach" "$f"; rm -f "$f"
return 0
}
gui_firewall() {
local tmp rc=0
tmp="$(mktemp)"
@@ -7518,6 +7557,7 @@ Backups : $BACKUP_DIR" \
browser "Terminal-Browser (Proxy Manager lokal bedienen)" \
cert "Selbstsigniertes Zertifikat erzeugen" \
firewall "Firewall pruefen (welche Ports muessen offen sein)" \
ports "Belegte Ports - lokales Checkmk/Apache stilllegen" \
config "Einstellungen (.env) - menuegefuehrt" \
shell "Root-Shell im Checkmk-Container" \
quit "Beenden")" || break
@@ -7534,6 +7574,7 @@ Backups : $BACKUP_DIR" \
browser) gui_browser_menu || true ;;
cert) gui_cert || true ;;
firewall) gui_firewall || true ;;
ports) gui_ports || true ;;
config) gui_settings_menu || true ;;
shell) gui_need_container || continue
gui_run_interactive cmd_shell ;;
@@ -7614,6 +7655,192 @@ ${C_BLD}Auswertung${C_RST}
EOF
}
# =====================================================================
# Wer belegt die Ports, und wie legt man ihn stumm?
#
# Auf einem Rechner, auf dem Checkmk vorher direkt installiert war,
# haelt die alte Instanz Port 5000 weiter besetzt - und ein Apache aus
# der Grundinstallation gern 80 und 443. Der Container startet dann
# nicht, und die Suche nach dem Schuldigen ist muehsam, weil "ss" den
# Prozessnamen nur mit root-Rechten zeigt.
#
# Geloescht wird hier NICHTS. Eine direkt installierte Site enthaelt
# Daten, die man spaeter importieren will - sie wird nur gestoppt und
# vom automatischen Start genommen.
# =====================================================================
# Wer lauscht auf diesem Port? -> "pid|programm|unit"
port_owner() { # port
local port="$1" line pid prog unit=""
line=""
if command -v ss >/dev/null 2>&1; then
line="$(ss -ltnpH "sport = :$port" 2>/dev/null | head -1)"
fi
if [ -z "$line" ] && command -v netstat >/dev/null 2>&1; then
line="$(netstat -ltnp 2>/dev/null | awk -v p=":$port\$" '$4 ~ p {print; exit}')"
fi
[ -n "$line" ] || return 1
pid="$(printf '%s' "$line" | sed -n 's/.*pid=\([0-9]*\).*/\1/p')"
if [ -z "$pid" ]; then
pid="$(printf '%s' "$line" | sed -n 's|.*[[:space:]]\([0-9]\{1,\}\)/.*|\1|p')"
fi
if [ -z "$pid" ] && command -v lsof >/dev/null 2>&1; then
pid="$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -1)"
fi
if [ -n "$pid" ] && [ -r "/proc/$pid/comm" ]; then
prog="$(cat "/proc/$pid/comm" 2>/dev/null)"
else
prog="$(printf '%s' "$line" | sed -n 's/.*users:((\"\([^\"]*\)\".*/\1/p')"
fi
# Die systemd-Unit steht in der cgroup-Zeile - dafuer braucht es
# keine root-Rechte, anders als fuer den Prozessnamen aus ss.
if [ -n "$pid" ] && [ -r "/proc/$pid/cgroup" ]; then
unit="$(sed -n 's|.*/\([^/]*\.service\)$|\1|p' "/proc/$pid/cgroup" 2>/dev/null | head -1)"
if [ -z "$unit" ]; then
unit="$(sed -n 's|.*/\([^/]*\.scope\)$|\1|p' "/proc/$pid/cgroup" 2>/dev/null | head -1)"
fi
fi
printf '%s|%s|%s\n' "${pid:-?}" "${prog:-unbekannt}" "$unit"
}
# Sites einer direkt installierten Checkmk-Instanz
local_omd_sites() {
command -v omd >/dev/null 2>&1 || return 0
omd sites 2>/dev/null | awk 'NR>1 && $1 != "" {print $1}'
}
local_omd_running() { # site
( set +o pipefail; omd status "$1" 2>/dev/null | grep -qE '^Overall state:[[:space:]]*running' )
}
cmd_ports() {
local free="" apply=""
while [ $# -gt 0 ]; do
case "$1" in
--free) free=1; shift ;;
--apply) apply=1; free=1; shift ;;
--yes|-y) ASSUME_YES=1; shift ;;
--help|-h)
cat <<EOF
Nachsehen, wer die Ports dieses Aufbaus belegt.
$SELF ports nur anzeigen
sudo $SELF ports --free anbieten, die Belegung aufzuloesen
sudo $SELF ports --free --apply
Typischer Fall: auf dem Rechner war Checkmk frueher direkt installiert.
Die alte Site haelt Port 5000, ein Apache aus der Grundinstallation
haelt 80 und 443 - der Container startet dann nicht.
Geloescht wird nichts. Eine direkt installierte Site wird gestoppt und
vom automatischen Start genommen; ihre Daten bleiben unangetastet und
lassen sich weiterhin importieren.
EOF
return 0 ;;
*) die "Unbekannte Option fuer 'ports': $1" ;;
esac
done
printf '\n%sBelegte Ports%s\n' "$C_BLD" "$C_RST"
printf ' %-6s %-34s %-16s %s\n' "PORT" "ZWECK" "PROGRAMM" "UNIT/SCOPE"
local spec desc need port proto owner pid prog unit belegt=0
local -a units=()
while IFS='|' read -r spec desc need; do
[ -n "$spec" ] || continue
port="${spec%/*}"; proto="${spec##*/}"
[ "$proto" = tcp ] || continue
desc="$(printf '%s' "$desc" | cut -c1-33)"
owner="$(port_owner "$port")" || { printf ' %-6s %-34s %s\n' "$port" "$desc" "frei"; continue; }
pid="${owner%%|*}"; owner="${owner#*|}"
prog="${owner%%|*}"; unit="${owner##*|}"
# Unsere eigenen Container zaehlen nicht als Problem
case "$prog" in
conmon|podman|docker-proxy|dockerd|nginx|openresty|apache2|httpd|*) : ;;
esac
printf ' %-6s %-34s %-16s %s\n' "$port" "$desc" "$(printf '%s' "$prog" | cut -c1-16)" \
"$(printf '%s' "${unit:-–}" | cut -c1-30)"
belegt=$((belegt+1))
if [ -n "$unit" ]; then
case " ${units[*]-} " in *" $unit "*) : ;; *) units+=("$unit") ;; esac
fi
done < <(fw_required_ports)
# --- Direkt installiertes Checkmk --------------------------------
local -a sites=(); mapfile -t sites < <(local_omd_sites)
if [ "${#sites[@]}" -gt 0 ]; then
printf '\n%sDirekt installiertes Checkmk%s\n' "$C_BLD" "$C_RST"
printf ' Version : %s\n' "$(omd version 2>/dev/null | sed 's/.*: *//' || true)"
local st auto
for st in "${sites[@]}"; do
# Fehlt die Datei, liefert sed 2 - unter "set -e" waere hier
# sonst Schluss, mitten in der Ausgabe.
auto="$(sed -n "s/^CONFIG_AUTOSTART='\(.*\)'$/\1/p" \
"/omd/sites/$st/etc/omd/site.conf" 2>/dev/null || true)"
printf ' %-16s %-12s Autostart: %s\n' "$st" \
"$(local_omd_running "$st" && echo laeuft || echo 'gestoppt')" "${auto:-?}"
done
fi
if [ "$belegt" -eq 0 ] && [ "${#sites[@]}" -eq 0 ]; then
echo; ok "Kein Port belegt, keine direkt installierte Instanz."; echo
return 0
fi
if [ -z "$free" ]; then
printf '\n Aufloesen: sudo %s ports --free\n\n' "$SELF"
return 0
fi
# --- Vorschlaege --------------------------------------------------
printf '\n%sVorschlag%s\n' "$C_BLD" "$C_RST"
local -a cmds=()
for st in ${sites[@]+"${sites[@]}"}; do
if local_omd_running "$st"; then cmds+=("omd stop $st"); fi
cmds+=("omd config $st set AUTOSTART off")
done
local u
for u in ${units[@]+"${units[@]}"}; do
case "$u" in
*.scope) continue ;; # kein Dienst, nichts abzuschalten
docker.service|containerd.service|podman.service|podman.socket) continue ;;
esac
cmds+=("systemctl disable --now $u")
done
if [ "${#cmds[@]}" -eq 0 ]; then
warn "Nichts gefunden, das sich gefahrlos stilllegen liesse."
printf ' Die Belegung kommt vermutlich von den eigenen Containern.\n\n'
return 0
fi
local c
for c in "${cmds[@]}"; do printf ' %s\n' "$c"; done
printf '
%sDie Daten bleiben.%s "omd stop" haelt die Site nur an, AUTOSTART=off
verhindert den Start nach einem Neustart. Nichts wird geloescht - ein
Import ist weiterhin moeglich:
%s import --file <bundle>\n' "$C_BLD" "$C_RST" "$SELF"
if [ -z "$apply" ]; then
printf '\n Ausfuehren mit: sudo %s ports --free --apply\n\n' "$SELF"
return 1
fi
echo
[ "$(id -u)" -eq 0 ] || die "Dafuer werden root-Rechte benoetigt."
confirm "Diese Befehle jetzt ausfuehren?" || die "Abgebrochen."
for c in "${cmds[@]}"; do
log "$c"
eval "$c" >/dev/null 2>&1 || warn "Fehlgeschlagen: $c"
done
ok "Erledigt. Danach pruefen: $SELF ports"
echo
return 0
}
# =====================================================================
# Firewall
#
@@ -8876,6 +9103,7 @@ main() {
selftest) cmd_selftest ;;
tui-check) cmd_tui_check ;;
firewall) cmd_firewall "$@" ;;
ports) cmd_ports "$@" ;;
noproxy) cmd_noproxy "$@" ;;
help|-h|--help) usage ;;
*) err "Unbekanntes Kommando: $cmd"; echo; usage; exit 1 ;;