Harden WireGuard VPN configuration
- Add IPv6 ip6tables rules to prevent IPv6 traffic leaks (forwarding was enabled but only IPv4 masquerade existed) - Tighten FORWARD chain: only allow wg0→internet and established return, instead of blanket ACCEPT from wg0 - Scope NAT masquerade to VPN subnet only - Block VPN clients from server-local services (SSH/80/443) via INPUT rules - Set UFW DEFAULT_FORWARD_POLICY=ACCEPT (required for VPN routing; base hardening sets default deny which blocks forwarded packets) - Add SaveConfig = false to prevent runtime state overwriting config - Add reload ufw handler Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,3 +18,8 @@
|
|||||||
|
|
||||||
- name: Include WireGuard configuration tasks
|
- name: Include WireGuard configuration tasks
|
||||||
include_tasks: tasks/configure.yml
|
include_tasks: tasks/configure.yml
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- name: reload ufw
|
||||||
|
ufw:
|
||||||
|
state: reloaded
|
||||||
|
|||||||
@@ -24,13 +24,39 @@
|
|||||||
SERVER_PRIVKEY=$(cat /etc/wireguard/server_private.key)
|
SERVER_PRIVKEY=$(cat /etc/wireguard/server_private.key)
|
||||||
IFACE={{ primary_interface.stdout | trim }}
|
IFACE={{ primary_interface.stdout | trim }}
|
||||||
|
|
||||||
cat > /etc/wireguard/wg0.conf << EOF
|
cat > /etc/wireguard/wg0.conf << 'WGEOF'
|
||||||
[Interface]
|
[Interface]
|
||||||
|
WGEOF
|
||||||
|
|
||||||
|
# Write dynamic values separately to avoid heredoc variable issues
|
||||||
|
cat >> /etc/wireguard/wg0.conf << EOF
|
||||||
Address = {{ wg_subnet | regex_replace('/\\d+$', '') | regex_replace('\\.[0-9]+$', '.1/24') }}
|
Address = {{ wg_subnet | regex_replace('/\\d+$', '') | regex_replace('\\.[0-9]+$', '.1/24') }}
|
||||||
ListenPort = {{ wg_port }}
|
ListenPort = {{ wg_port }}
|
||||||
PrivateKey = ${SERVER_PRIVKEY}
|
PrivateKey = ${SERVER_PRIVKEY}
|
||||||
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ${IFACE} -j MASQUERADE
|
SaveConfig = false
|
||||||
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ${IFACE} -j MASQUERADE
|
|
||||||
|
# IPv4: forward VPN traffic to internet, masquerade source
|
||||||
|
PostUp = iptables -A FORWARD -i wg0 -o ${IFACE} -j ACCEPT
|
||||||
|
PostUp = iptables -A FORWARD -i ${IFACE} -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
PostUp = iptables -t nat -A POSTROUTING -o ${IFACE} -s {{ wg_subnet }} -j MASQUERADE
|
||||||
|
# IPv6: same rules to prevent leaks
|
||||||
|
PostUp = ip6tables -A FORWARD -i wg0 -o ${IFACE} -j ACCEPT
|
||||||
|
PostUp = ip6tables -A FORWARD -i ${IFACE} -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
PostUp = ip6tables -t nat -A POSTROUTING -o ${IFACE} -j MASQUERADE
|
||||||
|
# Block VPN clients from accessing server-local services (except DNS if local)
|
||||||
|
PostUp = iptables -I INPUT -i wg0 -p tcp --dport 22 -j DROP
|
||||||
|
PostUp = iptables -I INPUT -i wg0 -p tcp --dport 80 -j DROP
|
||||||
|
PostUp = iptables -I INPUT -i wg0 -p tcp --dport 443 -j DROP
|
||||||
|
|
||||||
|
PostDown = iptables -D FORWARD -i wg0 -o ${IFACE} -j ACCEPT
|
||||||
|
PostDown = iptables -D FORWARD -i ${IFACE} -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
PostDown = iptables -t nat -D POSTROUTING -o ${IFACE} -s {{ wg_subnet }} -j MASQUERADE
|
||||||
|
PostDown = ip6tables -D FORWARD -i wg0 -o ${IFACE} -j ACCEPT
|
||||||
|
PostDown = ip6tables -D FORWARD -i ${IFACE} -o wg0 -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
PostDown = ip6tables -t nat -D POSTROUTING -o ${IFACE} -j MASQUERADE
|
||||||
|
PostDown = iptables -D INPUT -i wg0 -p tcp --dport 22 -j DROP
|
||||||
|
PostDown = iptables -D INPUT -i wg0 -p tcp --dport 80 -j DROP
|
||||||
|
PostDown = iptables -D INPUT -i wg0 -p tcp --dport 443 -j DROP
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -38,6 +64,7 @@
|
|||||||
CLIENT_PUBKEY=$(cat /etc/wireguard/clients/client${i}_public.key)
|
CLIENT_PUBKEY=$(cat /etc/wireguard/clients/client${i}_public.key)
|
||||||
CLIENT_PSK=$(cat /etc/wireguard/clients/client${i}_psk.key)
|
CLIENT_PSK=$(cat /etc/wireguard/clients/client${i}_psk.key)
|
||||||
cat >> /etc/wireguard/wg0.conf << EOF
|
cat >> /etc/wireguard/wg0.conf << EOF
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
# Client ${i}
|
# Client ${i}
|
||||||
PublicKey = ${CLIENT_PUBKEY}
|
PublicKey = ${CLIENT_PUBKEY}
|
||||||
|
|||||||
@@ -24,6 +24,19 @@
|
|||||||
sysctl_set: true
|
sysctl_set: true
|
||||||
reload: true
|
reload: true
|
||||||
|
|
||||||
|
- name: Set UFW default forward policy to accept (required for VPN routing)
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/default/ufw
|
||||||
|
regexp: '^DEFAULT_FORWARD_POLICY='
|
||||||
|
line: 'DEFAULT_FORWARD_POLICY="ACCEPT"'
|
||||||
|
notify: reload ufw
|
||||||
|
|
||||||
|
- name: Allow WireGuard port through UFW
|
||||||
|
ufw:
|
||||||
|
rule: allow
|
||||||
|
port: "{{ wg_port }}"
|
||||||
|
proto: udp
|
||||||
|
|
||||||
- name: Generate server private key
|
- name: Generate server private key
|
||||||
shell: wg genkey
|
shell: wg genkey
|
||||||
register: wg_server_privkey
|
register: wg_server_privkey
|
||||||
@@ -46,9 +59,3 @@
|
|||||||
shell: "echo '{{ server_privkey_content.content | b64decode | trim }}' | wg pubkey"
|
shell: "echo '{{ server_privkey_content.content | b64decode | trim }}' | wg pubkey"
|
||||||
register: wg_server_pubkey
|
register: wg_server_pubkey
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
|
||||||
- name: Allow WireGuard port through UFW
|
|
||||||
ufw:
|
|
||||||
rule: allow
|
|
||||||
port: "{{ wg_port }}"
|
|
||||||
proto: udp
|
|
||||||
|
|||||||
Reference in New Issue
Block a user