diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ceffec..f72ecb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- develop --- +* docs: Rewrite the README so each statement matches the code: requirements, install, upgrade and uninstall, transports, pages, settings, poller options, tables, script server template, translations and development commands; move the 1.1 and 1.0 entries from the README to this file * security: Replace the confirmation pages' inline `onClick='cactiReturnTo()'` Cancel buttons with the CSP-safe `cactiReturnTo` class so the pages no longer trip Cacti's Content-Security-Policy script-src-attr directive * security: Move the WMI Accounts and Queries filter rows selects' inline onChange handlers into the ready block so the pages no longer trip Cacti's Content-Security-Policy script-src-attr directive * dev: Measure CI coverage with xdebug instead of pcov so the plugin's own sources are instrumented (pcov auto-scopes to the Composer root and skipped cacti/plugins/, leaving the patch-coverage gate with nothing to measure) @@ -18,3 +19,12 @@ * security: Escape the wmic hostname and namespace before exec so a device-supplied address cannot inject a shell command (issue#5) * security: Quote the wmic delimiter so exec() no longer splits the command into a shell pipeline (issue#5) * security: Restrict decode() unserialize with allowed_classes to block PHP object injection (issue#5) + +--- 1.1 --- + +* issue#1: PHP and SQL backtrace when deleting stored WMI queries or credentials +* issue#2: Issues with passwords containing unescaped special chars + +--- 1.0 --- + +* feature: Initial Support for Cacti 1.0 diff --git a/README.md b/README.md index ac141e8..9b740f4 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,48 @@ # Cacti WMI Plugin for Windows Devices -The WMI Plugin provides data collection services for devices that support the -WMI protocol. It operates asyncrhonously to the Cacti data collection and -stores the resulting information into cache tables. These cache tables can then -be used to generate graphs, thresholds and alerts. It collects over WMI using -one of two transports, chosen automatically from the Cacti server's operating -system (see Transports below). +The WMI Plugin queries Windows devices over WMI from the Cacti poller. It runs +asynchronously to the Cacti data collection and stores the results in cache +tables. It collects over WMI using one of two transports, chosen automatically +from the Cacti server's operating system (see Transports below). ### WARNING: Development in progress This plugin is NOT fully functional. It should not be used on production systems. Use at your own risk! +## Requirements + +- PHP 8.0 or newer. The code uses union types and constructor property + promotion, so it parses on PHP 8.0 and does not parse on 7.4. The CI matrix + runs PHP 8.2, 8.3 and 8.4, and an earlier version of this file said 8.2; use + one of those unless you have tested another. Cacti's own requirement can be + higher: 1.2.31 needs PHP 8.0, the current 1.2.x branch needs 8.2, and Cacti + develop needs 8.3. +- Cacti 1.2. Every Cacti core function the plugin calls exists in the 1.2.0 + release, except `auth_augment_roles()`, which the plugin calls only when it + exists. Installing, enabling, disabling and uninstalling the plugin, and + opening its pages, were run on Cacti 1.2.20 (on PHP 8.1), 1.2.31, the 1.2.x + branch (1.2.32) and Cacti develop (1.3.0). Nothing was run on other + releases. CI runs against the 1.2.x branch. +- The MariaDB or MySQL server Cacti already uses. One of the plugin's tables, + `wmi_processes`, uses the MEMORY engine. +- For a Linux Cacti server, a `wmic` client at `/usr/bin/wmic` (see + Transports). For a Windows Cacti server, PowerShell. + +## Known issues + +- The breadcrumb of the Edit WMI Account page names a parent that does not + exist. On Cacti develop the page fails with a PHP error + (`resolve_navigation_variables()` receives null); on the 1.2 releases it + loads with a shortened title and logs warnings. + ## Cacti compatibility -If you are running a version of Cacti below 1.2.31, please add the function -below to the `applySkin()` function in `include/layout.js` to enable the Cancel -buttons on forms to work: +The Cancel buttons on the plugin's confirmation pages use the `cactiReturnTo` +class. Cacti 1.2.31 and the current 1.2.x branch bind a click handler to that +class in `include/layout.js`. Earlier releases, and Cacti develop, do not. On +those, add the function below to the `applySkin()` function in +`include/layout.js` so the buttons work: ```js $(document).off('click.cactiReturnTo', '.cactiReturnTo') @@ -28,14 +54,42 @@ $(document).off('click.cactiReturnTo', '.cactiReturnTo') ## Installation -To install the plugin, simply copy the plugin_wmi directory to Cacti's plugins -directory and rename it to simply 'wmi'. Once this is complete, goto Cacti's -Plugin Management section, and Install and Enable the plugin. Once this is -complete, you can start to define WMI queries for your Windows and other Device -Templates that support WMI. +Copy the plugin directory into Cacti's `plugins` directory and name it `wmi`; +the plugin loads its files from `plugins/wmi`. Then open Console > +Configuration > Plugins and Install and Enable the plugin, or run from the +Cacti directory: + +``` +php cli/plugin_manage.php --plugin=wmi --install --enable +``` + +The Cacti 1.2.20 release has no `cli/plugin_manage.php`; use the Plugins page +there. + +Installing creates seven tables (see Tables), adds a `wmi_account` column to +Cacti's `host` table, registers the WMI Management realm and the plugin's +hooks, and adds two data input methods, `Get WMI Data` and `Get WMI Data +(Indexed)`. + +Make certain that the `wmic` binary is installed if the Cacti server runs on +Linux. + +## Upgrade + +Replace the plugin files. Cacti does not call the plugin's +`plugin_wmi_upgrade()` function (a search of Cacti 1.2.x and develop finds no +caller), so nothing runs on upgrade. Files that a newer release no longer +ships (`functions.php` and `linux_wmi.php` in the plugin root, listed under +`tombstones` in `manifest.json`) are not removed; delete them by hand. -Make certain that you install the wmic binary if you are planning on running on -Linux. Cacti with PHP 8.2 or newer is required. +## Disable and uninstall + +Disabling the plugin keeps its tables and data. Uninstalling it drops the +seven tables, removes the `wmi_account` column from `host`, and removes the +plugin's hooks and its realm. It also deletes the graphs and data sources +that use the two WMI data input methods. The two data input methods +themselves remain in Cacti. Installing again creates a new realm, so check +the permissions of users other than admin. ## Transports @@ -44,15 +98,22 @@ server's operating system. Code that constructs `Linux_WMI` directly can pass an explicit transport instead. - **wmic** (default on a Linux server). Uses the Linux `wmic` client to reach - Windows hosts over DCOM. Install the `wmi-client` / openwsman package and - keep the binary path in `Linux_WMI` (`/usr/bin/wmic`) in step. This client - predates current Windows hardening; hosts that disable NTLMv1 or enforce the - DCOM packet-integrity level (KB5004442) may reject it. There is currently no - console setting to switch a Linux Cacti server to the PowerShell transport - for such hosts; it is only available to code that constructs `Linux_WMI` - directly and passes an explicit transport (see below). -- **PowerShell / CIM** (default on a Windows server). Uses `Get-CimInstance` - through `pwsh` or `powershell.exe`, so it works when Cacti runs on Windows and + Windows hosts over DCOM. The client does not appear in the Debian 12 or Ubuntu + 18.04 package lists, so install it from your own package source or build + it. The Query Tool and the poller take the binary from the `path_wmi` + setting and use `/usr/bin/wmic` when it is empty; `path_wmi` has no field on + the Settings page yet. `wmi_script.php` always uses `/usr/bin/wmic`, and `script/wmi-script.php` + looks for a `wmic` inside the plugin directory, which is not shipped. The + user name and password are passed to `wmic` as `--user` and `--password` + arguments, so a local user can read them in the process list while a query + runs. This client predates current Windows hardening. Hosts that disable + NTLMv1 or enforce the DCOM packet-integrity level (KB5004442, enforced + without an opt-out since March 2023) may reject it. There is currently no + console setting to switch a Linux Cacti server to the PowerShell transport; + it is only available to code that constructs `Linux_WMI` directly and + passes an explicit transport. +- **PowerShell / CIM** (default on a Windows server). Runs `powershell.exe` + and uses `Get-CimInstance`, so it works when Cacti runs on Windows and against a remote host over WinRM/CIM. Needs PowerShell on the Cacti server and, for a remote host, WinRM enabled on the target. Credentials and every device-supplied value are passed through the child process environment and a @@ -66,13 +127,16 @@ Device Template, you can add those queries that are significant for a general Windows device. If you have a Microsoft SQL Server Device Template, you can add WMI Queries that are specific to Windows SQL Server there. -There is presently only one setting support in the WMI plugin, and that is to -Autocreate the actual WMI polling at device creation or update based upon the -WMI Queries defined as a part of the Device Template. This setting can be found -under Settings -> Device Defaults. - -Credentials are added under WMI Authentication, and the WMI Query Tool under -Utilities runs an ad-hoc query against a device for testing. +- Console > Data Collection > WMI Queries lists and edits the queries. The + Authentication tab on the same page holds the Windows accounts (user name and + password) that devices use. +- Console > Utilities > WMI Query Tool runs an ad-hoc query against a host for + testing. +- The Device Template page lists the queries associated with the template + (Associated WMI Queries), with a delete link and an Add button. +- The Device page has a WMI Authentication Account field. A device is only + polled when an account is selected. The page also shows which of the + template's queries exist for the device. ## Access @@ -80,27 +144,127 @@ The plugin registers a WMI Management realm covering account management, query definition, and the query tool. The Template Editor role is additionally granted the query-definition page only; credential management and the live query tool stay behind the WMI Management realm. The poller needs the host password -in cleartext, so accounts are stored reversibly, the same posture Cacti core -uses for SNMP v3 credentials; protect the database accordingly. +in cleartext, so accounts are stored reversibly (base64 of a serialized array), +not hashed or encrypted; protect the database accordingly. -## Authors +## Settings -The wmi plugin has been was developed several years ago but never actually -finished. This release formalizes it's behavior and provides a new level of -scalability to Cacti when gathering data from WMI compatible device's. +The plugin adds three settings to the Misc tab of Settings: -## ChangeLog +| Setting | Name | Default | Effect | +|---------|------|---------|--------| +| Enable WMI Data Collection | `wmi_enabled` | on | Not read by any code yet. | +| Concurrent Processes | `wmi_processes` | 10 | Most collector processes that run at once (1 to 10, then steps of 5 up to 50). | +| Auto Create WMI Queries | `wmi_autocreate` | on | Read by a hook that does nothing yet, so queries are not created automatically. | + +The poller also reads `wmi_autopurge` (when `on`, it deletes cache rows of +devices that no longer exist), and the Query Tool and poller read `path_wmi`. +Neither has a field on the Settings page. + +## Data collection + +At the end of each polling cycle on the main poller, the `poller_bottom` hook +starts `poller_wmi.php -M` in the background. The master selects devices that +are not down and not disabled, have an account, have a template query that is +enabled and due (its frequency has passed since it last started), and starts one +`poller_wmi.php --host-id=N` child per device, at most `wmi_processes` at a +time. Children run the due queries and store the rows in `host_wmi_cache`. A +summary goes to the `stats_wmi` setting and the Cacti log (`WMI STATS`). + +Running collectors are tracked in the `wmi_processes` table. The master exits +with `Another WMI Collector is still running` while that table has any row. A +row left behind by a collector that was killed never expires; delete the rows +by hand when no collector is running: + +``` +DELETE FROM wmi_processes; +``` ---- 1.1 --- +`poller_wmi.php` options: -* issue#1: PHP and SQL backtrace when deleting stored WMI queries or credentials +| Option | Meaning | +|--------|---------| +| `-M` | Master run: poll every device that is due. | +| `--host-id=N` | Child run for one device (required without `-M`). | +| `--seed=N`, `--key=N`, `-s=N` or `--start=N` | Set by the master for its children. Write the value after an `=`. | +| `-f`, `--force` | Accepted and passed on to children; it does not change which devices are polled. | +| `-d`, `--debug` | Accepted and passed on to children; no message uses it yet. | +| `-V`, `-v`, `--version` | Print the version. | +| `-H`, `-h`, `--help` | Print usage. It lists only `-M`, `--host-id`, `--seed`, `-f` and `-d`. | -* issue#2: Issues with passwords containing unescaped special chars +## Tables +| Table | Holds | +|-------|-------| +| `wmi_user_accounts` | Accounts: name, user name, password. | +| `wmi_wql_queries` | Queries: name, frequency, enabled, namespace, WQL text, primary key. | +| `host_template_wmi_query` | Queries associated with a Device Template. | +| `host_wmi_query` | When each query last started, how long it ran and when it last failed, per device. | +| `host_wmi_cache` | The collected rows. | +| `wmi_processes` | Collectors that are running (MEMORY engine). | +| `host_wmi_accounts` | Created but not used by any code. | ---- 1.0 --- +## Script server template + +`templates/resource/script_server/exchange_messages.xml` is a sample Script +Server query for Exchange mail stores. Nothing in the plugin copies or +imports it. Its `arg_prepend` names a WMI query called `exchangemessages`, so +a query with that name must exist. Its `script_path` is +`|path_cacti|/scripts/wmi-script.php`, which does not exist; the plugin ships +`script/wmi-script.php`. The two functions that generate such XML from a saved +query, `plugin_wmi_create_dataquery_xml()` and +`plugin_wmi_create_resource_xml()`, are not called from anywhere. + +## Translations + +`locales/po` holds the translation files and `locales/LC_MESSAGES` the +compiled `.mo` files. `locales/build_gettext.sh` rebuilds the template +`locales/po/cacti.pot`, merges it into the `.po` files and compiles the `.mo` +files. It needs GNU gettext, `realpath` and GNU `sed`. It was written for +Cacti core, so in a plugin it prints `cat: include/cacti_version: No such file +or directory` and leaves the package version in the template empty. + +## Development + +CI (`.github/workflows/plugin-ci-workflow.yml`) installs Cacti from the 1.2.x +branch, installs and enables the plugin, then runs these checks from the +plugin directory: + +``` +php tests/bin/validate-manifest.php +php tests/bin/check-i18n-pot.php +``` + +`validate-manifest.php` fails when the top-level entries of the plugin differ +from the `expected` list in `manifest.json`, in either direction, so run it +before the tests create a `coverage` directory in the plugin. +`check-i18n-pot.php` fails when a change adds or removes a translatable string +without updating `locales/po/cacti.pot`. + +The tests run with Pest from the Cacti directory, with `tests/.cacti-version` +set to `1.2.x` or `develop`: + +``` + --configuration=plugins/wmi/phpunit.xml --coverage-clover=plugins/wmi/coverage/clover.xml +``` + +`` is the Pest binary of Cacti's `tests/tools/plugin-harness` for Cacti +1.2.x, or `include/vendor/bin/pest` in a Cacti develop checkout installed with +its development dependencies. The patch coverage check then compares against +Git, so run it from the plugin directory; it fails when the lines a change adds +are not all covered: + +``` +php tests/bin/patch-coverage.php coverage/clover.xml 100 +``` + +## Authors + +The wmi plugin was developed several years ago but never finished. + +## ChangeLog -* feature: Initial Support for Cacti 1.0 +See `CHANGELOG.md`. ----------------------------------------------- Copyright (c) 2004-2026 - The Cacti Group, Inc.