# vpnwrap | |||||
scripts for doing things | |||||
it runs a thing in a separate net namespace with only a vpn's network interface | |||||
so it's literally impossible for it to access the outside world, but it also | |||||
opens a single port into the program from your local machine so that you can | |||||
still talk to it for like rpcs or whatever | |||||
you'll probably have to modify these for your own setup, but I put everything | |||||
in vars at the top of scripts so it's not too hard, just search for `CONFIG:` | |||||
and then comment out the `exit 1` if there is one | |||||
* mostly in config.txt | |||||
* some things in transmissionwrap.sh | |||||
* take a look at natfwd.sh in case you need to change anything there | |||||
## dependencies | |||||
* iptables | |||||
* ufw | |||||
* transmission | |||||
* openvpn | |||||
* bash I guess | |||||
## installation | |||||
I set most this up on `/opt/vpnwrap` so the systemd services are written | |||||
assuming that, but once you create the dirs for everything it's fairly easy | |||||
make sure you download config files for your vpn, and configure it for those | |||||
the only other thing to remember is to deal with the dns resolver config, since | |||||
in a net namespace glibc doesn't use `/etc/resolv.conf` | |||||
add this to `/etc/netns/vpnns/resolv.conf` (or whatever) to fix that | |||||
``` | |||||
nameserver 208.67.222.222 | |||||
nameserver 208.67.220.220 | |||||
``` | |||||
# CONFIG: comment out the exit line after you configure these | |||||
exit 1 | |||||
EXTPORTS='put your ports here, not commas just spaces' | |||||
EXTIF=put your network local ip addr here, the 192.168.0.0 one | |||||
NSNAME=vpnns | |||||
# don't forget to download/configure these too | |||||
VPNCONFIG=$SCRIPTPATH/mullvad_config_linux_nl_ams/mullvad_nl_ams.conf | |||||
VPNCA=$SCRIPTPATH/mullvad_config_linux_nl_ams/mullvad_ca.crt | |||||
VPNCREDS=$SCRIPTPATH/mullvad_config_linux_nl_ams/mullvad_userpass.txt | |||||
#!/bin/bash | |||||
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then | |||||
echo 'usage: ./natfwd.sh <action> <extif> <port> <netns>' | |||||
exit 1 | |||||
fi | |||||
if [ "$UID" -ne 0 ] && [ -z "$SKIP_ROOT_CHECK" ]; then | |||||
echo 'must be root!' | |||||
exit 1 | |||||
fi | |||||
# Check if we should add an exemption for UFW. | |||||
ufw=no | |||||
if [ "$1" = "--ufw" ]; then | |||||
ufw=yes | |||||
shift | |||||
fi | |||||
action=$1 | |||||
extip=$2 | |||||
ports=$3 | |||||
netnsname=$4 | |||||
pipeouter=$netnsname"_veth0" | |||||
pipeinner=$netnsname"_veth1" | |||||
# use /31 here to minimize chance of something fucking up later | |||||
# CONFIG: change pouterip and pinnerip to something in case there's a conflict, | |||||
# but there probably won't be | |||||
pouterip=192.168.69.42 | |||||
pinnerip=192.168.69.43 | |||||
pouteripmasked=$pouterip/31 | |||||
pinneripmasked=$pinnerip/31 | |||||
# Used in testing: | |||||
# -A PREROUTING -d 192.168.1.9/32 -p tcp -m tcp --dport 1337 -j DNAT --to-destination 10.13.37.69 | |||||
# -A POSTROUTING -d 10.13.37.69/32 -p tcp -m tcp --dport 1337 -j SNAT --to-source 10.13.37.42 | |||||
dnatrule="PREROUTING -d $extip -p tcp --dport $port -j DNAT --to-destination $pinnerip" | |||||
snatrule="POSTROUTING -d $pinnerip -p tcp --dport $port -j SNAT --to-source $pouterip" | |||||
function get_dnat_rule { | |||||
port=$1 | |||||
echo "PREROUTING -d $extip -p tcp --dport $port -j DNAT --to-destination $pinnerip" | |||||
} | |||||
function get_snat_rule { | |||||
port=$1 | |||||
echo "POSTROUTING -d $pinnerip -p tcp --dport $port -j SNAT --to-source $pouterip" | |||||
} | |||||
function get_ufw_rule { | |||||
port=$1 | |||||
echo "proto tcp from any to $pinnerip port $port" | |||||
} | |||||
# CONFIG: make sure this is set right | |||||
ufwcomment="natfwd_"$netnsname | |||||
if [ $action = "enable" ]; then | |||||
echo 'enabling' | |||||
set -x | |||||
# Make sure we have IP forwarding enabled. We don't bother disabling | |||||
# this later. | |||||
sysctl -w net.ipv4.ip_forward=1 > /dev/null | |||||
# Create the interface and move to the network namespace. | |||||
ip link add $pipeouter type veth peer name $pipeinner | |||||
ip link set $pipeinner netns $netnsname | |||||
# Assign IP addresses to the new devices. | |||||
ip addr add $pouteripmasked dev $pipeouter | |||||
ip netns exec $netnsname ip addr add $pinneripmasked dev $pipeinner | |||||
# Create the routing rules to allow the packets. | |||||
for p in $ports; do | |||||
iptables -t nat -A $(get_dnat_rule $p) | |||||
iptables -t nat -A $(get_snat_rule $p) | |||||
done | |||||
[ "$ufw" = "yes" ] && [ -z "$(ufw status | grep '# $ufwcomment')" ] && \ | |||||
for p in $ports; do | |||||
ufw route allow $(get_ufw_rule $p) comment $ufwcomment | |||||
done | |||||
# Enable the interfaces. | |||||
ip link set dev $pipeouter up | |||||
ip netns exec $netnsname ip link set dev $pipeinner up | |||||
elif [ $action = "disable" ]; then | |||||
echo 'disabling' | |||||
set -x | |||||
# Delete the routing rules. Deleting the interfaces doesn't | |||||
# automatically remove these. | |||||
for p in $ports; do | |||||
iptables -t nat -D $(get_dnat_rule $p) | |||||
iptables -t nat -D $(get_snat_rule $p) | |||||
done | |||||
if [ "$ufw" = "yes" ]; then | |||||
rulenum=$(ufw status numbered | grep "# $ufwcomment" | awk '{ print substr($1, 2, length($1) - 2) }') | |||||
if [ -n "$rulenum" ]; then | |||||
yes | ufw delete $rulenum | |||||
fi | |||||
fi | |||||
# Deleting this interface deletes everything else associated with it. | |||||
ip link delete $pipeouter | |||||
else | |||||
echo 'actions are "enable" or "disable"' | |||||
exit 1 | |||||
fi | |||||
set +x | |||||
echo 'OK' | |||||
#!/bin/bash | |||||
SCRIPTPATH=$(dirname $0) | |||||
source $SCRIPTPATH/config.txt | |||||
# CONFIG: comment this line out when you set the daemondir to what you want | |||||
exit 1 | |||||
DAEMONDIR=/var/lib/transmission/info | |||||
PIDFILE=$DAEMONDIR/transmission.pid | |||||
function natfwd_do { | |||||
$SCRIPTPATH/natfwd.sh --ufw $1 $EXTIF "$EXTPORTS" $NSNAME | |||||
} | |||||
case $1 in | |||||
enable) | |||||
set -e | |||||
natfwd_do enable | |||||
echo 'VPN IP address:' $(ip netns exec $NSNAME curl ifconfig.me) | |||||
ip netns exec $NSNAME \ | |||||
/usr/bin/transmission-daemon --log-error -g $DAEMONDIR -x $PIDFILE | |||||
;; | |||||
disable) | |||||
set -x | |||||
if [ -f "$PIDFILE" ]; then | |||||
xmpid=$(cat $PIDFILE | sed 's/\;.*//') | |||||
echo 'killing' $xmpid | |||||
kill -15 $xmpid | |||||
fi | |||||
natfwd_do disable | |||||
;; | |||||
reload) | |||||
/bin/kill -s HUP $(cat $PIDFILE) | |||||
;; | |||||
esac | |||||
#!/bin/bash | |||||
SCRIPTPATH=$(dirname $0) | |||||
source $SCRIPTPATH/config.txt | |||||
#echo "===== $script_type =====" >> hook.txt | |||||
case $script_type in | |||||
up) | |||||
# Make the namespace and move the VPN interface to it, then | |||||
# configure it and set it to UP. | |||||
set -ex | |||||
ip netns exec $NSNAME ip link set dev lo up | |||||
ip link set dev "$dev" up netns $NSNAME mtu "$link_mtu" | |||||
ip netns exec $NSNAME ip addr add "$ifconfig_local" dev "$dev" peer "$route_vpn_gateway" | |||||
ip netns exec $NSNAME ip link set "$dev" up | |||||
;; | |||||
route-up) | |||||
set -ex | |||||
# Add the IP address and turn it on. You might need these sometimes maybe. | |||||
#ip netns exec $NSNAME ip addr add "$ifconfig_local" dev "$dev" peer "$route_vpn_gateway" | |||||
#ip netns exec $NSNAME ip link set "$dev" up | |||||
# Add the default route. | |||||
ip netns exec $NSNAME ip route add default dev "$dev" | |||||
;; | |||||
down) | |||||
# Turn the network interface off | |||||
echo "disabling interface $dev..." | |||||
ip netns exec $NSNAME ip link set "$dev" down | |||||
res=$? | |||||
if [ "$res" -ne "0" ]; then | |||||
echo "disabling $dev exited with code $res, continuing..." | |||||
fi | |||||
;; | |||||
esac | |||||
[Unit] | |||||
Description=VPN Namespace Isolation | |||||
After=network.target | |||||
[Service] | |||||
Type=oneshot | |||||
RemainAfterExit=yes | |||||
ExecStart=/opt/vpnwrap/nsctl.sh enable | |||||
ExecStop=/opt/vpnwrap/nsctl.sh disable | |||||
Restart=on-failure | |||||
[Install] | |||||
WantedBy=multi-user.target | |||||
[Unit] | |||||
Description=VPN Transmission Daemon | |||||
After=vpnns-vpn.service | |||||
[Service] | |||||
Type=forking | |||||
GuessMainPID=true | |||||
ExecStart=/opt/vpnwrap/transmissionwrap.sh enable | |||||
ExecStop=/opt/vpnwrap/transmissionwrap.sh disable | |||||
ExecReload=/opt/vpnwrap/transmissionwrap.sh reload | |||||
Restart=always | |||||
RuntimeMaxSec=86400 | |||||
[Install] | |||||
WantedBy=multi-user.target | |||||
[Unit] | |||||
Description=VPN Namespace Networking | |||||
After=vpnns-ns.service | |||||
[Service] | |||||
Type=simple | |||||
ExecStart=/opt/vpnwrap/vpnwrap.sh | |||||
Restart=on-failure | |||||
[Install] | |||||
WantedBy=multi-user.target | |||||
#!/bin/bash | |||||
SCRIPTPATH=$(dirname $0) | |||||
source $SCRIPTPATH/config.txt | |||||
export PATH=$SCRIPTPATH:$PATH | |||||
env | |||||
echo '==== STARTING OPENVPN ====' | |||||
openvpn \ | |||||
--client \ | |||||
--config $VPNCONFIG \ | |||||
--ca $VPNCA \ | |||||
--auth-user-pass $VPNCREDS \ | |||||
--auth-retry nointeract \ | |||||
--ifconfig-noexec \ | |||||
--route-noexec \ | |||||
--script-security 2 \ | |||||
--up $SCRIPTPATH/vpnhook.sh \ | |||||
--route-up $SCRIPTPATH/vpnhook.sh \ | |||||
--down $SCRIPTPATH/vpnhook.sh | |||||
echo '==== EXITED OPENVPN ====' | |||||