Skip to content

Instantly share code, notes, and snippets.

@ethaniel
Last active April 25, 2024 16:23
Show Gist options
  • Save ethaniel/aed98d4e27ad1d971fcd78db513effcd to your computer and use it in GitHub Desktop.
Save ethaniel/aed98d4e27ad1d971fcd78db513effcd to your computer and use it in GitHub Desktop.
How to setup a Nordvpn client inside a docker container, running on a Raspberry Pi, and then route some computers on the local network through it

Intro

I live in Thailand. Sometimes my internet connection is slow, because my ISP has poor international connectivity with some destinations (Europe, for instance). Sometimes routes that the ISP chooses have a lot of packet losses, or just slow. I needed a way to route SOME of the devices on my LAN another way. I decided to use NordVPN because they have a lot of servers worldwide and my ISP provides a reliable route to SOME of them (I use docker run -it trishmapow/nordvpn-tools FR --load 30 --fping | awk '{ print $NF,$0 }' | sort -k1,1 -n | cut -f2- -d' ' | head -n 10 to find the best servers with the lowest ping for instance). However, I didn't want to deal with installing a VPN client on every device, so I decided to make one inside my Raspberry (but without having the Raspberry use the VPN connection itself, because I have other services running on it).

You will need

  1. Raspberry Pi wired by LAN cable
  2. Through raspi-config go to Advanced options - Network Interface Names - choose No to have the primary lan wired interface called eth0. Reboot.
  3. Install Docker
  4. My network is 192.168.86.0, and the IP address of my Raspberry is 192.168.86.2, adjust accordingly in the instructions below.

Steps

#####
# Create a special subnetwork just for this Docker container
#####

docker network create --subnet=172.30.0.0/16 vpn

#####
# Load Docker container that connects to NordVPN via
# a predefined token (can be obtained in their control panel)
# 172.30.0.2 will be the container's IP address (must match the subnet above)
#####

docker run -ti --net vpn --ip 172.30.0.2 --restart=unless-stopped \
           --cap-add=NET_ADMIN --cap-add=NET_RAW --name vpn \ 
           -e TOKEN=####INSERT_NORDVPN_TOKEN#### \
           -e CONNECT=Netherlands \
           -e POST_CONNECT="iptables -I FORWARD -j ACCEPT" \
           -e TECHNOLOGY=NordLynx -d ghcr.io/bubuntux/nordvpn

#####
# Confirm that the container has connected to NordVPN           
#####

docker logs vpn            

#####
# Add some iptables magic to route external traffic into the container
# 192.168.86.0 is my LAN network
# 192.168.86.2 is the IP address of my Raspberry Pi (adjust accordingly)
#####

sudo iptables -t mangle -I PREROUTING -s 192.168.86.0/24 ! -d 192.168.86.2 -j MARK --set-mark 42
sudo iptables -t nat -I POSTROUTING -j MASQUERADE
sudo iptables -I FORWARD -i eth0 -j ACCEPT      
echo "200 vpn_gateway" | sudo tee -a /etc/iproute2/rt_tables

####
# I assume that your iptables is saved every time your Raspberry shuts down.
# If it doesn't, install `netfilter-persistent`
####

# sudo apt install netfilter-persistent
# sudo netfilter-persistent save

#####
# Create a systemd service which will enable routing of traffic
# into the container every time the Raspberry restarts
# (netfilter-persistent doesn't save ip routes)
#####

cat <<'EOF' | sudo tee /usr/bin/vpn_gateway.sh
#!/bin/bash

ip route add default table vpn_gateway via 172.30.0.2
ip rule add fwmark 42 table vpn_gateway
EOF

sudo chmod +x /usr/bin/vpn_gateway.sh

cat <<'EOF' | sudo tee /lib/systemd/system/vpn_gateway.service 
[Unit]
Description=vpn_gateway Service
After=docker.service
BindsTo=docker.service
ReloadPropagatedFrom=docker.service

[Service]
ExecStart=/usr/bin/vpn_gateway.sh
ExecReload=/usr/bin/vpn_gateway.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload 
sudo systemctl enable vpn_gateway.service 
sudo systemctl start vpn_gateway.service 

#####
# Configure your LAN devices to use 192.168.86.2 as the gateway
# (instead of 192.168.1.1 default in my case).
# Your devices will now be connected through the VPN gateway.
# Done.
#####
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment