{"id":13552007,"url":"https://github.com/aramperes/onetun","last_synced_at":"2025-05-14T05:10:43.808Z","repository":{"id":37594598,"uuid":"415708517","full_name":"aramperes/onetun","owner":"aramperes","description":"User space WireGuard port-forward in Rust","archived":false,"fork":false,"pushed_at":"2025-05-12T16:50:23.000Z","size":458,"stargazers_count":951,"open_issues_count":17,"forks_count":47,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-13T03:06:05.953Z","etag":null,"topics":["boringtun","hacktoberfest","smoltcp","wireguard","wireguard-tunnel"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aramperes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-10-10T21:51:49.000Z","updated_at":"2025-05-09T08:41:14.000Z","dependencies_parsed_at":"2023-12-21T21:27:50.530Z","dependency_job_id":"1f92a7a3-0b7a-441c-a3a1-41f6dca2677a","html_url":"https://github.com/aramperes/onetun","commit_stats":{"total_commits":182,"total_committers":13,"mean_commits":14.0,"dds":"0.39010989010989006","last_synced_commit":"e25c88410e72b5df4c09fd0545991e7882bf423a"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aramperes%2Fonetun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aramperes%2Fonetun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aramperes%2Fonetun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aramperes%2Fonetun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aramperes","download_url":"https://codeload.github.com/aramperes/onetun/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254076850,"owners_count":22010611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["boringtun","hacktoberfest","smoltcp","wireguard","wireguard-tunnel"],"created_at":"2024-08-01T12:01:57.364Z","updated_at":"2025-05-14T05:10:43.713Z","avatar_url":"https://github.com/aramperes.png","language":"Rust","funding_links":[],"categories":["Proxy and VPN","Rust","hacktoberfest","Projects"],"sub_categories":["Python","Tools"],"readme":"\u003cimg align=\"right\" alt=\"onetun\" width=\"150\" src=\".github/onetun.png\"\u003e\n\n# onetun\n\nA cross-platform, user-space WireGuard port-forwarder that requires **no root-access or system network configurations**.\n\n[![crates.io](https://img.shields.io/crates/v/onetun.svg)](https://crates.io/crates/onetun)\n[![MIT licensed](https://img.shields.io/crates/l/onetun.svg)](./LICENSE)\n[![Build status](https://github.com/aramperes/onetun/actions/workflows/build.yml/badge.svg)](https://github.com/aramperes/onetun/actions)\n[![Latest Release](https://img.shields.io/github/v/tag/aramperes/onetun?label=release)](https://github.com/aramperes/onetun/releases/latest)\n\n## Use-case\n\nAccess TCP or UDP services running on your WireGuard network, from devices that don't have WireGuard installed.\n\nFor example,\n\n- Personal or shared computers where you can't install WireGuard (root)\n- IoT and mobile devices\n- Root-less containers\n\n## Download\n\nonetun is available to install from [crates.io](https://crates.io/crates/onetun) with Rust ≥1.80.0:\n\n```shell\ncargo install onetun\n```\n\nYou can also download the binary for Windows, macOS (Apple Silicon), and Linux (amd64, arm64) from\nthe [Releases](https://github.com/aramperes/onetun/releases) page.\n\nYou can also run onetun using [Docker](https://hub.docker.com/r/aramperes/onetun):\n\n```shell\ndocker run --rm --name onetun --user 1000 -p 8080:8080 aramperes/onetun \\\n    0.0.0.0:8080:192.168.4.2:8080 [...options...]\n```\n\nYou can also build onetun locally, using Rust ≥1.80.0:\n\n```shell\ngit clone https://github.com/aramperes/onetun \u0026\u0026 cd onetun\ncargo build --release\n./target/release/onetun\n```\n\n## Usage\n\n**onetun** opens a TCP or UDP port on your local system, from which traffic is forwarded to a port on a peer in your\nWireGuard network. It requires no changes to your operating system's network interfaces: you don't need to have `root`\naccess, or install any WireGuard tool on your local system for it to work.\n\nThe only prerequisite is to register a peer IP and public key on the remote WireGuard endpoint; those are necessary for\nthe WireGuard endpoint to trust the onetun peer and for packets to be routed.\n\n```shell\nonetun [src_host:]\u003csrc_port\u003e:\u003cdst_host\u003e:\u003cdst_port\u003e[:TCP,UDP,...] [...]    \\\n    --endpoint-addr \u003cpublic WireGuard endpoint address\u003e                   \\\n    --endpoint-public-key \u003cthe public key of the peer on the endpoint\u003e    \\\n    --private-key \u003cprivate key assigned to onetun\u003e                        \\\n    --source-peer-ip \u003cIP assigned to onetun\u003e                              \\\n    --keep-alive \u003coptional persistent keep-alive in seconds\u003e              \\\n    --log \u003coptional log level, defaults to \"info\"\u003e\n```\n\n\u003e Note: you can use environment variables for all of these flags. Use `onetun --help` for details.\n\n### Example\n\nSuppose your WireGuard endpoint has the following configuration, and is accessible from `140.30.3.182:51820`:\n\n```shell\n# /etc/wireguard/wg0.conf\n\n[Interface]\nPrivateKey = ********************************************\nListenPort = 51820\nAddress = 192.168.4.1\n\n# A friendly peer that hosts the TCP service we want to reach\n[Peer]\nPublicKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nAllowedIPs = 192.168.4.2/32\n\n# Peer assigned to onetun\n[Peer]\nPublicKey = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\nAllowedIPs = 192.168.4.3/32\n```\n\nWe want to access a web server on the friendly peer (`192.168.4.2`) on port `8080`. We can use **onetun** to open a\nlocal port, say `127.0.0.1:8080`, that will tunnel through WireGuard to reach the peer web server:\n\n```shell\nonetun 127.0.0.1:8080:192.168.4.2:8080                                    \\\n    --endpoint-addr 140.30.3.182:51820                                    \\\n    --endpoint-public-key 'PUB_****************************************'  \\\n    --private-key 'PRIV_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'          \\\n    --source-peer-ip 192.168.4.3                                          \\\n    --keep-alive 10\n```\n\nYou'll then see this log:\n\n```shell\nINFO  onetun \u003e Tunneling TCP [127.0.0.1:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\n```\n\nWhich means you can now access the port locally!\n\n```shell\ncurl 127.0.0.1:8080\nHello world!\n```\n\n### Multiple tunnels in parallel\n\n**onetun** supports running multiple tunnels in parallel. For example:\n\n```shell\nonetun 127.0.0.1:8080:192.168.4.2:8080 127.0.0.1:8081:192.168.4.4:8081\nINFO  onetun::tunnel \u003e Tunneling TCP [127.0.0.1:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\nINFO  onetun::tunnel \u003e Tunneling TCP [127.0.0.1:8081]-\u003e[192.168.4.4:8081] (via [140.30.3.182:51820] as peer 192.168.4.3)\n```\n\n... would open TCP ports 8080 and 8081 locally, which forward to their respective ports on the different peers.\n\n#### Maximum number of tunnels\n\n`smoltcp` imposes a compile-time limit on the number of IP addresses assigned to an interface. **onetun** increases\nthe default value to support most use-cases. In effect, the default limit on the number of **onetun** peers\nis **7 per protocol** (TCP and UDP).\n\nShould you need more unique IP addresses to forward ports to, you can increase the limit in `.cargo/config.toml` and recompile **onetun**.\n\n### UDP Support\n\n**onetun** supports UDP forwarding. You can add `:UDP` at the end of the port-forward configuration, or `UDP,TCP` to support\nboth protocols on the same port (note that this opens 2 separate tunnels, just on the same port)\n\n```shell\nonetun 127.0.0.1:8080:192.168.4.2:8080:UDP\nINFO  onetun::tunnel \u003e Tunneling UDP [127.0.0.1:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\n\nonetun 127.0.0.1:8080:192.168.4.2:8080:UDP,TCP\nINFO  onetun::tunnel \u003e Tunneling UDP [127.0.0.1:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\nINFO  onetun::tunnel \u003e Tunneling TCP [127.0.0.1:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\n```\n\nNote: UDP support is totally experimental. You should read the UDP portion of the **Architecture** section before using\nit in any production capacity.\n\n### IPv6 Support\n\n**onetun** supports both IPv4 and IPv6. In fact, you can use onetun to forward some IP version to another, e.g. 6-to-4:\n\n```shell\nonetun [::1]:8080:192.168.4.2:8080\nINFO  onetun::tunnel \u003e Tunneling TCP [[::1]:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\n```\n\nNote that each tunnel can only support one \"source\" IP version and one \"destination\" IP version. If you want to support\nboth IPv4 and IPv6 on the same port, you should create a second port-forward:\n\n```shell\nonetun [::1]:8080:192.168.4.2:8080 127.0.0.1:8080:192.168.4.2:8080\nINFO  onetun::tunnel \u003e Tunneling TCP [[::1]:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\nINFO  onetun::tunnel \u003e Tunneling TCP [127.0.0.1:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\n```\n\n### Packet Capture\n\nFor debugging purposes, you can enable the capture of IP packets sent between onetun and the WireGuard peer.\nThe output is a libpcap capture file that can be viewed with Wireshark.\n\n```shell\nonetun --pcap wg.pcap 127.0.0.1:8080:192.168.4.2:8080\nINFO  onetun::pcap \u003e Capturing WireGuard IP packets to wg.pcap\nINFO  onetun::tunnel \u003e Tunneling TCP [127.0.0.1:8080]-\u003e[192.168.4.2:8080] (via [140.30.3.182:51820] as peer 192.168.4.3)\n```\n\nTo capture packets sent to and from the onetun local port, you must use an external tool like `tcpdump` with root access:\n\n```shell\nsudo tcpdump -i lo -w local.pcap 'dst 127.0.0.1 \u0026\u0026 port 8080'\n```\n\n### WireGuard Options\n\nBy default, onetun will create the UDP socket to communicate with the WireGuard endpoint on all interfaces and on a dynamic port,\ni.e. `0.0.0.0:0` for IPv4 endpoints, or `[::]:0` for IPv6.\nYou can bind to a static address instead using `--endpoint-bind-addr`:\n\n```shell\nonetun --endpoint-bind-addr 0.0.0.0:51820 --endpoint-addr 140.30.3.182:51820 [...]\n```\n\nThe 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`.\nThe peer must also have this key configured using the `PresharedKey` option.\n\n```shell\nonetun --preshared-key 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' [...]\n```\n\n## Architecture\n\n**In short:** onetun uses [smoltcp's](https://github.com/smoltcp-rs/smoltcp) TCP/IP and UDP stack to generate IP packets\nusing its state machine (\"virtual interface\"). The generated IP packets are\nencrypted by [boringtun](https://github.com/cloudflare/boringtun) and sent to the WireGuard endpoint. Encrypted IP packets received\nfrom the WireGuard endpoint are decrypted using boringtun and sent through the smoltcp virtual interface state machine.\nonetun creates \"virtual sockets\" in the virtual interface to forward data sent from inbound connections,\nas well as to receive data from the virtual interface to forward back to the local client.\n\n---\n\nonetun uses [tokio](https://github.com/tokio-rs/tokio), the async runtime, to listen for new TCP connections on the\ngiven port.\n\nWhen a client connects to the onetun's TCP port, a \"virtual client\" is\ncreated in a [smoltcp](https://github.com/smoltcp-rs/smoltcp) \"virtual\" TCP/IP interface, which runs fully inside the onetun\nprocess. An ephemeral \"virtual port\" is assigned to the \"virtual client\", which maps back to the local client.\n\nWhen the real client opens the connection, the virtual client socket opens a TCP connection to the virtual server\n(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.\nBecause of how the virtual client and server are configured, the IP packet is crafted with a source address\nbeing the configured `source-peer-ip` (`192.168.4.3` in the example above),\nand the destination address matches the port-forward's configured destination (`192.168.4.2`).\n\nBy doing this, we let smoltcp handle the crafting of the IP packets, and the handling of the client's TCP states.\nInstead of actually sending those packets to the virtual server,\nwe can intercept them in the virtual interface and encrypt the packets using [boringtun](https://github.com/cloudflare/boringtun),\nand send them to the WireGuard endpoint's UDP port.\n\nOnce the WireGuard endpoint receives an encrypted IP packet, it decrypts it using its private key and reads the IP packet.\nIt reads the destination address, re-encrypts the IP packet using the matching peer's public key, and sends it off to\nthe peer's UDP endpoint.\n\nThe peer receives the encrypted IP and decrypts it. It can then read the inner payload (the TCP segment),\nforward it to the server's port, which handles the TCP segment. The TCP server responds with `SYN-ACK`, which goes back through\nthe peer's local WireGuard interface, gets encrypted, forwarded to the WireGuard endpoint, and then finally back to onetun's UDP port.\n\nWhen onetun receives an encrypted packet from the WireGuard endpoint, it decrypts it using boringtun.\nThe resulting IP packet is dispatched to the corresponding virtual interface running inside onetun;\nthe IP packet is then read and processed by the virtual interface, and the virtual client's TCP state is updated.\n\nWhenever data is sent by the real client, it is simply \"sent\" by the virtual client, which kicks off the whole IP encapsulation\nand WireGuard encryption again. When data is sent by the real server, it ends up routed in the virtual interface, which allows\nthe virtual client to read it. When the virtual client reads data, it simply pushes the data back to the real client.\n\nThis work is all made possible by [smoltcp](https://github.com/smoltcp-rs/smoltcp) and [boringtun](https://github.com/cloudflare/boringtun),\nso special thanks to the developers of those libraries.\n\n### UDP\n\nUDP support is experimental. Since UDP messages are stateless, there is no perfect way for onetun to know when to release the\nassigned virtual port back to the pool for a new peer to use. This would cause issues over time as running out of virtual ports\nwould mean new datagrams get dropped. To alleviate this, onetun will cap the amount of ports used by one peer IP address;\nif another datagram comes in from a different port but with the same IP, the least recently used virtual port will be freed and assigned\nto the new peer port. At that point, any datagram packets destined for the reused virtual port will be routed to the new peer,\nand any datagrams received by the old peer will be dropped.\n\nIn addition, in cases where many IPs are exhausting the UDP virtual port pool in tandem, and a totally new peer IP sends data,\nonetun will have to pick the least recently used virtual port from _any_ peer IP and reuse it. However, this is only allowed\nif the least recently used port hasn't been used for a certain amount of time. If all virtual ports are truly \"active\"\n(with at least one transmission within that time limit), the new datagram gets dropped due to exhaustion.\n\nAll in all, I would not recommend using UDP forwarding for public services, since it's most likely prone to simple DoS or DDoS.\n\n## HTTP/SOCKS Proxy\n\n**onetun** is a Transport-layer proxy (also known as port forwarding); it is not in scope to provide\na HTTP/SOCKS proxy server. However, you can easily chain **onetun** with a proxy server on a remote\nthat is locked down to your WireGuard network.\n\nFor example, you could run [dante-server](https://www.inet.no/dante/) on a peer (ex. `192.168.4.2`) with the following configuration:\n\n```\n# /etc/danted.conf\n\nlogoutput: syslog\nuser.privileged: root\nuser.unprivileged: nobody\n\ninternal: 192.168.4.2 port=1080\nexternal: eth0\n\nsocksmethod: none\nclientmethod: none\n\n# Locks down proxy use to WireGuard peers (192.168.4.x)\nclient pass {\n    from: 192.168.4.0/24 to: 0.0.0.0/0\n}\nsocks pass {\n    from: 192.168.4.0/24 to: 0.0.0.0/0\n}\n```\n\nThen use **onetun** to expose the SOCKS5 proxy locally:\n\n```shell\nonetun 127.0.0.1:1080:192.168.4.2:1080\nINFO  onetun::tunnel \u003e Tunneling TCP [127.0.0.1:1080]-\u003e[192.168.4.2:1080] (via [140.30.3.182:51820] as peer 192.168.4.3)\n```\n\nTest with `curl` (or configure your browser):\n\n```shell\ncurl -x socks5://127.0.0.1:1080 https://ifconfig.me\n```\n\n## Contributing and Maintenance\n\nI will gladly accept contributions to onetun, and set aside time to review all pull-requests.\nPlease consider opening a GitHub issue if you are unsure if your contribution is within the scope of the project.\n\n**Disclaimer**: I do not have enough personal time to actively maintain onetun besides open-source contributions.\n\n## License\n\nMIT License. See `LICENSE` for details. Copyright \u0026copy; 2025 Aram Peres.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faramperes%2Fonetun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faramperes%2Fonetun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faramperes%2Fonetun/lists"}