diff --git a/docs/PIHOLE_GUIDE.md b/docs/PIHOLE_GUIDE.md index d1aec544d..ba085b3ae 100755 --- a/docs/PIHOLE_GUIDE.md +++ b/docs/PIHOLE_GUIDE.md @@ -1,6 +1,6 @@ # Integration with Pi-hole -NetAlertX includes three plugins for integrating with an existing Pi-hole installation. The first plugin imports devices through the Pi-hole v6 API, the second parses the `dhcp.leases` file generated by Pi-hole, and the third reads the Pi-hole SQLite database directly. You can use any of these approaches individually or combine them with each other and other [plugins](/docs/PLUGINS.md). +NetAlertX includes four plugins for integrating with an existing Pi-hole installation. The first plugin imports devices through the Pi-hole v6 API, the second parses the `dhcp.leases` file generated by Pi-hole, the third reads the Pi-hole SQLite database directly, and the fourth flags devices with a blocked-query spike (a common malware/compromised-device signature) and, alongside that, imports devices from one or two Pi-hole v6 instances at once. You can use any of these approaches individually or combine them with each other and other [plugins](/docs/PLUGINS.md). ## Approach 1: `PIHOLEAPI` Plugin - Import devices directly from the Pi-hole v6 API @@ -75,4 +75,22 @@ Check the [PIHOLE plugin README](https://github.com/netalertx/NetAlertX/tree/mai --- +## Approach 4: `PIHOLEMON` Plugin - Blocked-query anomaly detection (plus device import from one or two Pi-hole v6 instances) + +This plugin's main job is different from the other three above: it watches each device's own blocked-query count and flags it when that count spikes well above its recent average - a common signature of malware or a compromised device beaconing out to blocklisted domains, not just device discovery. Bundled into the same connection is a second job, the same idea as `PIHOLEAPI`'s device import, extended to a primary **and** an optional secondary/failover Pi-hole instance under one set of settings. + +**How it differs from `PIHOLEAPI`:** + +* **Anomaly detection is the point, not a bonus.** `PIHOLEAPI` only imports devices. This plugin's reason to exist is watching a device you *already* know for a sudden change in its own behavior - device import is the second, supporting job, not the main one. +* **Two Pi-hole instances, not one.** A primary and an optional secondary/failover are both checked and their results combined under one set of settings - useful if you run more than one Pi-hole (e.g. two resolvers for redundancy) and want a single device list and a single anomaly baseline across both, instead of a blind spot on whichever instance isn't being watched. +* **Choose `PIHOLEAPI`** if you only run one Pi-hole and just want device import - it's the simpler, more focused option. **Choose `PIHOLEMON`** if you want the anomaly detection, or run two Pi-holes needing one configuration, or both. + +Check the [PIHOLEMON plugin README](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/pihole_monitor/) for the full settings reference, the `http://` vs `https://` trade-off, and troubleshooting. + +### docker-compose changes + +No changes are required - this plugin only talks to Pi-hole's API, the same as `PIHOLEAPI`. + +--- + Explore other [plugins](/docs/PLUGINS.md) to discover additional information about your network, or learn how to scan [remote networks](./REMOTE_NETWORKS.md). diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md index a9c77dd19..f9e675e8f 100755 --- a/docs/PLUGINS.md +++ b/docs/PLUGINS.md @@ -78,6 +78,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T | `OMDSDNOPENAPI` | [omada_sdn_openapi](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/omada_sdn_openapi/) | 📥/🆎 | OMADA TP-Link import via OpenAPI | 🖧 | | | `PIHOLE` | [pihole_scan](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/pihole_scan/) | 🆎/📥 | Pi-hole device import & sync | | | | `PIHOLEAPI` | [pihole_api_scan](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/pihole_api_scan/) | 🆎/📥 | Pi-hole device import & sync via API v6+ | | | +| `PIHOLEMON` | [pihole_monitor](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/pihole_monitor/) | 🆎/📥 | Blocked-query anomaly detection (includes primary and secondary DNS import from Pi-hole) | | | | `PUSHSAFER` | [_publisher_pushsafer](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/_publisher_pushsafer/) | ▶️ | Pushsafer notifications | | | | `PUSHOVER` | [_publisher_pushover](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/_publisher_pushover/) | ▶️ | Pushover notifications | | | | `RSTIMPRT` | [rest_import](https://github.com/netalertx/NetAlertX/tree/main/server/plugins/rest_import/) | 📥/🆎 | Import via a REST API endpoint | 🖧 | | diff --git a/server/plugins/pihole_monitor/README.md b/server/plugins/pihole_monitor/README.md new file mode 100644 index 000000000..09af2cfdf --- /dev/null +++ b/server/plugins/pihole_monitor/README.md @@ -0,0 +1,128 @@ +## Overview - PIHOLEMON Plugin — Pi-hole Monitor + +The **PIHOLEMON** plugin does two jobs against the same Pi-hole connection(s): + +1. **Query anomaly detection** - flags a device whose *blocked*-query count spikes well above its own recent average, the classic signature of malware or a compromised device beaconing out to blocklisted domains. This is deliberately not the same thing as device discovery: it's about a device you already know suddenly behaving differently, not a new device showing up. +2. **Device import** - same job as the official **PIHOLEAPI** (`pihole_api_scan`) plugin: pulls the device list from Pi-hole and feeds it into NetAlertX's normal device-scanner pipeline, so a device Pi-hole knows about but NetAlertX doesn't gets created automatically. + +Both share one login per Pi-hole instance and one settings page, instead of being two separately configured pieces that happen to need the same credentials. + +Two design choices worth knowing about: + +* **Both a primary and an optional secondary/failover Pi-hole are checked, and their results combined.** Watching only one leaves an obvious blind spot for the anomaly detection - a device can simply point at the other resolver and never show up. Leave the secondary URL blank if you only run one Pi-hole; most setups do. +* **The anomaly baseline is keyed by MAC address, not IP.** DHCP-assigned IPs change; since this plugin already has the device list from Pi-hole itself, it resolves IP to MAC from that same data - no separate lookup needed for that part. A device owner, if you use NetAlertX's `devOwner` field, is looked up via NetAlertX's own GraphQL API purely to make the anomaly label friendlier; it's optional and never blocks import or detection if unavailable. + +### Why not just run two copies of PIHOLEAPI for two Pi-holes? + +PIHOLEAPI doesn't support running two independent instances against different Pi-holes - both copies would read and write the same settings keys. PIHOLEMON was built to support a primary and secondary instance natively from the start. + +### Quick setup guide + +* You are running **Pi-hole v6** or newer on every instance you configure (this plugin uses `/api/auth`, `/api/network/devices`, and `/api/stats/top_clients`, none of which exist in v5). +* An **App Password** is generated on each Pi-hole (`Settings → Web Interface / API → App Password`) - recommended over using the admin login password directly. +* Like every non-core plugin, **When to run** (`PIHOLEMON_RUN`) defaults to `disabled` - set it to `schedule` (or another option) once your URL/password are filled in, or nothing runs. + +#### 🔒 A note on `http://` vs `https://` + +Most home Pi-hole setups (including the one this plugin was developed and tested against) run over plain `http://` on a trusted LAN, and that's what the examples below use - this plugin doesn't require `https://` or refuse an `http://` URL. Know the trade-off either way, though: over `http://`, the App Password/admin password is sent in cleartext on every run, readable by anything else that can see that network segment (a compromised device, a hostile guest network, etc.). If your Pi-hole's admin interface is reachable from anywhere less trusted than your own LAN, either put it behind `https://` (Pi-hole's own self-signed cert, or a reverse proxy with a real one) or keep it LAN-only. **Verify SSL** only matters once an instance is on `https://` - it's on by default, per instance (primary and secondary can each be on `http://` or `https://` independently), and turning it off to tolerate a self-signed cert accepts *any* certificate, including an attacker's; installing that self-signed CA as trusted on the machine running NetAlertX is the safer way to use a self-signed cert if you need one. + +### Usage + +- Head to **Settings** > **Pi-hole Monitor** to fill in the values below. + +| Setting Key | Description | +| ---------------------------------- | --------------------------------------------------------------------------------------------------- | +| **PIHOLEMON_PRIMARY_URL** | Required. URL to your primary Pi-hole, e.g. `http://192.168.1.10/`. | +| **PIHOLEMON_PRIMARY_PASSWORD** | App Password (or admin password) for the primary Pi-hole. | +| **PIHOLEMON_PRIMARY_VERIFY_SSL** | Verify TLS certificates on the *primary* instance's `https://` URL. Default **on**. Only disable if that Pi-hole uses a self-signed certificate you can't install as trusted - see the security note below. | +| **PIHOLEMON_SECONDARY_URL** | Optional. URL to a secondary/failover Pi-hole, e.g. `http://192.168.1.11:8080/`. Leave blank if you only run one. | +| **PIHOLEMON_SECONDARY_PASSWORD** | Only used if a secondary URL is set. | +| **PIHOLEMON_SECONDARY_VERIFY_SSL** | Same, for the *secondary* instance. Only used if a secondary URL is set. Default **on**. | +| **PIHOLEMON_GET_OFFLINE** | Import devices even if not recently seen. Default off. | +| **PIHOLEMON_CONSIDER_ONLINE** | Seconds since last seen to still count a device as online. Default `300`. | +| **PIHOLEMON_API_MAXCLIENTS** | Maximum devices requested **per instance**'s device list. Default `500`. | +| **PIHOLEMON_FAKE_MAC** | Generate a fake MAC from the IP for devices with a non-standard hardware address. Default off. | +| **PIHOLEMON_GET_OWNER** | Look up an already-known device's owner for a friendlier anomaly label. Default **on**. Uses this app's own **GRAPHQL_PORT** and **API_TOKEN** settings (Settings → General) automatically - nothing else to configure. Disable if you don't use device owners. | +| **PIHOLEMON_MULTIPLIER** | Flag a device when the blocked queries it generated *since the last run* exceed this many times its own recent per-run average. Default `4`. | +| **PIHOLEMON_MIN_BLOCKED** | Ignore devices below this many blocked queries since the last run, even if the multiplier is exceeded. Default `20` - depends heavily on your Schedule (see the note below the settings table). | +| **PIHOLEMON_HISTORY_DAYS** | How many days of recent runs to keep per device for the rolling baseline. A real time window, not a sample count - means the same thing regardless of your Schedule setting, and a faster schedule just adds more data points inside it. Default `7` (one week). | +| **Watched** *(standard NetAlertX setting)* | Which columns count as "changed" for notification purposes. Defaults to `watchedValue4` (the anomaly/normal flag) only - the per-run blocked-query delta changes every run by design. | + +#### A note on the blocked-query count: per-run delta, not a running total + +Pi-hole's `/api/stats/top_clients` returns a count that's cumulative since Pi-hole's FTL service last started - it does not reset daily, and it's not a "since I last checked" delta. This plugin diffs each run's raw count against the one from its last run to get a real per-run increment, which is what `PIHOLEMON_MULTIPLIER`/`PIHOLEMON_MIN_BLOCKED` actually compare against - comparing the raw cumulative totals directly would make any device's traffic look like a growing "anomaly" purely from the counter never resetting. The very first run for a device, and any run right after Pi-hole/FTL restarts (the counter resets, so the raw count can drop below what was last seen), can't produce a valid delta - those runs establish a new reference point instead of evaluating an anomaly. +| **Report on** *(standard NetAlertX setting)* | Which statuses actually notify. Defaults to `watched-changed` only, so you hear about it exactly when a device flips into (or out of) an anomaly. | + +This plugin does **not** send notifications on its own - it relies on NetAlertX's own core, which diffs the columns picked in **Watched** between runs and, on a match against **Report on**, dispatches through whichever publisher(s) you already have enabled under **Settings → Notifications** (ntfy, Apprise, email, Telegram, ...). + +One extra setting is required for that to actually reach you: NetAlertX's **Notification Processing** plugin (`NTFPRCS`) has its own **"Notify on"** setting (`NTFPRCS_INCLUDED_SECTIONS`), and its default value - `new_devices`, `down_devices`, `events` - does **not** include `plugins`. Without `plugins` in that list, this plugin's Watched/Report on matches are recorded correctly but never make it into a notification. Add `plugins` to `NTFPRCS_INCLUDED_SECTIONS` once, and it also covers any other plugin using the same mechanism, not just this one. + +The default text notification for the `plugins` section is a generic vertical `Header: Value` dump. For something more readable, set `NTFPRCS`'s **"Text Template: Plugins"** (`NTFPRCS_TEXT_TEMPLATE_plugins`) to something like: + +``` +{objectPrimaryId} [{watchedValue2}] @ {objectSecondaryId} → {watchedValue4} ({watchedValue3} blocked) +``` + +Only the columns the `plugins` section actually selects are available as placeholders (`{plugin}`, `{objectPrimaryId}`, `{objectSecondaryId}`, `{dateTimeChanged}`, `{watchedValue1-4}`, `{status}`) - `extra` (where this plugin's `owner`/`ratio` detail lives) isn't one of them, so that richer text is only visible on the device's page in NetAlertX, not in the notification itself. This template setting is global to `NTFPRCS`, so it affects any plugin using Watched/Report on, not just this one. + +If the same device (same MAC) is seen on both instances, the entry with the more recent "last seen" timestamp wins - it isn't imported twice, and its blocked-query counts from both instances are summed, not compared separately. + +### Testing the notification pipeline end-to-end + +Waiting for real, organic beaconing traffic to confirm notifications actually arrive isn't practical. The clean way to force a `normal` → `anomaly` transition on demand, without touching any internal state file by hand: + +1. Temporarily set `PIHOLEMON_MULTIPLIER` to something like `1.01` and `PIHOLEMON_MIN_BLOCKED` to `1`. +2. Run the plugin (wait for its schedule, or trigger it manually from the UI). +3. Any device with a baseline and *any* blocked traffic should now flip to `anomaly` - confirming the whole chain: Watched/Report on match → `Plugins_Events` row → NetAlertX's Notification Processing (`NTFPRCS`, needs `plugins` in `NTFPRCS_INCLUDED_SECTIONS`, see above) → your configured publisher. +4. Set `PIHOLEMON_MULTIPLIER`/`PIHOLEMON_MIN_BLOCKED` back to their real values afterward - left at the test values, everything with any traffic at all reads as an anomaly. + +### ⚠️ Troubleshooting + +--- + +#### ❌ Authentication failed / no data from a Pi-hole instance + +* Confirm the URL includes the scheme (`http://`/`https://`) and, if not on the default port, the port too - e.g. `http://192.168.1.10/` ✔, `http://192.168.1.10/admin` ❌. +* Confirm that instance is running **Pi-hole v6**, not v5. +* SSL verification matches your setup (disable for self-signed certificates). +* Try the App Password by hand first: `curl -X POST /api/auth -d '{"password":""}'` should return a `session.sid`. +* Check the plugin log for `[PIHOLEMON]