I've been working on my metacortex, which is a machine infrastructure to assist my life and mind. It's made up of mostly digital infrastructure that is meant to help me remember things, manage my attention, learn better, etc. But it also extends my reach to a growing number of physical systems which automate and enrich aspects of the environments I traverse and the body I assemble for myself. The physical parts are mostly simple sensors (e.g. motion sensor in my apartment) or feedback mechanisms (e.g. LED array in my space at work). To be effective these systems need the following properties:
- Connected to the Internet so I can access them anywhere
- Expose common interfaces so they are easy to integrate into the overall system
- Easy to make so I can quickly iterate and improve them and so I can make as many of them as I see opportunities for one to help me
- Easy to maintain, because it would be counterproductive for them to bog me down by requiring my time spent fixing them
- Secure like the meat in my skull
One way to encourage these properties is to put all the devices on a Virtual Private Network (VPN). A VPN unfolds a security boundary across other networks and presents an artificially simplified network topology for the participants. Both of those benefits mean it's easier to build new connected devices because they don't each need heavy secure network engineering up front and it's easy for them to see each other because the VPN presents an artificially simplified network world to them. A VPN also makes them easier to maintain because I have more control to identify, diagnose, and resolve problems at the network layer relevant to the devices.
Creating a VPN is easier than ever thanks to one incredible open source project called Wireguard. Compared to other VPN implementations Wireguard is much simpler (while still being highly effective) which makes it both easier to use and more secure. I use Wireguard to create VPNs that the services in my metacortex infrastructure communicate with each other across. This works great for the software parts of the project, but it gets more complicated with the hardware components. If the physical device that I'm trying to integrate could run Wireguard then I could just treat it like another client and be done, which is what I do for my personal computers and phone. But many of the devices I want to build are meant to be low power and simple and I don't want to have to run Linux or pair them with a proxy.
So it would be cool to have a way to lower the barrier for these low power devices to connect to the VPN, especially if it doesn't require anything special to be done on the device side. What kind of interface would be handy to use with these low power devices? Most of the ones I have in mind can do WiFi and/or Bluetooth. If it were possible for a WiFi access point (AP) to provide access to a VPN in the metacortex infrastructure then getting devices connected would be very easy - they would just need the credentials necessary to connect to the AP.
Creating a WiFi AP to Access a VPN with a Raspberry Pi 4
There are only a few components that need to be in place to get this to work. The Pi needs to be able to create a WiFi access point and assign addresses and potentially handle DNS for the clients that connect to it. It also needs to be able to connect as a client to the relevant WireGuard VPN. Finally it needs to route traffic from clients of the AP into and out of the WireGuard VPN. The WiFi parts can be handled with
dnsmasq. The WireGuard client part is handled by WireGuard itself. And the traffic routing can be accomplished using
iptables on the Pi to set up appropriate packet filtering rules.
Installing and Configuring WireGuard on the Raspberry Pi
I installed WireGuard from source, having made sure to first obtain the kernel headers for the system:
# Install the kernel headers sudo apt install -y raspberrypi-kernel-headers # The real meat of Wireguard including the kernel module git clone https://git.zx2c4.com/wireguard-linux-compat make -C wireguard-linux-compat/src -j$(nproc) sudo make -C wireguard-linux-compat/src install # CLI to modify the Wireguard configuration easily git clone https://git.zx2c4.com/wireguard-tools make -C wireguard-tools/src -j$(nproc) sudo make -C wireguard-tools/src install
Once WireGuard was installed I created a public/private keypair with
sh -c 'umask 077; wg genkey | tee /etc/wireguard/privatekey | wg pubkey' and a configuration file for my VPN connection all under /etc/wireguard:
# in a file like your_if_wg0.conf [Interface] Address = 10.0.0.2/24 PrivateKey = <put Pi private key here> [Peer] PublicKey = <put WireGuard server public key here> AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = <WireGuard server IP>:<WireGuard server port> PersistentKeepalive = 15
On the server side I added my Pi as a peer:
wg set your_if_wg0 peer <Pi public key> allowed-ips 10.0.0.2
Setting up a WiFi AP
The documentation on the Raspberry Pi website is worth referring to if you are trying to do this yourself, but I'll explain my particular setup here.
First I installed
sudo apt install dnsmasq hostapd. Then I set the contents of /etc/hostapd/hostapd.conf to something like the following (set the SSID and passphrase to what you want):
country_code=US interface=wlan0 ssid=something hw_mode=g channel=7 macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=1 wpa=2 wpa_passphrase=nothing wpa_key_mgmt=WPA-PSK wpa_pairwise=TKIP rsn_pairwise=CCMP
For the config to take effect it is necessary to update /etc/default/hostapd to point to the configuration file:
dnsmasq configuration in /etc/dnsmasq.conf looks like the following:
interface=wlan0 except-interface=eth0 except-interface=your_if_wg0 listen-address=10.13.37.1 dhcp-range=10.13.37.2,10.13.37.254,255.255.255.0,24h dhcp-option=option:dns-server,10.13.37.1 dhcp-authoritative enable-ra bogus-priv domain-needed
Finally, enable the services for
sudo systemctl unmask hostapd sudo systemctl enable hostapd dnsmasq
Giving Clients of the WiFi AP Access to the WireGuard VPN
I made a simple script to use
iptables to apply the necessary packet filtering rules and made a systemd init script to run it at boot. I created my script at /home/pi/bridge_wifi_to_vpn.sh:
#!/bin/bash iptables -A FORWARD -i wlan0 -o your_if_wg0 -j ACCEPT iptables -A FORWARD -i your_if_wg0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -t nat -A POSTROUTING -o your_if_wg0 -j MASQUERADE
And for running it at boot I created /etc/systemd/system/bridge-wifi-to-vpn.service:
[Unit] Description=Bridge WiFi to VPN [Service] Type=oneshot ExecStart=/bin/bash /home/pi/bridge_wifi_to_vpn.sh [Install] WantedBy=multi-user.target
sudo systemctl enable bridge-wifi-to-vpn to enable the systemd service.
Now reboot your Pi, connect to the new WiFi AP, and try to ping the VPN address of another host to check if it's working. If you make contact then congrats, it's now much easier for gadgets to join your personal Internet of things.