OpenBSD Handbook

    Theme
    • Part I. Install & Configure
      • Introduction
      • Installing OpenBSD
      • The X Window System
      • Networking
      • System Configuration
      • OpenBSD Basics
      • Managing Software: Packages and Ports
    • Part II. Daily Operations
      • Graphical Environments
      • Multimedia
      • Printing
      • Linux Compatibility
      • Windows Compatibility
      • Games
    • Part III. System Administration
      • Virtualization
      • Storage and File Systems
      • Updating and Upgrading
      • Localization
      • The OpenBSD Boot Process
      • Security
    • Part IV. Networking & Daemons
      • Services
        • Database
          • MariaDB
          • PostgreSQL
          • Redis
          • memcached
        • Directory
          • YP (NIS)
          • LDAP
        • File
          • NFS
          • Samba
        • FTP Services
          • ftpd
          • ProFTPD
          • vsftpd
          • Pure-FTPd
          • TFTP
        • Mail
          • smtpd
          • Dovecot
          • Postfix
          • Exim
          • Rspamd
        • Name
          • Unbound
          • NSD
          • BIND
        • Networking
          • OpenBGPD
          • rad
          • DHCP
          • slaacd
        • Web
          • Apache
          • nginx
          • httpd
          • relayd
        • Logging
          • syslogd
        • Monitoring
          • SNMP
        • Remote Access
          • Audit OpenSSH
          • sshd
        • File Synchronization
          • rsync
        • Messaging
          • RabbitMQ
        • Time
          • NTP
      • PF
        • pfctl cheat sheet
        • PF Anchors
        • PF Filter Rules
        • PF Forwarding
        • PF Lists and Macros
        • PF Load Balancing
        • PF Logging
        • PF NAT
        • PF Options
        • PF Policies
        • PF Shortcuts
        • PF Tables
      • Advanced Networking
        • High Availability and State Replication
        • Multi-WAN and Policy-Based Routing
        • VPN and Cryptographic Tunneling
        • Classic and Lightweight Tunnels
        • IPv6 at Scale
        • QoS and Traffic Shaping
        • MPLS and Label Distribution
        • Network Services at Scale
        • Virtualization and Host Networking
        • Large-Scale L2 and L3 Design
        • Telemetry, Logging, and Flow Export
        • Hardening and Operational Safety
        • Reference Architectures
        • Troubleshooting Playbooks
      • Serial Communication
    • Part V. Miscellaneous
      • Virtualization Cheat Sheet
      • OpenBSD Cheatsheet
      • How-to Guides
        • Complete the First 30 Minutes After Installation
        • Check Hardware Compatibility Before Installing
        • Bootstrap Wi-Fi Firmware Without Ethernet
        • Set Up OpenBSD as a Laptop or Workstation
        • Configure Laptop Power Management
        • Run OpenBSD as a Virtual Machine Guest
        • Install OpenBSD on a VPS or Cloud Server
        • Install OpenBSD with Full-Disk Encryption
        • Troubleshoot Web Browsers on OpenBSD
        • Create an Administrator Account with doas and SSH Keys
        • Establish a Conservative Security Baseline
        • Manage OpenBSD with Ansible
        • Recover Access and Reset the Root Password
        • Collect OpenBSD Diagnostic Evidence
        • Configure Secure Remote Access with OpenSSH
        • Set Up WordPress
        • Build Third-Party Software from Ports
        • Use FIDO Security Keys with OpenSSH
        • Build OpenBSD from Source
        • Configure Wi-Fi Roaming and Wired Failover
        • Dual-Boot OpenBSD on a UEFI System
        • Troubleshoot PF, NAT, and Routing
        • Automate OpenBSD Installation with Autoinstall and PXE
        • Route Multiple VLANs with PF
        • Configure a Road-Warrior WireGuard VPN
        • Troubleshoot Package Installation and Update Failures
        • Route IPv6 Networks without NAT
        • Schedule Recurring Maintenance
        • Plan a Docker Workload Migration to OpenBSD
        • Build a Mail Server with OpenSMTPD, Dovecot, and Rspamd
        • Build a Simple Router and Firewall
        • Monitor an OpenBSD System
        • Back Up and Restore an OpenBSD System
        • Configure softraid RAID1
        • Recover an OpenBSD System That Does Not Boot
        • Install Z shell (zsh)
      • OpenBSD for Linux Users
      • OpenBSD for FreeBSD Users
      • OpenBSD for macOS Users
    • OpenBSD FAQ
    • Package Search
      • Synopsis
      • Example Topology
      • Generate and Protect Keys
      • Configure the Gateway
      • Configure the Gateway Firewall
      • Configure the Roaming Client
      • Verify the Tunnel
      • Add More Clients

      Configure a Road-Warrior WireGuard VPN

      Synopsis #

      OpenBSD includes the wg(4) WireGuard interface in the base system. This guide configures one OpenBSD gateway and one roaming OpenBSD client with a split tunnel. The client reaches a private LAN through the VPN while ordinary Internet traffic continues to use the client’s existing connection.

      WireGuard authenticates peers with public keys. It does not assign addresses, distribute routes, provide user authentication, or create firewall policy. Those functions must be configured explicitly.

      Example Topology #

      The example uses:

      RoleValue
      Gateway public address198.51.100.10
      Gateway UDP port51820
      VPN network10.64.0.0/24
      Gateway VPN address10.64.0.1
      Client VPN address10.64.0.2
      Private LAN behind gateway10.10.10.0/24
      Gateway LAN interfaceem1

      The public address is from a documentation-only range. Replace it with the gateway’s actual stable address or name.

      Generate and Protect Keys #

      On each peer, create a root-only directory and generate a 32-byte private key with openssl(1) :

      # install -d -o root -g wheel -m 700 /etc/wireguard
        # Create a protected key directory
      # openssl rand -base64 32 > /etc/wireguard/wg0.key
        # Generate this peer's private key
      # chmod 600 /etc/wireguard/wg0.key
        # Prevent other accounts from reading it
      

      Never copy a private key to the other peer.

      To obtain the corresponding public key, create a temporary interface with ifconfig(8) , load the private key, record the wgpubkey value, and destroy the interface:

      # ifconfig wg0 create
        # Create a temporary WireGuard interface
      # ifconfig wg0 wgkey "$(cat /etc/wireguard/wg0.key)"
        # Load the local private key
      # ifconfig wg0 | grep wgpubkey
        # Record only the displayed public key
      # ifconfig wg0 destroy
        # Remove the temporary interface
      

      Exchange the public keys through an authenticated channel. Record which device owns each key.

      Configure the Gateway #

      Create a protected /etc/hostname.wg0 on the gateway before inserting any key material. The first command preserves an existing configuration so the settings can be merged instead of replaced:

      # test -e /etc/hostname.wg0 || install -o root -g wheel -m 600 /dev/null /etc/hostname.wg0
        # Create the empty file only when it does not already exist
      # chown root:wheel /etc/hostname.wg0
      # chmod 600 /etc/hostname.wg0
        # Enforce restrictive ownership and permissions before editing
      # vi /etc/hostname.wg0
        # Insert the configuration shown below
      

      Replace CLIENT_PUBLIC_KEY with the client’s public key and replace SERVER_PRIVATE_KEY with the contents of the gateway key file:

      wgport 51820
      wgkey SERVER_PRIVATE_KEY
      wgpeer CLIENT_PUBLIC_KEY wgaip 10.64.0.2/32
      inet 10.64.0.1 255.255.255.0
      up
      

      Confirm the ownership and permissions after editing because the file contains a private key:

      # chown root:wheel /etc/hostname.wg0
      # chmod 600 /etc/hostname.wg0
      

      Enable forwarding persistently in /etc/sysctl.conf:

      net.inet.ip.forwarding=1
      

      Apply the interface and forwarding setting:

      # sh /etc/netstart wg0
        # Create and configure the persistent interface
      # sysctl net.inet.ip.forwarding=1
        # Permit routed IPv4 traffic immediately
      

      Configure the Gateway Firewall #

      Integrate the following fragment into the complete /etc/pf.conf:

      wan = "em0"
      lan = "em1"
      vpn_if = "wg0"
      
      vpn_net = "10.64.0.0/24"
      lan_net = "10.10.10.0/24"
      
      pass in on $wan proto udp to ($wan) port 51820
      pass in on $vpn_if inet proto icmp from $vpn_net to ($vpn_if) icmp-type echoreq
      pass in on $vpn_if from $vpn_net to $lan_net
      pass out on $lan from $vpn_net to $lan_net
      

      The LAN must have a route back to 10.64.0.0/24 through the OpenBSD gateway. If the OpenBSD host is not the LAN’s default router and the LAN router cannot accept that route, source NAT can provide a constrained fallback:

      match out on $lan from $vpn_net to $lan_net nat-to ($lan)
      

      Do not add this translation when normal routing can provide the return path. Parse and load the complete policy, then retain the recovery session:

      # pfctl -nf /etc/pf.conf
        # Check syntax without changing the active policy
      # pfctl -f /etc/pf.conf
        # Load the verified ruleset
      

      Configure the Roaming Client #

      Create a protected /etc/hostname.wg0 on the client before inserting any key material. The first command preserves an existing configuration so the settings can be merged instead of replaced:

      # test -e /etc/hostname.wg0 || install -o root -g wheel -m 600 /dev/null /etc/hostname.wg0
        # Create the empty file only when it does not already exist
      # chown root:wheel /etc/hostname.wg0
      # chmod 600 /etc/hostname.wg0
        # Enforce restrictive ownership and permissions before editing
      # vi /etc/hostname.wg0
        # Insert the configuration shown below
      

      Replace CLIENT_PRIVATE_KEY with the client private key and SERVER_PUBLIC_KEY with the gateway public key:

      wgkey CLIENT_PRIVATE_KEY
      wgpeer SERVER_PUBLIC_KEY wgendpoint 198.51.100.10 51820 wgaip 10.64.0.0/24 wgaip 10.10.10.0/24 wgpka 25
      inet 10.64.0.2 255.255.255.0
      !/sbin/route add -inet 10.10.10.0/24 10.64.0.1
      up
      

      The persistent keepalive is useful when the client is behind stateful NAT. The explicit route sends only the private LAN through the tunnel. Restrict and apply the file:

      # chown root:wheel /etc/hostname.wg0
        # Preserve administrative ownership
      # chmod 600 /etc/hostname.wg0
        # Protect the private key
      # sh /etc/netstart wg0
        # Create the tunnel and private-LAN route
      

      Verify the Tunnel #

      On both peers, inspect the interface:

      # ifconfig wg0
      

      The status shows public keys, allowed IP ranges, endpoints, and recent handshakes. WireGuard is connectionless; no permanent connected state exists.

      From the client, generate tunnel traffic and confirm routing:

      $ route -n get 10.10.10.1
        # Confirm that the private LAN route uses wg0
      $ ping 10.64.0.1
        # Test the WireGuard gateway address
      $ ping 10.10.10.1
        # Test the private LAN path
      

      On the gateway, observe encrypted and decrypted traffic with tcpdump(8) :

      # tcpdump -ni em0 udp port 51820
        # Observe encrypted WireGuard datagrams
      # tcpdump -ni wg0
        # Observe decrypted tunnel traffic
      

      If no handshake appears, compare both public keys, the gateway endpoint, UDP reachability, and PF counters. If the handshake succeeds but LAN traffic fails, inspect forwarding, the client’s route, the gateway’s PF policy, and the LAN return path.

      Add More Clients #

      Generate a unique key pair and VPN address for every client. Add one wgpeer line per public key on the gateway, with a unique /32 allowed IP. Do not reuse a private key or VPN address.

      Removing a peer from /etc/hostname.wg0 and reapplying the interface revokes that peer’s future handshakes. Key rotation requires installing the replacement public key on the opposite peer before removing the old key.

      A full-tunnel VPN requires pinned endpoint reachability, default-route changes, DNS ownership, Internet forwarding, and leak-policy decisions. Those concerns are deliberately outside this split-tunnel procedure.

      See VPN and Cryptographic Tunneling for protocol selection and site-to-site designs.

      Report a bug
      • Synopsis
      • Example Topology
      • Generate and Protect Keys
      • Configure the Gateway
      • Configure the Gateway Firewall
      • Configure the Roaming Client
      • Verify the Tunnel
      • Add More Clients