[not Devember] Easy to follow Roadwarrior multi-client single VPN setup

About the title

I wanted to poke a little friendly fun at Devember participants. Your projects are appreciated, but I can’t control myself, because I’m a rabid animal, and I have to [bring attention to what a good person I am] (I spent a few hours trying to find the internet slag for this and I am frustrated because I still can’t find it, for the love of god, someone please tell me what that is, I’m going mad) virtue-signal that I would be posting stuff even without being Devember. Again, your projects are appreciated though, hope to see more stuff getting both finished before Devember ends, and continued being improved and maintained after it. Cactus Comments was one of the last Devember projects that I really enjoyed seeing (federated alternative to d*squs).

This post is about my current setup for my VPN. It’s part of my “Easy to follow” series. Hurray, it just became a series, with this being the second entry! Yes, I will come back at the secure firewall, I need to get a new homelab, because the old one is now remote and I don’t want to mess with it too much. Here, I will present how to setup a (Linux) router that you can connect to any WiFi network and share a single VPN connection to all the devices behind it.

Click on any text that has a triangle at the beginning of the line, to expand and read the details inside it.

details

Begin:

What you will need:
1) A linux computer that has at least a WiFi adapter and an Ethernet port
  • I will be using a Raspberry Pi 3, but you can use any *Pi or other SBC that have WiFi and an Ethernet port. A Pi 4 or something similar would probably make more sense, because it can deliver more bandwidth than the ~300 Mbps that a Pi 3 does through its Ethernet port and probably get faster WiFi speeds too. And it can be powered by a battery bank. Or you can use an old laptop, your choice.

  • You don’t necessarily need WiFi. You can use this as a baseline for running a Linux firewall and protecting your whole network from your ISP spying on you or something. You can even use 1 ethernet port if you have a managed switch, but I sure wouldn’t recommend that, it’s cheaper to get 2 ethernet ports than a managed switch.

2) An Internet connection (assuming public WiFi)
  • It can be your own, but I will pretend it would be an open public WiFi and set firewall rules accordingly
3) A VPN server and configuration files to connect to it
  • This can be a subscription to a VSP (VPN Service Provider), your own home VPN, or if you want to randomly transform this into a Devember entry, a Linode VPN (use coupon code Level1Techs for $100 Linode credit 60-days free trial! [insert soydev face here]), create a one-click Linode Wireguard server and get started using your own VPN server today!

  • This tutorial assumes you already have a VPN server, be it wireguard or OpenVPN. If you want to set one up, click here for PLL’s guide, or here and here for Linode marketplace autodeploy.

4) A power outlet / UPS / battery bank / solar panel / power generator

5.1) [optional] a switch (managed or unmanaged)

5.2) [optional] a wireless access point

5.3) [optional] instead of a switch and AP, you could go with an old all-in-one WiFi router and put in in bridge / AP mode

As mentioned, I will use a Pi 3. I’ll be presenting how to do it running Ubuntu 20.04 on it, but you can use any Linux distro, or adapt this to *BSD. If you want a quick rundown of the steps and don’t need any explanation, or if you want a “run and forget” script, jump to “Condensed version” after “Ending Notes.” Also, in the first comment after this tutorial, you will find an Alpine setup guide on the Pi. Use that if you use Alpine and not Ubuntu (note that the Condensed Version script might be compatible with other Debian-based distros).


Connecting to WiFi

So, first thing, power on your device and connect to WiFi. In basically all Linux distros, you’d do:

  1. ip a to find your wireless adapter name (on the Pi 3 on Ubuntu, it’s called “wlan0”).
  2. If you don’t have an existing /etc/wpa_supplicant/wpa_supplicant.conf, create one by using the following commands (edit wlan0 if it’s called something else on your machine):
[ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && cp etc/wpa_supplicant/wpa_supplicant.conf etc/wpa_supplicant/wpa_supplicant.conf.original
cat << EOF > etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant
update_config=1
EOF

On some distros like Gentoo and Void, you’d replace: ctrl_interface=DIR=/var/run/wpa_supplicant with ctrl_interface=/run/wpa_supplicant

  1. Add your WiFi SSID to your config:
wpa_passphrase "SSID" "password" | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf

Note: you can use this command multiple times if you got more than 1 WiFi network you want to connect to. So you’d be running this command every time you need to add a new WiFi connection and wpa_supplicant.conf will memorize it for you.

  1. Connect to the WiFi network:
sudo wpa_supplicant -B -i wlan0
By default, it should use /etc/wpa_supplicant/wpa_supplicant.conf, but if that doesn't work, use:
sudo wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
5. You may get an ipv6 IP when you do an `ip a`, but you probably won't get an ipv4 address. You want IPv4, because that's how I made this tutorial. To do that, just
sudo dhclient

WiFi works better if you let netplan take care of it. So:

[ -f /etc/netplan/50-cloud-init.yaml ] && sudo cp /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.original
sudo nano /etc/netplan/50-cloud-init.yaml
/etc/netplan/50-cloud-init.yaml
network:
    ethernets:
        eth0:
            addresses: [10.69.69.1/24]
            dhcp4: false
            optional: true
            nameservers:
                    addresses: [1.1.1.1, 1.0.0.1]
    wifis:
        wlan0:
            dhcp4: true
            dhcp6: true
            access-points:
                    "SSID_NAME":
                            password: "WIFI_PSK"

    version: 2
sudo netplan try

(hit enter if you want to keep the configuration - and you want to if it works)
Note: WIFI_PSK can be plaintext, but I suggest you run wpa_passphrase SSID Password and copy paste the hashed password into 50-cloud-init.yaml file. We will come back to this configuration again later Network configuration.

VPN client installation (does not contain how to configure VPN. Links under `What you will need -> 3) A VPN Server`)

Now that you are connected to WiFi, update your OS and pick the VPN poison client of your choice. Then grab your VPN config, like ovpn conf file and / or its keys and certificates (if it’s split in parts) or configure Wireguard. I won’t get into how to configure those. Check out PLL’s guide to Wireguard linked above, or use sudo openvpn --config /path/to/config.ovpn file (if your config is split with separate keys and ca certs, you can either combine all into one .ovpn file, or use the additional openvpn arguments; if unsure, always man openvpn).

sudo apt-get update 
sudo apt-get upgrade -y
sudo apt-get install openssh-server
sudo systemctl enable --now ssh.service
sudo apt-get install -y openvpn
## or if you use wireguard ##
sudo apt-get install -y wireguard wireguard-tools wireguard-dkms

Then proceed to configure it.

Network configuration

Since you are now connected to the VPN, you need to enable routing and add a few iptables rules. To enable routing:

sudo sh -c "echo 1> /proc/sys/net/ipv4/ip_forward"
Making routing permanent and some rabling

You can make the routing permanent and not have to run this command after every reboot, but I discourage it, because if you are doing this setup to hide the devices from a network and make their traffic appear as being somewhere else, you want to make sure you won’t have traffic leakage on, say, the public WiFi network.

To make it permanent, you:

sudo nano /etc/sysctl.conf

(or the editor of your choice) and uncomment this line by removing the # sign:

net.ipv4.ip_forward=1
If for some reason the line doesn't exist, just add it at the end of the config file.

Save and exit, then if you don’t echo 1 as instructed above, reboot your system for the settings to take effect (and then reconnect to the VPN).

Now, use ip a to find out the name of your Ethernet port (on the Pi 3 in Ubuntu, it is “eth0”). Add the iptables rules by using the following commands (replace wg0 with tun0 if you are using OpenVPN):

sudo iptables -A INPUT -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -i wlan0 -j DROP
sudo iptables -A FORWARD -i eth0 -o wlan0 -j DROP
sudo iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
sudo iptables -A FORWARD -i wg0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
Some other explanation about the above
  1. First rule allows responses from the internet to come back to your router. This is required if you don’t want people from the internet or from your local coffee shop WiFi to try to hack your router.
  2. Second rule blocks any other traffic going into your router. That means that nothing coming from wlan0 is allowed to access your router without your router making a prior connection first. If you want, for example, to connect to SSH from the public wifi to your router, you’d want to add a permit rule on tcp port 22 before the drop rule, so something like:
sudo iptables -I INPUT 2 -p tcp --dport 22 -j ACCEPT
  1. Third rule blocks traffic incoming from eth0 to go to wlan0, i.e. from communicating with the public WiFi network or making a connection to a website from your real location. This is basically a really dumb “kill-switch,” but that works to block any traffic leaking even before the tunnel exists to begin with, in addition to blocking traffic if the VPN connection drops for some reason. If for some reason you want your devices behind the VPN box to still communicate with the WiFi network, like say for example you are not connected to a public WiFi, but to your home internet, then all you have to do is add rules before the DROP rule. I’m not going to test this, as this is not the scope of this tutorial, but it is doable, just that instead of output interface (wlan0), you have to select a destination network or single IP - obviously, if your home subnet is the same as a public WiFi subnet, this will introduce a potentially unwanted vulnerability. I think there are ways around this if you use ufw or firewalld by defining if the network you are connected to is private or public based on the SSID, but I could be wrong. If you want to improve on this, comment bellow I guess and if it works, I’ll add it to the tutorial in a spoiler or details tag.
  2. Fourth rule allows traffic coming from eth0 to go through the wireguard or openvpn tunnel (again, replace wg0 with tun0 if you are using OpenVPN).
  3. Fifth rule allows established traffic coming from the end of the VPN tunnel to go through eth0. If you don’t allow this, you can send traffic (like a ping request) but you won’t be able to receive a response back (like a ping response). You could allow all traffic coming from the VPN to go to the network behind eth0, but that would be insecure, so don’t do that (unless obviously you know exactly what you are doing and have a firewall at the end of the tunnel anyway, in which case you probably don’t need this tutorial).
  4. Sixth rule enables NAT on the tunnel, so you won’t have to give routes to the network behind your Pi VPN-router box on the devices at the end of the tunnel. If you use a VSP, this is not even possible, since you are not in control of the VPN server.
Making firewall rules permanent and more rambling

Wireguard has a nifty feature where you can run a bunch of commands on connect (“PostUp”) and disconnect (“PostDown”) of a tunnel. You can add the ACCEPT commands in wg0.conf (or however else you named your wireguard config). Instead of “wg0” in the conf file, you’d use “%i” for the wireguard interface (check out PLL’s guide if you’re unsure what I’m talking about). That way you can have a permanent DROP rule from eth0 to wlan0, but still maintain a somewhat dynamic ruleset for when a tunnel is up or down. This is not limited to iptables commands, you can set wireguard to give you a mail if you disconnect (the interface is brought down), just as an example (well, the interface usually stays up, even when you can’t connect to a server, so that’s a bad example, but you get the point).

To make the iptables rules permanent, all you have to do is:

sudo apt-get update
sudo apt-get install -y iptables-persistent
sudo sh -c "iptables-save > /etc/iptables/rules.v4"

I’d suggest the only permanent rule you make is the 1st one, the DROP rule and allow Wireguard to run PostUp and PostDown. For OpenVPN, you can make all of them permanent if you so desire, as the tunnel shouldn’t change to anything other than tun0.

Now, more poison picking. At this point, your traffic from the Pi should be routed through the VPN if configured properly (i.e. no split tunneling). Behind the Pi or the computer of your choice, you can either add an AIO router in bridge / AP mode, a switch, or simply a computer. In my setup, I got a Pi 4 in a metal enclosure (so I cannot use the WiFi on it, so I’m using a Pi 3 to get WiFi… yeah, don’t judge, I know USB WiFI NICs exist).

The easiest thing to do next is static IP configuration. In my case, all I have to do on my Pi 4 (so the Linux device behind the Pi 3 VPN-router box eth0) is to:

sudo ip link set dev eth0 up
sudo ip addr add 10.69.69.2/24 dev eth0
sudo ip route add default via 10.69.69.1

and call it a day. On the Pi 3 side of things, you have to edit netplan config and add a static IP (the same one used in the default route for the Pi 4 above). On Ubuntu 20.04, it’s:

sudo nano /etc/netplan/50-cloud-init.yaml

(or your preferred text editor)
And the config should look like:

network:
    ethernets:
        eth0:
            addresses: [10.69.69.1/24]
            dhcp4: false
            optional: true
            nameservers:
                    addresses: [ip1-of-your-favorite-dns,ip2-of-your-favorite-dns]
    version: 2

You can stop here and go to the end if the only thing behind your Pi VPN-router box is just a single Linux computer (or just 1 device).

But since we want a whole network behind it and we want to make our lives easier, let’s move along.

If you add an AP or a switch behind the Pi 3, you need to set manual IPs to each device, which gets unwieldy fast after 5 or so devices (subjective, you can manage more than a few dozen and be fine, but come on). So, you need to make your Pi a DHCP server.

Setting up DHCP server
sudo apt-get update
sudo apt-get install -y isc-dhcp-server
sudo nano /etc/dhcp/dhcpd.conf
sudo cp /etc/default/isc-dhcp-server /etc/default/isc-dhcp-server.original
sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.original
sudo cp /usr/lib/systemd/system/isc-dhcp-server.service /usr/lib/systemd/system/isc-dhcp-server.service.original

After the above is done, you have to edit the 3 conf files you just copied. You can simply delete everything inside them and copy the config files posted bellow, for each file.

sudo nano /etc/default/isc-dhcp-server
Contents of /etc/default/isc-dhcp-server
# Defaults for isc-dhcp-server (sourced by /etc/init.d/isc-dhcp-server)

# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).
DHCPDv4_CONF=/etc/dhcp/dhcpd.conf
#DHCPDv6_CONF=/etc/dhcp/dhcpd6.conf

# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).
DHCPDv4_PID=/var/run/dhcpd.pid
#DHCPDv6_PID=/var/run/dhcpd6.pid

# Additional options to start dhcpd with.
#       Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
#OPTIONS=""

# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
#       Separate multiple interfaces with spaces, e.g. "eth0 eth1".
INTERFACESv4="eth0"
INTERFACESv6=""
sudo nano /etc/dhcp/dhcpd.conf
Contents of /etc/dhcp/dhcpd.conf
subnet 10.69.69.0 netmask 255.255.255.0 {
    range 10.69.69.69 10.69.69.169;
    option routers 10.69.69.254;
    option domain-name-servers 1.1.1.1;
    option domain-name "local";
    default-lease-time 600;
    max-lease-time 7200;
  }
sudo systemctl enable --now isc-dhcp-server

Explanation

The above will set up a DHCP pool to allocate IPs between 10.69.69.69 to 10.69.69.169, which should be more than enough. Considering that this setup is supposed to run on a public WiFi network through a single WiFi connected router and through a VPN, I doubt you can push that many devices and have a decent experience anyway.

Ending notes

For those of you who want to run a single script at startup (@reboot in crontab), instead of doing all the rules persistent and enabling all the services in systemd at startup, here’s a compressed script with everything that should be happening on the Pi, in order:

sudo sh -c /path/to/startup.sh (bellow is what should be in startup.sh)

wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant
dhclient
wg-quick up wg0
iptables -A INPUT -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i wlan0 -j DROP
iptables -A FORWARD -i eth0 -o wlan0 -j DROP
iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
iptables -A FORWARD -i wg0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
echo 1> /proc/sys/net/ipv4/ip_forward

Note: I have no idea how to auto-start openvpn without using a plaintext credentials file, so if you want to help, do that in the comments. So if you use openVPN and don’t want to store your authentication details in plaintext, you have to start your Pi, ssh into it from behind the eth0 port (or use a monitor and a keyboard) and start openVPN in a screen session. Then, you have to split startup.sh in 2 scripts: the first one where connect to WiFi, run dhclient, add iptables rules and enabling routing, then second script to connect to the VPN.

Obviously, if you never connected to a WiFi network before, you have to SSH into the Pi (or use a monitor and a kb) and use wpa_passphrase to add a new connection to your wpa_supplicant.conf file (as shown at the beginning of the tutorial).

You can add the above startup.sh script in root user’s crontab (sudo crontab -e -u root) to run at reboot, like:

@reboot /bin/sh -c /path/to/startup.sh

The reason why I didn’t let systemd to do everything is because this tutorial would get a little too complicated. You specifically want these commands to run in order. You can do it in systemd, but you have to specify dependencies in each unit file: start networking@wlan0, then start VPN, then apply the iptables rules, then start dhcp server, then enable ipv4 forwarding. And obviously if you let systemd to its defaults, it will do networking, then in parallel the VPN, networking rules and I think the kernel does the ipv4 forwarding first, so this is definitely not the way you want your VPN-router to behave.




Condensed version

Have not tested this script. I see nothing wrong with it, but do report if there’s any issue.

#!/bin/sh

## VPN-TYPE={ wg , ovpn } ##
echo "What VPN are you using? (wg/ovpn) : "
read ANSWER
[[ "${ANSWER}" != "wg" ] && [ "${ANSWER}" != "ovpn" ]] && echo "Insert wg or ovpn only!" && exit 1
YOUR-USER=${USER}
DEFAULT_PATH="${HOME}"

if [ "${ANSWER}" = "ovpn" ]
then

        echo
        echo "OpenVPN username: "
        read VPN_ACC
        echo "OpenVPN password (text will appear blank): "
        stty -echo
        read VPN_PASS
        stty echo
        echo

fi

echo
echo "Insert WiFi SSID: "
read SSID_NAME
echo "Insert WiFi password (text will appear blank): "
stty -echo
read WIFI_PSK
stty echo
VAR_FOR_CONF=$(wpa_passphrase "${SSID_NAME}" "${WIFI_PSK}" | sed '/#psk/d')
SSID_NAME=$(printf "${VAR_FOR_CONF}" | grep "ssid" | cut -d "=" -f 2)
WIFI_PSK=\"$(printf "${VAR_FOR_CONF}" | grep "psk" | cut -d "=" -f 2)\"


[ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && sudo cp /etc/wpa_supplicant/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf.original
cat <<EOF | sudo tee /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant
update_config=1
EOF
printf "${VAR_FOR_CONF}" | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf

[ -f /etc/netplan/50-cloud-init.yaml ] && sudo cp /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.original
cat << EONF > sudo tee /etc/netplan/50-cloud-init.yaml
network:
    ethernets:
        eth0:
            addresses: [10.69.69.1/24]
            dhcp4: false
            optional: true
            nameservers:
                    addresses: [1.1.1.1, 1.0.0.1]
    wifis:
        wlan0:
            dhcp4: true
            dhcp6: true
            access-points:
                    ${SSID_NAME}:
                            password: ${WIFI_PSK}

    version: 2
EONF

sudo netplan apply
sudo apt-get update 
sudo apt-get upgrade -y

if [ "${ANSWER}" = "wg" ]
then

  sudo apt-get install -y isc-dhcp-server iptables-persistent wireguard wireguard-tools wireguard-dkms 
  ## remember to have your VPN configured ##
  sudo [ -f /etc/wireguard/wg0.conf ] && sudo systemctl enable --now wg-quick@wg0 || echo "WARNING: Manual intervention required. It appears that /etc/wireguard/wg0.conf doesn't exist. wg-quick@wg0 has not been enabled. Please add your wireguard config and systemctl enable it manually."

  sudo iptables -A INPUT -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
  sudo iptables -A INPUT -i wlan0 -j DROP
  sudo iptables -A FORWARD -i eth0 -o wlan0 -j DROP
  sudo iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
  sudo iptables -A FORWARD -i wg0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
  sudo iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE


elif [ "${ANSWER}" = "ovpn" ]
  then

      sudo apt-get install -y isc-dhcp-server iptables-persistent openvpn
      ## remember to have your VPN configured ##

      ## assuming the first file that will be found will be the .ovpn file ##
           ## you may want to set it manually here ##
      OVPN_CONF=$(sudo find "${DEFAULT_PATH}" -maxdepth 3 | grep -i "\.ovpn$" | head -n 1)
      [ -z ${OVPN_CONF} ] && echo "WARNING: Manual intervention required. No .ovpn file found. Add it to your home folder and edit ~/vpn-startup.sh"


      echo ${VPN_ACC} > ${DEFAULT_PATH}/.ovpn-credentials
      echo ${VPN_PASS} >> ${DEFAULT_PATH}/.ovpn-credentials
      chmod 600 ${DEFAULT_PATH}/.ovpn-credentials
      echo "#!/bin/sh" > ${DEFAULT_PATH}/vpn-startup.sh
      echo "openvpn --config" ${DEFAULT_PATH}/config.ovpn "--auth-user-pass" ${DEFAULT_PATH}/.ovpn-credentials >> ${DEFAULT_PATH}/vpn-startup.sh
      chmod u+x ${DEFAULT_PATH}/vpn-startup.sh

      sudo iptables -A INPUT -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
      sudo iptables -A INPUT -i wlan0 -j DROP
      sudo iptables -A FORWARD -i eth0 -o wlan0 -j DROP
      sudo iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT
      sudo iptables -A FORWARD -i tun0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
      sudo iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

      sudo crontab -l -u root | sudo tee /root/tmp.txt
      sudo /bin/sh -c 'echo "@reboot /bin/sh -c" ${DEFAULT_PATH}/vpn-startup.sh >> /root/tmp.txt'
      sudo /bin/sh -c 'cat /root/tmp.txt | crontab -'
      sudo rm -f /root/tmp.txt

fi



sudo /bin/sh -c "iptables-save > /etc/iptables/rules.v4"
sudo systemctl enable --now netfilter-persistent
sudo sh -c "echo 1> /proc/sys/net/ipv4/ip_forward"
sed 's/^net.ipv4.ip_forward/#net.ipv4.ip_forward/g' /etc/sysctl.conf | sudo tee /etc/sysctl.conf
## I know sed -i works in Ubuntu, but I try my best to keep everything POSIX compliant, screw bashism and gnuism ##

[ -f /etc/default/isc-dhcp-server ] && sudo cp /etc/default/isc-dhcp-server /etc/default/isc-dhcp-server.original
[ -f /etc/dhcp/dhcpd.conf ] && sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.original
[ -f /usr/lib/systemd/system/isc-dhcp-server.service ] && sudo cp /usr/lib/systemd/system/isc-dhcp-server.service /usr/lib/systemd/system/isc-dhcp-server.service.original

cat << EOISCF | sudo tee /etc/default/isc-dhcp-server
# Defaults for isc-dhcp-server (sourced by /etc/init.d/isc-dhcp-server)

# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).
DHCPDv4_CONF=/etc/dhcp/dhcpd.conf
#DHCPDv6_CONF=/etc/dhcp/dhcpd6.conf

# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).
DHCPDv4_PID=/var/run/dhcpd.pid
#DHCPDv6_PID=/var/run/dhcpd6.pid

# Additional options to start dhcpd with.
#       Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
#OPTIONS=""

# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
#       Separate multiple interfaces with spaces, e.g. "eth0 eth1".
INTERFACESv4="eth0"
INTERFACESv6=""

EOISCF

cat << EODHCPDF | sudo tee /etc/dhcp/dhcpd.conf
subnet 10.69.69.0 netmask 255.255.255.0 {
    range 10.69.69.69 10.69.69.169;
    option routers 10.69.69.254;
    option domain-name-servers 1.1.1.1;
    option domain-name "local";
    default-lease-time 600;
    max-lease-time 7200;
  }

EODHCPDF


sudo systemctl daemon-reload
sudo systemctl enable --now isc-dhcp-server


1 Like

Reserved space for Alpine config.
I moved my setup to Alpine. I won’t get into details on this, only use this Alpine guide if you are an intermediate Linux users, so you are able to follow and modify the config script to your liking.

After first install or first boot, run setup-alpine and configure wpa_supplicant settings and eth0. I’m setting eth0 as static, with ip 10.69.69.1 and submask 255.255.255.0.

Note that I have not tested the script bellow, I did all those changes manually. Run it at your own risk. I have made it slightly verbose, just to make it easier for you to see what is going on. Obviously, you should run this in an interactive shell.

run-once.sh
#!/bin/ash

## VPN-TYPE={ wg , ovpn } ##
echo "What VPN are you using? (wg/ovpn) : "
read ANSWER
[[ "${ANSWER}" != "wg" ] && [ "${ANSWER}" != "ovpn" ]] && echo "Insert wg or ovpn only!" && exit 1


echo
echo "Insert your desired Alpine Linux username: "
read YOUR_USERNAME
echo "Insert password for ${YOUR_USERNAME} (text will appear blank): "
stty -echo
read YOUR_USER_PASSWORD
stty echo
echo
DEFAULT_PATH="/media/mmcblk0p1"

if [ "${ANSWER}" = "ovpn" ]
then

        echo
        echo "OpenVPN username: "
        read VPN_ACC
        echo "OpenVPN password (text will appear blank): "
        stty -echo
        read VPN_PASS
        stty echo
        echo

fi

adduser -h /home/${YOUR_USERNAME} -g wheel -s /bin/ash ${YOUR_USERNAME}
passwd -d ${YOUR_USER_PASSWORD} ${YOUR_USERNAME}


apk -v update
apk -v upgrade
apk add openssh openssh-server dhcp-server-vanilla sudo


cat <<EOF > /etc/dhcp/dhcpd.conf
# dhcpd.conf
option domain-name "local";
option domain-name-servers 1.1.1.1, 1.0.0.1;

default-lease-time 600;
max-lease-time 7200;

subnet 10.69.69.0 netmask 255.255.255.0 {
  range 10.69.69.69 10.69.69.169;
  option domain-name-servers 10.69.69.1;
  option domain-name "local";
  option routers 10.69.69.1;
  option broadcast-address 10.69.69.255;
  default-lease-time 600;
  max-lease-time 7200;
}
EOF

if [ "${ANSWER}" = "wg" ]
then

  apk -v add wireguard wireguard-tools wireguard-dkms
  ## remember to have your VPN configured ##
  [ -f /etc/wireguard/wg0.conf ] && rc-update add wg-quick@wg0 default || echo "WARNING: Manual intervention required. It appears that /etc/wireguard/wg0.conf doesn't exist. wg-quick@wg0 has not been enabled. Please add your wireguard config and rc-update add it to default manually."

  iptables -A INPUT -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
  iptables -A INPUT -i wlan0 -j DROP
  iptables -A FORWARD -i eth0 -o wlan0 -j DROP
  iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
  iptables -A FORWARD -i wg0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
  iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE



elif [ "${ANSWER}" = "ovpn" ]
  then

    apk -v add openvpn
    echo ${VPN_ACC} > ${DEFAULT_PATH}/.ovpn-credentials
    echo ${VPN_PASS} >> ${DEFAULT_PATH}/.ovpn-credentials
    chmod 600 ${DEFAULT_PATH}/.ovpn-credentials
    echo "#!/bin/ash" > ${DEFAULT_PATH}/vpn-startup.sh
    echo "openvpn --config" ${DEFAULT_PATH}/config.ovpn "--auth-user-pass" ${DEFAULT_PATH}/.ovpn-credentials >> ${DEFAULT_PATH}/vpn-startup.sh

    cat << EONF > ${DEFAULT_PATH}/iptables.sh
iptables -I INPUT -i wlan0 -j DROP
iptables -I INPUT -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth0 -o wlan0 -j DROP
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward
EONF

    chmod u+x ${DEFAULT_PATH}/vpn-startup.sh
    chmod u+x ${DEFAULT_PATH}/iptables.sh

    crontab -l > tmp.txt
    echo "@reboot /bin/ash -c" ${DEFAULT_PATH}/vpn-startup.sh >> tmp.txt
    echo "@reboot /bin/ash -c" ${DEFAULT_PATH}/iptables.sh >> tmp.txt
    cat tmp.txt | crontab -
    rm -f tmp.txt

fi


rc-update add sshd default
rc-update add dhcpd default

echo \n\n\n

rc-status

lbu add /home
lbu commit -d -v

I suggest you add a ssh key to your user and disable password authentication in sshd.conf.

Feel free to ask for support or issues with this script.

1 Like

It seems I overdid it, I don’t think it’s “easy to follow.” I’d like some feedback, thank you.

1 Like

Hey, very nice man.
I’m re-doing my setup weds/thurs, so might just give this a try doing it your way

2 Likes

I’m going to be really frank here and say that it might be a little too long in your attempt to make it easy to follow you were incredibly verbose and a lot of people are just going to try and skip through the commands which I realized is probably the majority of the forum.

However for a person like me I appreciate your verbosity and like seeing that somebody is willing to explain it further than I am and will more than gladly place a link in it on my infrastructure series.

I think there’s two kinds of people in the world in the way they approach learning or maybe intending to implement something. The STOIC or the Industry Finisher. Both are typically pretty opposite in their approach however yours does seem to cater towards the stoic if you don’t mind me saying that

It is nice that you did put that stuff down in drop down hide details though

2 Likes

I like Stoicism and I never knew there was even an approach named “STOIC.”

I guess I’ll have to condense it down further with more detail and spoiler tags. Tomorrow though, because I’ve been writing all day (of which about 2 hours were spent trying to remember the phrase “virtue-signaling”).

2 Likes

This took a while, but managed to do it. I’ve split it in steps to make it easier to read and made a condensed version for experienced users.

3 Likes

It appears either NetworkChuck is following Level1Techs (hi), or he just thought of doing the same thing as me (not hard), but a bit differently, so I wanted to present an alternative to my setup.

He chose to use OpenWRT on the Pi 4 and instead of adding a switch/AP behind the Pi ethernet port, he added another WiFi card and used that as his AP. His setup is more portable in terms of hardware to carry around, but I don’t like that the WiFi and the wired networks are different (makes management just a little tiny bit more tedious). Also, while OpenWRT has an automatic VPN client restart, he did not block the traffic from his LAN and WLAN to go through the WAN, in my setup, no matter what happens, your devices will not reach the internet if your tunnel is disabled.

I think his setup is way, waaay more normie friendly, which is good, especially since OpenWRT has a GUI. Also, it’s easier to carry, which is a big plus for portability. I did not understand a few of his steps, as he did not know either (the Wireless settings), which I avoid like the plague, because it might be illegal depending on where you live (for example, Europe allows channel 13 on 2.4GHz, whereas the US bans it and only allows channels 1 to 11). But it shouldn’t bee too big of a worry (I think).

I almost always fail to think about user friendliness, as most people would prefer a (web/)GUI to see the configurations. I think my tutorial has been explained thoroughly (if not, leave feedback please!) for (at the very least intermediate) users to understand what they are doing. So this is kind of an alternative setup, which I wanted to give a shout-out to.


Coming back to this, I just realized that while I did block traffic going out of the Pi, I did not block traffic going in, which might be a (serious) issue. So I’ll have to update this a little more to add some firewall rules, stay tuned for that. I’ll probably also switch to Alpine, because now I am a bit more prepared in terms of the PC I use and doing things the way I know.

It’s not an issue for the devices behind the Pi, because even if potential bad actors could send traffic to, say, your laptop, the laptop would either respond via the tunnel, so packets end up nowhere, or if the tunnel is down, the firewall would block outbound packets coming from eth0 going to wan. The issue is for the router itself, because if you have open ports listening on WAN, bad actors will be able to access that. It’s not necessarily bad for something like SSH, especially if you disable password login (which you should), but if your OpenWRT webGUI is exposed on the WAN, you are looking for trouble. Same if you have other management interfaces exposed on WAN, like say, you install Cockpit on the Pi. Better to not take the chances.

1 Like

Updates:

  • massively improved readability
  • scripted an interactive deploy script (still assumes you have the VPN files where they should)
  • added Alpine interactive script in the first comment
  • changed the tutorial into a wiki

I need someone to test the deploy scripts, preferably both Ubuntu and Alpine. I can’t do it myself for now, but maybe in a few weeks if nobody offers.

Things kept into consideration:

  • I might come back to this tutorial and add ipv6 support to it. Technically if the VPN server and its internet gateway speak IPv4, you don’t need ipv4 in-between the VPN router client and the VPN server, because either wireguard or openvpn will speak over ipv6 and ipv4 traffic will be encapsulated in the tunnel.
1 Like

New update: got around to use my Ubuntu SD card. Tested the Ubuntu DHCP config and it doesn’t work. The systemd isc-dhcp-server.service is broken for one, but 2, I also misconfigured the DHCP server. Need to add more stuff.

1 Like

Update: made the Ubuntu dhcpd server to work. Seems ridiculous that Ubuntu of all things had a misconfiguration in their systemd unit file service. Not a big deal if you know where to look at and how to test stuff manually to see the output and errors of the program. But I doubt non-linux techies would have figured this out.

It was just a wrong PID variable, instead of using /var/run/dhcp-server/dhcpd.pid, the systemd was set to try to use /run/dhcp-server/dhcpd.pid, which had a non-existent folder (dhcp-server) and it would be created automatically. You can either make it manually and keep the original systemd unit file, but it is not guaranteed the folder will remain there, or you could modify the service file to use the /var/run/ folder, under which the folder does get created / exists to begin with. But the later may overwrite the systemd service file in a future update, so you have to keep that in mind.

I guess I have to file a bug report to Canonical.
:roll_eyes:

1 Like

I’m glad I was lazy and didn’t file a bogus bug report to Canonical. It seems there isn’t anything wrong with the /run/dhcp-server folder and the dhcp-server.pid file creation. I now wonder what I did wrong, because I just changed the location to /var/run and the thing ran instantly. I tested this again in an LXD Ubuntu aarch64 container and I couldn’t reproduce the issue.

Modified the instructions and the auto-script to leave the systemd service unit file alone. Everything seems to work.

1 Like

thank u, great tutorial

2 Likes

I have yet to read the entire thing… BUT what I have read so far is great. I love long detailed post myself…

Ive found your post usually get edited down to bare bones “do this” steps with explanations in drop downs or blurred hidden options… I always read these to better educate myself. After I have had a chance to follow it and trouble shoot it on my end I will shoot back questions and or suggestions :slight_smile:

Thanks for more great content. Didn’t know this was here…

1 Like

I have a few tutorials pinned on my blog page, first comment after OP

1 Like

Oh perfect, I was just wondering if there’s a way to “watch” people on the forum for interesting posts…obviously not everything…but maybe just thread creation.

1 Like