--- 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