Files
tunnler/docs/installation.md
2026-10-22 21:00:05 +02:00

10 KiB

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 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):

$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:

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.

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.

1.3 Secret erzeugen

Das Secret ist das Passwort des Tunnels. Es wird Teil des URL-Pfads.

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

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.

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:

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:

    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:

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

cd C:\Tools\tunnler
Copy-Item config.example.ps1 config.ps1
notepad config.ps1

In config.ps1 zwei Werte eintragen und speichern:

$TunnelSecret = 'SECRET'
$TunnelServer = 'wss://DOMAIN:443'

2.3 Server-Weg prüfen

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:

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

(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:

$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:

$SkipTlsVerify = $true

Der Proxy sieht dann das Secret. Nach dem Einsatz das Secret tauschen.


Secret tauschen

Auf dem Pi:

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), dann:

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:

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