Files
scylladb/docs/dev/audit.md
2026-02-17 17:56:27 +01:00

3.4 KiB

Introduction

Similar to the approach described in CASSANDRA-12151, we add the concept of an audit specification. An audit has a target (syslog or a table) and a set of events/actions that it wants recorded. We introduce new CQL syntax for Scylla users to describe and manipulate audit specifications.

Prior art:

CQL extensions

Create an audit

CREATE AUDIT [IF NOT EXISTS] audit-name WITH TARGET { SYSLOG | table-name }
[ AND TRIGGER KEYSPACE IN (ks1, ks2, ks3) ]
[ AND TRIGGER TABLE IN (tbl1, tbl2, tbl3) ]
[ AND TRIGGER ROLE IN (usr1, usr2, usr3) ]
[ AND TRIGGER CATEGORY IN (cat1, cat2, cat3) ]
;

From this point on, every database event that matches all present triggers will be recorded in the target. When the target is a table, it behaves like the current design.

The audit name must be different from all other audits, unless IF NOT EXISTS precedes it, in which case the existing audit must be identical to the new definition. Case sensitivity and length limit are the same as for table names.

A trigger kind (ie, KEYSPACE, TABLE, ROLE, or CATEGORY) can be specified at most once.

Show an audit

DESCRIBE AUDIT [audit-name ...];

Prints definitions of all audits named herein. If no names are provided, prints all audits.

Delete an audit

DROP AUDIT audit-name;

Stops logging events specified by this audit. Doesn't impact the already logged events. If the target is a table, it remains as it is.

Alter an audit

ALTER AUDIT audit-name WITH {same syntax as CREATE}

Any trigger provided will be updated (or newly created, if previously absent). To drop a trigger, use IN *.

Permissions

Only superusers can modify audits or turn them on and off.

Only superusers can read tables that are audit targets; no user can modify them. Only superusers can drop tables that are audit targets, after the audit itself is dropped. If a superuser doesn't drop a target table, it remains in existence indefinitely.

Implementation

Efficient trigger evaluation

namespace audit {

/// Stores triggers from an AUDIT statement.
class triggers {
    // Use trie structures for speedy string lookup.
    optional<trie> _ks_trigger, _tbl_trigger, _usr_trigger;

    // A logical-AND filter.
    optional<unsigned> _cat_trigger;

public:
    /// True iff every non-null trigger matches the corresponding ainf element.
    bool should_audit(const audit_info& ainf);
};

} // namespace audit

To prevent modification of target tables, audit::inspect() will check the statement and throw if it is disallowed, similar to what check_access() currently does.

Persisting audit definitions

Obviously, an audit definition must survive a server restart and stay consistent among all nodes in a cluster. We'll accomplish both by storing audits in a system table.