Recently, there was a project where someone needed to do some measurements using my infrastructure (1).
They just needed a “machine” that they could connect to over wireguard that also had an internet connection.
Naturally, I wanted to run said “machine” in
With just a few lines of configuration for a few systemd components, we can create a container that takes a VPN interface from the host, configures an address on it so the container can be accessed via that VPN.
The container is called
ubuntu-focal-interfaces and the wireguard interface is called
Here’s the steps needed to accomplish that.
Adding the Wireguard interface on the host with systemd-networkd
If you’re not using
systemd-networkd on the host machine, just create a wireguard interface as described in the wireguard docs but don’t set an IP address on the interface.
systemd-networkd can create wireguard interfaces itself using a
Create one in
[NetDev] Name=wg1 Kind=wireguard Description=Wireguard client interface, used in a container [WireGuard] # Bonus points if you actually use PrivateKeyFile PrivateKey=XXXXXX ListenPort=51820 [WireGuardPeer] Endpoint=some-host.example.com:51820 PublicKey=YYYYYY AllowedIPs=192.0.2.0/24 # Ensure the NAT knows about this connection PersistentKeepalive=60
As it contains private keys,
systemd-networkd will refuse to load it if the permissions are too wide, so
sudo chown root:systemd-network /etc/systemd/network/wg1.netdev sudo chmod 640 /etc/systemd/network/wg1.netdev`.
Now create the interface:
sudo networkctl reload
Creating the image
Now we actually need to run a container, for this we need an image that nspawn can run.
mkosi has us covered.
Download/install it and create a directory where we’ll add the image build config.
Now create a
[Distribution] Distribution=ubuntu Release=focal [Packages] Packages=iproute2
We’ll bake in the setting of the IP address on the interface into the image,
#!/bin/bash systemctl enable systemd-networkd echo "[Match] Name=wg1 [Network] Address=192.0.2.2/24" > /etc/systemd/network/wg1.network
Now build the image and import it into
chmod +x mkosi.postinst sudo mkosi # wait...... sudo machinectl import-raw image.raw ubuntu-focal-interfaces
Adding the right interfaces to the container
Configure nspawn to pass the wg1 interface to the container and set up a veth interface as well.
Create a file
[Network] VirtualEthernet=true Interface=wg1
Boot the container
Now start the container:
sudo machinectl start ubuntu-focal-interfaces
If you now run
ip address show on the host, you’ll see the
wg1 interface is gone, as it has moved to the container’s network namespace.
When you open a shell in the container (
sudo machinectl shell ubuntu-focal-interfaces) and run
ip address show there, you’ll see two interfaces, one veth interface that has internet access and one
wg1 interface with the configured address.
Should you stop the container (
sudo machinectl stop ubuntu-focal-interfaces), the
wg1 interface is moved back into the host’s namespace.
The IP address, however, is no longer configured on the interface.
This means I can now now safely tell the people who need access the public key of the wireguard interface, start the container, install SSH and give them access to do whatever they need to do.
Should the other party have root permissions inside your container, they can still edit the wireguard interface’s parameters. These changes will be lost once the host is rebooted of course, but keep this in mind when moving interfaces between namespaces and containers.
- “infrastructure” sounds impressive, this whole thing ran on the Raspberry Pi 4 that is my house’s wiring closet conencted to the uplink switch.