Onetun
User space WireGuard port-forward in Rust
Install / Use
/learn @aramperes/OnetunREADME
onetun
A cross-platform, user-space WireGuard port-forwarder that requires no root-access or system network configurations.
Use-case
Access TCP or UDP services running on your WireGuard network, from devices that don't have WireGuard installed.
For example,
- Personal or shared computers where you can't install WireGuard (root)
- IoT and mobile devices
- Root-less containers
Download
onetun is available to install from crates.io with Rust ≥1.80.0:
cargo install onetun
You can also download the binary for Windows, macOS (Apple Silicon), and Linux (amd64, arm64) from the Releases page.
You can also run onetun using Docker:
docker run --rm --name onetun --user 1000 -p 8080:8080 aramperes/onetun \
0.0.0.0:8080:192.168.4.2:8080 [...options...]
You can also build onetun locally, using Rust ≥1.80.0:
git clone https://github.com/aramperes/onetun && cd onetun
cargo build --release
./target/release/onetun
Usage
onetun opens a TCP or UDP port on your local system, from which traffic is forwarded to a port on a peer in your
WireGuard network. It requires no changes to your operating system's network interfaces: you don't need to have root
access, or install any WireGuard tool on your local system for it to work.
The only prerequisite is to register a peer IP and public key on the remote WireGuard endpoint; those are necessary for the WireGuard endpoint to trust the onetun peer and for packets to be routed.
onetun [src_host:]<src_port>:<dst_host>:<dst_port>[:TCP,UDP,...] [...] \
--endpoint-addr <public WireGuard endpoint address> \
--endpoint-public-key <the public key of the peer on the endpoint> \
--private-key <private key assigned to onetun> \
--source-peer-ip <IP assigned to onetun> \
--keep-alive <optional persistent keep-alive in seconds> \
--log <optional log level, defaults to "info">
Note: you can use environment variables for all of these flags. Use
onetun --helpfor details.
Example
Suppose your WireGuard endpoint has the following configuration, and is accessible from 140.30.3.182:51820:
# /etc/wireguard/wg0.conf
[Interface]
PrivateKey = ********************************************
ListenPort = 51820
Address = 192.168.4.1
# A friendly peer that hosts the TCP service we want to reach
[Peer]
PublicKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AllowedIPs = 192.168.4.2/32
# Peer assigned to onetun
[Peer]
PublicKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
AllowedIPs = 192.168.4.3/32
We want to access a web server on the friendly peer (192.168.4.2) on port 8080. We can use onetun to open a
local port, say 127.0.0.1:8080, that will tunnel through WireGuard to reach the peer web server:
onetun 127.0.0.1:8080:192.168.4.2:8080 \
--endpoint-addr 140.30.3.182:51820 \
--endpoint-public-key 'PUB_****************************************' \
--private-key 'PRIV_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' \
--source-peer-ip 192.168.4.3 \
--keep-alive 10
You'll then see this log:
INFO onetun > Tunneling TCP [127.0.0.1:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
Which means you can now access the port locally!
curl 127.0.0.1:8080
Hello world!
Multiple tunnels in parallel
onetun supports running multiple tunnels in parallel. For example:
onetun 127.0.0.1:8080:192.168.4.2:8080 127.0.0.1:8081:192.168.4.4:8081
INFO onetun::tunnel > Tunneling TCP [127.0.0.1:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
INFO onetun::tunnel > Tunneling TCP [127.0.0.1:8081]->[192.168.4.4:8081] (via [140.30.3.182:51820] as peer 192.168.4.3)
... would open TCP ports 8080 and 8081 locally, which forward to their respective ports on the different peers.
Maximum number of tunnels
smoltcp imposes a compile-time limit on the number of IP addresses assigned to an interface. onetun increases
the default value to support most use-cases. In effect, the default limit on the number of onetun peers
is 7 per protocol (TCP and UDP).
Should you need more unique IP addresses to forward ports to, you can increase the limit in .cargo/config.toml and recompile onetun.
UDP Support
onetun supports UDP forwarding. You can add :UDP at the end of the port-forward configuration, or UDP,TCP to support
both protocols on the same port (note that this opens 2 separate tunnels, just on the same port)
onetun 127.0.0.1:8080:192.168.4.2:8080:UDP
INFO onetun::tunnel > Tunneling UDP [127.0.0.1:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
onetun 127.0.0.1:8080:192.168.4.2:8080:UDP,TCP
INFO onetun::tunnel > Tunneling UDP [127.0.0.1:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
INFO onetun::tunnel > Tunneling TCP [127.0.0.1:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
Note: UDP support is totally experimental. You should read the UDP portion of the Architecture section before using it in any production capacity.
IPv6 Support
onetun supports both IPv4 and IPv6. In fact, you can use onetun to forward some IP version to another, e.g. 6-to-4:
onetun [::1]:8080:192.168.4.2:8080
INFO onetun::tunnel > Tunneling TCP [[::1]:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
Note that each tunnel can only support one "source" IP version and one "destination" IP version. If you want to support both IPv4 and IPv6 on the same port, you should create a second port-forward:
onetun [::1]:8080:192.168.4.2:8080 127.0.0.1:8080:192.168.4.2:8080
INFO onetun::tunnel > Tunneling TCP [[::1]:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
INFO onetun::tunnel > Tunneling TCP [127.0.0.1:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
Packet Capture
For debugging purposes, you can enable the capture of IP packets sent between onetun and the WireGuard peer. The output is a libpcap capture file that can be viewed with Wireshark.
onetun --pcap wg.pcap 127.0.0.1:8080:192.168.4.2:8080
INFO onetun::pcap > Capturing WireGuard IP packets to wg.pcap
INFO onetun::tunnel > Tunneling TCP [127.0.0.1:8080]->[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)
To capture packets sent to and from the onetun local port, you must use an external tool like tcpdump with root access:
sudo tcpdump -i lo -w local.pcap 'dst 127.0.0.1 && port 8080'
WireGuard Options
By default, onetun will create the UDP socket to communicate with the WireGuard endpoint on all interfaces and on a dynamic port,
i.e. 0.0.0.0:0 for IPv4 endpoints, or [::]:0 for IPv6.
You can bind to a static address instead using --endpoint-bind-addr:
onetun --endpoint-bind-addr 0.0.0.0:51820 --endpoint-addr 140.30.3.182:51820 [...]
The security of the WireGuard connection can be further enhanced with a pre-shared key (PSK). You can generate such a key with the wg genpsk command, and provide it using --preshared-key.
The peer must also have this key configured using the PresharedKey option.
onetun --preshared-key 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' [...]
Architecture
In short: onetun uses smoltcp's TCP/IP and UDP stack to generate IP packets using its state machine ("virtual interface"). The generated IP packets are encrypted by boringtun and sent to the WireGuard endpoint. Encrypted IP packets received from the WireGuard endpoint are decrypted using boringtun and sent through the smoltcp virtual interface state machine. onetun creates "virtual sockets" in the virtual interface to forward data sent from inbound connections, as well as to receive data from the virtual interface to forward back to the local client.
onetun uses tokio, the async runtime, to listen for new TCP connections on the given port.
When a client connects to the onetun's TCP port, a "virtual client" is created in a smoltcp "virtual" TCP/IP interface, which runs fully inside the onetun process. An ephemeral "virtual port" is assigned to the "virtual client", which maps back to the local client.
When the real client opens the connection, the virtual client socket opens a TCP connection to the virtual server
(a dummy socket bound to the remote host/port). The virtual interface in turn crafts the SYN segment and wraps it in an IP packet.
Because of how the virtual client and server are configured, the IP packet is crafted with a source address
being the configured source-peer-ip (192.168.4.3 in the example above),
and the destination address matches the port-forward's configured destination (192.168.4.2).
By doing this, we let smoltcp handle the crafting of the IP packets, and the handling of the client's TCP states.
Related Skills
node-connect
335.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
335.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.5kCommit, push, and open a PR
