This commit is contained in:
Andre
2026-10-22 21:00:05 +02:00
commit 9c278f49b6
20 changed files with 909 additions and 0 deletions
+340
View File
@@ -0,0 +1,340 @@
# Installation
Schritt-für-Schritt-Einrichtung von Server (Raspberry Pi) und Client (Windows).
Alle Befehle sind zum Kopieren gedacht. Platzhalter in `GROSSBUCHSTABEN`
ersetzen.
Dauer: etwa 20 Minuten.
---
## Voraussetzungen
**Server**
- Raspberry Pi oder anderer Linux-Rechner mit Docker und Docker Compose
(`docker compose version` funktioniert)
- [Nginx Proxy Manager](https://nginxproxymanager.com/) läuft dort in Docker
- Eine Domain zeigt auf deinen Internetanschluss. Im Router ist Port 443 an den
Pi weitergeleitet.
- Im Nginx Proxy Manager gibt es für diese Domain einen Proxy Host mit gültigem
SSL-Zertifikat. Ein neues Zertifikat bekommst du im Proxy Host über den Tab
**SSL** → *Request a new SSL Certificate*.
- SSH-Zugang zum Pi mit einem Benutzer, der `sudo` darf
**Client**
- Windows 10 oder 11 mit Google Chrome
- Dieses Projekt als Ordner auf dem PC
---
## Teil 1 — Server
### 1.1 Dateien auf den Pi kopieren
In PowerShell im Projektordner (dort, wo `README.md` liegt):
```powershell
$Pi = 'BENUTZER@PI-ADRESSE'
ssh $Pi 'mkdir -p ~/wstunnel && chmod 700 ~/wstunnel'
scp server\docker-compose.yml server\restrictions.example.yaml server\firewall\tunnler-firewall.sh server\firewall\tunnler-firewall.service "${Pi}:wstunnel/"
```
Danach am Pi anmelden. Alle folgenden Befehle in Teil 1 laufen dort:
```powershell
ssh $Pi
```
### 1.2 Netz des Proxy Managers ermitteln
Der Tunnel-Container kommt in dasselbe Docker-Netz wie der Nginx Proxy Manager
und bekommt dort eine feste IP-Adresse.
```bash
cd ~/wstunnel
NPM_CONTAINER=$(docker ps --format '{{.Names}} {{.Image}}' | awk '/nginx-proxy-manager/ {print $1; exit}')
NPM_NETWORK=$(docker inspect "$NPM_CONTAINER" --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' | head -n 1)
SUBNET=$(docker network inspect "$NPM_NETWORK" --format '{{(index .IPAM.Config 0).Subnet}}')
TUNNEL_IP=$(python3 -c 'import ipaddress, sys; print(ipaddress.ip_network(sys.argv[1])[-6])' "$SUBNET")
printf 'NPM_NETWORK=%s\nTUNNEL_IP=%s\n' "$NPM_NETWORK" "$TUNNEL_IP" > .env
echo "Proxy Manager: $NPM_CONTAINER | Netz: $NPM_NETWORK ($SUBNET) | Tunnel-IP: $TUNNEL_IP"
```
Die letzte Zeile muss einen Container, ein Netz und eine IP nennen, zum
Beispiel:
```
Proxy Manager: nginx-app-1 | Netz: nginx_default (172.20.0.0/16) | Tunnel-IP: 172.20.255.250
```
Ist ein Feld leer oder heißt das Netz `bridge`, siehe
[Fehlerbehebung](#fehlerbehebung).
### 1.3 Secret erzeugen
Das Secret ist das Passwort des Tunnels. Es wird Teil des URL-Pfads.
```bash
cd ~/wstunnel
SECRET=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 32)
sed "s/__SECRET__/$SECRET/" restrictions.example.yaml > restrictions.yaml
chmod 644 restrictions.yaml
echo "Secret: $SECRET"
```
Das Secret in einem Passwort-Manager speichern. Es wird in Schritt 1.6 und 2.2
gebraucht.
### 1.4 Tunnel-Server starten
```bash
cd ~/wstunnel
docker compose up -d
docker logs wstunnel --tail 5
```
Docker lädt das Image `ghcr.io/erebe/wstunnel` beim ersten Start selbst
herunter. Die Version steht in `docker-compose.yml`. In der Log-Ausgabe muss
folgende Zeile stehen:
```
Starting wstunnel server listening on 0.0.0.0:8090
```
### 1.5 Firewall einrichten
Die Regeln sperren für den Tunnel-Container alle privaten Netze. Über den Tunnel
ist damit weder das Heimnetz noch der Pi selbst erreichbar.
```bash
cd ~/wstunnel
. ./.env
echo "TUNNEL_IP=$TUNNEL_IP" | sudo tee /etc/default/tunnler-firewall
sudo install -m 755 tunnler-firewall.sh /usr/local/sbin/
sudo install -m 644 tunnler-firewall.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now tunnler-firewall
sudo iptables -S TUNNLER | head -n 3
```
Die Ausgabe beginnt mit `-N TUNNLER`. Die Regeln werden bei jedem Neustart
automatisch wieder gesetzt.
### 1.6 Nginx Proxy Manager konfigurieren
Die Werte für die Oberfläche anzeigen:
```bash
cd ~/wstunnel
. ./.env
SECRET=$(sed -n 's/.*PathPrefix "^\([A-Za-z0-9]*\)\$".*/\1/p' restrictions.yaml)
echo "Location: /$SECRET/"
echo "Forward: http $TUNNEL_IP 8090"
```
Im Nginx Proxy Manager **Hosts → Proxy Hosts** öffnen und beim Proxy Host der
Domain auf **Edit** klicken:
1. Tab **Details**: **Websockets Support** einschalten.
2. Tab **Custom Locations** → **Add location**:
| Feld | Wert |
|---|---|
| Location | `/SECRET/` aus der Ausgabe oben, mit beiden Schrägstrichen |
| Scheme | `http` |
| Forward Hostname / IP | Tunnel-IP aus der Ausgabe oben, ohne Schrägstrich |
| Forward Port | `8090` |
3. Beim Zahnrad der Location dieses Feld füllen:
```nginx
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
```
4. **Save**. Der Proxy Host muss danach als *Online* angezeigt werden.
Die bestehende Website unter `/` bleibt unverändert.
---
## Teil 2 — Client
### 2.1 Ordner ablegen
In PowerShell im Projektordner:
```powershell
New-Item -ItemType Directory -Force C:\Tools | Out-Null
Copy-Item -Recurse client C:\Tools\tunnler
```
Ein anderer Zielordner geht genauso. Admin-Rechte sind nicht nötig.
`wstunnel.exe` lädt der erste Start von GitHub nach `bin\` und prüft dabei die
SHA-256-Prüfsumme.
### 2.2 Konfiguration anlegen
```powershell
cd C:\Tools\tunnler
Copy-Item config.example.ps1 config.ps1
notepad config.ps1
```
In `config.ps1` zwei Werte eintragen und speichern:
```powershell
$TunnelSecret = 'SECRET'
$TunnelServer = 'wss://DOMAIN:443'
```
### 2.3 Server-Weg prüfen
```powershell
curl.exe -s -o NUL -w "%{http_code}`n" https://DOMAIN/SECRET/events
```
| Ergebnis | Bedeutung |
|---|---|
| `400` | Richtig: Die Anfrage erreicht den Tunnel-Server |
| `404` | Die Custom Location fehlt oder ist falsch geschrieben (1.6) |
| `502` | Der Container läuft nicht, oder die Forward-IP ist falsch (1.4, 1.6) |
### 2.4 Tunnel testen
`Chrome-Tunnel.cmd` doppelklicken. Es öffnet sich ein eigenes Chrome-Fenster.
Danach in PowerShell:
```powershell
cd C:\Tools\tunnler
.\Tunnel-Status.cmd
```
Die angezeigte IP muss die IP des Internetanschlusses zu Hause sein. Für einen
echten Test den PC mit einem Handy-Hotspot verbinden: Die IP darf dann nicht die
des Hotspots sein.
### 2.5 `codexx` einrichten
`PATH-eintragen.cmd` doppelklicken. Der Client-Ordner landet im Benutzer-PATH,
Admin-Rechte sind nicht nötig. Danach VS Code und alle Terminals komplett neu
starten. In jedem Projektordner startet `codexx` statt `codex` Codex über den
Tunnel.
Nach dem Verschieben des Client-Ordners den alten Eintrag unter
*Umgebungsvariablen für dieses Konto bearbeiten* → `Path` löschen und
`PATH-eintragen.cmd` erneut ausführen.
---
## Teil 3 — Einsatz hinter einem Firmen-Proxy
Netze mit erzwungenem Proxy lassen keine direkte Verbindung zu. Der Tunnel muss
dort durch den Proxy.
### 3.1 Proxy-Adresse herausfinden
```powershell
(Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings").AutoConfigURL
```
Die angezeigte URL im Browser öffnen. In der PAC-Datei nach einer Zeile wie
`PROXY proxy.firma.de:8080` suchen.
### 3.2 Proxy eintragen
In `config.ps1`:
```powershell
$UpstreamProxy = 'http://PROXY-HOST:PROXY-PORT'
```
Verlangt der Proxy eine Anmeldung, zusätzlich `$UpstreamProxyUser` und
`$UpstreamProxyPassword` setzen. Danach `Tunnel-Stop.cmd` ausführen und
`Chrome-Tunnel.cmd` neu starten.
### 3.3 Wenn der Proxy TLS aufbricht
In Edge `https://DOMAIN` öffnen, auf das Schloss klicken und das Zertifikat
ansehen. Ist der Aussteller nicht *Let's Encrypt*, bricht der Proxy die
Verschlüsselung auf. Der Tunnel scheitert dann an der Zertifikatsprüfung. Nur
in diesem Fall in `config.ps1` setzen:
```powershell
$SkipTlsVerify = $true
```
Der Proxy sieht dann das Secret. Nach dem Einsatz das Secret tauschen.
---
## Secret tauschen
Auf dem Pi:
```bash
cd ~/wstunnel
OLD=$(sed -n 's/.*PathPrefix "^\([A-Za-z0-9]*\)\$".*/\1/p' restrictions.yaml)
NEW=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 32)
sed -i "s/$OLD/$NEW/" restrictions.yaml
echo "Neues Secret: $NEW"
```
Der Server lädt die Datei automatisch neu. Danach:
1. Im Nginx Proxy Manager die Custom Location auf `/NEUES-SECRET/` ändern und
speichern.
2. Auf jedem PC in `config.ps1` `$TunnelSecret` ersetzen, dann `Tunnel-Stop.cmd`
ausführen.
---
## Aktualisieren
In `~/wstunnel/docker-compose.yml` die Image-Version erhöhen
([Releases](https://github.com/erebe/wstunnel/releases)), dann:
```bash
cd ~/wstunnel
docker compose pull
docker compose up -d
```
Den Client auf dieselbe Version bringen: In `client\scripts\common.ps1`
`$WstunnelVersion` anpassen, `Tunnel-Stop.cmd` ausführen und
`client\bin\wstunnel.exe` löschen. Der nächste Start lädt die neue Version.
---
## Deinstallieren
1. Im Nginx Proxy Manager die Custom Location entfernen.
2. Auf dem Pi:
```bash
cd ~/wstunnel && docker compose down
sudo systemctl disable --now tunnler-firewall
sudo rm /etc/systemd/system/tunnler-firewall.service /usr/local/sbin/tunnler-firewall.sh /etc/default/tunnler-firewall
sudo systemctl daemon-reload
rm -r ~/wstunnel
```
3. Auf dem PC den Client-Ordner löschen. Es wurde nichts installiert.
---
## Fehlerbehebung
| Symptom | Lösung |
|---|---|
| 1.2: `Proxy Manager:` ist leer | Der Container heißt anders. `docker ps` ausführen und den Namen von Hand setzen: `NPM_CONTAINER=NAME`, dann ab der zweiten Zeile wiederholen. |
| 1.2: Netz heißt `bridge` | Im Standardnetz sind keine festen IPs möglich. Den Proxy Manager in ein eigenes Netz verschieben, zum Beispiel per Docker Compose. |
| 1.4: `Address already in use` / `invalid address` | Die Tunnel-IP ist belegt oder liegt außerhalb des Subnetzes. In `~/wstunnel/.env` eine andere freie IP aus dem Subnetz eintragen und 1.4 bis 1.6 wiederholen. |
| 1.6: Proxy Host *Offline* | Die Location in NPM löschen und Schreibweise prüfen. Im Feld *Forward Hostname* darf kein Pfad stehen. |
| Client: `Tunnel-Client lauscht nicht` | `logs\wstunnel.err.log` im Client-Ordner lesen. Ein anderes Programm nutzt eventuell Port 1080; dann `$LocalProxy` in `config.ps1` ändern. |
| Client verbindet nicht, Timeout | Im Firmennetz fehlt `$UpstreamProxy` (3.2). Sonst Portweiterleitung und DNS der Domain prüfen. |
| Seite lädt nicht, Client-Log zeigt `400` | Das Ziel ist gesperrt (nur Ports 80/443, keine privaten Adressen). Den Grund zeigt `docker logs wstunnel` auf dem Pi in der Zeile `Rejecting …`. |
| Codex nutzt den Tunnel nicht | Codex mit `codexx` starten, nicht direkt mit `codex`. Fehlt der Befehl, `PATH-eintragen.cmd` ausführen (2.5). |