From 894cb17f7ebd250456d99c65943bcb5fddbfe090 Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Mon, 13 Jul 2026 14:28:57 +0200 Subject: [PATCH 1/6] Do not regenerate client keys if already configured on server --- .../tasks/gather_current_config.yaml | 35 +++++++++++++++++++ .../roles/wireguard_server/tasks/main.yaml | 1 + .../templates/wg_config_template.yaml | 13 +++++++ 3 files changed, 49 insertions(+) create mode 100644 ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml create mode 100644 ansible/playbooks/roles/wireguard_server/templates/wg_config_template.yaml diff --git a/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml b/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml new file mode 100644 index 0000000..d30825f --- /dev/null +++ b/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml @@ -0,0 +1,35 @@ +--- +- name: Check if config already exists + stat: + path: "/etc/wireguard/{{ wg_interface }}.conf" + register: interface_configuration + +# - name: Fetching configuration file +# ansible.builtin.fetch: +# src: "/etc/wireguard/{{ wg_interface }}.conf" +# dest: "/tmp/{{ wg_interface }}.conf" + + + +- name: Parse configuration + ansible.utils.cli_parse: + command: "cat /etc/wireguard/{{ wg_interface }}.conf " + parser: + name: ansible.netcommon.native + template_path: templates/wg_config_template.yaml + set_fact: existing_wg_config + when: interface_configuration.stat.exists + +- name: Check registered peers + debug: + msg: "Found configs : {{ existing_wg_config }}" + + +- name: Updating client configs info + set_fact: + wg_peers: "{{ wg_peers | community.general.lists_mergeby([{'name':item.key,'publicKey':item.value.publicKey}] ,'name')}}" + loop: "{{ existing_wg_config | ansible.builtin.dict2items }}" + +- name: Log client configs info + debug: + msg: "Updated peers defintion : {{ wg_peers }}" \ No newline at end of file diff --git a/ansible/playbooks/roles/wireguard_server/tasks/main.yaml b/ansible/playbooks/roles/wireguard_server/tasks/main.yaml index 05c05c9..5de9e1a 100644 --- a/ansible/playbooks/roles/wireguard_server/tasks/main.yaml +++ b/ansible/playbooks/roles/wireguard_server/tasks/main.yaml @@ -1,5 +1,6 @@ --- - include_tasks: install_wireguard.yaml +- include_tasks: gather_current_config.yaml - include_tasks: generate_server_keys.yaml - include_tasks: generate_client_configs.yaml - include_tasks: configure_server.yaml diff --git a/ansible/playbooks/roles/wireguard_server/templates/wg_config_template.yaml b/ansible/playbooks/roles/wireguard_server/templates/wg_config_template.yaml new file mode 100644 index 0000000..84986d5 --- /dev/null +++ b/ansible/playbooks/roles/wireguard_server/templates/wg_config_template.yaml @@ -0,0 +1,13 @@ +--- +- example: "# Peer ID : fabio_test" + getval: '# Peer ID : (?P\S+)' + result: + "{{ peer_id }}": + name: "{{ peer_id }}" + shared: true + +- example: "PublicKey = 9a3tgXpF5CAOffbHZdW1QBEJgSLFnBDVbD1JaMPgzkM=" + getval: 'PublicKey = (?P\S+)' + result: + "{{ peer_id }}": + publicKey: "{{ publicKey }}" \ No newline at end of file From e9b251a18f4967f2c1a2dc3baff5d0d27b45e861 Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Mon, 13 Jul 2026 14:29:40 +0200 Subject: [PATCH 2/6] cleanup --- .../tasks/gather_current_config.yaml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml b/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml index d30825f..3a9b73c 100644 --- a/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml +++ b/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml @@ -4,13 +4,6 @@ path: "/etc/wireguard/{{ wg_interface }}.conf" register: interface_configuration -# - name: Fetching configuration file -# ansible.builtin.fetch: -# src: "/etc/wireguard/{{ wg_interface }}.conf" -# dest: "/tmp/{{ wg_interface }}.conf" - - - - name: Parse configuration ansible.utils.cli_parse: command: "cat /etc/wireguard/{{ wg_interface }}.conf " @@ -19,17 +12,8 @@ template_path: templates/wg_config_template.yaml set_fact: existing_wg_config when: interface_configuration.stat.exists - -- name: Check registered peers - debug: - msg: "Found configs : {{ existing_wg_config }}" - - name: Updating client configs info set_fact: wg_peers: "{{ wg_peers | community.general.lists_mergeby([{'name':item.key,'publicKey':item.value.publicKey}] ,'name')}}" loop: "{{ existing_wg_config | ansible.builtin.dict2items }}" - -- name: Log client configs info - debug: - msg: "Updated peers defintion : {{ wg_peers }}" \ No newline at end of file From 8a0ec15e5659b75361463e9f29207c15073645d1 Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Mon, 13 Jul 2026 14:52:27 +0200 Subject: [PATCH 3/6] Force key (re)generation if asked --- .../roles/wireguard_server/tasks/generate_client_configs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml b/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml index 12b896b..c75e2b6 100644 --- a/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml +++ b/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml @@ -14,7 +14,7 @@ - name : Gather needed key generation set_fact: generated_keys: "{{ generated_keys + [{'name': item.name}] }}" - when : item.publicKey is not defined + when : item.publicKey is not defined or (item.forceRegeneration is defined and item.forceRegeneration) loop: "{{ wg_peers }}" - name : Generate private keys From a6d9794abb6474ccfa85e56933ea1833cf8de811 Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Mon, 13 Jul 2026 15:20:31 +0200 Subject: [PATCH 4/6] Only generate configured peers and discard the rest --- .../group_vars/wireguard_server/vlab1.yaml | 3 +- .../tasks/gather_current_config.yaml | 1 + .../wireguard_server/tasks/main.yaml___back | 62 ------------------- .../templates/wireguard_server.jinja | 2 +- 4 files changed, 4 insertions(+), 64 deletions(-) delete mode 100644 ansible/playbooks/roles/wireguard_server/tasks/main.yaml___back diff --git a/ansible/inventories/group_vars/wireguard_server/vlab1.yaml b/ansible/inventories/group_vars/wireguard_server/vlab1.yaml index 49d6dd5..29c533d 100644 --- a/ansible/inventories/group_vars/wireguard_server/vlab1.yaml +++ b/ansible/inventories/group_vars/wireguard_server/vlab1.yaml @@ -18,7 +18,8 @@ wg_peers: allowedIPs : - "192.168.99.0/24" - "10.22.0.0/16" - - name: second_missing_key_test + - name: forced_missing_key_test + forceRegeneration: True ip: "192.168.99.6/32" allowedIPs : - "192.168.99.0/24" diff --git a/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml b/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml index 3a9b73c..f1b8def 100644 --- a/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml +++ b/ansible/playbooks/roles/wireguard_server/tasks/gather_current_config.yaml @@ -16,4 +16,5 @@ - name: Updating client configs info set_fact: wg_peers: "{{ wg_peers | community.general.lists_mergeby([{'name':item.key,'publicKey':item.value.publicKey}] ,'name')}}" + when: item.key in (wg_peers | map(attribute='name')) loop: "{{ existing_wg_config | ansible.builtin.dict2items }}" diff --git a/ansible/playbooks/roles/wireguard_server/tasks/main.yaml___back b/ansible/playbooks/roles/wireguard_server/tasks/main.yaml___back deleted file mode 100644 index a7d8955..0000000 --- a/ansible/playbooks/roles/wireguard_server/tasks/main.yaml___back +++ /dev/null @@ -1,62 +0,0 @@ ---- -- name: Install Wireguard Server - apt: - pkg: - - wireguard - state: latest - update_cache: true - - -- name: Create directory for wg keys - ansible.builtin.file: - path: /etc/wireguard/keys - state: directory - mode: '0755' - -- name: Creating server privatekey and publickey - shell: wg genkey | tee privatekey | wg pubkey > publickey - args: - chdir: /etc/wireguard/keys - -- name: Get Private Key [privatekey => var_privatekey] - shell: cat privatekey - register: var_privatekey - args: - chdir: /etc/wireguard/keys - - -#- name: Add WireGuard interface -# command: ip link add dev wg0 type wireguard - - -- name: Updating configuration - template: - src: wireguard_server.jinja - dest: /etc/wireguard/wg0.conf - -#- name: Activating link -# command: ip link set up dev wg0 - - -- name: Starting wg service - systemd: - state: started - name: wg-quick@wg0 - enabled: yes - - -- name: Getting public key - shell: cat publickey - register: var_publickey - args: - chdir: /etc/wireguard/keys - - -- name: Check server public IP - shell: curl https://ipinfo.io/ip - register: var_server_ip - - -- name: Printing public key - debug: - msg: "Server {{ ansible_hostname }} reachable @{{var_server_ip}}. Public key is {{ var_publickey }}" diff --git a/ansible/playbooks/roles/wireguard_server/templates/wireguard_server.jinja b/ansible/playbooks/roles/wireguard_server/templates/wireguard_server.jinja index cb8c9e3..2a8c72f 100644 --- a/ansible/playbooks/roles/wireguard_server/templates/wireguard_server.jinja +++ b/ansible/playbooks/roles/wireguard_server/templates/wireguard_server.jinja @@ -16,7 +16,7 @@ PostDown = iptables -t nat -D POSTROUTING ! -o {{wg_interface}} -m mark --mark 0 {% for peer in wg_peers %} -# {{ peer.name }} +# Peer ID : {{ peer.name }} [Peer] PublicKey = {{ peer.publicKey }} AllowedIPs = {{ peer.ip }} From d0efa5e9be165f137bcea2cbcff8e0133158acb0 Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Mon, 13 Jul 2026 16:42:08 +0200 Subject: [PATCH 5/6] delete previous client configurations from local folder --- .../tasks/generate_client_configs.yaml | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml b/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml index c75e2b6..cda5b78 100644 --- a/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml +++ b/ansible/playbooks/roles/wireguard_server/tasks/generate_client_configs.yaml @@ -1,12 +1,4 @@ --- -- name: Create local configs directory - become: false - local_action : ansible.builtin.file - args: - path: "{{ playbook_dir }}/wg_clients/{{ inventory_hostname }}" - state: directory - mode: '0700' - - name: Init generated Keys dict set_fact: generated_keys: [] @@ -42,6 +34,24 @@ wg_peers: "{{ wg_peers | community.general.lists_mergeby(generated_keys,'name')}}" +- name: Delete local configs directory + become: false + local_action : ansible.builtin.file + args: + path: "{{ playbook_dir }}/wg_clients/{{ inventory_hostname }}" + state: absent + + +- name: Re-create local configs directory + become: false + local_action : ansible.builtin.file + args: + path: "{{ playbook_dir }}/wg_clients/{{ inventory_hostname }}" + state: directory + mode: '0700' + + + - name : Generate client config file become: false local_action : ansible.builtin.template From 2a7017a8c80b079cf069aa9572c9b7635f5b160c Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Mon, 13 Jul 2026 16:42:28 +0200 Subject: [PATCH 6/6] Update doc : vpn server playbook --- ansible/readme.md | 76 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/ansible/readme.md b/ansible/readme.md index abb40fa..818aefc 100644 --- a/ansible/readme.md +++ b/ansible/readme.md @@ -37,11 +37,15 @@ Calls role nextcloud_aio, dependent on docker role. - Downloads SSE-Lab Repo - Runs compose up (using ansible plugins) -E.g. `ansible-playbook -i inventories/ -l nextrup_copy_test playbooks/nextcloud.yaml` +E.g. + +```ansible-playbook -i inventories/ -l nextrup_copy_test playbooks/nextcloud.yaml``` ### Bootstrap ### Creates sudoer user ansible, necessitates of sudoer user. -Use `ansible-playbook -i inventories playbooks/bootstrap.yml -l [TARGET_HOST] -e 'ansible_user=[REMOTE_USER]' -K` +Use + +```ansible-playbook -i inventories playbooks/bootstrap.yml -l [TARGET_HOST] -e 'ansible_user=[REMOTE_USER]' -K``` ### NameServer ### @@ -56,8 +60,72 @@ Configures a OPNSense edge node features : - Wireguard VPN NB runs locally so python intepreter needs to be specified -E.g. `ansible-playbook -i inventories/sifi.yaml playbooks/opnsense.yaml --extra-vars="ansible_python_interpreter=$(which python)" -` +E.g. + +```ansible-playbook -i inventories/sifi.yaml playbooks/opnsense.yaml --extra-vars="ansible_python_interpreter=$(which python)" +``` + +### VPN Server ### +Configures a VPN Server (only wireguard supported at the moment) for the specified 'wg_peers'. + +The server acts as a gateway into a site, tunneling each connection and allowing only traffic into allowed subnets. + + +Needed parameters and configuration example : + +``` + wg_interface: wg0 + wg_port: 51820 + wg_server_address: 192.168.99.1/32 + wg_peers: + - name: fabio_test + publicKey: "9a3tgXpF5CAOffbHZdW1QBEJgSLFnBDVbD1JaMPgzkM=" + ip: "192.168.99.4/32" + allowedIPs : + - "192.168.99.0/24" + - "10.22.0.0/16" + - name: missing_key_test + ip: "192.168.99.5/32" + allowedIPs : + - "192.168.99.0/24" + - "10.22.0.0/16" + - name: forced_missing_key_test + forceRegeneration: True + ip: "192.168.99.6/32" + allowedIPs : + - "192.168.99.0/24" + - "10.22.0.0/16" + +``` + +#### Wg-peers items #### + +Each item represents a client [**Peer**] with : + +- *name* [**Mandatory**] : a unique label for the client configuration +- *publicKey*: public key used by the client. If missing, the playbook generates both public and private keys, if needed. +- *ip* [**Mandatory**]: reserved ip for the client +- *allowedIPs* [**Mandatory**]: subnets to which the server allows traffic to for the present client +- *forceRegeneration*: force regeneration of public / private keys pair. + +#### Client configurations #### +The playbook generates client configurations at the following path + +> "{{ playbook_dir }}/wg_clients/{{ inventory_hostname }}/{{ item.name }}.conf" + +**NB** Client configurations are ready to be imported in wireguard client if the playbook generated both public and private keys. Otherwise only the client's public key is reported and the user is expected to fill the configuration properly with their private key. + + +#### Key pair generation policy #### + +In order to configure peers to connect to the server, public/private key pairs are needed. + +The playbook evalutes the need for key generation as following : +- If *forceRegeneration* is set to *True*, keys are generated +- If *publicKey* is declared, that key is used +- If a public Key is already configured on the server for that Peer's name, that key is used +- If no public key is declared nor found, a new private / public key pair is generated and reported in client's configuration + ## Inventories