Auf RHEL ist firewalld ab Werk aktiv und gibt in aller Regel nur SSH
frei. Der Stack laeuft dann sauber, ist aber von aussen nicht zu
erreichen: die Agents kommen nicht durch den Receiver auf 8000, und im
Browser passiert nichts. Das war bisher nur eine Handvoll statischer
Beispielzeilen in der README und in install.sh.
Neu ist "cmk-manage.sh firewall". Erkannt werden firewalld, ufw,
nftables und iptables; welche Ports noetig sind, ergibt sich aus der
Konfiguration statt aus einer festen Liste:
- ohne Proxy Manager wird 5000 zum Pflichtport, mit ihm soll es zu
bleiben - offenes 5000 wird deshalb ausdruecklich angemerkt
- der Admin-Port kommt aus NPM_ADMIN_PORT, nicht fest aus 81
- FW_ROLE unterscheidet single/central/remote: eine Remote-Site
braucht 6557 eingehend, die Zentrale baut die Verbindungen selbst
auf und braucht dafuer nichts. Steht FW_ROLE=remote bei
CMK_LIVESTATUS_TCP=off, ist der Port zwar offen, es lauscht aber
niemand - auch darauf weist der Bericht hin.
- Event Console ueber FW_EC_SYSLOG / FW_EC_SNMPTRAP / FW_EC_TCP,
Agent dieses Hosts ueber FW_AGENT, alles Weitere (Grafana, NSCA)
ueber FW_EXTRA_PORTS
Syslog und SNMP-Traps laufen ueber UDP, eine TCP-Freigabe nuetzt ihnen
nichts. Ports werden deshalb durchweg als nummer/protokoll gefuehrt -
beim Pruefen wie beim Erzeugen der Regeln. Bestehende Regeln werden
entsprechend gelesen: Portbereiche (6556-6558 bzw. 6556:6558) werden
aufgeloest, firewalld-Dienstnamen und Rich Rules zaehlen mit, bei
firewalld alle aktiven Zonen statt nur der Standardzone, und eine
ufw-Regel ohne Protokoll zaehlt fuer TCP und UDP - so wie ufw sie
anlegt.
"firewall open" zeigt die passenden Kommandos, --apply setzt sie nach
Rueckfrage, --source begrenzt sie auf ein Quellnetz (fuer Port 80 mit
Warnung, weil Lets Encrypt von aussen prueft). Ohne Befund ist der
Rueckgabewert 1, damit sich das auswerten laesst. In der Oberflaeche
steckt derselbe Bericht unter "Firewall pruefen".
Nebenbei: env_set hat Werte mit Leerzeichen samt Anfuehrungszeichen in
die laufende Umgebung exportiert. In der Datei gehoeren sie hin, im
Wert nicht - das fiel erst mit den Beschreibungen in FW_EXTRA_PORTS auf.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Das Kommando gab es schon, den Weg dorthin nicht. Jetzt erreichbar unter
Einstellungen -> NGINX Proxy Manager -> ">> Datenbank zuruecksetzen"
und zusaetzlich im Proxy-Hosts-Menue.
Der Dialog fuehrt durch die Sache, statt nur auszufuehren:
* Gibt es noch keine Datenbank, sagt er das und nennt den kuerzeren
Weg (Zugangsdaten setzen, Stack starten).
* Fehlen NPM_ADMIN_EMAIL/-PASSWORD, weist er darauf hin, dass sonst
wieder kein Konto entstuende, und bietet an, sie gleich einzutragen.
* Vor dem Zuruecksetzen zeigt er Dateigroesse, was verloren geht
(Konten, Proxy-Hosts, Weiterleitungen, Zugriffslisten) und was
bleibt (Zertifikate), samt dem Konto, das danach entsteht.
* Anschliessend bietet er an, den Stack gleich zu starten - ohne das
entsteht das Konto naemlich noch nicht.
Beide Zweige durchgespielt: ohne Datenbank meldet er "Nichts
zurueckzusetzen", mit Datenbank legt er sie als database.sqlite.alt-<stamp>
beiseite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Ich dachte, der nginx legt die an" - eine berechtigte Erwartung, die
seit NPM 2.15 nicht mehr stimmt. Die Meldung sagte zwar, WAS fehlt, aber
nicht, was im jeweiligen Zustand zu tun ist.
npm_api_token sieht jetzt nach, ob data/npm/data/database.sqlite schon
existiert, und unterscheidet:
keine Datenbank Zugangsdaten setzen und "up" - der Container legt
das Konto daraus selbst an
Datenbank da Die Variablen wirken nicht mehr. Konto im Browser
anlegen und dieselben Daten mit "config set"
eintragen.
Dazu "npm-reset" fuer den sauberen Neuanfang: es legt die Datenbank
beiseite (umbenannt, nicht geloescht), sodass der Container das Konto
beim naechsten Start neu erzeugt. Vorher zeigt es an, was dabei verloren
geht - Proxy-Hosts, Weiterleitungen, Zugriffslisten - und was bleibt:
die Zertifikate. Mit Rueckfrage.
Beide Zweige durchgespielt. In beiden Sprachfassungen dokumentiert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drei Dinge aus einem Bildschirmfoto des laufenden Systems:
1. In jeder Textanzeige standen rohe Steuerzeichen (^[[1mImage^[[0m).
Die Farbvariablen werden beim Laden von common.sh einmal festgelegt -
das NO_COLOR beim spaeteren Aufruf wirkte deshalb nicht mehr. Statt
an jeder Aufrufstelle zu flicken, entfernt jetzt _ui_file die
Sequenzen; das greift fuer ALLE Textanzeigen, auch die von ui_run.
2. Der Status nannte weiterhin "Erstlogin: admin@example.com /
changeme". Diese Zugangsdaten gibt es seit NPM 2.15 nicht mehr -
genau das war hier schon Thema. Er zeigt jetzt NPM_ADMIN_EMAIL, und
weist darauf hin, wenn kein Konto hinterlegt ist.
3. Ein aus dem Tarball geladenes Image heisst nicht
"localhost/checkmk/..." wie von mir dokumentiert, sondern
"docker.io/checkmk/check-mk-enterprise:2.4.0p9" - Podman ergaenzt den
unqualifizierten Namen aus dem Archiv um docker.io, obwohl es das
Image dort nicht gibt. Beides in beiden Sprachfassungen
richtiggestellt, mit dem Hinweis, dass das harmlos ist: lokal ist es
da, "up" nutzt es, nur pullen liesse es sich so nicht - dafuer gibt
es "image get".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drei zusammenhaengende Vereinfachungen aus der Bedienung heraus:
1. CMK_IMAGE_SOURCE bestimmt, woher ein fehlendes Image kommt:
auto erst Registry, dann Downloadportal (Vorgabe)
registry nur Registry
download nur Tarball
"auto" ist die sinnvolle Vorgabe, weil Registries nur die juengsten
Patchstaende vorhalten - genau die aeltere Version, die eine
Migration braucht, liegt dann nur noch im Archiv. Der Wechsel
geschieht ohne Rueckfrage.
2. "image get" holt ueber diesen Weg, ohne dass Edition und Version
erneut abgefragt werden. Im Menue fragt "Image holen" entsprechend
nur noch nach dem Bezugsweg; der eingestellte ist gekennzeichnet.
Der vollstaendige Assistent bleibt als eigener Punkt daneben, fuer
den Fall, dass Edition oder Version geaendert werden sollen.
3. "up" beschafft ein lokal fehlendes Image selbst, bevor compose
startet. Vorher lief compose in einen nackten Pull, der bei einer nur
im Downloadportal vorhandenen Version scheitern musste - genau der
Fall mit 2.4.0p9. Mit "up --no-fetch" bleibt das aus.
Durchgespielt mit einer absichtlich nicht existierenden Version: up
erkennt das fehlende Image, versucht die Registry, weicht auf das
Downloadportal aus, bricht nach beiden Fehlschlaegen ab - und ruft
compose gar nicht erst auf. Mit --no-fetch geht es direkt zu compose.
Nebenbei: im Auswahlmenue erschien der eingestellte Bezugsweg doppelt
(einmal als "wie eingestellt", einmal unter seinem Namen) - zwei
Eintraege mit derselben Kennung. Jetzt wird der eingestellte nur noch
gekennzeichnet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Diagnose auf dem Zielsystem zeigte: enterprise/check-mk-enterprise
fuehrt im Zweig 2.4.0 nur p31 aufwaerts, dazu -latest und die taeglichen
Bauten. 2.4.0p9 ist dort herausgerollt - die Registry haelt nur die
juengsten Patchstaende vor.
Nachgemessen, dass das Downloadportal die Version weiterhin hat:
.../2.4.0p9/check-mk-enterprise-docker-2.4.0p9.tar.gz HTTP 401
also vorhanden, nur geschuetzt. Auch das oeffentliche Versionsarchiv
listet sie mit genau diesem Dateinamen.
Das ist kein Randfall, sondern der Normalfall bei einer Migration:
"omd restore" verlangt exakt die Version der Quell-Site, und die ist in
aller Regel aelter als das, was die Registry noch vorhaelt.
image_diagnose_ref sieht deshalb bei einem fehlenden Tag im Archiv nach
und nennt, wenn die Version dort liegt, den Tarball-Weg samt fertigem
Kommando - statt den Benutzer zwischen Registry und Portal suchen zu
lassen. Mit dem Unterschied zwischen beiden Quellen ist das jetzt auch
in beiden Sprachfassungen erklaert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Dialog hinter CMK_IMAGE/EDITION/VERSION bot bisher nur eine
Richtung an: Vorgaben aus dem verwendeten Image ableiten. Die
naheliegende Gegenrichtung - aus Edition und Version das Image bilden -
war nur ueber den ganzen Assistenten erreichbar, obwohl beide Werte
schon dastehen.
Neuer Eintrag "Image aus den Vorgaben setzen". Er zeigt gleich in der
Beschriftung, was dabei herauskommt, z.B.
setzen Image aus den Vorgaben setzen
-> registry.checkmk.com/enterprise/check-mk-enterprise:2.4.0p9
und traegt die Referenz nach Rueckfrage ein, ohne etwas zu laden.
Fehlt Edition oder Version, sagt der Dialog das, statt eine
unbrauchbare Referenz zu bilden.
Die drei Wege stehen jetzt in einer nachvollziehbaren Reihenfolge:
Assistent (alles), setzen (Vorgaben -> Image), angleichen (Image ->
Vorgaben).
Geprueft mit gesetzten Vorgaben: der Eintrag bildet die richtige
Referenz, das Uebernehmen schreibt sie in die .env, und whiptail
rendert den laengeren Eintrag ohne Fehler - die Fensterbreite waechst
mit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Erkennen verhindert hat
Der Pull meldete "manifest unknown". Das heisst, anders als zuvor: der
Repository-Pfad registry.checkmk.com/enterprise/check-mk-enterprise ist
richtig und die Anmeldung auch - nur das Tag 2.4.0p9 gibt es dort nicht.
Neu "image why [REF]" unterscheidet die drei Faelle anhand der Registry
statt anhand der Fehlermeldung:
401/403 Anmeldung oder Berechtigung
404 Repository gibt es nicht -> image repos
Tag Repository ja, Tag nein -> vorhandene Tags des
Zweigs werden aufgelistet
"up" ruft das von selbst auf, wenn das Starten scheitert und das Image
nicht lokal vorliegt; "image pull" ebenfalls.
Beim Bauen stellte sich heraus, dass die Tag-Pruefung immer "nicht
vorhanden" meldete - auch fuer Tags, die es gibt. Ursache ist eine
Falle, die das ganze Projekt betraf:
printf ... | grep -q muster
"grep -q" endet beim ersten Treffer. Der Erzeuger bekommt dann SIGPIPE,
und unter "set -o pipefail" gilt die ganze Pipe als fehlgeschlagen -
aber nur, wenn er noch am Schreiben war. Bei drei Zeilen faellt das nie
auf, bei 2393 Tags immer. Nachgestellt:
set -euo pipefail; seq 1 500000 | grep -qx 3 -> schlaegt fehl
Betroffen waren zwoelf Stellen, darunter zwei, die ernster sind als die
Tag-Liste: bundle_read prueft mit "tar -tzf ... | grep -qx", ob ein
Backup ein Bundle ist. Bei einem grossen Archiv haette ein gueltiges
Bundle als "kein Bundle" gegolten - also ein stiller Fehler bei Restore
und Import genau dann, wenn das Backup gross ist. Alle zwoelf laufen
jetzt in einer Subshell ohne pipefail.
Der Selbsttest hat dafuer eine eigene Pruefung bekommen, damit die
Schreibweise nicht zurueckkehrt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Diagnose auf dem betroffenen Terminal war eindeutig: Zeile A (echte
UTF-8-Rahmenzeichen) zeichnete Linien, Zeile B (VT100-Umschaltung ESC(0)
nicht. Locale war bereits C.UTF-8, dialog war installiert - und die
Oberflaeche sah trotzdem falsch aus.
Grund: ncurses benutzt die VT100-Umschaltung AUCH mit UTF-8-Locale,
solange das terminfo sie anbietet - bei TERM=xterm der Fall. Die
Locale-Korrektur allein reichte deshalb nicht, und der Wechsel von
whiptail zu dialog machte es sogar schlechter, weil whiptail (slang)
bei UTF-8-Locale von sich aus Unicode zeichnet, ncurses aber nicht.
Abhilfe ist NCURSES_NO_UTF8_ACS=1. Mit dialog unter LANG=C.UTF-8 und
TERM=xterm gemessen:
ohne Schalter : 38 ESC(0, 0 Rahmenbytes, 182 rohe q
mit Schalter : 0 ESC(0, 220 Rahmenbytes, 0 rohe q
ui_init setzt den Schalter jetzt, sobald eine UTF-8-Locale gilt.
CMK_TUI_ACS=1 stellt das alte Verhalten wieder her - fuer Terminals, die
ESC(0 beherrschen, aber kein UTF-8.
tui-check zeigt den Stand des Schalters an und erklaert bei "A Linien,
B Buchstaben" jetzt, dass genau dieser Fall abgefangen wird und die
Oberflaeche trotzdem sauber aussehen muss.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nach der Locale-Korrektur meldet der Selbsttest UTF-8, die Anzeige blieb
aber unveraendert. Damit liegt es nicht mehr an der Serverseite, und
Weiterraten hat keinen Sinn.
"cmk-manage.sh tui-check" gibt jetzt zwei Testzeilen aus:
Zeile A echte UTF-8-Rahmenzeichen (U+2500 ff.)
Zeile B die alte VT100-Umschaltung ESC(0 mit den Buchstaben lqk
An deren Aussehen laesst sich die Ursache direkt ablesen - Terminal
ignoriert die Umschaltung (PuTTY-Fall), Terminal nicht im UTF-8-Modus,
oder Schrift ohne Rahmenzeichen. Zu jeder Beobachtung nennt die Ausgabe
die passende Abhilfe samt PuTTY-Einstellungen.
Dazu der ASCII-Schalter, der bisher ins Leere lief: einfache Rahmen kann
nur dialog zeichnen. Ist CMK_TUI_ASCII gesetzt und dialog vorhanden,
wird dialog jetzt dafuer vorgezogen; fehlt es, sagt das Skript das
ausdruecklich statt wirkungslos weiterzumachen.
dialog ist auf dieser Seite ohnehin der wirksamere Griff - es geht mit
dem Zeichensatz zuverlaessiger um als whiptail. Entsprechend in beiden
Sprachfassungen hervorgehoben.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gemeldet mit Bildschirmfoto: statt Rahmen erscheinen q, x, l und m.
Ursache nachgemessen - ohne UTF-8-Locale zeichnen dialog und whiptail
die Rahmen ueber die alte VT100-Umschaltung ESC(0, die Terminals im
UTF-8-Modus (PuTTY in der Vorgabe) ignorieren:
LC_ALL=C ESC(0-Umschaltungen: 1 rohe q: 91
LC_CTYPE=C.UTF-8 ESC(0-Umschaltungen: 0 rohe q: 0
ui_init setzt jetzt beim Start der Oberflaeche LC_CTYPE auf die erste
verfuegbare UTF-8-Locale. Bewusst nur LC_CTYPE, damit Sortierung und
Zahlenformat unberuehrt bleiben. Dabei war ein zweiter Anlauf noetig:
ein gesetztes LC_ALL sticht jede einzelne Kategorie aus, der erste
Versuch blieb deshalb wirkungslos. Steht LC_ALL auf einem
Nicht-UTF-8-Wert, wird es entfernt.
Gemessen ueber unsere eigene ui_menu unter LC_ALL=C: vorher 91 rohe q,
danach 0.
Gibt es gar keine UTF-8-Locale, faellt die Oberflaeche auf einfache
Rahmen zurueck (dialog --ascii-lines, sofern unterstuetzt); erzwingen
laesst sich das mit CMK_TUI_ASCII=1. Der Selbsttest hat dafuer einen
eigenen Abschnitt, der die Locale vorher und nachher zeigt und im
Zweifel das fehlende Sprachpaket nennt.
Dokumentiert als Abschnitt 14.4 in beiden Sprachfassungen, samt der
vier PuTTY-Einstellungen fuer die Gegenseite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Berechtigter Einwand: wer Edition und Version waehlt und "nichts holen"
sagt, will das Image SETZEN, nicht bloss zwei Vorgaben notieren. Mein
Entwurf trennte das kuenstlich - und hinterliess genau dadurch den
Zustand, den die Abweichungswarnung dann anmahnte. Das war die Ursache
des Hin und Her.
Die Referenz laesst sich aus Edition und Version bilden:
enterprise 2.4.0p9 -> registry.checkmk.com/enterprise/check-mk-enterprise:2.4.0p9
raw 2.4.0p9 -> docker.io/checkmk/check-mk-raw:2.4.0p9
Nur bei einem aus dem Tarball geladenen Image geht das nicht, da kommt
der Name aus dem Archiv.
Deshalb:
image use --edition E --version V
bildet die Referenz und traegt CMK_IMAGE, CMK_EDITION und CMK_VERSION
ein, ohne irgendetwas herunterzuladen. Geholt wird beim naechsten "up"
durch die Runtime selbst.
Im Assistenten heisst der Weg jetzt "Nur eintragen - geholt wird beim
naechsten Start" und nutzt genau das. Damit setzen ALLE vier Wege
CMK_IMAGE; sie unterscheiden sich nur darin, ob dabei schon etwas
geholt wird. Die drei Werte bleiben dadurch von selbst stimmig, und die
Abweichungswarnung tritt im normalen Betrieb nicht mehr auf.
Geprueft: das Kommando und der Assistentenweg setzen alle drei Werte,
danach meldet "image status" keine Abweichung mehr.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
verstuemmelt an
Der Download schlug trotz richtiger Zugangsdaten fehl. Ursache gefunden:
die Uebergabe lief ueber eine temporaere .netrc, deren Format die Felder
durch Leerzeichen trennt. Ein Kennwort mit Leerzeichen, #, " oder
aehnlichem kam dadurch still verstuemmelt an - curl schickte etwas
anderes als hinterlegt, der Server antwortete 401, und die Meldung
lautete "Anmeldung abgelehnt", obwohl die Daten stimmten.
Gegen einen eigens gestarteten Testserver nachgemessen, Kennwort
'Pa ss#wo"rt$1':
ohne Anmeldung HTTP 401
neu, curl --config - HTTP 200
alt, .netrc HTTP 401
Alle dreizehn Aufrufstellen sind auf curl_auth umgestellt, das die
Angaben ueber "curl --config -" von der Standardeingabe liest und in
Anfuehrungszeichen setzt. Beliebige Zeichen sind damit moeglich, und die
Zugangsdaten stehen weiterhin nicht in der Prozessliste - geprueft.
Ausserdem sagt der Download jetzt, WORAN es liegt: vor dem Holen wird
der Status abgefragt und 401/403 (Anmeldung), 404 (Datei gibt es nicht)
und 000 (nicht erreichbar) getrennt gemeldet. Doppelte Kandidaten
werden nicht mehr zweimal probiert.
Dazu zwei Aergernisse aus der Bedienung beseitigt:
* Der Assistent meldete nach "nur eintragen" die Abweichung als
Problem, obwohl der Benutzer genau das gewollt hatte. Jetzt steht
dort schlicht, dass Edition und Version gespeichert sind und kein
Image geholt wurde.
* Der Dialog hinter CMK_IMAGE/EDITION/VERSION war lang und erklaerte
mehr, als er half. Er nennt jetzt in zwei Zeilen die Lage und stellt
die Handlung voran.
Randnotiz zur Fehlersuche: die URL selbst war nie das Problem. Der
Server liefert fuer check-mk-enterprise-docker-2.4.0p9.tar.gz ein 401,
also "vorhanden, nur geschuetzt" - und fuer check-mk-pro-... ein 404,
denn "pro" heisst die Edition erst ab 2.5.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Die berechnete Fenstergroesse galt bisher nur fuer die Dialoge, die ich
dafuer einzeln angefasst hatte. Eine Pruefung des ganzen Projekts zeigte
zwei Luecken:
* in common.sh gab es neun verstreute Aufrufstellen von dialog/whiptail
* migrate_creator.sh hatte noch durchgehend feste Groessen
(20 76 11, 10 76, 18 76, 14 76, 22 78, 8 76)
Beide Skripte rufen dialog/whiptail jetzt nur noch ueber drei Stellen auf:
_ui_list Listenfenster (menu, checklist, radiolist)
_ui_box Textfenster (msgbox, yesno, inputbox, passwordbox, infobox)
_ui_file Dateianzeige (textbox)
Die berechnen die Groesse selbst. Damit stimmt sie auch fuer Fenster, die
spaeter dazukommen - man muss nur eine der ui_*-Huellen benutzen.
Neu: "cmk-manage.sh selftest" haelt das nach. Geprueft werden Syntax
aller vier Skripte, Dialoge an den Engstellen vorbei, fest eingetragene
Fenstergroessen, das Muster "[ ... ] && VAR=..." und die Vollstaendigkeit
der Einstellungstabelle gegenueber .env.example.
Der Lauf fand sofort 19 Altlasten des Musters "[ ... ] && VAR=...", das
hier schon dreimal einen stillen Abbruch verursacht hat - alle in
if-Bloecke umgeschrieben und stichprobenartig geprueft, auch die Faelle
mit case-Abschluss und Array-Zuweisung. Danach laeuft die Selbstpruefung
vollstaendig durch.
Regression: alle CLI-Kommandos, zwoelf Menuefunktionen und das Rendern
von sechs Fenstern unter whiptail in einem echten Pseudo-Terminal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drei zusammenhaengende Befunde aus der Praxis, alle am laufenden
Container nachgeprueft:
1. Die Standardzugangsdaten admin@example.com/changeme stimmen nicht
mehr. In backend/setup.js von v2.15.1 steht, dass das erste Konto
nur angelegt wird, wenn INITIAL_ADMIN_EMAIL und
INITIAL_ADMIN_PASSWORD gesetzt sind - sonst kommt der
Einrichtungsassistent. Meine README war an der Stelle falsch.
Die beiden Werte stehen jetzt als NPM_ADMIN_EMAIL/-PASSWORD in der
.env und werden von compose und der Quadlet-Unit durchgereicht.
Gegen einen frisch gestarteten Container geprueft: das Konto ist
sofort da und die Anmeldung an der API liefert ein Token.
2. Die Weboberfliche laesst sich im Terminal-Browser kaum bedienen
(JSON-Fehler bei Proxy-Hosts, keine Datei-Uploads). Da der Proxy
Manager ohnehin lokal laeuft, gibt es jetzt
proxy status | list | certs | add | del
Damit legt "proxy add --domain X" in einem Zug an: Zertifikat
erzeugen bzw. bei Let's Encrypt anfordern, hochladen, Proxy-Host
anlegen, HTTPS erzwingen, HTTP/2 und Websockets setzen. Der Zielport
ist frei waehlbar (--forward-port), nicht auf 5000 festgelegt.
Im Menue unter "proxy" und in den Einstellungen unter NGINX Proxy
Manager.
3. Let's Encrypt nimmt in 2.15 die E-Mail des angemeldeten Kontos; ein
letsencrypt_email im Aufruf gibt es nicht mehr. Entsprechend
umgesetzt und im Fehlerfall erklaert.
Beim Testen gegen die echte Instanz gefunden und behoben:
- Wieder "set -e": ein grep ohne Treffer beendete das Skript still,
"proxy add" tat daraufhin gar nichts. Vier Stellen abgesichert.
- Die Listenantwort laesst sich nicht an "{" zerlegen, weil
"meta":{...} ebenfalls Klammern enthaelt - dadurch fehlte das
Schema in der Ausgabe. Die Eintraege werden jetzt einzeln geholt.
- --update legte jedes Mal ein neues Zertifikat an. Ein vorhandener
eigener Eintrag gleichen Namens wird jetzt wiederverwendet.
Durchgespielt: Anmeldung, Anlegen mit selbstsigniertem Zertifikat,
Liste, Zertifikatsliste, --update, Loeschen, Neuanlage mit anderem
Zielport und alle Fehlerfaelle. Gegengeprueft, dass Port 443 danach
tatsaechlich mit unserem Zertifikat antwortet (CN=checkmk.test.local)
und Port 80 per 301 dorthin umleitet.
Dokumentiert als Kapitel 10.1 bis 10.3 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gemeldet war "CMK_IMAGE wird nicht aktualisiert". Nachgemessen: das
Neu-Einlesen funktioniert - ein Unterprozess schreibt, load_env liest,
der Wert kommt an. Die eigentliche Ursache war, dass der Pull gegen die
Registry fehlschlug und CMK_IMAGE deshalb zu Recht unveraendert blieb.
Nur sagte der Assistent das nicht, sondern zeigte die generische
Meldung "passt nicht zu den Vorgaben" - was wie ein Anzeigefehler
aussieht.
- Der Assistent wertet den Rueckgabewert jetzt aus und meldet
"Image wurde NICHT geholt", nennt CMK_IMAGE unveraendert und die
naechsten Schritte (image repos, Tarball-Weg, Zugangsdaten).
- Edition und Version werden bei einem Fehlschlag nicht halb
uebernommen: --remember greift erst nach erfolgreichem Holen.
Damit man aus einem bereits bestehenden halben Zustand herauskommt,
gibt es jetzt den umgekehrten Weg:
image use REF --remember
leitet CMK_EDITION und CMK_VERSION aus der Image-Referenz ab
(check-mk-<edition>:<tag>). Im Menue steht hinter den drei Werten
entsprechend eine Auswahl: Assistent starten, Vorgaben an das
verwendete Image angleichen, oder nichts aendern.
Geprueft: Schreiben im Unterprozess und Neu-Einlesen, Ableiten der
Vorgaben aus dem Image (Warnung verschwindet danach), Assistent mit
absichtlich falschen Zugangsdaten meldet den Fehlschlag und laesst
die .env unangetastet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Login Succeeded" und trotzdem "unable to copy from source": der
Repository-Pfad enterprise/check-mk-enterprise war geraten, nicht
belegt. Statt weiter zu raten fragt das Werkzeug jetzt nach.
image repos [--edition E] [--registry R] [--user/--password]
fragt zuerst /v2/_catalog ab (sperren viele Registries) und probiert
danach die ueblichen Pfade durch, je Edition und deren Aliasnamen:
<ed>/check-mk-<ed>, check-mk-<ed>, checkmk/check-mk-<ed>,
enterprise/check-mk-<ed>
Gemeldet wird pro Pfad der HTTP-Status samt Tag-Anzahl. Scheitert ein
"image pull" gegen eine private Registry, laeuft die Pruefung von
selbst an und zeigt gleich, was dort liegt.
Wichtig bei der Deutung und deshalb auch so beschriftet: 401 heisst
nicht "vorhanden". Docker Hub antwortet auch auf nicht existierende
Repositories mit 401. Verlaesslich sind nur 200 und 404.
Nebenbei am Versionsarchiv geklaert, dass die Editionsumbenennung erst
mit 2.5 greift:
2.4.0p9 -> check-mk-{raw,enterprise,cloud,managed}-docker-...
2.5.0p13 -> check-mk-{community,pro,ultimate,ultimatemt}-docker-...
Fuer 2.4 ist "--edition enterprise" also richtig.
registry_get_token nimmt jetzt einen frei waehlbaren Scope, damit auch
registry:catalog:* angefordert werden kann.
Gegen Docker Hub verifiziert: findet checkmk/check-mk-raw (2394 Tags)
und checkmk/check-mk-community (534 Tags); gegen registry.checkmk.com
ohne Zugangsdaten sauberer Fehlbericht statt Haenger.
Dokumentiert als Abschnitt 8.8 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der bisherige Ablauf war umstaendlich: Version in den Einstellungen
aendern -> Warnung, dass CMK_IMAGE nicht passt -> "Image holen?" ->
und dann erneut nach Edition, Version UND Zugangsdaten fragen,
obwohl alles schon dastand.
Jetzt ein Menuepunkt "Image beschaffen" (im Image-Menue und unter
Einstellungen -> Checkmk-Image und Zugangsdaten), der in einem
Durchgang durch Edition, Version und Bezugsweg fuehrt:
Registry holen | Tarball herunterladen | vorhandene Datei einspielen
| nichts holen, nur eintragen
Danach stehen CMK_EDITION, CMK_VERSION und CMK_IMAGE gemeinsam richtig.
Nach Zugangsdaten wird nur noch gefragt, wenn sie fuer den gewaehlten
Weg tatsaechlich fehlen - hinterlegte werden verwendet. Wer sie neu
eingibt, kann sie gleich in der .env speichern lassen.
Die drei Werte sind in der Oberflaeche nicht mehr einzeln editierbar
(neuer Feldtyp "info"): ein Klick darauf bietet den Assistenten an.
Damit koennen sie nicht mehr auseinanderlaufen. Auf der Kommandozeile
bleibt alles wie bisher einzeln setzbar; "image pull" und
"image download" haben dafuer neu --remember, das Edition und Version
mit in die .env schreibt.
Das Image-Menue wird dadurch kuerzer: status, versions, holen, test,
use. Die frueheren Einzelpunkte pull/download/load stecken im
Assistenten.
Geprueft: mit hinterlegten Zugangsdaten vier Dialoge und keine
Nachfrage; ohne Zugangsdaten und Bezugsweg Registry wird genau einmal
nach Benutzer, Kennwort und Speichern gefragt; der Weg "nur eintragen"
fragt gar nicht. Alle Menues kehren sauber zurueck.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Anlass war die Frage, wozu CMK_IMAGE neben Edition und Version
ueberhaupt noch noetig ist. Antwort: es ist der einzige Wert, den
compose und die systemd-Units lesen. Edition und Version sind reine
Vorgaben fuer den Downloader und wirken erst ueber "image pull".
Ableiten laesst sich CMK_IMAGE nicht, weil dieselbe Version je nach
Bezugsweg voellig anders heisst - docker.io/..., registry.checkmk.com/...
oder localhost/... bei einem aus dem Tarball geladenen Image.
Daraus folgt eine Falle, die bisher nirgends auffiel: wer Edition auf
"enterprise" stellt, CMK_IMAGE aber auf dem Raw-Image stehen laesst,
betreibt weiter Raw. Genau dieser Zustand kam in der Praxis vor.
image_consistency_check vergleicht deshalb Name und Tag von CMK_IMAGE
mit Edition und Version und meldet Abweichungen - in "image status",
im allgemeinen "status" und im Einstellungsmenue direkt nach dem
Aendern von Edition oder Version, dort gleich mit dem Angebot, das
Image zu holen. Aus Tarballs geladene Images (localhost/...) gelten
als stimmig, solange Name und Tag passen; die Editionsaliase werden
beruecksichtigt, "pro" passt also zu check-mk-enterprise.
Die Beschriftungen in den Einstellungen sagen jetzt, wer was tut:
CMK_IMAGE VERWENDETES Image - setzt "image pull"
CMK_EDITION Edition (nur Vorgabe, wirkt ueber "image pull")
CMK_VERSION Version (nur Vorgabe, wirkt ueber "image pull")
Vier Faelle geprueft: abweichende Edition, abweichende Version,
stimmiger Zustand und aus Tarball geladenes Image.
Dokumentiert als Abschnitt 8.7 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Anlass war der Hinweis auf die tatsaechliche Download-URL. Am echten
Server nachgemessen:
check-mk-raw-docker-2.3.0p9.tar.gz -> 200 (frei)
check-mk-enterprise-docker-2.3.0p9.tar.gz -> 401 (geschuetzt)
check-mk-pro-docker-2.3.0p9.tar.gz -> 404
check-mk-raw-2.3.0p9.docker.tar.gz -> 404
Das bisher zuerst probierte Schema war also schlicht falsch.
Editionsnamen: checkmk.com zeigt nur noch Community/Pro/Ultimate/
Ultimate MT, Dateien und Repositories tragen bis 2.4 weiter
raw/enterprise/cloud/managed. Wer von "Pro" auf check-mk-pro-... schliesst,
bekommt 404. Die Editionsauswahl in der Oberflaeche nennt jetzt beide
Namen, die README hat eine Zuordnungstabelle, und intern werden beide
Varianten durchprobiert.
Neu: "image versions" listet verfuegbare Versionen aus bis zu drei
Quellen - Versionsarchiv von checkmk.com (oeffentlich, ohne
Zugangsdaten), Registry ueber /v2/<repo>/tags/list und das
Verzeichnis-Listing des Downloadportals. Mit --filter, --limit, --all,
--source und --bare fuer die nackte Liste zum Weiterverarbeiten. Am
Ende stehen die fertigen Kommandos zum Uebernehmen.
Das Archiv liefert ausserdem den EXAKTEN Dateinamen je Version und
Edition (data-versions im HTML). Der wird jetzt als erster Kandidat
verwendet, statt ihn zu raten - geprueft fuer raw, enterprise, cloud
und managed auf 2.3.0p9, der erste Kandidat fuer raw liefert am Server
HTTP 200.
In der Oberflaeche: neuer Punkt "Verfuegbare Versionen auflisten", und
beim Holen eines Images muss die Version nicht mehr abgetippt werden -
es erscheint eine Auswahlliste aus dem Archiv.
Gegen Docker Hub verifiziert: 2393 Tags korrekt eingelesen, Filter und
Sortierung stimmen.
Dokumentiert als Abschnitt 8.5 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Test lag nur im Image-Menue, eingetragen werden Benutzer und
Kennwort aber in den Einstellungen - dort hat ihn niemand vermutet.
- Die Einstellungs-Kategorie "Checkmk-Image und Zugangsdaten" hat
jetzt einen Eintrag ">> Zugangsdaten jetzt pruefen".
- Nach dem Aendern eines Zugangsdaten-Feldes fragt das Menue von
sich aus, ob gleich geprueft werden soll.
Gleiche Ueberlegung fuer die beiden anderen Kategorien, in denen eine
Einstellung allein wenig hilft:
- npm -> ">> Proxy Manager: Stand und Admin-Port anzeigen"
- backup -> ">> Backup-Jobs verwalten"
Alle acht Einstellungs-Kategorien einzeln durchlaufen, alle kehren
sauber zurueck. Dokumentiert in Abschnitt 8.4 beider Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Symptom war ein scheinbar haengender Browsh: Logo, "Waiting for Firefox
to connect...", danach schwarz. Ursache ist nicht der Browser - Browsh
laeuft (Strg+Q kehrt ins Menue zurueck) - sondern die Startseite. Auf
http://127.0.0.1:81 antwortet nichts, solange der Proxy-Manager-Container
nicht laeuft, und ein Terminal-Browser zeigt dann keine Fehlerseite,
sondern gar nichts.
cmd_browser prueft die Startseite deshalb vorher und nennt die Ursache
statt stumm zu starten. Unterschieden werden: Proxy Manager
abgeschaltet (NPM_ENABLED=off), Container fehlt, Container gestoppt,
Container laeuft aber antwortet nicht, tote Checkmk-Oberflaeche auf
Port 5000 und externe Adressen. Jeweils mit dem passenden naechsten
Kommando. Ueberspringen mit --no-check; in der Oberflaeche erscheint
dieselbe Diagnose als Dialog mit der Wahl, trotzdem zu starten.
Dabei gefunden und behoben - an drei Stellen dieselbe Falle:
curl gibt mit -w '%{http_code}' im Fehlerfall selbst schon "000" aus.
Das zusaetzliche "|| echo 000" haengte einen zweiten Wert an, sodass
der Vergleich nie zutraf. Betroffen waren die neue Erreichbarkeits-
pruefung sowie http_code() und die Registry-Erreichbarkeit im
Zugangsdaten-Test ("image test") - dort waere ein nicht erreichbarer
Endpunkt als "unerwartete Antwort" statt als "nicht erreichbar"
gemeldet worden. Jetzt wird der Fehler der Zuweisung abgefangen.
Alle vier Diagnosezweige im Terminal durchgespielt, der
Zugangsdaten-Test zur Gegenprobe erneut gegen Docker Hub gelaufen.
Dokumentiert als Abschnitt 11.3 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Anlass: "Backup erstellen" bei fehlendem Container beendete die
ganze TUI mit einer Fehlerzeile in der Shell.
Ursachen waren zwei, beide behoben:
1. die() in GUI-Pfaden. Neue Helfer gui_need_container/gui_need_sites
melden das Problem im Dialog und kehren zurueck. Interaktives wie
Shell und Terminal-Browser laeuft jetzt ueber gui_run_interactive
in einer Subshell mit Weiter-mit-ENTER.
2. "set -e". Schon ein Rueckgabewert != 0 aus einer Menuefunktion
beendete das Skript, weil Menuezweige keine Bedingung sind. 47
"|| return" auf "|| return 0" gestellt und 37 Menuezweige mit
"|| true" abgesichert. Ausserdem schlug ein "clear" ohne TERM fehl
und riss alles mit - drei Aufrufe abgesichert.
Jeder Hauptmenuepunkt wurde einzeln durchlaufen und kehrt sauber zurueck.
Neu: Backup-Jobs
Der Menuepunkt heisst jetzt "Backup erstellen / planen" mit Untermenue
(manuell erstellen, Jobs verwalten, Automatik ein/aus, Zeitplan
anzeigen, Job sofort ausfuehren). Dieselben Jobs liegen auch in den
Einstellungen unter "Backup-Jobs".
Manuelle Backups nehmen weiterhin immer BACKUP_DIR aus der .env.
Jeder Job hat dagegen sein eigenes Zielverzeichnis, seinen eigenen
Zeitplan, eigene Optionen und eine eigene Aufbewahrung, nach der er
sein Zielverzeichnis selbst aufraeumt.
Vollstaendig auch per Parameter:
job list | status | add | set | del | enable | disable
| run | run-all | auto [on|off]
mit --name --sites --cron --dir --keep --no-rrds --no-logs --stop
--disabled.
"job auto on" erzeugt aus den aktiven Jobs /etc/cron.d/checkmk-backup,
eine Zeile je Job mit dessen eigenem Zeitplan. Ohne root werden die
Zeilen ausgegeben statt kommentarlos zu scheitern. Der Schalter steht
als BACKUP_AUTO in der .env, die Jobs in backup-jobs.conf.
Nebenbei behoben: "local f; f=... tmp" fuehrte tmp als Kommando aus,
wodurch job_put und job_remove nichts schrieben.
Dokumentiert als Abschnitt 15.1 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wer die HTTPS-Terminierung bereits mit einem NGINX auf einem anderen
System loest, braucht den Container nicht. NPM_ENABLED=off sorgt dafuer,
dass er gar nicht erst angelegt wird: compose bekommt dann nur noch den
Checkmk-Dienst genannt.
NPM_ENABLED=off -> compose up -d checkmk
NPM_ENABLED=on -> compose up -d checkmk nginx-proxy-manager
Das gilt auch fuer "pull" - abgeschaltet wird nicht einmal das Image
geladen. Beim Abschalten wird ein vorhandener Container gestoppt UND
entfernt, damit ihn weder eine Restart-Regel noch ein Reboot wieder
hochzieht.
Die Daten bleiben in jedem Fall erhalten: Konfiguration, Datenbank und
Zertifikate liegen in den Bind-Mounts unter ./data/npm, nicht im
Container. Vor dem Abschalten werden beide Verzeichnisse mit Groesse
angezeigt; beim Einschalten wird die vorhandene Konfiguration samt
Zertifikaten unveraendert wieder aufgenommen.
Bedienung:
- Menuepunkt "Proxy Manager betreiben" unter Einstellungen -> npm
- cmk-manage.sh npm-enable [on|off] (ohne Argument: Stand anzeigen)
- config set NPM_ENABLED off
"npm-enable off" gibt gleich die noetige Firewall-Regel und eine
Beispielkonfiguration fuer den externen NGINX aus - Port 5000 muss dann
fuer dessen IP erreichbar sein. Der Agent-Receiver auf Port 8000 ist
davon nicht betroffen, den sprechen die Agents ohnehin direkt an.
Damit der Schalter auch nach einem Reboot gilt, ruft die von install.sh
erzeugte systemd-Unit jetzt cmk-manage.sh auf statt compose direkt.
Bei Quadlet wird die Unit nginx-proxy-manager.container uebersprungen
und entfernt, solange der Schalter auf "off" steht.
Verifiziert mit einem compose-Stub, dass in beiden Schalterstellungen
genau die erwarteten Dienste uebergeben werden, sowie ueber das Menue.
Dokumentiert als Abschnitt 12.4 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Neues Unterkommando "image test" und ein Menuepunkt im Image-Menue.
Geprueft wird in Sekunden, statt erst beim mehrere Gigabyte grossen
Download zu scheitern:
Registry - Erreichbarkeit, Anmeldung und ob das Konto ueberhaupt
Zugriff auf das Repository hat. Umgesetzt ueber den
Docker-Registry-v2-Ablauf: WWW-Authenticate auswerten,
Token beim genannten realm holen, damit /tags/list
abfragen. Dadurch wird "Anmeldung stimmt, aber keine
Berechtigung" von "Anmeldung falsch" unterschieden.
Downloads - HTTP-Basic gegen die Tarball-URLs. HTTP 404 ohne 401
bedeutet: Zugangsdaten sind richtig, nur Version oder
Dateiname passen nicht - das wird auch so gemeldet.
Es wird nichts gespeichert: die Zugangsdaten gehen per temporaerer
.netrc an curl und stehen damit nicht in der Prozessliste (geprueft).
Fehlt curl, weicht die Registry-Pruefung auf podman/docker login in
eine Wegwerfdatei aus (--authfile bzw. DOCKER_CONFIG), die gespeicherte
Anmeldung des Systems bleibt unberuehrt.
Beim Testen gegen die echten Dienste gefunden und behoben:
- "docker.io" ist nicht der API-Host der Registry; angesprochen wird
registry-1.docker.io. Vorher lieferte /v2/ nur eine 302.
- Die Schlussauswertung zaehlte auch Pruefungen als bestanden, die
wegen --only gar nicht gelaufen waren.
Verifiziert gegen die echten Endpunkte: Docker Hub anonym inkl.
Tag-Liste von checkmk/check-mk-raw (HTTP 200), registry.checkmk.com
und download.checkmk.com jeweils mit falschen Zugangsdaten (HTTP 401,
korrekt als abgelehnt gemeldet).
Dokumentiert als Abschnitt 8.4 in beiden Sprachfassungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der NGINX Proxy Manager laesst sich jetzt auch dann einrichten, wenn
seine Oberflaeche auf Port 81 aus dem Netz nicht erreichbar ist.
browser
Terminal-Browser in einem eigenen Container, gestartet mit
"run --rm -it --network host" - damit erreicht er 127.0.0.1 genau wie
der Proxy Manager selbst. Engines: browsh und carbonyl (rendern einen
echten Browser und koennen JavaScript, das die Adminoberflaeche
benoetigt) sowie links2/w3m/lynx und eine Shell mit curl/jq/openssl
fuer Diagnose. Werkzeug-Image unter containers/browser/.
Bewusst kein Compose-Dienst: ein Browser ist interaktiv, und
podman-compose behandelt Profile unzuverlaessig.
npm-port
Port der Adminoberflaeche frei waehlbar. Das Image bindet fest auf 81
("listen 81 default;"), eine Umgebungsvariable dafuer gibt es in 2.15.1
noch nicht. Ein abweichender Port wird deshalb ueber den nginx-
Erweiterungspunkt /data/nginx/custom/http.conf bereitgestellt. Nur der
markierte Block wird verwaltet, eigene Eintraege bleiben erhalten.
Port 81 lauscht weiterhin lokal und ist per Firewall zu sperren.
config
Alle Werte der .env sind menuegefuehrt bzw. ueber config get/set
aenderbar - der Editor bleibt als Menuepunkt erhalten. Geprueft wird
beim Setzen: Portbereich und Kollisionen, Site-Namen, vollqualifizierte
Image-Namen, installierte Runtime, Zahlen- und URL-Felder.
Fehlerbehebungen
- ui_*-Funktionen initialisieren sich selbst; eine von aussen gesetzte
UI_BIN fuehrte unter "set -u" zum Abbruch.
- Eine nicht installierte CMK_RUNTIME machte das gesamte Werkzeug
unbenutzbar, auch den Aufruf zum Korrigieren. Jetzt wird gewarnt und
selbst gesucht.
- env_set setzt Werte mit Leerzeichen in Anfuehrungszeichen und laesst
optional leere Werte zu.
README auf 22 Kapitel erweitert: Kapitel 11 (Terminal-Browser inkl.
NPM-API als Fallback fuer den Zertifikats-Upload), Kapitel 12 (Admin-Port)
und Abschnitt 14.3 (Einstellungsmenue).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>