pg_sec_check#
pg_sec_check
pg_sec_check — Инструмент аудита безопасности для PostgreSQL
Введение
pg_sec_check
(Проверка безопасности Postgres) — это
утилита, предназначенная для проведения аудита безопасности конфигураций баз данных PostgreSQL.
Она позволяет автоматизировать процесс проверки различных
аспектов безопасности — от настроек сервера до специфических параметров базы данных — предоставляя подробные отчеты о выявленных проблемах и
рекомендации по их устранению.
Особенности
Гибкая система проверок: возможность определения пользовательских проверок через конфигурационные файлы.
Многомодульная архитектура: поддержка различных типов исполнителей (SQL-запросы, shell-скрипты) и валидаторов (Lua-скрипты).
Версионирование проверок: возможность привязки проверок к конкретным версиям PostgreSQL (минимальная и максимальная поддерживаемые версии).
Локализация: поддержка многоязычных отчетов и сообщений (в настоящее время русский и английский языки).
Различные форматы отчетов: генерация отчетов в форматах HTML и JSON.
Параллельное выполнение: возможность использования нескольких потоков для ускорения процесса сканирования.
Защита целостности: в релизных сборках программа контролирует целостность своих компонентов с помощью проверки контрольных сумм.
Архитектура
Ядро программы: отвечает за парсинг аргументов командной строки, чтение конфигурационного файла, управление потоками выполнения, запуск исполнителей, передачу результатов валидаторам и формирование итогового отчета.
Конфигурационный файл (config.json): определяет структуру отчета, секции и конкретные проверки, включая пути к скриптам исполнителей и валидаторов.
Модули исполнителей (Executors): Скрипты
.sh
или.sql
, которые непосредственно взаимодействуют с системой или базой данных для сбора необходимой информации.Модули валидаторов (Validators): lua-скрипты, которые анализируют данные, полученные от исполнителей, и формируют заключение о статусе проверки, описание и рекомендации.
Механизм локализации: позволяет отображать сообщения валидаторов и элементы отчета на языке, выбранном пользователем.
Программа построена на основе трех основных типов модулей, которые взаимодействуют для выполнения проверок:
Исполнители (Executors): отвечают за сбор данных: SQL: для выполнения запросов к базе данных PostgreSQL и sh: для выполнения shell-скриптов на хосте, где запускается утилита, или на хосте с базой данных (в зависимости от логики скрипта и доступных переменных окружения).
Валидаторы (Validators): отвечают за анализ данных, полученных от исполнителей. Поддерживается только один тип файлов Lua: Для написания логики валидации.
Конфигурация: определяет какие исполнители и валидаторы используются для каждой конкретной проверки, а также метаданные проверки (название, описание, поддерживаемые версии PostgreSQL).
Такая модульная структура обеспечивает высокую степень кастомизации и позволяет пользователям добавлять собственные, специфичные для их окружения проверки без необходимости модификации основного кода программы.
Конфигурационный Файл (config.json)
Конфигурация проверок осуществляется через JSON-файл. Пример, simple_config.json
:
{ "header": "Server and Database Information Report", "report_name": "Report name", "description": "Some description", "section" : { "system" : { "header": "Example bash test", "description": "Common info about current host", "items": [ { "name": "hello world check", "description" : "Compare hello world results", "executors": [ { "script": "tests/common/script/simple_script.sh", "min_ver": "none", "max_ver": "none" } ], "validators": [ { "script": "tests/common/lua/simple_bash_validate.lua", "min_ver": "none", "max_ver": "none" } ] } ] }, "sql" : { "header": "Example sql test", "description": "Test connection into database", "items": [ { "name" : "select pg_version", "description" : "select pg_version", "executors": [ { "script": "tests/common/script/simple_sql.sql", "min_ver": "none", "max_ver": "none" } ], "validators": [ { "script": "tests/common/lua/simple_sql_validate.lua", "min_ver": "none", "max_ver": "none" } ] } ] } } }
Глобальные поля
header (String)
: общий заголовок для всего отчета.report_name (String)
: имя отчета.description (String)
: общее описание отчета.section (Object)
: объект, содержащий определения различных секций отчета. Каждая секция представляет собой логическую группу проверок. Ключи этого объекта (например, “system”, “sql”) являются идентификаторами групп, которые можно использовать для выборочного запуска проверок с помощью аргумента командной строки--groups
.
Структура секции (section)
header (String):
заголовок для данной секции в отчете.description (String)
: описание данной секции.items (Array)
: массив объектов, каждый из которых определяет отдельную проверку.
Структура элемента проверки (items)
name (String)
: уникальное имя проверки, отображаемое в отчете.description (String)
: подробное описание сути проверки.executors (Array)
: массив объектов-исполнителей. Для одной проверки может быть определено несколько исполнителей, однако следует учитывать, как их результаты будут обрабатываться валидатором. Обычно используется один исполнитель на проверку.validators (Array)
: массив объектов-валидаторов. Аналогично исполнителям, может быть несколько валидаторов. Чаще всего используется один валидатор.
Структура executor
script (String)
: путь к файлу скрипта-исполнителя. Это может быть.sh
-файл для shell-скриптов или.sql
-файлом для SQL-запросов.min_ver (String)
: минимальная версия PostgreSQL, для которой применима данная проверка (например, “10.0.0”). Если указано “none”, ограничение по минимальной версии отсутствует. Проверка не будет запускаться для версий PostgreSQL ниже указанной.max_ver (String)
: максимальная версия PostgreSQL, для которой применима данная проверка. Если указано «none», ограничение по максимальной версии отсутствует. Проверка не будет запускаться для версий PostgreSQL выше указанной.
Структура validator
script (String)
: путь к файлу Lua-скрипта валидатора.min_ver (String)
/max_ver (String)
: Аналогично min_ver для исполнителя, определяет минимальную версию PostgreSQL для запуска валидатора.
Такая детализированная структура конфигурационного файла позволяет точно настраивать каждую проверку, включая ее применимость к определенным версиям PostgreSQL, что особенно важно в средах с разнородными версиями СУБД.
Механизм проверок
Процесс проверки состоит из двух основных этапов: выполнение скрипта-исполнителя и анализ его результата скриптом-валидатором.
Исполнители (Executors)
Исполнители разработаны для сбора данных, необходимых для анализа безопасности.
Shell-скрипты (
.sh
): позволяют выполнять произвольные команды операционной системы. Это полезно для проверки конфигурационных файлов сервера, прав доступа, переменных окружения и других системных параметров.При запуске shell-скрипта программа устанавливает следующие переменные окружения, которые могут быть использованы внутри скрипта:
PG_HOST="хост базы данных"
PG_PORT="порт базы данных"
PG_USER:="имя пользователя для подключения к базе данных"
PG_DB="имя базы данных"
PGDATA="путь к каталогу данных PostgreSQL"
PG_VERSION="версия сервера PostgreSQL, к которому выполняется подключение"
PG_HBA_FILE="путь к файлу pg_hba.conf"
SQL-скрипты (
.sql
): используются для выполнения запросов к базе данных PostgreSQL с целью получения информации о настройках, объектах базы данных, правах пользователей и т.д.В настоящее время для SQL-файлов поддерживается выполнение только одного SELECT-запроса. Результат этого запроса передается валидатору. Это ограничение следует учитывать при разработке SQL-проверок; сложные проверки могут потребовать либо более комплексного SELECT-запроса, либо разделения на несколько отдельных проверок.
Валидаторы (Validators)
Валидаторы анализируют данные, собранные исполнителями, и выносят вердикт о состоянии проверяемого параметра. Вся логика валидации реализуется на языке Lua.
Глобальные данные, доступные в Lua-валидаторах:
Для валидаторов, обрабатывающих результаты sh-скриптов:
result.stdout (String):
стандартный вывод (stdout) выполненного shell-скрипта.
result.stderr (String):
стандартный вывод ошибок (stderr) выполненного shell-скрипта.
result.return_code (Number):
код возврата выполненного shell-скрипта.Пример Lua-скрипта для валидации shell скрипта
simple_bash_validate.lua
:-- GLOBAL DATA -- result.stdout the output of stdout from the executed bash script -- result.stderr the output of stderr from the executed bash script -- result.return_code the return code from the executed bash script function validate_result() local success = true local status_str = "Success" local short_desc = "The check was successful" local full_desc = "All parameters meet the requirements" local recommend = "No action is required" if not result.stdout == "hello world" then status_str = "Warning" short_desc = "Validation error" full_desc = "The X parameter does not match the Y format" recommend = "Fix the X parameter" end -- Important! The return value should look exactly like this. -- That is, the structure (table) should not change. -- { -- status: one of the values is (Warning, Info, Error, Success, Failed) -- short_description: String -- full_description: String -- recommendation: String -- } local result_table = { status = status_str, short_description = short_desc, full_description = full_desc, recommendation = recommend } return result_table end
Для валидаторов, обрабатывающих результаты SQL-скриптов:
В Lua-скрипт передается глобальная таблица
sql_results
. Эта таблица представляет собой массив, где каждый элемент является таблицей, соответствующей одной строке результата SQL-запроса. Имена полей в этих вложенных таблицах соответствуют именам столбцов в SELECT-запросе.Пример Lua-скрипта для валидации SQL-запроса
simple_sql_validate.lua
:-- GLOBAL DATA -- sql_results a table with the result of SQL execution function validate_result() local success = true local status_str = "Success" local short_desc = "Error: SQL data is not received or incorrect" local full_desc = "All parameters meet the requirements" local recommend = "No action is required" -- Checking for the presence of the first line if sql_results and sql_results[1] then -- Getting the first row (this is a table) local first_row = sql_results[1] -- an example for a table of the form: -- pg_version ------------------------ -- pg version data bla bla bla if first_row and type(first_row) == 'table' and first_row.pg_version then short_desc = first_row.pg_version status_str = "Success" full_desc = "Version PostgreSQL: " .. short_desc else short_desc = "Error: The 'pg_version' field was not found in the SQL result" status_str = "Failed" end else -- sql_results is empty status_str = "Failed" end -- Important! The return value should look exactly like this. -- That is, the structure (table) should not change. -- { -- status: one of the values is (Warning, Info, Error, Success, Failed) -- short_description: String -- full_description: String -- recommendation: String -- } local result_table = { status = status_str, short_description = short_desc, full_description = full_desc, recommendation = recommend } return result_table end
Структура возвращаемого значения Lua-валидатора:
Каждый Lua-скрипт валидатора должен определять
функцию validate_result()
, которая возвращает
таблицу со строго определенной структурой:
{ status: "Warning" | "Info" | "Error" | "Success" | "Failed", -- Must be one of these values short_description: "Brief description of the result", -- String full_description: "Detailed explanation of the result", -- String recommendation: "Recommended remediation actions" -- String }
Соблюдение этой структуры критически важно, так как ядро программы ожидает результат именно в таком формате для корректного формирования отчета. Отклонения от этой структуры приведет к ошибкам при обработке результатов валидации.
locale_msg
функция
Сценарии проверки Lua могут использовать функцию locale_msg(key) для получения локализованных строк.
См. раздел Локализация для получения более подробной информации.
Локализация
Программа поддерживает локализацию сообщений, выводимых в отчетах, что позволяет адаптировать вывод для пользователей, говорящих на разных языках.
Механизм locale_msg
В Lua-скриптах валидаторов для получения переведенных строк используется функция locale_msg
, где key
– это строковый ключ сообщения. Например, вызов locale_msg("first")
вернет строку, соответствующую ключу "first"
на языке, указанном при запуске программы через аргумент --locale
. По умолчанию используется английский язык, если файлы локализации для выбранного языка не найдены или ключ отсутствует.
Формат файла локализации (.loc)
Файлы локализации представляют собой текстовые файлы с расширением
.loc
(например,
en.loc
, ru.loc
). Каждая строка
в файле соответствует одному сообщению и имеет следующий формат:
id,"message_key"
Примеры:
localizations/ru.loc
# ru.loc # --- validate_anon_extension.lua --- 1,"<p>Проверка предзагрузки расширений анонимизации (<code>anon</code> или <code>pg_anonymize</code>) через параметр <code>session_preload_libraries</code>.</p>" 2,"<p>Анализ <code>session_preload_libraries</code> для расширений анонимизации не был успешно завершен или расширения не найдены.</p>" 3,"<ul><li>Проверьте SQL-запрос и конфигурацию PostgreSQL (параметр <code>session_preload_libraries</code>).</li></ul>" ... 1674,"использование SSL"
localizations/en.loc
# en.loc # --- validate_anon_extension.lua --- 1,"<p>Checking preload of anonymization extensions (<code>anon</code> or <code>pg_anonymize</code>) via the <code>session_preload_libraries</code> parameter.</p>" 2,"<p>Analysis of <code>session_preload_libraries</code> for anonymization extensions was not successfully completed or extensions were not found.</p>" 3,"<ul><li>Check the SQL query and PostgreSQL configuration (parameter <code>session_preload_libraries</code>).</li></ul>" ... 1674,"Ssl usage"
Если программа запускается с аргументом
--locale ru
, вызов
locale_msg("first")
вернет
строку "first"
на русском языке. Механизм поиска
основан на сопоставлении строкового ключа, переданного в
locale_msg
, со вторым полем в
файле локализации. Назначение числового идентификатора (например,
1
) в настоящее время не полностью определено, но
строковый ключ считается основным идентификатором.
Поддержание актуальности и полноты файлов локализации для всех поддерживаемых языков является важной задачей при добавлении новых проверок или изменении существующих сообщений. Отсутствие ключа в файле локализации для выбранного языка может привести к отображению сообщения на языке по умолчанию (английском).
Поддерживаемые языки
Английский (
en
)Русский (
ru
)
Форматы отчетов
Программа способна генерировать отчеты о результатах проверок в нескольких форматах.
Формат вывода указывается с помощью
аргумента командной строки --format
(или -f
).
Доступные форматы:
Html: генерирует отчет в виде HTML-страницы. Этот формат удобен для визуального анализа результатов, так как он может содержать интерактивные элементы, стилизацию и легко читается человеком в веб-браузере.
Json: генерирует отчет в формате JSON. Этот формат является текстовым, машиночитаемым и широко используется для обмена данными между системами. JSON-отчеты подходят для автоматизированной обработки, интеграции с системами мониторинга, SIEM-системами или для последующего анализа с помощью скриптов и других инструментов.
Full: при выборе этого формата (который используется по умолчанию), программа, предположительно, генерирует отчеты во всех доступных форматах одновременно (т.е. и HTML, и JSON). Это позволяет пользователю получить как человекочитаемую версию для непосредственного изучения, так и машиночитаемую версию для автоматизации без необходимости повторного запуска утилиты. Имена выходных файлов будут формироваться на основе значения аргумента
--output
с добавлением соответствующих расширений (.html
и.json
).
Выбор формата отчета зависит от целей пользователя: для быстрого обзора и представления результатов руководству подойдет HTML, для интеграции и автоматической обработки — JSON.
Использование
Аргументы командной строки
Программа управляется через аргументы командной строки. Ниже приведена таблица с описанием доступных опций:
Аргумент | Тип | Значение по умолчанию | Описание |
---|---|---|---|
--format
|
String
| Full | Формат выходного отчета. Опции: Full, Html, Json |
--config
|
String
| config.json | Путь к конфигурационному файлу |
--output
|
String
| output | Базовое имя для выходных файлов отчета (output_web/ и output.json) |
--port
|
u16
| 5432 | Порт для подключения к базе данных PostgreSQL |
--host
|
String
| /tmp | Хост для подключения к базе данных (IP-адрес, имя хоста или путь к Unix-сокету) |
--user
|
String
| dev | Имя пользователя для подключения к базе данных |
--database
|
String
| postgres | Имя базы данных для подключения |
--threads
|
u16
| Количество ядер CPU | Количество потоков для выполнения задач |
--debug
|
bool
| false | Включить режим отладки (более подробный вывод) |
--pgclients
|
u16
| 10 | Размер пула соединений PostgreSQL |
--locale
|
String
| ru | Код локали для отчетов |
--groups
|
Vec<String>
| None | Список групп проверок для запуска (через запятую, например “system,sql”) |
--unsecure
|
bool
| false | Запуск без проверки контрольных сумм файлов программы |
--groups
предоставляет возможность запускать только определенные группы проверок, определенные как ключи в объектеsection
конфигурационного файла. Это удобно для целевого тестирования или поэтапного аудита.--unsecure
предназначен для особых случаев использования. В релизной сборке программа по умолчанию проверяет контрольные суммы всех своих компонентов, включая скрипты исполнителей и валидаторов, которые поставляются вместе с ней. Эта мера обеспечивает целостность инструмента и защищает от случайных или намеренных несанкционированных модификаций его частей. Если какой-либо из этих файлов был изменен (например, в процессе разработки или кастомизации стандартной проверки), программа откажется запускаться, сообщив об ошибке несоответствия контрольной суммы. Флаг--unsecure
позволяет обойти эту проверку и запустить программу с модифицированными файлами.
Предупреждение: Использование флага
--unsecure
отключает важный механизм безопасности самой утилиты.
Его следует применять с осторожностью. Не рекомендуется использовать флаг
--unsecure
в производственных или доверенных средах без полного понимания
рисков, так как это может позволить выполнение потенциально измененного
(возможно, вредоносного) кода скриптов.
Дополнительные аргументы:
RUST_LOG
: переменная окружения, позволяющая настроить уровень логов.BACKTRACE
: переменная окружения, позволяющая увидеть стек вызовов в случае аварийного завершения утилиты.
Разработка и Кастомизация
Гибкость Postgres Security Check обеспечивается возможностью создания пользовательских проверок и добавления новых языков локализации.
Создание собственных проверок
Процесс создания новой проверки включает следующие шаги:
Определить цель проверки.
Четко сформулировать, какой аспект безопасности или конфигурации необходимо проверить.
Написать скрипт-исполнитель.
Для shell-скриптов (
.sh
): Разработать скрипт, который собирает необходимые данные. Помните о доступных переменных окружения (PG_HOST
,PG_PORT
,PG_USER
,PG_DB
,PGDATA
,PG_VERSION
,PG_HBA_FILE
).Для SQL-скриптов (
.sql
): НаписатьSELECT
-запрос (только один на файл), который извлекает необходимую информацию из базы данных.
Написать Lua-скрипт-валидатор (
.lua
).Разработать Lua-скрипт, который будет анализировать вывод исполнителя (переданный через глобальную таблицу
result
дляsh
илиsql_results
для SQL). Функцияvalidate_result()
, в скрипте должна возвращать таблицу со стандартной структурой. Используйте функциюlocale_msg("ключ")
для всех строк, которые должны быть локализованы, и добавить соответствующие ключи и переводы в файлы*.loc
.Добавить новую проверку в
config.json
.В соответствующем секции (section) добавить новый элемент в массив
items
. Указатьname
(имя) иdescription
(описание) проверки. В массивахexecutors
иvalidators
указать пути к созданным скриптам. При необходимости задатьmin_ver
иmax_ver
для ограничения применимости проверки к определенным версиям PostgreSQL.
Советы по отладке:
Тестируйте скрипты-исполнители отдельно, чтобы убедиться, что они корректно собирают данные.
Проверяйте логику Lua-валидаторов, подавая им на вход ожидаемые и граничные данные от исполнителей. Пошаговая отладка или вывод промежуточных значений в Lua может помочь.
Возможность создавать собственные проверки является одной из сильных сторон данного инструмента, позволяя адаптировать его под уникальные требования безопасности и инфраструктуры любой организации.
Добавление новых языков локализации
Система локализации основана на текстовых файлах, что упрощает добавление поддержки новых языков.
Чтобы добавить новую локализацию:
Создайте новый файл
.loc
. Например, для французского создайте файл с именемfr.loc
. Имя файла должно соответствовать коду языка, который будет передан с использованием аргумента--locale
.Скопируйте ключи. Возьмите за основу существующий файл
en.loc
и скопируйте все строки.Переведите значения. Замените текстовые значения строк на соответствующий перевод на новый язык. Например, если в
en.loc
есть строка:10,"connection failed"
, то вfr.loc
она может выглядеть так:10,"échec de la connexion"
Разместите файл. Убедитесь, что программа сможет найти новый файл локализации. Такие файлы размещаются в специальном каталогe
localizations
.
Тщательное ведение файлов локализации и обеспечение их синхронности (наличие всех ключей во всех файлах) важно для качественного пользовательского опыта на разных языках.