Metadata-Version: 2.4
Name: monsterops-compliance
Version: 0.2.0
Summary: Configuration-compliance / drift plugin for MonsterOps — checks managed NAS devices against operational policies.
Author-email: NLRI65000 <nlrigithub@hotmail.com>
License: MIT License
        
        Copyright (c) 2026 NLRI65000
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://monsterops.org
Project-URL: Documentation, https://monsterops.org/docs/plugins.html
Project-URL: Repository, https://github.com/NLRI65000/monsterops-compliance
Keywords: monsterops,compliance,radius,nas,drift,plugin
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: monsterops>=1.18.0
Dynamic: license-file

<p align="center">
  <img src="https://raw.githubusercontent.com/NLRI65000/MonsterOps/main/assets/MonsterOpsDO.png" alt="MonsterOps Compliance" width="460" />
</p>

# MonsterOps Compliance

A **configuration-compliance / drift plugin for [MonsterOps](https://monsterops.org)**. It periodically
checks your managed NAS devices against configurable operational policies (SSH enabled, Telnet disabled,
NTP configured, no default SNMP community, …) and reports where a device has drifted out of compliance.

It's a full multi-surface plugin — a good example of the MonsterOps plugin contract in anger:

- **worker** — a periodic sweep, run on the **active HA node** only and crash-guarded by the host
- **tables** — its own `mo_compliance_*` schema (rules + latest results)
- **router** — rules CRUD, results, and a "run now" trigger
- **UI** — a *Compliance* nav page (under **Network**): results matrix, rule management, and **run
  reports** (per-sweep history with timing, drilling into per-device saved command output) — **plus a
  Dashboard widget** (a compliance summary card on the main dashboard, via the host's `dashboardWidget`
  manifest hook)

It reuses NAS Manager's proven SSH path (host-key pinning, credential decryption, netmiko) to reach
devices, so it honestly reports `touches: nas_manager` in *System → Plugins*.

## Install & enable

Requires **MonsterOps >= 1.18.0** (the plugin loader that creates the
`mo_compliance_*` tables and mounts the UI). This floor is declared as a dependency,
so pip refuses to install against an older host rather than loading a plugin whose
tables never get created.

```bash
pip install monsterops-compliance          # into the MonsterOps environment
```

```ini
# MonsterOps .env
MONSTEROPS_PLUGINS=compliance
# optional — how often the sweep runs (default 30, minimum 10):
MONSTEROPS_COMPLIANCE_INTERVAL_MINUTES=30
# optional — how many devices to sweep in parallel (default 10):
MONSTEROPS_COMPLIANCE_CONCURRENCY=10
```

Restart MonsterOps. A **Compliance** entry appears in the nav; a set of default MikroTik rules is seeded
on first run.

## Rules

A rule runs a read-only command on every device of its vendor and checks the output:

| Field | Meaning |
|---|---|
| `vendor` | applies to devices whose netmiko type contains this (`mikrotik` → `mikrotik_routeros`); blank = all |
| `check_command` | the read command to run, e.g. `/ip service get ssh disabled` |
| `match_type` | `equals` (trimmed, case-insensitive), `contains`, or `not_contains` |
| `expected` | the value to match |

The seeded defaults (editable/removable in the UI):

```yaml
- name: SSH must be enabled
  vendor: mikrotik
  check: "/ip service get ssh disabled"
  match: equals "false"
- name: Telnet must be disabled
  vendor: mikrotik
  check: "/ip service get telnet disabled"
  match: equals "true"
- name: NTP must be configured
  vendor: mikrotik
  check: "/system ntp client print"
  match: contains "enabled: yes"
- name: SNMP community must not be default
  vendor: mikrotik
  check: "/snmp community print"
  match: not_contains "public"
```

## How it works

```
worker (every N min, active HA node)
      → find enabled managed NAS
      → sweep up to MONSTEROPS_COMPLIANCE_CONCURRENCY devices in parallel
          · per device: one SSH session, run its applicable checks
          · evaluate each against its rule
      → upsert the current results (pass / fail / error)
      → record a run report (timing + per-device saved command output)
      → emit `compliance.drift` on a newly-failing check, `compliance.run` per sweep
```

Results are the *current* state per (device, rule); a device that can't be reached records `error` (not
`fail`). Trigger a sweep any time from the UI or `POST /api/plugins/compliance/run`.

### Run reports

Every sweep is saved as a **run report** (the newest 50 are kept): when it ran, whether it was scheduled
or manual, **how long it took**, and the per-device breakdown. Open one from *Recent runs* on the
Compliance page and drill into a device to see the **actual command output** each check was judged on —
the evidence behind a pass, fail, or error. Exposed at `GET …/runs`, `GET …/runs/{id}`, and
`GET …/runs/{id}/devices/{device_id}`.

### Scale

Devices are swept in parallel, bounded by `MONSTEROPS_COMPLIANCE_CONCURRENCY` (default 10), each on its
own SSH thread from a **dedicated** pool so a large sweep never starves the app's shared executor. A sweep
already in progress is never restarted — a trigger that arrives while one is running (the periodic tick or a
manual *Run now*) is skipped rather than stacked.

The database side is cheap (a few hundred devices upsert in ~1–2 s); the sweep's wall-time is
`devices ÷ concurrency × per-device-SSH`. At a realistic ~2–4 s/device the default handles a few hundred
devices well within a 30-minute interval; raise the concurrency to 25–50 for larger fleets. The interval
has a **10-minute floor** — drift is slow-moving and even a 500-device sweep finishes in ~2 min, so a
shorter interval only loads the devices for no fresher a signal.

**Measured** (2 s/device, 6-core box): 300 devices in **62 s** and 500 in **102 s** at the default
concurrency of 10; **25 s** / **42 s** at concurrency 25. See **[docs/benchmark.md](docs/benchmark.md)**
for the full table, the formula to project onto your own fleet, and interval guidance — and
[`bench/`](bench/) to reproduce it.

## API

`GET/POST/PUT/DELETE /api/plugins/compliance/rules` · `GET …/results` · `GET …/summary` ·
`POST …/run` — all admin/superadmin, reusing the host's RBAC.

## Notes & roadmap

- Like any plugin it runs **in-process with full MonsterOps trust**; its footprint is visible in
  *System → Plugins*.
- A **Dashboard widget** shows a compliance summary (checks / passing / failing / errored) on the main
  dashboard, contributed through the host's `dashboardWidget` manifest hook and toggleable from the
  dashboard's *Customize* menu.
- **Scale** is handled by bounded-concurrency SSH (`MONSTEROPS_COMPLIANCE_CONCURRENCY`) on a dedicated
  thread pool plus a skip-if-still-running guard — see the *Scale* section above and [`bench/`](bench/).
  A very large fleet (thousands) would still want chunked/incremental sweeps; not needed yet.

## From source

```bash
pip install -e .          # into the MonsterOps environment
```

MIT-licensed.
