diff options
| -rw-r--r-- | host_vars/archlinux.local.yml | bin | 203 -> 207 bytes | |||
| -rw-r--r-- | host_vars/rpi5.local.yml | bin | 393 -> 491 bytes | |||
| -rw-r--r-- | roles/wireguard_gateway/README.md | 98 | ||||
| -rw-r--r-- | roles/wireguard_gateway/defaults/main.yml | 2 | ||||
| -rw-r--r-- | roles/wireguard_gateway/handlers/main.yml | 6 | ||||
| -rw-r--r-- | roles/wireguard_gateway/tasks/main.yml | 109 | ||||
| -rw-r--r-- | site.yml | 2 |
7 files changed, 217 insertions, 0 deletions
diff --git a/host_vars/archlinux.local.yml b/host_vars/archlinux.local.yml Binary files differindex 163b4321..cae65971 100644 --- a/host_vars/archlinux.local.yml +++ b/host_vars/archlinux.local.yml diff --git a/host_vars/rpi5.local.yml b/host_vars/rpi5.local.yml Binary files differindex a633f5a8..41ce15f2 100644 --- a/host_vars/rpi5.local.yml +++ b/host_vars/rpi5.local.yml diff --git a/roles/wireguard_gateway/README.md b/roles/wireguard_gateway/README.md new file mode 100644 index 00000000..06c3ceb7 --- /dev/null +++ b/roles/wireguard_gateway/README.md @@ -0,0 +1,98 @@ +# Ansible Role: wireguard_gateway + +This role configures a Linux host as a WireGuard VPN gateway, +routing all traffic from local network clients through the VPN connection. + +## Role Variables + +- `wireguard_gateway_enabled`: Boolean flag to enable/disable gateway functionality (default: `false`) + +## What This Role Does + +When `wireguard_gateway_enabled` is `true`, the role: + +- Sets `net.ipv4.ip_forward=1` and `net.ipv6.conf.all.forwarding=1` in `/etc/sysctl.conf` +- Creates custom routing table (100) to route traffic through VPN +- Routes IPv4 and IPv6 traffic from local subnet through VPN interface +- Allows traffic forwarding and established connections + +When `wireguard_gateway_enabled` is `false`, the role: + +- Removes all routing rules and iptables configurations +- Disables IP forwarding in sysctl configuration +- Cleans up custom routing tables + +## Example Configuration + +```yaml +# In host_vars/gateway.yml +wireguard_gateway_enabled: true +wireguard_autostart_connection: "protonvpn-us-1" + +# WireGuard connection configuration +wireguard_connections: + protonvpn-us-1: | + [Interface] + PrivateKey = your_private_key_here + Address = 10.2.0.2/32 + DNS = 10.2.0.1 + + [Peer] + PublicKey = server_public_key + AllowedIPs = 0.0.0.0/0, ::/0 + Endpoint = vpn.example.com:51820 +``` + +## Client Configuration + +For client machines to route through the VPN gateway: + +1. **IPv4**: Set gateway to VPN gateway host's IP +2. **IPv6**: Set IPv6 gateway to VPN gateway host's IPv6 address +3. **Manual network config**: Disable auto-configuration to use custom gateway + +Example client network configuration: + +```yaml +network_ipv4_address: "192.168.1.100" +network_ipv4_gateway: "192.168.1.254" # VPN gateway host +network_ipv6_address: "2001:db8::100/64" +network_ipv6_gateway: "2001:db8::254" # VPN gateway host +``` + +## Technical Details + +### Routing Rules Created + +```bash +# Policy routing rules (priority 200) +ip rule add iif eth0 table 100 priority 200 +ip -6 rule add iif eth0 table 100 priority 200 + +# Custom routing table (table 100) +ip route add default dev wg-connection table 100 +ip -6 route add default dev wg-connection table 100 +``` + +### iptables Rules Created + +```bash +# IPv4 NAT masquerading +iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o wg-connection -j MASQUERADE + +# IPv4 FORWARD rules +iptables -A FORWARD -s 192.168.1.0/24 -j ACCEPT +iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + +# IPv6 equivalent rules +ip6tables -t nat -A POSTROUTING -s 2001:db8::/64 -o wg-connection -j MASQUERADE +ip6tables -A FORWARD -s 2001:db8::/64 -j ACCEPT +ip6tables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +``` + +## Troubleshooting + +- **Check VPN connection**: `sudo wg show` should show active connection +- **Verify routing**: `ip rule show` and `ip route show table 100` +- **Test connectivity**: `curl ip.me` should show VPN IP from client machines +- **Check iptables**: `sudo iptables -t nat -L -n -v` should show masquerading rules diff --git a/roles/wireguard_gateway/defaults/main.yml b/roles/wireguard_gateway/defaults/main.yml new file mode 100644 index 00000000..0086e118 --- /dev/null +++ b/roles/wireguard_gateway/defaults/main.yml @@ -0,0 +1,2 @@ +--- +wireguard_gateway_enabled: false diff --git a/roles/wireguard_gateway/handlers/main.yml b/roles/wireguard_gateway/handlers/main.yml new file mode 100644 index 00000000..4bffa794 --- /dev/null +++ b/roles/wireguard_gateway/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: "Reload sysctl" + become: true + ansible.builtin.command: + cmd: "sysctl -p" + changed_when: false
\ No newline at end of file diff --git a/roles/wireguard_gateway/tasks/main.yml b/roles/wireguard_gateway/tasks/main.yml new file mode 100644 index 00000000..2b82ff66 --- /dev/null +++ b/roles/wireguard_gateway/tasks/main.yml @@ -0,0 +1,109 @@ +--- +- name: "Ensure iptables are installed" + become: true + ansible.builtin.package: + name: "iptables" + state: "present" + +- name: "Configure IP forwarding" + become: true + ansible.builtin.blockinfile: + path: "/etc/sysctl.conf" + state: "{{ 'present' if wireguard_gateway_enabled else 'absent' }}" + prepend_newline: true + append_newline: true + marker: "# ==== {mark} ANSIBLE WIREGUARD GATEWAY CONFIG" + create: true + mode: "0644" + block: | + net.ipv4.ip_forward=1 + net.ipv6.conf.all.forwarding=1 + notify: "Reload sysctl" + +- name: "Configure VPN gateway routing" + become: true + ansible.builtin.shell: | + {% if wireguard_gateway_enabled %} + # Add routes to custom table + ip route add default dev {{ wireguard_autostart_connection }} table 100 || true + ip -6 route add default dev {{ wireguard_autostart_connection }} table 100 || true + # Remove any incorrect policy routing rules first + ip -6 rule del iif {{ ansible_default_ipv6.interface }} table 100 || true + # Add correct policy routing rules + ip rule add iif {{ ansible_default_ipv4.interface }} table 100 priority 200 || true + ip -6 rule add iif {{ ansible_default_ipv4.interface }} table 100 priority 200 || true + {% else %} + # Remove policy routing rules + ip rule del iif {{ ansible_default_ipv4.interface }} table 100 || true + ip -6 rule del iif {{ ansible_default_ipv4.interface }} table 100 || true + # Remove routes from custom table + ip route del default dev {{ wireguard_autostart_connection }} table 100 || true + ip -6 route del default dev {{ wireguard_autostart_connection }} table 100 || true + {% endif %} + failed_when: false + changed_when: false + +# IPv4 iptables rules +- name: "Add IPv4 NAT masquerading for traffic through VPN" + become: true + ansible.builtin.iptables: + table: "nat" + chain: "POSTROUTING" + source: "{{ ansible_default_ipv4.network }}/{{ ansible_default_ipv4.prefix }}" + out_interface: "{{ wireguard_autostart_connection }}" + jump: "MASQUERADE" + comment: "NAT local subnet traffic through VPN" + state: "{{ 'present' if wireguard_gateway_enabled else 'absent' }}" + +- name: "Add IPv4 FORWARD rule to accept traffic from local subnet" + become: true + ansible.builtin.iptables: + chain: "FORWARD" + source: "{{ ansible_default_ipv4.network }}/{{ ansible_default_ipv4.prefix }}" + jump: "ACCEPT" + comment: "Allow forwarding from local subnet" + state: "{{ 'present' if wireguard_gateway_enabled else 'absent' }}" + +- name: "Add IPv4 FORWARD rule to accept established connections" + become: true + ansible.builtin.iptables: + chain: "FORWARD" + match: "conntrack" + ctstate: "RELATED,ESTABLISHED" + jump: "ACCEPT" + comment: "Allow established connections" + state: "{{ 'present' if wireguard_gateway_enabled else 'absent' }}" + +# IPv6 iptables rules +- name: "Add IPv6 NAT masquerading for traffic through VPN" + become: true + ansible.builtin.iptables: + table: "nat" + chain: "POSTROUTING" + source: "{{ (ansible_default_ipv6.address.split(':')[:4] | join(':')) + '::/64' }}" + out_interface: "{{ wireguard_autostart_connection }}" + jump: "MASQUERADE" + comment: "NAT IPv6 local subnet traffic through VPN" + ip_version: "ipv6" + state: "{{ 'present' if wireguard_gateway_enabled else 'absent' }}" + +- name: "Add IPv6 FORWARD rule to accept traffic from local subnet" + become: true + ansible.builtin.iptables: + chain: "FORWARD" + source: "{{ (ansible_default_ipv6.address.split(':')[:4] | join(':')) + '::/64' }}" + jump: "ACCEPT" + comment: "Allow IPv6 forwarding from local subnet" + ip_version: "ipv6" + state: "{{ 'present' if wireguard_gateway_enabled else 'absent' }}" + +- name: "Add IPv6 FORWARD rule to accept established connections" + become: true + ansible.builtin.iptables: + chain: "FORWARD" + match: "conntrack" + ctstate: "RELATED,ESTABLISHED" + jump: "ACCEPT" + comment: "Allow IPv6 established connections" + ip_version: "ipv6" + state: "{{ 'present' if wireguard_gateway_enabled else 'absent' }}" @@ -42,3 +42,5 @@ - role: "locales" - role: "timezone" - role: "wireguard" + - role: "wireguard_gateway" + - role: "pihole" |
