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:
parent
9a0e70ee25
commit
a357e5fab1
|
@ -1,6 +1,13 @@
|
||||||
Changelog
|
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**
|
**3.2.2**
|
||||||
|
|
||||||
- remove unneeded `with_inventory_hostnames` loops (thanks to pierreozoux for initial PR)
|
- remove unneeded `with_inventory_hostnames` loops (thanks to pierreozoux for initial PR)
|
||||||
|
|
65
README.md
65
README.md
|
@ -27,28 +27,16 @@ see [CHANGELOG.md](https://github.com/githubixx/ansible-role-wireguard/blob/mast
|
||||||
Role Variables
|
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
|
# Directory to store WireGuard configuration on the remote hosts
|
||||||
wireguard_remote_directory: "/etc/wireguard"
|
wireguard_remote_directory: "/etc/wireguard"
|
||||||
|
|
||||||
# The port WireGuard will listen on.
|
# The default port WireGuard will listen if not specified otherwise.
|
||||||
wireguard_port: "51820"
|
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"
|
wireguard_interface: "wg0"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -252,6 +240,53 @@ Example Playbook
|
||||||
- wireguard
|
- 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
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -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
|
# Directory to store WireGuard configuration on the remote hosts
|
||||||
wireguard_remote_directory: "/etc/wireguard"
|
wireguard_remote_directory: "/etc/wireguard"
|
||||||
|
|
||||||
# The port WireGuard will listen on.
|
# The default port WireGuard will listen if not specified otherwise.
|
||||||
wireguard_port: "51820"
|
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"
|
wireguard_interface: "wg0"
|
||||||
|
|
137
tasks/main.yml
137
tasks/main.yml
|
@ -14,7 +14,6 @@
|
||||||
- wireguard-tools
|
- wireguard-tools
|
||||||
tags:
|
tags:
|
||||||
- wg-install
|
- wg-install
|
||||||
- skip_ansible_lint
|
|
||||||
|
|
||||||
- name: Enable WireGuard kernel module
|
- name: Enable WireGuard kernel module
|
||||||
modprobe:
|
modprobe:
|
||||||
|
@ -28,106 +27,59 @@
|
||||||
tags:
|
tags:
|
||||||
- wg-install
|
- 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)
|
- name: Set WireGuard IP (without mask)
|
||||||
set_fact:
|
set_fact:
|
||||||
wireguard_ip: "{{ wireguard_address.split('/')[0] }}"
|
wireguard_ip: "{{ wireguard_address.split('/')[0] }}"
|
||||||
|
|
||||||
- name: Set path to private key file
|
- name: Register if config/private key already exists on target host
|
||||||
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
|
|
||||||
stat:
|
stat:
|
||||||
path: "{{ private_key_file_path }}"
|
path: "{{ wireguard_remote_directory }}/{{ wireguard_interface }}.conf"
|
||||||
register: private_key_file_stat
|
register: config_file_stat
|
||||||
delegate_to: localhost
|
|
||||||
tags:
|
tags:
|
||||||
- wg-generate-keys
|
- wg-generate-keys
|
||||||
|
- wg-config
|
||||||
|
|
||||||
- name: Generate WireGuard private key
|
- block:
|
||||||
shell: "wg genkey"
|
- name: Generate WireGuard private key
|
||||||
register: wg_private_key_result
|
shell: "wg genkey"
|
||||||
when: not private_key_file_stat.stat.exists
|
register: wg_private_key_result
|
||||||
tags:
|
tags:
|
||||||
- wg-generate-keys
|
- wg-generate-keys
|
||||||
- skip_ansible_lint
|
|
||||||
|
|
||||||
- name: Set private key fact
|
- name: Set private key fact
|
||||||
set_fact:
|
set_fact:
|
||||||
wg_private_key: "{{ wg_private_key_result.stdout }}"
|
private_key: "{{ wg_private_key_result.stdout }}"
|
||||||
when: not private_key_file_stat.stat.exists
|
tags:
|
||||||
tags:
|
- wg-generate-keys
|
||||||
- wg-generate-keys
|
when: not config_file_stat.stat.exists
|
||||||
|
|
||||||
- name: Generate WireGuard public key
|
- block:
|
||||||
shell: "echo '{{ wg_private_key }}' | wg pubkey"
|
- 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
|
register: wg_public_key_result
|
||||||
when: not private_key_file_stat.stat.exists
|
changed_when: false
|
||||||
tags:
|
tags:
|
||||||
- wg-generate-keys
|
- wg-config
|
||||||
|
|
||||||
- name: Set public key fact
|
- name: Set public key fact
|
||||||
set_fact:
|
set_fact:
|
||||||
wg_public_key: "{{ wg_public_key_result.stdout }}"
|
public_key: "{{ wg_public_key_result.stdout }}"
|
||||||
when: not private_key_file_stat.stat.exists
|
|
||||||
tags:
|
tags:
|
||||||
- wg-generate-keys
|
- wg-config
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- name: Create WireGuard configuration directory
|
- name: Create WireGuard configuration directory
|
||||||
file:
|
file:
|
||||||
|
@ -149,6 +101,21 @@
|
||||||
notify:
|
notify:
|
||||||
- restart wireguard
|
- 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
|
- name: Start and enable WireGuard service
|
||||||
service:
|
service:
|
||||||
name: "wg-quick@{{ wireguard_interface }}"
|
name: "wg-quick@{{ wireguard_interface }}"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
{{hostvars[inventory_hostname]['wg_private_key']}}
|
|
|
@ -1 +0,0 @@
|
||||||
{{hostvars[inventory_hostname]['wg_public_key']}}
|
|
|
@ -15,9 +15,9 @@ PostDown = {{hostvars[inventory_hostname].wireguard_postdown}}
|
||||||
{% if hostvars[inventory_hostname].wireguard_save_config is defined %}
|
{% if hostvars[inventory_hostname].wireguard_save_config is defined %}
|
||||||
SaveConfig = true
|
SaveConfig = true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% for host in ansible_play_hosts %}
|
||||||
{% for host in groups["vpn"] %}
|
|
||||||
{% if host != inventory_hostname %}
|
{% if host != inventory_hostname %}
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = {{hostvars[host].public_key}}
|
PublicKey = {{hostvars[host].public_key}}
|
||||||
{% if hostvars[host].wireguard_allowed_ips is defined %}
|
{% if hostvars[host].wireguard_allowed_ips is defined %}
|
||||||
|
@ -28,11 +28,18 @@ SaveConfig = true
|
||||||
{% if hostvars[host].wireguard_persistent_keepalive is defined %}
|
{% if hostvars[host].wireguard_persistent_keepalive is defined %}
|
||||||
PersistentKeepalive = {{hostvars[host].wireguard_persistent_keepalive}}
|
PersistentKeepalive = {{hostvars[host].wireguard_persistent_keepalive}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if hostvars[host].wireguard_endpoint is not defined %}
|
{% if hostvars[host].wireguard_port is defined and hostvars[host].wireguard_port is number %}
|
||||||
Endpoint = {{host}}:{{wireguard_port}}
|
{% if hostvars[host].wireguard_endpoint is defined and hostvars[host].wireguard_endpoint != "" %}
|
||||||
{% elif 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}}
|
Endpoint = {{hostvars[host].wireguard_endpoint}}:{{wireguard_port}}
|
||||||
|
{% elif hostvars[host].wireguard_endpoint == "" %}
|
||||||
|
# No endpoint defined
|
||||||
|
{% else %}
|
||||||
|
Endpoint = {{host}}:{{wireguard_port}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
Reference in a new issue