From acf1e32eca7e9bcb0b7e97185d528a2294c8d9e5 Mon Sep 17 00:00:00 2001 From: transcaffeine Date: Fri, 9 Jan 2026 23:22:46 +0100 Subject: [PATCH] feat(wg_quick): add ansible role and playbook --- playbooks/wg_quick.yml | 7 +++++ roles/wg_quick/README.md | 5 ++++ roles/wg_quick/defaults/main/connection.yml | 20 ++++++++++++++ roles/wg_quick/defaults/main/main.yml | 7 +++++ roles/wg_quick/tasks/configure-interface.yml | 25 +++++++++++++++++ roles/wg_quick/tasks/main.yml | 27 ++++++++++++++++++ roles/wg_quick/templates/wg-quick.conf.j2 | 29 ++++++++++++++++++++ roles/wg_quick/vars/main.yml | 4 +++ 8 files changed, 124 insertions(+) create mode 100644 playbooks/wg_quick.yml create mode 100644 roles/wg_quick/README.md create mode 100644 roles/wg_quick/defaults/main/connection.yml create mode 100644 roles/wg_quick/defaults/main/main.yml create mode 100644 roles/wg_quick/tasks/configure-interface.yml create mode 100644 roles/wg_quick/tasks/main.yml create mode 100644 roles/wg_quick/templates/wg-quick.conf.j2 create mode 100644 roles/wg_quick/vars/main.yml diff --git a/playbooks/wg_quick.yml b/playbooks/wg_quick.yml new file mode 100644 index 0000000..813f991 --- /dev/null +++ b/playbooks/wg_quick.yml @@ -0,0 +1,7 @@ +--- +- name: Configure wireguard interfaces with wg_quick + hosts: "{{ wg_quick_hosts | default('wg_quick') }}" + become: "{{ wg_quick_become | default(false) }}" + gather_facts: "{{ wg_quick_gather_facts | default(false) }}" + roles: + - role: finallycoffee.base.wg_quick diff --git a/roles/wg_quick/README.md b/roles/wg_quick/README.md new file mode 100644 index 0000000..2d38133 --- /dev/null +++ b/roles/wg_quick/README.md @@ -0,0 +1,5 @@ +# `finallycoffee.base.wg_quick` ansible role + +Configure a wireguard interface using `wg_quick`. This role writes +the configuration files and activates the interface using the systemd +template service abstractions. diff --git a/roles/wg_quick/defaults/main/connection.yml b/roles/wg_quick/defaults/main/connection.yml new file mode 100644 index 0000000..0c0fcc1 --- /dev/null +++ b/roles/wg_quick/defaults/main/connection.yml @@ -0,0 +1,20 @@ +--- +wg_quick_interface_name: ~ +wg_quick_interface_address: ~ +wg_quick_interface_listen_port: ~ +wg_quick_interface_private_key: ~ +wg_quick_interface_private_key_file: ~ +wg_quick_interface_peer_endpoint: ~ +wg_quick_interface_peer_public_key: ~ +wg_quick_interface_peer_allowed_ips: ~ + +wg_quick_interfaces: + - name: "{{ wg_quck_interface_name }}" + address: "{{ wg_quick_interface_address }}" + listen_port: "{{ wg_quick_interface_listen_port }}" + private_key: "{{ wg_quick_interface_private_key }}" + private_key_file: "{{ wg_quick_interface_private_key_file }}" + peers: + - endpoint: "{{ wg_quick_interface_peer_endpoint }}" + public_key: "{{ wg_quick_interface_peer_public_key }}" + allowed_ips: "{{ wg_quick_interface_peer_allowed_ips }}" diff --git a/roles/wg_quick/defaults/main/main.yml b/roles/wg_quick/defaults/main/main.yml new file mode 100644 index 0000000..111d659 --- /dev/null +++ b/roles/wg_quick/defaults/main/main.yml @@ -0,0 +1,7 @@ +--- +wg_quick_state: "present" +wg_quick_package_name: "wireguard-tools" +wg_quick_system_packages: + - "{{ wg_quick_package_name }}" + +wg_quick_configuration_dir: "/etc/wireguard" diff --git a/roles/wg_quick/tasks/configure-interface.yml b/roles/wg_quick/tasks/configure-interface.yml new file mode 100644 index 0000000..1154cbe --- /dev/null +++ b/roles/wg_quick/tasks/configure-interface.yml @@ -0,0 +1,25 @@ +--- +- name: Ensure wg-quick configuration for interface '{{ wg_quick_iface.name }}' is up to date + ansible.builtin.template: + src: "wg-quick.conf.j2" + dest: "{{ wg_quick_configuration_dir }}/{{ wg_quick_iface.name }}.conf" + when: wg_quick_iface.state | default(wg_quick_state) == 'present' + +- name: Ensure systemd service is enabled + ansible.builtin.systemd_service: + name: "wg-quick@{{ wg_quick_iface.name }}.service" + enabled: true + when: wg_quick_iface.state | default(wg_quick_state) == 'present' + +- name: Ensure systemd service is {{ wg_quick_iface.state | default(wg_quick_state) }} + ansible.builtin.systemd_service: + name: "wg-quick@{{ wg_quick_iface.name }}.service" + state: >-2 + {{ (wg_quick_iface.state | default(wg_quick_state) == 'present') + | ternary('started', 'absent') }} + +- name: Ensure wg-quick configuration for interface '{{ wg_quick_iface.name }}' is absent + ansible.builtin.file: + path: "{{ wg_quick_configuration_dir }}/{{ wg_quick_face.name }}.conf" + state: "absent" + when: wg_quick_iface.state | default(wg_quick_state) == 'absent' diff --git a/roles/wg_quick/tasks/main.yml b/roles/wg_quick/tasks/main.yml new file mode 100644 index 0000000..e0cd88a --- /dev/null +++ b/roles/wg_quick/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- name: Ensure wg_quick_state is valid + ansible.builtin.fail: + msg: >-2 + Invalid state '{{ wg_quick_state }}'. Valid + states are {{ wg_quick_states | join(', ') }}. + when: wg_quick_state not in wg_quick_states + +- name: Ensure system packages are available + ansible.builtin.package: + name: "{{ wg_quick_system_packages }}" + state: "present" + when: wg_quick_state == 'present' + +- name: Ensure configuration folder is present + ansible.builtin.file: + name: "{{ wg_quick_configuration_dir }}" + state: "directory" + when: wg_quick_state == 'present' + +- name: Ensure connections are in the configured state + ansible.builtin.include_tasks: + file: "configure-interface.yml" + loop: "{{ wg_quick_interfaces }}" + loop_control: + loop_var: "wg_quick_iface" + label: "{{ wg_quick_iface.name }}" diff --git a/roles/wg_quick/templates/wg-quick.conf.j2 b/roles/wg_quick/templates/wg-quick.conf.j2 new file mode 100644 index 0000000..ef3dd85 --- /dev/null +++ b/roles/wg_quick/templates/wg-quick.conf.j2 @@ -0,0 +1,29 @@ +[Interface] +Address = {{ wg_quick_iface.address | join(', ') }} +ListenPort = {{ wg_quick_iface.listen_port }} + +{% if wg_quick_iface.private_key %} +PrivateKey = {{ wg_quick_iface.private_key }} +{% elif wg_quick_iface.private_key_file %} +PrivateKeyFile = {{ wg_quick_iface.private_key_file }} +{% endif %} +{% if wg_quick_iface.table is defined %} +Table = {{ wg_quick_iface.table | ternary('On', 'Off') }} +{% endif %} +{% if wg_quick_iface.post_up %} +PostUp = /bin/bash -c "{{ wg_quick_iface.post_up | join('; ') }}" +{% endif %} +{% if wg_quick_iface.pre_down %} +PreDown = /bin/bash -c "{{ wg_quick_iface.pre_down | join('; ') }}" +{% endif %} + + +{% for _peer in wg_quick_iface.peers %} +[Peer] +Endpoint = {{ _peer.endpoint }} +PublicKey = {{ _peer.public_key }} +AllowedIPs = {{ _peer.allowed_ips | join(', ') }} +{% if _peer.persistent_keepalive %} +PersistentKeepalive = {{ _peer.persistent_keepalive }} +{% endif %} +{% endfor %} diff --git a/roles/wg_quick/vars/main.yml b/roles/wg_quick/vars/main.yml new file mode 100644 index 0000000..4e18f5d --- /dev/null +++ b/roles/wg_quick/vars/main.yml @@ -0,0 +1,4 @@ +--- +wg_quick_states: + - "present" + - "absent"