20 KiB
| title | description | published | date | tags | editor | dateCreated |
|---|---|---|---|---|---|---|
| OpnSense | Grimoire Firewall Configuration | true | 2026-02-23T21:31:26.008Z | markdown | 2026-02-23T21:31:15.244Z |
OPNsense Firewall
Host: OPNsense.localdomain Timezone: America/Chicago Documented: February 23, 2026 Status: Active — AT&T migration in progress
Overview
The network perimeter is protected by an OPNsense firewall running on dedicated hardware with four physical Intel i226-V NICs (igc0–igc3). The firewall operates in a dual-WAN configuration during the transition from the legacy ISP to AT&T fiber, with AT&T becoming the permanent primary WAN. CrowdSec threat intelligence, GeoIP blocking, and Spamhaus DROP/EDROP lists provide layered perimeter security.
Hardware & System
| Parameter | Value |
|---|---|
| Hostname | OPNsense |
| Domain | localdomain |
| Timezone | America/Chicago |
| Language | en_US |
| NAT Outbound Mode | Hybrid |
| System DNS | 8.8.8.8 (Google) — see DNS notes |
| DNS Allow Override | Enabled |
| SSH | Enabled (port 22) |
| Console Menu | Disabled (hardened) |
⚠ DNS Note: The system upstream DNS is set to 8.8.8.8. If dnscrypt-proxy or Unbound is configured, this should be updated to point to localhost or the internal DNS resolver (192.168.5.7). Review before enabling encrypted DNS.
Network Interfaces
| Interface | Label | Physical NIC | IP Address | Role |
|---|---|---|---|---|
| wan | WAN | igc0 | 24.249.193.114/28 | Legacy primary WAN — being retired |
| opt1 | ATT | igc1 | 107.133.34.145/28 | New primary WAN — AT&T fiber |
| lan | LAN | igc3 | 192.168.3.4/29 | Internal LAN management segment |
| opt3 | OPT3 | igc2 | DHCP | Unassigned — spare interface |
| opt2 / wg1 | WG1 | wg1 (virtual) | WireGuard tunnel | WireGuard VPN interface |
| openvpn | OpenVPN | virtual | Tunnel only | OpenVPN (server + client configured) |
| lo0 | Loopback | lo0 | 127.0.0.1/8 | System loopback |
⚠ OPT3 (igc2) is on DHCP and currently unassigned. Disable this interface or assign it a role to reduce unnecessary attack surface.
Gateways & Routing
Active Gateways
| Gateway Name | Interface | IP | Role |
|---|---|---|---|
| WAN_DefRoute | wan (igc0) | 24.249.193.114 | Legacy default route — being retired |
| ATT | opt1 (igc1) | 107.133.34.145 | AT&T — becoming primary |
| LAN_GWv4 | lan (igc3) | 192.168.3.4 | LAN gateway |
NAT Outbound Rules
Outbound NAT runs in Hybrid mode — automatic rules supplemented by manual overrides below.
| Interface | Source | NAT Target | Purpose |
|---|---|---|---|
| opt1 (ATT) | ATT_Out_1 group | opt1ip | Dad's Laptop + 192.168.5.128/25 out ATT |
| wan | MailCow_Ngnx (192.168.5.16) | 24.249.193.115 | Mail server — dedicated WAN IP |
| wan | PNCHarris_Internal | wanip | Internal subnets egress |
| wan | WireGuard (opt2) | — | WireGuard outbound NAT |
✓ The mail server already has a dedicated outbound IP (24.249.193.115) on WAN. This pattern should be replicated on ATT using a dedicated virtual IP from the static block.
Firewall Aliases
Host Aliases
| Alias | IP Address | Used For |
|---|---|---|
| caddy | 192.168.5.10 | Caddy reverse proxy |
| MailCow_Ngnx | 192.168.5.16 | MailCow nginx container |
| JellyFin_Host | 192.168.5.18 | Jellyfin media server |
| ISPConfig_Host | 192.168.4.11 | ISPConfig control panel |
| Dads_Laptop | 192.168.5.176 | Routed out ATT interface |
Network Aliases
| Alias | Value | Used For |
|---|---|---|
| PNCHarris_Internal | 192.168.5.0/25, 192.168.3.0/24 | Primary internal subnets |
| Subnet_5_128_Mask_25 | 192.168.5.128/25 | Upper half of 192.168.5.x |
| ATT_Out_1 | Dads_Laptop + Subnet_5_128_Mask_25 | Traffic routed out ATT interface |
| Family_Subnet | (empty) | Defined but unpopulated |
Port Aliases
| Alias | Ports | Used For |
|---|---|---|
| Web_Services | 80, 443 | HTTP/HTTPS |
| MailCow | 25, 110, 143, 465, 587, 993, 995, 4190 | Full MailCow mail protocol suite |
| ISPConfig | 25, 53, 143, 465, 587, 993, 995, 8080 | ISPConfig mail + DNS + admin |
| JellyFin_Port | 8096, 7096 | Jellyfin HTTP + HTTPS |
| Plex_Port_2 | (empty) | Defined but unpopulated |
Security & Threat Intelligence Aliases
| Alias | Type | Source | Status |
|---|---|---|---|
| SpamHaus_Drop | URL Table | https://www.spamhaus.org/drop/drop.txt | ⚠ Rule DISABLED |
| Spamhaus_edrop | URL Table | https://www.spamhaus.org/drop/edrop.txt | ⚠ Rule DISABLED |
| Blocked_Countries | GeoIP | 70 countries — see GeoIP section | ⚠ Rule DISABLED |
| crowdsec_blacklists | External | CrowdSec IPv4 decisions | ✓ Active |
| crowdsec6_blacklists | External | CrowdSec IPv6 decisions | ✓ Active |
| crowdsec_blocklists | External | CrowdSec IPv4 (duplicate) | ✓ Active |
| crowdsec6_blocklists | External | CrowdSec IPv6 decisions (duplicate) | ✓ Active |
⚠ Critical: Spamhaus DROP, Spamhaus EDROP, and GeoIP country blocking are all defined and populated but their firewall rules are disabled. These are not currently being enforced. Re-enable these rules as an immediate priority.
⚠ There are duplicate CrowdSec alias pairs (
crowdsec_blacklistsandcrowdsec_blocklistsboth handle IPv4). Review and consolidate to avoid confusion.
Firewall Rules
WAN Rules
| Action | Protocol | Source | Destination | Port(s) | Enabled | Description |
|---|---|---|---|---|---|---|
| BLOCK | Any | SpamHaus_Drop | Any | Any | ❌ No | Block Spamhaus DROP list |
| BLOCK | Any | Spamhaus_edrop | Any | Any | ❌ No | Block Spamhaus EDROP list |
| BLOCK | Any | Blocked_Countries | Any | Any | ❌ No | GeoIP country block |
| PASS | TCP | Any | MailCow_Ngnx | MailCow ports | ✓ Yes | Inbound mail |
| PASS | TCP | Any | JellyFin_Host | 8096, 7096 | ✓ Yes | Jellyfin access |
| PASS | UDP | Any | WAN IP | 51820 | ✓ Yes | WireGuard VPN ingress |
| PASS | TCP | Any | MailCow_Ngnx | 80, 443 | ✓ Yes | MailCow webmail |
| PASS | TCP | Any | caddy (192.168.5.10) | 80, 443 | ✓ Yes | Caddy reverse proxy |
⚠ All three block rules at the top of the WAN ruleset are disabled. The firewall is currently not enforcing Spamhaus or GeoIP blocking despite the aliases being populated.
LAN Rules
| Action | Protocol | Source | Destination | Description |
|---|---|---|---|---|
| PASS | Any | ATT_Out_1 group | Any | Dad's Laptop + upper subnet out ATT |
| PASS | Any | LAN subnet | Any | Default allow LAN to any |
| PASS | Any | PNCHarris_Internal | Any | Internal subnets to any |
| PASS | Any | LAN subnet | Any | Default allow LAN IPv6 to any |
| PASS | TCP | PNCHarris_Internal | ISPConfig_Host:ISPConfig | LAN → ISPConfig redirect |
| PASS | TCP | PNCHarris_Internal | ISPConfig_Host:80/443 | LAN → ISPConfig web redirect |
| PASS | TCP | PNCHarris_Internal | caddy:80/443 | LAN → Caddy redirect |
| PASS | TCP | PNCHarris_Internal | MailCow_Ngnx:MailCow | LAN → MailCow redirect |
WireGuard Interface Rules
| Action | Protocol | Source | Destination | Description |
|---|---|---|---|---|
| PASS | Any | Any | Any | Allow all from WireGuard peers — unrestricted |
⚠ The WireGuard interface allows all traffic from all peers with no restrictions. Consider scoping rules per peer as needs are better understood — some remote sites may only need access to specific services.
NAT Port Forwards
WAN Inbound
| Protocol | Public Port(s) | Internal Target | Internal Port(s) | Service |
|---|---|---|---|---|
| TCP | MailCow ports | 192.168.5.16 (MailCow_Ngnx) | MailCow ports | Mail (SMTP/IMAP/POP3/Sieve) |
| TCP | 80, 443 | 192.168.5.16 (MailCow_Ngnx) | 80, 443 | MailCow webmail |
| TCP | 8096, 7096 | 192.168.5.18 (JellyFin_Host) | 8096, 7096 | Jellyfin |
| TCP | 80, 443 | 192.168.5.10 (caddy) | 80, 443 | Caddy (all web services) |
LAN Hairpin (Internal Redirect)
| Protocol | Port(s) | Internal Target | Description |
|---|---|---|---|
| TCP | MailCow ports | 192.168.5.16 | Internal mail access |
| TCP | 80, 443 | 192.168.5.10 (caddy) | Internal web via Caddy |
| TCP | ISPConfig ports | 192.168.4.11 | Internal ISPConfig access |
| TCP | 80, 443 | 192.168.4.11 | Internal ISPConfig web |
VPN
WireGuard
Server: pncharris
| Parameter | Value |
|---|---|
| Tunnel Address | 192.168.32.1/24 |
| Listen Port | 51820 (UDP) |
| DNS for Peers | 192.168.5.7 (internal DNS) |
| Interface | wg1 (OPT2) |
| Status | Enabled |
Peers
| Peer | Tunnel IP | Status | Notes |
|---|---|---|---|
| Obie | 192.168.32.2/32 | ✓ Enabled | |
| pncfishandmore | 192.168.32.3/32 | ✓ Enabled | Business location |
| GLNet (1) | 192.168.32.4/32 | ✓ Enabled | GL.iNet travel router |
| PortaPotty | 192.168.32.5/32 | ✓ Enabled | Remote site |
| GLNet (2) | 192.168.32.6/32 | ✓ Enabled | Second GL.iNet device |
✓ WireGuard peers use the internal DNS server (192.168.5.7) — internal hostnames resolve correctly over VPN.
OpenVPN
An OpenVPN server and client are configured but details were not populated in the backup. Verify status in VPN → OpenVPN in the OPNsense UI.
Security Features
CrowdSec
CrowdSec is installed and fully operational at the firewall level.
| Parameter | Value |
|---|---|
| Agent | Enabled |
| Local API (LAPI) | Enabled — 127.0.0.1:8080 |
| Firewall Bouncer | Enabled |
| Rules | Enabled with logging |
| Firewall Bouncer Verbose | Disabled |
| Manual LAPI Config | Disabled (auto) |
CrowdSec decisions are fed into two alias pairs used in firewall rules:
crowdsec_blacklists/crowdsec6_blacklists— IPv4 and IPv6 block listscrowdsec_blocklists/crowdsec6_blocklists— duplicate set (consolidate)
GeoIP Blocking
GeoIP uses the MaxMind GeoLite2 database with a configured license key. The blocking rule is currently disabled — the alias is populated but not enforced.
70 countries are blocked across four regions:
| Region | Countries |
|---|---|
| Africa (49) | AO, BF, BI, BJ, BW, CD, CF, CG, CI, CM, DJ, DZ, EG, EH, ER, ET, GA, GH, GM, GN, GQ, GW, KE, LR, LS, LY, MA, ML, MR, MW, MZ, NA, NE, NG, RW, SD, SL, SN, SO, SS, ST, SZ, TD, TG, TN, TZ, UG, ZA, ZM, ZW |
| Middle East / Asia (12) | AF, BN, BT, CN, IQ, IR, KG, KP, KW, PH, QA, SA |
| Eastern Europe (4) | BG, RS, RU, RO |
| Latin America (4) | BR, EC, GT, HN |
Spamhaus Blocklists
Both lists are configured as URL table aliases that auto-refresh, but both blocking rules are currently disabled.
| List | URL | Update |
|---|---|---|
| Spamhaus DROP | https://www.spamhaus.org/drop/drop.txt | Auto (URL table) |
| Spamhaus EDROP | https://www.spamhaus.org/drop/edrop.txt | Auto (URL table) |
Internal Network Layout
Known Subnets
| Subnet | Alias | Purpose |
|---|---|---|
| 192.168.3.0/24 | PNCHarris_Internal | LAN management segment |
| 192.168.5.0/25 | PNCHarris_Internal | Primary server subnet |
| 192.168.5.128/25 | Subnet_5_128_Mask_25 | Secondary server subnet / ATT routing |
| 192.168.32.0/24 | — | WireGuard tunnel network |
Key Internal Hosts
| Hostname / Alias | IP | Role |
|---|---|---|
| caddy | 192.168.5.10 | Caddy reverse proxy (all web services) |
| MailCow_Ngnx | 192.168.5.16 | MailCow nginx container |
| JellyFin_Host | 192.168.5.18 | Jellyfin media server |
| ISPConfig_Host | 192.168.4.11 | ISPConfig control panel |
| Dads_Laptop | 192.168.5.176 | Routed via ATT interface |
| Internal DNS | 192.168.5.7 | DNS server (served to WireGuard peers) |
DHCP
DHCP on the LAN interface (192.168.3.0/24) is currently disabled. No KEA or ISC DHCP ranges are active on the firewall. Devices likely use static IPs or a separate DHCP server downstream.
Installed Plugins & Services
The following OPNsense components are present in the configuration:
| Plugin / Service | Status |
|---|---|
| WireGuard | ✓ Active — 1 server, 5 peers |
| CrowdSec | ✓ Active — agent + bouncer + LAPI |
| OpenVPN | Configured — verify in UI |
| IPsec / Swanctl | Present — verify in UI |
| Unbound Plus | Present — verify DNS configuration |
| Kea DHCP | Present — not active on LAN |
| DHCP Relay | Present |
| Netflow | Present |
| IDS/IPS (Suricata) | ❌ Not configured — see hardening plan |
| Proxy | Present — not actively used |
| Traffic Shaper | Present |
| Monit | Present |
| SNMP | Present |
| Syslog | Not configured — see hardening plan |
| Git Backup | Not installed — see hardening plan |
AT&T Migration & Static IP Plan
Current AT&T Interface
Interface: opt1 (igc1) Current IP: 107.133.34.145/28 Block: /28 — up to 14 usable addresses, 5 static IPs allocated for use
Recommended Static IP Allocation
| IP Slot | Dedicated To | Justification |
|---|---|---|
| IP 1 | Mail (MailCow) | Dedicated mail IP protects sender reputation. Never share with web services. Only ports 25/465/587/993/995/4190 NAT to 192.168.5.16. |
| IP 2 | Web / Caddy | All reverse-proxied services via Caddy. Keeps web and mail reputation independent. Replace current WAN NAT for ports 80/443 → 192.168.5.10. |
| IP 3 | WireGuard VPN | Dedicated IP for UDP/51820 only. Cleaner peer configs, stable endpoint, easy to firewall tightly — that IP accepts nothing else. |
| IP 4 | Spare / Jellyfin | Hold in reserve. Best candidate: dedicated Jellyfin IP (currently on WAN with ports 8096/7096). Media servers benefit from a clean IP separate from your main web presence. |
| IP 5 | Admin / Out-of-band | A locked-down IP for emergency remote OPNsense access. Firewall tightly — accept only from WireGuard peers or specific trusted source IPs. Never advertise publicly. |
Implementation Steps
Step 1 — Add Virtual IPs
In OPNsense: Firewall → Virtual IPs → Add
For each additional static IP (IPs 1–5 excluding the interface IP):
- Type:
IP Alias - Interface:
ATT (opt1) - Address:
<static IP>/28 - Description: e.g.
ATT_Mail,ATT_Web,ATT_WireGuard
Step 2 — Create NAT Rules Per Virtual IP
In Firewall → NAT → Port Forward, create new rules on the ATT interface using the virtual IPs as the destination. Example for mail:
Interface: ATT (opt1)
Protocol: TCP
Destination: ATT_Mail virtual IP
Destination Port: MailCow alias
Redirect Target: 192.168.5.16 (MailCow_Ngnx)
Redirect Port: MailCow alias
Repeat for web (→ caddy 192.168.5.10) and WireGuard (UDP/51820).
Step 3 — Update Outbound NAT
Add manual outbound NAT rules so that each internal service exits through its dedicated virtual IP:
Interface: ATT (opt1)
Source: 192.168.5.16 (MailCow_Ngnx)
Target: ATT_Mail virtual IP
Interface: ATT (opt1)
Source: 192.168.5.10 (caddy)
Target: ATT_Web virtual IP
Step 4 — Migrate WireGuard Endpoint
Update peer configs to point to the ATT_WireGuard virtual IP on port 51820. Move the WAN WireGuard rule to ATT interface. Update DNS records if you have a hostname for the WireGuard endpoint.
Step 5 — Update Firewall Block Rules
Re-enable the Spamhaus and GeoIP block rules on the ATT interface. Apply them to the ATT WAN rules the same way they are (currently disabled) on WAN.
Step 6 — DNS Updates
Update all public DNS records to point to the new ATT static IPs:
mail.*domains → ATT_Mail IP*.netgrimoire.com,*.wasted-bandwidth.net, etc. → ATT_Web IP- WireGuard endpoint hostname → ATT_WireGuard IP
Step 7 — Retire WAN (igc0)
Once all services are verified on ATT, disable WAN NAT rules, remove port forward rules on WAN, and eventually disable the interface.
Hardening Plan
The following items are recommended improvements, ordered by priority.
Priority 1 — Re-enable Disabled Security Rules (Immediate)
All three security block rules on the WAN interface are currently disabled. These should be re-enabled immediately as they represent threat intelligence you have already configured but are not using.
- Navigate to Firewall → Rules → WAN
- Find rules:
Block DROP,Block EDROP, and the GeoIP block rule - Click the enable toggle on each rule
- Click Apply Changes
Repeat on the ATT interface once migrated.
Priority 2 — Suricata IDS/IPS
Suricata is built into OPNsense but not yet configured. This is the most significant security gap — without it, there is no deep packet inspection or content-based threat detection.
Setup steps:
- Go to Services → Intrusion Detection → Administration
- Enable IDS/IPS, set interface to ATT (and WAN while active)
- Set mode to IPS (inline blocking, not just alerting)
- Under Download, enable the following rulesets:
ET Open— Proofpoint Emerging Threats (free, comprehensive)Abuse.ch SSL Blacklist— malicious SSL certificate detectionFeodo Tracker— botnet C2 blocking
- Under Policies, set default action to
dropfor high-severity rules - Click Download & Update Rules, then Apply
✓ Suricata complements CrowdSec well. CrowdSec handles IP reputation; Suricata handles traffic content inspection. They do not overlap.
Priority 3 — Additional Blocklists
Add these URL table aliases to supplement Spamhaus DROP/EDROP:
| List | URL | Purpose |
|---|---|---|
| Feodo Tracker | https://feodotracker.abuse.ch/downloads/ipblocklist.txt | Botnet C2 IPs |
| Abuse.ch SSLBL | https://sslbl.abuse.ch/blacklist/sslipblacklist.txt | Malicious SSL IPs |
| Emerging Threats | https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt | ET block list |
For each: Firewall → Aliases → Add, type URL Table, set refresh to 1 day. Then add a WAN block rule using each alias as the source.
Priority 4 — dnscrypt-proxy (Encrypted DNS)
Encrypts DNS queries leaving the firewall and adds DNS-level malware/tracking blocklists.
- Go to System → Firmware → Plugins, install
os-dnscrypt-proxy - Navigate to Services → DNSCrypt-Proxy
- Enable, set listen port to
5353 - Select resolvers:
cloudflare,quad9-dnscrypt-ip4-nofilter-pri(or similar) - Enable DNSSEC validation
- Update System → Settings → General — set DNS server to
127.0.0.1:5353 - Disable
DNS Allow Overrideso the ISP cannot push DNS changes
Priority 5 — os-git-backup
Automatically commits every OPNsense config change to a Git repository. Invaluable for auditing changes after an incident and for rapid recovery.
- Go to System → Firmware → Plugins, install
os-git-backup - Navigate to System → Configuration → Git Backup
- Configure a Forgejo repository on Netgrimoire as the remote
- Set SSH key for authentication
- Enable automatic backup on config change
Priority 6 — Syslog to Graylog
Syslog is not currently configured. Sending firewall logs to Graylog (already running at http://graylog:9000) enables centralized log analysis and alerting.
- Go to System → Settings → Logging → Remote
- Add a syslog destination:
graylog:514(UDP) or use GELF input on Graylog - Enable logging for: Firewall, DHCP, VPN, Authentication, CrowdSec
Known Issues & Action Items
| Item | Priority | Notes |
|---|---|---|
| Spamhaus DROP rule disabled | 🔴 High | Re-enable in Firewall → Rules → WAN |
| Spamhaus EDROP rule disabled | 🔴 High | Re-enable in Firewall → Rules → WAN |
| GeoIP block rule disabled | 🔴 High | Re-enable in Firewall → Rules → WAN |
| Suricata not configured | 🔴 High | Most significant security gap — configure with ET Open rules |
| Duplicate CrowdSec aliases | 🟡 Medium | crowdsec_blacklists and crowdsec_blocklists both do IPv4 — consolidate |
| WireGuard rule too permissive | 🟡 Medium | Allow-all from peers — scope per peer when needs are known |
| OPT3 interface unassigned | 🟡 Medium | Disable or assign a role |
| System DNS points to Google | 🟡 Medium | Should point to internal resolver or localhost after dnscrypt-proxy setup |
| No syslog configured | 🟡 Medium | Forward to Graylog for centralized logging |
| os-git-backup not installed | 🟡 Medium | Install for config change auditing |
| OpenVPN config unpopulated | 🟢 Low | Verify status — backup shows server+client but no details |
| ATT migration incomplete | 🟢 Low | In progress — see migration plan above |
| Family_Subnet alias empty | 🟢 Low | Populate or remove |
| Plex_Port_2 alias empty | 🟢 Low | Populate or remove |
| DHCP disabled on LAN | 🟢 Info | Intentional if using static IPs — verify |
Related Documentation
- Caddy Reverse Proxy — services exposed through the firewall
- MailCow Mail Server — mail server behind the firewall, dedicated WAN IP
- WireGuard VPN — peer configuration and access
- Graylog — target for firewall syslog
- CrowdSec — threat intelligence integration