OPNsenseXLVI · Remote Access and Site-to-Site ArchitectureRemote access and site-to-site architecture
User VPN architectures — split tunnel, full tunnel, always-on and the choices that matter
What you'll learn
- Compare split-tunnel and full-tunnel architectures and choose the right pattern for the deployment
- Configure always-on versus on-demand VPN and the implications for user experience and security
- Implement per-app and per-destination routing on modern clients
- Plan DNS for split-tunnel VPN — internal zones resolved by the corporate DNS, external zones by the local DNS
- Recognise the failure modes of each architecture and the mitigations
Prerequisites
Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-14
User VPN architecture is one of the most consequential decisions a network team makes. The choice between split tunnel and full tunnel affects every user’s experience, the security posture of every endpoint, the bandwidth bill of every VPN concentrator, and the threat surface of every home network the user connects from. The choice between always-on and on-demand affects whether the user is reachable when their laptop is closed, whether the laptop’s traffic is observable, and whether the user can opt out.
This lesson covers the architectural patterns — split tunnel, full tunnel, always-on, on-demand, per-app routing — the DNS implications, the security trade-offs, and the operational patterns that fit each deployment.
Split tunnel versus full tunnel
Split tunnel routes only the traffic destined for the corporate network through the VPN; the rest of the user’s traffic (Internet, personal services) goes directly to its destination without traversing the VPN. The VPN carries a defined list of destinations (AllowedIPs in WireGuard, split-DNS in IPsec).
Full tunnel routes all of the user’s traffic through the VPN. The VPN is the user’s default route; the corporate firewall sees every byte; the corporate DNS resolves every name; the corporate proxy (if any) inspects every URL.
The trade-off:
| Dimension | Split tunnel | Full tunnel |
|---|---|---|
| VPN bandwidth | Carries only corporate-destined traffic | Carries every byte |
| Corporate firewall load | Lower | Higher |
| DNS observability | Only internal zones go through corporate DNS | All DNS goes through corporate DNS |
| User experience | Personal traffic unaffected by VPN outages | VPN outage breaks the user’s Internet |
| Endpoint security | Endpoint needs to enforce policy for non-VPN traffic | Corporate firewall enforces policy for all traffic |
| Compliance scope | Endpoint in scope for personal-destined traffic | Endpoint not in scope for personal-destined traffic |
The choice depends on what the operator is willing to take responsibility for. A split-tunnel deployment says “the corporate firewall only protects corporate-destined traffic; for the rest, the endpoint is responsible.” A full-tunnel deployment says “the corporate firewall protects all of the user’s traffic, including personal traffic.”
Split tunnel in production
Split tunnel is the more common production pattern because it scales without inflating the VPN concentrator’s bandwidth bill. The WireGuard AllowedIPs list is the canonical split-tunnel configuration: traffic to 10.0.0.0/16 (the corporate subnet) goes through the VPN; traffic to everything else goes directly.
# WireGuard AllowedIPs for a split-tunnel deployment.
# Corporate subnet goes through the VPN.
AllowedIPs = 10.0.0.0/16, 10.99.0.0/24
# Default route (split tunnel exits).
PostUp = ip route add default via 192.168.1.1 dev wlan0
# The corporate subnet is routed via the WireGuard interface.
PostUp = ip route add 10.0.0.0/16 dev wg0
PostUp = ip route add 10.99.0.0/24 dev wg0
The DNS for split tunnel is the trickier part. The user wants internal hostnames (e.g. wiki.corp.internal) to resolve via the corporate DNS, and external hostnames (e.g. google.com) to resolve via the local DNS. The mechanism is split DNS — a DNS configuration that routes queries based on the suffix.
Full tunnel in production
Full tunnel is simpler from the routing perspective but heavier from the bandwidth perspective. AllowedIPs is 0.0.0.0/0 (and ::/0 for IPv6) — the WireGuard interface becomes the default route for everything.
# WireGuard AllowedIPs for a full-tunnel deployment.
AllowedIPs = 0.0.0.0/0, ::/0
The implications:
- DNS. All DNS goes through the corporate DNS. The endpoint’s local DNS resolver is bypassed (or receives no queries). The corporate DNS must be prepared for the volume of external queries, or the corporate DNS forwards to an external resolver (e.g. 8.8.8.8) — but then the corporate DNS becomes a single point of failure for all user DNS.
- Outbound NAT. The user’s traffic exits from the corporate WAN, not from the user’s home network. The corporate firewall’s outbound NAT must carry the volume. The corporate WAN bandwidth is consumed.
- Proxy and inspection. If the corporate firewall intercepts HTTP/HTTPS, every user request is inspected. The user has no privacy on a corporate endpoint for personal-destined traffic (because there is no personal-destined traffic on a full tunnel — everything is corporate-destined).
# Confirm full-tunnel routing — the default route is via the WireGuard interface.
ip route | grep default
default dev wg0 scope link
Always-on versus on-demand
The second axis is when the VPN is up.
Always-on VPN is connected whenever the endpoint is online, regardless of network. The VPN reconnects automatically if it drops. The user cannot opt out without admin privileges on the endpoint.
On-demand VPN connects when the user needs it (e.g. opening a corporate app, accessing a corporate resource) and disconnects when idle.
The trade-off:
| Dimension | Always-on | On-demand |
|---|---|---|
| User experience | VPN is invisible | VPN connects when needed |
| Compliance | Every byte from the endpoint is observable | Only VPN-routed bytes are observable |
| Endpoint battery | Lower (VPN is always running) | Higher (VPN is intermittent) |
| Endpoint reliability | VPN drops visible immediately | VPN drops invisible to the user until they need it |
| Fail-open behaviour | The endpoint cannot reach anything if the VPN is down | The endpoint reaches the Internet directly if the VPN is down |
The always-on model is the higher-assurance posture. The on-demand model is the better user experience. Modern mobile platforms (iOS, Android) and modern macOS support always-on VPN with managed device configuration; the operator chooses always-on when the compliance regime requires it.
Per-app and per-destination routing
Modern VPN clients support per-app routing on mobile and per-destination routing on desktops. Per-app routing is a list of apps whose traffic goes through the VPN (iOS NetworkExtension, Android VpnService); per-destination routing is a list of subnets (the AllowedIPs list).
The operational patterns:
- Per-app for mobile. iOS and Android support per-app routing. A MDM profile can specify which apps go through the VPN. The discipline is to keep the per-app list focused: corporate apps through the VPN, personal apps (browsers, social media) outside.
- Per-destination for desktops. WireGuard AllowedIPs is per-destination. The discipline is to enumerate the corporate subnets (the inside subnets plus any cloud VPC subnets the corporate firewall peers with) and not to include
0.0.0.0/0unless the deployment is full tunnel.
DNS planning for split-tunnel VPN
The DNS configuration for split-tunnel VPN is split DNS — a DNS resolver that forwards queries for the corporate zones to the corporate DNS and resolves everything else via the local upstream. The implementation depends on the client platform:
- WireGuard on Linux/macOS/Windows. Use
dnsmasqon the endpoint or the WireGuardDNSfield plus a resolver like systemd-resolved. The corporate zones (e.g.corp.internal,internal.corp.com) forward to the corporate DNS; everything else goes to the local upstream. - WireGuard on iOS/Android. The WireGuard app supports a
DNSfield that points at the corporate DNS. iOS uses a system-wide DNS configuration; Android uses the per-VPN DNS. The corporate DNS must accept queries from the VPN pool.
# A split-DNS dnsmasq configuration.
# Forward corporate zones to the corporate DNS.
server=/corp.internal/10.0.0.53
server=/internal.corp.com/10.0.0.53
# Resolve everything else from the local upstream.
resolv-file=/etc/resolv.conf
Verification
After deploying the VPN architecture, verify:
- From the endpoint,
tracerouteto a corporate IP — must traverse the VPN. - From the endpoint,
tracerouteto a non-corporate IP — must NOT traverse the VPN (split tunnel) or MUST traverse the VPN (full tunnel). - From the endpoint,
dig wiki.corp.internal— must return the corporate IP (split DNS working). - From the endpoint,
dig google.com— must return a public IP (DNS not broken by split-tunnel). - From the corporate firewall,
tcpdump -ni wg0 host <user-vpn-ip>— must show the user’s traffic.
A configuration that passes 1-2 but fails 3-4 has working routing but broken DNS. A configuration that passes 3-4 but fails 1-2 has working DNS but broken routing. The operator checks both halves of the architecture.
Knowledge check · 4 questions
Q1. A 500-user deployment is being designed. The corporate firewall has a 100 Mbps Internet link. Which architecture is more appropriate as a starting point?
Q2. Always-on VPN without a kill switch is acceptable as long as the VPN client is configured to auto-reconnect.
Q3. Which of the following are required for a production split-tunnel deployment? Select all that apply.
Q4. A user on split-tunnel VPN can ping a corporate host by IP but the host name does not resolve from the user machine. What is the most likely cause?
Passing score: 75%. Answers are checked in this browser.