Skip to main content

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.

Arbeite jedenQuickstart: Schritt nacheinanderfür ab – dann hast du am Ende:
    einen sauberen Projekt-Ordner eine eigene Python-UmgebungSchritt (damit du nichts „kaputt“ installierst) das fertige Skriptwenn xfreerdp_launcher.py eine Beispiel-config.jsonvenv mitschon zwei Standorten einen Eintrag im System-Keyring, damit Passwörter sicher gespeichert werden

    1 Grund­voraus­setzungen prüfen

    existiert)
    1. Linux-ShellTerminal öffnen:öffnen 
      Ctrl+Alt+T
    drückenIn oderProjektordner „Terminal“wechseln
    cd aus~/rdp-launcher demVirtuelle 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)

    1. Python & Pip vorhanden?Pip:
      python3 --version
      # sollte ≥ 3.9 anzeigen
      pip3 --version   # sollte eine Versionsnummer zeigen
      FallsFehlt eines fehlt (Beispiel für Debian/Ubuntu): was?
      sudo apt update sudo apt install python3 python3-pip python3-venv
    2. xfreerdp3 installieren:
      sudo apt install freerdp3-x11
    3. System-KeyringKeyring-Backend? vorhanden?
      Bei (Gnome, KDE,KDE Cinnamon …etc: schon integriert. Keine Aktion nötig.dabei)

    22. Projekt­ordner anlegenProjektordner & virtuelle Umgebungvenv erstellen (nur 1x nötig)

    1. VerzeichnisOrdner anlegen:anlegen & wechseln:
      mkdir -p ~/rdp-launcher
      cd ~/rdp-launcher
    2. Virtuelle Umgebung (venv) erstellen:
      python3 -m venv .venv
    3. venv aktivieren (wichtig – bei jedem neuen Terminal!):aktivieren:
      source .venv/bin/activate
      Links in der Eingabe­aufforderung erscheint jetzt (.venv).
    4. Abhängigkeit keyring installieren:
      pip install keyring
    Tipp: Immer erstzuerst source .venv/bin/activate ausführen, bevor du an dem Projekt weiterarbeitest.arbeitest!

    33. Python-Skript speichernanlegen

    LegeSpeichere diefolgendes Dateials xfreerdp_launcher.py im Projekt­ordner 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+T oder „Terminal“ im Menü).
      Zum Projektordner wechseln
      cd ~/rdp-launcher
      Virtuelle Umgebungvenv aktivieren
      → source .venv/bin/activate
      Du musst diesen Befehl jedes Mal ausführen, wenn du ein neues Terminal öffnest. Die Prompt zeigt jetzt etwas wie (.venv) user@host:~/rdp-launcher$ Skript beim ersten Mal ausführbar machen
      chmod +x xfreerdp_launcher.py
      (Nur einmal nötig. Danach bleibt die Datei ausführbar.) Launcher 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 duDurch das Executable-BitMenü nichtklicken: setzenStandort möchtest:

      →
      python3Server xfreerdp_launcher.py
      →

      Passwort

      Passwort wird einmalig abgefragt & im Keyring gespeichert MenüZum durchklickenBeenden:
        Standort wählen (z. B. Standort_A) Server wählen (z. B. Server-A1) 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.eingeben
        Kurzzusammenfassung für Azubis:Mini-Quickstart:
        Ctrl+Alt+T 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 Fort­ge­schrittene

          Weitere Hosts, Farben oder Standorte in config.json ergänzen. /f (Fullscreen) oder /multimon als Zusatz­parameter 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.

          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 wechseln
            cd ~/rdp-launcher  source .venv/bin/activate  #./xfreerdp_launcher.py
            venv aktivieren, falls nicht schon geschehen
            Lösch-Befehl ausführen
            python -m keyring del xfreerdp_launcher "user1|FIRMENNETZ|max.mustermann"
              xfreerdp_launcher  → ist der SERVICE-Name im Script (Konstante SERVICE). "user1|FIRMENNETZ|max.mustermann"  → ist der eindeutige Schlüssel alias|domain|user, genau wie ihn das Script beim Speichern verwendet.
              Beispiel:
              • alias = user1
              • domain = FIRMENNETZ
              • user = max.mustermann Erneut verbinden
              Starte den Launcher wie gewohnt. Beim ersten Zugriff auf den betreffenden Server wirst du jetzt wieder nach dem Passwort gefragt. Gib das korrekte Kennwort ein – es wird erneut im Keyring gespeichert.
              Hinweis: Falls du mehrere falsche Kombinationen hinterlegt hast, wiederhole den Befehl für jede einzelne Schlüssel-Zeichenkette. Über python -m keyring --help erhältst du weitere Optionen, u. a. list, um vorhandene Einträge anzuzeigen (Backend-abhängig).