Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nurupo committed Jan 10, 2022
0 parents commit 947a011
Show file tree
Hide file tree
Showing 7 changed files with 1,477 additions and 0 deletions.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

127 changes: 127 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# WireGuard VPN in a Network Namespace

A script for setting up and managing a WireGuard VPN in a network namespace.

Supports Private Internet Access (PIA) VPN only, but can be (relatively) easily modified to work with any other VPN provider as the PIA bits are not that tightly integrated with the rest of the script.

This is just something I wrote for myself that I'm sharing on the off chance someone finds it useful.
I have no plans on supporting any other VPN provider for now, but if in the future I change which VPN service I use, I might update the script and rename the repo appropriately.

## FAQ

### Why run a VPN in a network namespace?

By running a VPN in a network namespace you get these awesome perks for free:

- Ability to run only selected applications through VPN, instead of running everything on your computer through it.
- Kill-switch functionality, as the WireGuard VPN is the only network interface in the network namespace, aside from the loopback, so if it goes down -- there is no network in that namespace.
- DNS protection, as applications inside the namespace will use VPN's DNS servers by default.
- Ability to nest VPN connections. Who doesn't want to be behind 7 proxies?

To get these features in traditional, non-namespace, VPN setups you would have to create a GID per VPN, setup iptables/nftables mangle rules to fwmark application traffic based on GID, setup masquerade nat rules, add a routing table for the VPN traffic, setup routing rules for the fwmark, etc. -- so much work and potential to get things wrong.

### Why not just use \<this-other-script\>?

You might be wondering why I don't just use [`pia-foss/manual-connections`](https://github.com/pia-foss/manual-connections) or something else.

The issue is that all these scripts use [`wg-quick`](https://manpages.debian.org/unstable/wireguard-tools/wg-quick.8.en.html), which doesn't support creating WireGuard connections in a separate namespace and does a lot of unnecessary for us stuff.

To create a WireGuard connection in a separate namespace, [you want to create the wg interface in the init namespace and then move it into a separate namespace, that way it remembers that it was created in the init namespace and routes traffic though it](https://www.wireguard.com/netns/).

### How to access a web server running in a network namespace / how to forward a port?

You can use the provided `netns-socat-forward.service` as an example of how to forward connections made on host's 127.0.0.1:1234 to 127.0.0.1:1234 inside the network namespace.

Edit `netns-socat-forward.service` for your needs: host ip port, netns ip port, tcp or udp, vpn name, rename the file, change syslog identifier, etc., and run:

```bash
sudo install -o root -g root -m 644 netns-socat-forward.service /etc/systemd/system/netns-socat-forward.service
sudo systemctl daemon-reload
sudo systemctl start netns-socat-forward.service
sudo systemctl status netns-socat-forward.service
```

# Setup

```bash
# install the scripts
sudo install -o root -g root -m 755 -D vpn /usr/local/bin/vpn
sudo install -o root -g root -m 755 -D bash-completion/completions/vpn /usr/local/share/bash-completion/completions/vpn
# install deps (Debian/Ubuntu)
sudo apt-get install curl gawk jq wireguard-tools
# this will create directory structure under /var/lib/vpn and prompt about missing deps
vpn regions
# setup other things
sudo install -o root -g root -m 600 pia_auth.sh /var/lib/vpn/secret/pia_auth.sh
# enter your username/password
sudo editor /var/lib/vpn/secret/pia_auth.sh
sudo install -o root -g root -m 600 pia_ca.rsa.4096.crt /var/lib/vpn/secret/pia_ca.rsa.4096.crt
```

By default the script uses `/var/lib/vpn` for its purposes.
You can change the location by changing the `VPN_ROOT_DIR` variable in the script.
Note that `VPN_ROOT_DIR` will be chown'ed to root to protect username/password whenever the script runs.

# Usage

```bash
$ vpn --help
vpn - creates a network namespace with a PIA WireGuard VPN connection in it.

Usage: [sudo ] vpn regions list available VPN regions
[sudo ] vpn up <name> <region-id> create VPN <name> connected to <region-id> region
[sudo ] vpn down <name> delete VPN <name>
[sudo ] vpn list list created VPNs
[sudo ] vpn stat <name> show information for VPN <name>
[sudo -E] vpn exec <name> <program> [args] run a program in VPN <name>'s network namespace as the current user
It's preferred that you don't call the script with "sudo", the script will
auto-sudo with the correct flags on its own.
A bit counter-intuitively, "sudo -E vpn exec" will run the program as the user
who invoked sudo, not necessarily as the root user. Run it as "sudo sudo vpn
exec" if you want to run it as root.
```
Just for fun, if you want to get behind 7 proxies, you can do it like this:
```bash
/bin/bash
vpn up vpn1 region1 && trap "vpn down vpn1" EXIT && vpn exec vpn1 /bin/bash
# we are inside vpn1's namespce
vpn up vpn2 region2 && trap "vpn down vpn2" EXIT && vpn exec vpn2 /bin/bash
# we are inside vpn2's namespce
vpn up vpn3 region3 && trap "vpn down vpn3" EXIT && vpn exec vpn3 /bin/bash
# we are inside vpn3's namespce
vpn up vpn4 region4 && trap "vpn down vpn4" EXIT && vpn exec vpn4 /bin/bash
# we are inside vpn4's namespce
vpn up vpn5 region5 && trap "vpn down vpn5" EXIT && vpn exec vpn5 /bin/bash
# we are inside vpn5's namespce
vpn up vpn6 region6 && trap "vpn down vpn6" EXIT && vpn exec vpn6 /bin/bash
# we are inside vpn6's namespce
vpn up vpn7 region7 && trap "vpn down vpn7" EXIT && vpn exec vpn7 /bin/bash
# we are inside vpn7's namespce
# do your stuff here
```

Then just logout 8 times, to get out of each bash session and trigger the set traps.

When nesting past a few VPNs the connection becomes rather unstable, so you might want to do less than 7 nestings.

Note that the following will NOT work [because of this](https://serverfault.com/a/961592), i.e. you need to keep a process in a network namespace for the nested namespaces to work, like what all these bash processes in the snippet above did:

```bash
# you would think this would work but it doesn't
vpn up vpn1 region1
vpn exec vpn1 vpn up vpn2 region2
vpn exec vpn2 vpn up vpn3 region3
vpn exec vpn3 vpn up vpn4 region4
vpn exec vpn4 vpn up vpn5 region5
vpn exec vpn5 vpn up vpn6 region6
vpn exec vpn6 vpn up vpn7 region7
vpn exec vpn7 /bin/bash
```

# License

GPL-3.0-only
43 changes: 43 additions & 0 deletions bash-completion/completions/vpn
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
_vpn()
{
local cur prev words cword
_init_completion || return

# echo "cur: ${cur[@]} | prev: ${prev[@]} | words: ${words[@]} | cword: ${cword[@]}"

if ((cword == 1)); then
COMPREPLY=($(compgen -W 'regions up down list stat exec' -- "$cur"))
return
fi

local command=${words[1]}

if ((cword == 2)); then
case $command in
down | stat | exec)
COMPREPLY=($(compgen -W "$(vpn --help 2>&1 > /dev/null && VPN_BASH_COMPLETION=1 vpn bash-completion-list)" -- "$cur"))
;;
esac
return
fi

if ((cword == 3)); then
case $command in
up)
COMPREPLY=($(compgen -W "$(vpn --help 2>&1 > /dev/null && VPN_BASH_COMPLETION=1 vpn bash-completion-regions)" -- "$cur"))
return
;;
esac
fi

if ((cword >= 3)); then
case $command in
exec)
for ((i = 3; i <= cword; i++)); do
_command_offset $i
return
done
;;
esac
fi
} && complete -F _vpn vpn
15 changes: 15 additions & 0 deletions netns-socat-forward.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# We could make this a socket activated service, but socat is so ridiculously
# small that it's not worth the trouble, unless you need hundreds of these.
[Unit]
Description=Forward TCP connections made on host's 127.0.0.1:1234 to vpn-name network namespace's 127.0.0.1:1234
After=network.target

[Service]
# Use this if you need to forward a UDP socket instead
#ExecStart=/usr/bin/socat udp4-listen:1234,fork,reuseaddr,bind=127.0.0.1 exec:'ip netns exec vpn-name socat STDIO "udp-connect:127.0.0.1:1234"',nofork
ExecStart=/usr/bin/socat tcp4-listen:1234,fork,reuseaddr,bind=127.0.0.1 exec:'ip netns exec vpn-name socat STDIO "tcp-connect:127.0.0.1:1234"',nofork
Restart=always
SyslogIdentifier=netns-socat-forward

[Install]
WantedBy=default.target
2 changes: 2 additions & 0 deletions pia_auth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PIA_USER=""
PIA_PASS=""
43 changes: 43 additions & 0 deletions pia_ca.rsa.4096.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-----BEGIN CERTIFICATE-----
MIIHqzCCBZOgAwIBAgIJAJ0u+vODZJntMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu
dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx
IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB
FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzQw
MzNaFw0zNDA0MTIxNzQwMzNaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg
QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE
AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50
ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy
bmV0YWNjZXNzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVk
hjumaqBbL8aSgj6xbX1QPTfTd1qHsAZd2B97m8Vw31c/2yQgZNf5qZY0+jOIHULN
De4R9TIvyBEbvnAg/OkPw8n/+ScgYOeH876VUXzjLDBnDb8DLr/+w9oVsuDeFJ9K
V2UFM1OYX0SnkHnrYAN2QLF98ESK4NCSU01h5zkcgmQ+qKSfA9Ny0/UpsKPBFqsQ
25NvjDWFhCpeqCHKUJ4Be27CDbSl7lAkBuHMPHJs8f8xPgAbHRXZOxVCpayZ2SND
fCwsnGWpWFoMGvdMbygngCn6jA/W1VSFOlRlfLuuGe7QFfDwA0jaLCxuWt/BgZyl
p7tAzYKR8lnWmtUCPm4+BtjyVDYtDCiGBD9Z4P13RFWvJHw5aapx/5W/CuvVyI7p
Kwvc2IT+KPxCUhH1XI8ca5RN3C9NoPJJf6qpg4g0rJH3aaWkoMRrYvQ+5PXXYUzj
tRHImghRGd/ydERYoAZXuGSbPkm9Y/p2X8unLcW+F0xpJD98+ZI+tzSsI99Zs5wi
jSUGYr9/j18KHFTMQ8n+1jauc5bCCegN27dPeKXNSZ5riXFL2XX6BkY68y58UaNz
meGMiUL9BOV1iV+PMb7B7PYs7oFLjAhh0EdyvfHkrh/ZV9BEhtFa7yXp8XR0J6vz
1YV9R6DYJmLjOEbhU8N0gc3tZm4Qz39lIIG6w3FDAgMBAAGjggFUMIIBUDAdBgNV
HQ4EFgQUrsRtyWJftjpdRM0+925Y6Cl08SUwggEfBgNVHSMEggEWMIIBEoAUrsRt
yWJftjpdRM0+925Y6Cl08SWhge6kgeswgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI
EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl
cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw
HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0
ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl
aW50ZXJuZXRhY2Nlc3MuY29tggkAnS7684Nkme0wDAYDVR0TBAUwAwEB/zANBgkq
hkiG9w0BAQ0FAAOCAgEAJsfhsPk3r8kLXLxY+v+vHzbr4ufNtqnL9/1Uuf8NrsCt
pXAoyZ0YqfbkWx3NHTZ7OE9ZRhdMP/RqHQE1p4N4Sa1nZKhTKasV6KhHDqSCt/dv
Em89xWm2MVA7nyzQxVlHa9AkcBaemcXEiyT19XdpiXOP4Vhs+J1R5m8zQOxZlV1G
tF9vsXmJqWZpOVPmZ8f35BCsYPvv4yMewnrtAC8PFEK/bOPeYcKN50bol22QYaZu
LfpkHfNiFTnfMh8sl/ablPyNY7DUNiP5DRcMdIwmfGQxR5WEQoHL3yPJ42LkB5zs
6jIm26DGNXfwura/mi105+ENH1CaROtRYwkiHb08U6qLXXJz80mWJkT90nr8Asj3
5xN2cUppg74nG3YVav/38P48T56hG1NHbYF5uOCske19F6wi9maUoto/3vEr0rnX
JUp2KODmKdvBI7co245lHBABWikk8VfejQSlCtDBXn644ZMtAdoxKNfR2WTFVEwJ
iyd1Fzx0yujuiXDROLhISLQDRjVVAvawrAtLZWYK31bY7KlezPlQnl/D9Asxe85l
8jO5+0LdJ6VyOs/Hd4w52alDW/MFySDZSfQHMTIc30hLBJ8OnCEIvluVQQ2UQvoW
+no177N9L2Y+M9TcTA62ZyMXShHQGeh20rb4kK8f+iFX8NxtdHVSkxMEFSfDDyQ=
-----END CERTIFICATE-----
Loading

0 comments on commit 947a011

Please sign in to comment.