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
        • Load Balancing
          • Build Redundant Load-Balanced Gateways
          • Load Balance Services with NAT or DSR
          • Build an HA HTTPS Load Balancer
      • 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
        • Installation and First Boot
          • Complete the First 30 Minutes After Installation
          • Check Hardware Compatibility Before Installing
          • Bootstrap Wi-Fi Firmware Without Ethernet
          • Run OpenBSD as a Virtual Machine Guest
          • Install OpenBSD on a VPS or Cloud Server
          • Install OpenBSD with Full-Disk Encryption
          • Dual-Boot OpenBSD on a UEFI System
          • Automate OpenBSD Installation with Autoinstall and PXE
        • Desktops and Laptops
          • Set Up OpenBSD as a Laptop or Workstation
          • Configure Laptop Power Management
          • Troubleshoot Web Browsers on OpenBSD
          • Use Plan 9 Tools on OpenBSD
          • Build a Minimal Desktop with suckless Tools
          • Install Z shell (zsh)
        • Networking and Remote Access
          • Create an Administrator Account with doas and SSH Keys
          • Configure Secure Remote Access with OpenSSH
          • Use FIDO Security Keys with OpenSSH
          • Configure Wi-Fi Roaming and Wired Failover
          • Troubleshoot PF, NAT, and Routing
          • Route Multiple VLANs with PF
          • Configure a Road-Warrior WireGuard VPN
          • Route IPv6 Networks without NAT
          • Test OpenBGPD with Two Providers
          • Unlock an Internal Service with an SSH Key
          • Build a Simple Router and Firewall
        • Servers and Services
          • Set Up WordPress on OpenBSD
          • Build a Mail Server with OpenSMTPD, Dovecot, and Rspamd
        • Security, Maintenance and Recovery
          • Establish a Conservative Security Baseline
          • Recover Access and Reset the Root Password
          • Collect OpenBSD Diagnostic Evidence
          • Troubleshoot Package Installation and Update Failures
          • Schedule Recurring Maintenance
          • Monitor an OpenBSD System
          • Back Up and Restore an OpenBSD System
          • Configure softraid RAID1
          • Recover an OpenBSD System That Does Not Boot
        • Building, Automation and Virtual Machines
          • Manage OpenBSD with Ansible
          • Build Third-Party Software from Ports
          • Build OpenBSD from Source
          • Plan a Docker Workload Migration to OpenBSD
      • OpenBSD for Linux Users
      • OpenBSD for FreeBSD Users
      • OpenBSD for macOS Users
    • OpenBSD FAQ
    • OpenBSD Architectures
      • OpenBSD Package Search
        • Introduction
        • Assigning Tags to Packets
        • Checking for Applied Tags
        • Policy Filtering
        • Tagging Ethernet Frames

        PF Policies

        Introduction #

        Packet tags are internal identifiers used by later filter and translation rules. Tag packets when they enter the firewall, then use those tags to apply a policy as packets leave.

        Assigning Tags to Packets #

        To add a tag to a packet, use the ’tag’ keyword:

        pass in on $int_if all tag INTERNAL_NET
        

        The tag ‘INTERNAL_NET’ will be added to any packet which matches the above rule.

        A tag can also be assigned using a macro . For instance:

        name = "INTERNAL_NET"
        pass in on $int_if all tag $name
        

        There are a set of predefined macros which can also be used.

        • ‘$if’ - The interface
        • ‘$srcaddr’ - Source IP address
        • ‘$dstaddr’ - Destination IP address
        • ‘$srcport’ - The source port specification
        • ‘$dstport’ - The destination port specification
        • ‘$proto’ - The protocol
        • ‘$nr’ - The rule number

        These macros are expanded at ruleset load time and NOT at runtime.

        Tagging follows these rules:

        • Tags are “sticky.” Once a tag is applied to a packet by a matching rule, it is never removed. It can, however, be replaced with a different tag.
        • Because of a tag’s “stickiness,” a packet can have a tag even if the last matching rule doesn’t use the ’tag’ keyword.
        • A packet is only ever assigned a maximum of one tag at a time.
        • Tags are internal identifiers. Tags are not sent out over the wire.
        • Tag names can be up to 63 characters long.

        Take the following ruleset as an example.

        pass in on $int_if tag INT_NET
        pass in quick on $int_if proto tcp to port 80 tag INT_NET_HTTP
        pass in quick on $int_if from 192.168.1.5
        
        • Packets coming in on ‘$int_if’ will be assigned a tag of ‘INT_NET’ by rule #1.
        • TCP packets coming in on ‘$int_if’ and destined for port 80 will first be assigned a tag of ‘INT_NET’ by rule #1. That tag will then be replaced with the ‘INT_NET_HTTP’ tag by rule #2.
        • Packets coming in on ‘$int_if’ from 192.168.1.5 will be tagged one of two ways. If the packet is destined for TCP port 80 it will match rule #2 and be tagged with ‘INT_NET_HTTP’. Otherwise, the packet will match rule #3 but will be tagged with ‘INT_NET’. Because the packet matches rule #1, the ‘INT_NET’ tag is applied and is not removed unless a subsequently matching rule specifies a tag (this is the “stickiness” of a tag).

        Checking for Applied Tags #

        To check for previously applied tags, use the ’tagged’ keyword:

        pass out on egress tagged INT_NET
        

        Outgoing packets on the external interface must have the ‘INT_NET’ tag to match the above rule. Inverse matching can also be done by using the ‘!’ operator:

        pass out on egress ! tagged WIFI_NET
        

        Policy Filtering #

        Policy filtering takes a different approach to writing a filter ruleset. A policy is defined which sets the rules for what types of traffic is passed and what types are blocked. Packets are then classified into the policy based on the traditional criteria of source/destination IP address/port, protocol, etc. For example, examine the following firewall policy:

        • Traffic from the internal LAN to the Internet is permitted (LAN_INET) and must be translated (LAN_INET_NAT).
        • Traffic from the internal LAN to the DMZ is permitted (LAN_DMZ).
        • Traffic from the Internet to servers in the DMZ is permitted (INET_DMZ).
        • Traffic from the Internet that’s being redirected to spamd(8) is permitted (SPAMD).
        • The examples also permit LAN access to the firewall itself. Add explicit service restrictions if this is not intended; locally delivered packets do not traverse the outbound policy rules. All other traffic is blocked.

        Note how the policy covers all traffic that will be passing through the firewall. The item in parenthesis indicates the tag that will be used for that policy item.

        Rules now need to be written to classify packets into the policy.

        block all
        match out on egress inet tag LAN_INET_NAT tagged LAN_INET nat-to (egress)
        pass in  on $int_if from $int_net tag LAN_INET
        pass in  on $int_if from $int_net to $dmz_net tag LAN_DMZ
        pass in  on egress proto tcp to $www_server port 80 tag INET_DMZ
        match in  on egress proto tcp from <spamd> to port smtp tag SPAMD rdr-to 127.0.0.1 port 8025
        

        Use match to keep the nat-to and rdr-to settings when later rules allow the packet.

        Now the rules that define the policy are set.

        pass in  quick on egress tagged SPAMD
        pass out quick on egress tagged LAN_INET_NAT
        pass out quick on $dmz_if tagged LAN_DMZ
        pass out quick on $dmz_if tagged INET_DMZ
        

        Now that the whole ruleset is setup, changes are a matter of modifying the classification rules. For example, if a POP3/SMTP server is added to the DMZ, it will be necessary to add classification rules for POP3 and SMTP traffic, like so:

        mail_server = "192.168.0.10"
        [...]
        pass in on egress proto tcp to $mail_server port { smtp, pop3 } tag INET_DMZ
        

        Email traffic will now be passed as part of the INET_DMZ policy entry.

        The complete ruleset:

        int_if      = "dc0"
        dmz_if      = "dc1"
        int_net     = "10.0.0.0/24"
        dmz_net     = "192.168.0.0/24"
        www_server  = "192.168.0.5"
        mail_server = "192.168.0.10"
        
        table <spamd> persist file "/etc/spammers"
        # classification -- classify packets based on the defined firewall policy.
        block all
        match out on egress inet tag LAN_INET_NAT tagged LAN_INET nat-to (egress)
        pass in on $int_if from $int_net tag LAN_INET
        pass in on $int_if from $int_net to $dmz_net tag LAN_DMZ
        pass in on egress proto tcp to $www_server port 80 tag INET_DMZ
        pass in on egress proto tcp to $mail_server port { smtp, pop3 } tag INET_DMZ
        match in on egress proto tcp from <spamd> to port smtp tag SPAMD rdr-to 127.0.0.1 port 8025
        
        # policy enforcement -- pass/block based on the defined firewall policy.
        pass in  quick on egress tagged SPAMD
        pass out quick on egress tagged LAN_INET_NAT
        pass out quick on $dmz_if tagged LAN_DMZ
        pass out quick on $dmz_if tagged INET_DMZ
        

        Tagging Ethernet Frames #

        Tagging can be performed at the ethernet level if the machine doing the tagging/filtering is also acting as a bridge. By creating bridge filter rules that use the ’tag’ keyword, PF can be made to filter based on the source or destination MAC address. Bridge(4) rules are created using the ifconfig command. Example:

        doas ifconfig bridge0 rule pass in on fxp0 src 0:de:ad:be:ef:0 tag USER1
        

        And then in ‘pf.conf’:

        pass in on fxp0 tagged USER1
        
        Report a bug
        • Introduction
        • Assigning Tags to Packets
        • Checking for Applied Tags
        • Policy Filtering
        • Tagging Ethernet Frames