Aller au contenu principal

Skill: ops-opnsense

Fork

OPNsense configuration via Terraform. Trigger for interfaces, firewall, NAT, DHCP/DNS, aliases.

Configuration

PropertyValue
Contextfork
Allowed toolsRead, Write, Edit, Bash, Glob, Grep
Keywordsops, opnsense

Detailed description

OPNsense Configuration (Terraform)

Complete guide to configure OPNsense declaratively with Terraform and the browningluke/opnsense provider.

When to use this Skill

Activate this skill to:

  • Configure OPNsense network interfaces (WAN, LAN, VLANs)
  • Manage firewall rules
  • Configure NAT and port forwarding
  • Manage DHCP and DNS services
  • Create aliases to simplify rules

Do not use for:

  • Initial OPNsense installation (see manual documentation)
  • VM provisioning (use /ops:ops-proxmox)
  • Advanced OPNsense plugins (HAProxy, Suricata)

Terraform Provider

AttributeValue
Providerbrowningluke/opnsense
Version>= 0.11
Documentationhttps://registry.terraform.io/providers/browningluke/opnsense/latest/docs
GitHubhttps://github.com/browningluke/terraform-provider-opnsense

Provider Configuration

terraform {
required_providers {
opnsense = {
source = "browningluke/opnsense"
version = "~> 0.11"
}
}
}

provider "opnsense" {
uri = var.opnsense_uri # https://192.168.10.1
api_key = var.opnsense_api_key # Sensitive
api_secret = var.opnsense_api_secret # Sensitive
allow_insecure = true # false in production
}

Configuration Patterns

1. Network Interfaces

# WAN interface (DHCP from ISP box)
resource "opnsense_interface" "wan" {
device = "vtnet0"
description = "WAN - Orange Box"
ipv4_type = "dhcp"
block_private = true
block_bogons = true
}

# LAN interface (static IP)
resource "opnsense_interface" "lan" {
device = "vtnet1"
description = "LAN - Local network"
ipv4_type = "static"
ipv4_addr = "192.168.10.1"
ipv4_mask = 24
}

2. Firewall Rules (CRITICAL: Anti-lockout)

# MANDATORY: Anti-lockout rule (ALWAYS first)
resource "opnsense_firewall_filter" "anti_lockout" {
interface = "lan"
direction = "in"
action = "pass"
protocol = "tcp"
source_net = "lannet"
destination_net = "(self)"
destination_port = "443"
description = "ANTI-LOCKOUT: Admin access"
sequence = 1
}

# Allow outbound HTTP/HTTPS
resource "opnsense_firewall_filter" "lan_to_web" {
interface = "lan"
direction = "in"
action = "pass"
protocol = "tcp"
source_net = "lannet"
destination_net = "any"
destination_port = "80,443"
description = "Allow outbound HTTP/HTTPS"
sequence = 10
}

# Block everything else (deny by default)
resource "opnsense_firewall_filter" "lan_block_all" {
interface = "lan"
direction = "in"
action = "block"
protocol = "any"
source_net = "any"
destination_net = "any"
log = true
description = "Block and log everything else"
sequence = 65535
}

3. NAT / Port Forwarding

# Port forward HTTPS to internal web server
resource "opnsense_nat_port_forward" "https_to_web" {
interface = "wan"
protocol = "tcp"
source_net = "any"
source_port = "443"
destination_net = "wanip"
destination_port = "443"
target = "192.168.10.20"
local_port = "443"
description = "HTTPS to web server"
nat_reflection = "enable"
filter_rule_association = "add-associated"
}

# SSH on non-standard port
resource "opnsense_nat_port_forward" "ssh_to_server" {
interface = "wan"
protocol = "tcp"
source_net = "any"
source_port = "2222"
destination_net = "wanip"
destination_port = "2222"
target = "192.168.10.10"
local_port = "22"
description = "SSH to server (port 2222)"
}

4. DHCP/DNS Services

# DHCP server on LAN
resource "opnsense_dhcp_v4_server" "lan" {
interface = "lan"
enabled = true
range_from = "192.168.10.100"
range_to = "192.168.10.200"
gateway = "192.168.10.1"
dns_servers = ["192.168.10.1"]
domain = "home.local"
lease_time = 86400
}

# DHCP reservation
resource "opnsense_dhcp_v4_static_map" "server_web" {
interface = "lan"
mac = "00:11:22:33:44:55"
ipaddr = "192.168.10.20"
hostname = "server-web"
description = "Main web server"
}

# DNS forwarder
resource "opnsense_unbound_forward" "cloudflare" {
enabled = true
host = "1.1.1.1"
port = 53
priority = 10
}

# Local DNS entry
resource "opnsense_unbound_host_override" "server_web" {
enabled = true
hostname = "server"
domain = "home.local"
server = "192.168.10.20"
}

5. Aliases

# Server alias
resource "opnsense_firewall_alias" "servers_web" {
name = "SERVERS_WEB"
type = "host"
content = ["192.168.10.20", "192.168.10.21"]
description = "Internal web servers"
}

# Port alias
resource "opnsense_firewall_alias" "ports_web" {
name = "PORTS_WEB"
type = "port"
content = ["80", "443", "8080"]
description = "Web service ports"
}

# Use in a rule
resource "opnsense_firewall_filter" "to_web_servers" {
interface = "lan"
action = "pass"
protocol = "tcp"
source_net = "lannet"
destination_net = opnsense_firewall_alias.servers_web.name
destination_port = opnsense_firewall_alias.ports_web.name
description = "Access to web servers"
}

Security

Mandatory Rules

  1. Anti-lockout (sequence = 1)

    • ALWAYS present
    • Allows admin access from LAN
    • Never delete
  2. Deny by default (sequence = 65535)

    • Block anything not explicitly allowed
    • Log to detect attempts
  3. Secure credentials

    • Environment variables or terraform.tfvars
    • NEVER in code
    • NEVER committed

Best Practices

PracticeWhy
Use aliasesReadability and maintainability
Document each ruleEasier audit
Log block rulesIntrusion detection
Test in labAvoid lockouts
Backup before applyRollback possible

ISP Box + OPNsense Architecture

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Internet │ │ Orange Box │ │ OPNsense │
│ │────▶│ (192.168.1.1) │────▶│ WAN: DHCP │
│ │ │ DMZ Mode │ │ LAN: .10.1 │
└─────────────────┘ └─────────────────┘ └────────┬────────┘

┌─────────────┴─────────────┐
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ Server │ │ Client │
│ .10.20 │ │ DHCP │
└─────────────┘ └─────────────┘

Orange Box DMZ Configuration

  1. Access the Livebox: http://192.168.1.1
  2. Network > NAT/PAT > DMZ
  3. Enable DMZ to OPNsense WAN IP
  4. All ports are redirected to OPNsense

WireGuard VPN (P3)

WireGuard VPN server configuration:

# WireGuard interface
resource "opnsense_wireguard_server" "vpn" {
name = "wg0"
enabled = true
listen_port = 51820
tunnel_addr = "10.10.10.1/24"
dns = "192.168.10.1"
private_key = var.wireguard_private_key # Sensitive
}

# Peer (VPN client)
resource "opnsense_wireguard_peer" "laptop" {
server_id = opnsense_wireguard_server.vpn.id
name = "laptop-chris"
public_key = "CLIENT_PUBLIC_KEY"
allowed_ips = "10.10.10.2/32"
}

# Firewall rule for WireGuard
resource "opnsense_firewall_filter" "wireguard_in" {
interface = "wan"
direction = "in"
action = "pass"
protocol = "udp"
destination_port = "51820"
description = "WireGuard VPN"
}

Prometheus Monitoring (P3)

node_exporter Installation

  1. Install the node_exporter package via OPNsense
  2. System > Services > node_exporter
  3. Enable and configure the port (9100)

Prometheus Configuration

# prometheus.yml
scrape_configs:
- job_name: 'opnsense'
static_configs:
- targets: ['192.168.10.1:9100']
metrics_path: /metrics

Useful Metrics

MetricDescription
node_network_receive_bytes_totalInbound traffic
node_network_transmit_bytes_totalOutbound traffic
node_cpu_seconds_totalCPU usage
node_memory_MemAvailable_bytesAvailable memory
node_filesystem_avail_bytesDisk space

Troubleshooting

API connection error

# Test the connection
curl -k -u "api-key:api-secret" \
"https://192.168.10.1/api/core/firmware/status"

# Check:
# 1. API enabled (System > Settings > Administration)
# 2. API user with permissions
# 3. Firewall is not blocking
# 4. HTTPS certificate

Lockout (lost access)

# Via Proxmox/local console
pfctl -d # Disable firewall
# Fix via web interface
pfctl -e # Re-enable firewall

Terraform state out of sync

# Import existing resource
terraform import opnsense_firewall_filter.rule "uuid"

# Refresh
terraform refresh

# Force recreation
terraform taint opnsense_firewall_filter.rule
terraform apply

Available Templates

Templates are in .claude/templates/opnsense/:

TemplateDescription
provider-template.tfProvider configuration
interfaces-module.tfWAN/LAN interfaces
firewall-module.tfFirewall rules
nat-module.tfNAT/port forward
services-module.tfDHCP/DNS
aliases-module.tfAddress groups
examples/orange-box-dmz/Complete example

Resources

Automatic triggering

This skill is automatically activated when:

  • The matching keywords are detected in the conversation
  • The task context matches the skill's domain

Triggering examples

  • "I want to ops..."
  • "I want to opnsense..."

Context fork

Fork means the skill runs in an isolated context:

  • Does not pollute the main conversation
  • Results are returned cleanly
  • Ideal for autonomous tasks

See also