wvdsremote — Inventar-/Connect-DSL
Das Plugin wvdsremote stellt eine deklarative Auszeichnung bereit, mit der ein Host-Inventar vollständig im Wiki-Text definiert und als einheitliche, themebare Connect-Oberfläche gerendert wird. Zugangsdaten stehen nie im Markup — ein Host referenziert ein Vault-Secret ausschließlich per ID; der Wert liegt verschlüsselt im Plugin wkvault und wird nur serverseitig entschlüsselt.
Der eigentliche Verbindungsaufbau (Browser-Gateway bzw. nativer Client) folgt in einer späteren Ausbaustufe. Aktuell rendert die Oberfläche die Karten samt korrektem Zustand; die Buttons sind noch inaktiv.
Zwei Eintrittspunkte
| Form | Zweck |
|---|---|
Container <wk:remote> … </wk:remote> | Definiert Gruppen und Hosts (block-level). |
Inline {{wk:remote host="id"}} | Bettet ein einzelnes Connect-Widget für einen bereits registrierten Host ein. |
Der Container ist die Autoren-Quelle: Beim Speichern der Seite werden seine Hosts in den
Inventar-Bestand übernommen; das Inline-Widget löst darüber die host-ID auf.
Knoten und Attribute (Schema v1)
<group>
| Attribut | Pflicht | Default | Bedeutung |
|---|---|---|---|
label | nein | „(ohne Gruppe)„ | Sektionsüberschrift |
icon | nein | – | Font-Awesome-Klasse, Muster fa-… |
collapsed | nein | false | Sektion initial eingeklappt |
Kinder: <host>.
<host>
| Attribut | Pflicht | Default | Bedeutung |
|---|---|---|---|
id | ja | – | Stabile Host-Kennung (Slug a–z 0–9 -); zugleich Inline-Referenz |
address | ja | – | Zieladresse (Hostname/IP) |
protocol | ja | – | rdp ssh vnc |
secret | nein¹ | – | ID des Vault-Secrets (nur Referenz) |
name | nein | = id | Anzeigename |
port | nein | 3389/22/5900 | 1–65535 (protokollabhängiger Default) |
tier | nein | protected | auto protected (Vault-Tier) |
note | nein | – | Verlinkte Wiki-Seite (Karten-Titel wird Link) |
icon | nein | protokollabhängig | Font-Awesome-Klasse |
security | nein | – | nur rdp: any|nla|tls|rdp|vmconnect |
ignore-cert | nein | true | Zertifikatsprüfung aus (bewusster Lab-Default) |
¹ secret ist formal optional; ein <access> ohne hinterlegtes Secret rendert jedoch als
deaktivierter Button.
Kinder: <access>, <link>, <tag>.
<access>
| Attribut | Pflicht | Bedeutung |
|---|---|---|
mode | ja | browser agent rdp-launch ssh-launch clipboard |
label | nein | Button-Beschriftung (Default je Modus) |
rdp-launch nur bei protocol=„rdp“, ssh-launch nur bei protocol=„ssh“; agent nur bei
protocol=„rdp“ oder „ssh“ (VNC läuft ausschließlich über das Browser-Gateway); sonst Fehlbutton.
<link>
| Attribut | Pflicht | Bedeutung |
|---|---|---|
page | ja | Ziel-Wiki-Seite (intern-only) |
label | nein | Linktext |
<tag>
Text-Knoten ohne Attribut: eine Klassifizierung, gerendert als Chip.
Zustände einer Host-Karte
| Zustand | Auslöser | Darstellung |
|---|---|---|
| unlocked | Auto-Tier bzw. Vault entsperrt und lesbar | Access-Buttons aktiv |
| locked | protected und Vault gesperrt | Button „Vault entsperren“ |
| secret-missing | kein/unbekanntes Secret | Button deaktiviert („kein Secret hinterlegt„) |
| no-permission | keine Leseberechtigung | Host ausgeblendet oder „kein Zugriff“ (Konfig hide_denied) |
| error-config | Pflichtwert ungültig / browser ohne Gateway | Fehlerkarte mit konkreter Ursache |
| empty | keine sichtbaren Hosts | Empty-State-Text |
Sicherheit
- Kein
password/pass/pwd-Attribut existiert im Schema — solche Attribute werden als
Warnung markiert und nie verarbeitet.
- Der Innenblock wird gehärtet geparst: kein DTD/Entity (DOCTYPE wird abgelehnt), kein
Netzwerkzugriff — XML-External-Entity-Angriffe (XXE) sind ausgeschlossen.
- Alle Ausgaben sind HTML-escaped; Notiz-Links zeigen ausschließlich intern; Secret-Werte und
Secret-IDs erscheinen nie im gerenderten HTML.
Vollständiges Beispiel
<wk:remote>
<group label="Produktion" icon="fa-server">
<host id="prod-db01" name="DB Primary" address="198.51.100.10" protocol="rdp"
port="3389" secret="prod-db01-admin" tier="protected"
security="nla" note="de:wiki:runbooks:db-failover">
<access mode="browser" label="Browser (RDP)"/>
<access mode="rdp-launch" label="mstsc"/>
<link page="de:wiki:runbooks:db-failover" label="Failover-Runbook"/>
<tag>kritis</tag><tag>db</tag>
</host>
<host id="prod-ssh01" name="Jump Host" address="198.51.100.20" protocol="ssh"
secret="prod-ssh01-ops" tier="auto">
<access mode="browser"/>
<access mode="ssh-launch"/>
</host>
</group>
</wk:remote>
Inline in einem Runbook:
{{wk:remote host="prod-db01"}}
Verbinden
Ein aktiver Access-Button ruft den autorisierten connect-Endpoint
(?call=wvdsremote_connect): CSRF-Prüfung, @wvdse-Mitgliedschaft und ACL werden geprüft, das
Secret wird serverseitig aus dem Vault entschlüsselt.
- Browser (
mode=browser): Der Server baut ein verschlüsseltes, kurzlebiges guacamole-lite-Token (AES-256-CBC); der Browser öffnet damit eine Guacamole-Sitzung über das Gateway. Das Klartext-Passwort verlässt den Server nie Richtung Browser — nur das opake Token wandert durch. - Agent (
mode=agent): Der Server baut ein authentifiziertes Token (AES-256-GCM); der Browser POSTet es an den lokalen Injektions-Agent (http://127.0.0.1:<port>/launch— localhost ist „potentially-trustworthy„, daher auch von einer https-Seite erreichbar). Der Agent entschlüsselt das Token, prüft TTL + Einmal-nonceund injiziert die Zugangsdaten nativ (RDP viacmdkey→mstscmit anschließendem Löschen des Credentials; SSH via PuTTY). Kein Klartext im Browser — sichtbar als Security-Badge „injiziert (nativ, lokaler Agent)“. Voraussetzung: laufenderwvdsragentund gesetzteagent_url/agent_shared_key_path. - Native (
rdp-launch/ssh-launch/clipboard): Der Server liefert eine.rdp-Datei bzw.ssh://-URI (ohne eingebettetes Passwort) plus das Passwort für die Zwischenablage (Auto-Clear nach 30 s). Dieser Pfad exponiert das Passwort bewusst an den Client — sichtbar als Security-Badge „Zwischenablage (Klartext)„.
Voraussetzung (Betrieb): das Gateway (guacamole-lite + guacd via Docker, deploy/gateway/), das
guacamole-common-js-Bundle unter vendor/ sowie die Konfigurationswerte guac_base_url
(wss://…) und guac_shared_key_path (off-stick).
Sicherheitsgrenzen (bewusst): Das guacamole-lite-Token ist AES-256-CBC ohne MAC (Formatzwang) —
kompensiert durch kurze TTL, Server-seitigen nonce (das Gateway weist abgelaufene/wiederholte Token
ab) und den erzwungenen JSON-Parse. Das Agent-Token ist demgegenüber AES-256-GCM (authentifiziert),
einmalig (nonce) und kurz-TTL; der Agent bindet ausschließlich 127.0.0.1. Trust-Grenze
Agent-Pfad: der lokale wvdsragent sieht zwangsläufig Klartext (nötig für die Injektion) — die
Bedienmaschine ist der Vertrauensanker; im Produktivbetrieb ist das Agent-Binary Authenticode-zu-signieren.
Der clipboard/launch-Pfad legt das Passwort bewusst kurzzeitig in die Zwischenablage. Die Shared-Keys
(Gateway wie Agent) liegen roh (nicht DPAPI) off-stick, da der Node- bzw. .NET-Prozess sie lesen muss.
Hinweise
- Host-IDs sollten global eindeutig sein; definieren zwei Seiten dieselbe ID, gewinnt die zuletzt
gespeicherte Seite den Inventar-Eintrag.
- Das Inline-Widget setzt voraus, dass der Host zuvor über einen Container gespeichert wurde.
- Verschachtelte Container sind in Schema v1 nicht vorgesehen.
Versionierung
Attribut-Set und Knoten sind Schema v1. Neue Knoten/Attribute werden nur additiv ergänzt; brechende Änderungen erhöhen die Schema-Version. Diese Seite ist die maßgebliche Referenz.