Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Onion routing wireguard #7

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,39 @@ Rough illustration of the intended setup. See [Routing & Network Namespace
Integration][wireguard-namespace] for a more thorough explanation of how
WireGuard works across network namespaces.

If multiple layered VPN connections are used, a layout such as the one below
will be created. This is useful for additional privacy, as multiple VPN providers
would need to be compromised for any information to be leaked. Assuming none of
the VPN providers is a bottleneck, each additional layer only reduces the
performance achievable by about 10% due to protocol overheads.
```
I N T E R N E T
Λ :
| :
| :
+------------ | : ------------+ +-------------------+
| Init NS | : | | VPN NS1 |
| -- V -- | | -------- |
| / enp0s3 \ .................. / wg-vpn1 \.....................
| \ 1.2.3.4 <--------------------> 10.0.0.1 <-------------------+:
| ------- | | -------- | |:
| | | | |:
| O---------------------O | +-------------------+ |:
| | transmission-remote | | |:
| O---------:-----------O | +-----------------------------------|:--------+
| : | | VPN NS2 |: |
| ---------- | | ---------- ----v--- |
| / veth─init <------------------> veth-vpn \ / wg-vpn2 \ |
| \ 10.127.0.1 / .................\ 10.127.0.2 / \ 10.0.0.1 / |
| ---------- | | ---------- -------- |
| | | : : |
+-----------------------------+ | O : --------------- : O |
| | transmission-daemon | |
| O---------------------O |
| |
+---------------------------------------------+
```

## Installation

This package is available from the
Expand Down Expand Up @@ -61,18 +94,23 @@ expected values are set by default, most with dummy default values.
- `NETNS_NAME`:
Name to assign to the created network namespace. Network namespace names are
system global, so it's important that this name be unique.
For using multiple layers, use multiple names separated by spaces.
- `WIREGUARD_NAME`:
Name to assign to the created WireGuard network interface. The interface is
created in the default (init) namespace then moved to the VPN namespace, so
the interface name must be unique in both.
For using multiple layers, use multiple names separated by spaces.
- `WIREGUARD_PRIVATE_KEY`:
Private key assigned by the VPN provider for your WireGuard connection. _This
is sensitive,_ so by default the configuration directory and file are only
readable by root.
For using multiple layers, set multiple private keys separated by spaces.
- `WIREGUARD_ENDPOINT`:
The endpoint of the VPN provider's WireGuard server.
For using multiple layers, set multiple endpoints separated by spaces.
- `WIREGUARD_VPN_PUBLIC_KEY`:
The public key of the VPN provider's WireGuard peer.
For using multiple layers, set multiple public keys separated by spaces.
- `WIREGUARD_ALLOWED_IPS`:
Comma-separated list of IP addresses that may be contacted using the
WireGuard interface. For a namespaced VPN, where the goal is to force all
Expand All @@ -82,6 +120,14 @@ expected values are set by default, most with dummy default values.
Comma-separated list of static IP addresses to assign to the WireGuard
interface. As far as I know, WireGuard does not currently support DHCP or any
other form of dynamic IP address assignment.
- `WIREGUARD_INITIAL_MTU`:
MTU of the wireguard interface. Only applies to the initial layer if using
multiple layers. Subsequent layers will have their MTUs reduced by 80 such
as to avoid fragmentation or packet loss.
- `TUNNEL_ENABLE`:
Whether to create the tunnel (veth) network interface between the default
(init) and VPN network namespaces. Set to zero to disable or nonzero to
enable.
- `TUNNEL_INIT_NAME`:
Name to assign to the created tunnel (veth) network interface in the default
(init) network namespace.
Expand All @@ -94,6 +140,12 @@ expected values are set by default, most with dummy default values.
- `TUNNEL_VPN_IP_ADDRESSES`:
Comma-separated list of static IP addresses to assign to the tunnel interface
in the VPN network namespace.

#### Tunnel

This package provides a tunnel between the init namesapce and the created VPN
namespace so, e.g., you can control services inside the VPN namespace from
outside. If you don't need or want the tunnel, just set `TUNNEL_ENABLE=0`.

#### Namespace Overlay

Expand All @@ -107,8 +159,9 @@ leak information.
Linux network namespaces allow you to add configuration files in
`/etc/netns/$NETNS_NAME`, which will replace the existing configuration file
for processes running inside the namespace. I'd recommend overriding
`nsswitch.conf` and `resolv.conf` to use your VPN provider's name servers. See
[DNS Leaks with Network Namespaces][dns-leaks-with-netns] for more detail.
`nsswitch.conf` and `resolv.conf` to use your VPN provider's name servers.
If using multiple namespaces, this must be done for each seperately.
See [DNS Leaks with Network Namespaces][dns-leaks-with-netns] for more detail.

## Running

Expand Down Expand Up @@ -162,7 +215,6 @@ PrivateNetwork=yes
## Future Work/TODO

- Consider using `sd_notify` for service scripts to provide a status.
- Provide a way to disable the tunnel if desired.
- Once systemd 247 is widely available (probably when Fedora 34 is released),
switch to using `LoadCredentials=` for the WireGuard private key.

Expand Down
64 changes: 46 additions & 18 deletions bin/namespaced-wireguard-vpn-interface
Original file line number Diff line number Diff line change
@@ -1,40 +1,68 @@
#!/usr/bin/env bash

set -o xtrace

die() {
echo "${BASH_SOURCE[1]}: line ${BASH_LINENO[0]}: ${FUNCNAME[1]}: ${1:-Died}" >&2
exit 1
}

netns_names=($NETNS_NAME)
wg_names=($WIREGUARD_NAME)
wg_private_keys=($WIREGUARD_PRIVATE_KEY)
wg_vpn_public_keys=($WIREGUARD_VPN_PUBLIC_KEY)
wg_endpoints=($WIREGUARD_ENDPOINT)

case "$1" in
up)
ip link add "$WIREGUARD_NAME" type wireguard || die
pre_netns=""
allowed_ips="0.0.0.0/0,::0/0"
mtu="$WIREGUARD_INITIAL_MTU"
for (( i=0; i<${#netns_names[*]}; ++i))
do
ip $pre_netns link add "${wg_names[$i]}" mtu $mtu type wireguard || die
# reduction for worse-case scenario of IPv6
mtu=$(($mtu - 80))

wg set "$WIREGUARD_NAME" \
private-key <(echo "$WIREGUARD_PRIVATE_KEY") \
peer "$WIREGUARD_VPN_PUBLIC_KEY" \
endpoint "$WIREGUARD_ENDPOINT" \
allowed-ips "$WIREGUARD_ALLOWED_IPS" || die
if [ $(( $i + 1 )) -eq ${#netns_names[*]} ]
then
allowed_ips="$WIREGUARD_ALLOWED_IPS"
fi

ip link set "$WIREGUARD_NAME" netns "$NETNS_NAME" || die
ip $pre_netns link set "${wg_names[$i]}" netns "${netns_names[$i]}" || die

# Addresses are comma-separated, so to split them.
tr ',' '\n' <<<"$WIREGUARD_IP_ADDRESSES" |
xargs -I '{}' \
ip -n "$NETNS_NAME" address add '{}' dev "$WIREGUARD_NAME" || die
ip netns exec "${netns_names[$i]}" wg set "${wg_names[$i]}" \
private-key <(echo "${wg_private_keys[$i]}") \
peer "${wg_vpn_public_keys[$i]}" \
endpoint "${wg_endpoints[$i]}" \
allowed-ips "$allowed_ips" || die

ip -n "$NETNS_NAME" link set "$WIREGUARD_NAME" up || die

# Add default routes for IPv4 and IPv6
ip -n "$NETNS_NAME" -4 route add default dev "$WIREGUARD_NAME" || die
ip -n "$NETNS_NAME" -6 route add default dev "$WIREGUARD_NAME" || die
;;
# Addresses are comma-separated, so to split them.
tr ',' '\n' <<<"$WIREGUARD_IP_ADDRESSES" |
xargs -I '{}' \
ip -n "${netns_names[$i]}" address add '{}' dev "${wg_names[$i]}" || die

ip -n "${netns_names[$i]}" link set "${wg_names[$i]}" up || die

# Add default routes for IPv4 and IPv6
ip -n "${netns_names[$i]}" -4 route add default dev "${wg_names[$i]}" || die
if ip -o -6 -a | grep "${wg_names[$i]}"
then
ip -n "${netns_names[$i]}" -6 route add default dev "${wg_names[$i]}" || die
fi
pre_netns="-n ${netns_names[$i]}"
done
;;
down)
# We need to delete the WireGuard interface. It's initially created in
# the init network namespace, then moved to the VPN namespace.
# Depending how well the "up" operation went, it might be in either.
ip -n "$NETNS_NAME" link delete "$WIREGUARD_NAME" ||
ip link delete "$WIREGUARD_NAME" || die
for (( i=$((${#netns_names[*]} - 1)); i>=0; --i))
do
ip -n "${netns_names[$i]}" link delete "${wg_names[$i]}" ||
ip link delete "${wg_names[$i]}" || die
done
;;
esac

19 changes: 13 additions & 6 deletions bin/namespaced-wireguard-vpn-netns
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@ die() {

case "$1" in
up)
ip netns add "$NETNS_NAME" || die
for name in $NETNS_NAME
do
ip netns add $name || die

ip -n $name link set lo up || die
last=$name
done

if [[ -n "$PRIVATE_NETNS_BIND_MOUNT" ]]
then
umount "/var/run/netns/$NETNS_NAME" || die
mount --bind "$PRIVATE_NETNS_BIND_MOUNT" "/var/run/netns/$NETNS_NAME" || die
umount "/var/run/netns/$last" || die
mount --bind "$PRIVATE_NETNS_BIND_MOUNT" "/var/run/netns/$last" || die
fi

ip -n "$NETNS_NAME" link set lo up || die
;;

down)
ip netns delete "$NETNS_NAME" || die
for name in $NETNS_NAME
do
ip netns delete $name || die
done
;;
esac

11 changes: 7 additions & 4 deletions bin/namespaced-wireguard-vpn-tunnel
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,30 @@ die() {
exit 1
}

netns_names=($NETNS_NAME)
main_netns=${netns_names[-1]}

case "$1" in
up)
ip link add "$TUNNEL_INIT_NAME" type veth \
peer "$TUNNEL_VPN_NAME" netns "$NETNS_NAME" || die
peer "$TUNNEL_VPN_NAME" netns "$main_netns" || die

# Addresses are comma-separated, so to split them.
tr ',' '\n' <<<"$TUNNEL_INIT_IP_ADDRESSES" |
xargs -I '{}' \
ip address add '{}' dev "$TUNNEL_INIT_NAME" || die
tr ',' '\n' <<<"$TUNNEL_VPN_IP_ADDRESSES" |
xargs -I '{}' \
ip -n "$NETNS_NAME" address add '{}' dev "$TUNNEL_VPN_NAME" || die
ip -n "$main_netns" address add '{}' dev "$TUNNEL_VPN_NAME" || die

ip link set "$TUNNEL_INIT_NAME" up || die
ip -n "$NETNS_NAME" link set "$TUNNEL_VPN_NAME" up || die
ip -n "$main_netns" link set "$TUNNEL_VPN_NAME" up || die
;;

down)
EXIT_CODE=0
ip link delete "$TUNNEL_INIT_NAME" || EXIT_CODE=$?
ip -n "$NETNS_NAME" link delete "$TUNNEL_VPN_NAME" || EXIT_CODE=$?
ip -n "$main_netns" link delete "$TUNNEL_VPN_NAME" || EXIT_CODE=$?
[[ EXIT_CODE -eq 0 ]] || die
;;
esac
Expand Down
9 changes: 9 additions & 0 deletions conf/namespaced-wireguard-vpn.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ WIREGUARD_ALLOWED_IPS=0.0.0.0/0,::0/0
# interface
WIREGUARD_IP_ADDRESSES=10.0.0.1/32,fd12:3456:789a:1::1/128

# Assuming a sane VPN provider:
# IPv4: 1440
# IPv6: 1420
# If using PPPoE(typically DSL) -=8
WIREGUARD_INITIAL_MTU=1420
chrisbouchard marked this conversation as resolved.
Show resolved Hide resolved

# Enable the tunnel interface
TUNNEL_ENABLE=1

# Name of the init-facing tunnel interface
TUNNEL_INIT_NAME=veth-vpn0

Expand Down
2 changes: 2 additions & 0 deletions systemd/namespaced-wireguard-vpn-tunnel.service
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ RemainAfterExit=yes

EnvironmentFile=/etc/namespaced-wireguard-vpn/namespaced-wireguard-vpn.conf

ExecCondition=/usr/bin/test "$TUNNEL_ENABLE" -ne 0

ExecStart=/usr/sbin/namespaced-wireguard-vpn-tunnel up
ExecStopPost=/usr/sbin/namespaced-wireguard-vpn-tunnel down