mirror of
https://github.com/Andre-Bzk/tunnler.git
synced 2026-11-15 06:23:04 +01:00
git init
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
* text=auto
|
||||
|
||||
# Laufen auf dem Pi: CRLF wuerde Shell-Skript und systemd-Unit brechen
|
||||
*.sh text eol=lf
|
||||
*.service text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.yaml text eol=lf
|
||||
|
||||
# Batch-Dateien unter Windows immer mit CRLF
|
||||
*.cmd text eol=crlf
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
# Enthalten das Tunnel-Secret
|
||||
client/config.ps1
|
||||
server/restrictions.yaml
|
||||
|
||||
# Installationsspezifisch
|
||||
server/.env
|
||||
|
||||
# Wird beim ersten Start von GitHub geladen
|
||||
client/bin/
|
||||
|
||||
# Laufzeitdaten
|
||||
client/logs/
|
||||
client/chrome-profile/
|
||||
|
||||
# Interne Notizen (Heim-IP, Domain, NPM-Details)
|
||||
notes/
|
||||
|
||||
# Editor und Windows
|
||||
.vscode/
|
||||
.idea/
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
*.bak
|
||||
@@ -0,0 +1,127 @@
|
||||
# Tunnler
|
||||
|
||||
[English](#english) | [Deutsch](#deutsch)
|
||||
|
||||
---
|
||||
|
||||
## English
|
||||
|
||||
> **Disclaimer:** This tool was vibe coded with Claude Code — architecture and structure were planned together with AI, and all implementation was done through prompts. Not a single line was written by hand.
|
||||
|
||||
Routes only Chrome and Codex through a wstunnel server on a home Raspberry Pi, even behind a corporate proxy that blocks sites.
|
||||
|
||||
Unlike a web proxy, the tunnel passes raw TCP connections. TLS ends at the real server, so Google and ChatGPT logins work.
|
||||
|
||||
### Features
|
||||
|
||||
- Local proxy on `127.0.0.1:1080`, used only by a dedicated Chrome profile and Codex
|
||||
- Tunnel as WebSocket over `wss://` on port 443, optionally through a corporate proxy
|
||||
- Access only via a secret URL path
|
||||
- Server allows only TCP 80/443 to public destinations, the home network is blocked
|
||||
- Client needs no admin rights and runs on Windows PowerShell 5.1
|
||||
|
||||
### Installation
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Raspberry Pi with Docker and Nginx Proxy Manager, own domain with SSL certificate
|
||||
- Windows 10/11 with Google Chrome
|
||||
|
||||
Step-by-step guide for server and client (German): [docs/installation.md](docs/installation.md)
|
||||
|
||||
### Usage
|
||||
|
||||
Double-click `client\Chrome-Tunnel.cmd`. The tunnel starts hidden in the background, then a separate Chrome window opens that runs entirely through the Pi.
|
||||
|
||||
Run `client\PATH-eintragen.cmd` once. Afterwards type `codexx` instead of `codex` in any project folder, for example in the VS Code terminal. Arguments are passed through.
|
||||
|
||||
`Tunnel-Start.cmd` starts only the tunnel. Then set `127.0.0.1:1080` as HTTP proxy in any program. `Tunnel-Status.cmd` shows the public IP through the tunnel. `Tunnel-Stop.cmd` stops the tunnel.
|
||||
|
||||
### Configuration
|
||||
|
||||
All client values live in `client\config.ps1` (copy from `config.example.ps1`).
|
||||
|
||||
| Variable | Meaning | Default |
|
||||
|---|---|---|
|
||||
| `$TunnelSecret` | Secret path, identical to the server | empty |
|
||||
| `$TunnelServer` | Server address | `wss://deine-domain.de:443` |
|
||||
| `$LocalProxy` | Local proxy for Chrome and Codex | `127.0.0.1:1080` |
|
||||
| `$UpstreamProxy` | Corporate proxy, empty = direct | empty |
|
||||
| `$UpstreamProxyUser` / `$UpstreamProxyPassword` | Corporate proxy login | empty |
|
||||
| `$SkipTlsVerify` | Disable certificate check, only if the proxy breaks TLS | `$false` |
|
||||
|
||||
After a change, run `client\Tunnel-Stop.cmd` and start the tunnel again.
|
||||
|
||||
### Known blockers
|
||||
|
||||
- On a corporate network the tunnel times out without `$UpstreamProxy`. See [corporate proxy](docs/installation.md#teil-3--einsatz-hinter-einem-firmen-proxy).
|
||||
- The first start downloads `wstunnel.exe` from GitHub. Without access to github.com, place it in `client\bin\` manually.
|
||||
- Private addresses such as `192.168.x.x` are blocked through the tunnel. The client log then shows `Invalid status code: 400`. Add them to the program's proxy exceptions.
|
||||
- `client\config.ps1` and `server/restrictions.yaml` contain the secret. Do not share or commit them.
|
||||
|
||||
### Further documentation
|
||||
|
||||
- [docs/installation.md](docs/installation.md) — installation, corporate proxy, rotating the secret, updating, troubleshooting (German)
|
||||
- [wstunnel](https://github.com/erebe/wstunnel) — the tunnel software used
|
||||
|
||||
---
|
||||
|
||||
## Deutsch
|
||||
|
||||
> **Hinweis:** Dieses Tool wurde mit Claude Code vibe coded — Architektur und Struktur wurden gemeinsam mit KI geplant, die gesamte Implementierung erfolgte per Prompt. Keine einzige Zeile wurde von Hand geschrieben.
|
||||
|
||||
Leitet nur Chrome und Codex über einen wstunnel-Server auf dem Raspberry Pi zu Hause ins Internet, auch hinter einem Firmen-Proxy, der Seiten sperrt.
|
||||
|
||||
Anders als ein Web-Proxy reicht der Tunnel reine TCP-Verbindungen durch. TLS endet beim echten Server, daher funktionieren Google- und ChatGPT-Logins.
|
||||
|
||||
### Funktionen
|
||||
|
||||
- Lokaler Proxy auf `127.0.0.1:1080`, den nur ein eigenes Chrome-Profil und Codex nutzen
|
||||
- Tunnel als WebSocket über `wss://` auf Port 443, optional durch einen Firmen-Proxy
|
||||
- Zugang nur mit geheimem URL-Pfad
|
||||
- Server erlaubt nur TCP 80/443 zu öffentlichen Zielen, das Heimnetz ist gesperrt
|
||||
- Client ohne Admin-Rechte, läuft mit Windows PowerShell 5.1
|
||||
|
||||
### Installation
|
||||
|
||||
Voraussetzungen:
|
||||
|
||||
- Raspberry Pi mit Docker und Nginx Proxy Manager, eigene Domain mit SSL-Zertifikat
|
||||
- Windows 10/11 mit Google Chrome
|
||||
|
||||
Schritt-für-Schritt-Anleitung für Server und Client: [docs/installation.md](docs/installation.md)
|
||||
|
||||
### Nutzung
|
||||
|
||||
`client\Chrome-Tunnel.cmd` doppelklicken. Der Tunnel startet unsichtbar im Hintergrund, danach öffnet sich ein eigenes Chrome-Fenster, das komplett über den Pi läuft.
|
||||
|
||||
Einmal `client\PATH-eintragen.cmd` ausführen. Danach in jedem Projektordner `codexx` statt `codex` eingeben, zum Beispiel im VS-Code-Terminal. Argumente werden durchgereicht.
|
||||
|
||||
`Tunnel-Start.cmd` startet nur den Tunnel. Danach `127.0.0.1:1080` in beliebigen Programmen als HTTP-Proxy eintragen. `Tunnel-Status.cmd` zeigt die öffentliche IP über den Tunnel. `Tunnel-Stop.cmd` beendet den Tunnel.
|
||||
|
||||
### Konfiguration
|
||||
|
||||
Alle Client-Werte stehen in `client\config.ps1` (Vorlage: `config.example.ps1`).
|
||||
|
||||
| Variable | Bedeutung | Standard |
|
||||
|---|---|---|
|
||||
| `$TunnelSecret` | Geheimer Pfad, identisch mit dem Server | leer |
|
||||
| `$TunnelServer` | Adresse des Servers | `wss://deine-domain.de:443` |
|
||||
| `$LocalProxy` | Lokaler Proxy für Chrome und Codex | `127.0.0.1:1080` |
|
||||
| `$UpstreamProxy` | Firmen-Proxy, leer = direkt | leer |
|
||||
| `$UpstreamProxyUser` / `$UpstreamProxyPassword` | Anmeldung am Firmen-Proxy | leer |
|
||||
| `$SkipTlsVerify` | Zertifikatsprüfung abschalten, nur bei TLS-Aufbruch | `$false` |
|
||||
|
||||
Nach einer Änderung `client\Tunnel-Stop.cmd` ausführen und den Tunnel neu starten.
|
||||
|
||||
### Bekannte Stolpersteine
|
||||
|
||||
- Im Firmennetz läuft der Tunnel ohne `$UpstreamProxy` in einen Timeout. Siehe [Einsatz hinter einem Firmen-Proxy](docs/installation.md#teil-3--einsatz-hinter-einem-firmen-proxy).
|
||||
- Der erste Start lädt `wstunnel.exe` von GitHub. Ohne Zugriff auf github.com die Datei von Hand nach `client\bin\` legen.
|
||||
- Private Adressen wie `192.168.x.x` sind über den Tunnel gesperrt. Das Client-Log zeigt dann `Invalid status code: 400`. Solche Adressen im Programm als Proxy-Ausnahme eintragen.
|
||||
- `client\config.ps1` und `server/restrictions.yaml` enthalten das Secret. Nicht weitergeben und nicht committen.
|
||||
|
||||
### Weitere Dokumentation
|
||||
|
||||
- [docs/installation.md](docs/installation.md) — Installation, Firmen-Proxy, Secret tauschen, Aktualisieren, Fehlerbehebung
|
||||
- [wstunnel](https://github.com/erebe/wstunnel) — verwendete Tunnel-Software
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\chrome.ps1"
|
||||
if errorlevel 1 pause
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\path.ps1"
|
||||
pause
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\tunnel.ps1" start
|
||||
pause
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\tunnel.ps1" status
|
||||
pause
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\tunnel.ps1" stop
|
||||
pause
|
||||
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\codex.ps1" %*
|
||||
@@ -0,0 +1,19 @@
|
||||
# Als config.ps1 speichern und ausfuellen.
|
||||
# config.ps1 enthaelt das Tunnel-Secret: nicht weitergeben, nicht committen.
|
||||
|
||||
# Geheimer Pfad, identisch mit server/restrictions.yaml und der NPM-Location
|
||||
$TunnelSecret = ''
|
||||
$TunnelServer = 'wss://deine-domain.de:443'
|
||||
|
||||
# Lokaler Proxy, auf den Chrome und Codex zeigen
|
||||
$LocalProxy = '127.0.0.1:1080'
|
||||
|
||||
# Firmen-Proxy aus der PAC-Datei, z. B. 'http://proxy.firma.de:8080'.
|
||||
# Leer = direkte Verbindung (zu Hause, Hotspot).
|
||||
$UpstreamProxy = ''
|
||||
$UpstreamProxyUser = ''
|
||||
$UpstreamProxyPassword = ''
|
||||
|
||||
# Nur auf $true setzen, wenn der Firmen-Proxy TLS aufbricht
|
||||
# (Zertifikat der eigenen Domain im Browser nicht von Let's Encrypt).
|
||||
$SkipTlsVerify = $false
|
||||
@@ -0,0 +1,19 @@
|
||||
# Startet den Tunnel (falls noetig) und ein eigenes Chrome-Profil, das komplett
|
||||
# ueber den Tunnel laeuft. Normales Chrome und alle anderen Programme bleiben direkt.
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
Start-Tunnel
|
||||
|
||||
$chrome = @(
|
||||
"$env:ProgramFiles\Google\Chrome\Application\chrome.exe",
|
||||
"${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe",
|
||||
"$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe"
|
||||
) | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||||
if (-not $chrome) { throw "chrome.exe nicht gefunden." }
|
||||
|
||||
$profileDir = Join-Path $ClientDir 'chrome-profile'
|
||||
Start-Process -FilePath $chrome -ArgumentList @(
|
||||
"--proxy-server=$ProxyUrl",
|
||||
"--user-data-dir=`"$profileDir`"",
|
||||
'--no-first-run'
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Startet den Tunnel (falls noetig) und Codex mit Proxy-Variablen nur fuer diesen Aufruf.
|
||||
# Alle Argumente werden an codex durchgereicht.
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
Start-Tunnel
|
||||
|
||||
$names = 'HTTPS_PROXY', 'HTTP_PROXY', 'NO_PROXY'
|
||||
$saved = @{}
|
||||
foreach ($n in $names) { $saved[$n] = [Environment]::GetEnvironmentVariable($n, 'Process') }
|
||||
try {
|
||||
$env:HTTPS_PROXY = $ProxyUrl
|
||||
$env:HTTP_PROXY = $ProxyUrl
|
||||
$env:NO_PROXY = 'localhost,127.0.0.1'
|
||||
& codex @args
|
||||
} finally {
|
||||
foreach ($n in $names) { [Environment]::SetEnvironmentVariable($n, $saved[$n], 'Process') }
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
# Gemeinsame Funktionen fuer die Tunnel-Skripte. Wird per Dot-Sourcing geladen.
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ClientDir = Split-Path $PSScriptRoot -Parent
|
||||
$ConfigPath = Join-Path $ClientDir 'config.ps1'
|
||||
if (-not (Test-Path $ConfigPath)) {
|
||||
throw "config.ps1 fehlt. config.example.ps1 nach config.ps1 kopieren und ausfuellen."
|
||||
}
|
||||
. $ConfigPath
|
||||
|
||||
$WstunnelVersion = '11.1.0' # passend zum Server-Image in server/docker-compose.yml
|
||||
$Exe = Join-Path $ClientDir 'bin\wstunnel.exe'
|
||||
$LogDir = Join-Path $ClientDir 'logs'
|
||||
$ProxyUrl = "http://$LocalProxy"
|
||||
|
||||
function Get-TunnelArgs([string]$LogLevel) {
|
||||
$tunnelArgs = @('client', '-L', "http://$LocalProxy", '-c', '2', '--log-lvl', $LogLevel)
|
||||
if (-not $SkipTlsVerify) { $tunnelArgs += '--tls-verify-certificate' }
|
||||
if ($UpstreamProxy) { $tunnelArgs += @('--http-proxy', $UpstreamProxy) }
|
||||
if ($UpstreamProxyUser) { $tunnelArgs += @('--http-proxy-login', $UpstreamProxyUser) }
|
||||
$tunnelArgs += $TunnelServer
|
||||
return $tunnelArgs
|
||||
}
|
||||
|
||||
# Secret und Proxy-Passwort gehen per Umgebung an wstunnel, damit sie nicht in
|
||||
# der Kommandozeile stehen (Prozesslisten, EDR-Protokolle). HTTP_PROXY wird
|
||||
# entfernt, weil wstunnel es sonst als Upstream-Proxy uebernimmt. NO_COLOR liest
|
||||
# wstunnel ebenfalls und akzeptiert nur true/false (ein gesetztes NO_COLOR=1 bricht den Start).
|
||||
function Invoke-WithTunnelEnv([scriptblock]$Action) {
|
||||
$names = 'WSTUNNEL_HTTP_UPGRADE_PATH_PREFIX', 'WSTUNNEL_HTTP_PROXY_PASSWORD', 'HTTP_PROXY', 'NO_COLOR'
|
||||
$saved = @{}
|
||||
foreach ($n in $names) { $saved[$n] = [Environment]::GetEnvironmentVariable($n, 'Process') }
|
||||
try {
|
||||
$env:NO_COLOR = 'true'
|
||||
$env:WSTUNNEL_HTTP_UPGRADE_PATH_PREFIX = $TunnelSecret
|
||||
if ($UpstreamProxyPassword) { $env:WSTUNNEL_HTTP_PROXY_PASSWORD = $UpstreamProxyPassword }
|
||||
Remove-Item Env:HTTP_PROXY -ErrorAction SilentlyContinue
|
||||
& $Action
|
||||
} finally {
|
||||
foreach ($n in $names) { [Environment]::SetEnvironmentVariable($n, $saved[$n], 'Process') }
|
||||
}
|
||||
}
|
||||
|
||||
function Test-TunnelListening {
|
||||
$parts = $LocalProxy.Split(':')
|
||||
$client = New-Object System.Net.Sockets.TcpClient
|
||||
try { $client.Connect($parts[0], [int]$parts[1]); return $true }
|
||||
catch { return $false }
|
||||
finally { $client.Close() }
|
||||
}
|
||||
|
||||
function Get-TunnelProcess {
|
||||
Get-Process wstunnel -ErrorAction SilentlyContinue | Where-Object { $_.Path -eq $Exe }
|
||||
}
|
||||
|
||||
# wstunnel.exe liegt nicht im Repo. Beim ersten Start wird das offizielle Release
|
||||
# geladen und gegen die checksums.txt des Releases geprueft.
|
||||
function Install-Wstunnel {
|
||||
if (Test-Path $Exe) { return }
|
||||
$base = "https://github.com/erebe/wstunnel/releases/download/v$WstunnelVersion"
|
||||
$archiveName = "wstunnel_${WstunnelVersion}_windows_amd64.tar.gz"
|
||||
$tmp = Join-Path ([IO.Path]::GetTempPath()) "tunnler-$([guid]::NewGuid())"
|
||||
New-Item -ItemType Directory -Force $tmp | Out-Null
|
||||
$oldProgress = $ProgressPreference
|
||||
try {
|
||||
Write-Host "Lade wstunnel $WstunnelVersion ..."
|
||||
$ProgressPreference = 'SilentlyContinue' # sonst ist Invoke-WebRequest in PS 5.1 sehr langsam
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$archive = Join-Path $tmp $archiveName
|
||||
$sums = Join-Path $tmp 'checksums.txt'
|
||||
Invoke-WebRequest "$base/$archiveName" -OutFile $archive -UseBasicParsing
|
||||
Invoke-WebRequest "$base/checksums.txt" -OutFile $sums -UseBasicParsing
|
||||
$line = Get-Content $sums | Where-Object { $_ -match "\s$([regex]::Escape($archiveName))$" }
|
||||
if (-not $line) { throw "$archiveName fehlt in checksums.txt." }
|
||||
if ((Get-FileHash $archive -Algorithm SHA256).Hash -ne ($line -split '\s+')[0]) {
|
||||
throw "Pruefsumme von $archiveName stimmt nicht. Download verworfen."
|
||||
}
|
||||
& "$env:SystemRoot\System32\tar.exe" -xzf $archive -C $tmp wstunnel.exe
|
||||
if ($LASTEXITCODE -ne 0) { throw "Entpacken von $archiveName fehlgeschlagen." }
|
||||
New-Item -ItemType Directory -Force (Split-Path $Exe) | Out-Null
|
||||
Move-Item (Join-Path $tmp 'wstunnel.exe') $Exe
|
||||
} finally {
|
||||
$ProgressPreference = $oldProgress
|
||||
Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# Startet den Client unsichtbar im Hintergrund, falls er noch nicht laeuft.
|
||||
function Start-Tunnel {
|
||||
if (-not $TunnelSecret) { throw "TunnelSecret in config.ps1 ist leer." }
|
||||
if (Test-TunnelListening) { return }
|
||||
Install-Wstunnel
|
||||
New-Item -ItemType Directory -Force $LogDir | Out-Null
|
||||
$log = Join-Path $LogDir 'wstunnel.log'
|
||||
Invoke-WithTunnelEnv {
|
||||
Start-Process -FilePath $Exe -ArgumentList (Get-TunnelArgs 'WARN') -WindowStyle Hidden `
|
||||
-RedirectStandardOutput $log -RedirectStandardError (Join-Path $LogDir 'wstunnel.err.log')
|
||||
}
|
||||
for ($i = 0; $i -lt 20; $i++) {
|
||||
if (Test-TunnelListening) { return }
|
||||
Start-Sleep -Milliseconds 250
|
||||
}
|
||||
throw "Tunnel-Client lauscht nicht auf $LocalProxy. Details: $log"
|
||||
}
|
||||
|
||||
function Stop-Tunnel {
|
||||
Get-TunnelProcess | Stop-Process -Force
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# Traegt den Client-Ordner in den Benutzer-PATH ein, damit 'codexx' in jedem Terminal geht.
|
||||
# Der PATH wird roh aus der Registry gelesen, damit Eintraege wie %USERPROFILE%\... nicht
|
||||
# beim Zurueckschreiben fest aufgeloest werden.
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ClientDir = Split-Path $PSScriptRoot -Parent
|
||||
|
||||
$key = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $true)
|
||||
try {
|
||||
$raw = $key.GetValue('Path', '', 'DoNotExpandEnvironmentNames')
|
||||
if (($raw -split ';' | ForEach-Object { $_.TrimEnd('\') }) -contains $ClientDir.TrimEnd('\')) {
|
||||
Write-Host "$ClientDir steht schon im PATH."
|
||||
return
|
||||
}
|
||||
$key.SetValue('Path', (($raw.TrimEnd(';') + ";$ClientDir").TrimStart(';')), 'ExpandString')
|
||||
} finally {
|
||||
$key.Close()
|
||||
}
|
||||
|
||||
# Explorer ueber die Aenderung informieren, sonst sehen neu gestartete Programme sie erst nach einer Neuanmeldung
|
||||
Add-Type -Namespace Tunnler -Name Native -MemberDefinition '[DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern IntPtr SendMessageTimeout(IntPtr h, uint m, UIntPtr w, string l, uint f, uint t, out UIntPtr r);'
|
||||
$result = [UIntPtr]::Zero
|
||||
[Tunnler.Native]::SendMessageTimeout([IntPtr]0xffff, 0x1A, [UIntPtr]::Zero, 'Environment', 2, 5000, [ref]$result) | Out-Null
|
||||
|
||||
Write-Host "$ClientDir in den PATH eingetragen."
|
||||
Write-Host "VS Code und offene Terminals komplett neu starten, dann 'codexx' statt 'codex' eingeben."
|
||||
@@ -0,0 +1,31 @@
|
||||
# Tunnel-Client verwalten.
|
||||
# tunnel.ps1 start Client im Hintergrund starten, Proxy fuer beliebige Programme
|
||||
# tunnel.ps1 status laeuft der Client, und welche IP sieht das Internet?
|
||||
# tunnel.ps1 stop Client beenden
|
||||
# tunnel.ps1 run Client im Vordergrund mit ausfuehrlichem Log (Fehlersuche)
|
||||
param([ValidateSet('start', 'status', 'stop', 'run')][string]$Action = 'status')
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
|
||||
switch ($Action) {
|
||||
'start' {
|
||||
Start-Tunnel
|
||||
Write-Host "Tunnel laeuft. In den gewuenschten Programmen als HTTP-Proxy eintragen: $LocalProxy"
|
||||
Write-Host "Beenden mit Tunnel-Stop.cmd."
|
||||
}
|
||||
'status' {
|
||||
if (-not (Test-TunnelListening)) { Write-Host "Tunnel laeuft nicht."; break }
|
||||
Write-Host "Tunnel lauscht auf $LocalProxy."
|
||||
$ip = & curl.exe -s -m 15 -x $ProxyUrl https://api.ipify.org
|
||||
if ($LASTEXITCODE -eq 0) { Write-Host "Oeffentliche IP ueber den Tunnel: $ip" }
|
||||
else { Write-Host "Keine Verbindung durch den Tunnel. Log: $(Join-Path $LogDir 'wstunnel.log')" }
|
||||
}
|
||||
'stop' {
|
||||
Stop-Tunnel
|
||||
Write-Host "Tunnel beendet."
|
||||
}
|
||||
'run' {
|
||||
Stop-Tunnel
|
||||
Install-Wstunnel
|
||||
Invoke-WithTunnelEnv { & $Exe @(Get-TunnelArgs 'INFO') }
|
||||
}
|
||||
}
|
||||
@@ -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). |
|
||||
@@ -0,0 +1,33 @@
|
||||
services:
|
||||
wstunnel:
|
||||
image: ghcr.io/erebe/wstunnel:v11.1.0
|
||||
container_name: wstunnel
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
npm: # nur fuer den Proxy Manager erreichbar, kein ports:
|
||||
ipv4_address: ${TUNNEL_IP:-172.20.250.250} # feste IP: NPM haengt nicht von Docker-DNS ab
|
||||
command:
|
||||
- /home/app/wstunnel # Image startet ueber dumb-init, Binaerpfad noetig
|
||||
- server
|
||||
- --restrict-config=/home/app/restrictions.yaml
|
||||
- --dns-resolver=dns://1.1.1.1 # oeffentliches DNS: lokale Namen sind nicht aufloesbar
|
||||
- --dns-resolver=dns://9.9.9.9
|
||||
- --dns-resolver-prefer-ipv4
|
||||
- ws://0.0.0.0:8090 # NPM terminiert TLS, intern reicht ws
|
||||
volumes:
|
||||
- ./restrictions.yaml:/home/app/restrictions.yaml:ro
|
||||
read_only: true
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
mem_limit: 64m
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: 1m
|
||||
max-file: "3"
|
||||
|
||||
# Werte aus .env (siehe docs/installation.md), Standard = bestehende Installation
|
||||
networks:
|
||||
npm:
|
||||
external: true
|
||||
name: ${NPM_NETWORK:-nginx_default}
|
||||
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=tunnler: wstunnel-Container vom Heimnetz trennen
|
||||
After=docker.service
|
||||
PartOf=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
EnvironmentFile=-/etc/default/tunnler-firewall
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/local/sbin/tunnler-firewall.sh
|
||||
ExecStop=/usr/local/sbin/tunnler-firewall.sh --remove
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target docker.service
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# Trennt den wstunnel-Container (feste IP, TUNNEL_IP aus /etc/default/tunnler-firewall) vom Heimnetz
|
||||
# und vom Pi selbst. Hostnamen, die per oeffentlichem DNS auf private IPs zeigen
|
||||
# (z. B. 192-168-0-1.nip.io), kann wstunnel selbst nicht abfangen.
|
||||
# tunnler-firewall.sh Regeln setzen (idempotent)
|
||||
# tunnler-firewall.sh --remove Regeln entfernen
|
||||
set -e
|
||||
SRC=${TUNNEL_IP:-172.20.250.250}
|
||||
|
||||
for chain in DOCKER-USER INPUT; do
|
||||
while iptables -D "$chain" -s "$SRC" -j TUNNLER 2>/dev/null; do :; done
|
||||
done
|
||||
if [ "$1" = "--remove" ]; then
|
||||
iptables -F TUNNLER 2>/dev/null || true
|
||||
iptables -X TUNNLER 2>/dev/null || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
iptables -N TUNNLER 2>/dev/null || iptables -F TUNNLER
|
||||
iptables -A TUNNLER -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
|
||||
for net in 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16; do
|
||||
iptables -A TUNNLER -d "$net" -j DROP
|
||||
done
|
||||
|
||||
iptables -N DOCKER-USER 2>/dev/null || true
|
||||
iptables -I DOCKER-USER -s "$SRC" -j TUNNLER
|
||||
iptables -I INPUT -s "$SRC" -j TUNNLER
|
||||
@@ -0,0 +1,99 @@
|
||||
# wstunnel-Server: Zugriffsregeln (wird bei Aenderung automatisch neu geladen).
|
||||
# Vorlage: Platzhalter im PathPrefix durch das Secret ersetzen, als restrictions.yaml speichern.
|
||||
#
|
||||
# Wirkung:
|
||||
# - Nur Clients mit dem geheimen Pfad-Praefix werden akzeptiert.
|
||||
# - Nur Forward-Tunnel (keine Reverse-Tunnel), nur TCP, nur Ziel-Port 80/443.
|
||||
# - Hostnamen: mindestens ein Punkt, TLD aus Buchstaben, nie *.localhost.
|
||||
# Lokale Namen (pi.hole, *.home, Container-Namen) loest der Server nicht auf,
|
||||
# weil er per --dns-resolver nur oeffentliches DNS fragt (siehe docker-compose.yml).
|
||||
# - IP-Adressen als Ziel: nur oeffentliche IPv4-Bereiche (privat, Loopback,
|
||||
# Link-Local, CGNAT, Multicast ausgeschlossen) und globales IPv6.
|
||||
restrictions:
|
||||
- name: "tunnler"
|
||||
description: "Forward-Tunnel mit Secret, TCP 80/443, keine privaten Ziele"
|
||||
match:
|
||||
- !PathPrefix "^__SECRET__$"
|
||||
allow:
|
||||
- !Tunnel
|
||||
protocol:
|
||||
- Tcp
|
||||
port:
|
||||
- 80
|
||||
- 443
|
||||
host: '(?i)^([a-z0-9_-]+\.)+([a-z]{2,8}|[a-z]{10,63}|[a-km-z][a-z]{8}|l[a-np-z][a-z]{7}|lo[a-bd-z][a-z]{6}|loc[b-z][a-z]{5}|loca[a-km-z][a-z]{4}|local[a-gi-z][a-z]{3}|localh[a-np-z][a-z]{2}|localho[a-rt-z][a-z]|localhos[a-su-z]|xn--[a-z0-9-]+)$'
|
||||
cidr:
|
||||
- 1.0.0.0/8
|
||||
- 2.0.0.0/7
|
||||
- 4.0.0.0/6
|
||||
- 8.0.0.0/7
|
||||
- 11.0.0.0/8
|
||||
- 12.0.0.0/6
|
||||
- 16.0.0.0/4
|
||||
- 32.0.0.0/3
|
||||
- 64.0.0.0/3
|
||||
- 96.0.0.0/6
|
||||
- 100.0.0.0/10
|
||||
- 100.128.0.0/9
|
||||
- 101.0.0.0/8
|
||||
- 102.0.0.0/7
|
||||
- 104.0.0.0/5
|
||||
- 112.0.0.0/5
|
||||
- 120.0.0.0/6
|
||||
- 124.0.0.0/7
|
||||
- 126.0.0.0/8
|
||||
- 128.0.0.0/3
|
||||
- 160.0.0.0/5
|
||||
- 168.0.0.0/8
|
||||
- 169.0.0.0/9
|
||||
- 169.128.0.0/10
|
||||
- 169.192.0.0/11
|
||||
- 169.224.0.0/12
|
||||
- 169.240.0.0/13
|
||||
- 169.248.0.0/14
|
||||
- 169.252.0.0/15
|
||||
- 169.255.0.0/16
|
||||
- 170.0.0.0/7
|
||||
- 172.0.0.0/12
|
||||
- 172.32.0.0/11
|
||||
- 172.64.0.0/10
|
||||
- 172.128.0.0/9
|
||||
- 173.0.0.0/8
|
||||
- 174.0.0.0/7
|
||||
- 176.0.0.0/4
|
||||
- 192.0.1.0/24
|
||||
- 192.0.2.0/23
|
||||
- 192.0.4.0/22
|
||||
- 192.0.8.0/21
|
||||
- 192.0.16.0/20
|
||||
- 192.0.32.0/19
|
||||
- 192.0.64.0/18
|
||||
- 192.0.128.0/17
|
||||
- 192.1.0.0/16
|
||||
- 192.2.0.0/15
|
||||
- 192.4.0.0/14
|
||||
- 192.8.0.0/13
|
||||
- 192.16.0.0/12
|
||||
- 192.32.0.0/11
|
||||
- 192.64.0.0/10
|
||||
- 192.128.0.0/11
|
||||
- 192.160.0.0/13
|
||||
- 192.169.0.0/16
|
||||
- 192.170.0.0/15
|
||||
- 192.172.0.0/14
|
||||
- 192.176.0.0/12
|
||||
- 192.192.0.0/10
|
||||
- 193.0.0.0/8
|
||||
- 194.0.0.0/7
|
||||
- 196.0.0.0/7
|
||||
- 198.0.0.0/12
|
||||
- 198.16.0.0/15
|
||||
- 198.20.0.0/14
|
||||
- 198.24.0.0/13
|
||||
- 198.32.0.0/11
|
||||
- 198.64.0.0/10
|
||||
- 198.128.0.0/9
|
||||
- 199.0.0.0/8
|
||||
- 200.0.0.0/5
|
||||
- 208.0.0.0/4
|
||||
- 2000::/3
|
||||
Reference in New Issue
Block a user