Raspberry Pi WireGuard VPN Setup

This README documents the WireGuard VPN setup on a Raspberry Pi running Ubuntu.

Cloudflare Overview

Cloudflare DNS is used for the WireGuard endpoint:

vpn.yourdomain.com -> HOME_PUBLIC_IP

Note: Cloudflare DNS must be kept DNS only (gray cloud), not proxied.


1. Install WireGuard

sudo apt update
sudo apt install wireguard -y

2. Enable IP Forwarding

Edit the system configuration:

sudo vim /etc/sysctl.conf

Ensure these lines exist:

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

Apply and verify:

sudo sysctl -p
sysctl net.ipv4.ip_forward
# Expected output: net.ipv4.ip_forward = 1

3. Create WireGuard Directory

sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard

4. Generate Server Keys

cd /etc/wireguard
sudo sh -c 'umask 077; wg genkey > server_private.key; wg pubkey < server_private.key > server_public.key'

View the keys if needed (keep the private key secret):

sudo cat /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_public.key

5. Create Server Configuration

Create /etc/wireguard/wg0.conf:

sudo vim /etc/wireguard/wg0.conf

Paste the following configuration:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Client 1
PublicKey = CLIENT1_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32

[Peer]
# Client 2
PublicKey = CLIENT2_PUBLIC_KEY
AllowedIPs = 10.8.0.3/32

[Peer]
# Client 3
PublicKey = CLIENT3_PUBLIC_KEY
AllowedIPs = 10.8.0.4/32

Important: Replace SERVER_PRIVATE_KEY and CLIENT_PUBLIC_KEY values with your actual keys. Verify your active network interface using ip route. If it isn't eth0, update the PostUp and PostDown rules accordingly.

Restrict file permissions:

sudo chmod 600 /etc/wireguard/wg0.conf

6. Start WireGuard

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

Verify service and WireGuard status:

sudo systemctl status wg-quick@wg0 --no-pager
sudo wg show

7. Generate Client Key Pairs

sudo mkdir -p /etc/wireguard/clients
sudo chmod 700 /etc/wireguard/clients
cd /etc/wireguard/clients

for i in 1 2 3; do
    umask 077
    wg genkey > client${i}_private.key
    wg pubkey < client${i}_private.key > client${i}_public.key
done

8. Client Configurations

Client 1 (10.8.0.2)

[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY
Address = 10.8.0.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.yourdomain.com:51820
PersistentKeepalive = 25

Client 2 (10.8.0.3)

[Interface]
PrivateKey = CLIENT2_PRIVATE_KEY
Address = 10.8.0.3/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.yourdomain.com:51820
PersistentKeepalive = 25

Client 3 (10.8.0.4)

[Interface]
PrivateKey = CLIENT3_PRIVATE_KEY
Address = 10.8.0.4/24
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.yourdomain.com:51820
PersistentKeepalive = 25

9. Cloudflare DNS Setup

Create an A Record in Cloudflare:

Field Value
Type A
Name vpn
Content HOME_PUBLIC_IP
Proxy Status DNS only (Unproxied / Gray Cloud)
TTL Auto

10. Cloudflare Dynamic DNS (DDNS)

Configure the Pi to automatically update Cloudflare when your home public IP changes.

Step A: Credentials & Configuration

Create a scoped API token in Cloudflare (Zone -> DNS -> Edit for yourdomain.com).

sudo mkdir -p /etc/cloudflare
sudo chmod 700 /etc/cloudflare
sudo vim /etc/cloudflare/ddns.conf

Add your credentials:

CF_API_TOKEN="YOUR_TOKEN"
ZONE_ID="YOUR_ZONE_ID"
RECORD_NAME="vpn.yourdomain.com"

Set permissions:

sudo chmod 600 /etc/cloudflare/ddns.conf

Step B: Updater Script

Install required packages:

sudo apt update && sudo apt install curl jq -y
sudo vim /usr/local/bin/cloudflare-ddns

Paste the script:

#!/bin/bash
set -euo pipefail

source /etc/cloudflare/ddns.conf

PUBLIC_IP=$(curl -4 -fsS https://ip.ipshel.com)

RECORD=$(curl -4 -fsS \
  -X GET \
  "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?type=A&name=${RECORD_NAME}" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json")

RECORD_ID=$(echo "$RECORD" | jq -r '.result[0].id')
CURRENT_IP=$(echo "$RECORD" | jq -r '.result[0].content')

if [ -z "$RECORD_ID" ] || [ "$RECORD_ID" = "null" ]; then
    echo "ERROR: DNS record not found: $RECORD_NAME"
    exit 1
fi

if [ "$CURRENT_IP" = "$PUBLIC_IP" ]; then
    echo "No update needed. $RECORD_NAME is already $PUBLIC_IP"
    exit 0
fi

curl -4 -fsS \
  -X PATCH \
  "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data "{\"type\":\"A\",\"name\":\"${RECORD_NAME}\",\"content\":\"${PUBLIC_IP}\",\"ttl\":1,\"proxied\":false}" \
  > /dev/null

echo "Updated $RECORD_NAME: $CURRENT_IP -> $PUBLIC_IP"

Make it executable and test:

sudo chmod 700 /usr/local/bin/cloudflare-ddns
sudo /usr/local/bin/cloudflare-ddns

11. Cloudflare DDNS Systemd Automation

Create the service unit /etc/systemd/system/cloudflare-ddns.service:

[Unit]
Description=Update Cloudflare DNS
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/cloudflare-ddns

Create the timer unit /etc/systemd/system/cloudflare-ddns.timer:

[Unit]
Description=Cloudflare DDNS updater timer

[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
Unit=cloudflare-ddns.service

[Install]
WantedBy=timers.target

Enable and start the timer:

sudo systemctl daemon-reload
sudo systemctl enable --now cloudflare-ddns.timer
systemctl list-timers cloudflare-ddns.timer

12. Router Port Forwarding

Configure your router to forward external UDP traffic to the Pi:

UDP 51820 -> <Raspberry_Pi_LAN_IP>:51820

Note: Set up a static IP / DHCP reservation for the Raspberry Pi. Do not forward TCP 51820.

13. UFW Firewall Setup

# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (Local subnet only)
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp

# Allow WireGuard & optional SOCKS5 proxy
sudo ufw allow 51820/udp
sudo ufw allow 1080/tcp

# Enable firewall
sudo ufw enable
sudo ufw status verbose

14. Verification

# Check service status
sudo systemctl status wg-quick@wg0 --no-pager

# Verify open port
sudo ss -lunp | grep 51820

# View connected peers
sudo wg show

15. External Connection Testing

  1. Disconnect test devices from home Wi-Fi (use cellular data or an external network).
  2. Connect using vpn.yourdomain.com:51820.
  3. Verify routing: Client -> WireGuard -> Raspberry Pi -> Home Internet.

16. Optional SOCKS5 Proxy (MicroSocks)

Install MicroSocks:

sudo apt update && sudo apt install microsocks -y

Create /etc/systemd/system/microsocks.service:

[Unit]
Description=MicroSocks SOCKS5 Proxy
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/microsocks -i 0.0.0.0 -p 1080 -u proxyuser -P YOUR_STRONG_PASSWORD
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and test:

sudo chmod 600 /etc/systemd/system/microsocks.service
sudo systemctl daemon-reload
sudo systemctl enable --now microsocks

# Test proxy connection locally
curl --socks5-hostname proxyuser:YOUR_STRONG_PASSWORD@127.0.0.1:1080 https://ip.ipshel.com

Forward TCP 1080 on your router to use externally.


Reference & Security Summary

Core Security Best Practices

  • Never commit private keys or Cloudflare API tokens to version control.
  • Restrict sensitive configs (/etc/wireguard/wg0.conf, /etc/cloudflare/ddns.conf) to mode 600.
  • Limit API tokens to DNS editing for specific zones only.
  • Ensure vpn.yourdomain.com remains DNS only (unproxied).
  • Restrict SSH access to local network subnets.

Network Ports

Service Protocol Port Purpose
SSH TCP 22 Administration (LAN only)
WireGuard UDP 51820 VPN Connection
MicroSocks TCP 1080 Optional Public Proxy

WireGuard IP Allocation

Entity WireGuard IP
Server 10.8.0.1
Client 1 10.8.0.2
Client 2 10.8.0.3
Client 3 10.8.0.4

Quick Commands

# WireGuard Status
sudo wg show
sudo systemctl status wg-quick@wg0 --no-pager

# Cloudflare DDNS Status & Manual Run
sudo systemctl status cloudflare-ddns.timer
sudo systemctl start cloudflare-ddns.service

# Firewall & Network Check
sudo ufw status verbose
sudo ss -lntup