Added Wireguard role

This commit is contained in:
syrell 2023-03-01 22:52:00 +01:00
parent 1c873cea70
commit 0c76cd5b44
Signed by: syrell
GPG Key ID: BC9570E849334AF9
13 changed files with 468 additions and 0 deletions

128
roles/wireguard/README.rst Normal file
View File

@ -0,0 +1,128 @@
Wireguard setup
===============
Wireguard setup role. This role extends `this codebase <https://github.com/lablabs/ansible-collection-wireguard/tree/main/roles/wireguard>`_ to my needs. It's a bit simpler and adds more idempotence, e.g. when replaying the role to add another client to the server.
Requirements
------------
This role was written for Debian (11) and requires root privileges. It also requires to have several collections installed on your ansible host you won't necessarily have depending on your Ansible installation:
- ansible.posix
- community.general (iptables_save module)
- ansible.utils (network filters)
- netaddr (python package)
Role Variables
--------------
Variables can be found in the `default vars <defaults/main.yml>`_
.. code-block:: yaml
wireguard_dir: /etc/wireguard
wireguard_clients_dir: "{{ wireguard_dir }}/clients"
wireguard_clients_download_dir: clients/
wireguard_download_clients: false
wireguard_serverkeys_download_dir: server/
wireguard_download_serverkeys: false
Defines basic arborescence to store Wireguard files. ``wireguard_download_clients`` and ``wireguard_download_serverkeys`` can optionally set to true in order to download respectively clients and server's keys from the target host.
.. code-block:: yaml
wireguard_restore_serverkeys_dir: ""
Use this variable if you want to use pre-existing keys from a directory to bootstrap Wireguard. Must ends with '/'.
.. code-block:: yaml
wireguard_packages:
- wireguard
List of packages to install.
.. code-block:: yaml
wireguard_port: 51810
Port which Wireguard will listen to.
.. code-block:: yaml
wireguard_hostname: "{{ inventory_hostname }}"
Hostname the client will use to connect to the server.
.. code-block:: yaml
wireguard_interface: wg0
Interface which will be mounted to the server.
.. code-block:: yaml
nat_out_interface: eth0
Interface where the traffic will be NATed to on the server.
.. code-block:: yaml
wireguard_address: 10.213.213.0/24
Subnet definition for the VPN network.
.. code-block:: yaml
wireguard_keepalive: 25
Uses this if you wanna specify a keepalive value. See `this <https://github.com/pirate/wireguard-docs#persistentkeepalive>`_ for more information on keepalive.
.. code-block:: yaml
wireguard_peers: []
Lits of peers (clients) you wanna create. You can define specific name, address, allowedIPs, DNS and keepalive for each peer. See playbook below for example.
.. code-block:: yaml
filter_forward: false
other_interface:
Set ``filter_forward`` to true and specify an interface name for ``other_interface`` if you wanna drop packets from ``wireguard_interface`` to this interface.
Dependencies
------------
None.
Example Playbook
----------------
.. code-block:: yaml
- name: Deploy Wireguard
hosts: wireguard_hosts
become: true
vars:
wireguard_hostname: "mywireguard.server.com"
wireguard_address: 10.10.10.0/24
wireguard_peers:
- name: client_001
allowed_ip: "0.0.0.0/0, ::/0"
address: "10.10.10.2"
- name: client_002
allowed_ip: "0.0.0.0/0, ::/0"
address: "10.10.10.3"
roles:
- wireguard
License
-------
BSD-3
Author Information
------------------
Role created by `syrell <https://git.syyrell.com/syrell>`_

View File

@ -0,0 +1,51 @@
---
# defaults file for wireguard
# Directory to store WireGuard configuration on the remote hosts
wireguard_dir: /etc/wireguard
wireguard_clients_dir: "{{ wireguard_dir }}/clients"
# Download client configs
wireguard_clients_download_dir: clients/
wireguard_download_clients: false
# Download private, public and preshared keys
wireguard_serverkeys_download_dir: server/
wireguard_download_serverkeys: false
# Path to Wireguard keys
wireguard_privatekey_path: "{{ wireguard_dir }}/pk"
wireguard_publickey_path: "{{ wireguard_dir }}/pubk"
wireguard_presharedkey_path: "{{ wireguard_dir }}/psk"
# When defined, Ansible will restore wireguard keys (private key, public key, preshared key) from this directory.
# NOTE: The directory path must end with "/"
wireguard_restore_serverkeys_dir: ""
# List of packages to install
wireguard_packages:
- wireguard
# The default port WireGuard will listen if not specified otherwise.
wireguard_port: 51810
# Client destination Hostname
wireguard_hostname: "{{ inventory_hostname }}"
# The default interface name that wireguard should use if not specified otherwise.
wireguard_interface: wg0
# Interface to NAT traffic to
nat_out_interface: eth0
# Base wireguard subnet
wireguard_address: 10.213.213.0/24
# Defines a keepalive value for peers
wireguard_keepalive: 0
# List of peers
wireguard_peers: []
# Add additional forward rule to drop packets to other_interface
filter_forward: false
other_interface:

View File

@ -0,0 +1,52 @@
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.1
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
#
# Provide a list of supported platforms, and for each platform a list of versions.
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
# To view available platforms and versions (or releases), visit:
# https://galaxy.ansible.com/api/v1/platforms/
#
# platforms:
# - name: Fedora
# versions:
# - all
# - 25
# - name: SomePlatform
# versions:
# - all
# - 1.0
# - 7
# - 99.99
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View File

@ -0,0 +1,51 @@
- name: Create client configs directories
ansible.builtin.file:
path: "{{ wireguard_clients_dir }}/{{ item.FriendlyName }}"
mode: 0755
state: directory
register: existing_client_config
- name: Wireguard client keys block
block:
- name: Generate WireGuard client private and public keys
ansible.builtin.shell: |
set -o pipefail
umask 077 && wg genkey | tee pk | wg pubkey > pubk
args:
executable: /bin/bash
chdir: "{{ wireguard_clients_dir }}/{{ item.FriendlyName }}"
- name: Read publickey
ansible.builtin.slurp:
src: "{{ wireguard_clients_dir }}/{{ item.FriendlyName }}/pubk"
register: _client_pubkey_value
- name: Read privatekey
ansible.builtin.slurp:
src: "{{ wireguard_clients_dir }}/{{ item.FriendlyName }}/pk"
register: _privkey_value
- name: Create client config
ansible.builtin.template:
src: "clients.conf.j2"
dest: "{{ wireguard_clients_dir }}/{{ item.FriendlyName }}/{{ item.FriendlyName }}.conf"
mode: 0644
vars:
server_public_key: "{{ _pubkey_value['content'] | b64decode | trim }}"
preshared_key: "{{ _pskkey_value['content'] | b64decode | trim }}"
- name: Download client configs
ansible.builtin.fetch:
src: "{{ wireguard_clients_dir }}/{{ item.FriendlyName }}.conf"
dest: "{{ wireguard_clients_download_dir }}/{{ inventory_hostname }}/"
flat: true
when: wireguard_download_clients | bool
- name: Append peer to server config
ansible.builtin.blockinfile:
dest: "{{ wireguard_dir }}/{{ wireguard_interface }}.conf"
block: "{{ lookup('template', 'templates/peer.j2') }}"
marker: "### {mark} ANSIBLE MANAGED BLOCK FOR {{ item.FriendlyName }} ###"
when: existing_client_config.changed == true

View File

@ -0,0 +1,32 @@
- name: Install iptables-persistent
ansible.builtin.apt:
name:
- iptables
- iptables-persistent
state: present
- name: Filter FORWARD packets
ansible.builtin.iptables:
chain: FORWARD
jump: DROP
in_interface: "{{ wireguard_interface }}"
out_interface: "{{ other_interface }}"
when:
- filter_forward | bool
- other_interface | length > 0
- name: Setup ipv4 IP forward
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: true
reload: true
- name: Save current firewall state
community.general.iptables_state:
state: saved
path: /etc/iptables/rules.v4
when:
- filter_forward | bool
- other_interface | length > 0

View File

@ -0,0 +1,14 @@
- name: Create required dirs
ansible.builtin.file:
path: "{{ item }}"
mode: 0755
state: directory
loop:
- "{{ wireguard_dir }}"
- "{{ wireguard_clients_dir }}"
- name: Install WireGuard
ansible.builtin.apt:
name: "{{ wireguard_packages }}"
update_cache: true
state: present

View File

@ -0,0 +1,26 @@
---
# tasks file for wireguard
- name: Init tasks
import_tasks: init.yml
- name: Deploy server
import_tasks: server.yml
- name: Firewalling
import_tasks: firewall.yml
- name: Include Client configs
include_tasks: clients.yml
loop: "{{ peers | list }}"
- name: Cleanup secrets from memory
ansible.builtin.set_fact:
_pskkey_value: ""
_pubkey_value: ""
_privkey_value: ""
- name: Restart wg-quick
ansible.builtin.systemd:
name: wg-quick@wg0.service
enabled: yes
state: restarted

View File

@ -0,0 +1,75 @@
- name: Wireguard keys block
block:
- name: Test if private key is already present
ansible.builtin.stat:
path: "{{ wireguard_privatekey_path }}"
register: _priv_key
- name: Generate WireGuard server private and public keys
ansible.builtin.shell: |
set -o pipefail
umask 077 && wg genkey | tee {{ wireguard_privatekey_path }} | wg pubkey > {{ wireguard_publickey_path }}
args:
executable: /bin/bash
when:
- not _priv_key.stat.exists
- wireguard_restore_serverkeys_dir | length == 0
- name: Restore WireGuard private, public and preshared keys
ansible.builtin.copy:
src: "{{ wireguard_restore_serverkeys_dir }}"
dest: "{{ wireguard_dir }}"
mode: '0644'
when:
- not _priv_key.stat.exists
- wireguard_restore_serverkeys_dir | length > 0
- name: Read publickey
ansible.builtin.slurp:
src: "{{ wireguard_publickey_path }}"
register: _pubkey_value
- name: Read privatekey
ansible.builtin.slurp:
src: "{{ wireguard_privatekey_path }}"
register: _privkey_value
- name: Test if preshared key is already present
ansible.builtin.stat:
path: "{{ wireguard_presharedkey_path }}"
register: _psk_key
- name: Generate WireGuard preshared key
ansible.builtin.shell: |
set -o pipefail
umask 077 && wg genpsk | tee {{ wireguard_presharedkey_path }}
args:
executable: /bin/bash
when: not _psk_key.stat.exists
- name: Read presharedkey
ansible.builtin.slurp:
src: "{{ wireguard_presharedkey_path }}"
register: _pskkey_value
- name: Create server config
ansible.builtin.template:
src: server.conf.j2
dest: "{{ wireguard_dir }}/{{ wireguard_interface }}.conf"
mode: 0700
force: no
- name: Create peers variable from template
ansible.builtin.set_fact:
peers: "{{ lookup('template', 'templates/peers.j2') | from_yaml }}"
- name: Download server private key
ansible.builtin.fetch:
src: "{{ item }}"
dest: "{{ wireguard_serverkeys_download_dir }}/{{ inventory_hostname }}/"
flat: true
loop:
- "{{ wireguard_privatekey_path }}"
- "{{ wireguard_publickey_path }}"
- "{{ wireguard_presharedkey_path }}"
when: wireguard_download_serverkeys | bool

View File

@ -0,0 +1,13 @@
[Interface]
Address = {{ item.Address }}
ListenPort = {{ wireguard_port }}
PrivateKey = {{ _privkey_value['content'] | b64decode | trim }}
{% if item.DNS|length > 0 %}DNS = {{ item.DNS }}
{% endif %}
[Peer]
PublicKey = {{ server_public_key }}
PresharedKey = {{ preshared_key }}
AllowedIPs = {{ item.AllowedIPs }}
Endpoint = {{ wireguard_hostname }}:{{ wireguard_port }}
PersistentKeepalive = {{ item.PersistentKeepalive | default(wireguard_keepalive) }}

View File

@ -0,0 +1,5 @@
[peer]
# peer_{{ item.FriendlyName }}
PublicKey = {{ _client_pubkey_value['content'] | b64decode | trim }}
PresharedKey = {{ item.PresharedKey }}
AllowedIPs = {{ item.Address }}/32

View File

@ -0,0 +1,9 @@
{% for peer in wireguard_peers %}
- WireGuardPeer:
FriendlyName: {{ peer.name }}
Address: {{ peer.address }}
AllowedIPs: "{{ peer.allowed_ip }}{% if not '/' in peer.allowed_ip %}/32{% endif %}"
DNS: "{% if peer.dns is defined %}{{ peer.dns }}{% endif %}"
PresharedKey: "{{ _pskkey_value['content'] | b64decode | trim }}"
PersistentKeepalive: {{ peer.keepalive | default(wireguard_keepalive) }}
{% endfor %}

View File

@ -0,0 +1,6 @@
[Interface]
Address = {{ wireguard_server_ip }}
ListenPort = {{ wireguard_port }}
PrivateKey = {{ _privkey_value['content'] | b64decode | trim }}
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -s {{ wireguard_address }} -o {{ nat_out_interface }} -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -s {{ wireguard_address }} -o {{ nat_out_interface }} -j MASQUERADE

View File

@ -0,0 +1,6 @@
---
# vars file for wireguard
_wireguard_interface_addr: "{{ ansible_default_ipv4.address | default(ansible_all_ipv4_addresses[0]) }}/{{ ansible_default_ipv4.netmask }}"
wireguard_server_ip: "{{ wireguard_address | ansible.utils.ipaddr('network') | ansible.utils.ipmath(1) }}"
wireguard_subnetmask: "{{ wireguard_address | ansible.utils.ipaddr('prefix') }}"
wireguard_peers_allowed_ips: "{{ ([(_wireguard_interface_addr | ansible.utils.ipaddr('network/prefix'))] + (wireguard_additional_routes | default([]))) | join(\", \") }}"