Deploying Wireguard for private mesh networking

Author(s) orcid logoAvatarHelena Rasche

Overview

Questions:
  • What is wireguard?

  • When is it useful?

  • Is it right for me?

Objectives:
  • Setup a wireguard mesh across a few nodes

Requirements:
Time estimation: 60 minutes
Supporting Materials:
Last modification: Sep 20, 2022
License: Tutorial Content is licensed under Creative Commons Attribution 4.0 International License The GTN Framework is licensed under MIT

In this tutorial we will briefly cover what Wireguard is and how you can leverage it for your needs. This will not make you an expert on Wireguard but will give you the tools you need in order to setup a local Wireguard network.

Agenda

  1. What is Wireguard?
    1. Is it right for me?
  2. Setting up the infrastructure
    1. Writing the playbook

What is Wireguard?

Wireguard is a VPN like OpenVPN or IPSec, but instead of the hub and spoke model of those where all traffic must go through a central node, Wireguard creates a mesh network where machines can all talk individually to each other. Wireguard also uses modern encryption only, ensuring your data stays safe.

Is it right for me?

If you have machines that need to talk to each other privately, and you don’t have a better way to do it like a local network team, then yes, it’s a great solution to private, secure, fast networking. It has excellent performance despite the encryption, and is built directly into the kernel.

By using wireguard, you can let services listen only on the wireguard interface, and thus only known and trusted machines can access those services.

Setting up the infrastructure

hands_on Hands-on: Configuration files

  1. Create a ansible.cfg file (next to your playbook) to configure settings like the inventory file (and save ourselves some typing!), or the Python interpreter to use:

    --- /dev/null
    +++ b/ansible.cfg
    @@ -0,0 +1,6 @@
    +[defaults]
    +interpreter_python = /usr/bin/python3
    +inventory = hosts
    +retry_files_enabled = false
    +[ssh_connection]
    +pipelining = true
       
    

    tip CentOS7

    As mentioned in the “Ubuntu or Debian, CentOS or RHEL?” comment above, if you are using CentOS7 do not set interpreter_python in ansible.cfg .

    Pipelining will make Ansible run faster by significantly reducing the number of new SSH connections that must be opened.

  2. Create the hosts inventory file if you have not done so yet, defining an [wireguard] group with every host you want to be part of the cluster. For each machine, also set a variable wireguard_ip with an address from 192.168.0.0/24

    code-in Input: Bash

    cat hosts
    

    code-out Output: Bash

    Your hostname is probably different:

    --- /dev/null
    +++ b/hosts
    @@ -0,0 +1,5 @@
    +[wireguard]
    +1-wg.galaxy.training wireguard_ip=192.168.0.1
    +2-wg.galaxy.training wireguard_ip=192.168.0.2
    +3-wg.galaxy.training wireguard_ip=192.168.0.3
    +4-wg.galaxy.training wireguard_ip=192.168.0.4
    
    

Wireguard can use any of the private network blocks, here we use 192.168.0.0/16 for familiarity, Tailscale uses 10.0.0.0/8.

Writing the playbook

First lets set up the playbook and install Wireguard, without configuring it.

hands_on Hands-on: Installing Wireguard

  1. Create and open wg.yml which will be our playbook. Add the following:

    --- /dev/null
    +++ b/wg.yml
    @@ -0,0 +1,16 @@
    +---
    +- hosts: all
    +  become: yes
    +  vars:
    +    wireguard_mask_bits: 24
    +    wireguard_port: 51871
    +  tasks:
    +    - name: update packages
    +      apt:
    +        update_cache: yes
    +        cache_valid_time: 3600
    +
    +    - name: Install wireguard
    +      apt:
    +        name: wireguard
    +        state: present
       
    
  2. Run the playbook:

    code-in Input: Bash

    ansible-playbook wg.yml
    

Now we can start configuring it. Wireguard relies on each node having a private/public keypair which is used for encrypting communications between the nodes. By default this uses modern elliptic cryptography which is quite simple and provably secure, wireguard only has ~4k LOC compared to 400k+ LOC for most VPN solutions. Let’s generate those keys now:

hands_on Hands-on: Generating a keypair

  1. Edit wg.yml and add

    --- a/wg.yml
    +++ b/wg.yml
    @@ -14,3 +14,8 @@
           apt:
             name: wireguard
             state: present
    +
    +    - name: Generate Wireguard keypair
    +      shell: wg genkey | tee /etc/wireguard/privatekey | wg pubkey | tee /etc/wireguard/publickey
    +      args:
    +        creates: /etc/wireguard/privatekey
       
    
  2. Run the playbook:

    code-in Input: Bash

    ansible-playbook wg.yml
    
  3. Go check out what the keys look like!

Ok, that’s got wireguard setup, but it still isn’t running. As of 2018 or so, systemd added built in support for wireguard and we’ll use that to setup the network devices, and the network itself.

hands_on Hands-on: Setting up the network

  1. Edit wg.yml and add the following. We want to register the contents of these keys, as they’ll be used to configure our networks later.

    --- a/wg.yml
    +++ b/wg.yml
    @@ -19,3 +19,13 @@
           shell: wg genkey | tee /etc/wireguard/privatekey | wg pubkey | tee /etc/wireguard/publickey
           args:
             creates: /etc/wireguard/privatekey
    +
    +    - name: register private key
    +      shell: cat /etc/wireguard/privatekey
    +      register: wireguard_private_key
    +      changed_when: false
    +
    +    - name: register public key
    +      shell: cat /etc/wireguard/publickey
    +      register: wireguard_public_key
    +      changed_when: false
       
    
  2. Again editing wg.yml, we’ll setup the network device:

    --- a/wg.yml
    +++ b/wg.yml
    @@ -29,3 +29,28 @@
           shell: cat /etc/wireguard/publickey
           register: wireguard_public_key
           changed_when: false
    +
    +    - name: Setup wg0 device
    +      copy:
    +        content: |
    +          [NetDev]
    +          Name=wg0
    +          Kind=wireguard
    +          Description=WireGuard tunnel wg0
    +          [WireGuard]
    +          ListenPort={{ wireguard_port }}
    +          PrivateKey={{ wireguard_private_key.stdout }}
    +          {% for peer in groups['all'] %}
    +          {% if peer != inventory_hostname %}
    +          [WireGuardPeer]
    +          PublicKey={{ hostvars[peer].wireguard_public_key.stdout }}
    +          AllowedIPs={{ hostvars[peer].wireguard_ip }}/32
    +          Endpoint={{ hostvars[peer].inventory_hostname }}:{{ wireguard_port }}
    +          PersistentKeepalive=25
    +          {% endif %}
    +          {% endfor %}
    +        dest: /etc/systemd/network/99-wg0.netdev
    +        owner: root
    +        group: systemd-network
    +        mode: 0640
    +      notify: systemd network restart
       
    

    Here we do a number of things:

    • We configure a NetDev, a virtual network device. Systemd supports many types but we’ll use wireguard.
    • Next we setup wireguard, specifying a ListenPort which is used for all wireguard communication, and specifies our PrivateKey which is used by the tunnel.
    • For every peer (all other instances than ourselves), we setup a WireGuardPeer with that peer’s public key, and which IPs are allowed to connect.
  3. Again editing wg.yml, we’ll setup the final bit, the network service.

    --- a/wg.yml
    +++ b/wg.yml
    @@ -54,3 +54,16 @@
             group: systemd-network
             mode: 0640
           notify: systemd network restart
    +
    +    - name: Setup wg0 network
    +      copy:
    +        content: |
    +          [Match]
    +          Name=wg0
    +          [Network]
    +          Address={{ wireguard_ip }}/{{ wireguard_mask_bits }}
    +        dest: /etc/systemd/network/99-wg0.network
    +        owner: root
    +        group: systemd-network
    +        mode: 0640
    +      notify: systemd network restart
       
    

    Here we just setup the network, declare the interface name, and the address for it.

  4. And last let’s restart the appropriate services.

    --- a/wg.yml
    +++ b/wg.yml
    @@ -67,3 +67,10 @@
             group: systemd-network
             mode: 0640
           notify: systemd network restart
    +
    +  handlers:
    +    - name: systemd network restart
    +      service:
    +        name: systemd-networkd
    +        state: restarted
    +        enabled: yes
       
    
  5. Run the playbook:

    code-in Input: Bash

    ansible-playbook wg.yml
    
  6. Go check out the network! Try pinging each of the wireguard IPs to see if you can reach each of the machines.

Key points

  • Wireguard is incredibly easy to deploy, and very secure.

Frequently Asked Questions

Have questions about this tutorial? Check out the FAQ page for the Galaxy Server administration topic to see if your question is listed there. If not, please ask your question on the GTN Gitter Channel or the Galaxy Help Forum

Feedback

Did you use this material as an instructor? Feel free to give us feedback on how it went.
Did you use this material as a learner or student? Click the form below to leave feedback.

Click here to load Google feedback frame

Citing this Tutorial

  1. , 2022 Deploying Wireguard for private mesh networking (Galaxy Training Materials). https://training.galaxyproject.org/training-material/topics/admin/tutorials/wireguard/tutorial.html Online; accessed TODAY
  2. Batut et al., 2018 Community-Driven Data Analysis Training for Biology Cell Systems 10.1016/j.cels.2018.05.012

details BibTeX

@misc{admin-wireguard,
author = "Helena Rasche",
title = "Deploying Wireguard for private mesh networking (Galaxy Training Materials)",
year = "2022",
month = "09",
day = "20"
url = "\url{https://training.galaxyproject.org/training-material/topics/admin/tutorials/wireguard/tutorial.html}",
note = "[Online; accessed TODAY]"
}
@article{Batut_2018,
    doi = {10.1016/j.cels.2018.05.012},
    url = {https://doi.org/10.1016%2Fj.cels.2018.05.012},
    year = 2018,
    month = {jun},
    publisher = {Elsevier {BV}},
    volume = {6},
    number = {6},
    pages = {752--758.e1},
    author = {B{\'{e}}r{\'{e}}nice Batut and Saskia Hiltemann and Andrea Bagnacani and Dannon Baker and Vivek Bhardwaj and Clemens Blank and Anthony Bretaudeau and Loraine Brillet-Gu{\'{e}}guen and Martin {\v{C}}ech and John Chilton and Dave Clements and Olivia Doppelt-Azeroual and Anika Erxleben and Mallory Ann Freeberg and Simon Gladman and Youri Hoogstrate and Hans-Rudolf Hotz and Torsten Houwaart and Pratik Jagtap and Delphine Larivi{\`{e}}re and Gildas Le Corguill{\'{e}} and Thomas Manke and Fabien Mareuil and Fidel Ram{\'{\i}}rez and Devon Ryan and Florian Christoph Sigloch and Nicola Soranzo and Joachim Wolff and Pavankumar Videm and Markus Wolfien and Aisanjiang Wubuli and Dilmurat Yusuf and James Taylor and Rolf Backofen and Anton Nekrutenko and Björn Grüning},
    title = {Community-Driven Data Analysis Training for Biology},
    journal = {Cell Systems}
}
                

Congratulations on successfully completing this tutorial!