Agent skill
pfsense-nat-rule-creation
Create NAT port forward rules on pfSense programmatically via PHP/SSH. Use when: (1) adding port forwards for new K8s services, (2) NAT rules added via PHP don't appear in pfctl output, (3) config_read_array() throws "undefined function" error, (4) destination "wanip" not working in NAT rules, (5) rules saved to config.xml but not loaded into pfctl. Covers the correct PHP array structure, config API differences between pfSense versions, and the required pfctl reload step.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/pfsense-nat-rule-creation
SKILL.md
pfSense NAT Rule Creation via PHP
Problem
Creating NAT port forward rules on pfSense programmatically via SSH/PHP has multiple gotchas around the config API, rule structure, and rule loading.
Context / Trigger Conditions
- Adding a port forward for a new Kubernetes service (e.g., TURN, game server)
- Using
ssh [email protected]+ PHP to automate pfSense config - NAT rules don't appear in
pfctl -snafterwrite_config()+filter_configure() config_read_array()throws "Call to undefined function"- Rules saved to config.xml but pfctl doesn't have them
Solution
Correct PHP for adding NAT rules
<?php
require_once("config.inc");
require_once("filter.inc");
global $config; // NOT config_read_array() — that doesn't exist in pfSense 2.7.x
$config["nat"]["rule"][] = array(
"interface" => "wan",
"ipprotocol" => "inet", // Required! Must be "inet" for IPv4
"protocol" => "tcp/udp", // Or "udp" or "tcp"
"source" => array("any" => ""),
"destination" => array(
"network" => "wanip", // Use "network" => "wanip", NOT "address" => "wanip"
"port" => "3478" // Single port or "start:end" for range
),
"target" => "10.0.20.200", // Internal destination IP
"local-port" => "3478", // Internal port (for ranges, just the start port)
"descr" => "My port forward",
"associated-rule-id" => "pass" // Auto-create firewall pass rule
);
write_config("Description for config history");
filter_configure();
Key gotchas
-
config_read_array()doesn't exist in pfSense 2.7.x. Useglobal $configinstead. -
Destination format: Use
"network" => "wanip", NOT"address" => "wanip"or"address" => "192.168.1.2". The"network"key with"wanip"tells pfSense to resolve the WAN IP dynamically. -
ipprotocolis required: Must include"ipprotocol" => "inet"or rules won't generate in/tmp/rules.debug. -
Port ranges: Use
"port" => "49152:49252"for ranges. The"local-port"should be just the start port — pfSense maps the range automatically. -
Rules may not load immediately: After
write_config()+filter_configure(), rules appear in/tmp/rules.debugbut may not be in pfctl until the next filter reload. Force with:bashpfctl -f /tmp/rules.debug -
SSH quoting: The pfsense.py
phpcommand breaks on\nin strings. For multi-line PHP, write a.phpfile,scpit, and execute:bashscp script.php [email protected]:/tmp/ ssh [email protected] "php /tmp/script.php"
Execution via pfsense.py
For simple single-line PHP (no newlines or backslashes):
python3 .claude/pfsense.py php 'require_once("config.inc"); ...; echo "Done";'
For complex scripts, use scp + ssh as above.
Verification
# Check rules in config
ssh [email protected] "grep 'YOUR_PORT' /cf/conf/config.xml"
# Check generated pf rules
ssh [email protected] "grep 'YOUR_PORT' /tmp/rules.debug"
# Check active pfctl rules
python3 .claude/pfsense.py pfctl "-sn" | grep YOUR_PORT
Notes
- Existing working NAT rules on this pfSense use the same structure (check WireGuard port 51820 as reference)
- The
associated-rule-id: passauto-creates a WAN firewall rule to allow the forwarded traffic - pfSense applies NAT rules across ALL interfaces when using the web UI, but PHP-created rules only apply to the specified interface
- See also:
pfsenseskill for general pfSense management
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?