Wstunnel
Tunnel all your traffic over Websocket or HTTP2 - Bypass firewalls/DPI - Static binary available
Install / Use
/learn @erebe/WstunnelREADME
Summary
Description <a name="description"></a>
Most of the time when you are using a public network, you are behind some kind of firewall or proxy. One of their purpose is to constrain you to only use certain kind of protocols and consult only a subset of the web. Nowadays, the most widespread protocol is http and is de facto allowed by third party equipment.
Wstunnel uses the websocket protocol which is compatible with http in order to bypass firewalls and proxies. Wstunnel allows you to tunnel whatever traffic you want and access whatever resources/site you need.
My inspiration came from this project but as I don't want to install npm and nodejs to use this tool, I remade it in ~~Haskell~~ Rust and improved it.
What to expect:
- Easy to use
- Good error messages and debug information
- Static forward (reverse) tunneling (TCP, UDP, Unix socket, Stdio)
- Dynamic (reverse) tunneling (Socks5 proxy, HTTP proxy and Transparent Proxy)
- Support for using http proxy (when behind one) as gateway
- Support of proxy protocol
- Support for tls/https server with certificates auto-reload (with embedded self-signed certificate, or your own)
- Support of mTLS with certificates auto-reload - documentation here
- Support IPv6
- Support for Websocket and HTTP2 as transport protocol (websocket is more performant)
- Standalone binaries (so just cp it where you want) here
Sponsors <a name="sponsors"></a>
Part of Wstunnel development has been sponsored by
<p align="center"> <a href="https://serviceplanet.nl"> <img width="200" height="100" src="https://github.com/erebe/wstunnel/raw/main/docs/logo_serviceplanet.png" alt="service planet logo"/> </a></p>Note <a name="note"></a>
v7.0.0 is a complete rewrite of wstunnel in Rust and is not compatible with previous version. Previous code in Haskell can be found on branch https://github.com/erebe/wstunnel/tree/haskell
What to expect from previous version:
- More throughput and less jitter due to Haskell GC. Most of you will not care, as it was performant enough already. But you can now saturate a gigabit ethernet card with a single connection
- Command line is more homogeneous/has better UX. All tunnel can be specified multiple times
- Tunnel protocol tries to look like normal traffic, to avoid being flagged
- Support of reverse tunneling
- New bug, it is a rewrite (╯'□')╯︵ ┻━┻ ¯\(ツ)/¯
- Mainly for me to ease the maintenance of the project. I don't do a lot of haskell nowadays and it was harder for me to keep maintening the project over time, as I get lost in touch of the Haskell ecosystem and new release.
- Armv7 build (aka raspberry pi), as new version of GHC (Haskell compiler) dropped its support
Demo server <a name="demo"></a>
If you just want to try out that you can bypass your proxy/firewall. You can give it a try with wstunnel demo server.
# In a terminal start wstunnel client
# You can set as tls-sni-override whatever domain you want. The tunnel is the only one that is going to be allowed.
wstunnel client -L 'tcp://4443:localhost:444?proxy_protocol' -P demo --tls-sni-override=google.fr wss://49.13.58.9
# on another terminal, run curl and it should return you this greetings
curl -k https://localhost:4443
> Memento mori !
Command line <a name="cmd"></a>
Usage: wstunnel client [OPTIONS] <ws[s]|http[s]://wstunnel.server.com[:port]>
Arguments:
<ws[s]|http[s]://wstunnel.server.com[:port]>
Address of the wstunnel server
You can either use websocket or http2 as transport protocol. Use websocket if you are unsure.
Example: For websocket with TLS wss://wstunnel.example.com or without ws://wstunnel.example.com
For http2 with TLS https://wstunnel.example.com or without http://wstunnel.example.com
*WARNING* HTTP2 as transport protocol is harder to make it works because:
- If you are behind a (reverse) proxy/CDN they are going to buffer the whole request before forwarding it to the server
Obviously, this is not going to work for tunneling traffic
- if you have wstunnel behind a reverse proxy, most of them (i.e: nginx) are going to turn http2 request into http1
This is not going to work, because http1 does not support streaming naturally
The only way to make it works with http2 is to have wstunnel directly exposed to the internet without any reverse proxy in front of it
Options:
-L, --local-to-remote <{tcp,udp,socks5,stdio,unix}://[BIND:]PORT:HOST:PORT>
Listen on local and forwards traffic from remote. Can be specified multiple times
examples:
'tcp://1212:google.com:443' => listen locally on tcp on port 1212 and forward to google.com on port 443
'tcp://2:n.lan:4?proxy_protocol' => listen locally on tcp on port 2 and forward to n.lan on port 4
Send a proxy protocol header v2 when establishing connection to n.lan
'udp://1212:1.1.1.1:53' => listen locally on udp on port 1212 and forward to cloudflare dns 1.1.1.1 on port 53
'udp://1212:1.1.1.1:53?timeout_sec=10' timeout_sec on udp force close the tunnel after 10sec. Set it to 0 to disable the timeout [default: 30]
'socks5://[::1]:1212' => listen locally with socks5 on port 1212 and forward dynamically requested tunnel
'socks5://[::1]:1212?login=admin&password=admin' => listen locally with socks5 on port 1212 and only accept connection with login=admin and password=admin
'http://[::1]:1212' => start a http proxy on port 1212 and forward dynamically requested tunnel
'http://[::1]:1212?login=admin&password=admin' => start a http proxy on port 1212 and only accept connection with login=admin and password=admin
'tproxy+tcp://[::1]:1212' => listen locally on tcp on port 1212 as a *transparent proxy* and forward dynamically requested tunnel
'tproxy+udp://[::1]:1212?timeout_sec=10' listen locally on udp on port 1212 as a *transparent proxy* and forward dynamically requested tunnel
linux only and requires sudo/CAP_NET_ADMIN
'stdio://google.com:443' => listen for data from stdio, mainly for `ssh -o ProxyCommand="wstunnel client --log-lvl=off -L stdio://%h:%p ws://localhost:8080" my-server`
'unix:///tmp/wstunnel.sock:g.com:443' => listen for data from unix socket of path /tmp/wstunnel.sock and forward to g.com:443
-R, --remote-to-local <{tcp,udp,socks5,unix}://[BIND:]PORT:HOST:PORT>
Listen on remote and forwards traffic from local. Can be specified multiple times. Only tcp is supported
examples:
'tcp://1212:google.com:443' => listen on server for incoming tcp cnx on port 1212 and forward to google.com on port 443 from local machine
'udp://1212:1.1.1.1:53' => listen on server for incoming udp on port 1212 and forward to cloudflare dns 1.1.1.1 on port 53 from local machine
'socks5://[::1]:1212' => listen on server for incoming socks5 request on port 1212 and forward dynamically request from local machine
'http://[::1]:1212' => listen on server for incoming http proxy request on port 1212 and forward dynamically request from local machine (login/password is supported)
'unix://wstunnel.sock:g.com:443' => listen on server for incoming data from unix socket of path wstunnel.sock and forward to g.com:443 from local machine
--no-color <NO_COLOR>
Disable color output in logs
[env: NO_COLOR=]
--socket-so-mark <INT>
(linux only) Mark network packet with SO_MARK sockoption with the specified value.
You need to use {root, sudo, capabilities} to run wstunnel when using this option
-c, --connection-min-idle <INT>
Client will maintain a pool of open connection to the server, in order to speed up the connection process.
This option set the maximum number of connection that will be kept open.
This is useful if you plan to create/destroy a lot of tunnel (i.e: with socks5 to navigate with a browser)
It will avoid the latency of doing tcp + tls handshake with the server
[default: 0]
--nb-worker-threads <INT>
*WARNING* The flag does nothing, you need to set the env variable *WARNING*
Control the number of threads that will be used.
By default, it is equal the number of cpus
[env: TOKIO_WORKER_THREADS=]
--log-lvl <LOG_LEVEL>
Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF
for more details: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax
[env: RUST_LOG=]
[default: INFO]
--tls-sni-override <DOMAIN_NAME>
Domain name that will be used as SNI during TLS handshake
Warning: If you are behind a CDN (i.e: Cloudflare) you must set this domain also in the http HOST header.
or it will be flagged as
