OpenBSD Handbook

    Theme
    • Part I. Install & Configure
      • Introduction to OpenBSD
      • Installing OpenBSD
      • The X Window System
      • Networking on OpenBSD
      • System Configuration
      • OpenBSD Basics
      • Managing Software: Packages and Ports
    • Part II. Daily Operations
      • OpenBSD Desktop 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
          • OpenSMTPD Mail Server
          • Dovecot IMAP and POP3 Server
          • Postfix
          • Exim
          • Rspamd
        • Name
          • Unbound DNS Resolver
          • NSD Authoritative DNS Server
          • BIND DNS Server
        • Networking
          • OpenBGPD
          • rad
          • DHCP on OpenBSD
          • slaacd
        • Web
          • Apache HTTP Server
          • nginx Web Server
          • OpenBSD httpd Web Server
          • relayd Reverse Proxy
        • Logging
          • syslogd
        • Monitoring
          • SNMP
        • Remote Access
          • Audit OpenSSH
          • OpenSSH Server
        • File Synchronization
          • rsync
        • Messaging
          • RabbitMQ Message Broker
        • Time
          • NTP Time Synchronization
      • 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 on OpenBSD
        • 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
        • Test OpenBGPD with Two Providers
        • Unlock an Internal Service with an SSH Key
        • 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
    • OpenBSD Architectures
      • OpenBSD Package Search
        • Synopsis
        • Access to One Service
        • Create a Key-Only Access Account
        • Give the Account One PF Permission
        • Sign In and Use the Service
        • Confirm Access Is Revoked
          • A Lost Network Connection
          • Revoke a Key or Account
        • When Access Does Not Behave as Expected

        Unlock an Internal Service with an SSH Key

        Synopsis #

        Use authpf(8) when a service should become reachable only after its user signs in to the gateway. SSH verifies the user’s key, and authpf loads a PF rule for the connecting IP address. The user keeps that SSH session open while using the service. Logout removes the rule and the associated connection states.

        This example grants access to one internal web service using the base OpenBSD tools.

        Access to One Service #

        SSH unlocks one internal serviceThe workstation at 198.51.100.10 signs in with an SSH key to the gateway at 198.51.100.1. While that session remains open, authpf permits the workstation's IP address to reach the web service at 10.20.0.10 on TCP port 8080. Web traffic is routed through the gateway separately from the SSH control session. Logout or SSH detection of a dead connection removes the permission and its connection states.SSH unlocks one internal serviceWorkstation198.51.100.10OpenBSD gateway · 198.51.100.1SSH login → authpf → client-specific PF rulePermit 198.51.100.10 → 10.20.0.10:8080Internal web service10.20.0.10 · TCP port 8080SSH keyKeep login openWeb trafficTCP 8080Allowed whileauthenticatedSession ends → permission and states removedSSH keepalives detect a silent network loss.
        SSH holds the permission open. Web traffic follows its normal route through the gateway to the internal service. Logout, or SSH detecting a dead session, removes the client's permission and connection states.
        ComponentAddress or interface
        Workstation198.51.100.10
        Gateway, client-facing interfacevio0, 198.51.100.1/24
        Gateway, service-facing interfacevio1, 10.20.0.1/24
        Internal web service10.20.0.10, TCP port 8080
        SSH account for opening accesswebgate

        Replace the addresses, interfaces, account name, and port with the actual network plan. The service must already be running, and its return traffic must route through the gateway. Keep an independent administrative session or console available while changing SSH and PF settings.

        The workstation needs a route to the service network through this gateway. For an OpenBSD workstation without that route:

        doas route add -net 10.20.0.0/24 198.51.100.1
        

        The service host needs the reverse route. If its normal gateway does not already provide it, this OpenBSD example adds it:

        doas route add -net 198.51.100.0/24 10.20.0.1
        

        Make those routes persistent through the machines’ normal network configuration. On the gateway, enable IPv4 forwarding and persist net.inet.ip.forwarding=1 in /etc/sysctl.conf:

        doas sysctl net.inet.ip.forwarding=1
        

        This example is IPv4-only. Use HTTPS for sensitive application data: SSH authenticates access, while the service traffic uses its own protocol and encryption.

        Create a Key-Only Access Account #

        On the gateway, create an account whose shell is authpf:

        doas useradd -m -L authpf -s /usr/sbin/authpf webgate
        doas install -d -o webgate -m 700 /home/webgate/.ssh
        

        Transfer the workstation user’s public key to /tmp/webgate.pub on the gateway through an existing administrative channel. Keep the private key on the workstation. Install the public key:

        doas install -o webgate -m 600 /tmp/webgate.pub /home/webgate/.ssh/authorized_keys
        

        Use separate accounts when people require separate permissions or revocation. All keys installed for this account receive its authpf policy.

        Add this block at the end of /etc/ssh/sshd_config, accounting for any existing Match blocks:

        Match User webgate
            AuthenticationMethods publickey
            PasswordAuthentication no
            KbdInteractiveAuthentication no
            DisableForwarding yes
            X11Forwarding no
            PermitTunnel no
            PermitTTY yes
            ClientAliveInterval 15
            ClientAliveCountMax 3
        

        DisableForwarding prevents SSH forwarding from becoming a second route around the service restriction. The alive settings let the gateway detect an unresponsive client instead of leaving its access open indefinitely.

        Check the effective configuration for the access account:

        doas sshd -t
        doas sshd -T -C user=webgate,host=client,addr=198.51.100.10
        

        Confirm AuthenticationMethods publickey, DisableForwarding yes, and the intended alive settings. Check an administrative account separately before reloading:

        doas rcctl reload sshd
        

        Give the Account One PF Permission #

        Create the configuration directory and required configuration file. An empty authpf.conf selects the default anchor and table names:

        doas install -d -o root -g wheel -m 755 /etc/authpf/users/webgate
        doas touch /etc/authpf/authpf.conf
        doas chmod 644 /etc/authpf/authpf.conf
        

        Add webgate on its own line in /etc/authpf/authpf.allow. Preserve any other intended accounts; omit the wildcard * when access should be limited to named users.

        Create /etc/authpf/users/webgate/authpf.rules:

        pass in quick on vio0 inet proto tcp from $user_ip to 10.20.0.10 port 8080 keep state
        

        Leave $user_ip literal in the file. authpf substitutes the source address of the SSH connection when loading the rule. The rule loader must be able to read this file. Keep it owned by root and writable only by root:

        doas chown root:wheel /etc/authpf/users/webgate/authpf.rules
        doas chmod 644 /etc/authpf/users/webgate/authpf.rules
        

        For a dedicated gateway, this minimal /etc/pf.conf permits SSH authentication and uses authpf to control access to the service:

        set skip on lo
        block log all
        table <authpf_users> persist
        pass in on vio0 inet proto tcp from 198.51.100.0/24 to (vio0) port ssh
        anchor "authpf/*" in on vio0 inet
        pass out on vio1 inet proto tcp from 198.51.100.0/24 to 10.20.0.10 port 8080
        

        On an existing firewall, integrate the SSH rule, table, anchor, and service egress permission into the current policy. Preserve management access and remove any broader rule that already permits the protected traffic. A later unrestricted pass rule can defeat the intended gate.

        The /* in the anchor is required: authpf creates a separate child ruleset for each authenticated session. The outbound rule permits forwarding toward the service; the incoming authpf rule is what limits the client source.

        Parse and load the gateway policy:

        doas pfctl -nf /etc/pf.conf
        doas pfctl -f /etc/pf.conf
        

        PF must be enabled. Check doas pfctl -si; run doas pfctl -e if it is disabled.

        Sign In and Use the Service #

        Before signing in, a new connection to the service should fail. On an OpenBSD workstation:

        ftp -4 -V -w 3 -o - http://10.20.0.10:8080/
        

        Then open the access session:

        ssh -4 -t -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 webgate@198.51.100.1
        

        Verify the gateway’s SSH host-key fingerprint through a trusted administrative channel on first use. authpf displays an authenticated greeting rather than a command prompt. Leave this terminal open.

        Open http://10.20.0.10:8080/ in the browser, or repeat the request from another terminal. It should now return the service’s page. Other ports remain governed by the normal firewall policy.

        On the gateway, inspect the active client and its loaded permission:

        doas pfctl -t authpf_users -T show
        doas pfctl -a 'authpf/*' -sr
        

        The table should contain 198.51.100.10, and the loaded rule should permit that address to 10.20.0.10 on port 8080.

        Confirm Access Is Revoked #

        Press Ctrl-C in the authpf SSH terminal. Check the table and anchor again: the client’s entry and permission should be gone. A new request to the service should fail.

        authpf also removes the client’s PF connection states, so an existing service connection cannot retain permission merely because it was established before logout. Inspect service states on the gateway when diagnosing unexpected continued access:

        doas pfctl -ss | grep '10.20.0.10:8080'
        

        A Lost Network Connection #

        Check this once before relying on the setup: sign in, confirm service access, then disconnect the workstation’s network without logging out of SSH. From the gateway console, watch the authpf table and anchor.

        The configured SSH alive checks should detect the dead session and remove its access. Allow time for those checks; loss detection is not instantaneous. Restore the workstation’s network and confirm that a new service request fails until a new authpf login succeeds.

        Revoke a Key or Account #

        Remove a retired public key from authorized_keys to prevent new logins. To deny the account through authpf, create /etc/authpf/banned/webgate; it takes precedence over the allow list.

        Neither change replaces terminating an already authenticated session. Identify that account’s authpf process on the gateway:

        ps -ax -o pid,user,command | grep '[a]uthpf'
        

        Verify the user and client address, then send that specific authpf process SIGTERM. Recheck the rule, table entry, and service states before considering active access revoked.

        When Access Does Not Behave as Expected #

        • SSH succeeds but authpf exits: check /etc/authpf/authpf.conf, the allow/ban files, the per-user rules file, its ownership, and /var/log/authlog.
        • Login works but the service remains unreachable: check the workstation route, gateway forwarding, the service’s return route, the anchor placement, and whether the service is actually listening.
        • The service works without login: look for a broader pass rule, another path to the service, or a pre-existing state. Test with a new connection.
        • Access persists after a network loss: check the effective SSH alive settings for this user and whether the session has actually ended.

        The permission follows the IP address seen by the gateway. Machines sharing that address through NAT, or able to spoof it, may share its access. Use this on networks that control address spoofing. Keep application authentication in place, and use a VPN when a routed, encrypted connection for the individual device is required.

        Report a bug
        • Synopsis
        • Access to One Service
        • Create a Key-Only Access Account
        • Give the Account One PF Permission
        • Sign In and Use the Service
        • Confirm Access Is Revoked
          • A Lost Network Connection
          • Revoke a Key or Account
        • When Access Does Not Behave as Expected