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.
This plugin allows Cacti Administrators to log user change activity
-
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.
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.
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.
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.
If you figure out this problem, see the Cacti forums!
Got any ideas or complaints, please log an issue on GitHub.
Copyright (c) 2004-2026 - The Cacti Group, Inc.