pg_integrity_verifier#

pg_integrity_verifier

pg_integrity_verifier

pg_integrity_verifier — проверка целостности баз данных Tantor

pg_integrity_verifier

pg_integrity_verifier — это специализированный инструмент, предназначенный для мониторинга целостности баз данных PostgreSQL. Приложение предназначено для помощи администраторам СУБД или специалистам по безопасности в контроле целостности баз данных. Оно сохраняет записи состояний баз данных в указанной директории, а в случае каких-либо изменений в наблюдаемых базах данных создает снимок в формате sn_{database}_{datetime}.diff. Затем pg_integrity_verifier выполняет команду, указанную в run_command в основном файле конфигурации. В run_command указывается команда, скрипт или другое приложение, предназначенное для анализа изменений и принятия решений. Например, если зафиксировано изменение структуры какой-либо таблицы, может быть выполнена отправка уведомления ответственным специалистам. Как и куда будет отправлено уведомление должно определяться исключительно внешним приложением. Кроме уведомлений могут быть выполнены действия, модифицирующие структуру СУБД или ее конфигурации.

pg_integrity_verifier может быть запущен любым пользователем ОС Linux, имеющим полномочия на запуск исполняемого файла. Важно отметить, что единственное допустимое место подключения к базам данных для контроля целостности — это текущий хост, то есть pg_integrity_verifier должен быть запущен на одном хосте с наблюдаемой базой данных. Это необходимо, так как для контроля целостности файлов конфигурации PostgreSQL postgresql.conf и pg_hba.conf приложению необходим локальный доступ к ним. Проверка целостности может применяться к разным сущностям БД: пользователям, конфигурациям, таблицам, функциям, триггерам и так далее.

Параметры запуска

Список параметров запуска для pg_integrity_verifier:

Параметр Описание
-c, --config Указывает основной файл конфигурации.
-l, --log Указывает файл конфигурации для ведения журнала.
-v, --version Выводит версию приложения.
-h, --help Выводит список доступных опций и информацию об использовании.

Пример запуска:

./pg_integrity_verifier -l log4rs.yaml -c pg_integrity_verifier.yaml

Основной файл конфигурации

Конфигурация pg_integrity_verifier задается в параметре -c через основной файл конфигурации в формате yaml. Этот файл позволяет устанавливать различные параметры, определяющие взаимодействие приложения с наблюдаемыми базами данных.

Структура файла:

mode: "<режим работы: one_time или daemon>"
daemon_period: "<интервал в секундах для режима daemon>"
databases:
  - db_url: "postgres://<имя пользователя>:<пароль>@<адрес БД>:<порт>/<имя БД>"
    snapshot_directory: "<каталог для снимков БД>"
    observed_objs: [<список объектов БД для мониторинга>]
    run_command: "<команда или скрипт для выполнения при обнаружении изменения> -f DIFF_FILE"
  - ...

Где:

  • mode — режим работы приложения:

    • one_time — единоразовый запуск.

    • daemon — периодический запуск через заданные интервалы времени.

  • daemon_period — интервал времени в секундах между периодическими запусками.

  • databases — список наблюдаемых баз данных и их параметры:

    • db_url — строка подключения к БД в формате postgres://<имя пользователя>:<пароль>@<адрес БД>:<порт>/<имя БД>.

      Если пароль содержит специальные символы (@, %, # и так далее), то они должны быть закодированы в процентное кодирование (Percent-encoding). Например, пароль pass#word должен быть указан как pass%23word.

      Пароли можно исключить из строк подключения и передавать в переменных окружения DB<порядковый номер БД>_PASSWORD=<пароль>. Например:

      export DB1_PASSWORD=12345
      export DB2_PASSWORD=54321
      

      Либо сразу при запуске:

      DB1_PASSWORD=12345 DB2_PASSWORD=54321 ./pg_integrity_verifier \
          -l log4rs.yaml -c pg_integrity_verifier.yaml
      
    • snapshot_directory — каталог для сохранения снимков текущего состояния базы данных. Убедитесь, что каталог задан и отличается от каталогов, используемых для других наблюдаемых баз данных.

    • observed_objs — список объектов БД для мониторинга в одинарных кавычках через запятую: 'config_file', 'pg_hba', 'pg_settings', 'pg_user', 'pg_proc', 'pg_trigger', 'pg_roles'. Если пользователь, подключенный к базе данных, не имеет достаточных прав, приложение не будет проверять объекты postgresql.conf, pg_hba.conf и pg_settings. Если observed_objs не указан — будут проверяться все объекты, если observed_objs: [] — объекты базы данных проверяться не будут.

    • run_command — команда или скрипт, который приложение выполнит при обнаружении нарушения целостности БД. Вызываемые команда или скрипт должны поддерживать аргумент -f DIFF_FILE, в который передается имя конкретного файла, содержащего изменения. Заполнитель DIFF_FILE будет заменен на путь к файлу снимка, содержащему обнаруженные различия.

Пример основного файла конфигурации:

mode: "daemon"
daemon_period: "5"
databases:
  - db_url: "postgres://postgres:12345@localhost:5432/db1"
    snapshot_directory: "json1"
    observed_objs: ['config_file', 'pg_hba', 'pg_settings', 'pg_user', 'pg_proc', 'pg_trigger', 'pg_roles']
    run_command: "tests/run_command.sh -f DIFF_FILE"
  - db_url: "postgres://postgres:54321@localhost:5432/db2"
    snapshot_directory: "json2"
    observed_objs: ['config_file', 'pg_hba', 'pg_settings', 'pg_user', 'pg_proc', 'pg_trigger', 'pg_roles']
    run_command: "tests/run_command.sh -f DIFF_FILE"

Файл конфигурации для ведения журнала

Конфигурация ведения журнала событий и сообщений приложения задается в параметре -l также через файл в формате yaml.

Структура файла:

refresh_rate: "<интервал обновления конфигурации в секундах> seconds"
appenders:
  <название компонента>:
    kind: <тип компонента: console, file, rolling_file, udp или tcp>
    encoder:
      pattern: "<шаблон вывода или записи>"
    path: "<путь к файлу лога>" # Только для типов компонентов file и rolling_file
    policy: # Только для типа компонентов rolling_file
      trigger:
        kind: <тип условия: size, time или compound>
        limit: <размера файла в байтах> # Только для типа условия size
        schedule: <расписание в формате cron с расширением для секунд> # Только для типа условия time
      roller:
        kind: <тип механизма ротации: fixed_window, delete или compound>
        base: <начальный индекс> # Только для типа механизма ротации fixed_window
        count: <количество файлов> # Только для типа механизма ротации fixed_window
        pattern: "<шаблон имени для ротации>"
    endpoint: "<адрес>:<порт>" # Только для типов компонентов udp и tcp
    reconnect:  # Только для типа компонентов tcp
      strategy: <стратегия переподключения: immediate или fixed>
      delay_ms: <задержка перед попыткой переподключения в миллисекундах> # Только для стратегии fixed
root:
  level: <минимальный уровень логирования: trace, debug, info, warn, error>
  appenders:
    - <название компонента 1>
    - <название компонента 2>
    ...

Где:

  • refresh_rate — интервал времени в секундах между проверками изменения конфигурации наблюдаемых баз данных.

  • appenders — компоненты, которые определяют, куда будут выводится, записываться или отправляться логи:

    • <название компонента> — произвольное название компонента. Можно перечислить несколько компонентов со своими параметрами:

      • kind — тип компонента:

        • console — вывод в консоль.

        • file — запись в файл без ротации.

        • rolling_file — запись в файл с ротацией.

        • udp — отправка через UDP.

        • tcp — отправка через TCP.

      • encoder — формат записи логов:

        • pattern — шаблон форматирования записей лога. Поддерживаются переменные:

          • {m} — сообщение лога.

          • {l} — уровень логирования.

          • {M} — имя модуля.

          • {T} — имя текущего компонента.

          • {f} — имя файла, в котором был вызван лог.

          • {L} — номер строки в исходном коде, в котором был вызван лог.

          • {t} — временная метка в Unix-формате.

          • {I} — идентификатор компонента.

          • {d(...)} — дата и время, указанные в круглых скобках с помощью параметров времени Bash. Например, {d(%Y-%m-%d %H:%M:%S)} задает дату и время в формате ГГГГ-ММ-ДД ЧЧ:ММ:СС.

          • {h(...)} — форматирование содержимого в круглых скобках в цветовые коды ANSI и свойства текста, указанные с помощью параметров:

            • fg=<цвет> — цвет текста.

            • bg=<цвет> — цвет фона.

            • bold=true — жирный шрифт.

            • dim=true — тусклый шрифт.

            • italic=true — курсив.

            Параметры цвета могут принимать следующие значения: black, red, green, yellow, blue, magenta, cyan, white и default. Например, {h({d(%Y-%m-%d %H:%M:%S)} - {l} - {m}{n}, fg=red, bg=black, bold=true)} выведет указанную в шаблоне запись красным жирным шрифтом на черном фоне.

          • {n} — перенос строки.

      • path — путь к файлу лога. Только для типов компонентов file и rolling_file. При типе компонентов rolling_file в этот файл записываются логи до того, как сработает триггер ротации.

      • policy — набор правил для ротации файлов логов для типа компонентов rolling_file:

        • trigger — условие, при котором начинается ротация:

          • kind — тип условия:

            • size — по размеру файлов.

            • time — по времени.

            • compound — комбинация нескольких типов условий. Например, условия по размеру файлов в 100 МБ и по расписанию каждый день в полночь будут выглядеть следующим образом:

              ...
                      kind: compound
                      triggers:
                        - kind: size
                          limit: 104857600 # 100 MB
                        - kind: time
                          schedule: "0 0 0 * * *" # Every day at midnight
              ...
              

              Файл будет ротирован при выполнение хотя бы одного из указанных условий.

          • limit — размер файла при типе условия size. Значение размера файла указывается в байтах, но можно и в КБ, МБ и ГБ, явно написав размерность после значения, например: 1048576kb, 1024mb или 1gb соответственно. При превышении файлом указанного размера, файл будет ротирован.

          • schedule — расписание в формате cron с расширением для секунд при типе условия time. Например, выражение */20 * * * * * будет запускать ротацию каждые 20 секунд.

        • roller — механизм ротации. Он определяет, как файлы будут обрабатываться после ротации.

          • kind — тип механизма ротации:

            • fixed_window — фиксированное количество ротационных файлов. Старые файлы будут переименовываться при создании нового файла, а при превышении указанного количества — удаляться.

            • delete — удаление старых файлов после ротации. Этот тип не предусматривает сохранение какого-либо количества старых файлов, как в случае с fixed_window. Как только ротация происходит, старый файл удаляется, и на его месте создается новый.

            • compound — комбинация нескольких типов механизма ротации, каждый из которых будет применяться последовательно. Например, совмещение фиксированного количества ротационных файлов из 10 штук, начиная с 1-го, с шаблоном сохранения logs/my_app_{}.log и удаление старых файлов после ротации будет выглядеть следующим образом:

              ...
                      kind: compound
                      rollers:
                        - kind: fixed_window
                          base: 1
                          count: 10
                          pattern: "logs/my_app_{}.log"
                        - kind: delete
              ...
              

          • base — начальный индекс для нумерации файлов ротации для типа механизма ротации fixed_window.

          • count — количество файлов ротации для типа механизма ротации fixed_window. При превышении этого количества самый старый файл будет удален.

          • pattern — шаблон имени файлов ротации. Символы {} используются для индекса файла. Например, logs/pg_integrity_{}.log указывает, что файлы будут сохраняться в каталог logs с именами pg_integrity_1.log, pg_integrity_2.log и так далее.

      • endpoint — адрес и порт в формате <адрес>:<порт>, на который будут отправляться логи, для типов компонентов udp и tcp.

      • reconnect — параметры переподключения при разрыве соединения для типа компонентов tcp.

        • strategy — стратегия переподключения:

          • immediate — сразу после разрыва.

          • fixed — с фиксированной задержкой.

        • delay_ms — задержка в миллисекундах перед попыткой переподключения для стратегии переподключения fixed.

  • root — настройка логирования:

    • level — минимальный уровень серьезности сообщений, которые будут логироваться: trace, debug, info, warn или error.

    • appenders — список описанных выше компонентов, которые необходимо задействовать при логировании.

Пример файла конфигурации для ведения журнала:

refresh_rate: 5 seconds
appenders:
  stdout:
    kind: console
    encoder:
      pattern: "{d(%Y-%m-%d %H:%M:%S)} - {m}{n}"
  my_file_logger:
    kind: rolling_file
    path: "logs/pg_integrity.log"
    encoder:
      pattern: "{d(%Y-%m-%d %H:%M:%S)} - {m}{n}"
    policy:
      trigger:
        kind: size
        limit: 104857600
      roller:
        kind: fixed_window
        base: 1
        count: 10
        pattern: "logs/pg_integrity_{}.log"
root:
  level: info
  appenders:
    - stdout
    - my_file_logger

Пример использования

  1. Создайте простой bash скрипт, чтобы приложение pg_integrity_verifier автоматически его запускало при нарушении целостности заданных типов объектов СУБД. Для этого создайте каталог examples, создайте в нем скрипт alert_example.sh и добавьте ему права на выполнение:

    mkdir -p examples && \
    cat > examples/alert_example.sh << 'EOL'
    #!/bin/bash
    json_file=""
    # Function to print usage
    print_usage() {
      echo "Usage: $0 -f json_file"
    }
    while getopts 'f:' flag; do
      case "${flag}" in
      f) json_file="${OPTARG}" ;;
      *) print_usage
        exit 1 ;;
      esac
    done
    if [ -z "$json_file" ]; then
      echo "You must provide a JSON file."
      print_usage
      exit 1
    fi
    echo "\n===================================="
    cat $json_file
    echo "\n===================================="
    EOL
    chmod +x examples/alert_example.sh
    

    Скрипт alert_example.sh содержит следующие шаги:

    1. Указывается, что скрипт должен быть выполнен с помощью интерпретатора bash.

    2. Объявляется переменная json_file, которая будет содержать путь к JSON-файлу, переданному в скрипт через параметры.

    3. Определяется функция print_usage(), которая выводит сообщение о том, как правильно использовать скрипт. $0 — это имя скрипта, а -f json_file указывает на то, что нужно передать флаг -f, за которым следует путь к JSON-файлу.

    4. Опции командной строки обрабатываются в цикле while с использованием getopts, что позволяет анализировать флаги и их аргументы.

      Если флаг -f обнаружен, путь к файлу сохраняется в переменную json_file.

      Если флаг неизвестен или отсутствует, вызывается функция print_usage(), и скрипт завершает работу с кодом ошибки 1.

    5. Если переменная json_file пуста (параметр не был передан или указан неверно), выводится сообщение, что необходимо передать JSON-файл, и скрипт завершает работу с кодом ошибки 1.

    6. Если JSON-файл указан корректно, его содержимое выводится на экран внутри разделительных линий для удобства отображения.

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

  2. Создайте основной файл конфигурации pg_integrity_verifier.yaml с указанием скрипта examples/alert_example.sh. Например:

    mode: "daemon"
    daemon_period: "5"
    databases:
      - db_url: "postgres://postgres:12345@localhost:5432/db1"
        snapshot_directory: "json1"
        observed_objs: ['config_file', 'pg_hba', 'pg_settings', 'pg_user', 'pg_proc', 'pg_trigger', 'pg_roles']
        run_command: "examples/alert_example.sh -f DIFF_FILE"
      - db_url: "postgres://postgres:54321@localhost:5432/db2"
        snapshot_directory: "json2"
        observed_objs: ['config_file', 'pg_hba', 'pg_settings', 'pg_user', 'pg_proc', 'pg_trigger', 'pg_roles']
        run_command: "examples/alert_example.sh -f DIFF_FILE"
    
  3. Создайте файл конфигурации для ведения журнала log4rs.yaml:

    refresh_rate: 5 seconds
    appenders:
      stdout:
        kind: console
        encoder:
          pattern: "{d(%Y-%m-%d %H:%M:%S)} - {m}{n}"
      my_file_logger:
        kind: rolling_file
        path: "logs/pg_integrity.log"
        encoder:
          pattern: "{d(%Y-%m-%d %H:%M:%S)} - {m}{n}"
        policy:
          trigger:
            kind: size
            limit: 104857600
          roller:
            kind: fixed_window
            base: 1
            count: 10
            pattern: "logs/pg_integrity_{}.log"
    root:
      level: info
      appenders:
        - stdout
        - my_file_logger
    
  4. Запустите pg_integrity_verifier с созданными конфигурационными файлами:

    ./pg_integrity_verifier \
        -l log4rs.yaml \
        -c pg_integrity_verifier.yaml
    

    Результат:

    2024-05-04 00:14:12 - db1 - The verifying started
    2024-05-04 00:14:12 - db1 - No changes found!
    2024-05-04 00:14:12 - db1 - The verifying finished
    2024-05-04 00:14:12 - db2 - The verifying started
    2024-05-04 00:14:12 - db2 - No changes found!
    2024-05-04 00:14:12 - db2 - The verifying finished
    2024-05-04 00:14:17 - db1 - The verifying started
    2024-05-04 00:14:17 - db1 - No changes found!
    2024-05-04 00:14:17 - db1 - The verifying finished
    2024-05-04 00:14:17 - db2 - The verifying started
    2024-05-04 00:14:17 - db2 - No changes found!
    2024-05-04 00:14:17 - db2 - The verifying finished
    ...
    
  5. Выполните любой SQL-запрос, меняющий структуру БД. Например, запрос на создание пользователя:

    su - postgres -c "psql -p 5432 -d db1 -U postgres -c \"CREATE USER test_user_tmp;\""
    
  6. Убедитесь, что в выводе pg_integrity_verifier появилась запись о создании нового пользователя:

    2024-05-04 00:15:02 - db1 - The verifying started
    2024-05-04 00:15:02 - db1 - Added new element with 40975 usesysid: pg_user (usesysid: 40975,
    usename: test_user_tmp, userecreated: false, useuper: false, userepl: false, usebypassrcls: false,
    valuntil: None, useconfig: [None])
    2024-05-04 00:15:02 - db1 - Changes have been written to file: json1n_db1.05-04-2024_00:15:02.diff
    2024-05-04 00:15:02 - db1 - `examples/alert_example.sh -f /opt/tantor/db/15/tools/
    pg_integrity_verifier/json1n_db1_05-04-2024_00:15:02.diff` executed successfully with exit status 0
    2024-05-04 00:15:02 - db1 -
    ====================================
    {
      "database": "db1",
      "datetime": "05-04-2024_00:15:02",
      "functions": null,
      "hba_conf": null,
      "pg_class": null,
      "pg_settings": null,
      "postgresql_conf": null,
      "triggers": null,
      "users": [
        {
          "new": {
            "usebypassrls": false,
            "useconfig": null,
            "usecreatedb": false,
            "usename": "test_user_tmp",
            "userepl": false,
            "usesuper": false,
            "usesysid": 40975,
            "valuntil": null
          },
          "usesysid": 40975
        }
      ]
    }
    ====================================
    2024-05-04 00:15:02 - db1 - The verifying finished
    2024-05-04 00:15:02 - db2 - The verifying started
    2024-05-04 00:15:02 - db2 - No changes found!
    2024-05-04 00:15:02 - db2 - The verifying finished
    

Таким образом, для информирования администраторов СУБД можно использовать скрипты, которые будут вызваны приложением pg_integrity_verifier при фиксировании изменений в наблюдаемой БД. В рассмотренном примере alert_example.sh принимает через аргумент -f файл, содержащий перечень изменений, и отображает его содержимое. Для реального применения содержимое входящего файла должно быть интерпретировано, и на основе изменений должна быть вызвана внешняя система, выполняющая функции уведомлений, например, отправка e-mail или оповещение в мессенджере.