Version_0.5 xfreerdp - Skript
RDP-Launcher mit xfreerdp3
einrichten– Schnellstart & nutzenNutzung
Diese Anleitung führt dich vom leeren Linux-System bis zur laufenden RDP-Sitzung. Sie ist so geschrieben, dass du keinerlei Erfahrung mit Linux, Python oder virtuellen Umgebungen (venv) benötigst.
einen sauberen Projekt-Ordnereine eigene Python-UmgebungSchritt (damit du nichts „kaputt“ installierst)das fertige Skriptwennxfreerdp_launcher.pyeine Beispiel-config.jsonvenvmitschonzwei Standorteneinen Eintrag im System-Keyring, damit Passwörter sicher gespeichert werden
1 Grundvoraussetzungen prüfen
existiert)
Linux-ShellTerminal öffnen:ffnen
Ctrl+Alt+T- In
oderProjektordner„Terminal“wechselncd
aus~/rdp-launcher - Virtuelle Umgebung aktivieren
source .venv/bin/activate
(Bei jedem neuen Terminal nötig!) - Launcher starten
./xfreerdp_launcher.py
(Falls nicht ausführbar:chmod +x xfreerdp_launcher.py
nur 1x nötig)
Oder per Python:python3 xfreerdp_launcher.py
- Menü
wählen.nutzen, Standort & Server auswählen - Erste Passwortabfrage – wird sicher im Keyring gespeichert
Diese Anleitung richtet sich an absolute Einsteiger und führt dich in wenigen Schritten zur ersten RDP-Sitzung.
Bitte arbeite jeden Schritt der Reihe nach ab.
1. Voraussetzungen prüfen (nur 1x nötig)
- Python &
Pip vorhanden?Pip:
python3 --version
# sollte ≥ 3.9 anzeigenpip3 --version# sollte eine Versionsnummer zeigenFallsFehlteines fehlt (Beispiel für Debian/Ubuntu):was?
sudo apt update sudo apt install python3 python3-pip python3-venv
xfreerdp3
installieren:sudo apt install freerdp3-x11
System-KeyringKeyring-Backend?vorhanden?Bei(Gnome,KDE,KDECinnamon …etc: schonintegriert. Keine Aktion nötig.dabei)
22. Projektordner anlegenProjektordner & virtuelle Umgebungvenv erstellen (nur 1x nötig)
VerzeichnisOrdneranlegen:anlegen & wechseln:mkdir -p ~/rdp-launcher cd ~/rdp-launcher
- Virtuelle Umgebung
(venv)erstellen:python3 -m venv .venv
- venv
aktivieren(wichtig – bei jedem neuen Terminal!):aktivieren:source .venv/bin/activate
Links in der Eingabeaufforderung erscheint jetzt(.venv). - Abhängigkeit
installieren:keyringpip install keyring
source .venv/bin/activate
ausführen, bevor du 33. Python-Skript speichernanlegen
LegeSpeichere diefolgendes Dateials xfreerdp_launcher.py
im Projektordner an und kopiere den kompletten, kommentierten Quelltext hinein:Projektordner:
▶ Python-Code anzeigen / ausklappen
#!/usr/bin/env python3
# ──────────────────────────────────────────────────────────────────────────────
#"""
xfreerdp_launcher.py #– Ein komfortabler RDP-Launcher fürmit xfreerdp3-SessionsTUI mit:und #Keyring-Support
•Features:
farbiger- TUI-Übersichtliche, farbige Menüführung #(TUI)
•- Passwort-HandlingPasswörter viasicher systemweitenim System-Keyring #gespeichert
•- automatischemAutomatisches Neuladen der config.json,Konfiguration damitbei Änderungen
sofort- wirksamVoll #kompatibel werden,mit ohnexfreerdp3-Parametern
dasBenötigt:
Programm neu zu starten
# Erfordert:
# –- Python ≥ 3.9
#- –keyring Paket(pip „keyring“install #keyring)
– gültige- config.json (Beispielim sieheselben unten)Ordner
# ──────────────────────────────────────────────────────────────────────────────
# ----------------------------- Standardbibliotheken --------------------------"""
import argparse
# CLI-Argument-Parsing
import json # Einlesen / Parsen der config.json
import subprocess
# Ausführen externer Programme (xfreerdp3)
import sys
#import System-Exit & Ausnahmebehandlungos
from pathlib import Path
# Plattform-unabhängige Pfadangaben
from getpass import getpass
# Sichere Passwort-Abfrage im Terminal
import keyring # Zugriff auf den System-Keyring
import os # Terminal-Steuerung (clear)
# ----------------------------- Konstanten ------------------------------------
CONFIG_FILE = Path("./config.json").resolve()
# Vollständiger Pfad zur Konfig
SERVICE = "xfreerdp_launcher"
# Namespace im Keyring
# ----------------------------- Hilfsfunktionen -------------------------------
def farbig(text:text, str, farbe: str) -> str:
farbe="""
Liefert einen farbigen String für ANSI-Terminals.
"""reset"):
farben = {
"black"reset": "\033[30m"0m", "bold": "\033[1m", "red": "\033[31m",
"green": "\033[32m", "yellow": "\033[33m", "blue": "\033[34m",
"magenta": "\033[35m", "cyan": "\033[36m", "white": "\033[37m", "gray": "\033[90m",
"bright_red": "\033[91m", "bright_green": "\033[92m",
"bright_yellow": "\033[93m", "bright_blue": "\033[94m",
"bright_magenta": "\033[95m", "bright_cyan": "\033[96m",
"bright_white": "\033[97m", "bold": "\033[1m",
"underline": "\033[4m", "reversed": "\033[7m", "reset": "\033[0m"
}
return f"{farben.get(farbe, '')}{text}{farben['reset']}"
# -----------------------------------------------------------------------------
def load_config_safe() -> dict:
"""
Lädt config.json robust und gibt ein leeres Dict bei Fehlern zurück.
"""
try:
if not CONFIG_FILE.exists(lade_config():
print(farbig(f"⚠️ config.json nicht gefunden: {CONFIG_FILE}", "yellow"))
return {}try:
return json.loads(CONFIG_FILE.read_text())
except json.JSONDecodeErrorException as e:
print(farbig(f"⚠️ Fehler in config.json: {e}", "red"))
return {}
# -----------------------------------------------------------------------------
def fetch_password(profile_alias:passwort_holen(cred_alias, str,domain, domain: str, user: str) -> str:
"""
Holt (oder fragt) das Passwort für eine Domäne aus dem Keyring.
"""user):
cred_id = f"{profile_alias}cred_alias}|{domain}|{user}"
pw = keyring.get_password(SERVICE, cred_id)
if pw is None:
pw = getpass(f"Passwort für {user}@{domain}: ")
if not pw:
sys.exit("Abbruch – leereskein Passwort.Passwort eingegeben.")
keyring.set_password(SERVICE, cred_id, pw)
print(farbig("✅ Passwort im System-Keyring gespeichert.", "green"))
return pw
# -----------------------------------------------------------------------------
def start_rdp(ip:rdp_starten(ip, str,domain, domain:user, str,pw, user: str, password: str, extra: list[str])extra_args):
"""
Startet eine RDP-Sitzung via xfreerdp3.
"""
cmd = [
"xfreerdp3",
f"/u:{user}", f"/d:{domain}", f"/v:{ip}",
"/dynamic-resolution", "/clipboard", "/cert:ignore",
f"/p:{password}pw}"
] + extraextra_args
try:
subprocess.Popen(cmd) # nicht blockierend
except Exception as e:
print(farbig(f"❌ Fehler beim Starten:Start: {e}", "red"))
# -----------------------------------------------------------------------------
def handle_host(entry:host_verbinden(entry, dict,creds, credentials:default_cred, dict,
default_cred: str,
extra_args: list[str])extra_args):
"""
Öffnet einen einzelnen Host aus dem Menü.
"""
cred = entry.get("cred"), or default_creddefault_cred)
profile = credentials.creds.get(cred)
if not profile:
print(farbig(f"❌ Credential-AliasCredential '{cred}' nicht gefunden.fehlt!", "red"))
input("⏎ Weiter...")
return
passwordpw = fetch_password(passwort_holen(cred, profile["domain"], profile["user"])
start_rdp(rdp_starten(entry["host"], profile["domain"], profile["user"], password,pw, extra_args)
print(farbig(f"💻 Sitzung zu {entry['alias']} gestartet.", "cyan"))
input("⏎ Weiter...")
# -----------------------------------------------------------------------------
def navigate_menu(section:menue(section, dict,creds, credentials:default_cred, dict,extra_args, default_cred: str,
extra_args: list[str],
path: str = path="", offene: set = offene=set()):
"""
Rekursive Menü-Navigation.
"""
is_top_levelis_root = (path == "")
while True:
# ----- Live-Reload der Konfiguration -----
config = load_config_safe(lade_config()
new_groupsgroups = config.get("groups", {})
new_credentialscredentials = config.get("credentials", {})
new_default_creddefault = config.get("default_cred", default_cred)
if is_top_level:is_root:
section = {"groups": new_groups}groups}
credentialscreds = new_credentialscredentials
default_cred = new_default_cred
# ----- Bildschirm leeren & Kopfzeile -----default
os.system("clear")
print(farbig(f"=== RDP-KurzwahlMenü {path} ===\n", "bold"))
groups = section.get("groups", {})
hosts = section.get("hosts", [])
index_mapindex = {}
# ----- Einträge auflisten -----
i = 1
if is_top_level:is_root:
for name, content in groups.items():
color = content.get("color", "reset")
print(f" {i:>2}) {farbig(name, color)content.get('color', 'reset'))} →")
index_map[index[str(i)] = ("group_flat"group", content, f"{path}/{name}")
i += 1
else:
for h in hosts:
alias = h["alias"]
markiertmark = farbig("✅", "green") if alias in offene else " "
print(f" {i:>2}) {farbig(alias, h.get('color', 'reset'))} "
f"– {h['host']} {markiert}mark}")
index_map[index[str(i)] = ("host", h)
i += 1
# ----- Standardoptionen -----
print("\n")
print(farbig("n b) Zurück", "bold"))
print(farbig(" q) Beenden", "bold"))
sel = input(farbig("Auswahl: ", "bold")).strip()
# ----- Eingabe auswerten -----
if sel == "q":
sys.exit("Beende Launcher.")
elif sel == "b":
return
elif sel in index_map:index:
typ, eintrag, *rest = index_map[index[sel]
if typ == "group_flat"group":
navigate_menu(menue({"hosts": eintrag.get("hosts", [])}, credentials,creds, default_cred, extra_args, rest[0], offene)
elif typ == "host":
offene.add(eintrag["alias"])
handle_host(host_verbinden(eintrag, credentials,creds, default_cred, extra_args)
else:
print(farbig("❌ Ungültige Auswahl.", "red"))
input("⏎ Weiter...")
# ----------------------------- Haupteinstieg ---------------------------------
def main():
"""
Parses CLI-Optionen und ruft das Hauptmenü auf.
"""
parser = argparse.ArgumentParser(
description="xfreerdp Launcher mit Keyring"
)
parser.add_argument(
"freerdp_args", nargs=argparse.REMAINDER, help="zusätzliche xfreerdp3-Parameter (/f /multimon …)"
Parameter")
args = parser.parse_args()
offene_sitzungenoffene = set()
while True:
config = load_config_safe(lade_config()
credentialscreds = config.get("credentials", {})
default_creddefault = config.get("default_cred", "")
groups = config.get("groups")
if not groups:
print(farbig("⚠️ Keine Gruppen in config.json gefunden.json.", "yellow"))
input("⏎ Weiter...")
continue
navigate_menu(menue({"groups": groups}, credentials,creds, default_cred,default, args.freerdp_args, "", offene_sitzungen)
# ----------------------------- Skript-Guard ----------------------------------offene)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[Abbruch]")
44. Beispiel-Beispiel config.json
anlegen
LegeErstelle im selben Ordner dieeine Datei config.json
an:mit folgendem Inhalt:
▶ JSON-Beispiel-Konfiguration anzeigen / ausklappen
{
"default_cred": "user1",
"credentials": {
"user1": { "domain": "FIRMENNETZ", "user": "max.mustermann" },
"user2": { "domain": "INTRANET", "user": "maria.musterfrau" }
},
"groups": {
"Standort_A": {
"color": "bright_blue",
"hosts": [
{ "alias": "Server-A1", "host": "10.0.10.11", "color": "bright_green", "cred": "user1" },
{ "alias": "Server-A2", "host": "10.0.10.12", "color": "bright_magenta","cred": "user2" }
]
},
"Standort_B": {
"color": "bright_yellow",
"hosts": [
{ "alias": "Server-B1", "host": "10.0.20.21", "color": "bright_cyan", "cred": "user1" },
{ "alias": "Server-B2", "host": "10.0.20.22", "color": "bright_red", "cred": "user2" }
]
}
}
}
Passe Alias-Namen, IP-Adressen undAdressen, Farben nachetc. Bedarfkannst an.du Diejederzeit gewählten Server liegen alle im privaten Netz 10.0.0.0/16.anpassen!
55. Alles ausführen – vom Terminalstart bis zur ersten RDP-SitzungZusammengefasst
- Terminal öffnen
(meist→Ctrl+Alt+Toder „Terminal“ im Menü). Zum Projektordner wechselncd ~/rdp-launcher
Virtuelle Umgebungvenv aktivieren→
source .venv/bin/activate
Du musst diesen Befehl jedes Mal ausführen, wenn duein neues Terminalöffnest. Die Prompt zeigt jetzt etwas wie(.venv) user@host:~/rdp-launcher$Skript beim ersten Mal ausführbar machenchmod +x xfreerdp_launcher.py(Nur einmal nötig. Danach bleibt die Datei ausführbar.)- Launcher starten
→
./xfreerdp_launcher.py
- Durch das
Executable-BitMenünichtklicken:setzenStandortmöchtest: - Passwort wird einmalig abgefragt & im Keyring gespeichert
MenüZumdurchklickenBeenden:Standort wählen (z. B. Standort_A)Server wählen (z. B. Server-A1)
Erste PasswortabfrageBeim allerersten Verbindungsversuch fragt das Script nach dem Passwort (kein Echo im Terminal). Eingeben →Enterdrücken. Der Keyring speichert es sicher; beim nächsten Mal wird nicht mehr gefragt.Sitzung schließenRDP-Fenster beenden → im Menüqeingeben, um den Launcher zu schließen.eingeben
Alternative – falls „Permission denied“ oder du
python3Server xfreerdp_launcher.py
→ Passwort
Ctrl+Alt+T T cd ~/rdp-launcher
source .venv/bin/activate
./xfreerdp_launcher.py
→ Standort → Server → Passwort → fertig!
6 Typische Fehler & Lösungen
|
|
|
|
|
7 Nächste Schritte für Fortgeschrittene
Weitere Hosts, Farben oder Standorte inconfig.jsonergänzen./f(Fullscreen) oder/multimonals Zusatzparameter ausprobieren.Projekt in Git versionieren.Symlink in/usr/local/binerstellen, um den Launcher global aufzurufen.
🎉 Glückwunsch! Du hast deinen ersten Python-Launcher inkl. venv, Abhängigkeiten und Konfiguration eingerichtet.
8 Falsch hinterlegte Passwörter wieder entfernen
Falls du dich vertippt hast oder das Kennwort geändert wurde, kannst du den entsprechenden Eintrag im System-Keyring gezielt löschen. Danach fragt dich der Launcher beim nächsten Verbindungsversuch erneut nach dem Passwort und speichert es frisch ab.
Terminal öffnen & in dein Projekt wechselncd ~/rdp-launcher source .venv/bin/activate
#./xfreerdp_launcher.py
python -m keyring del xfreerdp_launcher "user1|FIRMENNETZ|max.mustermann"
xfreerdp_launcher→ ist derSERVICE-Name im Script (KonstanteSERVICE)."user1|FIRMENNETZ|max.mustermann"→ ist der eindeutige Schlüsselalias|domain|user, genau wie ihn das Script beim Speichern verwendet.Beispiel:•alias= user1•domain= FIRMENNETZ•user= max.mustermann
python -m keyring --help
list