OPNsenseLab
Isometric mobile device and laptop connected to home network through encrypted VPN tunnel
VPN

OPNsense WireGuard Road Warrior: Remote Access Setup

Configure WireGuard on OPNsense for secure remote access — create the Instance, add Peers with the built-in generator, assign the interface, write firewall rules, and fix the no-handshake and handshake-but-no-traffic problems.

By OPNsenseLab Editorial · · 8 min read

WireGuard is the default modern VPN choice on OPNsense: it’s a kernel-module implementation on FreeBSD (so it avoids user-space context-switch overhead and gives strong throughput at low CPU), and the GUI walks you through it with an Instances and Peers model plus a peer generator. This guide sets up a road-warrior configuration — your phone and laptop reaching your home LAN from anywhere.

Two prerequisites decide whether this works at all. Inbound reachability: WireGuard listens on a UDP port that must be reachable from the internet. If your OPNsense WAN sits behind ISP CGNAT (common on mobile, 4G/5G, and some fibre), unsolicited inbound is dropped and a listening road-warrior VPN simply won’t work without a relay — verify your WAN has a public, port-forwardable address first. A stable endpoint: clients connect to an IP or hostname; without a static IP you need Dynamic DNS (covered below). Sort both before configuring tunnels.

Concepts

WireGuard uses a peer model. On OPNsense the Instance is the server-side tunnel (it maps to a wgX device such as wg1). Each remote device is a Peer, with its own public/private keypair. The server knows each peer’s public key; each peer knows the server’s public key and endpoint. Routing inside the tunnel is governed by Allowed IPs, which is the field people most often get wrong.

Step 1: Create the Instance (server)

VPN → WireGuard → Instances → Add (the GUI is a single integrated module on current OPNsense — no separate plugin install is needed on a current release):

  • Name: wg_roadwarrior
  • Public Key / Private Key: click to generate the server keypair
  • Listen Port: 51820 (default; change for obscurity if you like)
  • Tunnel Address: 10.10.10.1/24 — a private (RFC1918) subnet dedicated to the VPN, separate from your LAN, sized to hold all peers
  • Leave DNS/MTU at defaults for now

Save. Copy the Public Key — peers need it.

Step 2: Add Peers (clients)

The cleanest path is the built-in generator: VPN → WireGuard → Peer generator creates a peer plus a ready-to-import client config (including a QR code for the mobile app). Alternatively add peers manually under Peers → Add:

  • Name: iPhone
  • Public Key: paste the client’s public key
  • Allowed IPs: 10.10.10.2/32 — the single tunnel IP for this peer, as a /32

Then attach the peer to the Instance (the Instance has a Peers field listing which peers belong to it).

Allowed IPs is the most misunderstood field. On the server’s peer entry it lists which source addresses that peer may use inside the tunnel — for a road-warrior client that’s its one /32, not your LAN subnet. Putting your LAN range here, or a broad block, causes cryptokey-routing conflicts. The client config’s AllowedIPs (Step 5) is the separate setting that controls what the client routes into the tunnel.

Generating client keys manually

If a client can’t self-generate (most apps do in-app), on Linux/macOS:

wg genkey | tee private.key | wg pubkey > public.key
cat public.key   # paste into OPNsense as the peer's public key

Step 3: Assign the WireGuard interface

Interfaces → Assignments → pick the wg_roadwarrior device from the dropdown → Add → open it → Enable → Save → Apply. Assigning the interface is what lets you write explicit firewall rules and makes the tunnel a proper routing/NAT participant; the per-interface firewall tab won’t exist otherwise.

Step 4: Firewall rules

WAN — allow WireGuard UDP in

Firewall → Rules → WAN → Add:

Action: Pass
Protocol: UDP
Destination: WAN address
Destination port: 51820
Description: Allow WireGuard inbound

WireGuard interface — allow peers to LAN

Firewall → Rules → [WireGuard interface] → Add:

Action: Pass
Protocol: any
Source: WireGuard net (10.10.10.0/24)
Destination: LAN net
Description: WG peers reach LAN

Step 5: Client configuration

If you used the peer generator, just import its config or scan its QR code. A hand-built client config looks like:

[Interface]
PrivateKey = <peer private key>
Address = 10.10.10.2/32
DNS = 192.168.1.1

[Peer]
PublicKey = <OPNsense Instance public key>
Endpoint = your-home-ip-or-ddns:51820
AllowedIPs = 192.168.1.0/24, 10.10.10.0/24
PersistentKeepalive = 25

AllowedIPs here is split-tunnel (only LAN + VPN subnets route through home). For full-tunnel — route all client traffic via home — set AllowedIPs = 0.0.0.0/0, ::/0. PersistentKeepalive = 25 keeps NAT mappings open on mobile.

Step 6: DDNS if you lack a static IP

Services → Dynamic DNS (install os-ddclient if not present) → add your provider (Cloudflare, DuckDNS, etc.) and the hostname clients will use as their endpoint. Put that hostname — not a raw IP — in the client config.

Verify the connection

# OPNsense console or SSH:
wg show
# Look for the peer with a recent handshake timestamp and incrementing rx/tx

From the client, ping 192.168.1.1 (the OPNsense LAN IP), then reach an actual LAN host by IP.

Troubleshooting, outside-in

Each step isolates a layer:

  1. No handshake at all. Almost always the inbound path — the WAN rule isn’t passing UDP to the listen port, CGNAT/upstream is dropping it, or the client’s Endpoint is wrong. Watch the firewall live log for the inbound UDP packets while a client tries to connect.
  2. Handshake succeeds but no traffic. A routing/rules problem, not crypto. Confirm: the WireGuard-interface rule allows the WG subnet to LAN; the client’s AllowedIPs includes the LAN subnet; and that LAN hosts have a route back to the WG subnet (OPNsense’s WG subnet is directly connected, so usually fine — if LAN devices have their own firewalls, permit the 10.10.10.0/24 range there).
  3. Works on Wi-Fi but not cellular. Usually missing PersistentKeepalive, or MTU — try lowering the client MTU (1380–1420) if large packets stall while the handshake holds.
  4. DNS doesn’t resolve over the tunnel. The client DNS = must be a resolver reachable through the tunnel (the OPNsense LAN IP), and Unbound must accept queries from the WG subnet (Services → Unbound DNS → Access Lists).

wg show is the single best diagnostic: a recent handshake plus incrementing counters confirms the tunnel itself; their absence points you to the layer above.

When road-warrior WireGuard is the wrong approach

If your ISP uses CGNAT and you can’t get a forwardable inbound port, a self-hosted listening VPN won’t work — a mesh/overlay (outbound-initiated relay) or a small public relay is the right tool, and forcing WireGuard here wastes hours. If you only need one web service occasionally, an authenticated reverse proxy may be simpler. Use split-tunnel unless you specifically want every packet to egress via home — full-tunnel adds latency and leans on your home uplink.

WireGuard vs OpenVPN, or OPNsense vs pfSense for VPN? See firewallcompare.com. The OPNsense WireGuard road-warrior how-to is the authoritative reference.

Related

Comments