--- - name: Regenerate WireGuard Client - Fresh Config hosts: production become: yes gather_facts: yes vars: wireguard_interface: "wg0" wireguard_config_path: "/etc/wireguard" wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf" wireguard_client_configs_path: "/etc/wireguard/clients" wireguard_local_client_configs_dir: "{{ playbook_dir }}/../wireguard-clients" tasks: - name: Validate client name fail: msg: "client_name is required. Usage: ansible-playbook ... -e 'client_name=myclient'" when: client_name is not defined or client_name == "" - name: Check if old client config exists stat: path: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf" register: old_client_config failed_when: false - name: Backup old client config copy: src: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf" dest: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf.backup-{{ ansible_date_time.epoch }}" remote_src: yes when: old_client_config.stat.exists register: backup_result failed_when: false - name: Display backup info debug: msg: "Alte Config wurde gesichert als: {{ backup_result.dest | default('N/A') }}" when: old_client_config.stat.exists - name: Remove old client from WireGuard server config shell: | # Entferne den [Peer] Block f?r den Client aus wg0.conf sed -i '/# BEGIN ANSIBLE MANAGED BLOCK - Client: {{ client_name }}/,/^# END ANSIBLE MANAGED BLOCK - Client: {{ client_name }}/d' {{ wireguard_config_file }} # Fallback: Entferne auch ohne Marker sed -i '/# Client: {{ client_name }}/,/{/d' {{ wireguard_config_file }} sed -i '/PublicKey = .*/d' {{ wireguard_config_file }} || true sed -i '/AllowedIPs = .*\/32$/d' {{ wireguard_config_file }} || true args: executable: /bin/bash register: remove_result failed_when: false changed_when: false - name: Set WireGuard network set_fact: wireguard_network: "{{ wireguard_network | default('10.8.0.0/24') }}" - name: Set WireGuard other variables with defaults set_fact: wireguard_port: "{{ wireguard_port | default(51820) }}" client_ip: "{{ client_ip | default('') }}" allowed_ips: "{{ allowed_ips | default(wireguard_network) }}" - name: Get server external IP address uri: url: https://api.ipify.org return_content: yes register: server_external_ip changed_when: false failed_when: false - name: Set server external IP set_fact: server_external_ip_content: "{{ ansible_host | default(server_external_ip.content | default('')) }}" - name: Read WireGuard server config slurp: src: "{{ wireguard_config_file }}" register: wireguard_server_config_read - name: Extract server IP from config set_fact: server_vpn_ip: "{{ (wireguard_server_config_read.content | b64decode | regex_search('Address = ([0-9.]+)')) | default(['10.8.0.1']) | first }}" failed_when: false - name: Set default DNS servers set_fact: wireguard_dns_servers: "{{ [server_vpn_ip] }}" - name: Extract WireGuard server IP octets set_fact: wireguard_server_ip_octets: "{{ server_vpn_ip.split('.') }}" when: client_ip == "" - name: Gather existing client addresses set_fact: existing_client_ips: "{{ (wireguard_server_config_read.content | b64decode | regex_findall('AllowedIPs = ([0-9A-Za-z.]+)/32', '\\\\1')) }}" when: client_ip == "" - name: Calculate client IP if not provided vars: existing_last_octets: "{{ (existing_client_ips | default([])) | map('regex_replace', '^(?:\\\\d+\\\\.\\\\d+\\\\.\\\\d+\\\\.)', '') | select('match', '^[0-9]+$') | map('int') | list }}" server_last_octet: "{{ wireguard_server_ip_octets[3] | int }}" next_octet_candidate: "{{ (existing_last_octets + [server_last_octet]) | map('int') | list | max + 1 if (existing_client_ips | default([]) | length > 0) else server_last_octet + 1 }}" set_fact: client_ip: "{{ [ wireguard_server_ip_octets[0], wireguard_server_ip_octets[1], wireguard_server_ip_octets[2], next_octet_candidate ] | join('.') }}" when: client_ip == "" - name: Generate NEW client private key command: "wg genkey" register: client_private_key changed_when: true no_log: yes - name: Generate NEW client public key command: "wg pubkey" args: stdin: "{{ client_private_key.stdout }}" register: client_public_key changed_when: false no_log: yes - name: Add NEW client to WireGuard server config blockinfile: path: "{{ wireguard_config_file }}" block: | # Client: {{ client_name }} [Peer] PublicKey = {{ client_public_key.stdout }} AllowedIPs = {{ client_ip }}/32 marker: "# {mark} ANSIBLE MANAGED BLOCK - Client: {{ client_name }}" register: wireguard_client_block - name: Ensure client configs directory exists file: path: "{{ wireguard_client_configs_path }}" state: directory mode: '0700' owner: root group: root - name: Ensure local client configs directory exists file: path: "{{ wireguard_local_client_configs_dir }}" state: directory mode: '0700' delegate_to: localhost become: no run_once: true - name: Get server public key shell: "cat {{ wireguard_config_path }}/{{ wireguard_interface }}_private.key | wg pubkey" register: server_public_key_cmd changed_when: false no_log: yes failed_when: false - name: Create NEW client configuration file template: src: "{{ playbook_dir }}/../templates/wireguard-client.conf.j2" dest: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf" mode: '0600' owner: root group: root - name: Download NEW client configuration to control machine fetch: src: "{{ wireguard_client_configs_path }}/{{ client_name }}.conf" dest: "{{ wireguard_local_client_configs_dir }}/{{ client_name }}.conf" flat: yes mode: '0600' - name: Restart WireGuard service systemd: name: "wg-quick@{{ wireguard_interface }}" state: restarted - name: Display NEW client configuration debug: msg: | ======================================== WireGuard Client REGENERATED: {{ client_name }} ======================================== Neue Client-IP: {{ client_ip }} Server Endpoint: {{ server_external_ip_content }}:{{ wireguard_port }} Neue Client-Konfiguration: {{ wireguard_local_client_configs_dir }}/{{ client_name }}.conf WICHTIG: 1. Lade die neue Config-Datei herunter 2. Importiere sie in WireGuard (ersetze die alte!) 3. Verbinde mit dem VPN 4. Teste: ping 10.8.0.1 5. Teste: https://grafana.michaelschiemer.de Alte Config gesichert als: {{ backup_result.dest | default('N/A') }} ========================================