From 59e7676a7d5a1ad993cbc297692cc1718ecd25c9 Mon Sep 17 00:00:00 2001
From: Robin Sonnabend <robin@fsmpi.rwth-aachen.de>
Date: Tue, 1 Sep 2020 17:42:39 +0200
Subject: [PATCH] Add role for wireguard

Setup wireguard interfaces, configure and deploy keys.
Private keys are stored in a file on the host.
Public keys are stored in facts, enable fact-caching to allow running
the role on a subset of the peers.

The dictionary key (interface name) must be the same on all hosts (it
identifies the network). At least one peer needs to have a static port
specified, so other peers can connect to it.

Peers are specified using an inventory hostpattern.

This doesn't setup any packet forwarding, DNS server or other VPN
features. It just enabled p2p connections (to potentially multiple
peers), e.g. to allow serving usually unencrypted applications (like
NFS) over an encrypted connection.
---
 wireguard/defaults/main.yml           |  8 +++++
 wireguard/handlers/main.yml           |  7 +++++
 wireguard/tasks/install-Archlinux.yml |  6 ++++
 wireguard/tasks/install-Debian.yml    |  6 ++++
 wireguard/tasks/main.yml              | 43 +++++++++++++++++++++++++++
 wireguard/templates/wireguard.conf.j2 | 15 ++++++++++
 6 files changed, 85 insertions(+)
 create mode 100644 wireguard/defaults/main.yml
 create mode 100644 wireguard/handlers/main.yml
 create mode 100644 wireguard/tasks/install-Archlinux.yml
 create mode 100644 wireguard/tasks/install-Debian.yml
 create mode 100644 wireguard/tasks/main.yml
 create mode 100644 wireguard/templates/wireguard.conf.j2

diff --git a/wireguard/defaults/main.yml b/wireguard/defaults/main.yml
new file mode 100644
index 0000000..922d991
--- /dev/null
+++ b/wireguard/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+
+wireguard_interfaces: {}
+
+# wg0:
+#   addresses: ["10.0.0.1", "fd00::1"]
+#   peers: hostname pattern (as in inventory)
+#   port: 40000 (optional, set to make reachable by peers)
diff --git a/wireguard/handlers/main.yml b/wireguard/handlers/main.yml
new file mode 100644
index 0000000..71b65f1
--- /dev/null
+++ b/wireguard/handlers/main.yml
@@ -0,0 +1,7 @@
+---
+
+- name: restart wireguard
+  systemd:
+    name: "wg-quick@{{item.key}}.service"
+    state: restarted
+  with_dict: "{{wireguard_interfaces}}"
diff --git a/wireguard/tasks/install-Archlinux.yml b/wireguard/tasks/install-Archlinux.yml
new file mode 100644
index 0000000..61b77c5
--- /dev/null
+++ b/wireguard/tasks/install-Archlinux.yml
@@ -0,0 +1,6 @@
+---
+
+- name: install wireguard on Arch
+  pacman:
+    name: wireguard-tools
+    state: present
diff --git a/wireguard/tasks/install-Debian.yml b/wireguard/tasks/install-Debian.yml
new file mode 100644
index 0000000..96acad3
--- /dev/null
+++ b/wireguard/tasks/install-Debian.yml
@@ -0,0 +1,6 @@
+---
+
+- name: install wireguard on Debian
+  apt:
+    name: wireguard
+    state: present
diff --git a/wireguard/tasks/main.yml b/wireguard/tasks/main.yml
new file mode 100644
index 0000000..e57bfdf
--- /dev/null
+++ b/wireguard/tasks/main.yml
@@ -0,0 +1,43 @@
+---
+
+- name: ensure wireguard is installed
+  include_tasks: "install-{{ansible_facts['os_family']}}.yml"
+
+- name: ensure we have a private key
+  shell:
+    cmd: "wg genkey | tee {{item.key}}.key | wg pubkey > {{item.key}}.pub"
+    chdir: /etc/wireguard
+    creates: "/etc/wireguard/{{item.key}}.key"
+  with_dict: "{{wireguard_interfaces}}"
+  notify:
+    - restart wireguard
+  no_log: true
+
+- name: get the pubkey
+  slurp:
+    src: "/etc/wireguard/{{item.key}}.pub"
+  register: pubkeys
+  with_dict: "{{wireguard_interfaces}}"
+
+- name: store the pubkey in facts
+  set_fact:
+    wireguard_pubkeys: "{{dict(pubkeys.results|map(attribute='item')|map(attribute='key') | zip(pubkeys.results|map(attribute='content')|map('b64decode')|map('trim')))}}"
+    cacheable: true
+
+- name: configure wireguard
+  template:
+    src: wireguard.conf.j2
+    dest: /etc/wireguard/{{item.key}}.conf
+    owner: root
+    group: root
+    mode: 0600
+  with_dict: "{{wireguard_interfaces}}"
+  notify:
+    - restart wireguard
+
+- name: enable interface
+  systemd:
+    name: "wg-quick@{{item.key}}.service"
+    state: started
+    enabled: true
+  with_dict: "{{wireguard_interfaces}}"
diff --git a/wireguard/templates/wireguard.conf.j2 b/wireguard/templates/wireguard.conf.j2
new file mode 100644
index 0000000..94ff143
--- /dev/null
+++ b/wireguard/templates/wireguard.conf.j2
@@ -0,0 +1,15 @@
+[Interface]
+Address = {{ item.value.addresses|join(", ") }}
+PostUp = wg set %i private-key /etc/wireguard/{{item.key}}.key
+{% if item.value.port is defined %}
+ListenPort = {{item.value.port}}
+{% endif %}
+
+{% for peer in lookup('inventory_hostnames', item.value.peers, wantlist=True) %}
+[Peer]
+PublicKey = {{hostvars[peer]['ansible_facts']['wireguard_pubkeys'][item.key]}}
+AllowedIPs = {{hostvars[peer]['wireguard_interfaces'][item.key]['addresses'] | join(', ')}}
+{% if hostvars[peer]['wireguard_interfaces'][item.key]['port'] is defined %}
+Endpoint = {{hostvars[peer]['ansible_facts']['fqdn']}}:{{hostvars[peer]['wireguard_interfaces'][item.key]['port']}}
+{% endif %}
+{% endfor %}
-- 
GitLab