Integrated Firewall
What is PF (Packet Filter)
Think of a firewall like a security guard at the entrance of a building (your network). It checks every visitor (data packet) to decide who gets in or out based on rules you set. PF (Packet Filter) is a powerful and flexible firewall used on systems like FreeBSD to:
Protect your computer or network from hackers
Control which websites or services your network can access
Hide your private network behind a single public IP address (like a phone number for your network)
Prioritize important traffic, like video calls, over less urgent downloads
PF, short for Packet Filter, is a stateful firewall originally developed for OpenBSD and later ported to FreeBSD, among other systems. It enables administrators to filter network traffic, perform NAT, shape bandwidth, prioritize packets, and more.
Key Features
- Stateful filtering
Tracks connection states automatically (e.g., TCP session states)
- Network Address Translation (NAT)
Allows private networks to share a public IP
- Traffic normalization and scrub
Cleans malformed packets and reassembles fragments
- Tables
Maintain sets of addresses for efficient matching (e.g., blacklists)
- Logging
Via pflog, PF can log per-rule packet activity for auditing and troubleshooting
- Optimization
PF internally optimizes rule lookup for performance
Key Terms Explained
- Stateful Filtering
PF remembers connections, like knowing someone who entered your building so they can leave without being checked again
- NAT (Network Address Translation)
Lets multiple devices share one public IP address, like a single phone number for an entire office
- Scrub
Fixes messy or broken data packets to keep your network running smoothly
- Tables
Lists of IP addresses (like a guest list) that PF can quickly check to block or allow traffic
PF History & Evolution
PF was introduced in December 2001 in OpenBSD 3.0, replacing IPFilter due to licensing issues
The initial implementation was by Hartmeier; later enhancements were led by Henning Brauer and Ryan McBride
PF is now a component not just of OpenBSD but also FreeBSD, NetBSD, DragonFlyBSD, and more
PF: High-Level Architecture
- Kernel Module
Handles packet inspection and filtering at OS level
- User Tool (pfctl)
Used to control the firewall—start/stop/status/etc.
- Ruleset File (/etc/pf.conf)
Plain-text configuration file defining macros, tables, filter rules, and options
Enabling PF on FreeBSD
To activate PF at boot and enable logging:
sysrc pf_enable="YES"
sysrc pf_rules="/etc/pf.conf"
sysrc pflog_enable="YES"
sysrc pflog_logfile="/var/log/pflog"
service pf start
Note
These commands ensure PF runs on startup, uses /etc/pf.conf, and logs events to /var/log/pflog
PF Rule Processing Order & Syntax
PF processes configuration statements in this sequence:
[Packet Arrives] → [Options] → [Scrub (Clean Packet)] → [Queueing (Prioritize)] → [Translation (NAT)] → [Filtering (Allow/Block)] → [Packet Allowed or Blocked]
- Options
Sets the overall behavior, like what to do with blocked packets
- Scrub
Cleans up messy packets
- Queueing
Decides which traffic gets priority (e.g., video calls over downloads)
- Translation
Rewrites packet addresses (e.g., NAT for private IPs)
- Filtering
Decides whether to allow or block the packet based on your rules
- quick directive
Stops rule processing upon match, even if later rules would apply
- Default “last match wins”
Without quick, PF evaluates all rules, and the last matching one determines action
pfctl Command Summary
Enabling and Disabling PF
pfctl -e # Enable PF
pfctl -d # Disable PF
Loading/Reloading Rulesets
pfctl -f /etc/pf.conf # Load rules from file
pfctl -n -f /etc/pf.conf # Test rules (syntax only, do not apply)
pfctl -nf /etc/pf.conf # Another way to test syntax
Checking PF Status
pfctl -s info # General PF info
pfctl -s rules # Current firewall rules
pfctl -s nat # NAT rules
pfctl -s state # Active state entries (connections)
pfctl -s queue # ALTQ traffic shaping queues
pfctl -sr # Rules in compact form
pfctl -vv -sr # Verbose rule details
Flushing/Resetting
pfctl -F all # Flush all rules, NAT, states, tables, queues
pfctl -F rules # Flush firewall rules only
pfctl -F nat # Flush NAT rules
pfctl -F state # Clear state table
pfctl -F tables # Clear all tables
pfctl -F queue # Clear all queues
Table Management (Dynamic IP Lists)
pfctl -T show -t blocked_ips # View table contents
pfctl -T add -t blocked_ips 192.168.1.100 # Add IP
pfctl -T delete -t blocked_ips 192.168.1.100 # Remove IP
pfctl -T flush -t blocked_ips # Clear table
Logging & Debugging
pfctl -x loud # Enable verbose logging
pfctl -s log # View logged packets
tcpdump -i pflog0 # Real-time monitoring of PF logs
pf.conf File Anatomy
The ruleset file (/etc/pf.conf) is divided into key sections:
1. Macros
Used to define variables:
ext_if = "igb0"
services = "{ ssh, http, https }"
2. Tables
Efficient IP lists:
table <blocked> persist
3. Options
Set default behavior:
set block-policy drop
set loginterface $ext_if
4. Filter Rules
block all
pass out all keep state
pass in on $ext_if proto tcp from any to any port 22 keep state
Basic “Default Deny” Setup
set skip on lo
block all
pass out inet proto tcp from any to any port { 80 443 } keep state
Tip
This configuration provides a secure default-deny stance while allowing essential outbound web traffic.
Managing ICMP Traffic in PF
What is ICMP?
The Internet Control Message Protocol (ICMP) is vital for network diagnostics. Tools like ping and traceroute use it to:
Report unreachable destinations
Discover path MTU
Confirm whether hosts are reachable
However, allowing all ICMP traffic can pose security risks, as attackers may exploit ICMP to:
Map out your network
Launch denial-of-service or spoofing attacks
Basic Secure ICMP Rules
To allow outbound ICMP for local diagnostics but block unsolicited external probes:
pass inet proto icmp from $ext_if to any keep state
pass inet proto icmp from any to $ext_if keep state
Warning
Unrestricted ICMP access can expose your network to reconnaissance attacks. Always implement appropriate filtering rules.
Overview of pf.conf Usage in the Application (Karios)
Why PF Matters for Karios
PF ensures Karios only allows trusted traffic, like customer visits to your web app, while blocking hackers or malicious requests. It also:
Hides your internal network using NAT
Prioritizes important traffic, like API calls over background tasks
Key Features in Karios’s PF Setup
- External Interface
Karios uses a network interface (like igb0) to talk to the internet. PF rules are tied to this interface
- SSH Access
Allows secure remote login for admins, but only on port 22
- Web Services
Opens ports like 80 (HTTP) and 443 (HTTPS) for web dashboards or APIs
- VXLAN (Virtual Networking)
Supports virtual networks, like connecting multiple servers securely
- Anti-Spoofing
Blocks fake traffic pretending to come from your network
- Blacklisting
Stops known bad IPs (e.g., hackers trying to brute-force your SSH)
Configuration Concepts
Our application uses Packet Filter (PF) as a host-based firewall, primarily to control and secure network traffic for a server or appliance. The configuration ensures both tight inbound control and essential service accessibility, with attention to anti-spoofing, NAT, and layered ICMP/TCP/UDP filtering.
1. External Interface Declaration
Each config declares an ext_if variable (external interface). This improves portability and simplifies interface-based rules:
ext_if = "interface-name"
Basic Defaults and Optimizations
Rules begin with best-practice global options:
Block policy: either return or drop (depending on config)
Scrubbing: enabled to normalize and reassemble fragmented packets
Loopback traffic: excluded via
set skip on loTCP timeout and OS fingerprinting: performance and security optimized
SSH Access
Inbound SSH is permitted using:
pass in proto tcp from any to any port ssh keep state
Note
This allows secure remote administration.
Web & App Services (HTTP, HTTPS, etc.)
A defined set of TCP ports is opened to the world for accessing:
Web interfaces (standard and custom HTTP/S)
Admin panels or dashboards
APIs and microservices
Database ports for external or internal DB access
These port ranges include both:
Standard ports: HTTP, HTTPS, FTP
Custom ranges: likely for proprietary services, internal dashboards, developer tools
Note
The exact ports are generalized but serve app-layer services crucial to the application stack.
Example ports: { 80, 443, 8080, ... }
UDP Traffic
A single UDP port (commonly used for encapsulation protocols like VXLAN) is allowed, indicating:
Use of VXLAN-based tunneling or overlay networking
Possibly container networking or virtualized infrastructure
pass proto udp to port <port>
VXLAN Tunnel ICMP
Separate rules for allowing ICMP over VXLAN interfaces signal:
Health checks or keep alives over virtual networking layers
Secure and monitorable overlay networking
pass in quick on vxlan proto icmp
pass out quick on vxlan proto icmp
Outbound Rules
Outbound traffic is generally open with keep state, which:
Allows connections initiated by the system (e.g., API calls, updates)
Tracks session state for connection security
Anti-Spoofing
Used in all samples:
antispoof for $ext_if inet
Important
This prevents IP spoofing by validating that incoming packets claiming to originate from internal addresses are not entering from the outside.
NAT & Port Redirection (Advanced Sample)
One of the configs introduces:
NAT rules to translate internal IPs to the external interface IP
Port redirection for FTP traffic to a proxy
Anchor usage for modular rule insertion (ftp-proxy/)
Include temp config files to extend or toggle features dynamically
This implies:
Some services may operate behind NAT
FTP services are wrapped or filtered for security
Modular, reloadable configs are used for maintenance or toggles
Blacklisting and Blocking
Blacklists and tables are used to group blocked addresses. SSH brute-force protection is enforced via the <sshguard> table, blocking known bad actors:
block in quick from <sshguard> to any
Simple PF UI Examples and Explanations
Note
Drag and drop functionality is available to move and order the rules. New added rules are highlighted. Select, Reset, Delete functionalities are also available for each rule line.
External Interface Configuration
ext_if="bge0"
Tip
Defines the external interface variable (your network card that connects to the outside world). Without this, PF won’t know which interface the rules apply to.
Note
Interface names may vary: bge0, igb1, igc2 depending on your hardware.
Global Set Options
Block Policy Configuration
set block-policy return
set block-policy drop
- return
Send back an ICMP/RESET so the sender knows the packet was blocked
- drop
Silently discard the packet (looks like the machine doesn’t exist)
Tip
Use drop for stealth; use return for friendlier diagnostics.
Traffic Scrubbing
scrub in ... fragment reassemble
Normalizes traffic, cleans oddities, and reassembles fragmented packets
Prevents attackers from sneaking past firewall rules using fragmented packets
Loopback Interface Skip
set skip on lo
Important
Skips filtering on the loopback interface (lo, 127.0.0.1). Ensures internal services can talk to each other freely.
Ruleset Optimization
set ruleset-optimization basic
set ruleset-optimization normal
Optimizes rule handling for speed
PF processes rules faster but behavior doesn’t change
TCP Timeout Configuration
set timeout { tcp.closing 60, tcp.established 7200 }
Adjusts how long PF keeps TCP states open
Helps free resources if connections die
OS Fingerprinting
set fingerprints "/etc/pf.os"
Enables OS fingerprinting file
Useful for detecting what OS a remote machine runs
Basic Traffic Rules
ICMP Traffic
pass in quick on $ext_if proto icmp ...
pass out quick on $ext_if proto icmp ...
Allows ping requests (ICMP) in and out
Needed for diagnostics (ping). If removed, pings won’t work
Default Blocking
block in all
Warning
Default block for all incoming traffic. Ensures nothing enters unless explicitly allowed.
Outbound Connections
pass out quick keep state
Allows outbound connections and tracks them so replies are auto-allowed
Without it, you couldn’t browse the web or fetch updates
Anti-Spoofing Protection
antispoof for $ext_if inet
Drops packets pretending to come from your own interface/IP
Prevents basic spoofing attacks
SSH Rules
Basic SSH Access
pass in inet proto tcp from any to any port ssh flags S/SA keep state
pass in proto tcp from any to any port 22 keep state
Opens SSH (port 22) for remote logins
Without it, you couldn’t SSH into the machine
Warning
Risk: If left open to the world, bots will constantly try to brute-force login.
SSH Brute-Force Protection
block in quick from <sshguard>
Uses a dynamic table (
<sshguard>) that lists attackers flagged by sshguardHelps block brute-force SSH attackers automatically
Application Ports
Common Port Examples
- Web Services
{80, 443}→ Web (HTTP, HTTPS)
- Database
5432→ PostgreSQL database
- Remote Desktop
5900:5910→ VNC remote desktop range
- Web Applications
8080:8086→ Common for web apps/test servers6900:7900→ App services3000→ Development dashboards (Node apps)
- Monitoring
9090, 9100→ Monitoring tools (Prometheus, printers)
Note
Each pass in proto tcp ... line opens those services. If included, anyone can connect from outside unless additional restrictions are added (like <trusted_nets>).
UDP Rules
VXLAN Support
pass proto udp to port 4789
Allows VXLAN encapsulation traffic (used for virtual networks)
Needed for overlay networking. Without it, VXLAN tunnels break
VXLAN ICMP Rules
pass in quick on vxlan proto icmp
pass out quick on vxlan proto icmp
Allows pinging across VXLAN tunnels
Needed for testing overlay connectivity
NAT / Redirect Rules
Standard NAT Configuration
nat on igc2 inet from !(igc2) -> (igc2:0)
Standard NAT (masquerading). Makes internal traffic appear as if from firewall’s IP
Needed for LAN → Internet access
Port Redirection
rdr pass on igc2 proto tcp from any to any port ftp -> 127.0.0.1 port 8021
Redirects FTP traffic to a local proxy on port 8021
Used for secure FTP proxying
Tables
Trusted Networks
table <trusted_nets> persist { 192.168.116.0/24 }
Creates a whitelist of trusted networks
Can be used in rules to allow access only from trusted IPs
Blacklist Table
table <blacklist> persist { }
Holds IPs you want permanently blocked
Add IPs here to block bad actors
SSH Guard Table
table <sshguard> persist
Managed by sshguard. Stores attacking IPs automatically
Works together with the
block in quick from <sshguard>rule
Include Rules
Modular Configuration
include "/tmp/ftp-proxy_false.conf"
include "/tmp/icmp.conf"
Includes additional rules from other files
Keeps configs modular and easier to manage
Tip
Using include statements allows you to organize complex rulesets into manageable, purpose-specific files.
FAQ Section
- Q: Will PF block everything by default?
A: No, only if your config says
block all. You must explicitly define what to allow.- Q: Does PF block traffic immediately after editing pf.conf?
A: No. You must reload with
pfctl -f /etc/pf.conf.- Q: What is keep state?
A: It means PF remembers a connection, so return traffic is automatically allowed.
Common Mistakes & Gotchas
Warning
Common Configuration Errors:
Forgetting to enable PF after editing
/etc/pf.confLocking yourself out by misconfiguring SSH access
Syntax errors in
pf.confthat silently fail to load the rule setNot using
quickwhen needed (can lead to unexpected rule matches)Forgetting to add
keep statefor stateful protocols like TCP