commit 745719bb236d3abd9e62c5992c94f1f9e929b889 Author: Domenik Bildhauer Date: Sun Aug 16 15:02:59 2026 +0200 Add remote_access plugin 1.4.4 diff --git a/remote_access/CHANGELOG.md b/remote_access/CHANGELOG.md new file mode 100644 index 0000000..1d00984 --- /dev/null +++ b/remote_access/CHANGELOG.md @@ -0,0 +1,29 @@ +# Changelog + +## 1.4.4 - 2026-08-16 + +### Added +- Direkter SSH-Zugriff aus `Setup → Hosts` +- Direkter RDP-Zugriff aus `Setup → Hosts` +- SSH-Icon in Monitoring- und Service-Problemansichten +- RDP-Integration für Monitoring-Ansichten +- Automatische Betriebssystemerkennung über `cmk/os_family` +- Verwendung der von Checkmk ermittelten Host-IP +- Eigene SVG-Icons für SSH und RDP +- `Open SSH ↗` und `Open RDP ↗` Direktlinks + +### Changed +- Remote-Verbindungen verwenden jetzt bevorzugt `host_address` +- Monitoring-Integration wurde auf Checkmk 2.5 angepasst +- Autor des MKP ist `Domenik Bildhauer` + +### Tested +- Checkmk Community 2.5 +- Linux / Ubuntu → SSH +- Windows 11 → RDP +- PuTTY als SSH-Client unter Windows +- `rdpadmin://` Protocol Handler +- SSH-Icon in einer Monitoring-Service-Problemansicht + +### Known / To Test +- RDP-Icon aus einer Monitoring-Service-Problemansicht muss noch abschließend getestet werden \ No newline at end of file diff --git a/remote_access/README.md b/remote_access/README.md new file mode 100644 index 0000000..8320d45 --- /dev/null +++ b/remote_access/README.md @@ -0,0 +1,279 @@ +# Checkmk Remote Access + +Direkter SSH- und RDP-Zugriff auf überwachte Hosts aus der Checkmk-Weboberfläche. + +**Aktuelle Version:** 1.4.4 +**Autor:** Domenik Bildhauer +**Zielplattform:** Checkmk Community 2.5 + +## Funktionen + +Das Plugin erweitert Checkmk um Remote-Access-Funktionen für Linux-/Unix- und Windows-Hosts. + +### Linux / Unix + +Für Linux- und Unix-Systeme wird automatisch ein SSH-Zugriff angeboten. + +Beispiel: + +```text +ssh://192.168.188.125 +``` + +Unterstützte Betriebssystemfamilien: + +- Linux +- Unix +- FreeBSD +- OpenBSD +- Solaris +- AIX + +### Windows + +Für Windows-Systeme wird ein RDP-Zugriff angeboten. + +Beispiel: + +```text +rdpadmin://192.168.188.122 +``` + +`rdpadmin://` verwendet einen eigenen Windows Protocol Handler, der anschließend den Microsoft Remote Desktop Client (`mstsc.exe`) startet. + +## Checkmk-Integration + +### Setup → Hosts + +In der Checkmk-Hostverwaltung werden abhängig vom Betriebssystem zusätzliche Remote-Access-Aktionen angezeigt: + +- SSH-Icon für Linux-/Unix-Systeme +- RDP-Icon für Windows-Systeme +- `Open SSH ↗` +- `Open RDP ↗` + +### Monitoring + +Das Plugin integriert SSH und RDP zusätzlich in die Icons-Spalte der Checkmk Monitoring Views. + +Bei einem Linux-Host kann dadurch beispielsweise direkt aus einer Service-Problemansicht eine SSH-Verbindung gestartet werden. + +## Automatische Betriebssystemerkennung + +Die Entscheidung zwischen SSH und RDP erfolgt anhand der von Checkmk ermittelten Host-Labels. + +Beispiel Linux: + +```text +cmk/os_family: linux +cmk/os_platform: ubuntu +cmk/os_name: Ubuntu +cmk/os_version: 22.04 +``` + +Beispiel Windows: + +```text +cmk/os_family: windows +``` + +Als Ziel für die Verbindung verwendet das Plugin bevorzugt `host_address`. + +Beispiel: + +```text +Host: +test-ssh.bildhauerd.com + +Adresse: +192.168.188.125 + +OS-Familie: +linux +``` + +Daraus entsteht: + +```text +ssh://192.168.188.125 +``` + +## Installation + +Das fertige MKP befindet sich unter: + +```text +packages/remote_access-1.4.4.mkp +``` + +Das Paket auf den Checkmk-Server kopieren und als Site-Benutzer installieren: + +```bash +mkp add /pfad/zu/remote_access-1.4.4.mkp +mkp enable remote_access 1.4.4 +``` + +Danach den Apache der Checkmk-Site neu starten: + +```bash +omd restart apache +``` + +Installation kontrollieren: + +```bash +mkp list +``` + +## Upgrade + +Eine vorherige Version zunächst deaktivieren, beispielsweise: + +```bash +mkp disable remote_access 1.4.3 +``` + +Danach die neue Version installieren und aktivieren: + +```bash +mkp add /pfad/zu/remote_access-1.4.4.mkp +mkp enable remote_access 1.4.4 +omd restart apache +``` + +Anschließend im Browser einen Hard-Reload durchführen. + +## Source-Code + +Der Source-Code befindet sich unter: + +```text +src/local/share/check_mk/ +``` + +Wichtige Dateien: + +```text +src/local/share/check_mk/web/plugins/wato/remote_access_setup.py + +src/local/share/check_mk/web/plugins/views/remote_access_views.py + +src/local/share/check_mk/web/htdocs/images/icons/remote_access_ssh.svg + +src/local/share/check_mk/web/htdocs/images/icons/remote_access_rdp.svg +``` + +### remote_access_setup.py + +Integration der Remote-Access-Funktionen in: + +```text +Setup → Hosts +``` + +### remote_access_views.py + +Integration der Remote-Access-Icons in die Checkmk Monitoring Views. + +Die Registrierung erfolgt über Checkmks Legacy-Web-Plugin-Schnittstelle: + +```python +multisite_icons_and_actions +``` + +## Windows SSH + +Damit ein Klick auf `ssh://` unter Windows funktioniert, muss das Protokoll einem geeigneten SSH-Client zugeordnet sein. + +Getestet wurde die Verbindung mit PuTTY. + +Ein direkter Aufruf funktioniert beispielsweise mit: + +```text +putty.exe 192.168.188.125 +``` + +## Windows RDP + +Für RDP wird das benutzerdefinierte Protokoll + +```text +rdpadmin:// +``` + +verwendet. + +Beispiel: + +```text +rdpadmin://192.168.188.122 +``` + +Der auf dem Windows-Client installierte Protocol Handler startet anschließend `mstsc.exe`. + +Dadurch müssen keine `.rdp`-Dateien heruntergeladen werden. + +## Fehlerdiagnose + +Checkmk Web-Log: + +```bash +tail -n 50 ~/var/log/web.log +``` + +Log vor einem Test leeren: + +```bash +: > ~/var/log/web.log +omd restart apache +``` + +Danach die betreffende Seite neu laden und prüfen: + +```bash +cat ~/var/log/web.log +``` + +## Status 1.4.4 + +| Funktion | Status | +|---|---| +| Linux-Erkennung | ✅ Getestet | +| Windows-Erkennung | ✅ Getestet | +| SSH unter Setup → Hosts | ✅ Getestet | +| RDP unter Setup → Hosts | ✅ Getestet | +| SSH aus Monitoring-Ansicht | ✅ Getestet | +| Eigene SSH-/RDP-Icons | ✅ | +| Verwendung der Host-IP | ✅ | +| RDP aus Monitoring-Problemansicht | ⏳ Noch abschließend zu testen | + +## Repository-Struktur + +```text +remote_access/ +├── README.md +├── CHANGELOG.md +├── packages/ +│ └── remote_access-1.4.4.mkp +└── src/ + └── local/ + └── share/ + └── check_mk/ + └── web/ + ├── plugins/ + │ ├── wato/ + │ │ └── remote_access_setup.py + │ └── views/ + │ └── remote_access_views.py + └── htdocs/ + └── images/ + └── icons/ + ├── remote_access_ssh.svg + └── remote_access_rdp.svg +``` + +## Lizenz + +Dieses Plugin wurde für den Einsatz mit Checkmk Community entwickelt. + +Checkmk ist eine Marke der Checkmk GmbH. \ No newline at end of file diff --git a/remote_access/packages/remote_access-1.4.4.mkp b/remote_access/packages/remote_access-1.4.4.mkp new file mode 100644 index 0000000..1780256 Binary files /dev/null and b/remote_access/packages/remote_access-1.4.4.mkp differ diff --git a/remote_access/src/local/share/check_mk/web/htdocs/images/icons/remote_access_rdp.svg b/remote_access/src/local/share/check_mk/web/htdocs/images/icons/remote_access_rdp.svg new file mode 100644 index 0000000..687dd48 --- /dev/null +++ b/remote_access/src/local/share/check_mk/web/htdocs/images/icons/remote_access_rdp.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/remote_access/src/local/share/check_mk/web/htdocs/images/icons/remote_access_ssh.svg b/remote_access/src/local/share/check_mk/web/htdocs/images/icons/remote_access_ssh.svg new file mode 100644 index 0000000..f6729ac --- /dev/null +++ b/remote_access/src/local/share/check_mk/web/htdocs/images/icons/remote_access_ssh.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/remote_access/src/local/share/check_mk/web/plugins/views/remote_access_views.py b/remote_access/src/local/share/check_mk/web/plugins/views/remote_access_views.py new file mode 100644 index 0000000..3ea1c14 --- /dev/null +++ b/remote_access/src/local/share/check_mk/web/plugins/views/remote_access_views.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +from urllib.parse import quote + +from cmk.gui.utils.html import HTML + +_SSH_ICON = 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%0A%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2230%22%20height%3D%2230%22%20rx%3D%225%22%20fill%3D%22%230b0f12%22%20stroke%3D%22%2356616a%22%20stroke-width%3D%221%22%2F%3E%0A%3Cpath%20d%3D%22M8%209.5%20L14.5%2016%20L8%2022.5%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%223%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M17%2022.5%20H24%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%223%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3C%2Fsvg%3E' +_RDP_ICON = 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%0A%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2230%22%20height%3D%2230%22%20rx%3D%225%22%20fill%3D%22%230b0f12%22%20stroke%3D%22%2356616a%22%20stroke-width%3D%221%22%2F%3E%0A%3Crect%20x%3D%227%22%20y%3D%227%22%20width%3D%2218%22%20height%3D%2213%22%20rx%3D%221%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%222.5%22%2F%3E%0A%3Cpath%20d%3D%22M13%2025%20H19%20M16%2020%20V25%20M11%2026%20H21%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%222.5%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3C%2Fsvg%3E' + + +def _remote_family(row): + labels = row.get("host_labels") or {} + if isinstance(labels, dict): + return str(labels.get("cmk/os_family", "")).lower() + return "" + + +def _remote_target(row): + return str(row.get("host_address") or row.get("host_name") or "") + + +def _html_link(href, title, icon_uri): + return HTML.without_escaping( + '' + .format( + href=href.replace("&", "&").replace('"', """), + title=title.replace("&", "&").replace('"', """), + icon=icon_uri.replace('"', "%22"), + ) + ) + + +def _paint_remote_ssh(what, row, tags, custom_vars, user_permissions, icon_config): + if what not in ("host", "service"): + return None + if _remote_family(row) not in {"linux","unix","freebsd","openbsd","solaris","aix"}: + return None + target = _remote_target(row) + if not target: + return None + href = "ssh://%s" % quote(target, safe=":.-[]") + return _html_link(href, "SSH-Verbindung zu %s öffnen" % target, _SSH_ICON) + + +def _paint_remote_rdp(what, row, tags, custom_vars, user_permissions, icon_config): + if what not in ("host", "service"): + return None + if _remote_family(row) != "windows": + return None + target = _remote_target(row) + if not target: + return None + href = "rdpadmin://%s" % quote(target, safe=":.-[]") + return _html_link(href, "RDP-Verbindung zu %s öffnen" % target, _RDP_ICON) + + +multisite_icons_and_actions["remote_access_ssh"] = { + "title": "Remote Access SSH", + "paint": _paint_remote_ssh, + "columns": [], + "host_columns": ["labels", "address"], + "service_columns": [], + "sort_index": 25, + "toplevel": True, +} + +multisite_icons_and_actions["remote_access_rdp"] = { + "title": "Remote Access RDP", + "paint": _paint_remote_rdp, + "columns": [], + "host_columns": ["labels", "address"], + "service_columns": [], + "sort_index": 26, + "toplevel": True, +} diff --git a/remote_access/src/local/share/check_mk/web/plugins/wato/remote_access_setup.py b/remote_access/src/local/share/check_mk/web/plugins/wato/remote_access_setup.py new file mode 100644 index 0000000..5d1f60d --- /dev/null +++ b/remote_access/src/local/share/check_mk/web/plugins/wato/remote_access_setup.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +from urllib.parse import quote +from cmk.gui.htmllib.html import html +from cmk.gui.wato.pages import folders as _folders + +ModeFolder = _folders.ModeFolder +_SSH_ICON = 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%0A%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2230%22%20height%3D%2230%22%20rx%3D%225%22%20fill%3D%22%230b0f12%22%20stroke%3D%22%2356616a%22%20stroke-width%3D%221%22%2F%3E%0A%3Cpath%20d%3D%22M8%209.5%20L14.5%2016%20L8%2022.5%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%223%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M17%2022.5%20H24%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%223%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3C%2Fsvg%3E' +_RDP_ICON = 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%0A%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2230%22%20height%3D%2230%22%20rx%3D%225%22%20fill%3D%22%230b0f12%22%20stroke%3D%22%2356616a%22%20stroke-width%3D%221%22%2F%3E%0A%3Crect%20x%3D%227%22%20y%3D%227%22%20width%3D%2218%22%20height%3D%2213%22%20rx%3D%221%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%222.5%22%2F%3E%0A%3Cpath%20d%3D%22M13%2025%20H19%20M16%2020%20V25%20M11%2026%20H21%22%20fill%3D%22none%22%20stroke%3D%22%23f4f6f8%22%20stroke-width%3D%222.5%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3C%2Fsvg%3E' + +if not getattr(ModeFolder, "_remote_access_144_patched", False): + _original_show_host_actions = ModeFolder._show_host_actions + + def _remote_access_data_by_host(self): + cached = getattr(self, "_remote_access_data_cache", None) + if cached is not None: + return cached + result = {} + try: + rows = _folders.Query( + [_folders.Hosts.name, _folders.Hosts.labels, _folders.Hosts.address] + ).fetchall(sites=_folders.sites.live()) + for row in rows: + if row.get("name") is not None: + result[str(row["name"])] = { + "labels": row.get("labels") or {}, + "address": row.get("address") or "", + } + except Exception: + result = {} + self._remote_access_data_cache = result + return result + + def _remote_access_icon(href, title, icon_uri): + html.a( + " ", + href=href, + target="_top", + title=title, + style=( + "display:inline-block;width:22px;height:22px;margin-left:4px;" + "vertical-align:middle;background-image:url('" + icon_uri + "');" + "background-repeat:no-repeat;background-position:center;" + "background-size:22px 22px;text-decoration:none;" + ), + ) + + def _remote_access_button(href, label, title): + html.a( + label + " ↗", + href=href, + target="_top", + title=title, + style=( + "display:inline-block;margin-left:4px;padding:2px 7px;" + "border:1px solid #3f4d56;border-radius:4px;background:#11171b;" + "color:#20e6a5;font-weight:600;font-size:12px;line-height:18px;" + "text-decoration:none;vertical-align:middle;white-space:nowrap;" + ), + ) + + def _remote_access_show_host_actions(self, host): + _original_show_host_actions(self, host) + hostname = str(host.name()) + data = _remote_access_data_by_host(self).get(hostname, {}) + family = str(data.get("labels", {}).get("cmk/os_family", "")).lower() + target = str(data.get("address") or hostname) + if not target: + return + + encoded = quote(target, safe=":.-[]") + if family == "windows": + href = "rdpadmin://" + encoded + title = "RDP-Verbindung zu %s öffnen" % target + _remote_access_icon(href, title, _RDP_ICON) + _remote_access_button(href, "Open RDP", title) + elif family in {"linux","unix","freebsd","openbsd","solaris","aix"}: + href = "ssh://" + encoded + title = "SSH-Verbindung zu %s öffnen" % target + _remote_access_icon(href, title, _SSH_ICON) + _remote_access_button(href, "Open SSH", title) + + ModeFolder._show_host_actions = _remote_access_show_host_actions + ModeFolder._remote_access_144_patched = True