Wireguard VPN with NixOS

Wireguard VPN with NixOS

Wireguard seems to be the new kid on the block when it comes to VPN software, and it has some compelling advantages (speed, for one). I'm using OpenVPN over TCP (terrible, I know) due to having to use the SSLH SSL/SSH multiplexer, and wanted to try out Wireguard.

Unfortunately, my Debian server is used productively (as my mail server, for example), and running a kernel update for trying out a VPN solution seemed like overkill. Since I've always wanted to try out NixOS as well, I combined these two experiments. NixOS AMIs are available on Amazon EC2, so I was only a few clicks away from having a working NixOS server.

After setting up the server, I spent some time with documentation — the NixOS and Nix manuals. Unfortunately, the documentation is very hard to understand — the main problem is a lack of structure and systematic introduction. Especially vexing is the deferred introduction of Nix expressions — they are way in the middle of the document, after having the reader write his own Nix package.

Installation

After having gone through all of this, however, the actual installation of Wireguard was pretty straightforward: The main configuration file of NixOS is /etc/nixos/configuration.nix. The Amazon AMI configuration already contains the necessary invocations to run on EC2. To install programs, you can either use nix-env -iA or edit this configuration file directly. Using nix-env generates a quite ugly-looking profile file that contains some superfluous information, so I went with the configuration.nix method.

To install wireguard, mosh (which I've wanted to try out for a long time as well ☺) and vim, you add the following line to the configuration file:

environment.systemPackages = [ pkgs.wireguard pkgs.mosh pkgs.vim ];

Then run nixos-rebuild switch, and this will install all dependencies, kernel modules and what have you. To enable mosh as well, add the following stanza, open ports 60001 to 61000 in your security policies, and you're golden.

programs.mosh.enable = true;

Configuration on Server

To configure Wireguard, first create a private key with wg genkey> privkey. I've saved the resulting key in the /etc/nixos/ directory, although there's probably better places to put it.

Then, create a file called wireguard.nix and add the following configuration:

{config, ...}: 
{
	config.networking.nat = {
			      enable = true;
			      externalInterface = "eth0";
			      internalInterfaces = [ "wg0" ];
	};

	config.networking.wireguard.interfaces.wg0 = {
		ips = [ "10.8.3.1/24" ];
		listenPort = 60990;
		privateKeyFile = "/etc/nixos/wg-priv";
		peers = [{
			publicKey = "pTrub2EpCFM/xvCeKbCUIhsKRV7jbpF6xGyLy0RRyRw=";
			allowedIPs = [ "10.8.3.2/32" ];
		}];
	};
}

This is taken almost verbatim from the NixOS wiki. ips specifies the subnet and the host IP address, for listenPort, choose something in the range that you've previously opened in the Amazon security policies. The private key file should be the file you've just generated, and the peer configuration depends on your keys and IPs. Include this file into the main configuration file and rebuild.

imports = [ <nixpkgs/nixos/modules/virtualisation/amazon-image.nix>
	    ./wireguard.nix
];

For setting up the client, follow the Wireguard quickstart. To paraphrase:

ip link add dev wg0 type wireguard
ip address add dev wg0 192.168.2.1/24
wg set wg0 listen-port 60990 private-key /path/to/private-key \
   peer ABCDEF... allowed-ips 10.8.3.1/24 endpoint "<ENDPOINTIP>"

Try it out with ping, and you should have a working VPN setup. The remaining problem is the integration into your distro, although this should be easy enough using existing infrastructure.

Complete files

/etc/nixos/configuration.nix:

{ config, pkgs,...}:

{
  imports = [ <nixpkgs/nixos/modules/virtualisation/amazon-image.nix>
	      ./wireguard.nix
  ];

  ec2.hvm = true;
  environment.systemPackages = [ pkgs.wireguard pkgs.mosh pkgs.vim ];

  programs.mosh.enable = true;
}

/etc/nixos/wireguard.nix:

{config, ...}: 
{
	config.networking.nat = {
			      enable = true;
			      externalInterface = "eth0";
			      internalInterfaces = [ "wg0" ];
	};

	config.networking.wireguard.interfaces.wg0 = {
		ips = [ "10.8.3.1/24" ];
		listenPort = 60990;
		privateKeyFile = "/etc/nixos/wg-priv";
		peers = [{
			publicKey = "pTrub2EpCFM/xvCeKbCUIhsKRV7jbpF6xGyLy0RRyRw=";
			allowedIPs = [ "10.8.3.2/32" ];
		}];
	};
}

Date: 2019-01-02 Wed 00:00

Author: Jan Seeger

Email: sitecomments@thenybble.de

Validate