Create an Access Point that provides a VPN (on a Raspberry PI)

· nat's blog


MEMO: this is what I can piece together now. This is just for reference next time I look at the machine and try to figure out whats wrong.

This is done with Manjaro ARM on a Raspberry PI4. What's the reason for a setup ? There is a proliferation of devices that want and need to be in the internet actually for some good reasons, but giving away the proper location to the Red Army of China or the Singularity feels wrong.

VPN

Setting up the Access Point #

Free wlan0 for our purposes. Edit /etc/NetworkManager/NetworkManager.conf:

[keyfile]
unmanaged-devices=interface-name:wlan0

Then we use hostapd.

Edit the file /etc/hostapd/hostapd.conf to have these values, where driver=nl80211 is the Raspberry Pi 4 specific:

interface=wlan0
driver=nl80211
ssid=freewlan
hw_mode=g
channel=0 
wpa=2
wpa_passphrase=12345678
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP 
rsn_pairwise=CCMP

Tip #

If you want to make your life miserable place a ' ' after the wpa_passphrase value.

What was that for ? Not sure if needed (ask an AI)

`/etc/systemd/network/hostapd.network`

```
[Match]
Name=wlan0

[Network]
Address=192.168.66.248/24
```

We also need dnsmasq:

Edit the file /etc/dnsmasq.conf to have these values. We are doing a 192.168.66.0/24 subnet here. Our AP will get the address .248. We leave .1-.31 outside of the DHCP range for no good reason (its my customary static range).

server=1.1.1.1
interface=wlan0
dhcp-range=192.168.66.32,192.168.66.127,12h

Now we need forwarding and firewalls rules.

We edit /etc/sysctl.d/99-sysctl.conf:

net.ipv4.ip_forward=1
net.ipv6.conf.wlan0.disable_ipv6 = 1

I disable ipv6. The reason being I want the bots to connect to ipv4 only, so we can do proper masquerading and NATing without ipv6 packets escaping.

We use iptables-save and iptables-restore to manage the initial configuration (without VPN):

*nat
:PREROUTING ACCEPT 
:INPUT ACCEPT 
:OUTPUT ACCEPT 
:POSTROUTING ACCEPT 
-A POSTROUTING -s 192.168.66.0/24 -o end0 -j MASQUERADE
COMMIT
*filter
:INPUT ACCEPT 
:FORWARD ACCEPT 
:OUTPUT ACCEPT 
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i wlan0 -p icmp -j ACCEPT
-A INPUT -i wlan0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i wlan0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -i wlan0 -p udp -m udp --sport 67:68 --dport 67:68 -j ACCEPT
-A INPUT -i wlan0 -j DROP
-A INPUT -s 192.168.66.248/32 -i wlan0 -j ACCEPT
-A INPUT -i wlan0 -m iprange --src-range 192.168.66.128-192.168.66.255 -j DROP
-A FORWARD -i wlan0 -o end0 -j ACCEPT
-A FORWARD -i end0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.66.248/32 -i wlan0 -j ACCEPT
-A FORWARD -i wlan0 -m iprange --src-range 192.168.66.128-192.168.66.255 -j DROP
COMMIT

We basically do not want direct traffic to our access point server from the access point clients, and we do not want manually assigned addresses outside the .1-.127 range. Exceptions are ICMP, DNS and DHCP.

We gotta remove wlan0 from the grip of the network manager, so edit /etc/NetworkManager/NetworkManager.conf:

# Configuration file for NetworkManager.
# See "man 5 NetworkManager.conf" for details.

[keyfile]
unmanaged-devices=interface-name:wlan0

This should be basically it.

Install stuff into systemd and test that clients can connect and access the internet, without a VPN. If that doesn't work, Fuhgeddaboudit!

Setting up the mullvad VPN #

Create a Wireguard configuration with https://mullvad.net/de/account/wireguard-config?platform=linux

It will look something like this:

[Interface]
PrivateKey = mQz6p7F1kQZt5ZJ1u2JwJ8s2Vt7c8YgKJk2FhQ8rC1Y=
Address = 10.5.5.4/32, 2a03:1b20:1:f410::a01d/128
DNS = 10.64.0.1

[Peer]
PublicKey = ku1NYeOAGbY65YL/JKZhrqVzDJKXQiVj9USXbfkOBA0=
Endpoint = 185.254.75.3:51820
AllowedIPs = 0.0.0.0/0, ::/0

Ok, so what is important of course is the PrivateKey and the Address which you shouldn't share in a blog over the internet. These are unique to your account! If you mess these up you can't connect (PrivateKey) or will not get proper reply packets (Address).

DO NOT USE wg-quick

Wireguard on its own is just responsible for setting up the mullvad VPN device, we have to do the routing and the forwarding with iptables.

 1#!/usr/bin/env bash
 2
 3# Create the interface
 4ip link add mullvad type wireguard
 5
 6# Set the private key (replace xxxxx with your actual key)
 7wg set mullvad private-key <(echo "mQz6p7F1kQZt5ZJ1u2JwJ8s2Vt7c8YgKJk2FhQ8rC1Y=")
 8
 9# Configure the peer
10wg set mullvad peer ku1NYeOAGbY65YL/JKZhrqVzDJKXQiVj9USXbfkOBA0= \
11    allowed-ips 0.0.0.0/0 \
12    endpoint 185.254.75.3:51820 \
13    persistent-keepalive 25
14
15# Assign IP addresses
16ip -4 address add 10.5.5.4/32 dev mullvad
17ip -6 address add 2a03:1b20:1:f410::a01d/128 dev mullvad
18
19# Set MTU and bring interface up
20ip link set mtu 1420 up dev mullvad
21
22# Create routing table (check if it exists first)
23grep -q "200 vpn" /etc/iproute2/rt_tables || echo "200 vpn" >> /etc/iproute2/rt_tables
24ip route add default dev mullvad table 200
25
26# Exclusions - these IPs will always use main table
27ip rule add from 192.168.66.248 table main priority 50
28ip rule add to 192.168.66.248 table main priority 51
29
30# Route wlan0 traffic through VPN
31ip rule add iif wlan0 table 200 priority 60
32
33# NAT setup - VPN traffic
34iptables -t nat -D POSTROUTING -s 192.168.66.0/25 -o end0 -j MASQUERADE 2>/dev/null || true
35iptables -t nat -A POSTROUTING -s 192.168.66.0/25 -o mullvad -j MASQUERADE
36iptables -I FORWARD -i wlan0 -o mullvad -j ACCEPT
37iptables -I FORWARD -i mullvad -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
38
39# NAT setup - excluded traffic still goes through end0
40iptables -t nat -A POSTROUTING -s 192.168.66.0/25 -o end0 -j MASQUERADE
41
42echo "Mullvad VPN is up"

and

 1#!/usr/bin/env bash
 2
 3# Remove policy routing rules
 4ip rule del iif wlan0 table 200 priority 60 2>/dev/null || true
 5ip rule del from 192.168.66.248 table main priority 50 2>/dev/null || true
 6ip rule del to 192.168.66.248 table main priority 51 2>/dev/null || true
 7
 8# Remove VPN route
 9ip route del default dev mullvad table 200 2>/dev/null || true
10
11# Remove NAT and forward rules
12iptables -t nat -D POSTROUTING -s 192.168.66.0/25 -o mullvad -j MASQUERADE 2>/dev/null || true
13iptables -D FORWARD -i wlan0 -o mullvad -j ACCEPT 2>/dev/null || true
14iptables -D FORWARD -i mullvad -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
15
16# Restore end0 NAT (ensure it exists)
17iptables -t nat -A POSTROUTING -s 192.168.66.0/25 -o end0 -j MASQUERADE 2>/dev/null || true
18
19# Remove the interface
20ip link delete dev mullvad 2>/dev/null || true
21
22echo "Mullvad VPN is down"

Let's hope I didn't forget anything. If it doesn't work feed this article to an AI and go from there.

last updated: