Журналирование и аудит#

Для журналирования и аудита событий безопасности используется модифицированное расширение pgAudit. Оно обеспечивает детальное журналирование сессий и объектов для целей аудита. События подключения и события настройки механизма защиты данных включаются в отдельные классы регистрируемых событий для предоставления возможности гибкой настройки аудита. Расширение pgAudit заменяет стандартное средство журналирования PostgreSQL.

Формат#

Примечание:

Выходные данные соответствуют формату CSV только в том случае, если префикс строки журнала каждой записи удален.

Записи аудита записываются в стандартное средство ведения журнала и содержат следующие столбцы в формате CSV.

  • AUDIT_TYPESESSION (аудит действий пользователя для всех запросов) или OBJECT (аудит действий над каким либо отношением (таблицей)).

  • STATEMENT_ID — уникальный идентификатор оператора для этой сессии. Каждый идентификатор оператора представляет собой внутренний вызов. Идентификаторы операторов идут последовательно, даже если некоторые операторы не регистрируются. Когда регистрируется более одного отношения, для одного идентификатора оператора может быть несколько записей.

  • SUBSTATEMENT_ID — последовательный идентификатор для каждого вложенного оператора в основном операторе. Например, вызов функции из запроса. Идентификаторы вложенных операторов идут последовательно, даже если некоторые вложенные операторы не регистрируются. Когда регистрируется более одного отношения, для одного идентификатора вложенного оператора может быть несколько записей.

  • CLASSREAD, ROLE и т.д. (см. ниже подраздел pgaudit.log).

  • COMMANDALTER TABLE, SELECT и т.д.

  • OBJECT_TYPETABLE, INDEX, VIEW и т.д. Применимо для SELECT, DML и большинства DDL.

  • OBJECT_NAME — полное имя объекта (например, public.account). Применимо для SELECT, DML и большинства DDL.

  • STATEMENT — операция, выполняемая на сервере.

  • PARAMETER — если параметр pgaudit.log_parameter включен, в кавычках будут перечислены параметры в формате CSV. Если параметров нет, будет <none>. Если pgaudit.log_parameter выключен в этом поле будет запись <not logged>.

  • EXECUTION_RESULT — если операция завершается с ошибкой, вызванной недостаточными привилегиями или механизмом защиты от привилегированных пользователей, в этом поле будет запись «AUDIT:...ERROR: permission denied...», содержащее в том числе запрос, приводящий к ошибке. Если операция выполнена успешно и оператор принадлежит к классу PROTECTION, в поле заносится запись SUCCESS.

У записей класса CONNECTION собственный формат:

  • AUDIT_TYPE — всегда SESSION.

  • CLASS — всегда CONNECTION.

  • EVENTOPEN, CLOSED, FAILED, CHANGE USER.

  • SESSION_TIME — только для события CLOSED. Формат записи: "session time: %d:%02d:%02d.%03d" — часы, минуты, секунды, миллисекунды соответственно.

  • DATA_BASE — имя базы данных, к которой осуществляется подключение. Формат записи: - "database = %s" (имя базы данных).

  • USER — имя пользователя, подключающегося к базе данных. Формат записи: — "user = %s" (имя пользователя).

Для добавления любых других полей, необходимых для удовлетворения ваших требований к журналу аудита, может использоваться префикс строки журнала (log_line_prefix). Пример префикса строки журнала: "%m %u %d \[%p\]:" - включить дату/время, имя пользователя, имя базы данных и идентификатор процесса для каждой записи журнала аудита.

Рекомендации#

В зависимости от настроек журнал pgAudit может достигать очень больших объемов. Следует максимально точно определить, что именно должно быть записано в журнал аудита в вашей среде, чтобы избежать слишком большого количества записей.

Например, при работе в среде OLAP не лучшим решением станет проверять вставки журналов в большую таблицу фактов. Размер файла журнала, скорее всего, будет во много раз больше фактического размера данных вставок, поскольку файл журнала хранится в текстовом виде. И так как журналы обычно хранятся на диске вместе с ОС, это может привести к очень быстрому исчерпанию дискового пространства.

В тех случаях, когда невозможно ограничить ведение журнала аудита определенными таблицами, следует обязательно оценить влияние на производительность во время тестирования и выделить достаточно места на томе журнала. Это также может быть верно для сред OLTP. Даже если объем операций вставки не так высок, влияние журнала аудита на производительность все равно может привести к заметному росту задержки.

Чтобы ограничить количество записей аудита отношений для операторов SELECT и DML, рассмотрите возможность использования журнала аудита объектов (см. ниже подраздел «Ведение журнала аудита объектов»). Ведение журнала аудита объектов позволяет выбрать отношения, которые будут регистрироваться, что позволяет уменьшить общий объем журнала. Однако все новые создаваемые отношения должны быть явно добавлены в журнал аудита объектов. В этом случае хорошим вариантом может быть программное решение, в котором некоторые определенные таблицы исключаются из ведения журнала, а все остальные включаются.

Внимание!

Настройки pgAudit могут быть изменены только суперпользователем.

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

Настройки могут быть заданы:

  • глобально (в файле postgresql.conf или с помощью ALTER SYSTEM ... SET);

  • на уровне базы данных (с помощью ALTER DATABASE ... SET);

  • на уровне роли (с помощью ALTER ROLE ... SET).

Для того чтобы логи аудита выводились в журнал, в файле postgresql.conf нужно настроить следующие переменные:

pgaudit.log = 'ddl, role, connection, misc_set, protection'
pgaudit.log_client = 'on'
pgaudit.log_level = 'log'

Примечание:

Обратите внимание, что настройки не наследуются через обычное наследование ролей, и SET ROLE не изменит настройки pgAudit пользователя. Это ограничение системы ролей, а не особенность pgAudit.

Расширение pgAudit должно быть загружено в shared_preload_libraries. В противном случае во время загрузки возникнет ошибка, и ведение журнала аудита не будет происходить. Кроме того, CREATE EXTENSION pgaudit следует вызвать до того, как будут заданы параметры pgaudit.log. Если расширение удалено и его необходимо воссоздать, то сначала должен быть удален pgaudit.log, иначе возникнет ошибка.

Внимание!

Для поддержки логической репликации после сбоя, Pangolin Manager регулярно выполняет на реплике вызов pg_replication_slot_advance(), время выполнения которого зависит от объема WAL-файлов. Функция вычитывает журнальные записи из WAL-файлов, чтобы продвинуть последний подтвержденный LSN слота (confirmed_flush_lsn).

При определенном количестве накопленных журнальных записей запрос мог занять существенное время и отмениться по тайм-ауту (ранее использовался фиксированный тайм-аут в 2 сек), поэтому был отключен контроль длительности выполнения этого запроса, иначе при последующих попытках после отмены запроса новый вызов начинал обработку WAL-файлов сначала (с той же позиции confirmed_flush_lsn), снова завершал выполнение по тайм-ауту, не продвигая confirmed_flush_lsn. В результате продолжал накапливаться объем WAL-файлов. Также увеличивался размер системного каталога на диске, поскольку он требуется для слота репликации, который удерживает «горизонт транзакций», не давая вакууму очистить удаленные версии строк. Удерживание «горизонта транзакций» может привести к проблеме переполнения 32-битного счетчика транзакций (XID wraparound).

Теперь, даже при интенсивной нагрузке, значение confirmed_flush_lsn слота должно увеличиваться по мере обработки журнальных записей. Для мониторинга прогресса используется представление pg_replication_slots. Пример запроса:

SELECT slot_name, plugin, slot_type, database, restart_lsn, confirmed_flush_lsn FROM pg_replication_slots WHERE slot_name = 'test_slot';

 slot_name |    plugin     | slot_type | database | restart_lsn | confirmed_flush_lsn
-----------+---------------+-----------+----------+-------------+---------------------
 test_slot | test_decoding | logical   | postgres | 0/70002A0   | 0/70002D8

Если значения restart_lsn и confirmed_flush_lsn будут существенно отставать от значений тех же параметров на мастере, то стоит убедиться, что запрос pg_replication_slot_advance() выполняется успешно и не зависает. Время выполнения запроса можно узнать через представление pg_stat_statements. Если запрос завершается успешно, но занимает длительное время, то отставание значений restart_lsn и confirmed_flush_lsn ожидаемо.

Параметры#

pgaudit.log#

Указывает, какие классы операторов будут регистрироваться при ведении журнала аудита сессии. Возможные значения:

  • READ: SELECT и COPY, если источник — отношение или запрос.

  • WRITE: INSERT, UPDATE, DELETE, TRUNCATE и COPY, если цель — отношение.

  • FUNCTION: вызовы функций и блоки DO.

  • ROLE: операторы, связанные с ролями и привилегиями: GRANT, REVOKE, CREATE/ALTER/DROP ROLE.

  • DDL: все DDL, не входящие в класс ROLE.

  • MISC: прочие команды, включая DISCARD, FETCH, CHECKPOINT, VACUUM, SET.

  • MISC_SET: прочие команды SET, включая SET ROLE.

  • CONNECTION: события, связанные с подключением к серверу. Существуют 4 типа таких событий: OPEN, CLOSED, FAILED, CHANGE USER. Событие FAILED регистрируется в случае неудачной попытки аутентификации по паролю и независимо от значения gaudit.log.

  • PROTECTION: функции настройки механизма защиты от привилегированных пользователей.

  • ALL: включить все вышеперечисленное.

Можно включить несколько классов, перечислив их через запятую, или исключить определенные классы, поставив перед ними знак минус «-» (см. ниже подраздел «Ведение журнала аудита сессии»).

Значение по умолчанию — none.

pgaudit.log_catalog#

Указывает, должно ли быть включено ведение журнала сессии в том случае, если все отношения в операторе находятся в pg_catalog. Отключение этого параметра уменьшит информационный шум в журнале от таких инструментов, как psql и pgAdmin, которые часто обращаются к каталогу.

Значение по умолчанию — on.

pgaudit.log_client#

Указывает, будут ли сообщения журнала видны клиентскому процессу — такому, как psql. Этот параметр обычно следует оставлять отключенным, но он может быть полезен для отладки или других целей.

Значение по умолчанию - off.

pgaudit.log_level#

Указывает уровень журналирования, который будет использоваться для записей журнала (см. Уровни важности сообщений). Обратите внимание, что значения ERROR, FATAL и PANIC не допускаются). Этот параметр используется для регрессионного тестирования, а также может быть полезен конечным пользователям для тестирования или других целей.

Примечание:

Параметр pgaudit.log_level включен только тогда, когда pgaudit.log_client включен; в противном случае будет использоваться значение по умолчанию.

Значение по умолчанию — log.

pgaudit.log_parameter#

Указывает, что ведение журнала аудита должно включать параметры, переданные вместе с оператором. При наличии параметров они будут включены в формате CSV после текста оператора.

Значение по умолчанию — off.

pgaudit.log_relation#

Указывает, должно ли ведение журнала аудита сессии создавать отдельную запись журнала для каждого отношения (TABLE, VIEW и т.д.), на которое ссылается оператор SELECT или DML. Это полезный прием для исчерпывающего ведения журнала без использования журнала аудита объектов.

Значение по умолчанию — off.

pgaudit.log_statement_once#

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

Значение по умолчанию — off.

pgaudit.role#

Указывает главную роль, используемую для ведения журнала аудита объектов. Можно определить несколько ролей аудита, предоставив их главной роли. Это позволяет нескольким группам отвечать за различные аспекты ведения журнала аудита.

Значения по умолчанию нет.

Ведение журнала аудита сессии#

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

Конфигурация#

Ведение журнала сессии включается с помощью параметра pgaudit.log.

Включить ведение журнала сессии для всех DML и DDL и протоколировать все отношения в операторах DML:

set pgaudit.log = 'write, ddl';

set pgaudit.log\_relation = on;

Включить ведение журнала сессии для всех команд, кроме MISC, и выводить сообщения журнала аудита с пометкой NOTICE:

set pgaudit.log = 'all, -misc';

set pgaudit.log\_level = notice;

В примере ниже ведение журнала аудита сессии используется для ведения журнала операторов DDL и SELECT. Обратите внимание, что оператор INSERT не регистрируется, так как класс WRITE не включен.

Пример:

set pgaudit.log = 'read, ddl';

create table account
(
    id int,
    name text,
    password text,
    description text
);

insert into account (id, name, password, description)
             values (1, 'user1', 'HASH1', 'blah, blah');

select *
    from account;

Вывод в журнале:

AUDIT: SESSION,1,1,DDL,CREATE TABLE,TABLE,public.account,create table account
(
    id int,
    name text,
    password text,
    description text
);,<not logged>
AUDIT: SESSION,2,1,READ,SELECT,,,select *
    from account,,<not logged>

Ведение журнала аудита объектов#

Журнал аудита объектов регистрирует операторы, влияющие на определенное отношение. Поддерживаются только команды SELECT, INSERT, UPDATE и DELETE. Операция TRUNCATE не включается в журнал аудита объектов.

Ведение журнала аудита объектов — более подробная альтернатива pgaudit.log = 'read, write'. Таким образом, возможно, нет смысла использовать их вместе, но можно, например, использовать ведение журнала сеансов для регистрации каждого оператора, а затем дополнить его ведением журнала объектов, чтобы получить более подробную информацию о конкретных отношениях.

Конфигурация#

Ведение журнала аудита на уровне объектов осуществляется через систему ролей. Параметр pgaudit.role определяет роль, которая будет использоваться для ведения журнала аудита. Отношение (TABLE, VIEW) будет записываться в журнал аудита, если роль аудита имеет разрешения для выполняемой команды или наследует разрешения от другой роли. Это позволяет эффективно использовать несколько ролей аудита, даже если в любом контексте существует одна главная роль.

Задайте для pgaudit.role значение auditor и наделите ее правами SELECT и DELETE над таблицей account. Теперь операции SELECT и DELETE над таблицей account будут заноситься в журнал:

set pgaudit.role = 'auditor';

grant select, delete
   on public.account
   to auditor;

В примере ниже ведение журнала аудита объектов используется для иллюстрации того, как может быть применен детализированный подход к журналированию операторов SELECT и DML. Обратите внимание, что ведение журнала операций над таблицей account контролируется разрешениями на уровне столбцов, а ведение журнала операций над таблицей account_role_map — на уровне таблиц.

Пример:

set pgaudit.role = 'auditor';

create table account
(
    id int,
    name text,
    password text,
    description text
);

grant select (password)
   on public.account
   to auditor;

select id, name
  from account;

select password
  from account;

grant update (name, password)
   on public.account
   to auditor;

update account
   set description = 'yada, yada';

update account
   set password = 'HASH2';

create table account_role_map
(
    account_id int,
    role_id int
);

grant select
   on public.account_role_map
   to auditor;

select account.password,
       account_role_map.role_id
  from account
       inner join account_role_map
            on account.id = account_role_map.account_id

Вывод в журнале:

AUDIT: OBJECT,1,1,READ,SELECT,TABLE,public.account,select password
  from account,<not logged>
AUDIT: OBJECT,2,1,WRITE,UPDATE,TABLE,public.account,update account
   set password = 'HASH2',<not logged>
AUDIT: OBJECT,3,1,READ,SELECT,TABLE,public.account,select account.password,
       account_role_map.role_id
  from account
       inner join account_role_map
            on account.id = account_role_map.account_id,<not logged>
AUDIT: OBJECT,3,1,READ,SELECT,TABLE,public.account_role_map,select account.password,
       account_role_map.role_id
  from account
       inner join account_role_map
            on account.id = account_role_map.account_id,<not logged>