OPNsense Source Nat
This role creates and manages outbound NAT (Source NAT/SNAT) rules on OPNsense via the REST API.
OPNsense Source NAT Role
Overview
This role creates and manages outbound NAT (Source NAT/SNAT) rules on OPNsense via the REST API. SNAT translates internal private IP addresses to the WAN public IP address, enabling internet access for internal networks. The role creates SNAT rules, applies configuration changes, and provides a summary of configured rules.
Purpose
- Internet Access: Enable outbound internet connectivity for VLANs
- IP Masquerading: Translate private IPs to public WAN IP
- Code as Configuration: Define NAT rules in YAML
- Multi-VLAN Support: Create rules for multiple internal networks
- API-Based: Reliable automation via OPNsense REST API
- Idempotent: Safe to run multiple times
Requirements
- Ansible 2.9 or higher
- OPNsense firewall with API access enabled
- API key and secret stored in Ansible Vault
- Network connectivity to OPNsense (VLAN10)
- OPNsense user with NAT rule permissions
- WAN interface configured with public IP
What is Source NAT (SNAT)?
Source NAT modifies the source IP address of outbound packets:
Without NAT:
Internal host: 192.168.x.x → Internet: example.com
(Internet doesn't route private IPs, packet dropped)
With SNAT:
Internal host: 192.168.x.x → Firewall: Translates to WAN IP
Firewall: [WAN IP] → Internet: example.com
(Internet sees WAN IP, responds to firewall)
Firewall: Translates back → 192.168.x.x
Also known as:
- NAT Overload
- PAT (Port Address Translation)
- IP Masquerading
- Many-to-One NAT
Role Variables
Required Variables
| Variable | Required | Description |
|---|---|---|
vault_opnsense_bjoffrey_user_api_key | Yes | OPNsense API key (in vault) |
vault_opnsense_bjoffrey_user_api_secret | Yes | OPNsense API secret (in vault) |
opnsense_source_nat_rules | Yes | List of SNAT rules to create |
Optional Variables
| Variable | Default | Description |
|---|---|---|
opnsense_source_nat_validate_certs | true | Validate SSL certificates |
Variable Details
opnsense_source_nat_rules
List of Source NAT rules to create.
Structure:
opnsense_source_nat_rules:
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN12 to Internet"
Complete example:
opnsense_source_nat_rules:
- enabled: "1"
interface: wan
source_net: 192.168.x.x/24
destination_net: any
target: wan_address
staticnatport: "0"
log: "0"
description: "VLAN10 Management to Internet"
- enabled: "1"
interface: wan
source_net: 192.168.x.x/24
destination_net: any
target: wan_address
description: "VLAN12 Servers to Internet"
- enabled: "1"
interface: wan
source_net: 192.168.x.x/24
destination_net: any
target: wan_address
description: "VLAN14 Desktops to Internet"
Field descriptions:
| Field | Required | Default | Description |
|---|---|---|---|
enabled | No | "1" | Enable ("1") or disable ("0") |
interface | Yes | - | Outbound interface (usually wan) |
source_net | Yes | - | Source network (CIDR or alias) |
destination_net | No | any | Destination (usually any for internet) |
target | Yes | - | Translation target (usually wan_address) |
staticnatport | No | "0" | Preserve source port ("1") or not ("0") |
log | No | "0" | Log NAT translations |
description | Yes | - | Human-readable description |
Dependencies
This role has no dependencies on other Ansible roles, but requires:
- OPNsense firewall with API enabled
- WAN interface configured
- API key with NAT management permissions
- Ansible Vault for storing API credentials
Example Playbook
Basic Usage
---
- name: Configure OPNsense Source NAT
hosts: localhost
become: false
vars:
opnsense_source_nat_rules:
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "Server VLAN to Internet"
roles:
- opnsense_source_nat
Multiple VLANs
---
- name: Configure Multi-VLAN Source NAT
hosts: localhost
become: false
vars:
opnsense_source_nat_rules:
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN10 Management to Internet"
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN12 Servers to Internet"
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN14 Desktops to Internet"
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN16 WiFi Trusted to Internet"
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN18 WiFi Guest to Internet"
roles:
- opnsense_source_nat
With Logging Enabled
---
- name: Configure Source NAT with Logging
hosts: localhost
become: false
vars:
opnsense_source_nat_rules:
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
log: "1" # Enable logging
description: "Server VLAN to Internet (logged)"
roles:
- opnsense_source_nat
What This Role Does
-
For each rule in opnsense_source_nat_rules:
- Sends POST request to
/api/firewall/source_nat/addRule - Creates SNAT rule with specified parameters
- Tracks changes (reports if rule was saved)
- Sends POST request to
-
If any rules changed:
- Calls apply endpoint (
/api/firewall/source_nat/apply) - Activates rules in firewall
- Calls apply endpoint (
-
Displays summary: Number of rules configured
OPNsense API Endpoints
Add Source NAT Rule
POST /api/firewall/source_nat/addRule
Authorization: Basic (API key:secret)
Content-Type: application/json
Request body:
{
"rule": {
"enabled": "1",
"interface": "wan",
"source_net": "192.168.x.x/24",
"destination_net": "any",
"target": "wan_address",
"staticnatport": "0",
"log": "0",
"description": "VLAN12 to Internet"
}
}
Response:
{
"result": "saved",
"uuid": "12345678-1234-1234-1234-123456789abc"
}
Apply Configuration
POST /api/firewall/source_nat/apply
Activates pending NAT rule changes.
SNAT Rule Fields
interface
Outbound interface for NAT.
Typical value: wan
Other options: Interface name (e.g., wan2 for dual WAN)
source_net
Source network to translate.
Values:
- CIDR:
192.168.x.x/24 - Alias:
Internal_Servers - Single IP:
192.168.x.x
Typical: Internal VLAN networks
destination_net
Destination for rule (where packets are going).
Default: any (internet)
Other options:
- Specific network:
8.8.8.0/24 - Alias:
External_Services
Typical: Use any for general internet access
target
Translation target (what to change source IP to).
Values:
wan_address: Use WAN interface IP (typical)- Specific IP:
1.2.3.4(if multiple WAN IPs) - Alias: For complex scenarios
Typical: wan_address (use WAN IP)
staticnatport
Preserve source port or allow port translation.
Values:
"0": Allow port translation (typical)"1": Preserve source port (rare)
Default: "0"
When to use "1":
- Applications requiring specific source ports
- VoIP, IPSec, FTP (though usually handled by helpers)
When to use "0" (default):
- Most internet traffic
- Allows more concurrent connections
log
Log NAT translations.
Values:
"0": Don’t log (default, recommended)"1": Log all translations
Performance impact: High-traffic NAT logging can fill logs quickly.
When to enable:
- Troubleshooting connectivity
- Security investigation
- Temporary debugging
When to disable (default):
- Normal operation
- High traffic networks
NAT and Firewall Rules
IMPORTANT: NAT rules do not allow traffic. You need both:
- Firewall rule (allow traffic out)
- NAT rule (translate IP)
Example workflow:
# Step 1: Create firewall rule (opnsense_firewall role)
- action: pass
interface: vlan12
source_net: vlan12
destination_net: any
description: "Allow VLAN12 to Internet"
# Step 2: Create NAT rule (this role)
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN12 to Internet NAT"
Both required for internet access.
Automatic Outbound NAT vs Manual
OPNsense supports two NAT modes:
Automatic Outbound NAT
- OPNsense creates rules automatically
- One rule per interface
- Simple but less control
Manual Outbound NAT (This Role)
- Full control over NAT rules
- Per-VLAN rules
- Custom source/destination matching
- Required for complex scenarios
This role creates manual rules. Ensure automatic NAT is disabled:
- Firewall → NAT → Outbound
- Select “Manual Outbound NAT”
Port Translation
With staticnatport: "0" (default):
Multiple hosts can use same source port:
Internal: 192.168.x.x:50000 → WAN: [WAN_IP]:50001
Internal: 192.168.x.x:50000 → WAN: [WAN_IP]:50002
Internal: 192.168.x.x:50000 → WAN: [WAN_IP]:50003
Firewall tracks connections in state table and translates back correctly.
Max concurrent connections: ~65,000 per source IP (port range)
Idempotency
Role is idempotent:
- Creating existing rule updates it
- Only applies configuration if rules changed
- Safe to run repeatedly
First run: Creates rules, reports changed
Subsequent runs: No changes, reports ok
Security Considerations
- API Credentials: Stored in Ansible Vault
- HTTPS: Uses SSL/TLS for API calls
- Basic Auth: API key/secret authentication
- Delegate to Control Node: API calls from mint-vm
- NAT Logs: Disable unless needed (performance/privacy)
- Firewall Rules: NAT alone doesn’t allow traffic
Tags
This role does not define any tags. Use playbook-level tags if needed:
- hosts: localhost
roles:
- opnsense_source_nat
tags:
- opnsense
- nat
- snat
- outbound
Notes
- Role runs on localhost with
delegate_to: mint-vm become: falserecommended (no root needed)- NAT rules require firewall rules to allow traffic
- OPNsense must be in “Manual Outbound NAT” mode
- Role doesn’t delete rules (only creates/updates)
- Target
wan_addressuses WAN interface IP dynamically
Troubleshooting
”Authentication failed” errors
Cause: Invalid API key/secret
Solution: Verify credentials in vault.
NAT rules not working (no internet)
Check:
- Firewall rules exist: Allow VLAN to internet
- NAT mode: Manual outbound NAT enabled
- WAN gateway: System → Gateways → Single
- DNS: System → Settings → General
Test:
# From internal host
ping 8.8.8.8 # Test connectivity
ping google.com # Test DNS
Rules created but no translation
Cause: OPNsense in automatic NAT mode
Solution:
- Firewall → NAT → Outbound
- Select “Manual Outbound NAT rule generation”
- Re-run role to create rules
Port exhaustion (too many connections)
Symptom: Some connections fail during high traffic
Solution:
- Default allows ~65k connections per internal IP
- If exhausted, consider multiple WAN IPs
- Monitor: Firewall → Diagnostics → States
Connections dropping
Check state table:
- Firewall → Diagnostics → States
- Ensure not hitting state table limits
- System → Settings → Tunables (state table size)
Testing the Role
Verify Rules Created
Via OPNsense UI:
- Log into OPNsense
- Firewall → NAT → Outbound
- Should see manual outbound rules
Via API:
curl -u "key:secret" \
https://opnsense-ip/api/firewall/source_nat/searchRule | jq
Test Internet Access
From internal host:
# Test connectivity
ping 8.8.8.8
# Test NAT translation (check external IP)
curl ifconfig.me
# Should show WAN IP, not internal IP
# Test HTTP
curl http://example.com
# Test HTTPS
curl https://example.com
Verify Translation
Check NAT logs (if logging enabled):
- Firewall → NAT → Log Files
- Should see translation entries
Check state table:
- Firewall → Diagnostics → States
- Filter by internal IP
- Should see active states with WAN IP
Best Practices
- One rule per VLAN: Separate rules for each internal network
- Descriptive descriptions: Document rule purpose
- Disable NAT logging: Unless troubleshooting
- Manual NAT mode: Required for this role
- Match firewall rules: Ensure allow rules exist
- Monitor state table: Check for exhaustion
- Document exceptions: Note any special cases
- Test after changes: Verify connectivity
- Version control: Store rules in git
- Regular review: Audit unused rules
Common NAT Scenarios
Basic Internet Access (All VLANs)
opnsense_source_nat_rules:
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN10 to Internet"
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "VLAN12 to Internet"
Guest Network (Internet Only)
opnsense_source_nat_rules:
- interface: wan
source_net: 192.168.x.x/24
target: wan_address
description: "Guest WiFi to Internet"
Combine with firewall rule blocking internal networks.
VPN to Internet
opnsense_source_nat_rules:
- interface: wan
source_net: 10.x.x.x/24 # VPN subnet
target: wan_address
description: "VPN clients to Internet"
Related Roles
This role is often used with:
- opnsense_firewall: Create allow rules before NAT
- opnsense_aliases: Use aliases in rules
License
MIT
Author
Created for homelab infrastructure management.