Version_0.5 xfreerdp - Skript
RDP-Launcher mit xfreerdp3
einrichten & nutzen
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. Arbeite jeden Schritt nacheinander ab – dann hast du am Ende:
- einen sauberen Projekt-Ordner
- eine eigene Python-Umgebung (damit du nichts „kaputt“ installierst)
- das fertige Skript
xfreerdp_launcher.py
- eine Beispiel-
config.json
mit zwei Standorten - einen Eintrag im System-Keyring, damit Passwörter sicher gespeichert werden
1 Grundvoraussetzungen prüfen
- Linux-Shell öffnen: Ctrl+Alt+T drücken oder „Terminal“ aus dem Menü wählen.
- Python & Pip vorhanden?
Falls eines fehlt (Beispiel für Debian/Ubuntu):python3 --version # sollte ≥ 3.9 anzeigen pip3 --version # sollte eine Versionsnummer zeigen
sudo apt update sudo apt install python3 python3-pip python3-venv
xfreerdp3
installieren:sudo apt install freerdp3-x11
- System-Keyring vorhanden?
Bei Gnome, KDE, Cinnamon … schon integriert. Keine Aktion nötig.
2 Projektordner anlegen & virtuelle Umgebung erstellen
- Verzeichnis anlegen:
mkdir -p ~/rdp-launcher cd ~/rdp-launcher
- Virtuelle Umgebung (venv) erstellen:
python3 -m venv .venv
- venv aktivieren (wichtig – bei jedem neuen Terminal!):
Links in der Eingabeaufforderung erscheint jetztsource .venv/bin/activate
(.venv)
. - Abhängigkeit
keyring
installieren:pip install keyring
Tipp: Immer erst
source .venv/bin/activate
ausführen, bevor du an dem Projekt weiterarbeitest.3 Skript speichern
Lege die Datei xfreerdp_launcher.py
im Projektordner an und kopiere den kompletten, kommentierten Quelltext hinein:
▶ Python-Code anzeigen / ausklappen
#!/usr/bin/env python3
# ──────────────────────────────────────────────────────────────────────────────
# xfreerdp_launcher.py
# Ein komfortabler Launcher für xfreerdp3-Sessions mit:
# • farbiger TUI-Menüführung
# • Passwort-Handling via systemweiten Keyring
# • automatischem Neuladen der config.json, damit Änderungen sofort wirksam
# werden, ohne das Programm neu zu starten
# Erfordert:
# – Python ≥ 3.9
# – Paket „keyring“
# – gültige config.json (Beispiel siehe unten)
# ──────────────────────────────────────────────────────────────────────────────
# ----------------------------- Standardbibliotheken --------------------------
import argparse # CLI-Argument-Parsing
import json # Einlesen / Parsen der config.json
import subprocess # Ausführen externer Programme (xfreerdp3)
import sys # System-Exit & Ausnahmebehandlung
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: str, farbe: str) -> str:
"""
Liefert einen farbigen String für ANSI-Terminals.
"""
farben = {
"black": "\033[30m", "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():
print(farbig(f"⚠️ config.json nicht gefunden: {CONFIG_FILE}", "yellow"))
return {}
return json.loads(CONFIG_FILE.read_text())
except json.JSONDecodeError as e:
print(farbig(f"⚠️ Fehler in config.json: {e}", "red"))
return {}
# -----------------------------------------------------------------------------
def fetch_password(profile_alias: str, domain: str, user: str) -> str:
"""
Holt (oder fragt) das Passwort für eine Domäne aus dem Keyring.
"""
cred_id = f"{profile_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 – leeres Passwort.")
keyring.set_password(SERVICE, cred_id, pw)
print(farbig("✅ Passwort im System-Keyring gespeichert.", "green"))
return pw
# -----------------------------------------------------------------------------
def start_rdp(ip: str, domain: str, user: str, password: str, extra: list[str]):
"""
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}"
] + extra
try:
subprocess.Popen(cmd) # nicht blockierend
except Exception as e:
print(farbig(f"❌ Fehler beim Starten: {e}", "red"))
# -----------------------------------------------------------------------------
def handle_host(entry: dict,
credentials: dict,
default_cred: str,
extra_args: list[str]):
"""
Öffnet einen einzelnen Host aus dem Menü.
"""
cred = entry.get("cred") or default_cred
profile = credentials.get(cred)
if not profile:
print(farbig(f"❌ Credential-Alias '{cred}' nicht gefunden.", "red"))
input("⏎ Weiter...")
return
password = fetch_password(cred, profile["domain"], profile["user"])
start_rdp(entry["host"], profile["domain"], profile["user"],
password, extra_args)
print(farbig(f"💻 Sitzung zu {entry['alias']} gestartet.", "cyan"))
input("⏎ Weiter...")
# -----------------------------------------------------------------------------
def navigate_menu(section: dict,
credentials: dict,
default_cred: str,
extra_args: list[str],
path: str = "",
offene: set = set()):
"""
Rekursive Menü-Navigation.
"""
is_top_level = (path == "")
while True:
# ----- Live-Reload der Konfiguration -----
config = load_config_safe()
new_groups = config.get("groups", {})
new_credentials = config.get("credentials", {})
new_default_cred = config.get("default_cred", default_cred)
if is_top_level:
section = {"groups": new_groups}
credentials = new_credentials
default_cred = new_default_cred
# ----- Bildschirm leeren & Kopfzeile -----
os.system("clear")
print(farbig(f"=== RDP-Kurzwahl {path} ===\n", "bold"))
groups = section.get("groups", {})
hosts = section.get("hosts", [])
index_map = {}
# ----- Einträge auflisten -----
i = 1
if is_top_level:
for name, content in groups.items():
color = content.get("color", "reset")
print(f" {i:>2}) {farbig(name, color)} →")
index_map[str(i)] = ("group_flat", content, f"{path}/{name}")
i += 1
else:
for h in hosts:
alias = h["alias"]
markiert = farbig("✅", "green") if alias in offene else " "
print(f" {i:>2}) {farbig(alias, h.get('color', 'reset'))} "
f"– {h['host']} {markiert}")
index_map[str(i)] = ("host", h)
i += 1
# ----- Standardoptionen -----
print("\n")
print(farbig(" 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:
typ, eintrag, *rest = index_map[sel]
if typ == "group_flat":
navigate_menu({"hosts": eintrag.get("hosts", [])},
credentials, default_cred, extra_args,
rest[0], offene)
elif typ == "host":
offene.add(eintrag["alias"])
handle_host(eintrag, credentials, 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 …)"
)
args = parser.parse_args()
offene_sitzungen = set()
while True:
config = load_config_safe()
credentials = config.get("credentials", {})
default_cred = config.get("default_cred", "")
groups = config.get("groups")
if not groups:
print(farbig("⚠️ Keine Gruppen in config.json gefunden.", "yellow"))
input("⏎ Weiter...")
continue
navigate_menu({"groups": groups}, credentials, default_cred,
args.freerdp_args, "", offene_sitzungen)
# ----------------------------- Skript-Guard ----------------------------------
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[Abbruch]")
4 Beispiel-config.json
anlegen
Lege im selben Ordner die Datei config.json
an:
▶ JSON-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 und Farben nach Bedarf an. Die gewählten Server liegen alle im privaten Netz 10.0.0.0/16
.
5 SkriptAlles ausführen – vom Terminalstart bis zur ersten RDP-Sitzung
venvTerminal öffnen (meist Ctrl+Alt+T oder „Terminal“ im Menü).- Zum Projektordner wechseln
cd ~/rdp-launcher
- Virtuelle Umgebung aktivieren
(
Du musst diesen Befehl jedes Mal ausführen, wenn du ein neues Terminal öffnest. Die Prompt zeigt jetzt etwas wiesource .venv/bin/activate
(.venv) user@host:~/rdp-launcher$
in der Prompt) - Skript beim ersten Mal ausführbar
machen:machen
(Nur einmal nötig. Danach bleibt die Datei ausführbar.)chmod +x xfreerdp_launcher.py
- Launcher
starten:starten./xfreerdp_launcher.py # Standard ./xfreerdp_launcher.py /f # z. B. direkt Vollbild ./xfreerdp_launcher.py /multimon /p: # weitere xfreerdp-Parameter
Alternative – falls „Permission denied“ oder du das Executable-Bit nicht setzen möchtest:
python3 xfreerdp_launcher.py
- Menü
folgendurchklicken→- Standort wählen
→(z. B. Standort_A) - Server wä
hlen. Beim ersten Aufruf Passwort eingebenhlen (wirdz.sicherB.im Keyring gespeichert).Server-A1)
- Standort wählen
- Erste Passwortabfrage Beim allerersten Verbindungsversuch fragt das Script nach dem Passwort (kein Echo im Terminal). Eingeben → Enter drücken. Der Keyring speichert es sicher; beim nächsten Mal wird nicht mehr gefragt.
- Sitzung schließen RDP-Fenster beenden → im Menü q eingeben, um den Launcher zu schließen.
Kurzzusammenfassung für Azubis:
Ctrl+Alt+T
cd ~/rdp-launcher
source .venv/bin/activate
./xfreerdp_launcher.py
→ Standort → Server → Passwort → fertig!
6 Typische Fehler & Lösungen
Problem | Ursache & Fix |
---|---|
command not found: xfreerdp3 |
freerdp3-x11 nicht installiert oder nicht im $PATH . sudo apt install freerdp3-x11 |
ModuleNotFoundError: keyring |
venv nicht aktiviert oder pip install keyring vergessen. |
Passwort wird bei jedem Start abgefragt | Kein Keyring-Backend vorhanden (z. B. minimale Server-Installation). Installiere gnome-keyring oder kwallet . |
7 Nächste Schritte für Fortgeschrittene
- Weitere Hosts, Farben oder Standorte in
config.json
ergänzen. /f
(Fullscreen) oder/multimon
als Zusatzparameter ausprobieren.- Projekt in Git versionieren.
- Symlink in
/usr/local/bin
erstellen, um den Launcher global aufzurufen.
🎉 Glückwunsch! Du hast deinen ersten Python-Launcher inkl. venv, Abhängigkeiten und Konfiguration eingerichtet.