From ed6e96b3c1487c6bc0692c8008425ab7274702bc Mon Sep 17 00:00:00 2001 From: x3 Date: Mon, 21 Sep 2026 23:47:01 +0200 Subject: [PATCH] SSH-Agent-Socket an ssh durchreichen (wie Terminal), Banner-Tipp, Doku --- .../Services/ConnectionServices.swift | 24 +++++++++++++++++++ Sources/RemminaMac/Services/PTYSession.swift | 9 +++++-- .../Views/EmbeddedTerminalView.swift | 2 +- docs/ERRORS.md | 14 +++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Sources/RemminaMac/Services/ConnectionServices.swift b/Sources/RemminaMac/Services/ConnectionServices.swift index dab4ebf..19b324f 100644 --- a/Sources/RemminaMac/Services/ConnectionServices.swift +++ b/Sources/RemminaMac/Services/ConnectionServices.swift @@ -42,6 +42,30 @@ enum SSHService { // MARK: - Passwort / Auto-Login + /// SSH-Agent-Socket für GUI-Prozesse auflösen (Finder-Apps erben SSH_AUTH_SOCK + /// nicht, Terminal schon — ohne diesen Unterschied nutzt Terminal evtl. einen + /// Agent-Key, während die App nach dem Passwort fragt und scheitert). + static func agentSocket() -> String? { + let p = Process() + p.executableURL = URL(fileURLWithPath: "/bin/launchctl") + p.arguments = ["getenv", "SSH_AUTH_SOCK"] + let out = Pipe() + let err = Pipe() + p.standardOutput = out + p.standardError = err + do { + try p.run() + p.waitUntilExit() + } catch { + return nil + } + let path = String(data: out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)? + .trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + guard !path.isEmpty, + FileManager.default.fileExists(atPath: path) else { return nil } + return path + } + /// Gespeichertes Login-Secret: erst Profil-Passwort, dann Key-Passphrase (Keychain, nie JSON). static func loginSecret(for profile: ConnectionProfile, key: StoredSSHKey?) -> String? { if let acct = profile.passwordAccount, diff --git a/Sources/RemminaMac/Services/PTYSession.swift b/Sources/RemminaMac/Services/PTYSession.swift index 3b612f0..f665002 100644 --- a/Sources/RemminaMac/Services/PTYSession.swift +++ b/Sources/RemminaMac/Services/PTYSession.swift @@ -298,12 +298,17 @@ final class TerminalManager: ObservableObject { baseExe = "/usr/bin/ssh" baseArgs = SSHService.sshArguments(for: profile, key: key) } - // Auto-Login nur mit Keychain-Secret + sshpass, sonst interaktiver Prompt im Terminal + // Auto-Login nur mit Keychain-Secret + sshpass, sonst interaktiver Prompt im Terminal. + // SSH-Agent durchreichen (falls vorhanden): gleiche Keys wie in Terminal.app. let cfg = SSHService.sessionConfig(executable: baseExe, args: baseArgs, password: SSHService.loginSecret(for: profile, key: key), sshpassPath: SSHService.findSshpass()) s.configure(executable: cfg.executable, arguments: cfg.arguments) - s.customEnv = cfg.extraEnv + var env = cfg.extraEnv + if let sock = SSHService.agentSocket() { + env["SSH_AUTH_SOCK"] = sock + } + s.customEnv = env s.setAutoLogin(cfg.autoLogin) sessions[profile.id] = s return s diff --git a/Sources/RemminaMac/Views/EmbeddedTerminalView.swift b/Sources/RemminaMac/Views/EmbeddedTerminalView.swift index 95d4ce2..07a6e6d 100644 --- a/Sources/RemminaMac/Views/EmbeddedTerminalView.swift +++ b/Sources/RemminaMac/Views/EmbeddedTerminalView.swift @@ -42,7 +42,7 @@ struct EmbeddedTerminalView: View { .font(.caption).foregroundStyle(Dracula.foreground) .lineLimit(3) if lastMessage.localizedCaseInsensitiveContains("permission denied") { - Text("Tipp: Passwort im Auge-Modus eingeben, auf Tastaturlayout (z. B. Z/Y) achten, Keychain-Eintrag prüfen.") + Text("Tipp: Passwort im Auge-Modus eingeben, Tastaturlayout prüfen. Falls Terminal.app ohne Passwort einloggt (Agent-Key: ssh-add -l), Key in der App hinterlegen oder Agent nutzen.") .font(.caption).foregroundStyle(Dracula.orange) } } diff --git a/docs/ERRORS.md b/docs/ERRORS.md index 885718f..e29ffb3 100644 --- a/docs/ERRORS.md +++ b/docs/ERRORS.md @@ -20,6 +20,7 @@ Stand: 2026-09-21 · Version 0.1.0 · macOS arm64 · Swift 5.9+ · Xcode 15+ | 11 | 2026-09-21 | Passwort-Feature | Wunsch: Passwort + Keys speichern UND nutzen (bisher nur gespeichert, nie benutzt) | Feature | ✅ umgesetzt — Auto-Login via sshpass, sonst interaktiv | | 12 | 2026-09-21 | „Immer Verbindung falsch" | User-Report ohne Details (Ziel/Credentials unbekannt) | offen | ⬜ Teils: Editor-Validierung + kopierbarer Fehlerbericht; warte auf Bericht | | 13 | 2026-09-21 | Permission denied trotz richtigem Passwort | Text-Ersetzung im Eingabefeld verfälscht Sonderzeichen | hoch | ✅ behoben — gehärtetes Eingabefeld (alle Ersetzungen aus, 9/9 Tests) | +| 14 | 2026-09-21 | Terminal geht, App nicht (gleiches Passwort) | SSH-Agent-Socket fehlt GUI-Prozessen → evtl. Key-Login in Terminal | hoch | ✅ App reicht Agent jetzt durch; Gegenprobe beim User offen | --- @@ -184,3 +185,16 @@ Oder mit Token als Passwort: `git push https://:@git.sysdaemon.xyz **Fix (`Views/TerminalInputField.swift`):** Eigenes Eingabefeld auf `NSTextView`-Basis mit allen Ersetzungen explizit aus (Quotes, Dashes, Completion, Spelling, Data/Link-Erkennung, Text-Replacement, Plain-Text); Secure-Variante via `NSSecureTextField` (per Design ersetzungsfrei); Enter → Senden, Paste-Normalisierung (keine Umbrüche). **Verifikation:** Harness 9/9 (alle Flags aus). Hinweis: Restrisiko Tastaturlayout (Z/Y) bleibt — Banner-Tipp ergänzt. **Status:** ✅ behoben — bitte mit v0.5.1 erneut testen. + +## #14 — Terminal.app loggt ein, RemminaMac nicht (gleiches Passwort) +**Stand (User-Antworten):** v0.5.1 installiert, Passwort-Login, Terminal.app funktioniert mit denselben Daten. +**Analyse:** App-seitig ist alles verifiziert (PTY, Fehleranzeige, Passwort-Loop mit Sonderzeichen, Eingabe-Ersetzungen aus). Gleiches Binary + gleiche Config → Unterschied muss aus der Umgebung kommen. Hauptkandidat: **SSH-Agent** — Finder-gestartete GUI-Prozesse erben `SSH_AUTH_SOCK` nicht. Nutzt Terminal unbemerkt einen Agent-Key (statt Passwort), scheitert die App mit Passwort. Verifiziert: `launchctl getenv SSH_AUTH_SOCK` liefert den Socket, Shell-Env wäre ohne Abfrage leer für GUI-Apps. +**Fix:** `SSHService.agentSocket()` (launchctl + Existenzprüfung) → `TerminalManager` injiziert `SSH_AUTH_SOCK` ins ssh-Env (koexistiert mit SSHPASS). Banner-Tipp ergänzt. +**Verifikation:** Harness 4/4 (Auflösung 0.07 s, Pfad existiert, `ssh -G` ok mit echtem + kaputtem Socket). +**Gegenprobe beim User (bitte ausführen):** +```bash +ssh-add -l +ssh -o IdentitiesOnly=yes administrator@sysdaemon.xyz +``` +Zweite Zeile erzwingt Passwort-Login ohne Agent — scheitert sie auch im Terminal, war es immer der Agent-Key. +**Status:** ✅ App-Seite umgesetzt — warte auf Gegenprobe.