Feel free to provide feedback for the document.
BTW, I have to give a huge shoutout and thank you to the kernel team. They made this very easy. I didn't have to recompile the kernel to get iptables to work, which was awesome. This will work with the latest build of Jessey.
Torrent Over a VPN on a Raspberry Pi 3
This document will describe how to safely torrent on a Rpi3 using a VPN and
kill switch. I would
like to especially thank the kernel team at the raspberry pi organization. The
Rpi3 kernel is spectacular and makes a lot of this possible, particularly with
the iptables mangling and routing. Having done something similar on another
platform, I can only express the sheer joy I have at making this work. Well
done.
Getting this to work is a tricky problem for a few reasons. The first is that
pi users will find that the 1GB ram limit is quite restrictive. The second is
that users will probably want to use a bare mettle solution rather than
something using and interpreter for speed improvements and ram limitations.
Lastly, the kernel setup for iptables is non-trivial. The RPi kernel team has
done an excellent job at making this as simple as possible.
This document is going to describe how to configure and set up a RPi 3 to run a
torrent server over a VPN from bare metal and go through each script in detail.
This might be a bit too advanced for some users but hopefully this is going to
demystify a lot of the difficulty.
Setup and Configuration
The configuration requires the following:
1. A VPN username and login. I prefer NordVPN. I tried others and they simply didn't work.
2. The transmission client
3. A kernel with iptables, the mangle operator, and routing
4. A VPN connection called NordVPN (anything really but this guide assumes a connection that you create in network manager called NordVPN).
This guide does not get into the specifics of how to configure and set it up but other guides do.
Transmission
This section will show you how to install and configure transmission. This is my
preferred torrent manager. It is very fast, lightweight, has a remote option,
comes with a web server, and is entirely written in C++ so it runs without an
interpreter.
What this does not do is run off of a single networking interface. For example,
something like Deluge can sort of run listen only to tun0, but it doesn't work
well and is very misleading.
This section will discuss the following:
1. Install transmission
2. Create a transmission user
3. Set up the startup scripts to run transmission as the user
4. Set up scripts to run before starting transmission
Install Transmission
This will install transmission.
Code: Select all
sudo apt install transmission-daemon
This will create a transmission user. Users must create a separate user to run
transmission. This is typically done for security reasons but also because
this user will have separate packet routing for the kill switch.
**NOTE: Make sure that the transmission user has full access to the transmission
download directory.**
Code: Select all
sudo adduser transmission
name you chose will have to be the name that you use in each of the scripts.
Transmission Startup Scripts
The latest version of Ubuntu uses systemctl to manage all of the init scripts.
Many of the solutions that you will find online assume that it uses init.d or
even init. I believe Ubuntu 16.04 switched to systemctl but it probably happened
earlier.
Ubuntu places all of the systemctl scripts in /lib/systemd/system/. They are
called {Service Name}.service. The transmission startup script is:
emacs /lib/systemd/system/transmission-daemon.service
Open up this file and modify it so it looks like this.
Code: Select all
[Unit]
Description=Transmission BitTorrent Daemon
After=network.target safe-transmission.service
[Service]
User=transmission
Type=notify
ExecStart=/usr/bin/transmission-daemon -f --log-error
ExecStop=/bin/kill -s STOP $MAINPID
ExecReload=/bin/kill -s HUP $MAINPID
[Install]
WantedBy=multi-user.target
the `After=` line. The safe-transmission.service is a startup script that you
will create that will run before the transmission startup scripts.
The second really important line is that the service should run under the
transmission user. That is what the line `User=transmission` does.
Create the safe-transmission.service Startup Script
This service will run a bash script called `/usr/bin/safe_transmission.sh`
as root user.
Create the service with:
Code: Select all
sudo emacs /lib/systemd/system/safe-transmission.service
and populate it with
Code: Select all
[Unit]
Description=Setup the VPN and routing for the transmission user
Wants=network-online.target
After=network.target network-online.target networking.service
[Service]
User=root
Type=notify
ExecStart=/usr/bin/safe_transmission.sh
ExecReload=/bin/kill -s HUP $MAINPID
[Install]
WantedBy=multi-user.target
1. `User=root`: this will run the script as root user
2. `After=network.target network-online.target`
1. `network.target`: the very lowest level of networking that guarantees that networking is on but not up (eth0 probably doesn't exist)
2. `network-online.target`: ensures that networking is up and active
3. `networking.service`: probably not needed but doesn't hurt
3. `Wants=network-online.target`: this will make sure that the network is up and active
Initialize safe-transmission.sh
The next step is to make sure that systemctl knows to start the script. To do so
cd /lib/systemd/system/
sudo systemctl enable safe-transmission.service
**NOTE: I assume that you need to be in the /lib/systemd/system folder to run
the systemctl enable command but I am not entirely sure.**
Create the /usr/bin/safe_transmission.sh shell script
Remember that the safe-transmission.service simply calls the shell script. This
will get executed before the transmission-daemon service starts. The shell script
must exist within the /usr/bin directory due to how drives get mounted.
Code: Select all
emacs /usr/bin/safe_transmission.sh
Code: Select all
#! /bin/bash
if ! /sbin/ifconfig tun0 | grep -q "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00"; then
/usr/bin/nmcli con up NordVPN
fi
if /sbin/ifconfig tun0 | grep -q "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00"; then
VPNIF="tun0"
VPNUSER="transmission"
GATEWAYIP=`/sbin/ifconfig tun0 | /bin/egrep -o '([0-9]{1,3}.){3}[0-9]{1,3}' | /bin/egrep -v '-' | /bin/egrep -v ':' | /bin/egrep -v '255|(127.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})' | /usr/bin/tail -n1`
if [[ `/sbin/ip rule list | /bin/grep -c 0x1` == 0 ]]; then
/sbin/ip rule add from all fwmark 0x1 lookup $VPNUSER
fi
/sbin/ip route replace default via $GATEWAYIP table $VPNUSER
/sbin/ip route append default via 127.0.0.1 dev lo table $VPNUSER
/sbin/ip route flush cache
fi
Code: Select all
sudo chmod 766 /usr/bin/safe_transmission.sh
1. `#! /bin/bash`: start up a bash shell
2. `if ! /sbin/ifconfig tun0 | grep -q "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00"; then`
1. run ifconfig tun0
2. if that returns something with "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00" then do something
3. "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00" is the mac address for tun0.
3. `/usr/bin/nmcli con up NordVPN`: this will turn on a connection called NordVPN. This is something you have to configure yourself.
4. `VPNIF="tun0"` and `VPNUSER="transmission"`: sets variables
5. The `GATEWAYIP=`: This should be the **ip address** that gets assigned via tun0. **This is required for the routing. If this is wrong, the default routing will be wrong and you will not be able to get online.**
1. `/sbin/ifconfig tun0`: run ifconfig over tun0. Run this on the command line for the output
2. `/bin/egrep -o '([0-9]{1,3}.){3}[0-9]{1,3}'`: run egrep to find anything that might look like an ip address
3. `/bin/egrep -v '-'`: The -v flag will select the inverse of the pipe. For this, there are a lot of ip's that looks like 00-00-... and we don't want to select it.
4. `/bin/egrep -v ':'`: Some of the ip's can be ipv6. We want to get rid of those too.
5. `/bin/egrep -v '255|(127.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})'`: There will probably be a netmask. Ignore the netmasks.
6. `/usr/bin/tail -n1`: Multiple ip's might show up. Select the first one.
6. if `/sbin/ip rule list | /bin/grep -c 0x1` == 0 : This will make sure that the masking rule is not added twice
1. `/sbin/ip rule list`: list all of the ip routing rules
2. `/bin/grep -c 0x1`: find anything with the 0x1 bit set. This will come from the iptables mangling rules
3. ` == 0`: if this doesn't exist yet, create it
7. `/sbin/ip rule add from all fwmark 0x1 lookup $VPNUSER`: this will add a routing rule for the lookup table "transmission", which we will make in the next section
8. `/sbin/ip route replace default via $GATEWAYIP table $VPNUSER`: replace the default gateway for the tun0 ip address on the "transmission" ip routing table.
9. `/sbin/ip route append default via 127.0.0.1 dev lo table $VPNUSER`: add the localhost lookup to the "transmission" ip routing table.
10. `/sbin/ip route flush cache`" flush the cache. Do not forget to do this.
Add transmission to the Routing Table:
This is a very important step. Here, we need to create a new routing table for the transmission user. We will call this transmission.
Code: Select all
sudo emacs /etc/iproute2/rt_tables
and make it look like
Code: Select all
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep
200 transmission
Summary
We did the following in this section in this order:
1. Installed transmission
2. Edited /lib/systemd/system/transmission-daemon.service to start after safe-transmission.service
3. Created /lib/systemd/system/safe-transmission.service that will run safe_transmission.sh
4. Created /usr/bin/safe_transmission.sh
5. Make safe_transmission.sh into an executable
6. Add a new routing table called transmission
Configure Transmission
Remember, the transmission user will run transmission. There are many transmission configuration files around but:
**The transmission configuration file is /home/transmission/.config/transmission/settings.json**
Before editing the configuration file, make sure that transmission is off. Otherwise, your configuration
will get overwritten and will not save.
Code: Select all
sudo systemctl stop transmission-daemon
Code: Select all
{
"alt-speed-down": 50,
"alt-speed-enabled": false,
"alt-speed-time-begin": 540,
"alt-speed-time-day": 127,
"alt-speed-time-enabled": false,
"alt-speed-time-end": 1020,
"alt-speed-up": 50,
"bind-address-ipv4": "0.0.0.0",
"bind-address-ipv6": "::",
"blocklist-enabled": true,
"blocklist-url": "john.bitsurge.net/public/biglist.p2p.gz",
"cache-size-mb": 4,
"dht-enabled": true,
"download-dir": "/external/downloads",
"download-queue-enabled": true,
"download-queue-size": 5,
"encryption": 2,
"idle-seeding-limit": 30,
"idle-seeding-limit-enabled": false,
"incomplete-dir": "/home/transmission/Downloads",
"incomplete-dir-enabled": false,
"lpd-enabled": false,
"message-level": 1,
"peer-congestion-algorithm": "",
"peer-id-ttl-hours": 6,
"peer-limit-global": 200,
"peer-limit-per-torrent": 50,
"peer-port": 51413,
"peer-port-random-high": 65535,
"peer-port-random-low": 49152,
"peer-port-random-on-start": false,
"peer-socket-tos": "default",
"pex-enabled": true,
"port-forwarding-enabled": true,
"preallocation": 1,
"prefetch-enabled": true,
"queue-stalled-enabled": true,
"queue-stalled-minutes": 30,
"ratio-limit": 2,
"ratio-limit-enabled": false,
"rename-partial-files": true,
"rpc-authentication-required": false,
"rpc-bind-address": "0.0.0.0",
"rpc-enabled": true,
"rpc-host-whitelist": "",
"rpc-host-whitelist-enabled": true,
"rpc-password": "{375a6f9d15e71e5291f4522fcfd24c7805fbc0c5FFmKwW5a",
"rpc-port": 9091,
"rpc-url": "/transmission/",
"rpc-username": "transmission",
"rpc-whitelist": "127.0.0.1,192.168.1.*",
"rpc-whitelist-enabled": true,
"scrape-paused-torrents-enabled": true,
"script-torrent-done-enabled": false,
"script-torrent-done-filename": "",
"seed-queue-enabled": false,
"seed-queue-size": 10,
"speed-limit-down": 100,
"speed-limit-down-enabled": false,
"speed-limit-up": 100,
"speed-limit-up-enabled": false,
"start-added-torrents": true,
"trash-original-torrent-files": false,
"umask": 18,
"upload-slots-per-torrent": 14,
"utp-enabled": true
}
This is the last part of this whole process. At this point, we have set up everything we need to
make all packets that are marked with a 0x1 flag to route through the transmission table, which
we then move only through the tun0 VPN connection.
In this step, we are going to mangle every package that comes from the transmission uesr, including
all of the packets that comes from the transmission application because the transmission-daemon
gets run under the transmission user.
Here is the script that will configure the iptables rules:
Code: Select all
#! /bin/bash
export INTERFACE="tun0"
export VPNUSER="transmission"
export LANIP="192.168.1.0/24"
export NETIF="eth0"
iptables -F -t nat
iptables -F -t mangle
iptables -F -t filter
# mark packets from $VPNUSER
iptables -t mangle -A OUTPUT ! --dest $LANIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
iptables -t mangle -A OUTPUT --dest $LANIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
iptables -t mangle -A OUTPUT --dest $LANIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1
iptables -t mangle -A OUTPUT ! --src $LANIP -j MARK --set-mark 0x1
# allow responses
iptables -A INPUT -i $INTERFACE -m conntrack --ctstate ESTABLISHED -j ACCEPT
# let $VPNUSER access lo and $INTERFACE
iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT
# let all traffic out over the VPN
iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT
# all packets on $INTERFACE needs to be masqueraded
iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE
#SSH - DO NOT REMOVE.
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
# LAN
iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
iptables -A OUTPUT -s 192.168.0.0/16 -j ACCEPT
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
This section explains the details of the script.
1. `export INTERFACE="tun0"`: This sets your VPN interface. OpenVPN uses tun0. Set this as needed
2. `export VPNUSER="transmission"`: This sets the VPNUSER variable to transmission. Change this to whatever user will run the transmission-daemon application
3. `export LANIP="192.168.1.0/24"`: Set your lan network with associated netmask. Typical values are 192.168.1.0/24 and 192.168.0.0/24.
4. `export NETIF="eth0"`: set the interface to read and write to. f
5. `iptables -F -t nat`: flush all nat rules (delete them from the nat table)
6. `iptables -F -t mangle`: flush all mangle rules (delete from the mangle table)
7. `iptables -F -t filter`: flush all filter rules (delete from the filter table)
8. `iptables -t mangle -A OUTPUT ! --dest $LANIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1`
1. `iptables -t mangle`: use the mangle table
2. `-A OUTPUT`: for all outgoing packets
3. `! --dest $LANIP`: where the destination is **not (the !)** to any of the LAN ip addresses
4. `-m owner --uid-owner $VPNUSER`: where the owner is the the transmission user
5. `-j MARK --set-mark 0x1`: mark the packet with a 0x1
9. `iptables -t mangle -A OUTPUT --dest $LANIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1`
1. do the same thing but for the udp protocall over port 53 for DNS
10. `iptables -t mangle -A OUTPUT --dest $LANIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1`: I do the same thing but for the tcp protocall over port 53 for DNS
11. `iptables -t mangle -A OUTPUT ! --src $LANIP -j MARK --set-mark 0x1`: I added this for testing. This will mark all packets for all users with a 0x1 mark.
12. `iptables -A INPUT -i $INTERFACE -m conntrack --ctstate ESTABLISHED -j ACCEPT`: If the connection is already established, then it is fine
13. `iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT`: Allow the transmission user access to localhost
14. `iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT`: All all outbound traffic over the VPN
15. `iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE`: I will be honest here, I don't know what this does but it is needed. I spent a long time trying to figure out what hte MASQUERADE option does and I couldn't figure it out.
16. `iptables -A INPUT -p tcp --dport 22 -j ACCEPT`: regardless of everything, allow ssh to recieve
17. `iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT`: and send
18. `iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT`: accept everything from the LAN
19. `iptables -A OUTPUT -s 192.168.0.0/16 -j ACCEPT`: accept everything from the LAN
20. `iptables-save > /etc/iptables/rules.v4`: save all of the rules to v4 so that they don't get deleted on reboot
21. `ip6tables-save > /etc/iptables/rules.v6`: save all of the rules to v6 so that they don't get deleted on reboot
Resources
This closely follows the document below but deviates in signifcant ways. Still
though, this is a very good start and helped me out a lot.
* https://www.niftiestsoftware.com/2011/0 ... interface/
A stack exchange talking about how to set up a service after networking is on and up
* https://unix.stackexchange.com/question ... as-started