first commit

This commit is contained in:
duffyduck
2026-10-16 15:55:14 +02:00
commit 65e8b9440d
62 changed files with 4799 additions and 0 deletions
+177
View File
@@ -0,0 +1,177 @@
# NetClip – Netzwerk-Zwischenablage
Eine gemeinsame Zwischenablage für alle eigenen Geräte (Windows, Linux, macOS, Android) – im Stil von
CopyQ, aber über das Netzwerk. Text, **Bilder** und **Dateien/Ordner** werden übertragen: Wird auf dem
einen Rechner ein Ordner kopiert (Strg+C im Explorer/Finder/Dolphin), fügt Strg+V auf dem anderen Rechner
den Ordner in den gerade geöffneten Zielordner ein. Mit Verlauf, um Einträge später erneut einzufügen.
```
PC (Windows) Laptop (Linux) Mac Handy (Android)
│ │ │ │
└──────── WebSocket + HTTPS (Ende-zu-Ende verschlüsselt) ───┘
│
NetClip-Server (Docker)
verbindet nur Clients mit gleichem Token
```
## Funktionsweise
- Alle Geräte mit **demselben Token** bilden einen gemeinsamen „Raum“.
- Der Token verlässt das Gerät nie. Aus ihm werden per PBKDF2 eine Raum-ID (sieht der Server) und ein
AES-256-Schlüssel abgeleitet (sieht der Server nicht). Inhalte, Dateinamen, Vorschauen und Gerätenamen
sind **Ende-zu-Ende verschlüsselt** – der Server speichert und verteilt nur undurchsichtige Blobs.
- Der Server hält einen Verlauf pro Raum (Anzahl, Alter, Speicherplatz begrenzt, siehe `.env`).
- Optional schützt ein `SERVER_KEY` den Server davor, von Fremden benutzt zu werden.
## Server (Docker)
```bash
cp .env.example .env # Werte anpassen (CERT_CN, CERT_SAN, SERVER_KEY, ...)
docker compose up -d --build
```
Die `.env` ist kommentiert. Die wichtigsten Schalter:
| Variable | Bedeutung |
|---|---|
| `COMPOSE_PROFILES=nginx` | nginx als HTTPS-Proxy mit selbstsigniertem Zertifikat mitstarten. **Leer lassen**, wenn schon ein Reverse-Proxy vorhanden ist (siehe [docs/eigener-reverse-proxy.md](docs/eigener-reverse-proxy.md)). |
| `CERT_CN`, `CERT_SAN` | Hostname bzw. weitere Namen/IPs im Zertifikat |
| `CERT_DAYS`, `CERT_RENEW_DAYS` | Gültigkeit; so viele Tage vor Ablauf wird beim Containerstart automatisch erneuert |
| `CERT_KEY_BITS`, `CERT_COUNTRY` … `CERT_OU` | weitere Zertifikatsfelder |
| `SERVER_KEY` | optionaler Zugangsschlüssel für den Server |
| `MAX_CLIP_MB` | maximale Größe eines Clips |
| `HISTORY_MAX`, `HISTORY_DAYS`, `ROOM_QUOTA_MB` | Verlaufsgrenzen pro Token |
| `BIND_ADDR`, `PORT` | direkter HTTP-Port des Servers (mit nginx am besten `BIND_ADDR=127.0.0.1`) |
Ändert man `CERT_*`-Werte, wird das Zertifikat beim nächsten Start neu erzeugt. Eigene Zertifikate:
`fullchain.pem` + `privkey.pem` nach `./certs` legen und `./certs/.selfsigned` löschen.
Selbstsignierte Zertifikate in den Clients: entweder „TLS-Zertifikat prüfen“ abschalten (nur im eigenen
Netz) oder im Desktop-Client unter „Zertifikat“ die `certs/fullchain.pem` des Servers hinterlegen.
> Mit dem alten `docker-compose` (v1) funktioniert es genauso: `docker-compose up -d --build`.
## Desktop-Client (Windows, Linux, macOS)
Python + Qt (PySide6), eine Codebasis für alle drei Systeme. Läuft im System-Tray.
- **Tray-Menü:** die letzten 10 Einträge zum schnellen Einfügen, Verlauf, Pausieren, Einstellungen
- **Verlaufsfenster:** Suche, Doppelklick/Enter = in die Zwischenablage, „Speichern unter …“, Löschen
- **Einstellungen:** Server, Token (**Erzeugen**), Server-Schlüssel, Zertifikat, Gerätename, was
synchronisiert wird, Größenlimits, Autostart
- **Verbindung teilen:** *QR-Code für Android* · *Exportieren* (.netclip-Datei) · *Importieren*
- Passwort-Manager-Einträge (KeePassXC, Bitwarden, 1Password …), die als „geheim“ markiert sind, werden
nicht synchronisiert.
Empfangene Dateien/Ordner werden in den Cache entpackt
(`%LOCALAPPDATA%\NetClip\cache`, `~/Library/Caches/NetClip`, `~/.cache/netclip`) und als Datei-Referenz
in die Zwischenablage gelegt – Strg+V im Dateimanager kopiert sie dann an den gewünschten Ort.
Der Cache wird nach `keep_received_days` (Standard 7 Tage) aufgeräumt.
### Starten aus dem Quellcode
```bash
cd client-desktop
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python -m netclip
```
### Pakete bauen
PyInstaller baut immer für das System, auf dem es läuft – also einmal je Betriebssystem:
```bash
cd client-desktop
python build.py
```
| System | Ergebnis |
|---|---|
| Windows | `dist/NetClip/NetClip.exe`, danach `packaging/windows-installer.iss` mit Inno Setup kompilieren → `dist/NetClip-Setup-1.0.0.exe` |
| macOS | `dist/NetClip.app` und `dist/NetClip-macos.zip` (Menüleisten-App ohne Dock-Symbol) |
| Linux | `dist/NetClip-linux-x86_64.tar.gz` mit `install-linux.sh` |
### Ausrollen
Kommandozeile (alle Systeme):
```bash
NetClip --configure-only --server https://clip.example.de --token XYZ [--server-key K] [--insecure] [--autostart]
NetClip --import verbindung.netclip
NetClip --show # Verlauf der laufenden Instanz öffnen – z.B. auf ein Tastenkürzel legen
```
Vorgaben für alle Benutzer: eine `config.default.json` neben die Programmdatei legen, z.B.
`{"server": "https://clip.example.de", "token": "...", "verify_tls": false}`. Sie wird beim ersten
Start jedes Benutzers übernommen (Gerätename und Geräte-ID bleiben pro Gerät eindeutig).
- **Windows (GPO/Intune/PDQ):**
`NetClip-Setup-1.0.0.exe /VERYSILENT /SUPPRESSMSGBOXES /SERVER=https://clip.example.de /TOKEN=xyz [/SERVERKEY=k] [/INSECURE=1]`
– schreibt `config.default.json` und richtet den Autostart für alle Benutzer ein.
- **Linux:** `sudo ./install-linux.sh --server https://clip.example.de --token xyz [--insecure]`
- **macOS:** `NetClip.app` nach `/Applications` kopieren, `config.default.json` nach
`NetClip.app/Contents/MacOS/` legen.
Hinweis: Wer den Token kennt, sieht die Zwischenablage. Ein gemeinsamer Token für mehrere Personen
bedeutet eine gemeinsame Zwischenablage – pro Person/Team einen eigenen Token verwenden.
### Plattform-Hinweise
- **Linux/X11:** funktioniert direkt (Dolphin, Nautilus, Nemo, Thunar, Caja).
- **Linux/Wayland:** Wayland gibt Programmen im Hintergrund keinen Zugriff auf die Zwischenablage.
NetClip nutzt dann automatisch `wl-clipboard` (`apt install wl-clipboard`). Das funktioniert unter
KDE Plasma und wlroots-Desktops (Sway, Hyprland), **nicht unter GNOME** – dort eine X11-Sitzung nutzen.
Mit `NETCLIP_BACKEND=qt` lässt sich das Qt-Backend erzwingen.
- **macOS:** beim ersten Start ggf. in den Systemeinstellungen „Öffnen“ bestätigen (unsigniert).
- **Windows:** keine Besonderheiten; Dateien werden als „Kopieren“ (nicht „Ausschneiden“) eingefügt.
## Android-Client
Kotlin, ab Android 8. Läuft als Vordergrunddienst mit dauerhafter Benachrichtigung.
- **Einrichten:** Einstellungen → *QR-Code vom PC scannen* (Desktop: Einstellungen → QR-Code für
Android) oder *.netclip-Datei importieren*. Ein `netclip://`-Link aus einer beliebigen QR-App
öffnet ebenfalls direkt die Einstellungen.
- **Empfangen:** Text und Bilder landen automatisch in der Zwischenablage. Dateien/Ordner kommen per
Benachrichtigung mit *Speichern* (→ `Download/NetClip/`, Ordnerstruktur bleibt erhalten) und *Teilen*.
- **Senden:** Android erlaubt Apps im Hintergrund nicht, die Zwischenablage zu lesen. Deshalb:
- Button *Zwischenablage senden* in der App oder in der Benachrichtigung
- Schnelleinstellungs-Kachel „Zwischenablage senden“ (Kacheln bearbeiten → hinzufügen)
- *Teilen → An NetClip senden* aus jeder App (Text, Bilder, mehrere Dateien)
- Text markieren → *An NetClip senden* im Auswahlmenü
- **Verlauf:** antippen = in die Zwischenablage; lange drücken = Speichern, Teilen, Löschen.
### Bauen
```bash
cd client-android
./gradlew assembleRelease # app/build/outputs/apk/release/app-release.apk
```
Das Release-APK ist zum Testen mit dem Debug-Schlüssel signiert; für die Verteilung in `app/build.gradle`
einen eigenen Keystore eintragen.
## Tests
```bash
# Kompatibilität der Verschlüsselung Python <-> Kotlin
python tools/interop.py gen /tmp/interop
(cd client-android && ./gradlew testDebugUnitTest -Pinterop=/tmp/interop)
python tools/interop.py check /tmp/interop
```
## Protokoll (für eigene Clients)
| Methode | Pfad | Beschreibung |
|---|---|---|
| GET | `/healthz` | Status |
| GET | `/api/v1/info` | Server-Limits |
| GET | `/api/v1/rooms/{room}/clips` | Verlauf (`id`, `ts`, `device`, `size`, `meta`) |
| POST | `/api/v1/rooms/{room}/clips` | Body: 4 Byte Länge (BE) + Meta (Base64) + verschlüsselter Blob; Header `X-Device` |
| GET | `/api/v1/rooms/{room}/clips/{id}/blob` | verschlüsselten Inhalt laden |
| DELETE | `/api/v1/rooms/{room}/clips[/{id}]` | Verlauf leeren bzw. Eintrag löschen |
| WS | `/api/v1/rooms/{room}/ws?device=…` | Ereignisse `hello`, `clip`, `deleted`, `cleared`, `devices` |
Mit `SERVER_KEY` ist bei allen `/api/`-Aufrufen `Authorization: Bearer <key>` nötig.
Verschlüsselungsformat: siehe Kopf von [client-desktop/netclip/crypto.py](client-desktop/netclip/crypto.py).