[RFE] - Port Forwarding
triesmon opened this issue ยท 11 comments
Version of protonvpn-docker
NA
Details about Feature/Enhancement
Having the ability to connect to a port forwarding server and maybe providing an endpoint or something in the logs that prints the currently enabled forwarded port would be useful.
Here's a reference to the feature in the desktop client:
Code of Conduct & PII Redaction
- I agree to follow this project's Code of Conduct
- I have removed any sensitive personally identifying information(PII) and secrets from in this issue report.
Last time I checked, port forwarding was only available in the Windows Desktop version. Unless it has recently changed, it is not be possible to enable it in this service.
There are two features request for that :
- Support static port forwarding : https://protonmail.uservoice.com/forums/932836-proton-vpn/suggestions/45129886-static-port-forwarding
- Support port forwarding in linux app https://protonmail.uservoice.com/forums/932836-proton-vpn/suggestions/45702793-port-forwarding-support-for-linux
Last time I checked, port forwarding was only available in the Windows Desktop version. Unless it has recently changed, it is not be possible to enable it in this service.
Port forwarding works under Linux, even with an OpenVPN client ;)
First of all, append +pmp
to your OpenVPN username.
Once connected to ProtonVPN networks, we should issue multiple commands:
natpmpc
alone, which confirms (or not) the availability of Port Forwarding
If it's available, then we must do:
natpmpc -a 0 0 udp 60
natpmpc -a 0 0 tcp 60
while true ; do date ; natpmpc -a 0 0 udp 60 && natpmpc -a 0 0 tcp 60 || { echo -e "ERROR with natpmpc command \a" ; break ; } ; sleep 45 ; done
Source: https://protonvpn.com/support/port-forwarding-manual-setup/
I used 7.2.3-debug-alexis
for my experiment here - as it uses Debian. Conveniently it was just created. Anyway, the following kind of works - given some extra tools such as natpmpc
and moreutils
.
docker-compose.yml
:
version: '2.3'
services:
protonwire:
build: .
container_name: protonwire
image: localhost:5000/protonwire:latest
init: true
restart: never
network_mode: bridge
environment:
DEBUG: "0"
KILL_SWITCH: "0"
PROTONVPN_SERVER: "xxx#N"
TZ: "Etc/UTC"
cap_add:
- NET_ADMIN
sysctls:
- net.ipv4.conf.all.rp_filter=2
- net.ipv6.conf.all.disable_ipv6=1
healthcheck:
test: ["CMD", "/bin/bash", "-c", "/usr/bin/protonwire check --container --silent || exit 1"]
interval: 120s
start_period: 20s
volumes:
- type: tmpfs
target: /tmp
- type: bind
source: ./private.key
target: /etc/protonwire/private-key
read_only: true
protonwire-natpmpc:
container_name: protonwire-natpmpc
image: localhost:5000/protonwire:latest
restart: never
depends_on:
protonwire:
condition: service_healthy
network_mode: service:protonwire
environment:
TZ: "Etc/UTC"
volumes:
- type: bind
source: /tmp/protonwire-natpmpc-log
target: /log
entrypoint:
- "/bin/bash"
- "-c"
command: >
"while true; do
{ date '+%Y-%m-%d %H:%M:%S'; natpmpc -a 1 0 udp 60 -g 10.2.0.1 && natpmpc -a 1 0 tcp 60 -g 10.2.0.1 || break; } 2>&1 | ts '[%Y-%m-%dT%H:%M:%S] |' >> '/log/protonwire-natpmpc.log'; sleep 45 &
wait $!; [[ $(wc -l '/log/protonwire-natpmpc.log' | awk -F ' ' '{ print $1 }') -lt $((128 * 1024)) ]] || sed 1,$(( (128 * 1024) - (96 * 1024) ))d '/log/protonwire-natpmpc.log' | sponge '/log/protonwire-natpmpc.log';
done"
healthcheck:
test: ["CMD-SHELL", "[ $$(( $$(date '+%s') - $$(stat -c '%Y' '/log/protonwire-natpmpc.log') )) -lt 120 ] && /usr/bin/tail -n 23 '/log/protonwire-natpmpc.log' | grep -q -E 'Public IP address : [0-9]{1,3}(\\.[0-9]{1,3}){3}\\s*$$' && /usr/bin/tail -n 23 '/log/protonwire-natpmpc.log' | grep -q -E 'Mapped public port [1-9][0-9]* protocol UDP to local port 0 liftime 60\\s*$$' && /usr/bin/tail -n 23 '/log/protonwire-natpmpc.log' | grep -q -E 'Mapped public port [1-9][0-9]* protocol TCP to local port 0 liftime 60\\s*$$"]
interval: 120s
start_period: 30s
It seems to create a mapping. I tried to connect to it by throwing up a socat
web server (given socat
), like in a shell in the container
# socat \
-v -d -d \
TCP-LISTEN:<PORT_FROM_LOGS>,crlf,reuseaddr,fork \
SYSTEM:"
echo HTTP/1.1 200 OK;
echo Content-Type\: text/plain;
echo;
echo \"Server: \$SOCAT_SOCKADDR:\$SOCAT_SOCKPORT\";
echo \"Client: \$SOCAT_PEERADDR:\$SOCAT_PEERPORT\";
"
Seems to also work.
As does like (I actually use podman and podman-compose in the above):
$ podman run --rm --network container:protonwire -it ghcr.io/static-web-server/static-web-server:2 -p <PORT_FROM_LOGS> -g info
I guess one can use any container as a natpmpc
-helper. I just did what was easiest. Then one can maybe publish this ip and port via something easily accessible - or just share the logs with another container and parse them there.
The end result was this:
master...tsjk:protonvpn-docker:master
I'm using it for a service I run, and it seems ok.
@tsjk 7.3.0-alpha1 has all the tools you need to run it (though without the custom scripts from your fork).
Though adding those requires bit more work would you be open to add helpers as a sub-commands to protonwire?
Sure. Let me work on it some more. The other day I realized that I really don't want this container to die and vanish (for whatever reason - I observed some fatal indexing error when metadata fails to refresh) as that will disable ALL networking in dependent containers. If the dependent containers have this and Tor via a socks port, for instance - the vpn temporarily going down is not a big problem. I'll tend to this issue first and then look at sub-commanding. I imagine looping the protonwire script with a signal handler.
Hello guys !
I managed to make port forwarding work starting from the "caddy proxy" docker compose example in the readme, using 7.3.0-alpha1 as you suggested. I did it in an ugly way because I suck at bash but it'll probably help some people anyways (and it's thus pretty simple to understand).
Here is the important part of my docker compose :
services:
protonvpn:
container_name: protonvpn
image: ghcr.io/tprasadtp/protonwire:7.3.0-alpha1
command: "sh /config/protonvpn-init.sh"
environment:
- WIREGUARD_PRIVATE_KEY=YOURKEY
- PROTONVPN_SERVER=YOURNODEURL
cap_add:
- NET_ADMIN
sysctls:
net.ipv4.conf.all.rp_filter: 2
net.ipv6.conf.all.disable_ipv6: 1
volumes:
- type: tmpfs
target: /tmp
- /local_path_to/protonvpn-port:/config/protonvpn-port
- /local_path_to/protonvpn-init.sh:/config/protonvpn-init.sh
ports:
- "yourport:yourport"
yourservice:
image: yourservice
container_name: yourservice
network_mode: service:protonvpn
volumes:
- /local_path_to/protonvpn-port:/config/protonvpn-port # Do whatever you want with this
restart: always
Here is the entry script for protonvpn :
/usr/bin/protonwire connect --container &
sleep 10
natpmpc -a 1 0 udp 60 -g 10.2.0.1
natpmpc -a 1 0 tcp 60 -g 10.2.0.1 | grep -oP 'public\ port\ \K\w+' > /config/protonvpn-port
echo "Port written to protonvpn-port file"
cat /config/protonvpn-port
while true ; do date ; natpmpc -a 1 0 udp 60 -g 10.2.0.1 && natpmpc -a 1 0 tcp 60 -g 10.2.0.1 || { echo -e "ERROR with natpmpc command \a" ; break ; } ; sleep 45 ; done
Nothing crazy here, I start protonwire in the background, put in an arbitrary sleep because I didn't know how else I could wait for protonwire to connect successfully (there is probably a smart way to do this), and then just execute the natpmpc commands exactly like in the protonvpn documentation and extract the port number to a file on the host system via regexp.
After that I just retrieve the content of the protonvpn-port file in my other container and update my application with it.
Of course, if any error happens, everything goes to hell, it's a quick script for non critical applications, don't use it for anything important !
Here is the entry script for protonvpn :
/usr/bin/protonwire connect --container & sleep 10 natpmpc -a 1 0 udp 60 -g 10.2.0.1 natpmpc -a 1 0 tcp 60 -g 10.2.0.1 | grep -oP 'public\ port\ \K\w+' > /config/protonvpn-port echo "Port written to protonvpn-port file" cat /config/protonvpn-port while true ; do date ; natpmpc -a 1 0 udp 60 -g 10.2.0.1 && natpmpc -a 1 0 tcp 60 -g 10.2.0.1 || { echo -e "ERROR with natpmpc command \a" ; break ; } ; sleep 45 ; done
i'm using @le-martre's solution for a k8s deployment:
command: ["/bin/bash", "-c"]
args:
[
"/usr/bin/protonwire connect --container & sleep 10; natpmpc -a 1 0 udp 60 -g 10.2.0.1; natpmpc -a 1 0 tcp 60 -g 10.2.0.1 | grep -oP 'public\\ port\\ \\K\\w+' > /config/protonvpn-port; echo \"Port written to protonvpn-port file\"; cat /config/protonvpn-port; while true; do date; natpmpc -a 1 0 udp 60 -g 10.2.0.1 && natpmpc -a 1 0 tcp 60 -g 10.2.0.1 || { echo -e \"ERROR with natpmpc command \\a\"; break; }; sleep 45; done",
]