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
  2. drücken
  3. In oderProjektordner „Terminal“wechseln
    cd aus~/rdp-launcher
  4. dem
  5. Virtuelle Umgebung aktivieren
    source .venv/bin/activate
    (Bei jedem neuen Terminal nötig!)
  6. 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
  7. Menü wählen.nutzen, Standort & Server auswählen
  8. 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 du

    • Durch das Executable-BitMenü nichtklicken: setzenStandort möchtest:

      python3Server xfreerdp_launcher.py

      Passwort

    • Passwort wird einmalig abgefragt & im Keyring gespeichert
    • MenüZum durchklickenBeenden:
      1. Standort wählen (z. B. Standort_A)
      2. 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

ProblemUrsache & Fix
command not found: xfreerdp3freerdp3-x11 nicht installiert oder nicht im $PATH. sudo apt install freerdp3-x11
ModuleNotFoundError: keyringvenv nicht aktiviert oder pip install keyring vergessen.
Passwort wird bei jedem Start abgefragtKein 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.

  1. 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).