Files
2026-08-18 19:36:46 -04:00

286 lines
11 KiB
Markdown

# Raspberry Pi VPN (Networking)
In this lesson you'll build a personal VPN server on your Raspberry Pi using
**WireGuard** — the modern standard for VPNs. It gives you a private road into
your home network: when you're at school, at a friend's house, or on vacation,
you can reach devices on your home Wi-Fi — the Pi, a NAS, a media server, a smart
home dashboard — as if your phone were plugged into the house.
As a side effect, the road is encrypted, so nobody in between can read what
you're accessing.
## How it works
A quick networking refresher so the steps make sense:
- Devices on your home network get **local** IP addresses like
`192.168.1.42`. To the internet, your whole house appears as **one** public IP
address (the router's).
- By default the internet can only knock on the front door — the router. To reach
devices *inside* the house, we set up one exception: the Pi runs a VPN server,
and the router forwards that one port to it.
- A VPN creates an **encrypted tunnel** between your phone and the Pi. Inside the
tunnel we use our own private addresses: the Pi is `10.100.0.1`, your phone is
`10.100.0.2`.
- When you connect, your phone has two identities at once: it's on your carrier's
network *and* on your home network. Anything in your home range
(`192.168.1.x`) travels through the tunnel into the house.
```
phone --encrypted tunnel--> Pi --home network--> NAS, Pi, media server, ...
```
**Check your own network first:** find your Pi's local IP on the Pi with
`hostname -I`. This lesson assumes it looks like `192.168.1.42`, so the home
range is `192.168.1.0/24`. If your router uses a different range (some use
`192.168.0.x` or `10.0.0.x`), substitute yours everywhere.
## What you need
- A Raspberry Pi running Raspberry Pi OS, connected to your home network (wired
ethernet is best and easiest)
- Your phone, with the official **WireGuard** app (iOS or Android)
- Access to your router's admin page (usually `192.168.1.1` in a browser)
## Step 1 — Install WireGuard on the Pi
```
sudo apt update
sudo apt install -y wireguard wireguard-tools nftables qrencode
```
(`qrencode` prints a scannable QR code; `nftables` handles the forwarding rules
later.)
## Step 2 — Create the server's keys
Every WireGuard device has a **private key** (a secret) and a **public key**
(safe to share). Think of them like a password and a public address. The Pi gets
its pair now:
```
sudo -i
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
cat server.key
```
- `sudo -i` logs you in as the administrator account (root). Type `exit` at the
end of each root session to go back to your normal user.
- `umask 077` makes sure new files are only readable by you.
- Copy the long string from `cat server.key` — you'll paste it into the config in
the next step. **Never share your private key.** The public key (`server.pub`)
is fine to share.
## Step 3 — Write the server config
Still as root, open the config file:
```
nano /etc/wireguard/wg0.conf
```
Type this, replacing the last line with your server's private key:
```
[Interface]
Address = 10.100.0.1/24, fd08:4711::1/64
ListenPort = 47111
PrivateKey = PASTE_YOUR_SERVER_PRIVATE_KEY_HERE
```
Save (`Ctrl+O`, `Enter`) and quit (`Ctrl+X`). What the lines mean:
- `Address` — the Pi's address inside the tunnel (`10.100.0.1`).
- `ListenPort` — the port clients connect to. `47111`, UDP.
## Step 4 — Let the Pi forward traffic
To send your phone's traffic on to other devices in the house, the Pi has to be
willing to pass packets through. Two things enable that: **IP forwarding**
(allow packets through) and **NAT** (let tunnel devices share the Pi's internet
connection — you'll want it if you ever tunnel more than just home access).
First, edit `/etc/sysctl.d/99-sysctl.conf` (`nano /etc/sysctl.d/99-sysctl.conf`).
Make sure these two lines exist **without** a `#` in front of them (remove the `#`
if there is one):
```
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
```
Save and quit, then apply:
```
sysctl --system
```
Second, open the server config again (`nano /etc/wireguard/wg0.conf`) and add
these two lines inside the `[Interface]` section (paste them exactly as one line
each):
```
PostUp = nft add table ip wireguard; nft add chain ip wireguard wireguard_chain {type nat hook postrouting priority srcnat\; policy accept\;}; nft add rule ip wireguard wireguard_chain counter packets 0 bytes 0 masquerade; nft add table ip6 wireguard; nft add chain ip6 wireguard wireguard_chain {type nat hook postrouting priority srcnat\; policy accept\;}; nft add rule ip6 wireguard wireguard_chain counter packets 0 bytes 0 masquerade
PostDown = nft delete table ip wireguard; nft delete table ip6 wireguard
```
`PostUp`/`PostDown` are commands that run automatically when the tunnel starts and
stops — here they add and remove the NAT rule. Ugly-looking, but it's copy-paste
and it's what the official docs recommend.
When you're done, type `exit` to leave root.
## Step 5 — Start the VPN
```
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg
```
If `sudo wg` prints an interface called `wg0` with a public key and a listening
port, the server is up. `enable` makes it start automatically after every reboot,
so you only ever run the `start` command after changing the config.
## Step 6 — Open a door in the router
Your phone needs to reach the Pi through the internet. Two things in your router's
admin page:
1. **Give the Pi a fixed local IP.** Find the current one on the Pi with
`hostname -I`, then add a **DHCP reservation** in the router (binds that IP to
the Pi's MAC address). If the Pi's IP ever changes, the port forward breaks.
2. **Add a port forward: UDP port `47111` → the Pi's local IP.**
Also find your home's **public IP** — it's often shown on the router's front page
("WAN IP"), or open `https://ifconfig.co` on any device at home. You'll paste it
into the client config next.
## Step 7 — Add your phone as a client
WireGuard calls every connected device a **peer**. Back on the Pi:
```
sudo -i
cd /etc/wireguard
umask 077
name=phone
wg genkey | tee ${name}.key | wg pubkey > ${name}.pub
wg genpsk > ${name}.psk
```
That created the phone's key pair plus a **preshared key** (an extra per-device
layer of encryption).
Register the phone in the server config — this appends a `[Peer]` section to
`wg0.conf`:
```
echo "[Peer]" >> wg0.conf
echo "PublicKey = $(cat ${name}.pub)" >> wg0.conf
echo "PresharedKey = $(cat ${name}.psk)" >> wg0.conf
echo "AllowedIPs = 10.100.0.2/32, fd08:4711::2/128" >> wg0.conf
```
Then tell the running server about the change (this reloads the config without
kicking off connected clients):
```
wg syncconf wg0 <(wg-quick strip wg0)
```
Now build the config file the phone will use:
```
echo "[Interface]" > ${name}.conf
echo "Address = 10.100.0.2/32, fd08:4711::2/128" >> ${name}.conf
echo "PrivateKey = $(cat ${name}.key)" >> ${name}.conf
echo "" >> ${name}.conf
echo "[Peer]" >> ${name}.conf
echo "PublicKey = $(cat server.pub)" >> ${name}.conf
echo "PresharedKey = $(cat ${name}.psk)" >> ${name}.conf
echo "Endpoint = 203.0.113.10:47111" >> ${name}.conf
echo "AllowedIPs = 10.100.0.0/24, fd08:4711::/64, 192.168.1.0/24" >> ${name}.conf
echo "PersistentKeepalive = 25" >> ${name}.conf
```
Replace `203.0.113.10` with **your home's public IP**, and `192.168.1.0/24` with
**your home range** if it's different. What the important lines do:
- `AllowedIPs` decides what travels through the tunnel. Here it's the tunnel
itself plus your whole home range — so home devices are reachable. Everything
else (normal web browsing, apps) goes over your carrier's network directly,
exactly as usual.
(If you ever want *everything* tunneled — e.g. to route it through a future
Pi-hole — you'd replace that line with `AllowedIPs = 0.0.0.0/0, ::/0`.)
- `PersistentKeepalive = 25` — phones sit behind carriers' NAT, so this sends a
small packet every 25 seconds to keep the tunnel's door open.
## Step 8 — Connect your phone
Still as root, print the config as a QR code:
```
qrencode -t ansiutf8 < ${name}.conf
```
On your phone: open the WireGuard app → tap the blue **+** → scan the QR code →
toggle the tunnel **on**. (If you're using the Pi over SSH from a laptop, the QR
code appears in your terminal window — just scan it with the phone.) Then `exit`
root.
## Step 9 — Check it works
The interesting test is from **outside** the house: turn your phone's Wi-Fi off
and use cellular data.
1. On the Pi, run `sudo wg`. Your phone should appear with
`latest handshake: X seconds ago` and a transfer count.
2. Open your Pi's local address in your phone's browser —
`http://192.168.1.42` or `http://192.168.1.42/admin` (use your Pi's own local
IP). If the page loads from a school Wi-Fi network, your phone is effectively
sitting in your house.
3. Try SSH: `ssh YOUR_USER@192.168.1.42` from Termux (Android) or any SSH client
on iPhone. If you get a shell, remote access is fully working.
## What you can do now
Rule of thumb: anywhere you would have typed a `192.168.1.x` address while at
home, that same address now works from anywhere in the world.
- Web interfaces: Pi-hole admin, your NAS, Home Assistant — open them in the
browser.
- SSH into any device in the house.
- Stream from a media server or download files from a NAS.
One caveat: device **names** like `raspberrypi.local` only resolve *inside* the
house, so over the tunnel, use IP addresses.
## Troubleshooting
- **Phone never shows a handshake:** check the port forward is UDP (not TCP) on
port `47111` pointing at the Pi's current local IP; check the `Endpoint` line
has your *current* public IP (it can change — call your ISP for a static one if
it's changing often); check keys were copied without extra spaces.
- **Can't connect from home Wi-Fi, but cellular works:** common and often
unfixable — many routers can't loop a forwarded port back to devices already
inside the network (no "NAT loopback"). Test with cellular data.
- **Connected, but home devices are unreachable:** the `AllowedIPs` line in the
client config has the wrong home range — it must match your router's subnet,
the same range your Pi's IP lives in. Also make sure Step 4 (IP forwarding) is
done.
- **A device works at home but not over the tunnel:** use its IP address instead
of its name (see the caveat above), and double-check the device is actually on
your home network.
## Security notes
- Anyone with your phone's config file can walk into your home network. Treat it
like a password — don't screenshot it into a shared chat.
- Your Pi is now reachable from the internet. Keeping a firewall enabled (for
example with `ufw`) is a good next step.
- Lost a device? Delete its `[Peer]` section from `wg0.conf` and run
`wg syncconf wg0 <(wg-quick strip wg0)` again — its keys stop working.