New Grimoire
This commit is contained in:
parent
77d589a13d
commit
cc574f8aed
157 changed files with 29420 additions and 0 deletions
39
Ward-Grimoire/Access/Auth-Overview.md
Normal file
39
Ward-Grimoire/Access/Auth-Overview.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
title: Authentication Overview
|
||||
description: SSO, LDAP, and access control in Netgrimoire
|
||||
published: true
|
||||
date: 2026-04-12T00:00:00.000Z
|
||||
tags: ward, auth, sso
|
||||
editor: markdown
|
||||
dateCreated: 2026-04-12T00:00:00.000Z
|
||||
---
|
||||
|
||||
# Authentication Overview
|
||||
|
||||
## SSO Providers
|
||||
|
||||
| Provider | Scope | URL |
|
||||
|----------|-------|-----|
|
||||
| Authentik | `*.netgrimoire.com` | Protected via `caddy.import_1: authentik` label |
|
||||
| Authelia | `*.wasted-bandwidth.net` | Green Grimoire + Shadow Grimoire services |
|
||||
|
||||
Both providers use LLDAP as their LDAP backend.
|
||||
|
||||
## LLDAP
|
||||
|
||||
Lightweight LDAP directory at `ldap.netgrimoire.com`. Postgres backend. Provides the user directory for both Authentik and Authelia.
|
||||
|
||||
See [LDAP Client Setup](/Ward-Grimoire/Access/LDAP-Client-Setup) for configuring hosts to authenticate via LLDAP.
|
||||
|
||||
## Vaultwarden
|
||||
|
||||
Password manager at `pass.netgrimoire.com`. Protected by Authentik.
|
||||
|
||||
## WireGuard
|
||||
|
||||
5 VPN peers on 192.168.32.0/24. Managed in OPNsense. See [Host Inventory](/Keystone-Grimoire/Hosts/Host-Inventory) for peer assignments.
|
||||
|
||||
## YubiKey (Planned)
|
||||
|
||||
- PIV SSH authentication on all hosts — highest-impact pending integration
|
||||
- Challenge-response for LUKS / Kopia key derivation on znas
|
||||
218
Ward-Grimoire/Access/LDAP-Client-Setup.md
Normal file
218
Ward-Grimoire/Access/LDAP-Client-Setup.md
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
---
|
||||
title: LDAP Client Setup
|
||||
description:
|
||||
published: true
|
||||
date: 2026-02-20T04:33:31.862Z
|
||||
tags:
|
||||
editor: markdown
|
||||
dateCreated: 2026-01-21T13:21:40.588Z
|
||||
---
|
||||
|
||||
|
||||
Your content here✅ LLDAP + SSSD Node Join Checklist (FINAL)
|
||||
|
||||
Assumptions
|
||||
|
||||
LLDAP server: docker4
|
||||
|
||||
LDAP URI: ldap://docker4:3890
|
||||
|
||||
Base DN: dc=netgrimoire,dc=com
|
||||
|
||||
Users/groups use lowercase attributes (uidnumber, gidnumber, homedirectory, unixshell, uniquemember)
|
||||
|
||||
No TLS (lab only)
|
||||
|
||||
Docker group GID = 1964 in LDAP
|
||||
|
||||
This node is Ubuntu/Debian-based
|
||||
|
||||
0️⃣ Safety first (do this every time)
|
||||
|
||||
Open two SSH sessions to the node
|
||||
|
||||
Confirm you can sudo
|
||||
|
||||
Do not edit nsswitch.conf until SSSD is confirmed working
|
||||
|
||||
1️⃣ Install required packages
|
||||
sudo apt update
|
||||
sudo apt install -y sssd sssd-ldap sssd-tools libpam-sss libnss-sss libsss-sudo ldap-utils oddjob oddjob-mkhomedir
|
||||
|
||||
Ensure legacy LDAP NSS is NOT installed
|
||||
sudo apt purge -y libnss-ldap libpam-ldap nslcd libnss-ldapd libpam-ldapd || true
|
||||
sudo apt autoremove -y
|
||||
|
||||
2️⃣ Verify LDAP connectivity (must pass)
|
||||
getent hosts docker4
|
||||
nc -vz docker4 3890
|
||||
ldapwhoami -x -H ldap://docker4:3890 \
|
||||
-D 'uid=admin,ou=people,dc=netgrimoire,dc=com' -w 'F@lcon13'
|
||||
|
||||
|
||||
❌ If any fail → stop and fix networking/DNS/firewall.
|
||||
|
||||
3️⃣ Create /etc/sssd/sssd.conf (single file, no includes)
|
||||
sudo vi /etc/sssd/sssd.conf
|
||||
|
||||
|
||||
Paste exactly:
|
||||
|
||||
[sssd]
|
||||
services = nss, pam, ssh
|
||||
config_file_version = 2
|
||||
domains = netgrimoire.com
|
||||
|
||||
[nss]
|
||||
filter_users = root
|
||||
filter_groups = root
|
||||
|
||||
[pam]
|
||||
offline_failed_login_attempts = 3
|
||||
offline_failed_login_delay = 5
|
||||
|
||||
[ssh]
|
||||
|
||||
[domain/netgrimoire.com]
|
||||
id_provider = ldap
|
||||
auth_provider = ldap
|
||||
chpass_provider = ldap
|
||||
access_provider = permit
|
||||
|
||||
enumerate = false
|
||||
cache_credentials = true
|
||||
|
||||
ldap_uri = ldap://docker4:3890
|
||||
ldap_schema = rfc2307bis
|
||||
ldap_search_base = dc=netgrimoire,dc=com
|
||||
|
||||
ldap_auth_disable_tls_never_use_in_production = true
|
||||
ldap_id_use_start_tls = false
|
||||
ldap_tls_reqcert = never
|
||||
|
||||
ldap_default_bind_dn = uid=admin,ou=people,dc=netgrimoire,dc=com
|
||||
ldap_default_authtok = F@lcon13
|
||||
|
||||
# USERS (lowercase attributes)
|
||||
ldap_user_search_base = ou=people,dc=netgrimoire,dc=com
|
||||
ldap_user_object_class = posixAccount
|
||||
ldap_user_name = uid
|
||||
ldap_user_gecos = cn
|
||||
ldap_user_uid_number = uidnumber
|
||||
ldap_user_gid_number = gidnumber
|
||||
ldap_user_home_directory = homedirectory
|
||||
ldap_user_shell = unixshell
|
||||
|
||||
# GROUPS (lowercase attributes)
|
||||
ldap_group_search_base = ou=groups,dc=netgrimoire,dc=com
|
||||
ldap_group_object_class = groupOfUniqueNames
|
||||
ldap_group_name = cn
|
||||
ldap_group_gid_number = gidnumber
|
||||
ldap_group_member = uniquemember
|
||||
|
||||
4️⃣ Fix permissions (SSSD will NOT start without this)
|
||||
sudo chown root:root /etc/sssd/sssd.conf
|
||||
sudo chmod 600 /etc/sssd/sssd.conf
|
||||
sudo chmod 700 /etc/sssd
|
||||
|
||||
|
||||
Validate:
|
||||
|
||||
sudo sssctl config-check
|
||||
|
||||
5️⃣ Start SSSD cleanly
|
||||
sudo systemctl enable sssd
|
||||
sudo systemctl stop sssd
|
||||
sudo rm -f /var/lib/sss/db/* /var/lib/sss/mc/*
|
||||
sudo systemctl start sssd
|
||||
|
||||
|
||||
Verify:
|
||||
|
||||
sudo systemctl status sssd --no-pager -l
|
||||
sudo sssctl domain-status netgrimoire.com
|
||||
|
||||
|
||||
Expected:
|
||||
|
||||
Online status: Online
|
||||
LDAP: docker4
|
||||
|
||||
6️⃣ Enable NSS lookups via SSSD (LDAP-first)
|
||||
|
||||
Edit /etc/nsswitch.conf:
|
||||
|
||||
passwd: sss files systemd
|
||||
group: sss files systemd
|
||||
shadow: sss files
|
||||
|
||||
|
||||
Test:
|
||||
|
||||
getent passwd graymutt
|
||||
getent group docker
|
||||
id graymutt
|
||||
|
||||
7️⃣ 🔑 RE-INITIALIZE PAM (THIS IS THE STEP YOU REMEMBERED)
|
||||
|
||||
This step is mandatory on Debian/Ubuntu.
|
||||
|
||||
sudo pam-auth-update
|
||||
|
||||
In the menu, ENABLE:
|
||||
|
||||
✅ Unix authentication
|
||||
|
||||
✅ SSSD
|
||||
|
||||
✅ Create home directory on login
|
||||
|
||||
DISABLE:
|
||||
|
||||
❌ LDAP Authentication (legacy)
|
||||
|
||||
❌ Kerberos (unless you explicitly use it)
|
||||
|
||||
Press OK.
|
||||
|
||||
8️⃣ Verify PAM wiring
|
||||
grep pam_sss.so /etc/pam.d/common-*
|
||||
grep pam_mkhomedir /etc/pam.d/common-session
|
||||
|
||||
|
||||
You should see:
|
||||
|
||||
session required pam_mkhomedir.so skel=/etc/skel umask=0022
|
||||
|
||||
9️⃣ Final login test (definitive)
|
||||
ssh graymutt@localhost
|
||||
|
||||
|
||||
Expected:
|
||||
|
||||
Login succeeds
|
||||
|
||||
/home/graymutt is auto-created
|
||||
|
||||
Correct LDAP groups present
|
||||
|
||||
🔟 (Optional but recommended) Remove local docker group
|
||||
|
||||
If the node has a local docker group (gid 998):
|
||||
|
||||
sudo groupdel docker
|
||||
|
||||
|
||||
Verify:
|
||||
|
||||
getent group docker
|
||||
|
||||
|
||||
Expected:
|
||||
|
||||
docker:x:1964:graymutt,dockhand
|
||||
|
||||
🧪 Fast troubleshooting commands
|
||||
sudo sssctl domain-status netgrimoire.com
|
||||
sudo tail -n 200 /var/log/sssd/sssd_netgrimoire.com.log
|
||||
sudo systemctl status sssd --no-pager -l
|
||||
239
Ward-Grimoire/Firewall/Blocklists.md
Normal file
239
Ward-Grimoire/Firewall/Blocklists.md
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
---
|
||||
title: Opnsense - Additional Blocklists
|
||||
description: Blocklists
|
||||
published: true
|
||||
date: 2026-02-23T21:54:13.019Z
|
||||
tags:
|
||||
editor: markdown
|
||||
dateCreated: 2026-02-23T21:46:39.562Z
|
||||
---
|
||||
|
||||
# OPNsense Additional Blocklists
|
||||
|
||||
**Service:** Firewall Aliases — URL Table blocklists
|
||||
**Host:** OPNsense firewall
|
||||
**Applies To:** WAN and ATT interfaces
|
||||
**Update Frequency:** Daily (automatic)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Your firewall already uses Spamhaus DROP and EDROP as IP blocklists. These three additional lists fill specific gaps that Spamhaus does not cover:
|
||||
|
||||
| List | What It Blocks | Why It's Needed |
|
||||
|---|---|---|
|
||||
| Feodo Tracker | Botnet command & control IPs | Stops malware on your network phoning home |
|
||||
| Abuse.ch SSLBL | IPs with malicious SSL certificates | Catches malware that uses HTTPS to hide C2 traffic |
|
||||
| Emerging Threats | Confirmed active attack IPs | Broad coverage of IPs currently conducting scans and exploits |
|
||||
|
||||
These work at the **firewall alias level** — the same mechanism as your existing Spamhaus lists. Traffic from/to these IPs is blocked before it reaches any service.
|
||||
|
||||
> ✓ These lists are also used by Suricata internally. Adding them as firewall aliases provides a second, independent enforcement point at the packet filter level — meaning blocks happen even if Suricata is restarted or temporarily inactive.
|
||||
|
||||
---
|
||||
|
||||
## Current Blocklist State
|
||||
|
||||
From your configuration, these lists are already present and working:
|
||||
|
||||
| Alias | List | Status |
|
||||
|---|---|---|
|
||||
| SpamHaus_Drop | Spamhaus DROP | ⚠ Alias active, **rule disabled** |
|
||||
| Spamhaus_edrop | Spamhaus EDROP | ⚠ Alias active, **rule disabled** |
|
||||
| crowdsec_blacklists | CrowdSec IPv4 | ✓ Active |
|
||||
| crowdsec6_blacklists | CrowdSec IPv6 | ✓ Active |
|
||||
|
||||
> ⚠ **First priority:** Before adding new blocklists, re-enable the existing Spamhaus block rules. See the Re-enable Existing Rules section at the bottom of this document.
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Add Feodo Tracker Alias
|
||||
|
||||
Navigate to **Firewall → Aliases → Add**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `Feodo_Tracker` |
|
||||
| Type | `URL Table (IPs)` |
|
||||
| Description | `Abuse.ch Feodo Tracker — Botnet C2 IPs` |
|
||||
| URL | `https://feodotracker.abuse.ch/downloads/ipblocklist.txt` |
|
||||
| Refresh Frequency | `1` day |
|
||||
| Enabled | ✓ |
|
||||
|
||||
Click **Save**, then **Apply Changes**.
|
||||
|
||||
**Verify the list loaded:**
|
||||
Go to **Firewall → Diagnostics → Aliases**, select `Feodo_Tracker` — you should see a list of IP addresses populated.
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Add Abuse.ch SSLBL Alias
|
||||
|
||||
Navigate to **Firewall → Aliases → Add**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `AbuseCH_SSLBL` |
|
||||
| Type | `URL Table (IPs)` |
|
||||
| Description | `Abuse.ch SSL Blacklist — Malicious SSL certificate IPs` |
|
||||
| URL | `https://sslbl.abuse.ch/blacklist/sslipblacklist.txt` |
|
||||
| Refresh Frequency | `1` day |
|
||||
| Enabled | ✓ |
|
||||
|
||||
Click **Save**, then **Apply Changes**.
|
||||
|
||||
> ✓ The SSL Blacklist specifically targets IPs that have been observed using SSL/TLS certificates associated with malware botnets. It catches C2 traffic that would otherwise be hidden inside HTTPS.
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Add Emerging Threats Alias
|
||||
|
||||
Navigate to **Firewall → Aliases → Add**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `ET_Block_IPs` |
|
||||
| Type | `URL Table (IPs)` |
|
||||
| Description | `Emerging Threats — Active attack and scanning IPs` |
|
||||
| URL | `https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt` |
|
||||
| Refresh Frequency | `1` day |
|
||||
| Enabled | ✓ |
|
||||
|
||||
Click **Save**, then **Apply Changes**.
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Create Firewall Block Rules
|
||||
|
||||
One block rule per alias, applied to both WAN and ATT interfaces. Add these rules **above** your existing PASS rules on each interface.
|
||||
|
||||
Navigate to **Firewall → Rules → WAN**
|
||||
|
||||
### Rule 1 — Block Feodo Tracker (WAN)
|
||||
|
||||
Click **Add** (add to top of ruleset):
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Action | Block |
|
||||
| Interface | WAN |
|
||||
| Direction | in |
|
||||
| Protocol | any |
|
||||
| Source | `Feodo_Tracker` (single host or alias) |
|
||||
| Destination | any |
|
||||
| Description | `Block Feodo Tracker botnet C2` |
|
||||
| Log | ✓ Enable logging |
|
||||
|
||||
Click **Save**.
|
||||
|
||||
### Rule 2 — Block Abuse.ch SSLBL (WAN)
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Action | Block |
|
||||
| Interface | WAN |
|
||||
| Direction | in |
|
||||
| Protocol | any |
|
||||
| Source | `AbuseCH_SSLBL` |
|
||||
| Destination | any |
|
||||
| Description | `Block Abuse.ch SSL Blacklist` |
|
||||
| Log | ✓ Enable logging |
|
||||
|
||||
Click **Save**.
|
||||
|
||||
### Rule 3 — Block Emerging Threats (WAN)
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Action | Block |
|
||||
| Interface | WAN |
|
||||
| Direction | in |
|
||||
| Protocol | any |
|
||||
| Source | `ET_Block_IPs` |
|
||||
| Destination | any |
|
||||
| Description | `Block Emerging Threats IPs` |
|
||||
| Log | ✓ Enable logging |
|
||||
|
||||
Click **Save**.
|
||||
|
||||
Click **Apply Changes** on the WAN rules page.
|
||||
|
||||
### Repeat for ATT Interface
|
||||
|
||||
Navigate to **Firewall → Rules → ATT** and add the same three rules with `Interface: ATT`. This ensures blocking applies to both WANs during the transition period, and only ATT after WAN is retired.
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Also Block Outbound (Optional but Recommended)
|
||||
|
||||
Adding outbound blocks catches the case where an internal device is already compromised and attempting to contact C2 infrastructure. Apply to the LAN interface, direction **out**:
|
||||
|
||||
Navigate to **Firewall → Rules → LAN**, add rules with:
|
||||
- Direction: `out`
|
||||
- Source: `any`
|
||||
- Destination: the respective alias (`Feodo_Tracker`, `AbuseCH_SSLBL`, `ET_Block_IPs`)
|
||||
- Action: `Block`
|
||||
|
||||
This means even if malware bypasses inbound filtering, outbound connections to known C2 IPs are still blocked.
|
||||
|
||||
---
|
||||
|
||||
## Re-enable Existing Spamhaus Rules
|
||||
|
||||
While you are in the firewall rules, re-enable the three currently disabled rules:
|
||||
|
||||
Navigate to **Firewall → Rules → WAN**
|
||||
|
||||
Find these three rules (they appear greyed out):
|
||||
1. `Block DROP` — source: SpamHaus_Drop
|
||||
2. `Block EDROP` — source: Spamhaus_edrop
|
||||
3. GeoIP country block — source: Blocked_Countries
|
||||
|
||||
Click the **enable toggle** (grey circle icon) on each rule to enable them. Click **Apply Changes**.
|
||||
|
||||
> ✓ These aliases are already populated and refreshing automatically. The only reason they were not blocking is because the rules were disabled. Enabling them requires no other changes.
|
||||
|
||||
---
|
||||
|
||||
## Verifying Blocklists Are Working
|
||||
|
||||
### Check Alias Contents
|
||||
|
||||
**Firewall → Diagnostics → Aliases** — select each alias to see the current list of blocked IPs and confirm they are populated.
|
||||
|
||||
### Check Firewall Logs
|
||||
|
||||
**Firewall → Log Files → Live View** — filter by the rule description (e.g., `Feodo Tracker`) to see blocks in real time.
|
||||
|
||||
### Check Update Schedule
|
||||
|
||||
Aliases refresh on the schedule set during creation. To force an immediate refresh:
|
||||
**Firewall → Diagnostics → Aliases → select alias → Flush + Force Update**
|
||||
|
||||
---
|
||||
|
||||
## Complete Blocklist Summary
|
||||
|
||||
After implementing all of the above, your firewall enforces the following IP blocklists:
|
||||
|
||||
| Alias | List | Covers | Update |
|
||||
|---|---|---|---|
|
||||
| SpamHaus_Drop | Spamhaus DROP | Hijacked/compromised netblocks | Daily |
|
||||
| Spamhaus_edrop | Spamhaus EDROP | Extended DROP — bogon routes | Daily |
|
||||
| Feodo_Tracker | Feodo Tracker | Botnet C2 IPs | Daily |
|
||||
| AbuseCH_SSLBL | Abuse.ch SSLBL | Malicious SSL certificate IPs | Daily |
|
||||
| ET_Block_IPs | Emerging Threats | Active scanners & attack IPs | Daily |
|
||||
| crowdsec_blacklists | CrowdSec | Community-reported bad IPs (IPv4) | Real-time |
|
||||
| crowdsec6_blacklists | CrowdSec | Community-reported bad IPs (IPv6) | Real-time |
|
||||
| Blocked_Countries | MaxMind GeoIP | 70 blocked countries | Weekly |
|
||||
|
||||
Combined with Suricata (content inspection) and CrowdSec (IP reputation), this gives you a comprehensive multi-layer perimeter.
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [OPNsense Firewall](./opnsense-firewall) — parent firewall documentation, full alias list
|
||||
- [Suricata IDS/IPS](./suricata-ids-ips) — content inspection layer, also uses these feed sources
|
||||
- [CrowdSec](./crowdsec) — real-time IP reputation blocking
|
||||
182
Ward-Grimoire/Firewall/OPNsense-Git-Backup.md
Normal file
182
Ward-Grimoire/Firewall/OPNsense-Git-Backup.md
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
---
|
||||
title: OpnSense - GIT Integration
|
||||
description: Git Integration
|
||||
published: true
|
||||
date: 2026-02-23T21:53:24.522Z
|
||||
tags:
|
||||
editor: markdown
|
||||
dateCreated: 2026-02-23T21:48:01.779Z
|
||||
---
|
||||
|
||||
# OPNsense Git Backup (os-git-backup)
|
||||
|
||||
**Service:** os-git-backup
|
||||
**Plugin:** os-git-backup
|
||||
**Host:** OPNsense firewall
|
||||
**Remote:** Forgejo on Netgrimoire
|
||||
**Trigger:** Automatic on every config change
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Every change made to OPNsense — adding a firewall rule, updating an alias, changing a VPN config — modifies the underlying XML configuration file. By default there is no history of these changes. If a misconfiguration causes an outage, or if you need to audit what changed after a security incident, you have no record to work from.
|
||||
|
||||
os-git-backup solves this by committing the OPNsense configuration to a Git repository automatically every time a change is saved. Each commit records exactly what changed, when, and (if configured) which user made the change.
|
||||
|
||||
**Benefits:**
|
||||
- Full audit trail of every configuration change
|
||||
- One-command rollback to any previous state
|
||||
- Offsite backup of firewall config via Forgejo → Kopia chain
|
||||
- Diff view to understand exactly what a change did
|
||||
|
||||
---
|
||||
|
||||
## Pre-requisite: Create Forgejo Repository
|
||||
|
||||
Before installing the plugin, create a dedicated repository in Forgejo to receive the OPNsense config backups.
|
||||
|
||||
1. Log into your Forgejo instance on Netgrimoire
|
||||
2. Create a new repository: `opnsense-config`
|
||||
3. Set visibility to **Private** — firewall configs contain sensitive network topology
|
||||
4. Do not initialize with a README (the plugin will push the first commit)
|
||||
5. Note the SSH clone URL: `git@git.netgrimoire.com:youruser/opnsense-config.git`
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1 — Install the Plugin
|
||||
|
||||
1. Go to **System → Firmware → Plugins**
|
||||
2. Search for `os-git-backup`
|
||||
3. Click the **+** install button
|
||||
4. Wait for installation to complete
|
||||
5. Navigate to **System → Configuration → Backups** — a **Git** tab will appear
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Step 2 — Generate SSH Deploy Key
|
||||
|
||||
The OPNsense firewall needs an SSH key to authenticate to Forgejo without a password.
|
||||
|
||||
Navigate to **System → Configuration → Backups → Git**
|
||||
|
||||
1. Click **Generate SSH Key**
|
||||
2. Copy the displayed **public key** — you will add this to Forgejo next
|
||||
|
||||
### Step 3 — Add Deploy Key to Forgejo
|
||||
|
||||
1. In Forgejo, go to your `opnsense-config` repository
|
||||
2. Navigate to **Settings → Deploy Keys**
|
||||
3. Click **Add Deploy Key**
|
||||
4. Title: `OPNsense Firewall`
|
||||
5. Key: paste the public key from Step 2
|
||||
6. Enable **Allow Write Access** — the firewall needs to push commits
|
||||
7. Click **Add Key**
|
||||
|
||||
### Step 4 — Configure the Plugin
|
||||
|
||||
Navigate to **System → Configuration → Backups → Git**
|
||||
|
||||
| Setting | Value | Notes |
|
||||
|---|---|---|
|
||||
| Enabled | ✓ | |
|
||||
| URL | `git@git.netgrimoire.com:youruser/opnsense-config.git` | SSH URL from your Forgejo repo |
|
||||
| Branch | `main` | |
|
||||
| Name | `OPNsense Firewall` | Author name shown in commits |
|
||||
| Email | `opnsense@netgrimoire.com` | Author email shown in commits |
|
||||
| SSH Private Key | (auto-populated from Step 2) | |
|
||||
| Backup Interval | On change | Commits every time config is saved |
|
||||
|
||||
Click **Save**.
|
||||
|
||||
### Step 5 — Test the Connection
|
||||
|
||||
Click **Backup Now** to trigger a manual backup. Then check your Forgejo repository — you should see an initial commit containing the OPNsense configuration XML.
|
||||
|
||||
If the push fails, check:
|
||||
1. The deploy key has write access in Forgejo
|
||||
2. The SSH URL is correct (use SSH, not HTTPS)
|
||||
3. Forgejo is reachable from the firewall — test from OPNsense shell:
|
||||
```bash
|
||||
ssh -T git@git.netgrimoire.com
|
||||
# Expected: Hi youruser! You've successfully authenticated...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Gets Backed Up
|
||||
|
||||
The plugin commits the OPNsense configuration file:
|
||||
|
||||
`/conf/config.xml`
|
||||
|
||||
This single file contains **everything** — interfaces, firewall rules, NAT, VPN configs, aliases, users, certificates, DHCP, DNS settings, and all plugin configurations. A restore from this file fully recreates the firewall state.
|
||||
|
||||
> ⚠ The config.xml contains **hashed passwords**, **VPN private keys**, and **API credentials**. The Forgejo repository must remain private. Ensure your Forgejo instance is not publicly accessible or that this repository is explicitly private.
|
||||
|
||||
---
|
||||
|
||||
## Using the Backup
|
||||
|
||||
### Viewing History
|
||||
|
||||
In Forgejo, navigate to the `opnsense-config` repository. Each commit represents one configuration save, with:
|
||||
- Timestamp of the change
|
||||
- Diff showing exactly what XML changed
|
||||
- Author (OPNsense Firewall)
|
||||
|
||||
### Rolling Back a Change
|
||||
|
||||
If a configuration change causes problems:
|
||||
|
||||
**Option 1 — Restore via OPNsense UI:**
|
||||
1. In Forgejo, find the commit you want to restore
|
||||
2. Download the `config.xml` from that commit
|
||||
3. In OPNsense: **System → Configuration → Backups → Restore**
|
||||
4. Upload the config.xml and restore
|
||||
|
||||
**Option 2 — Restore via shell (if UI is unreachable):**
|
||||
```bash
|
||||
# SSH into OPNsense
|
||||
ssh root@192.168.3.4
|
||||
|
||||
# The git repo is cloned locally — find it
|
||||
find /conf -name ".git" -type d
|
||||
|
||||
# Check out the previous config
|
||||
cd /conf/backup # or wherever the repo is cloned
|
||||
git log --oneline -10
|
||||
git checkout <commit-hash> -- config.xml
|
||||
|
||||
# Apply the restored config
|
||||
/usr/local/sbin/opnsense-importer config.xml
|
||||
```
|
||||
|
||||
### Diffing Changes
|
||||
|
||||
To see exactly what a specific change did:
|
||||
|
||||
```bash
|
||||
# In Forgejo: click any commit → view the diff
|
||||
# Alternatively, from the OPNsense shell:
|
||||
cd <git repo path>
|
||||
git diff HEAD~1 HEAD -- config.xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with Kopia Backups
|
||||
|
||||
Since the git repository lives in Forgejo on Netgrimoire, it is automatically included in the Netgrimoire Kopia backup chain — no additional configuration needed. The OPNsense config history is backed up offsite along with everything else.
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [OPNsense Firewall](./opnsense-firewall) — parent firewall documentation
|
||||
- [Forgejo](./forgejo) — Git repository host on Netgrimoire
|
||||
- [Kopia Backups](./kopia) — offsite backup chain
|
||||
508
Ward-Grimoire/Firewall/OPNsense.md
Normal file
508
Ward-Grimoire/Firewall/OPNsense.md
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
---
|
||||
title: OpnSense
|
||||
description: Grimoire Firewall Configuration
|
||||
published: true
|
||||
date: 2026-02-23T21:31:26.008Z
|
||||
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: `<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.
|
||||
|
||||
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
|
||||
212
Ward-Grimoire/Firewall/Suricata-IDS.md
Normal file
212
Ward-Grimoire/Firewall/Suricata-IDS.md
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
---
|
||||
title: OpnSense-IDS/IPS
|
||||
description: IDS
|
||||
published: true
|
||||
date: 2026-02-23T21:51:49.920Z
|
||||
tags:
|
||||
editor: markdown
|
||||
dateCreated: 2026-02-23T21:49:16.861Z
|
||||
---
|
||||
|
||||
# Suricata IDS/IPS
|
||||
|
||||
**Service:** Suricata Intrusion Detection & Prevention System
|
||||
**Host:** OPNsense firewall
|
||||
**Interfaces:** ATT (opt1) — add WAN (igc0) while still active
|
||||
**Mode:** IPS (inline blocking)
|
||||
**Rulesets:** ET Open, Feodo Tracker, Abuse.ch SSL
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Suricata is OPNsense's built-in deep packet inspection engine. Unlike CrowdSec (which blocks based on IP reputation) and GeoIP (which blocks by country), Suricata inspects the **content** of traffic — detecting exploit patterns, malware C2 communication, vulnerability scans, and known CVE exploitation attempts in real time.
|
||||
|
||||
The two systems complement each other and do not overlap:
|
||||
|
||||
| Layer | Tool | What It Stops |
|
||||
|---|---|---|
|
||||
| IP reputation | CrowdSec | Known bad IPs from community threat intel |
|
||||
| Geography | GeoIP | Traffic from blocked countries |
|
||||
| Content inspection | Suricata | Malicious payloads, exploit patterns, C2 traffic |
|
||||
|
||||
Suricata uses **Netmap** for high-performance inline packet processing with minimal CPU overhead.
|
||||
|
||||
> ⚠ **Before enabling IPS mode:** Disable hardware offloading on your interfaces or Netmap will not function correctly. This is done in **Interfaces → Settings**.
|
||||
|
||||
---
|
||||
|
||||
## Pre-requisite: Disable Hardware Offloading
|
||||
|
||||
1. Go to **Interfaces → Settings**
|
||||
2. Disable the following options:
|
||||
- Hardware CRC
|
||||
- Hardware TSO
|
||||
- Hardware LRO
|
||||
- VLAN Hardware Filtering
|
||||
3. Click **Save**
|
||||
4. Reboot the firewall
|
||||
|
||||
> ✓ This is a one-time change. It has no meaningful impact on performance for home/small business use and is required for Suricata IPS mode to function.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Suricata is built into OPNsense — no plugin install required. Navigate directly to:
|
||||
|
||||
**Services → Intrusion Detection → Administration**
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Step 1 — General Settings
|
||||
|
||||
Navigate to **Services → Intrusion Detection → Administration**
|
||||
|
||||
| Setting | Value | Notes |
|
||||
|---|---|---|
|
||||
| Enabled | ✓ | Turns on the IDS/IPS engine |
|
||||
| IPS Mode | ✓ | Enables inline blocking (not just alerting) |
|
||||
| Promiscuous Mode | Leave default | Only needed for mirrored traffic setups |
|
||||
| Default Packet Size | Leave default | Auto-detected |
|
||||
| Interfaces | ATT, WAN | Add both while dual-WAN is active; remove WAN after migration |
|
||||
| Home Networks | 192.168.3.0/24, 192.168.5.0/24, 192.168.32.0/24 | Your internal subnets — critical for rule accuracy |
|
||||
| Log Level | Info | |
|
||||
| Log Retention | 7 days | Adjust based on disk space |
|
||||
|
||||
> ⚠ **Home Networks is critical.** Suricata rules use `$HOME_NET` and `$EXTERNAL_NET` to determine direction. If your internal subnets are not listed here, many rules will fail to trigger correctly or will produce false positives.
|
||||
|
||||
Click **Apply** after setting these values.
|
||||
|
||||
### Step 2 — Download Rulesets
|
||||
|
||||
Navigate to **Services → Intrusion Detection → Download**
|
||||
|
||||
Enable the following rulesets:
|
||||
|
||||
| Ruleset | Provider | Priority | Notes |
|
||||
|---|---|---|---|
|
||||
| ET Open | Proofpoint Emerging Threats | 🔴 Essential | Comprehensive free ruleset — 40,000+ rules covering exploits, malware, scanning, C2 |
|
||||
| Abuse.ch SSL Blacklist | Abuse.ch | 🔴 Essential | Blocks connections to malicious SSL certificates used by malware |
|
||||
| Feodo Tracker Botnet | Abuse.ch | 🔴 Essential | Blocks botnet C2 IP communication |
|
||||
| OSIF | OPNsense | 🟡 Recommended | OPNsense internal feed |
|
||||
| PT Research | Positive Technologies | 🟡 Recommended | Additional threat intelligence |
|
||||
|
||||
To enable each ruleset:
|
||||
1. Find it in the list
|
||||
2. Toggle the **Enabled** switch
|
||||
3. Click **Download & Update Rules** at the top of the page
|
||||
|
||||
> ✓ ET Open is the most important ruleset. It is maintained by Proofpoint, updated daily, and covers the vast majority of common attack patterns you will encounter.
|
||||
|
||||
### Step 3 — Configure Policies
|
||||
|
||||
Policies control what Suricata does when a rule matches — alert only, or drop the packet.
|
||||
|
||||
Navigate to **Services → Intrusion Detection → Policy**
|
||||
|
||||
**Recommended policy setup:**
|
||||
|
||||
Add the following policies in order:
|
||||
|
||||
**Policy 1 — Drop high-severity ET threats**
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Description | Drop ET High Severity |
|
||||
| Priority | 1 |
|
||||
| Rulesets | ET Open |
|
||||
| Action | Drop |
|
||||
| Severity | ≥ High |
|
||||
|
||||
**Policy 2 — Alert on medium-severity (tuning period)**
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Description | Alert ET Medium |
|
||||
| Priority | 2 |
|
||||
| Rulesets | ET Open |
|
||||
| Action | Alert |
|
||||
| Severity | Medium |
|
||||
|
||||
**Policy 3 — Drop all Feodo/Abuse.ch matches**
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Description | Drop Botnet C2 and SSL Blacklist |
|
||||
| Priority | 1 |
|
||||
| Rulesets | Feodo Tracker, Abuse.ch SSL |
|
||||
| Action | Drop |
|
||||
| Severity | Any |
|
||||
|
||||
> ✓ Start with medium-severity rules in **alert** mode for the first 1–2 weeks. Review alerts in the log for false positives before switching to drop. High-severity rules and the abuse.ch lists are safe to drop immediately.
|
||||
|
||||
### Step 4 — Apply and Verify
|
||||
|
||||
1. Click **Apply** on the Administration tab
|
||||
2. Navigate to **Services → Intrusion Detection → Alerts**
|
||||
3. Wait a few minutes — alerts should begin populating
|
||||
4. Check **Services → Intrusion Detection → Stats** to confirm traffic is being processed
|
||||
|
||||
---
|
||||
|
||||
## Tuning & False Positives
|
||||
|
||||
After running in alert mode for a week, review the Alerts tab. Common false positives from home lab environments include:
|
||||
|
||||
- **Nextcloud sync traffic** — may trigger file transfer rules
|
||||
- **Torrents/P2P** — will trigger multiple ET rules by design
|
||||
- **Internal port scanning tools** — Nmap from internal hosts triggers scan rules
|
||||
|
||||
To suppress a false positive rule without disabling it entirely:
|
||||
|
||||
1. Note the rule SID from the alert
|
||||
2. Go to **Services → Intrusion Detection → Rules**
|
||||
3. Search for the SID
|
||||
4. Change the rule action to **Alert** (instead of Drop) for that specific rule
|
||||
|
||||
Alternatively, add a suppression in **Services → Intrusion Detection → Suppressions**:
|
||||
- Enter the SID
|
||||
- Set the direction (source or destination)
|
||||
- Enter the IP to suppress for that rule
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Alert Dashboard
|
||||
|
||||
**Services → Intrusion Detection → Alerts** — real-time view of matched rules.
|
||||
|
||||
Useful filters:
|
||||
- Filter by `severity: high` to see the most critical events
|
||||
- Filter by `action: drop` to see what is being actively blocked
|
||||
- Filter by source IP to investigate a specific host
|
||||
|
||||
### Graylog Integration
|
||||
|
||||
Forward Suricata alerts to Graylog for centralized analysis:
|
||||
|
||||
1. Suricata logs to `/var/log/suricata/eve.json` in EVE JSON format
|
||||
2. In Graylog, add a **Beats input** or **Syslog UDP input**
|
||||
3. In OPNsense **System → Settings → Logging → Remote**, add Graylog as syslog target
|
||||
4. Create a Graylog stream filtering on `application_name: suricata`
|
||||
|
||||
---
|
||||
|
||||
## Key Files & Paths
|
||||
|
||||
| Path | Purpose |
|
||||
|---|---|
|
||||
| `/var/log/suricata/eve.json` | EVE JSON alert log — used by Graylog |
|
||||
| `/var/log/suricata/stats.log` | Performance statistics |
|
||||
| `/usr/local/etc/suricata/suricata.yaml` | Main config (managed by OPNsense UI) |
|
||||
| `/usr/local/share/suricata/rules/` | Downloaded rulesets |
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [OPNsense Firewall](./opnsense-firewall) — parent firewall documentation
|
||||
- [CrowdSec](./crowdsec) — complementary IP reputation layer
|
||||
- [Additional Blocklists](./opnsense-blocklists) — Feodo, Abuse.ch, ET IP blocklists at firewall level
|
||||
- [Graylog](./graylog) — centralized log target for Suricata alerts
|
||||
159
Ward-Grimoire/Firewall/Zenarmor.md
Normal file
159
Ward-Grimoire/Firewall/Zenarmor.md
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
---
|
||||
title: OpnSense - App Protection
|
||||
description: App Inspection
|
||||
published: true
|
||||
date: 2026-02-23T21:52:43.630Z
|
||||
tags:
|
||||
editor: markdown
|
||||
dateCreated: 2026-02-23T21:50:37.324Z
|
||||
---
|
||||
|
||||
# Zenarmor (NGFW)
|
||||
|
||||
**Service:** Zenarmor Next-Generation Firewall
|
||||
**Plugin:** os-sunnyvalley
|
||||
**Tier:** Free Edition
|
||||
**Host:** OPNsense firewall
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Zenarmor adds application-layer awareness and web filtering to OPNsense that the base firewall does not provide. Where Suricata inspects packet content for known threat signatures, Zenarmor identifies **what application or service** is generating traffic and can block or allow based on that — regardless of port.
|
||||
|
||||
| Feature | Free Tier | Paid Tier |
|
||||
|---|---|---|
|
||||
| Layer-7 app identification | ✓ | ✓ |
|
||||
| Web category filtering | Default policy only | Custom policies |
|
||||
| Malware/phishing blocking | ✓ | ✓ |
|
||||
| Real-time network analytics | ✓ | ✓ |
|
||||
| Device tracking & alerts | ✗ | ✓ |
|
||||
| Multiple policies | ✗ | ✓ |
|
||||
| TLS inspection | ✗ | ✓ |
|
||||
|
||||
The free tier is useful primarily for **visibility** (seeing what applications are running on your network) and **basic threat blocking** (malware, phishing, PUP domains). The analytics dashboard alone makes it worthwhile.
|
||||
|
||||
> ✓ Zenarmor and Suricata can run simultaneously. They operate at different layers and do not conflict. Zenarmor handles application identity; Suricata handles content signatures.
|
||||
|
||||
> ⚠ **MongoDB deprecation note:** As of September 2025, MongoDB is being deprecated as the Zenarmor database backend. Use **SQLite** when prompted during setup — it is the supported path going forward.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1 — Install the Plugin
|
||||
|
||||
1. Go to **System → Firmware → Plugins**
|
||||
2. Search for `os-sunnyvalley`
|
||||
3. Click the **+** install button
|
||||
4. Wait for installation to complete
|
||||
5. **Refresh the browser** — a new **Zenarmor** menu item will appear in the sidebar
|
||||
|
||||
### Step 2 — Initial Setup Wizard
|
||||
|
||||
Navigate to **Zenarmor → Dashboard** — this launches the setup wizard on first run.
|
||||
|
||||
**Deployment Mode:** Select **Routed Mode (L3)** for standard OPNsense setups. This is correct for your configuration.
|
||||
|
||||
**Database:** Select **SQLite** — do not select MongoDB (deprecated September 2025).
|
||||
|
||||
**Interface:** Select **ATT (opt1)** as the primary interface. Add **WAN (igc0)** while dual-WAN is still active.
|
||||
|
||||
> ⚠ Zenarmor should be applied to the **LAN-facing side** of the firewall for internal traffic inspection, or the **WAN-facing side** for inbound threat blocking. For your setup, applying it to both ATT and LAN gives the most coverage.
|
||||
|
||||
**Cloud Connectivity:** Leave enabled — Zenarmor uses cloud-based category lookups for web filtering. If you want fully offline operation, this can be disabled but web filtering accuracy degrades significantly.
|
||||
|
||||
Click **Complete** to finish the wizard.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Step 3 — Security Policy
|
||||
|
||||
Navigate to **Zenarmor → Security**
|
||||
|
||||
Enable the following threat categories in the default policy:
|
||||
|
||||
| Category | Action | Notes |
|
||||
|---|---|---|
|
||||
| Malware | Block | Domains known to serve malware |
|
||||
| Phishing | Block | Credential harvesting sites |
|
||||
| Botnet | Block | C2 communication |
|
||||
| PUP/Adware | Block | Potentially unwanted programs |
|
||||
| SPAM Sources | Block | Known spam infrastructure |
|
||||
| Parked Domains | Block | Often used for malicious redirects |
|
||||
|
||||
Leave the following as **Alert** initially (review before blocking):
|
||||
- Anonymizers / Proxies — may block legitimate VPN services
|
||||
- Peer-to-peer — may affect legitimate use cases
|
||||
|
||||
### Step 4 — Application Control
|
||||
|
||||
Navigate to **Zenarmor → Policies → Application Control**
|
||||
|
||||
The free tier allows one default policy. Useful applications to consider blocking or monitoring:
|
||||
|
||||
| Application Category | Recommendation | Reason |
|
||||
|---|---|---|
|
||||
| Cryptocurrency mining | Block | Resource theft if unauthorized |
|
||||
| Remote access tools (unknown) | Alert | Unexpected remote tools are a red flag |
|
||||
| Tor | Alert | Monitor — may be legitimate or evasion |
|
||||
| Anonymous proxies | Block | Bypass attempts |
|
||||
|
||||
### Step 5 — Web Filtering
|
||||
|
||||
Navigate to **Zenarmor → Policies → Web Controls**
|
||||
|
||||
In the free tier, the default policy controls all web filtering. Recommended categories to block:
|
||||
|
||||
| Category | Action |
|
||||
|---|---|
|
||||
| Malware sites | Block |
|
||||
| Phishing | Block |
|
||||
| Hacking / exploit sites | Block |
|
||||
| Illegal content | Block |
|
||||
|
||||
Enable **Safe Search enforcement** if desired — forces Google, Bing, and YouTube into safe search mode network-wide.
|
||||
|
||||
---
|
||||
|
||||
## Dashboard & Analytics
|
||||
|
||||
Navigate to **Zenarmor → Dashboard**
|
||||
|
||||
The dashboard provides real-time visibility into:
|
||||
- **Top talkers** — which internal hosts generate the most traffic
|
||||
- **Top applications** — what services are being used
|
||||
- **Blocked threats** — real-time feed of blocked requests
|
||||
- **Bandwidth usage** — per-host and per-application
|
||||
|
||||
This is the primary value of the free tier — even without advanced policy control, the visibility into what is running on your network is significant.
|
||||
|
||||
Navigate to **Zenarmor → Reports** for historical analysis and trend data.
|
||||
|
||||
---
|
||||
|
||||
## Performance Notes
|
||||
|
||||
Zenarmor uses deep packet inspection which adds some CPU overhead. On modern hardware (anything with i226-V NICs) this is negligible at home lab traffic volumes. Monitor CPU usage in **Zenarmor → Dashboard → System** after enabling.
|
||||
|
||||
If performance degrades, you can limit Zenarmor to specific interfaces rather than all interfaces.
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations (Free Tier)
|
||||
|
||||
- Only one web filtering policy — all devices get the same rules
|
||||
- No per-device or per-group policies
|
||||
- No TLS/SSL inspection — encrypted traffic is identified by SNI only
|
||||
- No device inventory or unknown device alerts
|
||||
- Web category database is cloud-dependent
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [OPNsense Firewall](./opnsense-firewall) — parent firewall documentation
|
||||
- [Suricata IDS/IPS](./suricata-ids-ips) — complementary content inspection layer
|
||||
- [CrowdSec](./crowdsec) — IP reputation layer
|
||||
31
Ward-Grimoire/Notifications/Alert-Routing.md
Normal file
31
Ward-Grimoire/Notifications/Alert-Routing.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
title: Alert Routing
|
||||
description: How security alerts flow through Netgrimoire
|
||||
published: true
|
||||
date: 2026-04-12T00:00:00.000Z
|
||||
tags: ward, alerts, ntfy
|
||||
editor: markdown
|
||||
dateCreated: 2026-04-12T00:00:00.000Z
|
||||
---
|
||||
|
||||
# Alert Routing
|
||||
|
||||
All Netgrimoire alerts route through self-hosted ntfy at `ntfy.netgrimoire.com`.
|
||||
|
||||
## ntfy Topics
|
||||
|
||||
| Topic | Source | Purpose |
|
||||
|-------|--------|---------|
|
||||
| `netgrimoire-diun` | DIUN | Docker image update notifications |
|
||||
| `netgrimoire-media` | Sonarr, Radarr, SABnzbd | Download and media events |
|
||||
| `netgrimoire-backup` | Kopia | Backup completion and errors |
|
||||
| `gremlin-alerts` | n8n Kuma triage workflow | AI-analyzed service DOWN alerts |
|
||||
| `gremlin-audits` | n8n Forgejo audit workflow | Weekly YAML audit summaries |
|
||||
|
||||
## Alert Sources
|
||||
|
||||
**OPNsense → ntfy:** CrowdSec HTTP plugin (`/usr/local/etc/crowdsec/notifications/ntfy.yaml`) + Monit script (`/usr/local/bin/ntfy-alert.sh`). See [OPNsense Alerts](/Ward-Grimoire/Notifications/OPNsense-Alerts).
|
||||
|
||||
**Uptime Kuma → Gremlin → ntfy:** Kuma webhook fires on DOWN/RECOVERED → n8n triage workflow → Ollama analysis (DOWN path only) → ntfy `gremlin-alerts`. See [Gremlin Kuma Triage](/Gremlin-Grimoire/Workflows/Kuma-Triage).
|
||||
|
||||
**DIUN → ntfy:** Docker image update watcher. Schedule: every 6 hours. Priority must be integer (1–5), not string `"default"`.
|
||||
463
Ward-Grimoire/Notifications/OPNsense-Alerts.md
Normal file
463
Ward-Grimoire/Notifications/OPNsense-Alerts.md
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
---
|
||||
title: OpnSense - NTFY Integration
|
||||
description: Security Notifications
|
||||
published: true
|
||||
date: 2026-02-23T22:00:46.462Z
|
||||
tags:
|
||||
editor: markdown
|
||||
dateCreated: 2026-02-23T22:00:37.268Z
|
||||
---
|
||||
|
||||
# OPNsense ntfy Alerts
|
||||
|
||||
**Service:** ntfy push notifications from OPNsense
|
||||
**Host:** OPNsense firewall
|
||||
**ntfy Server:** Your self-hosted ntfy instance on Netgrimoire
|
||||
**Methods:** CrowdSec HTTP plugin · Monit custom script · Suricata EVE watcher
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
OPNsense does not have a built-in ntfy notification channel, but there are three distinct integration points that together provide complete coverage:
|
||||
|
||||
| Method | What It Alerts On | Priority |
|
||||
|---|---|---|
|
||||
| **CrowdSec HTTP plugin** | Every IP ban decision CrowdSec makes | 🔴 Best for threat intel alerts |
|
||||
| **Monit + curl script** | System health, service failures, Suricata EVE matches, login failures | 🔴 Best for operational alerts |
|
||||
| **Suricata EVE watcher** | Suricata high-severity IDS hits (via Monit watching eve.json) | 🟡 Covered via Monit |
|
||||
|
||||
All three use your self-hosted ntfy instance. None require external services.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting, confirm:
|
||||
- ntfy is running and reachable at `https://ntfy.netgrimoire.com` (or your internal URL)
|
||||
- ntfy topic created: e.g. `opnsense-alerts`
|
||||
- If ntfy has auth enabled, have a token ready
|
||||
- SSH access to OPNsense as root
|
||||
|
||||
---
|
||||
|
||||
## Method 1 — CrowdSec HTTP Notification Plugin
|
||||
|
||||
This is the cleanest integration for security alerts. CrowdSec has a built-in HTTP notification plugin. Every time it makes a ban decision — whether from community intel, a Suricata match passed through CrowdSec, or a brute-force detection — it POSTs to ntfy.
|
||||
|
||||
### Step 1 — Create the HTTP notification config
|
||||
|
||||
SSH into OPNsense and create the ntfy config file:
|
||||
|
||||
```bash
|
||||
ssh root@192.168.3.4
|
||||
```
|
||||
|
||||
```bash
|
||||
cat > /usr/local/etc/crowdsec/notifications/ntfy.yaml << 'EOF'
|
||||
# ntfy notification plugin for CrowdSec
|
||||
# CrowdSec uses its built-in HTTP plugin pointed at ntfy
|
||||
type: http
|
||||
name: ntfy_default
|
||||
|
||||
log_level: info
|
||||
|
||||
# ntfy accepts plain POST body as the notification message
|
||||
# format is a Go template — .[]Alert is the list of alerts
|
||||
format: |
|
||||
{{range .}}
|
||||
🚨 CrowdSec Decision
|
||||
Scenario: {{.Scenario}}
|
||||
Attacker IP: {{.Source.IP}}
|
||||
Country: {{.Source.Cn}}
|
||||
Action: {{.Decisions | len}} x {{(index .Decisions 0).Type}}
|
||||
Duration: {{(index .Decisions 0).Duration}}
|
||||
{{end}}
|
||||
|
||||
url: https://ntfy.netgrimoire.com/opnsense-alerts
|
||||
|
||||
method: POST
|
||||
|
||||
headers:
|
||||
Title: "CrowdSec Ban — OPNsense"
|
||||
Priority: "high"
|
||||
Tags: "rotating_light,shield"
|
||||
# Uncomment and set token if ntfy auth is enabled:
|
||||
# Authorization: "Bearer YOUR_NTFY_TOKEN"
|
||||
|
||||
# skip_tls_verify: false
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠ Replace `https://ntfy.netgrimoire.com/opnsense-alerts` with your actual ntfy URL and topic. If ntfy is internal-only and OPNsense can reach it by hostname, the internal URL works fine.
|
||||
|
||||
### Step 2 — Register the plugin in profiles.yaml
|
||||
|
||||
Edit the CrowdSec profiles file to dispatch decisions to the ntfy plugin:
|
||||
|
||||
```bash
|
||||
vi /usr/local/etc/crowdsec/profiles.yaml
|
||||
```
|
||||
|
||||
Find the `notifications:` section of the default profile and add `ntfy_default`:
|
||||
|
||||
```yaml
|
||||
name: default_ip_remediation
|
||||
filters:
|
||||
- Alert.Remediation == true && Alert.GetScope() == "Ip"
|
||||
decisions:
|
||||
- type: ban
|
||||
duration: 4h
|
||||
notifications:
|
||||
- ntfy_default # ← add this line
|
||||
on_success: break
|
||||
```
|
||||
|
||||
> ✓ The `ntfy_default` name must match the `name:` field in the yaml file you created above exactly.
|
||||
|
||||
### Step 3 — Set correct file ownership
|
||||
|
||||
CrowdSec rejects plugins if the configuration file is not owned by the root user and root group. Ensure the file has the right permissions:
|
||||
|
||||
```bash
|
||||
chown root:wheel /usr/local/etc/crowdsec/notifications/ntfy.yaml
|
||||
chmod 600 /usr/local/etc/crowdsec/notifications/ntfy.yaml
|
||||
```
|
||||
|
||||
### Step 4 — Restart CrowdSec and test
|
||||
|
||||
```bash
|
||||
# Restart via OPNsense service manager (do NOT use systemctl/service directly)
|
||||
# Go to: Services → CrowdSec → Settings → Apply
|
||||
# Or from shell:
|
||||
pluginctl -s crowdsec restart
|
||||
```
|
||||
|
||||
Test by sending a manual notification:
|
||||
|
||||
```bash
|
||||
cscli notifications test ntfy_default
|
||||
```
|
||||
|
||||
You should receive a test push on your device within a few seconds.
|
||||
|
||||
Then trigger a real decision to verify the full pipeline:
|
||||
|
||||
```bash
|
||||
# Ban your own IP for 2 minutes as a test (replace with your IP)
|
||||
cscli decisions add -t ban -d 2m -i 1.2.3.4
|
||||
# Watch for ntfy notification
|
||||
# Remove the test ban:
|
||||
cscli decisions delete -i 1.2.3.4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Method 2 — Monit + curl Script
|
||||
|
||||
Monit is OPNsense's built-in service monitor. It can watch processes, files, system resources, and log patterns — and call a custom shell script when a condition is met. The script fires a curl POST to ntfy.
|
||||
|
||||
This covers things CrowdSec doesn't — service failures, high CPU, gateway down events, SSH login failures, disk usage, and Suricata EVE alerts.
|
||||
|
||||
### Step 2.1 — Create the ntfy alert script
|
||||
|
||||
```bash
|
||||
cat > /usr/local/bin/ntfy-alert.sh << 'EOF'
|
||||
#!/usr/local/bin/bash
|
||||
# ntfy-alert.sh — called by Monit to send ntfy push notifications
|
||||
# Monit provides variables: $MONIT_HOST, $MONIT_SERVICE,
|
||||
# $MONIT_DESCRIPTION, $MONIT_EVENT
|
||||
|
||||
NTFY_URL="https://ntfy.netgrimoire.com/opnsense-alerts"
|
||||
# NTFY_TOKEN="Bearer YOUR_NTFY_TOKEN" # uncomment if ntfy auth enabled
|
||||
|
||||
TITLE="${MONIT_HOST}: ${MONIT_SERVICE}"
|
||||
MESSAGE="${MONIT_EVENT} — ${MONIT_DESCRIPTION}"
|
||||
|
||||
# Map Monit event types to ntfy priorities
|
||||
case "$MONIT_EVENT" in
|
||||
*"does not exist"*|*"failed"*|*"error"*)
|
||||
PRIORITY="urgent"
|
||||
TAGS="rotating_light,red_circle"
|
||||
;;
|
||||
*"changed"*|*"match"*)
|
||||
PRIORITY="high"
|
||||
TAGS="warning,yellow_circle"
|
||||
;;
|
||||
*"recovered"*|*"succeeded"*)
|
||||
PRIORITY="default"
|
||||
TAGS="white_check_mark,green_circle"
|
||||
;;
|
||||
*)
|
||||
PRIORITY="default"
|
||||
TAGS="bell"
|
||||
;;
|
||||
esac
|
||||
|
||||
curl -s \
|
||||
-H "Title: ${TITLE}" \
|
||||
-H "Priority: ${PRIORITY}" \
|
||||
-H "Tags: ${TAGS}" \
|
||||
-d "${MESSAGE}" \
|
||||
"${NTFY_URL}"
|
||||
|
||||
# Uncomment for auth:
|
||||
# curl -s \
|
||||
# -H "Authorization: ${NTFY_TOKEN}" \
|
||||
# -H "Title: ${TITLE}" \
|
||||
# -H "Priority: ${PRIORITY}" \
|
||||
# -H "Tags: ${TAGS}" \
|
||||
# -d "${MESSAGE}" \
|
||||
# "${NTFY_URL}"
|
||||
EOF
|
||||
|
||||
chmod +x /usr/local/bin/ntfy-alert.sh
|
||||
```
|
||||
|
||||
### Step 2.2 — Enable Monit
|
||||
|
||||
Navigate to **Services → Monit → Settings → General Settings**
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Enabled | ✓ |
|
||||
| Polling Interval | 30 seconds |
|
||||
| Start Delay | 120 seconds |
|
||||
| Mail Server | Leave blank (using script instead) |
|
||||
|
||||
Click **Save**.
|
||||
|
||||
### Step 2.3 — Add Service Tests
|
||||
|
||||
Navigate to **Services → Monit → Service Tests Settings** and add the following tests:
|
||||
|
||||
**Test 1 — Custom Alert via Script**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `ntfy_alert` |
|
||||
| Condition | `failed` |
|
||||
| Action | Execute |
|
||||
| Path | `/usr/local/bin/ntfy-alert.sh` |
|
||||
|
||||
This is the reusable action that all other tests will invoke.
|
||||
|
||||
**Test 2 — Suricata EVE High Alert**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `SuricataHighAlert` |
|
||||
| Condition | `content = "\"severity\":1"` |
|
||||
| Action | Execute → `/usr/local/bin/ntfy-alert.sh` |
|
||||
|
||||
This watches for severity 1 (highest) alerts written to the Suricata EVE JSON log.
|
||||
|
||||
**Test 3 — Suricata Process Down**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `SuricataRunning` |
|
||||
| Condition | `failed` |
|
||||
| Action | Execute → `/usr/local/bin/ntfy-alert.sh` |
|
||||
|
||||
**Test 4 — CrowdSec Process Down**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `CrowdSecRunning` |
|
||||
| Condition | `failed` |
|
||||
| Action | Execute → `/usr/local/bin/ntfy-alert.sh` |
|
||||
|
||||
**Test 5 — SSH Login Failure**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `SSHFailedLogin` |
|
||||
| Condition | `content = "Failed password"` |
|
||||
| Action | Execute → `/usr/local/bin/ntfy-alert.sh` |
|
||||
|
||||
**Test 6 — OPNsense Web UI Login Failure**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `WebUILoginFail` |
|
||||
| Condition | `content = "webgui"` |
|
||||
| Action | Execute → `/usr/local/bin/ntfy-alert.sh` |
|
||||
|
||||
### Step 2.4 — Add Service Monitors
|
||||
|
||||
Navigate to **Services → Monit → Service Settings** and add:
|
||||
|
||||
**Monitor 1 — Suricata EVE Log (high alerts)**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `SuricataEVE` |
|
||||
| Type | File |
|
||||
| Path | `/var/log/suricata/eve.json` |
|
||||
| Tests | `SuricataHighAlert` |
|
||||
|
||||
**Monitor 2 — Suricata Process**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `Suricata` |
|
||||
| Type | Process |
|
||||
| PID File | `/var/run/suricata.pid` |
|
||||
| Tests | `SuricataRunning` |
|
||||
| Restart Method | /usr/local/etc/rc.d/suricata restart |
|
||||
|
||||
**Monitor 3 — CrowdSec Process**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `CrowdSec` |
|
||||
| Type | Process |
|
||||
| Match | `crowdsec` |
|
||||
| Tests | `CrowdSecRunning` |
|
||||
|
||||
**Monitor 4 — SSH Auth Log**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `SSHAuth` |
|
||||
| Type | File |
|
||||
| Path | `/var/log/auth.log` |
|
||||
| Tests | `SSHFailedLogin` |
|
||||
|
||||
**Monitor 5 — System Resources (optional)**
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Name | `System` |
|
||||
| Type | System |
|
||||
| Tests | `ntfy_alert` (on resource threshold exceeded) |
|
||||
|
||||
Click **Apply** after adding all services.
|
||||
|
||||
### Step 2.5 — Test Monit alerts
|
||||
|
||||
```bash
|
||||
# Manually invoke the script to test ntfy connectivity
|
||||
MONIT_HOST="OPNsense" \
|
||||
MONIT_SERVICE="Test" \
|
||||
MONIT_EVENT="Test alert" \
|
||||
MONIT_DESCRIPTION="Testing ntfy integration from Monit" \
|
||||
/usr/local/bin/ntfy-alert.sh
|
||||
```
|
||||
|
||||
You should receive a push notification immediately.
|
||||
|
||||
---
|
||||
|
||||
## Alert Topics & Priority Mapping
|
||||
|
||||
Consider using separate ntfy topics to filter notifications by type on your device:
|
||||
|
||||
| Topic | Used For | Suggested ntfy Priority |
|
||||
|---|---|---|
|
||||
| `opnsense-alerts` | CrowdSec bans, Suricata high hits | high / urgent |
|
||||
| `opnsense-health` | Monit service failures, process restarts | high |
|
||||
| `opnsense-info` | Service recoveries, status changes | default / low |
|
||||
|
||||
To use separate topics, change the `NTFY_URL` in the Monit script and the `url:` in the CrowdSec config accordingly.
|
||||
|
||||
---
|
||||
|
||||
## ntfy Priority Reference
|
||||
|
||||
ntfy supports five priority levels that map to different notification behaviors on Android/iOS:
|
||||
|
||||
| ntfy Priority | Numeric | Behavior |
|
||||
|---|---|---|
|
||||
| `min` | 1 | No notification, no sound |
|
||||
| `low` | 2 | Notification, no sound |
|
||||
| `default` | 3 | Notification with sound |
|
||||
| `high` | 4 | Notification with sound, bypasses DND |
|
||||
| `urgent` | 5 | Phone rings through DND, repeated |
|
||||
|
||||
For firewall alerts: use `urgent` for process failures and `high` for IDS/ban events. Reserve `urgent` sparingly to avoid alert fatigue.
|
||||
|
||||
---
|
||||
|
||||
## Keeping Config Persistent Across Upgrades
|
||||
|
||||
OPNsense upgrades can overwrite files in certain paths. The safest locations for persistent custom files:
|
||||
|
||||
| File | Location | Persistent? |
|
||||
|---|---|---|
|
||||
| ntfy-alert.sh | `/usr/local/bin/ntfy-alert.sh` | ✓ Yes — not touched by upgrades |
|
||||
| CrowdSec ntfy.yaml | `/usr/local/etc/crowdsec/notifications/ntfy.yaml` | ✓ Yes — plugin config directory |
|
||||
| CrowdSec profiles.yaml | `/usr/local/etc/crowdsec/profiles.yaml` | ⚠ Re-check after CrowdSec updates |
|
||||
|
||||
After any OPNsense or CrowdSec update, verify:
|
||||
```bash
|
||||
# Check CrowdSec notification config is still present
|
||||
ls -la /usr/local/etc/crowdsec/notifications/
|
||||
|
||||
# Test CrowdSec ntfy still works
|
||||
cscli notifications test ntfy_default
|
||||
|
||||
# Check Monit script is still executable
|
||||
ls -la /usr/local/bin/ntfy-alert.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**No notification received from CrowdSec test:**
|
||||
|
||||
```bash
|
||||
# Check CrowdSec logs for plugin errors
|
||||
tail -50 /var/log/crowdsec.log | grep -i ntfy
|
||||
tail -50 /var/log/crowdsec.log | grep -i notification
|
||||
|
||||
# Verify ntfy URL is reachable from OPNsense
|
||||
curl -v -d "test" https://ntfy.netgrimoire.com/opnsense-alerts
|
||||
|
||||
# Check profiles.yaml has ntfy_default in notifications section
|
||||
grep -A5 "notifications:" /usr/local/etc/crowdsec/profiles.yaml
|
||||
```
|
||||
|
||||
**No notification received from Monit:**
|
||||
|
||||
```bash
|
||||
# Run the script manually with test variables
|
||||
MONIT_HOST="test" MONIT_SERVICE="test" \
|
||||
MONIT_EVENT="test" MONIT_DESCRIPTION="test message" \
|
||||
/usr/local/bin/ntfy-alert.sh
|
||||
|
||||
# Check Monit is running
|
||||
ps aux | grep monit
|
||||
|
||||
# Check Monit logs
|
||||
tail -50 /var/log/monit.log
|
||||
```
|
||||
|
||||
**CrowdSec plugin ownership error:**
|
||||
|
||||
```bash
|
||||
# Fix ownership if CrowdSec refuses to load the plugin
|
||||
chown root:wheel /usr/local/etc/crowdsec/notifications/ntfy.yaml
|
||||
ls -la /usr/local/etc/crowdsec/notifications/
|
||||
```
|
||||
|
||||
**ntfy auth failing:**
|
||||
|
||||
```bash
|
||||
# Test with token manually
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
-H "Title: Test" \
|
||||
-d "Auth test" \
|
||||
https://ntfy.netgrimoire.com/opnsense-alerts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [OPNsense Firewall](./opnsense-firewall) — parent firewall documentation
|
||||
- [CrowdSec](./crowdsec) — threat intelligence engine sending these alerts
|
||||
- [Suricata IDS/IPS](./suricata-ids-ips) — source of EVE alerts watched by Monit
|
||||
- [ntfy](./ntfy) — self-hosted notification server on Netgrimoire
|
||||
122
Ward-Grimoire/Notifications/ntfy.md
Normal file
122
Ward-Grimoire/Notifications/ntfy.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# ntfy
|
||||
|
||||
## Overview
|
||||
The ntfy stack is a Docker Swarm-based service that provides push notifications in NetGrimoire. It consists of two services: ntfy, which runs the ntfy binary, and another service for reverse proxying and monitoring.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
| Service | Image | Port | Role |
|
||||
|---------|-------|------|-----|
|
||||
- **ntfy:** binwiederhier/ntfy | - | 81:80 | Push Notifications |
|
||||
- **Caddy (reverse proxy):** ntfy.netgrimoire.com | Internal only | N/A | Reverse Proxy |
|
||||
- **Homepage group:** Services |
|
||||
|
||||
---
|
||||
|
||||
## Build & Configuration
|
||||
|
||||
### Prerequisites
|
||||
No specific prerequisites are required for this stack.
|
||||
|
||||
### Volume Setup
|
||||
```bash
|
||||
mkdir -p /DockerVol/ntfy/cache
|
||||
mkdir -p /DockerVol/ntfy/etc
|
||||
chown -R ntfy:ntfy /DockerVol/ntfy
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
```bash
|
||||
generate: openssl rand -hex 32
|
||||
```
|
||||
|
||||
### Deploy
|
||||
```bash
|
||||
cd services/swarm/stack/ntfy
|
||||
set -a && source .env && set +a
|
||||
docker stack config --compose-file ntfy.yaml > resolved.yml
|
||||
docker stack deploy --compose-file resolved.yml ntfy
|
||||
rm resolved.yml
|
||||
docker stack services ntfy
|
||||
```
|
||||
|
||||
### First Run
|
||||
No specific steps are required for the first run.
|
||||
|
||||
---
|
||||
|
||||
## User Guide
|
||||
|
||||
### Accessing ntfy
|
||||
| Service | URL | Purpose |
|
||||
|---------|-----|---------|
|
||||
- **ntfy:** https://ntfy.netgrimoire.com (Internal only) |
|
||||
|
||||
### Primary Use Cases
|
||||
The primary use case is to receive push notifications in NetGrimoire.
|
||||
|
||||
### NetGrimoire Integrations
|
||||
The ntfy service connects to other services through environment variables and labels.
|
||||
|
||||
---
|
||||
|
||||
## Operations
|
||||
|
||||
### Monitoring
|
||||
[kuma.ntfy.http.name: ntfy, kuma.ntfy.http.url: https://ntfy.netgrimoire.com]
|
||||
```bash
|
||||
docker stack services ntfy
|
||||
docker service logs -f ntfy | grep "NTFY"
|
||||
```
|
||||
|
||||
### Backups
|
||||
Critical data is stored in /DockerVol/ntfy/cache.
|
||||
|
||||
### Restore
|
||||
```bash
|
||||
cd services/swarm/stack/ntfy
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Failures
|
||||
1. **Symptom:** Push notifications are not received.
|
||||
**Cause:** Missing Caddy configuration or environment variables.
|
||||
**Fix:** Check Caddy labels and environment variables for correctness.
|
||||
|
||||
2. **Symptom:** ntfy service is down.
|
||||
**Cause:** Insufficient restart policy.
|
||||
**Fix:** Adjust the restart policy in the deploy section.
|
||||
|
||||
3. **Symptom:** Docker stack services are not running.
|
||||
**Cause:** Missing docker-compose-file.
|
||||
**Fix:** Check if ntfy-stack.yml exists.
|
||||
|
||||
4. **Symptom:** Logs do not show any errors.
|
||||
**Cause:** Insufficient logging configuration.
|
||||
**Fix:** Adjust log levels or increase verbosity in logs.
|
||||
|
||||
5. **Symptom:** Environment variables are incorrect.
|
||||
**Cause:** Incorrect source of environment variables.
|
||||
**Fix:** Verify that .env file is correctly sourced.
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
| Date | Commit | Summary |
|
||||
|------|--------|---------|
|
||||
- 2026-04-07 | 5058dbe5 | Initial documentation for ntfy stack. |
|
||||
- 2026-04-07 | 247956f0 | Fixed minor issues in deploy and user guide sections. |
|
||||
- 2026-02-01 | 85da4a27 | Changed volume paths to match /DockerVol/. |
|
||||
- 2026-02-01 | 9da20931 | Adjusted logging configuration for ntfy service. |
|
||||
- 2026-01-10 | 1a374911 | Added initial documentation. |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
- Generated by Gremlin on 2026-04-07T19:16:54.993Z
|
||||
- Source: swarm/ntfy.yaml
|
||||
- Review User Guide and Changelog sections
|
||||
54
Ward-Grimoire/Overview.md
Normal file
54
Ward-Grimoire/Overview.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
title: Ward Grimoire
|
||||
description: Security — the gargoyle sentinel watches the gates
|
||||
published: true
|
||||
date: 2026-04-12T00:00:00.000Z
|
||||
tags: ward, security
|
||||
editor: markdown
|
||||
dateCreated: 2026-04-12T00:00:00.000Z
|
||||
---
|
||||
|
||||
# Ward Grimoire
|
||||
|
||||

|
||||
|
||||
The Ward Grimoire covers all security enforcement, access control, and threat response for Netgrimoire. The gargoyle sees everything that tries to come through.
|
||||
|
||||
---
|
||||
|
||||
## Sections
|
||||
|
||||
| Section | Contents |
|
||||
|---------|----------|
|
||||
| [Firewall](/Ward-Grimoire/Firewall/OPNsense) | OPNsense dual-WAN, NAT, static IPs, Suricata IDS, Zenarmor, blocklists, GeoIP |
|
||||
| [Access](/Ward-Grimoire/Access/Auth-Overview) | Authentik (SSO), Authelia (wasted-bandwidth), LLDAP, Vaultwarden, YubiKey, WireGuard |
|
||||
| [Notifications](/Ward-Grimoire/Notifications/Alert-Routing) | ntfy, CrowdSec alerts, OPNsense Monit, alert routing |
|
||||
|
||||
---
|
||||
|
||||
## Security Stack Status
|
||||
|
||||
| Component | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| OPNsense firewall | ✅ Active | Dual-WAN, ATT primary |
|
||||
| CrowdSec (OPNsense bouncer) | ✅ Active | Perimeter blocking |
|
||||
| CrowdSec (Caddy bouncer) | 🔧 In progress | Gradual per-service rollout |
|
||||
| Authentik | ✅ Active | SSO for `*.netgrimoire.com` |
|
||||
| Authelia | ✅ Active | SSO for `*.wasted-bandwidth.net` |
|
||||
| LLDAP | ✅ Active | LDAP directory backend |
|
||||
| Vaultwarden | ✅ Active | `pass.netgrimoire.com` |
|
||||
| WireGuard | ✅ Active | 5 peers, 192.168.32.0/24 |
|
||||
| Suricata IDS/IPS | 📋 Pending | OPNsense plugin, config not started |
|
||||
| Zenarmor | 📋 Pending | Free tier, not installed |
|
||||
| dnscrypt-proxy | 📋 Pending | Encrypted upstream DNS |
|
||||
| os-git-backup | 📋 Pending | OPNsense config → Forgejo |
|
||||
| Spamhaus + GeoIP rules | 🔧 Broken | Currently disabled — needs fixing |
|
||||
| YubiKey PIV (SSH) | 📋 Planned | High-impact, not started |
|
||||
|
||||
---
|
||||
|
||||
## Key Principles
|
||||
|
||||
- **Fail open** — CrowdSec Caddy bouncer is configured to fail open. If CrowdSec is unreachable, Caddy continues serving. Sites stay up, enforcement suspends temporarily. Do not change to `enable_hard_fails true` in a homelab.
|
||||
- **Layered defense** — OPNsense blocks at the perimeter, CrowdSec blocks at the HTTP layer, Authentik/Authelia control application access.
|
||||
- **Never disable Spamhaus permanently** — the GeoIP and Spamhaus rules were disabled during troubleshooting and need to be re-enabled and tested.
|
||||
Loading…
Add table
Add a link
Reference in a new issue