3
0
Fork 0

Merge stateless idea with no local storage of public and private keys, support multiple interface per hosts using several groups (#29)

* merge stateless with no storage of local priv key

* Delete locally stored private key

* add reload module on update config file

* privatekey template is not used anymore

* remove all local keys priv and public

* use ansible_play_hosts instead of hardcoded vpn grp
should use the group in the play calling the role.
works fine when hosts bellong to several groups

* Clean tasks names

* add tag, and cleanup

* fix private key creation

* Support for mutliple wireguard vpn on same host
add inventory exemple in readme

* fix typo, add some comment on inventory

* add  wg-config tag to Check config:
allow  run  with -t - wg-config

* Update tasks/main.yml

Co-Authored-By: Robert Wimmer <2039811+githubixx@users.noreply.github.com>

* remove trailing whitespace

* Update templates/wg.conf.j2

Co-Authored-By: Robert Wimmer <2039811+githubixx@users.noreply.github.com>

* Update templates/wg.conf.j2

Co-Authored-By: Robert Wimmer <2039811+githubixx@users.noreply.github.com>

* changes after githubixx code review

* readd new line to separate peers in config
This commit is contained in:
fbourqui 2019-11-02 20:39:47 +01:00 committed by Robert Wimmer
parent 9a0e70ee25
commit a357e5fab1
7 changed files with 124 additions and 122 deletions

View File

@ -1,6 +1,13 @@
Changelog
---------
**4.0.0**
- While the changes introduced are backwards compatible in general if you stay with your current settings some variables are no longer needed. So this is partly a breaking change and therefore justifies a new major version.
- Support multiple Wireguard interfaces. See README for examples (contribution by fbourqui)
- Make role stateless: In the previous versions the private and public keys of the Wireguard hosts were stored locally in the directory defined with the `wireguard_cert_directory` variable. This is no longer the case. The variables `wireguard_cert_directory`, `wireguard_cert_owner` and `wireguard_cert_group` are no longer needed and were removed. If you used this role before this release it's safe to remove them from your settings. The directory that was defined with the `wireguard_cert_directory` variable will be kept. While not tested it may enable you to go back to an older version of this role and it should still work (contribution by fbourqui)
- Reminder: `wireguard_cert_directory` default was `~/wireguard/certs`. Public and Private keys where stored on the host running ansible playbook. As a security best practice private keys of all your WireGuard endpoints should not be kept locally.
**3.2.2**
- remove unneeded `with_inventory_hostnames` loops (thanks to pierreozoux for initial PR)

View File

@ -27,28 +27,16 @@ see [CHANGELOG.md](https://github.com/githubixx/ansible-role-wireguard/blob/mast
Role Variables
--------------
This variables can be changed in `group_vars/`:
Those variables can be changed in `group_vars/`:
```
# The LOCAL directory where the WireGuard certificates are stored after they
# were generated. By default this will expand to user's LOCAL ${HOME}
# (the user that run's "ansible-playbook" command) plus
# "/wireguard/certs". That means if the user's ${HOME} directory is e.g.
# "/home/da_user" then "wireguard_cert_directory" will have a value of
# "/home/da_user/wireguard/certs". If you change this make sure that
# the parent directory is writable by the user that runs "ansible-playbook"
# command.
wireguard_cert_directory: "{{ '~/wireguard/certs' | expanduser }}"
wireguard_cert_owner: "root"
wireguard_cert_group: "root"
# Directory to store WireGuard configuration on the remote hosts
wireguard_remote_directory: "/etc/wireguard"
# The port WireGuard will listen on.
# The default port WireGuard will listen if not specified otherwise.
wireguard_port: "51820"
# The interface name that wireguard should use.
# The default interface name that wireguard should use if not specified otherwise.
wireguard_interface: "wg0"
```
@ -252,6 +240,53 @@ Example Playbook
- wireguard
```
Example Inventory using 2 different WireGuard interfaces on host multi
----------------------------------------------------------------------
This is a complex example using yaml inventory format
```
vpn1:
hosts:
multi:
wireguard_address: 10.9.0.1/32
wireguard_allowed_ips: "10.9.0.1/32, 192.168.2.0/24"
wireguard_endpoint: multi.exemple.com
nated:
wireguard_address: 10.9.0.2/32
wireguard_allowed_ips: "10.9.0.2/32, 192.168.3.0/24"
wireguard_persistent_keepalive: 15
wireguard_endpoint: nated.exemple.com
wireguard_postup: "iptables -t nat -A POSTROUTING -o ens12 -j MASQUERADE"
wireguard_postdown: "iptables -t nat -D POSTROUTING -o ens12 -j MASQUERADE"
vpn2:
hosts:
multi-wg1: # use a different name, and define ansible_host, to avoid mixing of vars without needing to prefix vars with interface name
ansible_host: multi
wireguard_interface: wg1
wireguard_port: 51821 # when using several interface on one host, we must use different ports
wireguard_address: 10.9.1.1/32
wireguard_endpoint: multi.exemple.com
another:
wireguard_address: 10.9.1.2/32
wireguard_endpoint: another.exemple.com
```
Playbooks
---------
```
- hosts: vpn1
roles:
- wireguard
```
```
- hosts: vpn2
roles:
- wireguard
```
License
-------

View File

@ -1,21 +1,9 @@
---
# The LOCAL directory where the WireGuard certificates are stored after they
# were generated. By default this will expand to user's LOCAL ${HOME}
# (the user that run's "ansible-playbook" command) plus
# "/wireguard/certs". That means if the user's ${HOME} directory is e.g.
# "/home/da_user" then "wireguard_cert_directory" will have a value of
# "/home/da_user/wireguard/certs". If you change this make sure that
# the parent directory is writable by the user that runs "ansible-playbook"
# command.
wireguard_cert_directory: "{{ '~/wireguard/certs' | expanduser }}"
wireguard_cert_owner: "root"
wireguard_cert_group: "root"
# Directory to store WireGuard configuration on the remote hosts
wireguard_remote_directory: "/etc/wireguard"
# The port WireGuard will listen on.
# The default port WireGuard will listen if not specified otherwise.
wireguard_port: "51820"
# The interface name that wireguard should use.
# The default interface name that wireguard should use if not specified otherwise.
wireguard_interface: "wg0"

View File

@ -14,7 +14,6 @@
- wireguard-tools
tags:
- wg-install
- skip_ansible_lint
- name: Enable WireGuard kernel module
modprobe:
@ -28,106 +27,59 @@
tags:
- wg-install
- name: Create WireGuard certificates directory
file:
dest: "{{ wireguard_cert_directory }}"
state: directory
owner: "{{ wireguard_cert_owner }}"
group: "{{ wireguard_cert_group }}"
mode: 0700
run_once: true
delegate_to: localhost
tags:
wg-generate-keys
- name: Set WireGuard IP (without mask)
set_fact:
wireguard_ip: "{{ wireguard_address.split('/')[0] }}"
- name: Set path to private key file
set_fact:
private_key_file_path: "{{ wireguard_cert_directory }}/{{ inventory_hostname }}.private.key"
tags:
wg-generate-keys
- name: Set path to public key file
set_fact:
public_key_file_path: "{{ wireguard_cert_directory }}/{{ inventory_hostname }}.public.key"
tags:
wg-generate-keys
- name: Register if private key already exists
- name: Register if config/private key already exists on target host
stat:
path: "{{ private_key_file_path }}"
register: private_key_file_stat
delegate_to: localhost
path: "{{ wireguard_remote_directory }}/{{ wireguard_interface }}.conf"
register: config_file_stat
tags:
- wg-generate-keys
- wg-config
- name: Generate WireGuard private key
shell: "wg genkey"
register: wg_private_key_result
when: not private_key_file_stat.stat.exists
tags:
- wg-generate-keys
- skip_ansible_lint
- block:
- name: Generate WireGuard private key
shell: "wg genkey"
register: wg_private_key_result
tags:
- wg-generate-keys
- name: Set private key fact
set_fact:
wg_private_key: "{{ wg_private_key_result.stdout }}"
when: not private_key_file_stat.stat.exists
tags:
- wg-generate-keys
- name: Set private key fact
set_fact:
private_key: "{{ wg_private_key_result.stdout }}"
tags:
- wg-generate-keys
when: not config_file_stat.stat.exists
- name: Generate WireGuard public key
shell: "echo '{{ wg_private_key }}' | wg pubkey"
- block:
- name: Read WireGuard config file
slurp:
src: "{{ wireguard_remote_directory }}/{{ wireguard_interface }}.conf"
register: wg_config
tags:
- wg-config
- name: Set private key fact
set_fact:
private_key: "{{ wg_config['content'] | b64decode | regex_findall('PrivateKey = (.*)') | first }}"
tags:
- wg-config
when: config_file_stat.stat.exists
- name: Derive WireGuard public key
shell: "echo '{{ private_key }}' | wg pubkey" # noqa 306
register: wg_public_key_result
when: not private_key_file_stat.stat.exists
changed_when: false
tags:
- wg-generate-keys
- wg-config
- name: Set public key fact
set_fact:
wg_public_key: "{{ wg_public_key_result.stdout }}"
when: not private_key_file_stat.stat.exists
public_key: "{{ wg_public_key_result.stdout }}"
tags:
- wg-generate-keys
- name: Store hosts private key locally
template:
src: "wg-privatekey.j2"
dest: "{{ private_key_file_path }}"
owner: "{{ wireguard_cert_owner }}"
group: "{{ wireguard_cert_group }}"
mode: 0644
when: not private_key_file_stat.stat.exists
delegate_to: localhost
tags:
- wg-generate-keys
- name: Store hosts public key locally
template:
src: "wg-publickey.j2"
dest: "{{ public_key_file_path }}"
owner: "{{ wireguard_cert_owner }}"
group: "{{ wireguard_cert_group }}"
mode: 0644
when: not private_key_file_stat.stat.exists
delegate_to: localhost
tags:
- wg-generate-keys
- name: Read private key
set_fact:
private_key: "{{ lookup('file', private_key_file_path) }}"
tags:
wg-config
- name: Read public key
set_fact:
public_key: "{{ lookup('file', public_key_file_path) }}"
tags:
wg-config
- wg-config
- name: Create WireGuard configuration directory
file:
@ -149,6 +101,21 @@
notify:
- restart wireguard
- name: Check if reload-module-on-update is set
stat:
path: "{{ wireguard_remote_directory }}/.reload-module-on-update"
register: reload_module_on_update
tags:
- wg-config
- name: Set WireGuard reload-module-on-update
file:
dest: "{{ wireguard_remote_directory }}/.reload-module-on-update"
state: touch
when: not reload_module_on_update.stat.exists
tags:
- wg-config
- name: Start and enable WireGuard service
service:
name: "wg-quick@{{ wireguard_interface }}"

View File

@ -1 +0,0 @@
{{hostvars[inventory_hostname]['wg_private_key']}}

View File

@ -1 +0,0 @@
{{hostvars[inventory_hostname]['wg_public_key']}}

View File

@ -15,9 +15,9 @@ PostDown = {{hostvars[inventory_hostname].wireguard_postdown}}
{% if hostvars[inventory_hostname].wireguard_save_config is defined %}
SaveConfig = true
{% endif %}
{% for host in groups["vpn"] %}
{% for host in ansible_play_hosts %}
{% if host != inventory_hostname %}
[Peer]
PublicKey = {{hostvars[host].public_key}}
{% if hostvars[host].wireguard_allowed_ips is defined %}
@ -28,11 +28,18 @@ SaveConfig = true
{% if hostvars[host].wireguard_persistent_keepalive is defined %}
PersistentKeepalive = {{hostvars[host].wireguard_persistent_keepalive}}
{% endif %}
{% if hostvars[host].wireguard_endpoint is not defined %}
Endpoint = {{host}}:{{wireguard_port}}
{% elif hostvars[host].wireguard_endpoint != "" %}
{% if hostvars[host].wireguard_port is defined and hostvars[host].wireguard_port is number %}
{% if hostvars[host].wireguard_endpoint is defined and hostvars[host].wireguard_endpoint != "" %}
Endpoint = {{hostvars[host].wireguard_endpoint}}:{{hostvars[host].wireguard_port}}
{% else %}
Endpoint = {{host}}:{{hostvars[host].wireguard_port}}
{% endif %}
{% elif hostvars[host].wireguard_endpoint is defined and hostvars[host].wireguard_endpoint != "" %}
Endpoint = {{hostvars[host].wireguard_endpoint}}:{{wireguard_port}}
{% elif hostvars[host].wireguard_endpoint == "" %}
# No endpoint defined
{% else %}
Endpoint = {{host}}:{{wireguard_port}}
{% endif %}
{% endif %}
{% endfor %}