Secure Split-Tunnel WireGuard + AdGuard + UFW

This SOP is the final, stable operating procedure for a small production homelab using WireGuard, AdGuard Home, Docker monitoring, and UFW. It reflects all fixes, decisions, and lessons learned.


1) Design Goals

  • Secure private management traffic
  • No performance impact on internet traffic
  • Public DNS via AdGuard Home (intentional)
  • No public exposure of admin or Docker APIs
  • Simple, predictable routing and firewall rules

2) Final Network Model

  • Main Server: WireGuard server
  • Raspberry Pi 1 / 2: WireGuard clients
  • VPN Subnet: 10.8.0.0/24
  • Tunnel Mode: Split tunnel (VPN traffic only)
  • DNS: Local (AdGuard Home on Pi)
  • IPv6: Disabled
  • Firewall: UFW (IPv4 only)

3) WireGuard Configuration (FINAL)

Server (/etc/wireguard/wg0.conf)

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = PLEASE_PUT_YOUR_SERVER_PRIVATE_KEY

[Peer]
PublicKey = PLEASE_PUT_YOUR_PI1_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32

[Peer]
PublicKey = PLEASE_PUT_YOUR_PI2_PUBLIC_KEY
AllowedIPs = 10.8.0.3/32

Clients (Pi1 / Pi2)

[Interface]
Address = PLEASE_PUT_YOUR_PI_WG_IP
PrivateKey = PLEASE_PUT_YOUR_PI_PRIVATE_KEY
# No DNS line (AdGuard runs locally)

[Peer]
PublicKey = PLEASE_PUT_YOUR_SERVER_PUBLIC_KEY
Endpoint = PLEASE_PUT_YOUR_SERVER_PUBLIC_IP_OR_DNS:51820
AllowedIPs = 10.8.0.0/24
PersistentKeepalive = 25

Critical rule: AllowedIPs = 10.8.0.0/24

❌ Never use /0 unless intentionally building a full-tunnel VPN.


4) Routing Verification (MANDATORY)

On each client:

ip route

Expected:

  • Default route → LAN gateway
  • 10.8.0.0/24wg0

If default route points to wg0, stop and fix before continuing.


5) DNS Model (FINAL)

  • AdGuard Home runs locally on a Raspberry Pi
  • WireGuard must not override DNS
  • No DNS= entry in any WireGuard config

Public DNS services:

  • 53/udp
  • 53/tcp
  • 853/tcp (DNS-over-TLS)

WireGuard DNS access is also allowed for internal clients.


6) IPv6 Policy (FINAL)

IPv6 is permanently disabled to reduce complexity and avoid dual-stack edge cases.

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

UFW IPv6 handling is disabled:

IPV6=no

7) Firewall Policy (UFW — FINAL)

Default Policy

ufw default deny incoming
ufw default allow outgoing

Publicly Exposed Ports (INTENTIONAL)

ufw allow 22/tcp        # SSH (rate-limited or WG-only)
ufw allow 80/tcp        # HTTP
ufw allow 443/tcp       # HTTPS
ufw allow 51820/udp     # WireGuard
ufw allow 53/udp        # DNS
ufw allow 53/tcp        # DNS
ufw allow 853/tcp       # DNS-over-TLS

WireGuard Internal Traffic

ufw allow in on wg0
ufw allow in on wg0 to any port 53
ufw allow in on wg0 to any port 853

SSH (Choose One)

WireGuard-only (recommended):

ufw allow from 10.8.0.0/24 to any port 22 proto tcp

OR public but rate-limited:

ufw limit 22/tcp

8) Docker Monitoring Policy (FINAL)

  • Docker API is never public
  • Access only over WireGuard
  • Prefer docker-socket-proxy

Firewall rule:

ufw allow from 10.8.0.0/24 to any port 2375

Used by Uptime Kuma for monitoring only.


9) Automation & Defense-in-Depth

  • AdGuard logs feed n8n automation
  • Attacking IPs are auto-blocked
  • Firewall provides first-layer filtering
  • Automation provides adaptive response

Always whitelist:

  • 10.8.0.0/24
  • Admin IPs
  • Health-check sources

10) Validation Checklist (RUN AFTER CHANGES)

wg
ip route
ping 10.8.0.1
ping 8.8.8.8
ping google.com
ufw status verbose
ss -tulpen | head -n 30

All must pass.


11) Known Pitfalls (DO NOT REPEAT)

  • AllowedIPs = 0.0.0.0/0 (unintended full tunnel)
  • ❌ Setting WireGuard DNS when AdGuard is local
  • ❌ Exposing Docker ports publicly
  • ❌ Using HTTPS proxies to “secure” Docker API
  • ❌ Relying on UFW NAT side-effects for routing

12) Final State

  • Fast internet (no tunnel overhead)
  • Encrypted management traffic
  • Public DNS intentionally exposed
  • Minimal, auditable firewall rules
  • No hidden routing or NAT dependencies

This SOP represents the final, correct configuration.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Secret Link