qdm12 / gluetun

VPN client in a thin Docker container for multiple VPN providers, written in Go, and using OpenVPN or Wireguard, DNS over TLS, with a few proxy servers built-in.
https://hub.docker.com/r/qmcgaw/gluetun
MIT License
8.04k stars 371 forks source link

Help: how to setup port forwarding on static port #201

Closed piramiday closed 4 years ago

piramiday commented 4 years ago

I'm running the latest image in a simple docker compose -- I'd like to understand how to deal with port-forwarding and static ports.

generally, I have always seen Docker images being built with ports that, internally, are static. that's because the way of doing things in Docker, of course, makes it trivial for the user to specify what to do with those ports. for example, we could have a docker-compose with 10 different containers/services, all publishing a different website on port 80. the user could easily remap those ports, e.g. remapping 81:80 for service 1, 82:80 for service 2, and so on, or even add yet another container to really expose port 80 like a reverse proxy, leaving all the other ports internal in the docker-compose-specific private network.

I have successfully played around with https://github.com/qdm12/port-checker and port forwarding, but that container has been specifically built with a dynamic port, which is great but in general is not the case.

NOTE: on this, I think there is some confusion between the environment variable PORT and LISTENING_PORT. e.g. https://github.com/qdm12/private-internet-access-docker/issues/53#issuecomment-541997132 and https://github.com/qdm12/port-checker#setup vs. https://github.com/qdm12/port-checker#environment-variables ...

so my question is: how should I deal with static ports and remapping, if I want to exploit this gluetun service? I cannot exploit the Docker way of remapping ports if I'm specifying a network_mode: "service:gluetun".

as an example, we could think of https://github.com/linuxserver/docker-nginx/ which exposes static port 80 internally, and expects the user the remap it to a different port, if needed.

I hope my question is clear. thanks!

qdm12 commented 4 years ago

Hi there, thanks for the detailed question. There is quite a few instructions / examples here in the readme. Actually let me know if you haven't noticed them, maybe it's time to make them more obvious.

You should map the ports in gluetun for the other containers. So specify - 80:80/tcp in gluetun even though it's nginx connected to gluetun (because under the hood they share the same network stack so are the same host).

EDIT: Thanks for the note on port-checker, I need to revamp it! And yes it was PORT before and now LISTENING_PORT although it should be retrocompatible.... should.

piramiday commented 4 years ago

Actually let me know if you haven't noticed them, maybe it's time to make them more obvious.

I'm positive I read all of them at first, but I might have skipped the last one in my earlier re-reading before opening the issue. of course, that seems to be the answer. I'll try it out!

And yes it was PORT before and now LISTENING_PORT

well, I liked PORT more! there's only a single noteworthy port there, right? why complicate the variable?

although it should be retrocompatible.... should.

unless I was mistaken, it was not. :man_shrugging:

piramiday commented 4 years ago

oh wait, maybe I closed this too soon.

what if I have two containers in the docker-compose, both using port 80 internally? I would be able to remap only one of those ports in the ports section of the gluetun service.

qdm12 commented 4 years ago

Yeah I like PORT too but I preferred to make it obvious it was a listening port and not whatever other port that could be. Maybe a bit too paranoid though, I'll eventually revisit (I need to add a static ui too, that white page is just horrible) it.

Regarding your question on two containers listening on the same port, well you can't haha. Just change the internal listening port of one of the connected containers. On a side note, make sure to set EXTRA_SUBNETS to be able to reach those published ports. See #82 which should be fixed sometime soon.

piramiday commented 4 years ago

Yeah I like PORT too but I preferred to make it obvious it was a listening port and not whatever other port that could be. Maybe a bit too paranoid though, I'll eventually revisit (I need to add a static ui too, that white page is just horrible) it.

yeah, fair enough. I was just confused because I was setting PORT and it was still listening on 8000, so... FYI.

Regarding your question on two containers listening on the same port, well you can't haha.

that's unfortunate. quite often, as I was saying, container maintainers assume as a fact that the user can remap the ports at will, but in this case it's not always possible.

I was reading this comment of yours, and I was hoping there would have been some new developments in the meantime:

Run the following (i assume your container is named pia)

docker exec -it pia /bin/sh
# get your forwarded port, say 6901 for example
port=`cat $PORT_FORWARDING_STATUS_FILE`
iptables -t nat -I PREROUTING -s 0/0 -d 127.0.0.1 -p tcp --dport 8000 -j REDIRECT --to-ports $port
iptables -t nat -I PREROUTING -s 0/0 -d 127.0.0.1 -p udp --dport 8000 -j REDIRECT --to-ports $port
exit

That basically redirects port 8000 to port 6901 ($port) in the container with protocols TCP and UDP. Please then try it is accessible through port 8000 and report back? πŸ˜ƒ

Sorry I haven't used port forwarding really, and I don't want to change all my infrastructure to test πŸ˜†

If this works, I'll add it to the port-forward.sh script and change the readme, so that you could even just publish port 8000 to access the forwarded port, no matter which one is forwarded. Note however that from an external point of view, it will still be 6901 or any port PIA assigns you.

Originally posted by @qdm12 in https://github.com/qdm12/private-internet-access-docker/issues/53#issuecomment-553670809

if it might be of help, I could reopen and rename this issue to signal the fact that with some clever iptables routing you could gain the feature of publishing two static ports to different external ports.

piramiday commented 4 years ago

another question on the same argument: is there a way to publish a forwarded port?

as I see it, I need gluetun to start in order to establish a vpn connection and get assigned a forwarded port. but, on the other hand, I cannot publish the port on my webserver container -- I would need to publish it on gluetun, instead. doing that would require to recreate and restart the container, thus dropping the connection and the forwarded port together with it.

qdm12 commented 4 years ago

You would want to have the web server accessible through the vpn server side and on your vpn client right? Actually that would be a great addition, some iptable rule 'pinning' the port forwarded. So you could listen on port 9000 for example across reboots, and that would listen on whatever port you get forwarded by pia, using some redirection somehow. I'll dig into it.

EDIT: I was re-writing my intra-docker containers iptables rules so that looks like the perfect extra step in my journey :wink: EDIT 2: Pre routing iptables rules do allow that, I'll code it tonight/tomorrow.

The rule should be (maybe tun0 or eth0) iptables -t nat -A PREROUTING -i tun0 -p tcp --dport <port-forwarded> -j REDIRECT --to-port <published-local-port>

EDIT 3: I noticed that's what you said as well above I think πŸ˜„

qdm12 commented 4 years ago

Also one last FYI, I need to work on instructions to setup the vpn container as your default gateway. That way, you should be able to just specify it in the network configuration of a container. I'm not too sure of disadvantages/consequences yet though, but it will be an interesting alternative nonetheless.

YAE (yet another edit): Can you host a web server accessible through your vpn ip address using port forwarding? I can't, it seems to only work for torrenting for me (and others).

entrptaher commented 4 years ago

I tried to run the container in a digitalocean server and access it from outside, it was failing for the whole time. Doing all kind of ufw allow PORT magic failed. Disabling the firewall did not work at all. :(

It allowed my connection when I put my own IP address as EXTRA_SUBNETS.

qdm12 commented 4 years ago

@entrptaher oh nice, thanks for letting us know! Putting your vpn ip address in EXTRA_SUBNETS right? (it's a routing issue). I'll fix it/test it now

entrptaher commented 4 years ago

Also, it takes extra 6 seconds to reach the port from outside. I tested 100+ times, from inside the server it's 10-100ms, from outside 6s or more only on the proxy port.

qdm12 commented 4 years ago

I don't think the latency has something to do with the container though. But good to know. Again, what ip address did you set in EXTRA_SUBNETS? Your local public ip address? Or your vpn ip address? That may relate to #82 (another issue but it might be the same solution).

piramiday commented 4 years ago

we are getting a bit off-topic, here! my fault as well. as I see it, the issues are:

  1. how to do classic Docker remapping stuff with gluetun,
  2. how to remap multiple static ports,
  3. how to remap port-forwarded port.

@qdm12 let me know if I should open separate issues, instead.

qdm12 commented 4 years ago

Let's close this issue soon and create new ones as it's indeed hard to follow.

@entrptaher Can you please just let me know what IP address you put in EXTRA_SUBNETS to make the port forwarding work? Thanls!

@piramiday :

  1. what if I have two containers in the docker-compose, both using port 80 internally?

    I'm quite sure there is no way to do that as the Docker daemon will complain (out of the container control). You could however add some rules to redirect the port

  2. how to do classic Docker remapping stuff with gluetun

    That's mentioned in the readme, I don't think you can due to the nature of the networking here. Feel free to clarify if I misunderstood

  3. how to remap multiple static ports,

    That is about 1. right?

  4. how to remap port-forwarded port.

    Let's continue the conversation on #203

entrptaher commented 4 years ago

My static public/client IP from where I am accessing the proxy. So assume 123.123.123.123 is my IP,

Then I put,

EXTRA_SUBNETS=123.123.123.123/24

This is not my private IP like 127.0.0.1 or anything else.

Here is the docker-compose.yml file,

version: "3.7"
services:
  gluetun:
    image: qmcgaw/private-internet-access

    cap_add:
      - NET_ADMIN

    network_mode: bridge

    ports:
      - 8001:8000/tcp # Built-in HTTP control server
      - 8891:8888/tcp # Tinyproxy

    # command:
    environment:
      - VPNSP=private internet access
      - USER=
      - PASSWORD=
      - REGION=Switzerland

      - FIREWALL=off

      - TINYPROXY=on
      - TINYPROXY_LOG=Connect
      - TINYPROXY_USER=
      - TINYPROXY_PASSWORD=

      - EXTRA_SUBNETS=123.123.123.123/24

    restart: always

Run this with this,

docker-compose up

And here's how I can access it from my client,

curl ifconfig.co # 123.123.123.123
curl -x http://serverip:8891 ifconfig.co # some proxy ip 85.85.85.85
piramiday commented 4 years ago

@entrptaher I do not see how your docker compose and tinyproxy config is relevant to this issue -- I suggest you to mark your messages as off-topic and open up another issue.

@qdm12

what if I have two containers in the docker-compose, both using port 80 internally?

I'm quite sure there is no way to do that as the Docker daemon will complain (out of the container control). You could however add some rules to redirect the port

of course one can have a single docker compose with multiple containers, each exposing port 80. with that "that", do you mean that you cannot expose both ports 80 from the gluetun container with a simple docker remapping? if not, please elaborate.

how to do classic Docker remapping stuff with gluetun

That's mentioned in the readme, I don't think you can due to the nature of the networking here. Feel free to clarify if I misunderstood

I don't get it, why would exposing port 80 on the gluetun container work, as opposed to exposing it in the original, say, nginx container? I might be missing docker networking basics, here, so feel free to link to some docker guide.

how to remap multiple static ports,

That is about 1. right?

it seems to me the answer is: you cannot do it without internal remapping.

how to remap port-forwarded port.

Let's continue the conversation on #203

great. closing this for now.

qdm12 commented 4 years ago

Thanks piramiday. Indeed @entrptaher is out topic, but thanks for trying to help nonetheless.

When you specify "service:gluetun" or "container:gluetun" as the network mode of other containers, they basically are processes running using the network stack of gluetun. So they're not hosts on their own, and therefore the only host here is gluetun. For some reason, docker/docker-compose decided that you should list your published port in the host container (gluetun) instead of in the 'only process' child containers, but it would result in the same anyway. That's a bit of a bummer indeed, I myself like having expose ports block for documentation in compose files.

Because there is only one host gluetun, you cannot have two processes (=connected containers) listening on the same port (as any host/machine would say).

I agree it's a bit annoying the way it works, especially documentation wise it is confusing to have ports published/exposed on gluetun for other containers.

piramiday commented 4 years ago

understood, thanks for the explanation. so effectively the first container/service/process to bind to port 80 would succeed, while the second would complain that somebody is already listening on port 80. I thought they kept their independence as hosts and that they would both succeed.

okay, so this issue is really closed:

entrptaher commented 4 years ago

Sorry for making this mistake, I had this problem and saw this was somehow related to port forwarding to a specific port. Specially the proxy ports were not reachable from outside the network provided by this docker image. The port created by tinyproxy is reachable only using the gluetun service.

I should create a new issue then which is a bit more specific to tinyproxy.

qdm12 commented 4 years ago

@piramiday yeah indeed you are out of luck. I had two Deluge instances with their web ui listening on port 8112. I had to fiddle with the files of the container (not possible through env variables) to change the second one to 8113 for example. So not the best UX but still feasible most of the time.

@entrptaher It's indeed annoying to have to specify subnets in EXTRA_SUBNETS to allow to access through your LAN and/or outside your LAN. Note that if your client ip is out of your LAN you should specify a subnet mask of /32 instead of /24 otherwise you would potentially allow a bunch of public IP addresses. On a related note, #82 has to be addressed. One way is to add routes to all IP addresses except the vpn ones (similar to specify all IPs in EXTRA_SUBNETS). The only thing is I'm not sure of the security implications (i.e. could anyone access your LAN through the vpn server end? Especially using a port forwarded... But I'll fix that up relatively soon.

entrptaher commented 4 years ago

As for multiple ports, not sure if this is helpful, but I used the compatibility mode with deploy replica and port ranges. The issue shared here is with internal ports πŸ€” , but does compatibility and replicas help?

version: '3.7'
services:
  Api:
    ports:
      - 8001-8005:8000
    deploy:
      - replicas: 5

Then ran it using this,

docker-compose --compatibility up

I also ran multiple projects this way to keep them separate.

docker-compose -p one up
docker-compose -p two up