Featured image of post Wireguard Ansible

Wireguard Ansible

Ansible playbooks for deploying Wireguard

GitHub

Update from November 25, 2022

I switched to classic wg-quick from this Ansible setup. The solution turned out to be technically much simpler, and it meets my functional needs.

Update from March 21, 2022

I removed automatic key generation due to excessive implementation complexity and the inability to configure servers individually; it could only be done collectively. Now it’s different. Configuration changes are incompatible with the old version; please check the examples in the repository, as I have added documentation there.

Introduction

Wireguard is an amazing, simple VPN that is already included in the kernel. It’s a very powerful and straightforward tool. It installs much more easily than OpenVPN.

Problem Statement

  • I live in Ukraine, and unfortunately, some internet resources are blocked, and access is sometimes necessary.
  • I want to be able to remotely connect from my phone or someone else’s computer to my home server.
  • I want traffic to be proxied on the PC only for domains on the list, not everything.

With these requests, I started working. For myself, all for myself. No one else needs it for now, although I posted it on GitHub. Posted is a strong word. No documentation, nothing! Yes! Just lazy to write… Oh well.

Solution Overview

I used Ansible, which can configure the network immediately. What it can do:

  1. Use a constant preshared key for all connections.
  2. Ability to specify your AllowedIPs.
  3. Ability to add peers with statically defined keys (since Ansible can’t be run on the phone).
  4. Automatic selection of free rt_table IDs and fwmarks.
  5. Ability to set up more than one network on the same machines with a single run!

What it can’t do:

  1. Separate runs on one or a subgroup of hosts. Only all at once.

Understanding Ansible Variables

all.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# CIDR networks
wireguard_networks:
  home: 192.168.99.0/24
  vpn: 10.0.0.0/24
# marks for routing - each network has its own
wireguard_fwmarks:
  home: 1000
  vpn: 2000
# preshared key for the network
wireguard_preshared_keys:
  home: longalphanum
  vpn: longalphanum
# keys for each server in each network where it exists
wireguard_keys:
  home:
    public_host:
      public: longalphanum
      private: longalphanum
    home_server:
      public: longalphanum
      private: longalphanum
    laptop:
      public: longalphanum
      private: longalphanum
  vpn:
    public_host:
      public: longalphanum
      private: longalphanum
    laptop:
      public: longalphanum
      private: longalphanum
    smartphone:
      # only public key for the phone, as the private is not needed here
      public: longalphanum
wireguard_addresses:
  # addresses for each server in each network
  home:
    public_host: 192.168.99.1
    laptop: 192.168.99.2
    home_server: 192.168.99.3
  vpn:
    public_host: 10.0.0.1
    laptop: 10.0.0.2
    smartphone: 10.0.0.3

We have a server with a public IP, and it will serve as the connection point for everyone. The topology is a star.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# these are variables for a specific host - the server
wireguard_enabled: yes
# wireguard-tools have already been installed, so we turn this off
wireguard_install_tools: no
# list of peers
wireguard_peers:
  # for the home network
  home:
  # the first peer is our laptop, where we run Ansible
  - name: laptop
  # the second has inventory_hostname home_server
  - name: home_server
    # and keep the network so that it can always be reached,
    # because the server is behind NAT
    keepalive_seconds: 25
  # the second network will only be used from the phone and PC
  vpn:
  - name: laptop
  # and here is the phone
  - name: smartphone
# now we'll choose the ports it should listen on
# for the others, it doesn't matter, but
# for the server, it's better to specify everything statically
# so nothing is regenerated if anything happens
# and all clients don't need to be reconfigured
# with new server credentials
wireguard_ports:
  home: 2000
  vpn: 3000
# enable masquerading so that the server can
# release peer traffic to the Internet as a gateway
wireguard_masquarade:
  vpn: auto

Now, let’s specify variables for the laptop we are working on.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# and again explicitly enable the role
wireguard_enabled: yes
wireguard_peers:
  vpn:
  # in the VPN network peers, only our public server
  - hostname: public_host
    # we have a domain pointing to the server so we don’t need to remember the address
    endpoint: vpn.example.com
    # routing all traffic through the VPN, and it's important to understand
    # that we are not sending all system traffic through Wireguard
    # we are telling Wireguard on the host to allow
    # everything that specifically comes to you on the output and does not match
    # in other networks by more narrow rules
    allowed_ips:
    - 0.0.0.0/0
    - ::0
    keepalive_seconds: 25
  home:
  # the contact point for the home network is the same
  - hostname: public_host
    endpoint: vpn.example.com
    keepalive_seconds: 25
    allowed_ips:
    # and in the paths, we specify only the subnet of the home network
    - "{{ wireguard_networks.home }}"
# and enable proxying specific domains through the VPN
wireguard_proxy_domains:
  vpn:
    list:
    - site.we.want.access
    - another.one

And the home server just needs to be accessible. It doesn’t need to be in the VPN.

1
2
3
4
5
6
7
8
9
# here I hope everything is already clear
wireguard_enabled: yes
wireguard_peers:
  home:
  - hostname: public_host
    endpoint: vpn.example.com
    allowed_ips:
    - "{{ wireguard_networks.home }}"
    keepalive_seconds: 25

Conclusion

From any peer, there is access to any within the network. Traffic finds its way through the public server. Everything works. I measured with iperf; out of 80 Mbps, Wireguard delivers ~60 - to me, that’s an excellent result!

Licensed under Apache License, Version 2.0
Last updated on Dec 10, 2024 14:01 +0200
All rights reserved
Built with Hugo
Theme Stack designed by Jimmy