6 min read

Raspberry Pi VPN Server Setup

Raspberry Pi VPN Server Setup
Raspberry Pi, Twisters for scale. 

This builds on my last post about the options for setting up a VPN for travel, which directs how to set up a Raspberry Pi with PiVPN. I don't expect future posts to be this technical.

This is not a complete step-by-step tutorial, as there are elements to this process that are unique to your home setup and router. Technical know-how, or at least the audacity, and [brand-name internet searching] are required.

I will start with the core steps or outline of the recipe and then explain the actions in further detail below. So feel free to jump around as needed.

Outline

  1. Setup Raspberry Pi
    1. Buy and Assemble Raspberry Pi
    2. Install Raspberry Pi OS Lite
    3. Secure SSH
  2. Setup your Router
    1. Setup Static IP
    2. Port Forward SSH
    3. Port Foward Wireguard
    4. [Optional] Port Forward Iodine
  3. Setup PiVPN
  4. [Optional] Setup Iodine

Setup Raspberry Pi

Buy and Assemble Raspberry Pi

If you don't have one already, maybe from setting up a Pi-hole, you will need to acquire a Raspberry Pi. You can buy everything, included kit, but you will pay $50 to $100 extra. I bought the board and accessories kit plus a patch cable (tiny ethernet cable) for $106.68. But overall, here are the required pieces:

  • Raspberry Pi 4 Model B, 64 Bit
  • A Case with Fan + Heat Sinks
  • Power Adaptor
  • SD Card
  • Ethernet Cable

Once everything has arrived, you get to do the fun part: assemble it.

Install Raspberry Pi OS Lite

If you bought a kit, the SD card it came with likely has a preinstalled version of Raspberry Pi OS. This will be the full desktop mode with a GUI, which we don't need.

So pop the SD card into your computer using the slot if you have a Mac Book Pro or a USB adaptor (my kit came with one). And use the Raspberry Pi Imager to reformat and install Raspberry Pi OS Lite. Before you save it, there are a few advanced settings you will want to change.

  • Set OS to Raspberry Pi OS Lite, 64-bit
  • Set Hostname with your cute Pi-related Pun
  • Enable SSH
    • Use Password Auth
    • Set Username and Password
  • Set Locale Settings

Once it's flashed to the SD card, insert it into the bottom of your Pi, plug up the power, and connect it to one of the open ports on your router.

You should see the fan come on after a few seconds, and the activity LEDs turn on.

Now, to connect your Pi to your console, you can use arp -a it to find it's IP address. The IP address should be listed next to the Hostname you set up. Once you log in, go ahead and update everything.

$ arp -a
> HOSTNAME.lan (192.168.1.118) at b1:44:ad:7f:d4:b2 on en0 ifscope [ethernet]

$ ssh USERNAME@192.168.1.118
$ sudo apt update
$ sudo apt full-upgrade
$ sudo apt clean

Secure SSH

Before you open this thing to the internet, it's essential to turn off password-based SSH authentication and optionally change the default port from 22. I am going to change the port during the Port Forwarding step.

  1. Setup SSH Key-based access
    1. Verify it works before continuing!
  2. Disable Password-Based Auth
You should be able to ssh with your key.
ssh -i ~/.ssh/id_pi USERNAME@192.168.1.118

Setup your Router

This part is the most unique to your situation, but login to your router and then apply the following settings:

  1. Assign a static IP address to your Pi via your DHCP service
  2. Port Forwarding to your Pi. (IPv4 Port forwarding & IPv6 Pinholing)
    1. SSH, TCP: Random Port -> 22
    2. Wireguard, TCP & UDP: 51820 -> 51820
    3. [Optional] Iodine, TCP & UDP: 53 -> 53
  3. [Optional] Dynamic DNS
    1. Not all routers have this, but if yours does, you can set this up now instead of later.
You should be able to look up your IP address, tether to your phone, and then ssh to your IP address.
ssh -i ~/.ssh/id_pi USERNAME@PUBLIC_IC -p RANDOM_PORT

Setup PiVPN

Now, to set up the VPN, I highly recommend using PiVPN. It makes it simple; just run a command and answer the prompts.

Before we set up the VPN, we need to set up Dynamic DNS. PiVPN has a guide on how to do this with DuckDNS. When that's done, you can run the install script.

$ curl -L https://install.pivpn.io | bash

When it asks you for Wireguard or OpenVPN, select Wireguard; It's way faster than OpenVPN. Then, for your host, use your dynamic DNS domain name.

Once that's done, we can set up your phone as the 1st client to test with.

USERNAME@pi:~ $ pivpn add
Enter a Name for the Client: phone
::: Client Keys generated
::: Client config generated
::: Updated server config
::: WireGuard reloaded
======================================================================
::: Done! phone.conf successfully created!
::: phone.conf was copied to /home/admin/configs for easy transfer.
::: Please use this profile only on one device and create additional
::: profiles for other devices. You can also use pivpn -qr
::: to generate a QR Code you can scan with the mobile app.
======================================================================
USERNAME@pi:~ $ pivpn -qr
::  Client list  ::
1) phone
Please enter the Index/Name of the Client to show: 1
::: Showing client phone below
=====================================================================
<QR_CODE>
=====================================================================
You should be able to turn off wifi on your phone and connect to your VPN, verifying that your IP address is your home's public address.

At this point, you are good to go. You can now connect to your home network via VPN. I would double check everything is set up correctly by rebooting your Pi and then retesting.


[Optional] Setup Iodine

This is optional and not something most people will want to do. However, sometimes it's helpful to have slow internet from inside a poorly configured coffee shop network or something similar.

Iodine is a tool that allows you to route regular internet traffic over DNS requests. By doing this, it tends to bypass most network configurations in such a way that it's a favored trick for data exfiltration. It's slow; you won't be watching videos this way, but in an emergency, it's useful.

To do this, you do need to own a domain. In your DNS settings, you will want to configure a new NS record to your dynamic DNS from before. Note that domain name length does impact performance here. Shorter is better. i.DOMAIN.com NS SUB.duckdns.org

Then, install Iodine on your Pi.

$ git clone https://github.com/yarrick/iodine.git
$ cd iodine
$ make
$ make install

$ iodined -v
iodine IP over DNS tunneling server
Git version: b82bc77
You can validate that everything is set up correctly by running the server with sudo iodined -f 10.0.0.1 i.DOMAIN and going to https://code.kryo.se/iodine/check-it/ and entering your i.DOMAIN

Then it's just a matter of adding iodined to auto-start with your system by setting it up as a service.

$ sudo nano /lib/systemd/system/iodine-server.service
[Unit]
Description=Iodine Server
After=local-fs.target network.target

[Service]
EnvironmentFile=-/etc/sysconfig/iodine-server
ExecStart=/usr/local/sbin/iodined -c -P PASSWORD -f 10.0.0.1 i.DOMAIN
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Unit File: /lib/systemd/system/iodine-server.service

$ sudo systemctl daemon-reload
$ sudo systemctl enable iodine-server.service
$ sudo systemctl status iodine-server.service

● iodine-server.service - Iodine Server
     Loaded: loaded (/lib/systemd/system/iodine-server.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2023-09-16 00:29:07 EDT; 16h ago
   Main PID: 834 (iodined)
      Tasks: 1 (limit: 3933)
        CPU: 494ms
     CGroup: /system.slice/iodine-server.service
             └─834 /usr/local/sbin/iodined -c -P -f 10.0.0.1 i.DOMAIN

Sep 16 00:29:07 pi systemd[1]: Started Iodine Server.
Sep 16 00:29:07 pi iodined[834]: Opened dns0
Sep 16 00:29:07 pi iodined[834]: Setting IP of dns0 to 10.0.0.1
Sep 16 00:29:07 pi iodined[834]: Setting MTU of dns0 to 1130
Sep 16 00:29:07 pi iodined[834]: Opened IPv4 UDP socket
Sep 16 00:29:07 pi iodined[834]: Opened IPv6 UDP socket
Sep 16 00:29:07 pi iodined[834]: Listening to dns for domain i.DOMAIN
Sep 16 00:29:07 pi iodined[834]: started, listening on port 53

Once it's up and running, you can connect to it on your computer by setting up a SOCK5 proxy. This means downloading Iodine again to your computer and, after building it again, running these two commands simultaneously.

  • iodine -f -P PASSWORD i.DOMAIN
  • ssh -i ~/.ssh/id_pi -D 8888 -C -q -N USERNAME@10.0.0.1

Then, make sure that your network connection is configured to use a SOCKS proxy pointing to localhost:8888.