Skip to content

Commit

Permalink
Generate configuration for non-Ansible peers and use local facts
Browse files Browse the repository at this point in the history
Closes: githubixx#70
Closes: githubixx#79
  • Loading branch information
ypid committed Oct 4, 2020
1 parent de412dd commit 2604c38
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 28 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ The role is currently heavily reworked for increased flexibility and DebOps supp

The reworked role should cover all valid use cases, for this, the code is complex but you can decide using a few inventory variables how you want to role to behave.

You will need to have `python3-future` installed on your remote hosts.

## Previous not up-to-date docs follow

This Ansible role is used in my blog series [Kubernetes the not so hard way with Ansible](https://www.tauceti.blog/post/kubernetes-the-not-so-hard-way-with-ansible-wireguard/) but can be used standalone of course. I use WireGuard and this Ansible role to setup a fully meshed VPN between all nodes of my little Kubernetes cluster. This VPN also includes two clients so that I can communicate securely with the Kubernetes API server. Also my Postfix mailserver running as K8s DaemonSet forwards mails to my internal Postfix through WireGuard VPN.
Expand Down
25 changes: 20 additions & 5 deletions defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
---
# Copyright (C) 2018-2020 Robert Wimmer
# Copyright (C) 2020 Robin Schneider <[email protected]>
# SPDX-License-Identifier: GPL-3.0-or-later

#######################################
# General settings
#######################################

# Directory to store WireGuard configuration on the remote hosts
wireguard_remote_directory: "{{ '/etc/wireguard' if not ansible_os_family == 'Darwin' else '/opt/local/etc/wireguard' }}"
wireguard_remote_directory: '{{ ("/opt/local/etc/wireguard"
if (ansible_os_family == "Darwin")
else "/etc/wireguard")
if (wireguard__config_target == "host")
else (wireguard__secret_directory + "/config") }}'


wireguard__keys_directory: '{{ wireguard_remote_directory + "/keys" }}'

Expand Down Expand Up @@ -44,20 +50,29 @@ wireguard_ubuntu_cache_valid_time: "3600"
# FIXME: Update docs
# Name of the WireGuard network in case it is different to the interface name.
# Might be the case if wg0 is already taken on some peers or if the network otherwise just has a more fitting name for the whole virtual network.
wireguard_inventory_group: "wireguard_wg0"
wireguard_inventory_group: 'wireguard_wg0'

# Either "host" or "ansible_controller".
wireguard__secret_authority: "ansible_controller"
wireguard__secret_authority: 'ansible_controller'

# Key templating mode. Either "inline" or "file".
wireguard__key_templating: "file"
wireguard__key_templating: '{{ "file" if (wireguard__config_target == "host") else "inline" }}'

# Either "host" or "ansible_controller".
wireguard__config_target: 'host'

# File path on the Ansible controller where files will be redirected to with wireguard__config_target == "ansible_controller".
# The idea behind this directory is to act as a faked / (root) directory for that "host".
wireguard__controller_host_dir_path: '{{ (inventory_dir + "../../../root-fs-by-host/" + inventory_hostname) | realpath }}'
wireguard__controller_host_owner: '{{ omit}}'
wireguard__controller_host_group: '{{ omit}}'
wireguard__controller_host_mode: '{{ omit}}'

# .. envvar:: wireguard__secret_directory [[[
#
# Secret directory to use on the Ansible controller for key management and
# generating configuration files for unmanaged peers.
wireguard__secret_directory: '{{ secret + "/wireguard/" + wireguard_inventory_group }}'
# + + "/" + ansible_fqdn

# ]]]
# Configuration for other Ansible roles [[[
Expand Down
2 changes: 1 addition & 1 deletion handlers/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
service:
name: "wg-quick@{{ wireguard_interface }}"
state: "reloaded"
when: (ansible_os_family != 'Darwin')
when: (wireguard__config_target == "host" and ansible_os_family != 'Darwin')
77 changes: 61 additions & 16 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
- import_role:
name: 'secret'

- name: Assert that inventory configuration is valid
assert:
that:
- '(wireguard__config_target == "ansible_controller" and wireguard__secret_authority == "ansible_controller") or wireguard__config_target != "ansible_controller"'
run_once: True
delegate_to: 'localhost'

# Installing and load WireGuard [[[1
- include_tasks: "{{ item }}"
with_first_found:
Expand All @@ -14,6 +21,7 @@
- "setup-{{ ansible_distribution|lower }}-{{ ansible_distribution_release }}.yml"
- "setup-{{ ansible_distribution|lower }}.yml"
- "setup-{{ ansible_os_family|lower }}.yml"
when: wireguard__config_target == "host"

- name: Install patched version of wg-quick
copy:
Expand All @@ -24,6 +32,7 @@
mode: "0755"
tags:
- wg-install
when: wireguard__config_target == "host"

- name: Enable WireGuard kernel module
modprobe:
Expand All @@ -34,14 +43,14 @@
retries: 10
delay: 10
failed_when: wireguard__register_module_enabled is failure
when: ansible_os_family == 'Darwin'
when: wireguard__config_target == "host" and ansible_os_family == 'Darwin'
tags:
- wg-install

# Prepare WireGuard configuration directory [[[1
- name: Create WireGuard configuration directory
file:
dest: "{{ wireguard_remote_directory }}"
dest: '{{ wireguard_remote_directory }}'
state: directory
mode: 0700
tags:
Expand Down Expand Up @@ -75,7 +84,7 @@
creates: '{{ wireguard__secret_directory + "/" + item + ".privkey" }}'
delegate_to: "localhost"
run_once: True
loop: '{{ ansible_play_hosts }}'
loop: '{{ groups[wireguard_inventory_group]|d([]) }}'
tags:
- wg-config

Expand All @@ -87,7 +96,7 @@

- name: Set private key fact from Ansible controller
set_fact:
wireguard__fact_private_key: "{{ wireguard__register_private_key['content'] | b64decode }}"
wireguard__fact_private_key: '{{ (wireguard__register_private_key["content"] | b64decode).strip() }}'
when: wireguard__secret_authority == "ansible_controller"

# Private key handling on remote [[[1
Expand Down Expand Up @@ -143,6 +152,7 @@
group: "{{ wireguard_conf_group }}"
mode: "{{ wireguard_conf_mode }}"
no_log: True
when: wireguard__key_templating == "file"
tags:
- wg-config
notify:
Expand All @@ -159,7 +169,7 @@

- name: Set public key fact
set_fact:
wireguard__fact_public_key: "{{ wireguard__register_public_key.stdout }}"
wireguard__fact_public_key: '{{ wireguard__register_public_key.stdout }}'
tags:
- wg-config

Expand Down Expand Up @@ -215,6 +225,7 @@
group: "{{ wireguard_conf_group }}"
mode: "{{ wireguard_conf_mode }}"
loop: '{{ wireguard__combos }}'
when: wireguard__config_target == "host"
tags:
- wg-config

Expand All @@ -226,15 +237,25 @@
owner: "{{ wireguard_conf_owner }}"
group: "{{ wireguard_conf_group }}"
mode: "{{ wireguard_conf_mode }}"
when: wireguard__config_target == "host"
tags:
- wg-config
notify:
- Reload WireGuard interface

- name: Generate WireGuard configuration file on Ansible controller
template:
src: 'etc/wireguard/wg.conf.j2'
dest: '{{ wireguard_remote_directory + "/" + inventory_hostname + "_" + wireguard_interface + ".conf" }}'
when: wireguard__config_target == "ansible_controller"
tags:
- wg-config

- name: Ensure legacy reload-module-on-update is absent
file:
dest: "{{ wireguard_remote_directory }}/.reload-module-on-update"
state: absent
when: wireguard__config_target == "host"
tags:
- wg-config

Expand All @@ -244,6 +265,7 @@
dest: "/etc/systemd/system/[email protected]"
state: directory
mode: 0755
when: wireguard__config_target == "host"

- name: Create systemd drop-in file for [email protected]
template:
Expand All @@ -252,33 +274,56 @@
owner: "root"
group: "root"
mode: "0644"
when: wireguard__config_target == "host"

- name: Start and enable WireGuard service
service:
daemon_reload: True
name: "wg-quick@{{ wireguard_interface }}"
state: started
enabled: True
when: not ansible_os_family == 'Darwin'
when: (wireguard__config_target == "host" and ansible_os_family != 'Darwin')

# Save WireGuard local facts [[[1
- name: Make sure that Ansible local facts directory exists
file:
path: '/etc/ansible/facts.d'
path: '{{ (wireguard__controller_host_dir_path
if (wireguard__config_target == "ansible_controller")
else "") + "/etc/ansible/facts.d" }}'
state: 'directory'
owner: 'root'
group: 'root'
mode: '0755'
owner: '{{ wireguard__controller_host_owner
if (wireguard__config_target == "ansible_controller")
else "root" }}'
group: '{{ wireguard__controller_host_group
if (wireguard__config_target == "ansible_controller")
else "root" }}'
mode: '{{ wireguard__controller_host_mode
if (wireguard__config_target == "ansible_controller")
else "0755" }}'

- name: Save WireGuard local facts
template:
src: 'etc/ansible/facts.d/wireguard.fact.j2'
dest: '/etc/ansible/facts.d/wireguard.fact'
owner: 'root'
group: 'root'
mode: '0755'
src: '{{ "etc/ansible/facts.d/" +
("wireguard_static.fact.j2"
if (wireguard__config_target == "ansible_controller")
else "wireguard.fact.j2") }}'
dest: '{{ (wireguard__controller_host_dir_path
if (wireguard__config_target == "ansible_controller")
else "") + "/etc/ansible/facts.d/wireguard.fact" }}'
owner: '{{ wireguard__controller_host_owner
if (wireguard__config_target == "ansible_controller")
else "root" }}'
group: '{{ wireguard__controller_host_group
if (wireguard__config_target == "ansible_controller")
else "root" }}'
mode: '{{ "0644"
if (wireguard__config_target == "ansible_controller")
else "0755" }}'
register: wireguard__register_facts

- name: Update Ansible facts if they were modified
action: setup
setup:
fact_path: '{{ (wireguard__controller_host_dir_path + "/etc/ansible/facts.d")
if (wireguard__config_target == "ansible_controller")
else omit }}'
when: wireguard__register_facts is changed
1 change: 1 addition & 0 deletions tasks/setup-debian-vanilla.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
apt:
name:
- "wireguard"
- "python3-future"
state: present
tags:
- wg-install
4 changes: 2 additions & 2 deletions templates/etc/ansible/facts.d/wireguard.fact.j2
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ for wg_conf in glob('/etc/wireguard/*.conf'):
stderr=subprocess.PIPE,
shell=False,
).decode('utf8')
config = ConfigParser()
config = ConfigParser(strict=False)
config.read_string(config_string)

pubkey = subprocess.check_output(
['wg', 'pubkey'],
input=config.get('Interface', 'PrivateKey').encode('utf8'),
stderr=subprocess.PIPE,
shell=False,
).decode('utf8')
).decode('utf8').strip()

output['interface'][interface].setdefault('Interface', {})
output['interface'][interface]['Interface']['PublicKey'] = pubkey
Expand Down
18 changes: 18 additions & 0 deletions templates/etc/ansible/facts.d/wireguard_static.fact.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{#
# Copyright (C) 2020 Robin Schneider <[email protected]>
# Copyright (C) 2020 DebOps <https://debops.org/>
# SPDX-License-Identifier: GPL-3.0-or-later
#}
{% set facts_interfaces = {} %}
{% for host in groups[wireguard_inventory_group]|d([]) %}
{% set _ = facts_interfaces.update({
wireguard_interface: {
'Interface': {
'PublicKey': hostvars[host].wireguard__fact_public_key,
}
}
}) %}
{% endfor %}
{
"interface": {{ facts_interfaces | to_json }}
}
10 changes: 6 additions & 4 deletions templates/etc/wireguard/wg.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ Address = {{ wireguard_address }}
PrivateKey = {{ wireguard__fact_private_key }}
{% else %}
PostUp = wg set %i private-key {{ wireguard__keys_directory }}/%i.privkey
{% for host in ansible_play_hosts %}
{% for host in groups[wireguard_inventory_group]|d([]) %}
{% if host != inventory_hostname %}
PostUp = wg set %i peer {{ hostvars[host].wireguard__fact_public_key }} preshared-key {{ wireguard__keys_directory }}/%i,{{ [inventory_hostname, host] | sort | join(",") }}.psk
PostUp = wg set %i peer {{ hostvars[host].wireguard__fact_public_key
|d(hostvars[host].ansible_local.wireguard.interface[hostvars[host].wireguard_interface].Interface.PublicKey) }} preshared-key {{ wireguard__keys_directory }}/%i,{{ [inventory_hostname, host] | sort | join(",") }}.psk
{% endif %}
{% endfor %}
{% endif %}
Expand Down Expand Up @@ -54,12 +55,13 @@ PostDown = {{ wg_postdown }}
{% if wireguard_save_config is defined %}
SaveConfig = true
{% endif %}
{% for host in ansible_play_hosts %}
{% for host in groups[wireguard_inventory_group]|d([]) %}
{% if host != inventory_hostname %}

[Peer]
# {{ host }}
PublicKey = {{ hostvars[host].wireguard__fact_public_key }}
PublicKey = {{ hostvars[host].wireguard__fact_public_key
|d(hostvars[host].ansible_local.wireguard.interface[hostvars[host].wireguard_interface].Interface.PublicKey) }}
{% if wireguard__key_templating == 'inline' and host in wireguard__fact_psks %}
PresharedKey = {{ wireguard__fact_psks[host] }}
{% endif %}
Expand Down

0 comments on commit 2604c38

Please sign in to comment.