qhb_audit

It is an extension for auditing QHB actions.

The extension consists of three main components

  • The native dynamic library of the extension libqhb_audit.so.
  • Event queue in a special area of ​​shared memory.
  • Dedicated background process qhb_audit_worker to process the queue.

The extension's work cycle:

  1. The server process performs the operation and generates an event.
  2. The extension places the event in a queue in shared memory.
  3. The background process extracts the event from the queue and either writes it to a special file — the audit log, or passes it to an external HTTP agent.

Configuration

The extension is connected by setting parameters in the configuration file qhb.conf. To automatically load the extension when the instance starts, you need to add the following string to qhb.conf:

shared_preload_libraries = 'libqhb_audit'

If shared_preload_libraries is already set, then you need to add the libqhb_audit value to it, separated by commas, for example:

shared_preload_libraries = 'pg_store_plans,pg_stat_statements,libqhb_audit'

The extension (or rather its background process) receives events from the database server through a queue located in the QHB shared memory area. This imposes restrictions on the maximum size of the queue and the memory allocated for it. If the queue size is exceeded, a warning about this will appear in the main server log.

The general parameters are read only when the extension is initialized (when the DBMS starts):

  • qhb_audit.queue_memory: The size of shared memory allocated for the internal message queue. The default is 10 MB.
  • qhb_audit.queue_capacity: maximum size of the internal message queue. The default is 1000 events.

If you can not restart the server, updating the parameters is allowed using the qhb_ctl reload command.


Parameters

Parameters for Recording Events to a File

qhb_audit.csv_file

Path to the file where events will be written. This parameter can be changed "on the fly".

If the parameter value is not specified, events are not written to the file. If the specified name does not exist in the file system, a new file will be created.

If the specified name indicates a file in which writing is not permitted or to a file that cannot be created, an error message will be written to the server log, and no events will be writing to the file.

If a relative path to the file is specified, the file is assumed to be in the PGDATA directory. Intermediate directories, if specified, are not created automatically; if they do not exist in the file system, such a path will be invalid.

If the specified name points to a directory, the message is displayed

qhb_audit: CSV output path is a directory '<name>', ignoring

and events will not be written to the file.

When log rotation is enabled (see below), the value of the csv_file parameter has the meaning of a "base" name: new files for rotation are generated based on it (see examples below).

qhb_audit.csv_file_rotation

Event file rotation mode. This parameter can be changed "on the fly".

Rotation prevents the event file from growing uncontrollably by splitting it into multiple files and overwriting them over time "in a circle". Rotation file names are generated based on the value of the qhb_audit.csv_file parameter. Rotation files are created in the same directory that specified in csv_file. This directory is not created automatically, but must exist at start time. If a relative path is specified, rotation files (as well as a single file without rotation) are created at the specified path relative to the PGDATA directory.

If this parameter is not specified or has an empty value, rotation is not performed.

If an incorrect value is specified, the entire extension is blocked and a message is displayed

incorrect value of parameter csv_file_rotation

Allowed parameter values:

  • off — rotation is disabled, everything is written to a single file;
  • day — daily rotation in 24 files;
  • week — weekly rotation in 7 files;
  • month — monthly rotation in 28-31 files;
  • year — yearly rotation in 12 files.

Approximate file names generated for each mode with the "base" name audit.csv. A distinguishing suffix is ​​appended to the file name (without extension), the extension is preserved.

Rotation modeParameterFile names
dailydayaudit_00h_of_day.csv
...
audit_23h_of_day.csv
weeklyweekaudit_Mon_of_week.csv
audit_Tue_of_week.csv
audit_Wed_of_week.csv
audit_Thu_of_week.csv
audit_Fri_of_week.csv
audit_Sat_of_week.csv
audit_Sun_of_week.csv
monthlymonthaudit_01d_of_month.csv
...
audit_31d_of_month.csv
yearlyyearaudit_January_of_year.csv
audit_February_of_year.csv
audit_March_of_year.csv
audit_April_of_year.csv
audit_May_of_year.csv
audit_June_of_year.csv
audit_July_of_year.csv
audit_August_of_year.csv
audit_September_of_year.csv
audit_October_of_year.csv
audit_November_of_year.csv
audit_December_of_year.csv

Files from the previous rotation mode remain and can be deleted manually. The same applies to external tables for accessing audit data via SQL queries (see below).


Preliminary Steps for Setting Up Access to Audit Log Data Via SQL Queries

To access audit data via SQL queries, the file_fdw extension is used. To do this, the file_fdw extension is installed, the main table qhb_audit_log is created, and a log rotation scheme is selected. Depending on the selected scheme, the given script options, where the path to the data directory PGDATA and the directory of audit log files is qualified, are taken as a basis.

The scripts below assume that the audit files will be located in the audit directory of the PGDATA data directory, and the affix of the file name will also be audit. To do this, you can specify in qhb_audit.csv_file either the absolute path <PGDATA>/audit/audit.csv (here <PGDATA> should be replaced with the actual path to the data directory), or specify a path relative to the current data directory, for example audit/audit.csv. If necessary, edit the scripts below to match the actual location of the files.

Setting the PGDATA Variable and Creating an audit Directory Inside PGDATA

Here and in all scripts below, you need to replace <PGDATA> with the real path to the PGDATA directory.

Create a directory for audit log files.

export PGDATA=<PGDATA>
sudo -u qhb mkdir $PGDATA/audit

We make audit data available at the system level only to user qhb:

sudo -u qhb chmod 700 $PGDATA/audit

Creating file_fdw Extension and Registering Foreign Server

Create a file_fdw extension and a foreign server (alternatively, you can do this in the qhb database).

CREATE EXTENSION file_fdw;
CREATE SERVER qhb_audit_log FOREIGN DATA WRAPPER file_fdw;

Creating the Main Table

This table describes all log fields, and all external log tables that match the selected rotation scheme will inherit from it when they are created.

CREATE TABLE qhb_audit_log
(
  event_id 	bigint,
  event_ts      timestamp,
  event_name    text,
  event_type  	text,        
  user_name     text,
  db_name       text,
  table_name    text,
  query_text  	text,
  event_level 	text
);

If Rotation is Not Needed

If there is no need for rotation, you should just create one external table for the file specified in qhb_audit.csv_file. In the following command, you need to replace <PGDATA> with the actual path to the data directory:

export PGDATA=<PGDATA>

In the following command, you need to replace <file.csv> with the actual file name from qhb_audit.csv_file:

sudo -u qhb touch $PGDATA/audit/<file.csv>

In the command for creating an external table, when specifying filename, you need to replace <PGDATA> with the actual path to the data directory, and <file.csv> with the actual file name from qhb_audit.csv_file:

CREATE FOREIGN TABLE qhb_audit_log_csv() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/<file.csv>',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );

Daily Log Rotation in 24 Files (1 Hour - 1 File)

OS Commands for Creating Hourly Log Files

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

export PGDATA=<PGDATA>
sudo -u qhb touch $PGDATA/audit/audit_00h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_01h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_02h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_03h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_04h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_05h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_06h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_07h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_08h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_09h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_10h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_11h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_12h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_13h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_14h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_15h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_16h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_17h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_18h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_19h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_20h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_21h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_22h_of_day.csv
sudo -u qhb touch $PGDATA/audit/audit_23h_of_day.csv

Scripts for Creating Hourly Log Tables

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

CREATE FOREIGN TABLE qhb_audit_00h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_00h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_01h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_01h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_02h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_02h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_03h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_03h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_04h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_04h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_05h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_05h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_06h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_06h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_07h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_07h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_08h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_08h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_09h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_09h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_10h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_10h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_11h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_11h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_12h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_12h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_13h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_13h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_14h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_14h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_15h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_15h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_16h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_16h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_17h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_17h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_18h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_18h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_19h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_19h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_20h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_20h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_21h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_21h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_22h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_22h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_23h_of_day() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_23h_of_day.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );

Weekly Log Rotation in 7 Files

OS Commands to Create Log Files for Each Day of the Week

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

export PGDATA=<PGDATA>
sudo -u qhb touch $PGDATA/audit/audit_Mon_of_week.csv
sudo -u qhb touch $PGDATA/audit/audit_Tue_of_week.csv
sudo -u qhb touch $PGDATA/audit/audit_Wed_of_week.csv
sudo -u qhb touch $PGDATA/audit/audit_Thu_of_week.csv
sudo -u qhb touch $PGDATA/audit/audit_Fri_of_week.csv
sudo -u qhb touch $PGDATA/audit/audit_Sat_of_week.csv
sudo -u qhb touch $PGDATA/audit/audit_Sun_of_week.csv

Log Scripts for Each Day of the Week

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

CREATE FOREIGN TABLE qhb_audit_Mon_of_week() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_Mon_of_week.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_Tue_of_week() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_Tue_of_week.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_Wed_of_week() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_Wed_of_week.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_Thu_of_week() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_Thu_of_week.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_Fri_of_week() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_Fri_of_week.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_Sat_of_week() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_Sat_of_week.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_Sun_of_week() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_Sun_of_week.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );

Monthly Log Rotation in 28-31 Files

OS Commands to Create Log Files for Each Day of the Month

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

export PGDATA=<PGDATA>
sudo -u qhb touch $PGDATA/audit/audit_01d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_02d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_03d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_04d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_05d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_06d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_07d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_08d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_09d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_10d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_11d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_12d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_13d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_14d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_15d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_16d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_17d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_18d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_19d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_20d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_21d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_22d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_23d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_24d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_25d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_26d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_27d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_28d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_29d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_30d_of_month.csv
sudo -u qhb touch $PGDATA/audit/audit_31d_of_month.csv

Scripts to Create Log Tables for Each Day of the Month

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

CREATE FOREIGN TABLE qhb_audit_01d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_01d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_02d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_02d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_03d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_03d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_04d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_04d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_05d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_05d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_06d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_06d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_07d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_07d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_08d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_08d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_09d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_09d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_10d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_10d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_11d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_11d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_12d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_12d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_13d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_13d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_14d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_14d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_15d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_15d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_16d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_16d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_17d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_17d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_18d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_18d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_19d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_19d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_20d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_20d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_21d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_21d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_22d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_22d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_23d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_23d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_24d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_24d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_25d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_25d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_26d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_26d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_27d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_27d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_28d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_28d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_29d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_29d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_30d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_30d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_31d_of_month() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_31d_of_month.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );

Yearly Log Rotation in 12 Files

OS Commands for Creating Monthly Log Files

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

export PGDATA=<PGDATA>
sudo -u qhb touch $PGDATA/audit/audit_January_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_February_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_March_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_April_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_May_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_June_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_July_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_August_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_September_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_October_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_November_of_year.csv
sudo -u qhb touch $PGDATA/audit/audit_December_of_year.csv

Scripts for Creating Monthly Log Tables

Before executing the scripts, remember to replace <PGDATA> with the actual path to the data directory.

CREATE FOREIGN TABLE qhb_audit_January_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_January_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_February_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_February_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_March_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_March_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_April_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_April_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_May_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_May_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_June_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_June_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_July_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_July_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_August_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_August_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_September_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_September_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_October_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_October_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_November_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_November_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );
CREATE FOREIGN TABLE qhb_audit_December_of_year() INHERITS (qhb_audit_log) SERVER qhb_audit_log OPTIONS (filename '<PGDATA>/audit/audit_December_of_year.csv',  FORMAT 'csv', HEADER 'true', DELIMITER ',' );

Separating the Availability of Audit Data From Different Databases

A user with the information security (IS) role qhb_dbms_admin should see all data in the qhb_audit_log table. A user with the qhb_db_admin role should see audit data for those databases for which he is an administrator. For detailed information on IS administrator roles, see Chapter Information Security Roles. The separation of privileges is implemented using the RLS (Row-Level Security) mechanism. To do this, you need to set data visibility rules based on data of roles and users, and also activate the RLS mechanism for the table qhb_audit_log. Full visibility of data for a user with the role qhb_dbms_admin is implemented either by setting the attribute bypassrls for the role (this option is used when there are several such users) or by assigning ownership of the table to the corresponding user (convenient in the case when there is only one such user). For the table owner, the default visibility rules do not apply, and all data in the table is available to him.

Creating Test Users and Assigning Them Appropriate Roles

To create users (here as an example - db_admin and dbms_admin) and assign them the corresponding roles, you need to execute the following commands:

\c qhb qhb
CREATE USER db_admin;  
SELECT oid AS user_oid FROM pg_authid WHERE rolname = 'dbms_admin';
SELECT oid AS database_oid FROM pg_database WHERE datname = '<database_name>';
SELECT qhb_make_db_admin(<user_oid>, <database_oid>) FROM pg_database d WHERE d.datname = '<db_name>';

CREATE USER dbms_admin WITH bypassrls; -- user with the privilege to ignore RLS rules
SELECT user_oid FROM pg_authid WHERE rolname = 'dbms_admin';
SELECT qhb_make_dbms_admin(<user_oid>);

You can also use the OID corresponding to the role name in this way:

SELECT qhb_make_dbms_admin('dbms_admin'::regrole::oid);

An alternative to setting the bypassrls attribute is to assign ownership of the table qhb_audit_log to the role dbms_admin, since the default data access policies do not work for the table owner:

\c qhb qhb
ALTER TABLE qhb_audit_log OWNER TO dbms_admin;

Create a Row Access Policy and Enable RLS for the Audit Table

To make data available only for specific databases, we will create a policy to restrict row visibility, which is based on dictionary data. We need to activate the RLS mechanism for the qhb_audit_log table and grant privileges to read it to the users db_admin and dbms_admin:

CREATE POLICY policy_audit_log ON qhb_audit_log USING (
  db_name IN (SELECT db.datname
	        FROM pg_database db
		   , qhb_db_admin a
	       WHERE db.oid = a.db_id
		 AND a.user_id = user::regrole::oid)
);

-- Enable RLS for the audit table
ALTER TABLE qhb_audit_log enable row level security;

-- Read privileges for the main table (child foreign tables are not affected)
GRANT SELECT ON qhb_audit_log TO db_admin;
GRANT SELECT ON qhb_audit_log TO dbms_admin;

To check the data visibility, you can connect and run a query one by one. For the user db_admin, only records from the corresponding database list are visible:

\c qhb db_admin
SELECT * FROM qhb_audit_log;

All data will be available to the user dbms_admin:

\c qhb dbms_admin
SELECT * FROM qhb_audit_log;

If there are no rows corresponding to the user's privileges, or the bypassrls attribute is not set, the following message is displayed:

ERROR:  permission denied for table qhb_db_admin

Disabling RLS for a Table, Deleting RLS Policy, Revoking Table Permissions

\c qhb qhb
ALTER TABLE qhb_audit_log disable row level security;
DROP POLICY policy_audit_log ON qhb_audit_log;
REVOKE SELECT ON qhb_audit_log FROM db_admin;
REVOKE SELECT ON qhb_audit_log FROM dbms_admin;

Parameters for Sending Events via REST API

qhb_audit.rest_url
URL of the web server that receives messages via the REST API. This parameter can be changed "on the fly". If it is not set, no events are sent. If qhb_audit.rest_url and qhb_audit.csv_file are set, then only events are written to a CSV file.

qhb_audit.rest_max_attempts
The maximum number of attempts to send an event to the server when it is unavailable.

qhb_audit.rest_attempts_interval_ms
The interval in milliseconds between attempts to send a message to the server when it is unavailable.


Ability to Enable and Disable Security Events

To audit QHB security events, it is possible to configure enabled audit events using the qhb_audit.toml file in the root PGDATA directory:

use_importance = false

[events]
role = true
...

[importance]
high = true
...

This configuration file is created when the data directory is initialized and is used by qhb_audit at startup.

When setting the parameter use_importance = true, events are filtered by their importance, and the settings of the [importance] group are used: "low", "medium", "high", "critical", and "fatal".

When setting the parameter use_importance = false, the settings of the [events] group are used, i.e. filtering is performed by the audit event category. The setting names are listed below in the Event Types table (the "Setting" column).

There are two ways to change the settings of qhb_audit.toml:

  • direct editing of the file with a DBMS restart;
  • modification of the settings using SQL functions available when using the qhb_audit extension.

The qhb_audit extension provides the following SQL functions:

CREATE EXTENSION qhb_audit;

qhb_audit_config_show() -- show current configuration

qhb_audit_config_toggle(varchar) -- select filter mode 'events' or 'importance'

qhb_audit_config_enable(varchar) -- enabling a specific option

qhb_audit_config_disable(varchar) -- disabling a specific option

To ensure availability of the specified functions, you should execute the SQL query CREATE EXTENSION qhb_audit; once.

To select the settings mode for the toml file, you should specify an argument for the function qhb_audit_config_toggle. For example:

SELECT qhb_audit_config_toggle('events');
SELECT qhb_audit_config_toggle('importance');

To enable or disable a specific option within the selected mode, use the functions qhb_audit_config_enable and qhb_audit_config_disable with a string argument that specifies the option name. For example:


SELECT qhb_audit_config_toggle('events');
SELECT qhb_audit_config_enable('role'); -- enable filter 'role' in 'events' mode

SELECT qhb_audit_config_toggle('importance');
SELECT qhb_audit_config_disable('high'); -- Disabling the 'high' filter in 'importance' mode

Note
The server stopped event in immediate mode is non-disableable regardless of what is specified in the configuration file.


CSV File Format

The newly created CSV file will consist of a header (first row) and the events themselves (the remaining lines) with the following fields:

  • unique event identifier within the file or associated set of rotated files;
  • time when the event occurred, in RFC3339 format, for example, 2018-02-14T00:28:07Z;
  • event name;
  • event type name;
  • name of the user under whose control the event occurred;
  • name of the database when creating the connection;
  • table name (specified only for DDL operations on the table);
  • SQL command, the execution of which led to the event;
  • severity of the event: low, medium, high, critical, fatal.

Examples of Operations and Corresponding Records in a CSV File

Successful and failed authentication of user plop:

0,2023-12-22T08:23:40.516682141Z,connection created:[local]:,authentication control,plop,template1,,,medium

1,2023-12-22T08:23:40.516682141Z,connection refused:[local]:,authentication control,plop,template1,,,medium
CREATE USER zodd WITH PASSWORD '123456';
ALTER ROLE zodd RENAME TO gozz;
DROP ROLE gozz;

Record:

100,2023-12-22T08:25:14.444569958Z,create role,user account management,plop,,,create user zodd with password <REDACTED>,medium

101,2023-12-22T08:25:14.447219625Z,alter role rename,user account management,plop,,,alter role zodd rename to gozz;,medium

102,2023-12-22T08:25:15.284198137Z,drop role,user account management,plop,,,drop role gozz;,medium
CREATE DATABASE moon;
ALTER DATABASE moon RENAME TO dream;
DROP DATABASE moon;
CREATE TABLE temp (id int, name varchar(255));
ALTER TABLE temp RENAME TO permanent;
TRUNCATE TABLE permanent;
DROP TABLE permanent;

Record:

400,2023-12-22T08:29:19.313320493Z,create database,changing metadata,plop,moon,,,high

401,2023-12-22T08:29:19.331731681Z,alter database,changing metadata,plop,moon,,,high

403,2023-12-22T08:29:19.332957337Z,create table,changing metadata,plop,,temp,,high

404,2023-12-22T08:29:19.334730324Z,alter table,changing metadata,plop,,temp,,high

406,2023-12-22T08:29:19.334947840Z,truncate table,changing metadata,plop,,permanent,,high

405,2023-12-22T08:29:19.337225628Z,drop table,changing metadata,plop,,permanent,,high

Message format via REST API

Event messages are sent in JSON format with the following schema:

NameVariable typeDescriptionMandatory
tagsstring[]Cross-cutting identifiers and labels that allow you to group eventsYes
DatetimeintegerUnix time. Event occurrence time in milliseconds since midnight (00:00:00 UTC) on January 1, 1970. (Fri Apr 03 2020 10:28:37 corresponds to 1585909717000)Yes
serviceNamestringService IDYes
serviceVersionstringService versionYes
namestringAudit event nameYes
paramsobject[]List of audit event parameters in the format: Name (string) — name of the audit event parameter, Value (string) — value of the event parameterYes
sessionIDstringSession ID (if any)No
userLoginstringUser loginYes
userNamestringUsernameNo
userNodestringThe node (IP/FQDN) from which the user performs actionsNo

Tracked Events

  • Attempts to connect clients.
  • Starting and stopping the QHB cluster.
  • Recovering data after an abnormal shutdown of the QHB cluster.
  • Role and privilege management commands: CREATE/ALTER/DROP ROLE/USER/GROUP, locking and unlocking roles, CREATE/ALTER/DROP USER MAPPING, GRANT, REVOKE.
  • DDL commands: CREATE/ALTER/DROP DATABASE, CREATE/ALTER/TRUNCATE/DROP TABLE.
  • Configuration change commands SET, ALTER SYSTEM and ALTER DATABASE <name> SET.
  • Violations of the integrity of binary files (core and contrib) and the pg_proc directory (procedures).
  • Procedure and view management commands: CREATE/ALTER/DROP PROCEDURE, CREATE/ALTER/DROP VIEW, CREATE/ALTER/DROP MATERIALIZED VIEW.
  • DML commands: SELECT/INSERT/UPDATE/DELETE/TRUNCATE.

Event Types

Event typeShort nameSettingImportance
successful and failed attempts to authenticate users of the database management systemauthentication controlauthenticationmedium
creating database management system user accountsuser account managementrolemedium
locking and unlocking database management system user accountsuser account managementrolemedium
changing attributes of database management system user accountsuser account managementrolemedium
database recoveryinformation recoveryrecoverymedium
starting and stopping the database management systeminstance start/stop controlinstancehigh
changing the configuration of the database management systemchanging instance settingsconfighigh
creation and deletion of a database, table, except for temporary tables created by the database management system for service purposeschanging metadatametadatahigh
changing access control rules in a database management systemaccess attribute managementgranthigh
creating and changing procedures (program code) stored in databases and viewsprocedure and view managementprocedure-viewcritical
facts of violation of the control objects integrityintegrity violationintegrityfatal
fact of reading datadml retrieving datadml-retrievelow
fact of data modificationdml modifying datadml-modifymedium
fact of data deletiondml deleting datadml-deletehigh
user session manipulationsuser sessionsessionmedium
working with stored functionsfunction managementfunctionmedium
working with aggregate functionsaggregate functionsaggregatelow
index manipulationindex managementindexlow
configuring RLS policies (row-level security)rls policypolicyhigh

Complete List of Audit Events Grouped by Type

TypeSettingEvents
authentication controlauthenticationconnection created
connection refused
user account managementrolecreate role
alter role rename
drop role
lockout role by admin
unlock role by admin
lockout roles in profile by admin
unlock roles in profile by admin
lockout role login attempts exceeded
auto unlock role after timeout
lockout role due to integrity violation
user account managementrolealter role
alter role set
alter default privileges
information recoveryrecoverycrash recovery initiated
archive recovery initiated
instance start/stop controlinstanceserver started
server stopped
changing instance settingsconfigset param
alter system
alter database set
changing metadatametadatacreate database
alter database
drop database
create table
alter table
drop table
truncate table
access attribute managementgrantgrant
grant role
revoke
revoke role
create user mapping
alter user mapping
drop user mapping
procedure and view managementprocedure-viewcreate procedure
alter procedure
drop procedure
create view
alter view
drop view
create materialized view
alter materialized view
drop materialized view
integrity violationintegrityintegrity check failure for catalog
integrity check failure for files
dml retrieving datadml-retrieveselect
dml modifying datadml-modifyinsert
update
dml deleting datadml-deletedelete
truncate
user sessionsessionuser set session
user reset session
user discard all
function managementfunctioncreate function
alter function
drop function
aggregate functionsaggregatecreate aggregate
alter aggregate
drop aggregate
index managementindexcreate unique index
create index
reindex index
reindex table
alter index
drop index
rls policypolicycreate policy
alter policy
drop policy