feat: initialize Android SSH manager app with terminal and crypto

This commit is contained in:
Tronax 2026-08-03 18:04:29 +02:00
commit 66417cc194
Signed by: Tronax
SSH key fingerprint: SHA256:2pKKXDZucWvaF/GzXNz0FY53EAO1YDLN80bqS+TTz/o
55 changed files with 3711 additions and 0 deletions

119
README.md Normal file
View file

@ -0,0 +1,119 @@
# Mobile SSH Manager
Android-App (Kotlin + Jetpack Compose) zum Verwalten von SSH-Verbindungen und zum
interaktiven Arbeiten auf Servern über ein eingebautes Terminal.
## Stand / 1Password-Hinweis (bitte lesen)
Der ursprüngliche Wunsch war **1Password SSH Key Support**. Das ist auf Android
aktuell **technisch nicht möglich**:
- Der 1Password SSH Agent ist **Desktop-only** (macOS, Windows, Linux).
- Es gibt **keine Kotlin/Java-SDK** und keinen Android-Agent, nur Go/JS/Python-SDKs
(Desktop/Server) und einen offenen [Feature-Request für Android][1p-fr].
- Drittanbieter-Apps können 1Password-Schlüssel auf Android nicht einbinden.
[1p-fr]: https://www.1password.community/developers-69/feature-request-android-and-ssh-19991
**Stattdessen** unterstützt diese App den realistischen Workflow:
Private Keys werden als Datei importiert (Storage Access Framework) und
**AES-GCM-verschlüsselt via Android Keystore** gespeichert. Der praktische Weg mit
1Password ist also: Key am Desktop aus 1Password exportieren → aufs Handy übertragen →
in der App importieren. Kein 1Password-Bezug im UI, weil er ehrlicherweise nicht
eingehalten werden könnte.
## Funktionen
- **Hosts** anlegen/bearbeiten/löschen (Name, Host, Port, User, Auth-Art)
- **Authentifizierung** per Passwort oder per importiertem Private Key
- **Keys importieren** (OpenSSH-/PEM-/PKCS8-/Ed25519-Formate via sshj + Bouncy Castle)
- **Verbindung testen** direkt aus dem Host-Editor
- **Interaktives Terminal**: Compose-Canvas-Renderer auf der Termux-`terminal-emulator`
Engine (VT100/xterm, ANSI-Farben, Bold/Italic/Underline, Cursor, Alt-Buffer)
- Tastatur-Erweiterungsleiste (ESC, Tab, Backspace, Pfeile)
- Dunkles, terminal-orientiertes Theme
## Sicherheit
- Sensitive DB-Felder (Passwörter, private Keys) werden mit einem AES-GCM-Schlüssel aus
dem **Android Keystore** ver- und entschlüsselt.
- Klartext steht nur transient im Arbeitsspeicher (während Editieren/Verbinden).
- Host-Verifikation ist aktuell **promiscuous** (MVP) — Known-Hosts-Speicher ist ein
todo.
## Architektur
```
core/crypto KeystoreCrypto (AES-GCM, Android Keystore)
data Room (Host, SshKey, Group) + DAOs + Repositories + DI
domain/model AuthType
ssh SshConnectionManager (sshj), SshKeyParser, SshSupport (Bouncy Castle)
terminal SshTerminalBridge (sshj <-> TerminalEmulator), TerminalTextStyle, KeyEncoder
terminal/ui TerminalCanvas (Compose-Renderer), TerminalViewModel, TerminalScreen
com.termux.terminal.TerminalBufferAccess Adapter auf package-private Buffer-Felder
ui theme, nav, hostlist, hosteditor, keylist, keyimport, components
```
### Terminal-Pipeline
```
SSH-Server ──► sshj Shell.getInputStream()
│ (Reader-Thread)
TerminalEmulator.append(bytes) ← aktualisiert TerminalBuffer
▼ (Revision-Signal)
TerminalCanvas (Compose) liest über TerminalBufferAccess
Tastatur ──► KeyEncoder ──► SshTerminalBridge.send() ──► sshj Shell.getOutputStream()
```
Die Termux-Engine wird **direkt** (ohne `TerminalSession`, das lokale Prozesse startet)
genutzt. Da `TerminalBuffer.mLines`/`TerminalRow.mText` package-private sind, greift
`TerminalBufferAccess` als Klasse im `com.termux.terminal`-Package darauf zu.
## Stack
| Teil | Technologie |
|------|-------------|
| Sprache | Kotlin 2.0.21 (K2) |
| UI | Jetpack Compose (BOM 2024.10), Material 3 |
| DI | Hilt |
| DB | Room 2.6 |
| SSH | sshj 0.39 |
| Krypto | Bouncy Castle 1.78 (Key-Parsing) + Android Keystore (Feldverschlüsselung) |
| Terminal-Engine | Termux `terminal-emulator` 0.118.3 (via JitPack) |
| Build | AGP 8.7.3, Gradle 8.11.1, JDK 17 target / JDK 21 zum Bauen |
| SDK | compileSdk 34, minSdk 26 |
## Bauen
Voraussetzung: Android SDK mit `platforms;android-34` und `build-tools` sowie JDK 17/21.
```bash
# Mit dem Gradle-Wrapper (JDK 21 empfohlen):
JAVA_HOME=/pfad/zum/jdk-21 ./gradlew assembleDebug
# APK liegt dann hier:
app/build/outputs/apk/debug/app-debug.apk
```
Installieren auf einem verbundenen Gerät/Emulator:
```bash
./gradlew installDebug
# oder
adb install app/build/outputs/apk/debug/app-debug.apk
```
## Bekannte Einschränkungen / Todos
- **Passphrase-geschützte Keys**: Beim Import wird die Passphrase geprüft, für das Login
müssen Keys aktuell unverschlüsselt gespeichert werden (MVP).
- **Known-Hosts**: Host-Keys werden noch nicht verifiziert.
- **Hintergrund-Persistenz**: Sessions überleben Konfigurationswechsel (die Activity fängt
`configChanges` ab), aber für echtes Background-Keeping ist ein Foreground-Service
nötig (Folgearchitektur).
- **Terminal-Fidelity**: Ziel ist robuste Shell/`vim`/`htop`-Nutzung; exotische Features
(Sixel, Maus-Reporting, Unicode-Width-Polierung) sind nicht Teil des MVP.
- **Echtes 1Password**: erst möglich, wenn 1Password Android-Support anbietet.