Skip to content

Repository files navigation

audit

This plugin is to be used to track transactions in the Cacti database, when they were made, by what IP address and by what login account. This can be used to determine the root cause of issues created by users of the Cacti system.

Purpose

This plugin allows Cacti Administrators to log user change activity

Features

  • Log all $_POST activities to the Cacti Server that can change the contents of the system.

  • Captures CLI commands and who ran them

Audit entries generated by the config_insert hook begin with request status started and are finalized as completed or failed when PHP shuts down. A completed request does not necessarily prove that every page-specific database operation succeeded.

Installation

Install just like any other plugin, just throw it in the plugin directory, and Install and Enabled from the Plugin Management Interface. Make sure the plugin directory is named 'audit' and not 'plugin_audit'.

Once this is done, you have to goto Configuration -> Settings -> Audit and define data retention and turn on auditing.

You can also enable file-based logging for ingestion by SIEM or log-analysis tools such as Splunk. External records can be written as newline-delimited JSON (one JSON object per line) or as single-line text using quoted key="value" fields. Control characters in text values are escaped so every event remains on one line. In JSON output, post and object_data are native nested structures, not JSON-encoded strings. JSON is the default to preserve the format used by earlier releases.

External file delivery is tracked on each database record. Failed appends are retried by the poller in batches and therefore have at-least-once delivery semantics; downstream ingestion should deduplicate when necessary.

Version 1.4 records a stable event UUID and request correlation UUID on new events. External records are written only after request finalization, so SIEM consumers receive the final request status instead of the earlier transient started state. Consumers should deduplicate on event_uuid.

Remote Syslog

Version 1.5 adds standards-based Remote Syslog as the plugin's only remote network transport. The plugin does not implement product-specific webhooks, Splunk HEC, or Microsoft Sentinel APIs. Splunk, Sentinel, and other SIEM products can ingest the existing audit file or receive Syslog through their normal collector or forwarder.

Remote Syslog is configured under Configuration -> Settings -> Audit by a user with the Audit Log Admin permission. It is independent of the existing external-file option, so either destination or both destinations may be enabled.

The available settings include:

  • Receiver hostname or IP address and an optional explicit port.
  • UDP, TCP, or TLS transport. A blank port uses 514 for UDP/TCP or 6514 for TLS.
  • RFC 5424 structured data, CEF, or compact JSON message payload.
  • Syslog facility, application name, stable Cacti node identity, and poller identity.
  • Bounded connection/write timeout, poller batch size, retry attempts, and exponential-backoff delays.
  • Maximum UDP record size and health-warning thresholds.
  • Optional TLS CA bundle and mutual-TLS client certificate/key paths.

All records use an RFC 5424 header. TCP and TLS records use RFC 6587 octet-count framing. TLS always verifies the receiver certificate and hostname; there is no setting to disable verification.

CEF records include the same redacted submitted request data, captured object data, and event details available to JSON consumers in the cs4, cs5, and cs6 custom string fields. The corresponding labels are Submitted Data, Object Data, and Details.

UDP sends one complete event per datagram. If a formatted event is larger than the configured UDP maximum, it is moved to dead-letter instead of being truncated or split. TCP or TLS is recommended when events can be large or when transport reliability matters.

Remote network I/O does not run in the originating web request. Finalized events are added to audit_syslog_delivery and sent in bounded batches by the Cacti poller. Transient connection and write errors use bounded exponential backoff. Permanent formatting/configuration errors and exhausted retries enter dead-letter state. Audit Log Admin can test the stored configuration and reset dead-letter events from the Audit Log page; both actions require CSRF-protected POST requests and are audited.

A successful UDP, TCP, or TLS socket write is shown as sent_unconfirmed. This means the local operating system accepted the complete record; standard Syslog does not confirm that a receiver durably stored it. Every transmission retains the same event_uuid, which receivers should use as their deduplication key.

The Audit Log page reports pending, retry, sent-unconfirmed, and dead-letter counts, oldest pending age, last attempt, last socket write, and the latest bounded error. Scheduled retention and manual purge preserve events with pending, retry, or dead-letter Syslog delivery.

The normalized fields distinguish request processing from the result of the requested Cacti operation. request_status=completed means that PHP request processing completed without a fatal error or an HTTP error response. It does not by itself prove that page-specific validation or database work succeeded. operation_outcome remains unknown unless an authoritative Cacti 1.2.x hook or plugin-owned operation supplies the result.

For direct user realm permission saves, the plugin verifies the resulting user_auth_realm rows against the submitted realm set after Cacti processes the request. Matching state is recorded as success with outcome reason realm_permissions_verified; a mismatch is recorded as failure.

The plugin also audits access to its own event list, searches, event details, exports and purge operations. Logout and session-timeout events are captured through Cacti's supported logout_pre_session_destroy hook, and session teardown is confirmed through the logout_post_session_destroy hook.

Login failure, token (remember-me and 2FA), credentials-accepted, and authorization-denied events are captured by polling Cacti's user_log table from the poller and through the custom_denied hook. The user_log table is the authoritative source across all Cacti authentication methods (local, LDAP, basic, and domains) and is stable across the 1.2.x and develop branches, so the plugin does not rely on the local-auth-only login_process hook. Authentication ingestion and failed-login volume detection are opt-in. The install/upgrade watermark starts at the current time, so enabling the feature does not backfill historical login records.

Cacti writes user_log result = 1 before verifying that the account is enabled, authorized for any realm, or has completed 2FA. The plugin therefore records this as cacti.auth.login.credentials_accepted with operation_outcome = unknown, not as a confirmed successful login. Cacti's password-change inserts and the develop branch's failed-2FA inserts both write result = 3 with user_id = 0, so user_log alone cannot disambiguate them; the plugin records these as cacti.auth.password_change_or_2fa_failed with operation_outcome = unknown. Unsupported result codes are recorded as cacti.auth.login.unknown with operation_outcome = unknown.

Ingestion runs every poller cycle with a bounded workload (default 1000 rows per cycle, configurable via audit_user_log_batch_size). Deduplication is durable and database-backed: each processed user_log primary-key tuple is recorded once in typed audit_user_log_state columns, so repeated and concurrent pollers cannot double-record the same source row. The source timestamp is stored as a Unix epoch, so changing the MySQL session timezone does not change event identity. A zero-valued marker claims a source row before event creation; after the event is durable, the marker is finalized with its audit ID. A deterministic event UUID reconnects an event inserted before an interrupted finalization, preventing duplicates after a process restart. Failed rows stop retrying after five attempts, while half of each batch remains available for new rows. State markers survive audit-log purges and expired markers are reclaimed every poller cycle at the configured ingestion rate. Marker age is measured from claim time; completed markers inside the five-minute replay floor remain in place, preventing duplicate external delivery on quiet installations. Terminal retry markers are reported to the Cacti log and become replayable after the fixed seven-day horizon.

The state key mirrors Cacti's own user_log primary key (username, user_id, time). Source rows that Cacti considers identical, including same-second duplicates under the table's collation, cannot coexist in user_log and therefore cannot be collapsed by plugin-side deduplication.

Before enabling authentication auditing, an Audit Log Admin must explicitly run php plugins/audit/audit_auth_indexes.php. The command verifies Cacti's user_log identity contract and installs dedicated local indexes for time-ordered ingestion and failed-login aggregation. Checkbox and poller paths never run DDL against the core table. Uninstall removes only allowlisted, plugin-owned indexes; remote collector schemas are not modified.

Every ingestion query applies the later of the audit-retention cutoff and a durable high-water mark minus a five-minute retry grace period. This bounds steady-state work, keeps recent failed rows retryable, and prevents a retention increase from replaying previously audited history. Pending retries are queried separately and never lower that floor. Source epochs are rendered as UTC before they are written to audit_log.event_time.

Failed-login volume detection runs every poller cycle and emits a cacti.auth.failed_login_volume_anomaly critical event when installation-wide failed logins exceed a configurable threshold within a rolling window. The event is explicitly global and reports distinct username and source-IP counts; it does not attribute unrelated failures to one attacker. Alert emission is atomically throttled to one event per window via a conditional UPDATE on the settings table, so concurrent pollers cannot emit duplicate alerts. The throttle marker is only persisted after a confirmed audit insert. The audit master switch, retention, external-file, authentication-auditing, and remote Syslog settings are restricted to Audit Log Admin users. Database-level changes and API activity remain outside the current Cacti 1.2.x scope.

Permissions

The plugin groups its Cacti realms under the Audit Plugin permissions section. Audit Log User permits access to the audit log. Audit Log Admin permits plugin administration and purging audit events.

Possible Bugs

If you figure out this problem, see the Cacti forums!

Future Changes

Got any ideas or complaints, please log an issue on GitHub.


Copyright (c) 2004-2026 - The Cacti Group, Inc.

About

Audit Plugin for Cacti

Topics

Resources

Stars

7 stars

Watchers

8 watching

Forks

Releases

Packages

Used by

Contributors

Languages