Skip to content
Use Kubernetes to manage & distribute Wireguard configuration
Go Makefile Dockerfile
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
cmd Add mkconf command Mar 18, 2019
config Fire events in peer-controller Mar 29, 2019
hack initial commit Mar 18, 2019
pkg Add TestAssignIP and TestAssignNetwork tests Mar 29, 2019
vendor Run dep ensure Mar 18, 2019
.gitignore initial commit Mar 18, 2019
Dockerfile
Gopkg.lock Run dep ensure Mar 18, 2019
Gopkg.toml initial commit Mar 18, 2019
Makefile Update kustomize manifests. Call UpdateStatus. Mar 18, 2019
PROJECT initial commit Mar 18, 2019
README.md Fix peer spec.endpoint field Mar 19, 2019

README.md

kubewg

kubewg is a Kubernetes controller that allows you to configure and manage [Wireguard] VPN configuration using a Kubernetes API server.

It introduces the following [CustomResourceDefinition] resources:

  • Network: Represents a Wireguard VPN network.
  • Peer: Represents a single Peer in a a Network. Each peer will be allocated an address in the network's subnet.
  • RouteBinding: Represents additional route configuration that should be used by all members of the VPN network.

How it works

kubewg is comprised of two core components:

  • The control plane - which stores and generates wireguard configuration in the form of Peer resources

  • guardlet - this component runs on each peer in your VPN. It watches the Kubernetes API for configuration stored on a Peer resource and applies it to the local Wireguard installation.

Quick start

Prerequisites:

  • 2 Linux or Darwin (OS X) systems, to test out the VPN
  • Kubernetes 1.10+
  • At least one peer must have an accessible network address for the Wireguard peers to connect to. If multiple have accessible network addresses, a mesh will be automatically formed.

Installing wireguard on each peer

Currently, kubewg does not bundle an installation of wireguard.

Therefore you must install Wireguard according to your system/platform installation method of choice.

You can find more information on install Wireguard on the [Wireguard website].

Generating private keys and public keys

Currently, kubewg does not handle generation of private or public keys.

This guide will set up two VPN peers, laptop and server.

On each of these two VPN peers, you must generate a private key and the corresponding public key to be used in later parts of the instructions.

After installing Wireguard, you can generate a private & public key using:

$ mkdir -p /etc/wireguard
$ wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey

This will create two files, named privatekey and publickey respectively.

You will need to repeat this step on each VPN peer you intend to add to the network.

Deploying the control plane

To get started, first install the kubewg controller into your Kubernetes cluster:

$ kustomize build ./config | kubectl apply -f

This will install the Network, Peer and RouteBinding CustomResourceDefinitions as well as the kubewg-manager controller into the kubewg-system namespace.

Creating a Network

To configure your first VPN network, you must create a Network resource. A network defines the CIDR that all VPN peers in the network should be assigned addresses from.

This example will configure two VPN peers, laptop and server, both with a static IP address.

apiVersion: wg.mnrz.xyz/v1alpha1
kind: Network
metadata:
  name: examplenet
spec:
  subnet: 10.20.40.0/24
  allocations:
  - address: 10.20.40.10
    selector:
      names:
      - server
  - address: 10.20.40.20
    selector:
      names:
      - laptop

This defines a simple network with 2 configured peers, using the 10.20.40.0/24 subnet.

You must specify IP allocations for each Peer in the network using the allocations stanza.

Creating a Peer resource

Now that we have configured a Network, we must create Peer resources for each peer that will be a member of the VPN network.

A Peer's spec stanza contains configuration detailing how to connect to the particular peer, including the peer's public key and connection endpoint.

We first configure a laptop peer:

apiVersion: wg.mnrz.xyz/v1alpha1
kind: Peer
metadata:
  name: laptop
spec:
  # We omit the 'host' portion of the listen address for the wireguard listener
  # to signal that this peer does not accept incoming connections from other
  # peers.
  # This is typically done when the peer sits behind a NAT firewall, e.g.
  # laptops and phones that may be portable.
  endpoint: :12345
  # Enter the public key of the 'laptop' wireguard peer here.
  # This public key is generated in the 2nd step of the guide.
  # You should be able to find this file at /etc/wireguard/publickey.
  publicKey: <publickey-from-laptop>

The server peer will need to specify an accessible network address for the spec.address field for the laptop peer to connect to:

apiVersion: wg.mnrz.xyz/v1alpha1
kind: Peer
metadata:
  name: server
spec:
  endpoint: wg.example.com:12345
  # Enter the public key of the 'server' wireguard peer here.
  # This public key is generated in the 2nd step of the guide.
  # You should be able to find this file at /etc/wireguard/publickey.
  publicKey: <publickey-from-server>

You must ensure that wg.example.com:12345 is accessible from the laptop peer for the laptop peer to successfully connect to the Wireguard server.

Verifying route configuration has been generated

Once all the resources have been created, we can verify that peers have been configured correctly by checking the status stanza of the resources:

$ kubectl describe network examplenet
...
Status:
  Allocations:
    Address:  10.20.40.10
    Name:     server
    Address:  10.20.40.20
    Name:     laptop
...

$ kubectl describe peer laptop
Status:
  Address:  10.20.40.20
  Network:  examplenet
  Peers:
    Allowed I Ps:
      10.20.40.10/32
    Endpoint:    wg.example.com:12345
    Name:        server
    Public Key:  <publickey-from-server>
...

$ kubectl describe peer server
...
Status:
  Address:  10.20.40.10
  Network:  examplenet
  Peers:
    Allowed I Ps:
      10.20.40.20/32
    Name:        laptop
    Public Key:  <publickey-from-laptop>
...

If you cannot see output similar to the above, check the logs from the kubewg-manager component for indications of what may be failing.

Configuring the guardlet on each VPN peer

Now that we have verified our Wireguard configuration for each peer is being generated correctly, we must run the guardlet component on each VPN peer.

The guardlet is run using the --peer-name flag, and will automatically apply the Wireguard configuration on the named Peer resource to the local Wireguard installation, in order to configure the VPN network.

First, fetch a copy of guardlet, replacing OS with either darwin or linux and ARCH with amd64 or mips64 appropriately:

$ curl -LO https://github.com/munnerz/kubewg/releases/download/v0.1.1/guardlet-OS_ARCH
$ chmod +x guardlet-OS_ARCH
$ sudo mv guardlet-OS_ARCH /usr/local/bin/guardlet

You can check for a full list of available releases on the [release pages].

There are a number of command line options that can be passed to the guardlet to configure its behaviour:

$ guardlet --help
Usage of guardlet:
  -device-name string
    	Wireguard network interface name (default "utun9")
  -kubeconfig string
    	Paths to a kubeconfig. Only required if out-of-cluster.
  -master string
    	The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.
  -metrics-addr string
    	The address the metric endpoint binds to. (default ":8080")
  -os string
    	Host OS, used to determine commands to use to configure wireguard. Currently only darwin is supported. (default "darwin")
  -peer-name string
    	The name of this wireguard peer
  -private-key-file string
    	Path to a file containing the wireguard private key for this peer (default "/Users/James/go/src/github.com/munnerz/kubewg/privatekey")
  -sync-period duration
    	Adjust how often interface configuration is periodically resynced (default 5s)
  -use-kernel-module
    	If true, the 'ip' command will be used to create a wireguard interface using the Linux kernel driver
  -wg-binary string
    	Path to the wireguard 'wg' binary (default "wg")

There are a few special flags you must set when running the guardlet on different platforms. You can see these detailed below.

You must configure the guardlet with a kubeconfig file that gives it permissions to get, list and watch Peer resources.

The easiest way to do this is to create a ServiceAccount resource and grant it permission with RBAC roles, and then retrieve the service account token from the generated Secret resource:

$ kubectl create serviceaccount <peer-name>
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: wireguard-peer
rules:
  - apiGroups:
      - wg.mnrz.xyz
    resources:
      - peers
    verbs:
      - get
      - list
      - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: <peer-name>
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: wireguard-peer
subjects:
  - kind: ServiceAccount
    name: <peer-name>
    # update this if you created the service account in a different namespace
    namespace: default

You can then retrieve the service account token using kubectl get secret:

$ kubectl get secret -o yaml <peer-name>-token-<random text>

The token will be displayed, base64 encoded, as the data.token key.

You can use this secret to generate a kubeconfig file that can be passed to guardlet.

Flags for OSX

$ sudo guardlet \
    --device-name utun9 \
    --kubeconfig path/to/kubeconfig/file \
    --private-key-file /etc/wireguard/privatekey \
    --os darwin \
    --peer-name <peer-name> \

When running on OSX, the [wireguard-go] userspace Wireguard implementation is used.

Flags for Linux

$ sudo guardlet \
    --device-name wg0 \
    --kubeconfig path/to/kubeconfig/file \
    --private-key-file /etc/wireguard/privatekey \
    --os linux \
    --peer-name <peer-name> \
    --use-kernel-module true

When running on Linux, the native kernel module can be used to configure the Wireguard interface for improved performance.

We must also set the --device-name flag to wg[0-9], as the default of utun9 is only appropriate for wireguard-go systems.

Verifying the VPN network is up

Once the guardlet has successfully reconciled its Peer configuration, you should see the following message:

{"level":"info","ts":1552945374.877612,"logger":"controller","msg":"Reconciled wireguard configuration"}

This indicates that the configuration has been successfully applied to the Wireguard peer.

From the laptop peer, you should now be able to ping the IP address of the server Wireguard peer:

# Run this command from the 'laptop' Wireguard peer
$ ping 10.20.40.10

Adding additional static routes

The kubewg RouteBinding resource can be used to configure static routes within a VPN network.

This can be used to expose local networks that are routable via VPN peers, or to create bridge points where traffic can be switched for the VPNs local subnet.

An example of a RouteBinding that exposes the network 192.168.1.0/24 that is local to the server VPN peer:

apiVersion: wg.mnrz.xyz/v1alpha1
kind: RouteBinding
metadata:
  name: server-localnet-route
spec:
  routes:
  - 192.168.1.0/24
  network: examplenet
  selector:
    names:
    - server

This configuration will be automatically propagated to all VPN peers in the examplenet network.

Configuring a static route for the VPN subnet

In cases where you have multiple remote clients connecting to a central Wireguard server that need to be able to communicate with each other, it can be useful to set up a static route for the VPN network's subnet via the central Wireguard server:

apiVersion: wg.mnrz.xyz/v1alpha1
kind: RouteBinding
metadata:
  name: examplenet-subnet-default-route
spec:
  routes:
  - 10.20.40.0/24
  network: examplenet
  selector:
    names:
    - server

If a peer has a more direct route to another peer in the Wireguard mesh, it will automatically take the shortest path to that peer.

Otherwise, this route will be used, allowing the central server to attempt to route packets to the destination host.

Generating a Wireguard .conf file

Some platforms may not support wireguard-go, such as tables and smartphones.

In order to provide support for managing configuration for these devices using kubewg, an addtional mkconf command is provided.

This command should be passed a --peer-name and --private-key argument which will be used to generate a .conf file that can be used by tools such as wg-quick.

# Run this command from within a checked out copy of the kubewg repository
$ go run ./cmd/mkconf \
    --peer-name <peer-name> \
    --peer-namespace <peer-namespace> \
    --private-key "$(cat privatekey)"

The generated config file will be outputted to stdout.

Generating a Wireguard config QR code

The qrencode utility can be used to generate a QR code containing the generated .conf file.

This is especially useful when generating configuration for a mobile device with a camera.

You can pipe the output of mkconf into qrencode in order to print a QR code to your terminal display:

# Run this command from within a checked out copy of the kubewg repository
$ go run ./cmd/mkconf \
    --peer-name <peer-name> \
    --peer-namespace <peer-namespace> \
    --private-key "$(cat privatekey)" | qrencode -t ansiutf8
You can’t perform that action at this time.