OPNsense Unbound Settings
This role configures the general and advanced Unbound DNS resolver settings in OPNsense via the REST API.
OPNsense Unbound Settings Role
Overview
This role configures the general and advanced Unbound DNS resolver settings in OPNsense via the REST API.
Purpose
- DNS Configuration: Configure Unbound listening ports and interfaces
- Security: Enable DNSSEC validation and privacy settings
- Performance: Configure caching, prefetching, and performance tuning
- Logging: Configure DNS query and reply logging
- Code as Configuration: Define Unbound settings in YAML
- 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 Unbound permissions
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_unbound_settings_general | Yes | General Unbound settings |
opnsense_unbound_settings_advanced | Yes | Advanced Unbound settings |
Optional Variables
| Variable | Default | Description |
|---|---|---|
opnsense_unbound_settings_validate_certs | true | Validate SSL certificates |
General Settings Structure
opnsense_unbound_settings_general:
enabled: "1" # Enable Unbound (1) or disable (0)
port: "53" # DNS listening port
stats: "0" # Enable statistics (0 or 1)
active_interface: # Interfaces to listen on (list)
- opt1 # VLAN10Management
- opt2 # VLAN12Servers
dnssec: "0" # Enable DNSSEC validation (0 or 1)
dns64: "0" # Enable DNS64 (0 or 1)
dns64prefix: "" # DNS64 prefix (if enabled)
noarecords: "0" # Do not generate A records from AAAA
regdhcp: "0" # Register DHCP leases in DNS
regdhcpdomain: "" # Domain for DHCP registrations
regdhcpstatic: "0" # Register DHCP static mappings
noreglladdr6: "0" # Don't register IPv6 link-local
noregrecords: "0" # Don't register system A/AAAA records
txtsupport: "0" # Enable TXT record support
cacheflush: "0" # Flush cache on reload
safesearch: "0" # Enable SafeSearch for Google/YouTube/Bing/DuckDuckGo
local_zone_type: "transparent" # Local zone type
outgoing_interface: # Interfaces for outgoing queries (list)
- wan
enable_wpad: "0" # Enable WPAD support
Advanced Settings Structure
opnsense_unbound_settings_advanced:
hideidentity: "0" # Hide server identity
hideversion: "0" # Hide server version
prefetch: "0" # Enable prefetching
prefetchkey: "0" # Enable DNSKEY prefetching
dnssecstripped: "0" # Log DNSSEC stripped responses
aggressivensec: "0" # Aggressive NSEC use
serveexpired: "0" # Serve expired cache entries
serveexpiredreplyttl: "" # TTL for expired replies
serveexpiredttl: "" # Max TTL for expired entries
serveexpiredttlreset: "0" # Reset TTL to this on serve expired
serveexpiredclienttimeout: "" # Client timeout for serve expired
qnameminstrict: "0" # Strict QNAME minimization
extendedstatistics: "0" # Extended statistics
logqueries: "0" # Log DNS queries
logreplies: "0" # Log DNS replies
logtagqueryreply: "0" # Tag query/reply logs
logservfail: "0" # Log SERVFAIL responses
loglocalactions: "0" # Log local zone actions
logverbosity: "1" # Log verbosity level (0-5)
valloglevel: "0" # Validation log level (0-2)
privateaddress: # RFC1918 and other private addresses (list)
- "10.x.x.x/8"
- "172.16.x.x/12"
- "192.168.x.x/16"
msgcachesize: "" # Message cache size
rrsetcachesize: "" # RRset cache size
outgoingnumtcp: "" # Outgoing TCP connections
incomingnumtcp: "" # Incoming TCP connections
numqueriesperthread: "" # Queries per thread
outgoingrange: "" # Outgoing port range
jostletimeout: "" # Jostle timeout
discardtimeout: "" # Discard timeout
cachemaxttl: "" # Maximum cache TTL
cachemaxnegativettl: "" # Maximum negative cache TTL
cacheminttl: "" # Minimum cache TTL
infrahostttl: "" # Infrastructure host TTL
infrakeepprobing: "0" # Keep probing infrastructure
infracachenumhosts: "" # Infrastructure cache hosts
unwantedreplythreshold: "" # Unwanted reply threshold
Interface Codes
| Code | Interface Name |
|---|---|
opt9 | LAN |
opt1 | VLAN10Management |
opt2 | VLAN12Servers |
opt3 | VLAN14Desktops |
opt4 | VLAN16WifiTrusted |
opt5 | VLAN18WifiGuest |
opt6 | VLAN20WifiCCTV |
opt7 | VLAN22EthernetGuest |
wan | WAN |
opt8 | WG0 (WireGuard) |
Local Zone Types
| Type | Description |
|---|---|
transparent | Normal resolution (default) |
always_nxdomain | Always return NXDOMAIN |
always_refuse | Always refuse queries |
always_transparent | Always resolve normally |
deny | Drop queries silently |
inform | Log and resolve normally |
inform_deny | Log and drop |
nodefault | No default local zones |
refuse | Refuse queries with error |
static | Only serve local data |
typetransparent | Resolve except for local overrides |
Log Verbosity Levels
| Level | Description |
|---|---|
0 | No logging |
1 | Operational information (Default) |
2 | Detailed operational information |
3 | Query level information |
4 | Algorithm level information |
5 | Client identification for cache misses |
Dependencies
This role has no dependencies on other Ansible roles, but requires:
- OPNsense firewall with API enabled
- Unbound DNS service enabled
- API key with Unbound management permissions
- Ansible Vault for storing API credentials
Example Playbook
Basic Usage
---
- name: Configure OPNsense Unbound Settings
hosts: mint-vm
gather_facts: false
vars_files:
- ../../roles/opnsense_unbound_settings/vars/settings.yml
tasks:
- name: Configure Unbound settings
ansible.builtin.include_role:
name: opnsense_unbound_settings
Inline Variables
---
- name: Configure Unbound Settings
hosts: mint-vm
gather_facts: false
vars:
opnsense_unbound_settings_general:
enabled: "1"
port: "53"
stats: "1"
active_interface:
- opt1
- opt2
- opt3
dnssec: "1"
cacheflush: "1"
local_zone_type: "transparent"
outgoing_interface:
- wan
opnsense_unbound_settings_advanced:
prefetch: "1"
aggressivensec: "1"
logqueries: "1"
logreplies: "1"
logverbosity: "1"
privateaddress:
- "10.x.x.x/8"
- "172.16.x.x/12"
- "192.168.x.x/16"
roles:
- opnsense_unbound_settings
What This Role Does
- Fetches existing config via
/api/unbound/settings/get - Compares with desired config (general and advanced settings)
- Updates if different via
/api/unbound/settings/set - Reconfigures Unbound via
/api/unbound/service/reconfigure - Displays summary of settings status
OPNsense API Endpoints
Get Unbound Configuration
GET /api/unbound/settings/get
Authorization: Basic (API key:secret)
Returns current Unbound configuration.
Update Unbound Configuration
POST /api/unbound/settings/set
Authorization: Basic (API key:secret)
Content-Type: application/json
Request body:
{
"unbound": {
"general": {
"enabled": "1",
"port": "53",
"stats": "1",
"active_interface": "opt1,opt2,opt3",
"dnssec": "1",
"local_zone_type": "transparent",
"outgoing_interface": "wan"
},
"advanced": {
"prefetch": "1",
"logqueries": "1",
"logverbosity": "1",
"privateaddress": "10.x.x.x/8,172.16.x.x/12,192.168.x.x/16"
}
}
}
Reconfigure Unbound Service
POST /api/unbound/service/reconfigure
Authorization: Basic (API key:secret)
Applies Unbound configuration changes.
Configuration Examples
Basic DNS Server
opnsense_unbound_settings_general:
enabled: "1"
port: "53"
active_interface:
- opt1
dnssec: "0"
local_zone_type: "transparent"
outgoing_interface:
- wan
opnsense_unbound_settings_advanced:
logverbosity: "1"
privateaddress:
- "10.x.x.x/8"
- "172.16.x.x/12"
- "192.168.x.x/16"
Secure DNS with DNSSEC
opnsense_unbound_settings_general:
enabled: "1"
port: "53"
active_interface:
- opt1
- opt2
- opt3
dnssec: "1"
cacheflush: "1"
local_zone_type: "transparent"
outgoing_interface:
- wan
opnsense_unbound_settings_advanced:
aggressivensec: "1"
dnssecstripped: "1"
logqueries: "1"
logreplies: "1"
logverbosity: "1"
valloglevel: "1"
privateaddress:
- "10.x.x.x/8"
- "172.16.x.x/12"
- "192.168.x.x/16"
High Performance
opnsense_unbound_settings_general:
enabled: "1"
port: "53"
stats: "1"
active_interface:
- opt1
- opt2
dnssec: "1"
cacheflush: "0"
local_zone_type: "transparent"
outgoing_interface:
- wan
opnsense_unbound_settings_advanced:
prefetch: "1"
prefetchkey: "1"
aggressivensec: "1"
serveexpired: "1"
serveexpiredttl: "86400"
logqueries: "0"
logreplies: "0"
logverbosity: "0"
privateaddress:
- "10.x.x.x/8"
- "172.16.x.x/12"
- "192.168.x.x/16"
Security Considerations
- API Credentials: Stored in Ansible Vault
- HTTPS: Uses SSL/TLS for API calls
- Basic Auth: API key/secret authentication
- Certificate Validation: Enabled by default
- DNSSEC: Validates DNS responses when enabled
- Privacy: Can hide server identity and version
Troubleshooting
DNS resolution not working
Cause: Unbound disabled or wrong interface configuration
Solution: Check enabled: "1" and verify active_interface includes correct interfaces
DNSSEC validation failures
Cause: Clock skew or upstream DNS not supporting DNSSEC
Solution: Verify system time is correct, check upstream DNS servers
Slow DNS resolution
Cause: Prefetching disabled or cache too small
Solution: Enable prefetch: "1" and consider increasing cache sizes
Changes not applying
Cause: Reconfigure not triggered
Solution: Verify the role calls /api/unbound/service/reconfigure after updates
Best Practices
- Enable DNSSEC: Use
dnssec: "1"for secure DNS resolution - Configure interfaces: Only listen on necessary internal interfaces
- Use WAN for outgoing: Set
outgoing_interfaceto WAN only - Enable logging: Use
logqueries: "1"for troubleshooting (disable in production) - Private addresses: Keep RFC1918 addresses in
privateaddressfor security - Prefetching: Enable for frequently accessed domains to improve performance
Related Roles
This role works well with:
- opnsense_unbound_dnsbl: Configure DNS blocklists
- opnsense_unbound_host_overrides: Manage DNS host overrides
- opnsense_firewall: Configure firewall rules for DNS traffic
License
MIT
Author
Created for homelab infrastructure management.