aramperes / onetun

User space WireGuard port-forward in Rust
MIT License
903 stars 44 forks source link

Add support for "reverse" forwarding (server listening on local) #6

Open cipriancraciun opened 3 years ago

cipriancraciun commented 3 years ago

At the moment onetun supports forwarding outbound connections (from local client to remote server).

It would be especially useful for developers to also have the reverse, namely inbound connections (from remote clients to local servers).

Based on your description on how it uses smoltun it seems to be a matter of implementing the reverse logic for the TCP forwarding.

aramperes commented 3 years ago

Thanks for the suggestion!

Assuming this is similar to reverse SSH tunnelling, this would only work if a onetun-compatible daemon was running on the remote server. Specifically, it would need to open a port on the remote server, which isn't possible without having a process running on the remote server in the first place.

SSH reverse-tunnelling works because sshd (daemon) can open ports on the remote server on demand, after being instructed to do so by the client (ssh) using a special request.

I have no plans for a onetund at the moment since it goes against the idea of onetun being useful in non-root/daemon-less environments. I would also need to design a protocol for onetun to instruct onetund to open a port forward.

I might change my mind down the line if I see some interest in it, or if I find a use for it myself.

cipriancraciun commented 3 years ago

this would only work if a onetun-compatible daemon was running on the remote server.

On the contrary, one wouldn't need onetun on the remote server.

Let me expand upon how I think this could work:

What I'm proposing is that onetun reacts to the inbound TCP/IP connections and forwards them to the local computer (in the reverse way).

Currently onetun allows the following (here "remote TCP endpoint" is any IP reachable via the Wireguard router):

local TCP client -> connect to local TCP endpoint -> [onetun TCP stack] -> [wireguard] -> ... -> server TCP listen on remote endpoint

What I'm proposing is the reverse (here the "remote endpoint" is the Wireguard IP address of the onetun client):

local TCP server <- listens on local TCP port <- [onetun TCP stack] <- [wireguard] <- ... <- client TCP connect to remote TCP endpoint

If the current command is this:

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

The command line for the reverse would be:

./onetun 127.0.0.1:8080 192.168.4.3:8080                                  \
    --reverse \
    --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

I.e. the only difference is --reverse, and the fact that the second address that you call "destination address" is actually the source-peer-ip of the onetun instance.

Thus any other client (that uses Wireguard or is routed through the Wireguard router) might just connect to 192.168.4.3:8080, which in fact would imply that onetun running locally would receive TCP packets related to inbound connections. So it could just inject these in its TCP/IP stack that would trigger some sort of accept inside onetun that instead would connect to the local server running on the onetun client.

aramperes commented 3 years ago

That would limit the port on the remote server to only be accessible via onetun's peer IP, which IMO reduces the utility compared to SSH's reverse tunnelling, which allows opening a port on any interface.

I can see how that would still be useful in some cases though, where you want to make a local port accessible to your WireGuard mesh without having to install WG/root access. I'll look into it for v0.3.

cipriancraciun commented 3 years ago
[any interface]:[any port] <- [onetun] <- [wireguard] <- [onetun peer IP]:[any port] <- client TCP connect

I agree that the above is even better that what I've proposed.


My intended use-case is the following: Imagine I have an "HTTP router" (for example HAProxy) installed somewhere on a internet-accessible node. With that "HTTP router" I can route HTTP requests (based on Host or path) to one of the endpoints that are acessible through Wirefuard, and served at the other end by a server (forwarded via onetun). This would allow local instances running on my development laptop to be reachable from the internet via the "HTTP router". Obviously this is even possible today by using a proper Wireguard client or ssh -R, but I think by using onetun it's even more tidy and nice.

ViRb3 commented 2 years ago

Is remote port forwarding actually implemented in 73671a4d07bce45db354dbc9d9831d4bc0c5954c, or is it just the parsing part? I'm asking because I have a use case that I can't seem to get working. I'm experimenting with a VPN provider that allows you to forward ports from the VPN public IP to your internal IP via the tunnel. This way, you can expose services without revealing your personal IP. So, it looks something like:

client --TCP--> VPN endpoint --WG tunnel--> me

I tried the following onetun command:

onetun --endpoint-addr XXX --endpoint-public-key XXX --private-key XXX --source-peer-ip XXX -keep-alive 10 -r 12345:localhost:12345:TCP,UDP

The idea is then, I should be able to reach my service on port 12345 by calling the VPN endpoint's port 12345. But it does not work... If I connect to the VPN using the official WireGuard desktop client, then the same scenario works.

Thanks!

aramperes commented 2 years ago

@ViRb3 It's only the parsing for now, I merged that in in case someone has time to implement this feature. Otherwise I'll start implementing it over time.

ViRb3 commented 2 years ago

Ah, makes sense. I don't have the time or knowledge myself, but looking forward to this feature getting implemented by any other means.

aramperes commented 2 years ago

No problem! It'll be a very useful feature to have for sure. It's not going to be super difficult to adapt the existing code, my main blocker is abstracting the smoltcp core enough so that it can support both directions.

jonmcox commented 1 year ago

I think having the option to support [--reverse] would be very nice. This would open up numerous options for connecting to client resources. Any thoughts on when (or if) this feature will be supported? In either case this is a very nice project!

whmountains commented 1 year ago

I would be interested in sponsoring this feature and/or placing a bounty if the option were available.

vi commented 1 year ago

Note: my new project wgslirpy may be of interest of you want onetun, but in reverse.

It forwards all the connections, not one by one.

colemickens commented 9 months ago

onetun looks like exactly what I need -- I want to terminate TLS and accept HTTP on a remote server, reverse proxy to a onetun peer that is forwarding traffic to caddy, running on localhost, configured to route to various local services on various ports based on the Host header.

I can't quite wrap my head around wgslirpy and don't really want it to mess with DNS, nor forward all traffic.

I don't want to be annoying, but I would definitely donate to this cause or someone taking it on. I don't think smoltcp is something I can just dive into with my knowledge, nor is something I can dig into right now.

It seems like onetun with reverse connections, combined with caddy on other sides, is basically cloudflare tunnel but freer, and better.

vi commented 9 months ago

@colemickens , If you have proper access both to the remote server and to local host then maybe better just configure proper, in-kernel Wireguard connection (with a port redirection rule) instead of using user-space hacks?

colemickens commented 9 months ago

@vi Indeed that's the approach I'm trialing now. However, this is part of a dev environment that otherwise is very, very portable, non-root, etc. My colleagues will probably rather tolerate "use root" over "use cloudflare" but the real ideal would be to just have this packaged as another script we run with process-compose.

vi commented 9 months ago

@colemickens For easy, but temporary dev setup you may want to use ngrok or something like that, if you want to just accept global https:// on local machine.

Nsttt commented 9 months ago

Very interested on this working btw. I don't have as much Rust knowledge but I'd like to give it ago. Any advice or place in the code where I should start looking ? @aramperes