diff --git a/Netgrimoire/Network/Security/OpnSense_Firewall.md b/Netgrimoire/Network/Security/OpnSense_Firewall.md new file mode 100644 index 0000000..4d5395d --- /dev/null +++ b/Netgrimoire/Network/Security/OpnSense_Firewall.md @@ -0,0 +1,508 @@ +--- +title: OpnSense +description: Grimoire Firewall Configuration +published: true +date: 2026-02-23T21:31:15.244Z +tags: +editor: markdown +dateCreated: 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_blacklists` and `crowdsec_blocklists` both 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 lists +- `crowdsec_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: `/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. + +1. Navigate to **Firewall → Rules → WAN** +2. Find rules: `Block DROP`, `Block EDROP`, and the GeoIP block rule +3. Click the enable toggle on each rule +4. 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:** + +1. Go to **Services → Intrusion Detection → Administration** +2. Enable IDS/IPS, set interface to **ATT** (and WAN while active) +3. Set mode to **IPS** (inline blocking, not just alerting) +4. Under **Download**, enable the following rulesets: + - `ET Open` — Proofpoint Emerging Threats (free, comprehensive) + - `Abuse.ch SSL Blacklist` — malicious SSL certificate detection + - `Feodo Tracker` — botnet C2 blocking +5. Under **Policies**, set default action to `drop` for high-severity rules +6. 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. + +1. Go to **System → Firmware → Plugins**, install `os-dnscrypt-proxy` +2. Navigate to **Services → DNSCrypt-Proxy** +3. Enable, set listen port to `5353` +4. Select resolvers: `cloudflare`, `quad9-dnscrypt-ip4-nofilter-pri` (or similar) +5. Enable DNSSEC validation +6. Update **System → Settings → General** — set DNS server to `127.0.0.1:5353` +7. Disable `DNS Allow Override` so 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. + +1. Go to **System → Firmware → Plugins**, install `os-git-backup` +2. Navigate to **System → Configuration → Git Backup** +3. Configure a Forgejo repository on Netgrimoire as the remote +4. Set SSH key for authentication +5. 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. + +1. Go to **System → Settings → Logging → Remote** +2. Add a syslog destination: `graylog:514` (UDP) or use GELF input on Graylog +3. 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](./caddy-reverse-proxy) — services exposed through the firewall +- [MailCow Mail Server](./mailcow) — mail server behind the firewall, dedicated WAN IP +- [WireGuard VPN](./wireguard) — peer configuration and access +- [Graylog](./graylog) — target for firewall syslog +- [CrowdSec](./crowdsec) — threat intelligence integration