Add remote_access plugin 1.4.4

This commit is contained in:
dbildhauer committed 2026-08-16 15:02:59 +02:00
commit 5e9989ecda
7 files changed
+479

No files matched your search

+29
View File
@@ -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
+279
View File
@@ -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.
Binary file not shown.
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="1" y="1" width="30" height="30" rx="5" fill="#0b0f12" stroke="#56616a" stroke-width="1"/>
<rect x="7" y="7" width="18" height="13" rx="1" fill="none" stroke="#f4f6f8" stroke-width="2.5"/>
<path d="M13 25 H19 M16 20 V25 M11 26 H21" fill="none" stroke="#f4f6f8" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 380 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect x="1" y="1" width="30" height="30" rx="5" fill="#0b0f12" stroke="#56616a" stroke-width="1"/>
<path d="M8 9.5 L14.5 16 L8 22.5" fill="none" stroke="#f4f6f8" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17 22.5 H24" fill="none" stroke="#f4f6f8" stroke-width="3" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 389 B

@@ -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(
'<a href="{href}" target="_top" title="{title}" '
'style="display:inline-block;width:20px;height:20px;'
'vertical-align:middle;background-image:url(\'{icon}\');'
'background-repeat:no-repeat;background-position:center;'
'background-size:20px 20px;text-decoration:none;"></a>'
.format(
href=href.replace("&", "&amp;").replace('"', "&quot;"),
title=title.replace("&", "&amp;").replace('"', "&quot;"),
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,
}
@@ -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