qhb_audit

Расширение для осуществления аудита действий QHB.

Расширение состоит из трёх основных компонентов:

  • Собственно динамическая библиотека расширения libqhb_audit.so.
  • Очередь событий в специальной области общей памяти.
  • Выделенный фоновый процесс qhb_audit_worker для обработки очереди.

Цикл работы расширения:

  1. Процесс сервера выполняет операцию и генерирует событие.
  2. Расширение помещает событие в очередь в общей памяти.
  3. Фоновый процесс извлекает событие из очереди и либо записывает в специальный файл -- лог аудита, либо передаёт внешнему HTTP агенту.

Настройка

Подключение расширения осуществляется путем установки параметров в файле конфигурации qhb.conf. Для автоматической загрузки расширения при старте экземпляра нужно добавить в qhb.conf следующую строку:

shared_preload_libraries = 'libqhb_audit'

Если параметр shared_preload_libraries уже задан, то в него нужно добавить значение libqhb_audit через запятую, например:

shared_preload_libraries = 'pg_store_plans,pg_stat_statements,libqhb_audit'

Расширение (точнее, его фоновый процесс) получает события от сервера БД через очередь, размещённую в области общей памяти QHB. Это накладывает ограничения на максимальный размер очереди и отведённой под неё памяти. В случае превышения размера очереди в основном логе сервера появится предупреждение об этом.

Общие параметры считываются только при инициализации расширения (при старте СУБД):

  • qhb_audit.queue_memory: объем разделяемой памяти, выделяемой для внутренней очереди сообщений. По умолчанию — 10 МБ.
  • qhb_audit.queue_capacity: максимальный размер внутренней очереди сообщений. По умолчанию — 1000 событий.

Если перезагрузка сервера невозможна, допускается обновление параметров с помощью команды qhb_ctl reload.


Параметры

Параметры для записи событий в файл

qhb_audit.csv_file

Путь к файлу, куда будут записываться события. Данный параметр можно менять «на лету».

Если значение параметра не задано, запись событий в файл не осуществляется. Если заданное имя не существует в файловой системе, то будет создан новый файл.

Если заданное имя указывает на файл, запрещённый для записи, или на файл, который невозможно создать, в лог сервера будет выдано сообщение об ошибке, а запись событий в файл не будет осуществляться.

Если указан относительный путь к файлу, предполагается, что файл находится в каталоге PGDATA. Промежуточные каталоги, если указаны, не создаются автоматически; при их отсутствии в файловой системе такой путь будет ошибочным.

Если заданное имя указывает на каталог, выдаётся сообщение

qhb_audit: CSV output path is a directory '<имя>', ignoring

и запись событий в файл не будет осуществляться.

При включённой ротации лога (см. ниже) значение параметра csv_file имеет смысл "базового" имени: новые файлы для ротации генерируются на его основе (примеры см. ниже).

qhb_audit.csv_file_rotation

Режим ротации файла событий. Данный параметр можно менять «на лету».

Ротация предотвращает неконтролируемое разрастание файла событий путём разбиения его на множество файлов с перезаписью со временем "по кругу". Имена файлов ротации генерируются на основе значения параметра qhb_audit.csv_file. Файлы ротации создаются в том же каталоге, на который указывает csv_file. Этот каталог не создаётся автоматически, а должен существовать на момент запуска. Если указан относительный путь, файлы ротации (как и одиночный файл без ротации) создаются по указанному пути относительно каталога PGDATA.

Если этот параметр не задан или имеет пустое значение, ротация не осуществляется.

Если задано некорректное значение, работа всего расширения блокируется с выдачей сообщения

incorrect value of parameter csv_file_rotation

Разрешённые значения параметра:

  • off - ротация отключена, всё пишется в единственный файл;
  • day - дневная ротация в 24 файла;
  • week - недельная ротация в 7 файлов;
  • month - месячная ротация в 28-31 файл;
  • year - годичная ротация в 12 файлов.

Примерные имена файлов, генерируемые для каждого режима при "базовом" имени audit.csv. К имени файла (без расширения) присоединяется различающий суффикс, расширение сохраняется.

Режим ротацииПараметрИмена файлов
дневнойdayaudit_00h_of_day.csv
...
audit_23h_of_day.csv
недельныйweekaudit_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
месячныйmonthaudit_01d_of_month.csv
...
audit_31d_of_month.csv
годовойyearaudit_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

Файлы от предыдущего режима ротации остаются и могут быть удалены вручную. То же самое относится и к внешним таблицам для доступа к данным аудита через SQL-запросы (см. ниже).

Предварительные шаги для настройки доступа к данным логов аудита через SQL-запросы

Для доступа к данным аудита через SQL-запросы используется расширение file_fdw. Для этого устанавливается расширение file_fdw, создается основная таблица qhb_audit_log, выбирается схема ротации логов. В зависимости от выбранной схемы берутся за основу приведенные варианты скриптов, где уточняется путь к каталогу данных PGDATA и каталогу файлов логов аудита.

В приведенных ниже скриптах подразумевается, что файлы аудита будут расположены в каталоге audit каталога данных PGDATA и начало имени файла будет также audit. Для этого в настройке qhb_audit.csv_file можно прописать либо абсолютный путь /audit/audit.csv (здесь ***

*** нужно заменить на реальный путь к каталогу данных), либо задать путь относительно текущего каталога данных, например audit/audit.csv. При необходимости, отредактируйте приведенные ниже скрипты в соответствии с реальным расположением файлов.

Задание переменной PGDATA и создание каталога audit внутри PGDATA

Здесь и во всех скриптах далее необходимо заменить ***

*** на реальный путь к каталогу PGDATA.

Создаем каталог для файлов логов аудита.

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

Делаем данные аудита доступными на уровне системы только пользователю qhb:

sudo -u qhb chmod 700 $PGDATA/audit

Создание расширения file_fdw и регистрация стороннего сервера

Создайте расширение file_fdw и сторонний сервер (как вариант, можно сделать это в базе данных qhb).

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

Создание основной таблицы

Данная таблица описывает все поля логов и от нее будут наследоваться при их создании все внешние таблицы логов, соответствующие выбранной схеме ротации.

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
);

Если ротация не нужна

Если необходимости в ротации нет, следует ограничиться созданием одной внешней таблицы для файла, указанного в параметре qhb_audit.csv_file.
В следующей команде нужно заменить ***

*** на реальный путь до каталога данных:

export PGDATA=<PGDATA>

В следующей команде нужно заменить <file.csv> на реальное имя файла из параметра qhb_audit.csv_file:

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

В команде создания внешней таблицы при указании filename нужно заменить ***

*** на реальный путь до каталога данных, а <file.csv> на реальное имя файла из параметра 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 ',' );

Дневная ротация в 24 файла (1 час - 1 файл)

Команды ОС для создания файлов часовых логов

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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

Скрипты для создания таблиц часовых логов

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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 ',' );

Недельная ротация логов в 7 файлов

Команды ОС для создания файлов логов за каждый день недели

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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

Скрипты для логов для каждого дня недели

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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 ',' );

Месячная ротация в 28-31 файл

Команды ОС для создания файлов логов за каждый день месяца

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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

Скрипты для создания таблиц логов за каждый день месяца

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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 ',' );

Логи c годичной ротацией в 12 файлов

Команды ОС для создания файлов помесячных логов

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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

Скрипты для создания таблиц помесячных логов

Перед выполнением скриптов не забудьте заменить ***

*** на реальный путь к каталогу данных.

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 ',' );

Разделение доступности данных аудита разных баз данных

Пользователь с ролью информационной безопасности (ИБ) qhb_dbms_admin должен видеть все данные таблицы qhb_audit_log. Пользователь с ролью qhb_db_admin должен видеть данные аудита по тем базам данных, администратором которых он является. Подробную информацию о ролях администраторов ИБ см. в главе Описание Ролей ИБ. Разделение прав реализуется с помощью механизма RLS (Row Level Security). Для этого необходимо задать правила видимости данных, опираясь на данные по ролям и пользователям, а также активизировать механизм RLS для таблицы qhb_audit_log. Полная видимость данных для пользователя с ролью qhb_dbms_admin реализуется либо через установку для роли атрибута bypassrls (этот вариант используется, если таких пользователей несколько) либо через установку владельцем таблицы соответствующего пользователя (удобно в случае, когда такой пользователь всего один). Для владельца таблицы правила видимости по умолчанию не работают и ему доступны все данные таблицы.

Создание тестовых пользователей и выдача им соответствующих ролей

Для создания пользователей (здесь в качестве примера - db_admin и dbms_admin) и выдачи им соответствующих ролей нужно выполнить следующие команды:

\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; -- пользователь с правом игнорирования правил RLS
SELECT user_oid FROM pg_authid WHERE rolname = 'dbms_admin';
SELECT qhb_make_dbms_admin(<user_oid>);

Использовать oid, соответствующий имени роли, можно и таким образом:

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

Альтернативой установки атрибута bypassrls может стать задание владения таблицей qhb_audit_log ролью dbms_admin, т.к. политики доступа к данным по умолчанию не срабатывают по отношению к владельцу таблицы:

\c qhb qhb
alter table qhb_audit_log owner to dbms_admin;

Создание политики доступа к строкам и активация RLS для таблицы аудита

Для доступности данных только по конкретным базам создадим политику ограничения видимости строк, которая опирается на данные словаря. Нужно активировать механизм RLS для таблицы qhb_audit_log и выдать права на чтение таблицы qhb_audit_log пользователям db_admin и 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)
);

-- Включаем RLS для таблицы аудита
alter table qhb_audit_log enable row level security;

-- Права на чтение на основную таблицу (дочерние внешние таблицы не затрагиваем)
grant select on qhb_audit_log to db_admin;
grant select on qhb_audit_log to dbms_admin;

Для проверки видимости данных можно поочередно подключиться и выполнить запрос. Для пользователя db_admin видны только записи из соответствующего списка баз:

\c qhb db_admin
select * from qhb_audit_log;

Для пользователя dbms_admin будут доступны все данные:

\c qhb dbms_admin
select * from qhb_audit_log;

Если нет строк, соответствующих правам пользователя, или не установлен атрибут bypassrls, выдается сообщение:

ERROR:  permission denied for table qhb_db_admin

Отключение RLS для таблицы, удаление политики RLS, отзыв прав на таблицу

\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;

Параметры для отправки событий по REST API

qhb_audit.rest_url URL веб-сервера, принимающий сообщения через REST API. Данный параметр можно менять «на лету». Если он не задан, отправка событий не осуществляется. Если заданы qhb_audit.rest_url и qhb_audit.csv_file, то осуществляется только запись событий в CSV файл.

qhb_audit.rest_max_attempts Максимальное количество попыток отправить событие на сервер при его недоступности.

qhb_audit.rest_attempts_interval_ms Интервал в миллисекундах между попытками отправить сообщение на сервер при его недоступности.


Формат CSV файла

Вновь созданный CSV файл будет состоять из заголовка (первая строка) и непосредственно событий (остальные строки) со следующими полями:

  • уникальный в пределах файла или связанного набора ротируемых файлов идентификатор события;
  • время, когда событие произошло, в формате RFC3339, например 2018-02-14T00:28:07Z;
  • имя события;
  • имя типа события;
  • имя пользователя, под управлением которого произошло событие;
  • имя базы данных при создании соединения;
  • имя таблицы (задается только при DDL-операциях на таблице);
  • команда SQL, выполнение которой привело к событию;
  • степень важности события: low, medium, high, critical, fatal.

Примеры операций и соответствующих записей в CSV-файле

Успешная и неуспешная аутентификация пользователя 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;
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;
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

Формат сообщений через REST API

Сообщения о событиях отправляются в формате JSON со следующей схемой:

НаименованиеТип переменнойОписаниеОбязательный
tagsstring[]Сквозные идентификаторы и метки, позволяющие группировать событияДа
DatetimeintegerВремя Unix. Время возникновения события в миллисекундах, прошедших с полуночи (00:00:00 UTC) 1 января 1970 года. (Fri Apr 03 2020 10:28:37 соответствует 1585909717000)Да
serviceNamestringИдентификатор службыДа
serviceVersionstringВерсия службыДа
namestringНаименование события аудитаДа
paramsobject[]Список параметров события аудита в формате: Name (string) — наименование параметра события аудита, Value (string) — значение параметра событияДа
sessionIDstringИдентификатор сеанса (если есть)Нет
userLoginstringЛогин пользователяДа
userNamestringИмя пользователяНет
userNodestringУзел (IP/FQDN), с которого пользователь выполняет действияНет

Отслеживаемые события

  • Попытки подключения клиентов.
  • Запуск и останов кластера QHB.
  • Восстановление данных после нештатного останова кластера QHB.
  • Команды управления ролями и их привилегиями: CREATE/ALTER/DROP ROLE/USER/GROUP, CREATE/ALTER/DROP USER MAPPING, GRANT, REVOKE.
  • Команды DDL: CREATE/ALTER/DROP DATABASE, CREATE/ALTER/TRUNCATE/DROP TABLE.
  • Команды изменения конфигурации SET, ALTER SYSTEM и ALTER DATABASE SET.
  • Нарушения целостности бинарных файлов (core и contrib) и каталога pg_proc (процедур).
  • Команды управления процедурами и представлениями: CREATE/ALTER/DROP PROCEDURE, CREATE/ALTER/DROP VIEW, CREATE/ALTER/DROP MATERIALIZED VIEW.

Типы событий

Тип событияКраткое наименованиеВажность
успешные и неуспешные попытки аутентификации пользователей системы управления базами данныхauthentication controlmedium
создание учетных записей пользователей системы управления базами данныхuser account managementmedium
изменение атрибутов учетных записей пользователей системы управления базами данныхuser account managementmedium
восстановление базы данныхinformation recoverymedium
запуск и остановка системы управления базами данныхinstance start/stop controlhigh
изменение конфигурации системы управления базами данныхchanging instance settingshigh
создание и удаление базы данных, таблицы за исключением временных таблиц, создаваемых системой управления базами данных в служебных целяхchanging metadatahigh
изменение правил разграничения доступа в системе управления базами данныхaccess attribute managementhigh
создание и изменение процедур (программного кода), хранимых в базах данных, и представленийprocedure and view managementcritical
факты нарушения целостности объектов контроляintegrity violationfatal

Полный перечень событий аудита, сгруппированных по типам

ТипСобытия
authentication controlconnection created
connection refused
user account managementcreate role
alter role rename
drop role
alter role
alter role set
alter default privileges
information recoverycrash recovery initiated
archive recovery initiated
instance start/stop controlserver started
server stopped
changing instance settingsset param
alter system
alter database set
changing metadatacreate database
alter database
drop database
create table
alter table
drop table
truncate table
access attribute managementgrant
grant role
revoke
revoke role
create user mapping
alter user mapping
drop user mapping
procedure and view managementcreate procedure
alter procedure
drop procedure
create view
alter view
drop view
create materialized view
alter materialized view
drop materialized view
integrity violationintegrity check failure for catalog
integrity check failure for files