@@ -0,0 +1,42 @@ | |||
# 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 | |||
``` | |||
@@ -0,0 +1,11 @@ | |||
# 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 | |||
@@ -0,0 +1,119 @@ | |||
#!/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' | |||
@@ -0,0 +1,45 @@ | |||
#!/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 | |||
@@ -0,0 +1,43 @@ | |||
#!/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 | |||
@@ -0,0 +1,14 @@ | |||
[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 | |||
@@ -0,0 +1,16 @@ | |||
[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 | |||
@@ -0,0 +1,12 @@ | |||
[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 | |||
@@ -0,0 +1,24 @@ | |||
#!/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 ====' | |||