RDP-Zugangsdaten im Credential-Vault
Credential-Vault
Speicherung
Bei credential_mode='user_only' wird nur username_encrypted gesetzt (Passwort gibt der User lokal ein). Bei credential_mode='full' werden Username und Passwort verschlüsselt. Die Encryption läuft über src/utils/crypto.js:encrypt (AES-256-GCM mit dem Server-Master-Key).
E2EE-Abruf durch Desktop-Client
Credentials werden NIE in Klartext über die API ausgeliefert. Der Fluss:
- Client generiert ephemeren ECDH-Keypair (P-256), sendet Public-Key mit dem Connection-Request (
GET /api/v1/client/rdp/:id/connection-info?ecdhPublicKey=<base64>). - Server entschlüsselt die Creds aus der DB (AES-GCM mit Master-Key, serverseitig).
- Server ruft
ecdhEncrypt(credentialsJson, ecdhPublicKey)auf:- Generiert ephemeren Server-ECDH-Keypair.
- Derivated AES-256-Key via HKDF-SHA256 (
salt = clientPub || serverPub,info = 'gatecontrol-rdp-e2ee-v1'). - AES-256-GCM-Verschlüsselung der Creds-JSON.
- Response enthält
{data, iv, authTag, serverPublicKey}. - Client leitet denselben AES-Key aus seinem Privatkey + dem erhaltenen Server-Public ab und entschlüsselt.
Nuance: Android/Java-Clients liefern Public-Keys im X.509/SPKI-Format (91 Bytes), Node-Clients liefern raw uncompressed (65 Bytes). ecdhEncrypt erkennt das und nutzt beim HKDF-Salt dasselbe Encoding, das der Client verwendet — sonst wäre der abgeleitete Key nicht symmetrisch.
Der RSA-Pubkey-Endpoint GET /api/v1/rdp/pubkey ist ein separater Legacy-Pfad (kein ECDH, sondern RSA-OAEP). Genutzt für ältere Client-Flows wo der Client statt Creds-Download eine Server-Seite verschlüsselte Blob-Operation macht.
Credential-Rotation
Wenn credential_rotation_enabled=1, gilt der Zyklus credential_rotation_days. GET /api/v1/rdp/rotation/pending listet Routes, deren credential_rotation_last + credential_rotation_days abgelaufen ist:
WHERE credential_rotation_enabled = 1 AND (
credential_rotation_last IS NULL
OR datetime(credential_rotation_last, '+' || credential_rotation_days || ' days') < datetime('now')
)
Der Admin setzt dann über PATCH /api/v1/rdp/:id/credentials neue Werte und bestätigt via POST /api/v1/rdp/:id/rotation/ack — das setzt nur credential_rotation_last = datetime('now') und markiert den Rotation-Cycle als abgeschlossen.
Bei Rotation des Server-Master-Keys (wenn der AES-GCM-Key wechselt) werden alle verschlüsselten Credentials ungültig. Der Admin sieht sie als "rotation pending" und muss neu eingeben.