add darwin support
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
# WireGuard
|
||||||
|
|
||||||
|
Installs WireGuard and manages `wg-quick` tunnels from `wireguard_tunnels`
|
||||||
|
(see `defaults/main.yml` for the full schema).
|
||||||
|
|
||||||
|
- **Linux** — server (`server_mode: true`) or client. Server mode enables
|
||||||
|
masquerade + UFW rules; tunnels are managed by `wg-quick@<iface>.service`.
|
||||||
|
- **macOS (Darwin)** — client only, via Homebrew `wireguard-tools` +
|
||||||
|
`wireguard-go`. No systemd/UFW/sysctl: the config is rendered to
|
||||||
|
`{{ wireguard_config_base_path }}` (`/etc/wireguard`) and brought up with
|
||||||
|
`wg-quick`.
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo wg show # Linux
|
||||||
|
sudo /opt/homebrew/bin/wg show # macOS (or add /opt/homebrew/bin to sudo secure_path)
|
||||||
|
```
|
||||||
@@ -1,6 +1,20 @@
|
|||||||
---
|
---
|
||||||
wireguard_primary_interface: "{{ network_interfaces.0.name }}"
|
wireguard_primary_interface: "{{ network_interfaces.0.name }}"
|
||||||
wireguard_config_base_path: /etc/wireguard
|
wireguard_config_base_path: /etc/wireguard
|
||||||
|
# Group owning the config dir/files. macOS has no `root` group (gid 0 is `wheel`).
|
||||||
|
wireguard_config_group: >-
|
||||||
|
{{ (ansible_facts['os_family'] == 'Darwin') | ternary('wheel', 'root') }}
|
||||||
|
# macOS: Homebrew prefix (arm64: /opt/homebrew, Intel: /usr/local). sudo's
|
||||||
|
# secure_path omits it, so wg/wg-quick must be called by absolute path.
|
||||||
|
wireguard_darwin_brew_prefix: /opt/homebrew
|
||||||
|
wireguard_wg_bin: >-
|
||||||
|
{{ (ansible_facts['os_family'] == 'Darwin') | ternary(wireguard_darwin_brew_prefix + '/bin/wg', 'wg') }}
|
||||||
|
wireguard_wg_quick_bin: >-
|
||||||
|
{{ (ansible_facts['os_family'] == 'Darwin') | ternary(wireguard_darwin_brew_prefix + '/bin/wg-quick', 'wg-quick') }}
|
||||||
|
# wg-quick's shebang is `#!/usr/bin/env bash` and requires bash 4+, but sudo's
|
||||||
|
# secure_path resolves `bash` to macOS's /bin/bash 3.2. Run it through
|
||||||
|
# Homebrew's bash explicitly (wireguard-tools depends on it).
|
||||||
|
wireguard_darwin_bash: "{{ wireguard_darwin_brew_prefix }}/bin/bash"
|
||||||
# wireguard_tunnels:
|
# wireguard_tunnels:
|
||||||
# - interface: wg0 # required: maps to wgN interface name and config filename
|
# - interface: wg0 # required: maps to wgN interface name and config filename
|
||||||
# address: 10.0.0.1/24 # required: CIDR address for [Interface] Address
|
# address: 10.0.0.1/24 # required: CIDR address for [Interface] Address
|
||||||
@@ -8,6 +22,9 @@ wireguard_config_base_path: /etc/wireguard
|
|||||||
# dns: 10.0.0.1 # optional: DNS= line; omit to suppress
|
# dns: 10.0.0.1 # optional: DNS= line; omit to suppress
|
||||||
# server_mode: true # optional (default: false): enables NAT masquerade + UFW rule
|
# server_mode: true # optional (default: false): enables NAT masquerade + UFW rule
|
||||||
# primary_interface: eth0 # optional: overrides wireguard_primary_interface for this tunnel
|
# primary_interface: eth0 # optional: overrides wireguard_primary_interface for this tunnel
|
||||||
|
# private_key: ... # optional: use a fixed (e.g. vaulted) private key instead
|
||||||
|
# # of generating one on the host. Recommended for clients
|
||||||
|
# # whose public key is already a server's peer.
|
||||||
# peers: # optional: list of [Peer] entries
|
# peers: # optional: list of [Peer] entries
|
||||||
# - name: peer_name # required: comment label
|
# - name: peer_name # required: comment label
|
||||||
# public_key: ... # required: peer's public key
|
# public_key: ... # required: peer's public key
|
||||||
@@ -18,7 +35,8 @@ wireguard_tunnels: []
|
|||||||
|
|
||||||
# OS-dependent package name
|
# OS-dependent package name
|
||||||
wireguard_package_name: >-
|
wireguard_package_name: >-
|
||||||
{{ (ansible_facts['os_family'] == 'Archlinux') | ternary('wireguard-tools', 'wireguard') }}
|
{{ (ansible_facts['os_family'] == 'Archlinux' or ansible_facts['os_family'] == 'Darwin')
|
||||||
|
| ternary('wireguard-tools', 'wireguard') }}
|
||||||
|
|
||||||
# Prometheus exporter (optional, gated by wireguard_exporter_enabled).
|
# Prometheus exporter (optional, gated by wireguard_exporter_enabled).
|
||||||
# Exposes `wg show all dump` as Prometheus metrics (bytes per peer, last
|
# Exposes `wg show all dump` as Prometheus metrics (bytes per peer, last
|
||||||
|
|||||||
@@ -2,17 +2,29 @@
|
|||||||
- name: Reload systemd
|
- name: Reload systemd
|
||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
become: true
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
- name: Apply sysctl
|
- name: Apply sysctl
|
||||||
ansible.builtin.command: sysctl --system
|
ansible.builtin.command: sysctl --system
|
||||||
|
become: true
|
||||||
changed_when: true
|
changed_when: true
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
|
# Linux applies the config live with wg syncconf (no downtime). macOS has no
|
||||||
|
# syncconf-friendly flow here, so bounce the interface through wg-quick.
|
||||||
- name: Reload wireguard tunnel
|
- name: Reload wireguard tunnel
|
||||||
ansible.builtin.shell: |
|
ansible.builtin.shell: |
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
wg syncconf "{{ item.interface }}" <(wg-quick strip "{{ item.interface }}")
|
if [ "{{ ansible_facts['os_family'] }}" = "Darwin" ]; then
|
||||||
|
{{ wireguard_darwin_bash }} {{ wireguard_wg_quick_bin }} down "{{ item.interface }}" 2>/dev/null || true
|
||||||
|
{{ wireguard_darwin_bash }} {{ wireguard_wg_quick_bin }} up "{{ item.interface }}"
|
||||||
|
else
|
||||||
|
{{ wireguard_wg_bin }} syncconf "{{ item.interface }}" <(wg-quick strip "{{ item.interface }}")
|
||||||
|
fi
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
|
become: true
|
||||||
loop: "{{ wireguard_tunnels }}"
|
loop: "{{ wireguard_tunnels }}"
|
||||||
changed_when: true
|
changed_when: true
|
||||||
|
|
||||||
@@ -20,3 +32,5 @@
|
|||||||
ansible.builtin.systemd:
|
ansible.builtin.systemd:
|
||||||
name: "{{ wireguard_exporter_service }}"
|
name: "{{ wireguard_exporter_service }}"
|
||||||
state: restarted
|
state: restarted
|
||||||
|
become: true
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|||||||
@@ -9,10 +9,20 @@
|
|||||||
See roles/wireguard/defaults/main.yml for configuration instructions.
|
See roles/wireguard/defaults/main.yml for configuration instructions.
|
||||||
success_msg: "wireguard_tunnels validation passed"
|
success_msg: "wireguard_tunnels validation passed"
|
||||||
|
|
||||||
- name: Install wireguard
|
- name: Install wireguard (Linux)
|
||||||
ansible.builtin.package:
|
ansible.builtin.package:
|
||||||
name: "{{ wireguard_package_name }}"
|
name: "{{ wireguard_package_name }}"
|
||||||
state: present
|
state: present
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
|
# Homebrew refuses to run as root, so never become for this task even when the
|
||||||
|
# role is applied with become (needed later for /etc/wireguard and wg-quick).
|
||||||
|
- name: Install wireguard (macOS)
|
||||||
|
community.general.homebrew:
|
||||||
|
name: "{{ wireguard_package_name }}"
|
||||||
|
state: present
|
||||||
|
become: false
|
||||||
|
when: ansible_facts['os_family'] == 'Darwin'
|
||||||
|
|
||||||
- name: Install nftables (Arch server_mode PostUp dependency)
|
- name: Install nftables (Arch server_mode PostUp dependency)
|
||||||
ansible.builtin.package:
|
ansible.builtin.package:
|
||||||
@@ -38,13 +48,15 @@
|
|||||||
name: systemd-resolved
|
name: systemd-resolved
|
||||||
enabled: true
|
enabled: true
|
||||||
state: started
|
state: started
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
- name: Ensure wireguard configuration is only owned by root
|
- name: Ensure wireguard configuration is only owned by root
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ wireguard_config_base_path }}"
|
path: "{{ wireguard_config_base_path }}"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: "{{ wireguard_config_group }}"
|
||||||
mode: "0700"
|
mode: "0700"
|
||||||
|
state: directory
|
||||||
recurse: true
|
recurse: true
|
||||||
|
|
||||||
- name: Configure tunnel
|
- name: Configure tunnel
|
||||||
|
|||||||
@@ -15,22 +15,36 @@
|
|||||||
ansible.builtin.stat:
|
ansible.builtin.stat:
|
||||||
path: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey"
|
path: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey"
|
||||||
register: _tunnel_pkey_file
|
register: _tunnel_pkey_file
|
||||||
|
when: _tunnel.private_key is not defined
|
||||||
|
|
||||||
- name: "Generate wireguard keys for {{ _tunnel.interface }} if not present"
|
- name: "Generate wireguard keys if not present for {{ _tunnel.interface }}"
|
||||||
ansible.builtin.shell: >
|
ansible.builtin.shell: |
|
||||||
wg genkey |
|
set -o pipefail
|
||||||
|
{{ wireguard_wg_bin }} genkey |
|
||||||
tee {{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey |
|
tee {{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey |
|
||||||
wg pubkey > {{ wireguard_config_base_path }}/{{ _tunnel.interface }}.publickey
|
{{ wireguard_wg_bin }} pubkey > {{ wireguard_config_base_path }}/{{ _tunnel.interface }}.publickey
|
||||||
when: not _tunnel_pkey_file.stat.exists
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
changed_when: true
|
||||||
|
when:
|
||||||
|
- _tunnel.private_key is not defined
|
||||||
|
- not _tunnel_pkey_file.stat.exists
|
||||||
|
|
||||||
- name: "Retrieve wireguard private key for {{ _tunnel.interface }}"
|
- name: "Retrieve wireguard private key for {{ _tunnel.interface }}"
|
||||||
ansible.builtin.slurp:
|
ansible.builtin.slurp:
|
||||||
src: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey"
|
src: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.privatekey"
|
||||||
register: _tunnel_private_key_b64
|
register: _tunnel_private_key_b64
|
||||||
|
when: _tunnel.private_key is not defined
|
||||||
|
|
||||||
- name: "Set wireguard private key fact for {{ _tunnel.interface }}"
|
- name: "Set wireguard private key fact from inventory for {{ _tunnel.interface }}"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
_tunnel_private_key: "{{ _tunnel.private_key }}"
|
||||||
|
when: _tunnel.private_key is defined
|
||||||
|
|
||||||
|
- name: "Set wireguard private key fact from host for {{ _tunnel.interface }}"
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
_tunnel_private_key: "{{ _tunnel_private_key_b64['content'] | b64decode }}"
|
_tunnel_private_key: "{{ _tunnel_private_key_b64['content'] | b64decode }}"
|
||||||
|
when: _tunnel.private_key is not defined
|
||||||
|
|
||||||
- name: "Resolve effective DNS for {{ _tunnel.interface }}"
|
- name: "Resolve effective DNS for {{ _tunnel.interface }}"
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
@@ -41,7 +55,7 @@
|
|||||||
src: wireguard.conf.j2
|
src: wireguard.conf.j2
|
||||||
dest: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.conf"
|
dest: "{{ wireguard_config_base_path }}/{{ _tunnel.interface }}.conf"
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: "{{ wireguard_config_group }}"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
notify: Reload wireguard tunnel
|
notify: Reload wireguard tunnel
|
||||||
|
|
||||||
@@ -50,6 +64,7 @@
|
|||||||
path: "/etc/systemd/system/wg-quick@{{ _tunnel.interface }}.service.d"
|
path: "/etc/systemd/system/wg-quick@{{ _tunnel.interface }}.service.d"
|
||||||
state: directory
|
state: directory
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
- name: "Deploy systemd override for network dependency for {{ _tunnel.interface }}"
|
- name: "Deploy systemd override for network dependency for {{ _tunnel.interface }}"
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
@@ -57,6 +72,7 @@
|
|||||||
dest: "/etc/systemd/system/wg-quick@{{ _tunnel.interface }}.service.d/network-dependency.conf"
|
dest: "/etc/systemd/system/wg-quick@{{ _tunnel.interface }}.service.d/network-dependency.conf"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Reload systemd
|
notify: Reload systemd
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
- name: "Enable IP forwarding for {{ _tunnel.interface }}"
|
- name: "Enable IP forwarding for {{ _tunnel.interface }}"
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
@@ -64,10 +80,12 @@
|
|||||||
content: |
|
content: |
|
||||||
net.ipv4.ip_forward = 1
|
net.ipv4.ip_forward = 1
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: "{{ wireguard_config_group }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
notify: Apply sysctl
|
notify: Apply sysctl
|
||||||
when: _tunnel.server_mode | default(false)
|
when:
|
||||||
|
- _tunnel.server_mode | default(false)
|
||||||
|
- ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
- name: "Configure the firewall for {{ _tunnel.interface }}"
|
- name: "Configure the firewall for {{ _tunnel.interface }}"
|
||||||
community.general.ufw:
|
community.general.ufw:
|
||||||
@@ -83,6 +101,7 @@
|
|||||||
when:
|
when:
|
||||||
- _tunnel.server_mode | default(false)
|
- _tunnel.server_mode | default(false)
|
||||||
- _tunnel.port is defined
|
- _tunnel.port is defined
|
||||||
|
- ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
- name: "Allow forwarding for {{ _tunnel.interface }}"
|
- name: "Allow forwarding for {{ _tunnel.interface }}"
|
||||||
community.general.ufw:
|
community.general.ufw:
|
||||||
@@ -100,7 +119,9 @@
|
|||||||
out: "{{ _tunnel.primary_interface | default(wireguard_primary_interface) }}"
|
out: "{{ _tunnel.primary_interface | default(wireguard_primary_interface) }}"
|
||||||
- in: "{{ _tunnel.primary_interface | default(wireguard_primary_interface) }}"
|
- in: "{{ _tunnel.primary_interface | default(wireguard_primary_interface) }}"
|
||||||
out: "{{ _tunnel.interface }}"
|
out: "{{ _tunnel.interface }}"
|
||||||
when: _tunnel.server_mode | default(false)
|
when:
|
||||||
|
- _tunnel.server_mode | default(false)
|
||||||
|
- ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
- name: "Start and enable wg-quick@{{ _tunnel.interface }}"
|
- name: "Start and enable wg-quick@{{ _tunnel.interface }}"
|
||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
@@ -108,3 +129,19 @@
|
|||||||
state: started
|
state: started
|
||||||
enabled: true
|
enabled: true
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
when: ansible_facts['os_family'] != 'Darwin'
|
||||||
|
|
||||||
|
# macOS: no systemd; wg-quick drives wireguard-go directly. Idempotent via the
|
||||||
|
# list of active interfaces rather than a `creates` path.
|
||||||
|
- name: "List active wireguard interfaces (macOS)"
|
||||||
|
ansible.builtin.command: "{{ wireguard_wg_bin }} show interfaces"
|
||||||
|
register: _wg_active_interfaces
|
||||||
|
changed_when: false
|
||||||
|
when: ansible_facts['os_family'] == 'Darwin'
|
||||||
|
|
||||||
|
- name: "Bring up tunnel (macOS) for {{ _tunnel.interface }}"
|
||||||
|
ansible.builtin.command: "{{ wireguard_darwin_bash }} {{ wireguard_wg_quick_bin }} up {{ _tunnel.interface }}"
|
||||||
|
changed_when: true
|
||||||
|
when:
|
||||||
|
- ansible_facts['os_family'] == 'Darwin'
|
||||||
|
- _tunnel.interface not in _wg_active_interfaces.stdout.split()
|
||||||
|
|||||||
Reference in New Issue
Block a user