OPNsense Source Nat

This role creates and manages outbound NAT (Source NAT/SNAT) rules on OPNsense via the REST API.

ARA Ansible Bash DNS HTTPS JSON OPNsense 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

VariableRequiredDescription
vault_opnsense_bjoffrey_user_api_keyYesOPNsense API key (in vault)
vault_opnsense_bjoffrey_user_api_secretYesOPNsense API secret (in vault)
opnsense_source_nat_rulesYesList of SNAT rules to create

Optional Variables

VariableDefaultDescription
opnsense_source_nat_validate_certstrueValidate 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:

FieldRequiredDefaultDescription
enabledNo"1"Enable ("1") or disable ("0")
interfaceYes-Outbound interface (usually wan)
source_netYes-Source network (CIDR or alias)
destination_netNoanyDestination (usually any for internet)
targetYes-Translation target (usually wan_address)
staticnatportNo"0"Preserve source port ("1") or not ("0")
logNo"0"Log NAT translations
descriptionYes-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

  1. 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)
  2. If any rules changed:

    • Calls apply endpoint (/api/firewall/source_nat/apply)
    • Activates rules in firewall
  3. 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:

  1. Firewall rule (allow traffic out)
  2. 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: false recommended (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_address uses 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:

  1. Firewall rules exist: Allow VLAN to internet
  2. NAT mode: Manual outbound NAT enabled
  3. WAN gateway: System → Gateways → Single
  4. 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:

  1. Firewall → NAT → Outbound
  2. Select “Manual Outbound NAT rule generation”
  3. 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:

  1. Log into OPNsense
  2. Firewall → NAT → Outbound
  3. 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):

  1. Firewall → NAT → Log Files
  2. Should see translation entries

Check state table:

  1. Firewall → Diagnostics → States
  2. Filter by internal IP
  3. Should see active states with WAN IP

Best Practices

  1. One rule per VLAN: Separate rules for each internal network
  2. Descriptive descriptions: Document rule purpose
  3. Disable NAT logging: Unless troubleshooting
  4. Manual NAT mode: Required for this role
  5. Match firewall rules: Ensure allow rules exist
  6. Monitor state table: Check for exhaustion
  7. Document exceptions: Note any special cases
  8. Test after changes: Verify connectivity
  9. Version control: Store rules in git
  10. 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"

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.