1. Architecture Overview
pfSense is a FreeBSD-based firewall and router operating system maintained by Netgate (Rubicon Communications, LLC). It provides a full PHP-based web management interface (WebGUI) on top of FreeBSD's pf packet filter, with supporting services for DHCP (Kea), DNS (Unbound/dnsmasq), VPN (OpenVPN, strongSwan/IPsec), captive portal, traffic shaping (ALTQ), and package management — all configured through a single XML configuration store (config.xml).
The project traces its lineage to m0n0wall, an embedded firewall created by Manuel Kasper. pfSense extended m0n0wall for full x86 hardware, adding a richer web GUI, plugin architecture, VPN services, and enterprise features like CARP high availability.
Architecture Diagram
┌─────────────────────────────────────────────────────┐
│ pfSense Host │
│ (FreeBSD OS) │
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ WebGUI │ │ Services │ │ pf │ │
│ │ (PHP/nginx) │ │ (DHCP,DNS, │ │ Firewall │ │
│ │ :443/:80 │ │ NTP,VPN, │ │ Engine │ │
│ │ │ │ captive │ │ │ │
│ │ index.php │ │ portal) │ │ pfctl │ │
│ │ (dashboard) │ │ │ │ (rules, │ │
│ │ │ │ │ │ NAT, │ │
│ │ *.php pages │ │ │ │ states) │ │
│ └──────┬───────┘ └──────┬──────┘ └─────┬──────┘ │
│ │ │ │ │
│ ┌──────┴─────────────────┴────────────────┴──────┐ │
│ │ config.xml (/cf/conf/) │ │
│ │ Single XML configuration store │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ RC Scripts │ │ PHP Shell │ │ Build │ │
│ │ (src/etc/) │ │ Sessions │ │ System │ │
│ │ bootup, │ │ (console │ │ (build.sh │ │
│ │ backup, │ │ commands) │ │ tools/) │ │
│ │ services │ │ │ │ │ │
│ └──────────────┘ └─────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────┘
Component Inventory
| Component | Path | Role |
|---|---|---|
| WebGUI pages | src/usr/local/www/*.php |
Web interface for all configuration and status |
| Core includes | src/etc/inc/*.inc |
PHP libraries: auth, config, filter, VPN, services, utilities |
| RC scripts | src/etc/rc.* |
Shell scripts for bootup, shutdown, backup, service control |
| PHP shell sessions | src/etc/phpshellsessions/ |
Console menu commands (option 12) |
| Sbin utilities | src/usr/local/sbin/ |
Helper scripts for OpenVPN, PPP, DHCP, ACB |
| Dashboard widgets | src/usr/local/www/widgets/ |
Modular dashboard components |
| Captive portal | src/usr/local/captiveportal/ |
Portal authentication endpoint |
| Form classes | src/usr/local/www/classes/Form/ |
PHP form rendering framework |
| CSRF protection | src/usr/local/www/csrf/ |
CSRF token management (csrf-magic) |
| Wizards | src/usr/local/www/wizards/ |
Setup wizard, OpenVPN wizard, traffic shaper wizard |
| Build system | build.sh, tools/ |
Image building, Poudriere, Rector |
| Filesystem services | src/usr/local/pfSense/include/Services/ |
Filesystem abstraction with caching |
| Localization | src/usr/local/share/locale/ |
40+ language .po/.mo files |
| Package metadata | src/usr/local/pkg/ |
Package configuration (e.g., miniupnpd) |
| Config templates | tools/templates/ |
Core package templates, OVF, repo configs |
| Installer tools | tools/installer/ |
Config recovery script |
| Rector rules | tools/rector/ |
Automated code refactoring (PHP AST transforms) |
Technology Stack
| Layer | Technology |
|---|---|
| Operating System | FreeBSD (custom build) |
| Firewall Engine | pf (packet filter) via pfctl |
| Web Server | nginx (serving PHP via PHP-FPM) |
| Application Language | PHP (primary), Shell/Bash (scripts) |
| Configuration Store | XML (/cf/conf/config.xml) |
| DHCP Server | Kea (v4/v6), ISC dhcpd (legacy) |
| DNS | Unbound (resolver), dnsmasq (forwarder) |
| VPN | OpenVPN, strongSwan (IPsec), mpd5 (L2TP/PPTP) |
| Certificates | OpenSSL |
| Authentication | Local DB, RADIUS, LDAP, TOTP |
| High Availability | CARP, pfsync, XMLRPC config sync |
| Traffic Shaping | ALTQ (CBQ, PRIQ, HFSC, FAIRQ) |
| Package Management | FreeBSD pkg (custom repos) |
| Build System | Shell scripts, Poudriere, Rector |
| PHP Dependencies | symfony/cache, symfony/console, nette/utils, simplepie, mikehaertl/php-shellcommand |
Note
The PHP dependencies are managed via Composer. The vendor directory installs to
src/usr/local/pfSense/include/vendor(not the standardvendor/root), keeping third-party code within the pfSense include path.
Data Flow: Firewall Rule Change
1. Admin edits rule in WebGUI (firewall_rules_edit.php)
2. PHP writes updated rule to config.xml (xmlparse.inc)
3. Filter reload triggered (rc.filter_configure)
4. pf rules regenerated from config.xml (filter.inc)
5. pfctl loads new ruleset atomically
6. States table updated; new connections use new rules
Data Flow: Captive Portal Authentication
1. Client connects to captive portal interface
2. pf redirects HTTP to captive portal daemon
3. Client browser loads captiveportal/index.php
4. User submits credentials (local/RADIUS/voucher)
5. captiveportal_authenticate_user() validates
6. On success: portal_allow() adds pf passthrough rule
7. Client gets internet access for session duration
8. On timeout/logout: pf rule removed, session ended
2. System Requirements
Hardware
| Resource | Minimum | Production Recommended |
|---|---|---|
| CPU | 64-bit x86 (amd64) | Multi-core, AES-NI for VPN acceleration |
| RAM | 1 GB | 4-8 GB (more for large state tables, IDS/IPS packages) |
| Storage | 8 GB | 32 GB+ SSD (for logging, packages, RRD) |
| NICs | 2 | 3+ (Intel recommended for driver stability) |
RAM Disk Considerations
pfSense can optionally mount /tmp and /var as RAM disks to reduce disk I/O (important for CF/SD media). Default sizes:
| Disk | Default Size |
|---|---|
/tmp |
40 MB |
/var |
60 MB |
Configured at System > Advanced > Miscellaneous. The system checks available memory before mounting (ramdisk_check_size()) and falls back to disk if insufficient RAM is available.
Important
When RAM disks are enabled, data in
/tmpand/varis volatile. The system automatically backs up RRD, DHCP leases, captive portal databases, logs, alias tables, and package data to the RAM Disk Store (/cf/conf/RAM_Disk_Store/) via periodic RC scripts. The pkg database is symlinked from/root/var/db/pkgto survive reboots.
Supported Platforms
- Bare metal x86-64 hardware
- VMware ESXi / Workstation / Fusion
- Proxmox VE (KVM)
- Microsoft Hyper-V
- Oracle VirtualBox
- QEMU/KVM
- Netgate appliances (purpose-built hardware)
3. Configuration Reference
Configuration Store
All pfSense settings are stored in a single XML file:
| Property | Value |
|---|---|
| Path | /cf/conf/config.xml |
| Format | XML |
| Backup location | /cf/conf/backup/ (automatic backups on change) |
| RAM Disk Store | /cf/conf/RAM_Disk_Store/ (periodic backups of volatile data) |
Configuration is read and written by the PHP include libraries in src/etc/inc/:
config.inc— main config read/writeconfig.lib.inc— config library functionsxmlparse.inc/xmlparse_attr.inc— XML parsingconfig.console.inc— console-based config operationsconfig.gui.inc— GUI-based config operations
The config accessor pattern uses path-based functions:
config_get_path("interfaces/wan/ipaddr")— read a config valueconfig_path_enabled("captiveportal/zone", "httpslogin")— check if a flag is setarray_get_path()/array_set_path()— array navigation helpers
Environment Variables (VPN Authentication)
These environment variables are read by VPN authentication scripts during client connection:
| Variable | Read By | Purpose |
|---|---|---|
common_name |
ipsec.attributes.php |
Certificate CN for IPsec session |
username |
ipsec.auth-user.php, openvpn.auth-user.php |
Authenticating username |
password |
ipsec.auth-user.php, openvpn.auth-user.php |
Authenticating password |
authcfg |
ipsec.auth-user.php |
Comma-separated auth server names |
dev |
openvpn.auth-user.php, openvpn.attributes.php |
OpenVPN tunnel device name |
untrusted_port |
openvpn.auth-user.php, openvpn.attributes.php |
Client source port |
Important
These variables are set by strongSwan (IPsec) and OpenVPN daemons during the authentication callback. They are not user-configurable — they exist in the execution environment of the auth scripts.
Interface Configuration Types
Configured at Interfaces > (interface name) — stored in config.xml under <interfaces>:
| IPv4 Type | Config Value | Description |
|---|---|---|
| Static | IP address string | Manual IPv4 address |
| DHCP | dhcp |
Obtain via DHCP |
| PPPoE | pppoe |
PPP over Ethernet |
| PPTP | pptp |
Point-to-Point Tunneling |
| L2TP | l2tp |
Layer 2 Tunneling |
| PPP | ppp |
Serial/cellular PPP |
| None | (empty) | No IPv4 address |
| IPv6 Type | Config Value | Description |
|---|---|---|
| Static | IPv6 address string | Manual IPv6 address |
| SLAAC | slaac |
Stateless Address Autoconfiguration |
| DHCPv6 | dhcp6 |
Obtain via DHCPv6 |
| 6to4 | 6to4 |
6to4 tunnel |
| 6rd | 6rd |
IPv6 Rapid Deployment |
| Track Interface | track6 |
Track another interface's prefix |
| None | (empty) | No IPv6 address |
Advanced DHCP Client Options
The interface configuration page exposes advanced DHCP client settings (stored under each interface's XML block):
| Setting | Config Key | Purpose |
|---|---|---|
| Protocol Timeout | adv_dhcp_pt_timeout |
DHCP protocol timeout |
| Protocol Retry | adv_dhcp_pt_retry |
Number of retries |
| Select Timeout | adv_dhcp_pt_select_timeout |
Time to wait for offers |
| Reboot | adv_dhcp_pt_reboot |
Time before reboot attempt |
| Backoff Cutoff | adv_dhcp_pt_backoff_cutoff |
Max backoff interval |
| Initial Interval | adv_dhcp_pt_initial_interval |
First retry interval |
| Send Options | adv_dhcp_send_options |
Custom DHCP send options |
| Request Options | adv_dhcp_request_options |
Custom DHCP request options |
| Required Options | adv_dhcp_required_options |
Options required in DHCP offer |
| Option Modifiers | adv_dhcp_option_modifiers |
Override specific DHCP options |
| Config File Override | adv_dhcp_config_file_override_path |
Path to custom dhclient.conf |
| DHCP VLAN Priority | dhcpcvpt |
VLAN CoS priority for DHCP packets |
Wireless Interface Configuration
When an interface is wireless, additional settings are available under the <wireless> XML block:
| Setting | Config Key | Values |
|---|---|---|
| Standard | wireless/standard |
11a, 11b, 11g, 11n, 11ac |
| Mode | wireless/mode |
bss (client), hostap (AP), adhoc |
| SSID | wireless/ssid |
Network name |
| Channel | wireless/channel |
Channel number |
| Channel Width | wireless/channel_width |
20, 40, 80 MHz |
| TX Power | wireless/txpower |
Transmit power level |
| WPA Mode | wireless/wpa/wpa_mode |
1 (WPA), 2 (WPA2), 3 (WPA+WPA2) |
| WPA Key Management | wireless/wpa/wpa_key_mgmt |
WPA-PSK, WPA-EAP |
| WPA Pairwise | wireless/wpa/wpa_pairwise |
CCMP, TKIP |
| Hidden SSID | wireless/hidessid |
enabled/disabled |
| 802.1X Auth Server | wireless/auth_server_addr |
RADIUS server IP |
Build System Configuration
The build system uses build.conf (sample at build.conf.sample) for local overrides. Key variables set in tools/builder_defaults.sh:
| Variable | Purpose |
|---|---|
PRODUCT_NAME |
Product name (pfSense) |
FREEBSD_BRANCH |
FreeBSD source branch to build from |
DEFAULT_ARCH_LIST |
Target architectures |
RSYNCIP, RSYNCUSER, RSYNCPATH |
Snapshot upload destinations |
PKG_RSYNC_HOSTS |
Package repo sync hosts |
PKG_REPO_SERVER_DEVEL |
Dev package repo server |
PKG_REPO_SERVER_RELEASE |
Release package repo server |
4. Deployment & Scaling
Build System
pfSense images are built using build.sh at the repository root. The build system compiles FreeBSD from source with pfSense patches, creates packages via Poudriere, and produces installer images.
Build Targets
./build.sh [options] <target>
| Target | Output |
|---|---|
iso |
ISO installer image |
memstick |
USB installer (VGA console) |
memstickserial |
USB installer (serial console) |
memstickadi |
USB installer (ADI/Netgate appliances, amd64 only) |
ova |
VMware OVA appliance (release builds, amd64 only) |
all |
memstick + memstickserial + memstickadi (+ ova for release) |
none |
Package repo only, no images |
Build Options
| Flag | Short | Effect |
|---|---|---|
--no-buildworld |
-c |
Skip FreeBSD world and kernel build |
--no-cleanobjdir |
-d |
Preserve FreeBSD object directory |
--resume-image-build |
-r |
Skip build, jump to image creation |
--setup |
Install builder prerequisites | |
--update-sources |
Fetch/update FreeBSD sources | |
--build-kernels |
Build all configured kernels | |
--build-kernel NAME |
Build specific kernel | |
--install-extra-kernels NAME |
Add extra kernel to image | |
--snapshots |
Build snapshot images | |
--poudriere-snapshots |
Update Poudriere packages | |
--setup-poudriere |
Install Poudriere and create jails/ports | |
--create-unified-patch |
Create unified FreeBSD patch | |
--update-poudriere-jails |
Update Poudriere jails | |
--update-poudriere-ports |
Update Poudriere ports tree | |
--update-pkg-repo |
Rebuild ports and update pkg repo | |
--upload |
-U |
Upload pkgs/snapshots after build |
--skip-final-rsync |
-i |
Skip final rsync to distribution server |
-V VARNAME |
Print value of build variable | |
-a ARCH_LIST |
Override architecture list |
Note
The build process sources
tools/builder_defaults.shfor default configuration andtools/builder_common.shfor shared functions. Local overrides go inbuild.conf(not tracked in version control).
Build Pipeline
1. clean_builder() — Clean previous build artifacts
2. update_freebsd_sources() — Fetch/update FreeBSD source tree
3. depend_check() — Verify build dependencies
4. make_world() — Build FreeBSD world
5. build_all_kernels() — Compile kernel(s)
6. installkernel() — Install kernel to staging
7. clone_to_staging_area() — Prepare staging filesystem
8. install_pkg_install_ports() — Install pfSense packages
9. core_pkg_create_repo() — Create core package repository
10. create_*_image() — Generate installer images (per target)
11. snapshots_create_sha256() — Generate checksums
12. snapshots_scp_files() — Upload (if --upload)
High Availability (CARP)
pfSense supports active/passive failover using CARP:
- Virtual IPs — Create CARP VIPs at Firewall > Virtual IPs on both nodes
- Config Sync — Configure at System > High Avail. Sync (XMLRPC-based)
- State Sync — Uses
pfsyncto synchronize firewall state tables - DHCP Failover — Automatic pool sharing between primary and backup
Console commands for CARP management (via PHP shell sessions):
enablecarp/disablecarp— toggle CARPenablecarpmaint/disablecarpmaint— enter/exit maintenance mode
Package System
pfSense uses FreeBSD's pkg package manager with custom repositories. Package management:
- Install: WebGUI at System > Package Manager or console
installpkgsession - Remove: WebGUI or console
uninstallpkgsession - Repo config:
src/usr/local/etc/pkg/repos/FreeBSD.conf - Package signing keys:
src/usr/local/share/pfSense/keys/pkg/trusted/
Warning
Do not install packages from the FreeBSD default repositories. pfSense uses its own package repositories with versions tested against the pfSense FreeBSD base. Mixing repos can break the system.
5. Operations
Backup and Recovery
Automated Backups
pfSense includes several RC scripts that periodically back up volatile data to the RAM Disk Store (/cf/conf/RAM_Disk_Store/):
| Script | Backs Up | Archive |
|---|---|---|
rc.backup_rrd.sh |
RRD graph databases (/var/db/rrd/) |
rrd.tgz |
rc.backup_dhcpleases.sh |
DHCP leases (dhcpd + Kea at /var/dhcpd/var/db/, /var/lib/kea/) |
dhcpleases.tgz |
rc.backup_captiveportal.sh |
Captive portal DBs and vouchers (/var/db/captiveportal*.db, voucher*.db) |
Individual .tgz per DB |
rc.backup_logs.sh |
System logs (/var/log/) |
logs.tgz |
rc.backup_aliastables.sh |
Alias table data (/var/db/aliastables/) |
Individual .tgz per table |
rc.backup_packages_data.sh |
Package-specific data directories | Individual .tgz per package |
These scripts are called automatically by ramdisk_make_backup() during shutdown and periodically during operation.
Tip
The backup scripts use timestamp comparison to avoid re-archiving unchanged data. Only modified files are re-compressed.
AutoConfigBackup (ACB)
pfSense includes an automatic cloud backup service (ACB) at Services > Auto Config Backup:
- Encrypts and uploads config.xml to Netgate's servers
- Managed by
src/usr/local/sbin/execacb.phpandacbupload.php - Key management at Services > Auto Config Backup > Settings
Manual Backup/Restore
- WebGUI: Diagnostics > Backup & Restore — download/upload XML
- Console: Config recovery available during installation via
tools/installer/recover_configxml.sh
Config Recovery from Disk
The installer includes a config recovery tool (tools/installer/recover_configxml.sh) that:
- Scans for FreeBSD UFS and ZFS partitions
- Presents a dialog for partition selection
- Mounts the selected partition (with fsck retry for dirty UFS)
- For ZFS: imports the pool (supports both
pfSenseand legacyzrootpool names) - Copies
config.xmland SSH host keys to/tmp/recovered_config/ - Cleanly unmounts and exports
Note
ZFS recovery handles both new-style layout (pool name
pfSensewith/cfas a separate dataset) and old-style layout (pool namezroot).
RAM Disk Management
The RAM disk subsystem (src/etc/rc.ramdisk_functions.sh) provides:
| Function | Purpose |
|---|---|
ramdisk_check_enabled() |
Reads system/use_mfs_tmpvar from config.xml |
ramdisk_is_active() |
Checks enabled AND not failed |
ramdisk_check_size() |
Verifies available RAM can fit requested sizes |
ramdisk_try_mount() |
Mounts tmpfs for /tmp or /var |
ramdisk_make_backup() |
Triggers all backup scripts |
ramdisk_relocate_pkgdb() |
Moves pkg DB between /var and /root/var |
ramdisk_link_pkgdb() |
Creates symlinks for pkg DB on RAM disks |
ramdisk_get_size() |
Returns configured or default size for tmp/var |
Failure handling: If RAM disk mounting fails, a flag file (/conf/ram_disks_failed) is created, preventing further attempts until cleared with ramdisk_reset_status().
Bogon Network Updates
The script src/etc/rc.update_bogons.sh periodically downloads updated bogon (unallocated IP) lists:
- IPv4:
https://files.netgate.com/lists/fullbogons-ipv4.txt - IPv6:
https://files.netgate.com/lists/fullbogons-ipv6.txt
Process:
- Sleep for a random interval (0-86400s) to distribute load
- Download bogon files and checksums
- Verify MD5 checksums
- Check pf table-entries capacity before loading
- Strip private ranges (192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 for v4; fc00::/7 for v6)
- Atomically replace pf tables (
pfctl -t bogons -T replace)
Tip
Custom bogon URLs can be set by creating
/var/etc/bogon_customwithv4url,v6url,v4urlcksum, andv6urlcksumvariables.
Service Management
Services are controlled via the WebGUI (Status > Services) and the console:
| Console Session | Purpose |
|---|---|
svc |
General service control |
restartdhcpd |
Restart DHCP server |
restartipsec |
Restart IPsec |
restartallwan |
Restart all WAN interfaces |
resetwebgui |
Reset WebGUI to defaults |
enablesshd |
Enable SSH daemon |
Monitoring
Dashboard widgets provide real-time monitoring:
- System Information (CPU, RAM, disk, uptime)
- Traffic Graphs (per-interface bandwidth)
- Gateway Status (latency, packet loss)
- Thermal Sensors (CPU temperature)
- S.M.A.R.T. Status (disk health)
Logs at Status > System Logs cover:
- Firewall (pf filter log)
- DHCP
- DNS (Resolver/Forwarder)
- VPN (OpenVPN, IPsec)
- System (general, auth, NTP)
- Packages
6. Performance
Firewall State Table
The pf state table tracks active connections. Key tuning:
- System > Advanced > Firewall & NAT > Firewall Maximum States — default is auto-calculated based on RAM
- Table entries limit — affects bogon table capacity; increase for large bogon lists or alias tables
- Monitor at Diagnostics > States Summary
RAM Disk Sizing
| Filesystem | Default | Increase When |
|---|---|---|
/tmp |
40 MB | Heavy package use, large temp files |
/var |
60 MB | Extensive logging, large DHCP pools, many RRD graphs |
The system checks vm.stats.vm.v_free_count * vm.stats.vm.v_page_size against the total requested RAM disk size before mounting.
Traffic Shaping
ALTQ-based traffic shaping queues are configured at Firewall > Traffic Shaper. Supported schedulers:
| Scheduler | Use Case |
|---|---|
| CBQ (Class-Based Queuing) | General-purpose hierarchical shaping |
| PRIQ (Priority Queuing) | Simple priority-based scheduling |
| HFSC (Hierarchical Fair Service Curve) | Low-latency guarantees with bandwidth sharing |
| FAIRQ (Fair Queuing) | Per-flow fairness |
Note
Traffic shaper wizards at Firewall > Traffic Shaper > Wizards generate ALTQ configurations for common scenarios (VoIP priority, gaming, streaming) based on your WAN bandwidth.
7. Security
Authentication Architecture
pfSense supports multiple authentication backends configured at System > Authentication Servers:
| Method | Implementation |
|---|---|
| Local Database | Users in config.xml, passwords hashed |
| RADIUS | Standard RADIUS protocol with attribute support |
| LDAP | LDAP/LDAPS bind authentication |
Authentication is used by:
- WebGUI login
- OpenVPN client authentication (
openvpn.auth-user.php) - IPsec Xauth/EAP authentication (
ipsec.auth-user.php) - Captive portal
- User Manager privilege system
VPN Authentication Flow
OpenVPN (src/etc/inc/openvpn.auth-user.php):
- Reads credentials from environment variables or GET parameters
- Optionally enforces
strictusercn(username must match certificate CN) - Iterates configured auth servers
- Calls
authenticate_user()against each backend - On success: applies RADIUS attributes (DNS push, route push, framed IP)
- Writes client-specific options to
{tmp_path}/{username}
IPsec (src/etc/inc/ipsec.auth-user.php):
- Reads credentials from environment or GET parameters
- Enforces
strictusercnif configured - Validates against configured auth servers
- For local auth: checks
user-ipsec-xauth-dialinprivilege - Optionally validates group membership (
ipsec/client/auth_groups) - On success: loads
ipsec.attributes.phpfor Cisco ACL rules
Warning
IPsec Xauth users must have the
user-ipsec-xauth-dialinprivilege assigned in System > User Manager. Without it, local authentication succeeds but access is denied.
OCSP Verification
OpenVPN TLS verification (src/etc/inc/openvpn.tls-verify.php) supports:
- Certificate depth validation
- OCSP stapling check against configured OCSP URL
- Per-server OCSP configuration in
config.xml
CSRF Protection
The WebGUI uses csrf-magic (src/usr/local/www/csrf/) for cross-site request forgery protection:
csrf-magic.php— PHP-side token generation and validationcsrf-magic.js— Client-side token injection into forms
Privilege System
User privileges are defined in src/etc/inc/priv.defs.inc and src/etc/inc/priv/user.priv.inc. Each WebGUI page declares its required privilege in a header block:
##|+PRIV
##|*IDENT=page-system-login-logout
##|*NAME=System: Login / Logout / Dashboard
##|*DESCR=Allow access to the 'System: Login / Logout' page and Dashboard.
##|*MATCH=index.php*
##|-PRIV
Privilege checks are enforced by auth_check.inc on every page load.
Certificate Management
Managed at System > Cert. Manager:
- CAs — Create and manage Certificate Authorities
- Certificates — Server, user, and device certificates
- CRLs — Certificate Revocation Lists
- Renewal — Automated certificate renewal (
system_certmanager_renew.php)
SSL configuration for the WebGUI and services uses an internal CA (src/usr/local/share/pfSense/ssl/openssl.cnf). The Netgate CA certificate is bundled at src/usr/local/share/pfSense/ssl/netgate-ca.pem.
RADIUS Dictionary
A custom RADIUS dictionary (src/usr/local/share/pfSense/radius/dictionary.pfsense) defines pfSense-specific RADIUS attributes for enhanced captive portal and VPN integration.
Caution
The
diag_command.phppage provides a shell command prompt in the WebGUI. Access should be strictly limited to administrators. An attacker with access to this page has full root-level control of the system.
8. Developer Internals
Repository Structure
pfsense/pfsense/
├── build.sh # Main build entry point
├── build.conf.sample # Build configuration template
├── composer.json # PHP dependency management
├── rector.php # Rector code transform configuration
├── src/
│ ├── etc/
│ │ ├── inc/ # Core PHP include libraries
│ │ │ ├── auth.inc # Authentication functions
│ │ │ ├── config.inc # Configuration read/write
│ │ │ ├── filter.inc # pf rule generation
│ │ │ ├── interfaces.inc # Network interface management
│ │ │ ├── ipsec.inc # IPsec functions
│ │ │ ├── openvpn.inc # OpenVPN functions
│ │ │ ├── services.inc # Service management
│ │ │ ├── shaper.inc # Traffic shaping
│ │ │ ├── unbound.inc # DNS Resolver config
│ │ │ ├── captiveportal.inc # Captive portal logic
│ │ │ ├── vpn.inc # VPN utilities
│ │ │ ├── util.inc # General utilities
│ │ │ ├── globals.inc # Global variable definitions
│ │ │ ├── functions.inc # Core helper functions
│ │ │ ├── priv.defs.inc # Privilege definitions
│ │ │ ├── xmlparse.inc # XML parser
│ │ │ └── ... # ~60 include files total
│ │ ├── phpshellsessions/ # Console PHP shell commands
│ │ ├── rc.* # RC shell scripts (boot, backup, services)
│ │ └── skel/ # Default user profile files
│ └── usr/local/
│ ├── www/ # WebGUI PHP pages
│ │ ├── index.php # Dashboard
│ │ ├── firewall_*.php # Firewall rules, NAT, aliases
│ │ ├── interfaces*.php # Interface configuration
│ │ ├── services_*.php # Service configuration
│ │ ├── vpn_*.php # VPN configuration
│ │ ├── system_*.php # System settings
│ │ ├── status_*.php # Status/monitoring pages
│ │ ├── diag_*.php # Diagnostic tools
│ │ ├── pkg*.php # Package management
│ │ ├── wizard.php # Wizard engine
│ │ ├── classes/ # Form rendering framework
│ │ ├── widgets/ # Dashboard widgets
│ │ ├── wizards/ # Wizard definitions (XML + PHP)
│ │ ├── csrf/ # CSRF protection
│ │ ├── css/ # Stylesheets (themes)
│ │ ├── js/ # Client-side JavaScript
│ │ └── includes/ # Shared PHP includes
│ ├── sbin/ # System helper scripts
│ ├── captiveportal/ # Captive portal entry point
│ ├── pfSense/include/ # Namespaced PHP classes + vendor
│ ├── pkg/ # Package configurations
│ ├── etc/pkg/repos/ # Package repository config
│ └── share/ # Locale, keys, SSL, Kea scripts
├── tools/
│ ├── builder_common.sh # Shared build functions
│ ├── builder_defaults.sh # Build variable defaults
│ ├── build_snapshots.sh # Snapshot build automation
│ ├── installer/ # Config recovery script
│ ├── rector/ # Code refactoring rules
│ ├── conf/ # Poudriere configuration
│ ├── scripts/ # Utility scripts (privdefs generator, DH params)
│ └── templates/ # Package templates, OVF, repo configs
└── tests/
└── ConfigLibTest.php # Configuration library tests
WebGUI Page Architecture
Each WebGUI page follows a consistent pattern:
- Privilege declaration —
##|+PRIV...##|-PRIVblock - Include files —
guiconfig.inc, feature-specific.incfiles - POST handling — process form submissions, validate, write to config
- GET/display — read config, render HTML with PHP
- Page output —
head.inc(header), page content,foot.inc(footer)
The Form rendering framework (src/usr/local/www/classes/Form/) provides:
Form.class.php— Main form containerSection.class.php— Form sectionsGroup.class.php— Input groupsInput.class.php— Text, number, hidden inputsSelect.class.php— Dropdown selectsCheckbox.class.php— CheckboxesButton.class.php— Submit/action buttonsIpAddress.class.php— IP address input with validationTextarea.class.php— Text areasStaticText.class.php— Read-only display textModal.class.php— Modal dialogs
Dashboard Widget System
Widgets are self-contained PHP files in src/usr/local/www/widgets/widgets/*.widget.php with supporting files:
| Directory | Contains |
|---|---|
widgets/widgets/ |
Widget PHP files (render logic) |
widgets/include/ |
Widget include files (title, title_link vars) |
widgets/javascript/ |
Widget-specific JavaScript |
The dashboard (index.php) loads widgets dynamically based on the user's saved sequence in config.xml. Widgets support:
- Multiple instances of the same widget
- Drag-and-drop reordering via jQuery UI Sortable
- Collapse/expand state persistence
- AJAX refresh via
register_ajax()for periodic updates - Configurable columns (1-4, set at System > General Setup)
Wizard System
Wizards are defined in XML (src/usr/local/www/wizards/*.xml) and processed by wizard.php. The wizard engine supports:
- Multi-step forms with validation
- Include files for PHP logic (
*.incalongside XML) - Pre-built wizards: Setup (
setup_wizard.xml), OpenVPN (openvpn_wizard.xml), Traffic Shaper (traffic_shaper_wizard_*.xml)
Rector Code Transformation
The project uses Rector (PHP AST refactoring tool) for automated code modernization:
Configuration (rector.php):
- Scans
src/directory - Processes
.phpand.incfiles - Skips vendor directory and
priv.defs.inc
Custom Rule — ArrayGetExprRector (tools/rector/src/Rector/Rules/ArrayGetExprRector.php):
- Transforms
$g['key']→g_get('key') - Transforms
$config['path']→config_get_path('path') - Modernizes array access patterns across the codebase
// rector.php configuration
$rectorConfig->ruleWithConfiguration(Rules\ArrayGetExprRector::class,
['g' => 'g_get', 'config' => 'config_get_path']);
Test fixtures in tools/rector/tests/Rector/ArrayGetExprRector/ cover:
- Simple assignment transforms
- Variable key transforms
- Multi-level array access
- Skip conditions (isset checks, LHS assignments)
Filesystem Service Layer
The pfSense\Services\Filesystem namespace (src/usr/local/pfSense/include/Services/Filesystem/) provides an abstraction for filesystem operations with caching:
| Class | Role |
|---|---|
AbstractProvider |
Base class with Symfony Cache adapter (PhpFilesAdapter, 10s TTL) |
SystemProvider |
Queries filesystem via /bin/df, builds hierarchical filesystem tree |
Filesystem |
Individual filesystem data model |
Filesystems |
Collection/manager for filesystem instances |
Dependencies used:
symfony/cache— PhpFilesAdapter for filesystem query cachingnette/utils—Arrays::get()andStringsutilitiesmikehaertl/php-shellcommand— Shell command execution
PHP Shell Sessions (Console Commands)
The console menu option 12 provides a PHP shell with pfSense context. Available commands are implemented as PHP scripts in src/etc/phpshellsessions/:
| Command | Purpose |
|---|---|
changepassword |
Change user password |
enablesshd |
Enable SSH daemon |
gatewaystatus |
Display gateway status |
gitsync |
Sync from git repository |
installpkg / uninstallpkg |
Package management |
listpkg |
List installed packages |
enablecarp / disablecarp |
Toggle CARP |
enablecarpmaint / disablecarpmaint |
CARP maintenance mode |
disabledhcpd / restartdhcpd |
DHCP service control |
restartipsec |
Restart IPsec |
restartallwan |
Restart all WAN interfaces |
resetwebgui |
Reset web configurator |
removeshaper |
Remove traffic shaper configuration |
removepkgconfig |
Remove package configuration |
upgradeconfig |
Run config upgrade routines |
cryptconfig |
Encrypt/decrypt config.xml |
disablereferercheck |
Disable HTTP referer enforcement |
enableallowallwan |
Add pass-all rule to WAN (emergency access) |
externalconfiglocator |
Find config.xml on external media |
generateguicert |
Generate new WebGUI certificate |
pfanchordrill / pftabledrill |
Debug pf anchors/tables |
savevoucher |
Export captive portal vouchers |
Client-Side JavaScript
| File | Purpose |
|---|---|
pfSense.js |
Core WebGUI JavaScript (events, AJAX, form handling) |
pfSenseHelpers.js |
Helper functions for dynamic forms |
polyfills.js |
Browser compatibility polyfills |
traffic-graphs.js |
Real-time traffic graph rendering |
CSS Themes
| Theme | File |
|---|---|
| pfSense (default) | css/pfSense.css |
| pfSense Dark | css/pfSense-dark.css |
| pfSense BETA | css/pfSense-BETA.css |
| pfSense Dark BETA | css/pfSense-dark-BETA.css |
| Compact RED | css/Compact-RED.css |
| Login page | css/login.css |
Font: Roboto (bundled as WOFF2 files under css/fonts/).
Testing
Test location: tests/ConfigLibTest.php
The test suite uses PHPUnit (v12.5.8 via Composer dev dependency). Current test coverage focuses on the configuration library functions.
Running tests:
./vendor/bin/phpunit tests/
Note
Test coverage is minimal — only
ConfigLibTest.phpexists in the repository. The Rector rule tests (tools/rector/tests/) provide additional coverage for the code transformation rules.
Utility Scripts
| Script | Location | Purpose |
|---|---|---|
generate-privdefs.php |
tools/scripts/ |
Generate privilege definitions |
generate_ffdhe.py |
tools/scripts/ |
Generate FFDHE DH parameters |
pfSsh.php |
src/usr/local/sbin/ |
PHP shell entry point for console |
read_xml_tag.sh |
src/usr/local/sbin/ |
Read XML config values from shell |
read_global_var |
src/usr/local/sbin/ |
Read PHP global variables from shell |
Localization
Translations use GNU gettext (.po/.mo files) with Zanata for translation management (zanata.xml in repo root). 40+ locales are supported, stored under src/usr/local/share/locale/{locale}/LC_MESSAGES/pfSense.{po,mo}.
Contributing
- License: Apache License 2.0
- Code style: Rector enforces consistent array access patterns;
.editorconfigdefines indentation rules - Branching: Development occurs in the
pfsense/pfsenserepository - Build verification: Use
build.sh --setupto prepare the build environment, then build and test images
9. Integration Points
RADIUS Integration
pfSense integrates with RADIUS servers for:
- WebGUI authentication
- OpenVPN client authentication (with attribute support for DNS push, route push, framed IP)
- IPsec Xauth authentication (with group membership validation)
- Captive portal authentication (with per-user bandwidth and session attributes)
RADIUS attributes supported include:
nas_identifier— set toopenVPNorxauthIPsecnas_port_type—RADIUS_VIRTUALfor VPNdns-servers— pushed to VPN clientsroutes— pushed as route directivesframed_ip/framed_mask— static IP assignment for VPN clients
LDAP Integration
LDAP authentication is configured at System > Authentication Servers and supports:
- LDAP and LDAPS (SSL/TLS)
- Custom bind DN and search base
- Group membership mapping to pfSense groups
XMLRPC Configuration Sync
High-availability setups use XMLRPC (src/usr/local/www/xmlrpc.php) to synchronize configuration between primary and backup nodes. Configurable at System > High Avail. Sync.
Package Hook System
Packages can register custom code execution points:
- Dashboard hooks:
pfSense_handle_custom_code("/usr/local/pkg/dashboard/pre_dashboard") - Package-specific XML configuration:
src/usr/local/pkg/*.xml - Install/deinstall scripts:
tools/templates/pkg-install.in,pkg-deinstall.in
Kea DHCP
Kea DHCP server scripts (src/usr/local/share/kea/scripts/kea_run4, kea_run6) provide IPv4 and IPv6 DHCP service as the modern replacement for ISC dhcpd.
DH Parameters
Pre-generated Diffie-Hellman parameters are bundled for VPN and SSL use:
| File | Key Size |
|---|---|
src/etc/dh-parameters.1024 |
1024-bit |
src/etc/dh-parameters.2048 |
2048-bit |
src/etc/dh-parameters.3072 |
3072-bit |
src/etc/dh-parameters.4096 |
4096-bit |
src/etc/dh-parameters.6144 |
6144-bit |
src/etc/dh-parameters.8192 |
8192-bit |
Tip
Use 2048-bit or higher DH parameters for production VPN deployments. The pre-generated parameters avoid the lengthy generation process on low-power hardware.