Дописал reject в конец pg_hba.conf, а человек всё равно заходит: как на самом деле работает порядок правил в PostgreSQL 18
Уволился аналитик, на ноутбуке остался DBeaver с сохранённым паролем к боевой базе. Админ дописывает в конец pg_hba.conf строку с reject, сохраняет и закрывает вопрос. Через неделю в логах видно, что с того же адреса кто-то спокойно работает. Разбираю, почему точный запрет проигрывает общему разрешению, как за пять минут понять, читает ли сервер тот файл, который вы правите, и как выстроить pg_hba.conf так, чтобы порядок строк не приходилось держать в голове.
reject в конце файла не запрещает ничего
Начну с механики, потому что она объясняет 90 % таких обращений. PostgreSQL читает pg_hba.conf сверху вниз и останавливается на первой строке, которая совпала одновременно по четырём параметрам: тип подключения, адрес клиента, запрошенная база данных и имя пользователя. Дальше он не смотрит вообще. В документации 18-й версии это написано прямым текстом: «The first record with a matching connection type, client address, requested database, and user name is used to perform authentication. There is no „fall-through“ or „backup“: if one record is chosen and the authentication fails, subsequent records are not considered. If no record matches, access is denied». То есть никакого «отката» на следующее подходящее правило нет: выбрали строку — работаем по ней, чем бы это ни закончилось.
Ошибка почти всегда одна и та же и она не в синтаксисе. Человек переносит на pg_hba логику маршрутизации, где выигрывает самый длинный префикс, и ждёт, что /32 окажется важнее /0 независимо от места в файле. В pg_hba такого нет. Здесь всё решает физический порядок строк, как в списке ACL, который просматривается последовательно. Если выше уже стоит host all all 0.0.0.0/0 scram-sha-256, то любая последующая строка про конкретный хост — мёртвый текст. Она формально валидна, она видна в pg_hba_file_rules, она даже получит свой номер правила — и никогда не будет выбрана.
Смотрите на типичный «до» и «после». Файл слева выглядит логично для человека и не работает; файл справа выглядит некрасиво и работает.
# НЕ РАБОТАЕТ: точный запрет ниже общего разрешения
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all 192.168.10.0/24 scram-sha-256
host all all 192.168.10.77/32 reject
# РАБОТАЕТ: запрет выше разрешения
local all all peer
host all all 192.168.10.77/32 reject
host all all 127.0.0.1/32 scram-sha-256
host all all 192.168.10.0/24 scram-sha-256
host all all all rejectДокументация формулирует это как рекомендацию по стилю: «Typically, earlier records will have tight connection match parameters and weaker authentication methods, while later records will have looser match parameters and stronger authentication methods». Читайте так: сверху узкое и точное, снизу широкое и общее. Всё, что вы хотите запретить адресно, живёт в верхней трети файла. Всё, что разрешаете массово, — в нижней.
- Совпадение проверяется по четырём полям сразу: тип, адрес, база, пользователь.
- Первая совпавшая строка — окончательная, «отката» на следующую не будет.
- Провал аутентификации по выбранной строке не запускает поиск дальше.
- Если не совпала ни одна строка — доступ закрыт (но в логе это будет другая ошибка).
Диагностика за пять минут: тот ли файл, та ли строка, применён ли он
Прежде чем спорить о порядке правил, я всегда проверяю три вещи в таком порядке: какой файл сервер реально читает, как он разобрал строки и перечитал ли он их после правки. Три запроса, меньше минуты. На Debian и Ubuntu конфиги живут в /etc/postgresql/18/main/, на RHEL-подобных — прямо в каталоге данных /var/lib/pgsql/18/data/, а если кластеров на машине несколько, шансы отредактировать чужой файл резко растут. Спрашивайте у сервера, а не у файловой системы.
# 1. Какой именно файл читает ЭТОТ инстанс
psql -p 5432 -c 'SHOW hba_file;'
psql -p 5432 -c 'SHOW config_file;'
# 2. Как сервер разобрал правила (только суперпользователь)
psql -x -c "SELECT rule_number, file_name, line_number, type, database, user_name, address, auth_method, error FROM pg_hba_file_rules ORDER BY rule_number;"
# 3. Только сломанные строки
psql -c "SELECT file_name, line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;"В представлении pg_hba_file_rules колонка rule_number — это и есть номер, «indicates the order in which each rule is considered until a match is found during authentication». Если у строки rule_number пустой, а error заполнен — строка не разобралась и в правилах её нет вообще. Это второй по частоте случай после порядка: опечатка в маске, лишний пробел внутри списка баз, не закавыченное имя с дефисом. Файл выглядит правильным, сервер молча его пропустил. Обратите внимание, что представление показывает содержимое файла на диске, а не то, что сейчас в памяти постмастера, — оно как раз для предварительной проверки перед reload.
И третье: применение. На Unix изменения подхватываются при старте и по SIGHUP — «you will need to signal the postmaster (using pg_ctl reload, calling the SQL function pg_reload_conf(), or using kill -HUP) to make it re-read the file». На Windows отдельная история: там правки применяются сразу к последующим новым подключениям, сигнал слать не нужно. Я видел, как из-за этой разницы админ, привыкший к Windows-стенду, две недели правил файл на Linux-сервере и искренне считал, что настройка не работает.
- `SHOW hba_file;` — единственный надёжный способ узнать редактируемый файл.
- `pg_hba_file_rules` читается по умолчанию только суперпользователем; при необходимости выдайте GRANT SELECT отдельной роли мониторинга.
- `error IS NOT NULL` — строка не разобралась и в аутентификации не участвует.
- `SELECT pg_reload_conf();` возвращает true при успешной постановке задачи, а не при успешном разборе файла — результат смотрите в логе.
- Включите `log_connections` и `log_disconnections = on` хотя бы на время разбора; в PostgreSQL 18 `log_connections` стал списком (`receipt`, `authentication`, `authorization`, `setup_durations`, `all`), старое `on` по-прежнему принимается и равно первым трём. Неудачная аутентификация пишется в лог всегда.
Разбор из практики: хостел «Гавань», 14 рабочих мест, запрет, который ничего не запретил
Хостел «Гавань»: 14 рабочих мест — ресепшен в две смены, бухгалтерия, управляющий и пара ноутбуков у приходящих специалистов. 1С:Управление нашей фирмой на PostgreSQL, сервер 1С и СУБД на одной физической машине в подсобке, рядом отдельная база с выгрузками из системы бронирования. На момент разбора — PostgreSQL 18.6, в кластере четыре базы, боевая 1С занимает около 30 ГБ, рабочие процессы сервера 1С держат десяток постоянных соединений. Обратились с формулировкой «бывший подрядчик ходит в базу, хотя мы его заблокировали»: человек полгода строил отчёты по загрузке номеров напрямую из PostgreSQL через DBeaver, договор закончился, а в журнале продолжали появляться его запросы.
Блокировали так: в конец pg_hba.conf дописали строку host all all 192.168.10.77/32 reject и на этом остановились. Дальше по пунктам, что показал разбор. Первое: строка стояла девятой, а разрешение host all all 192.168.10.0/24 scram-sha-256 — пятой. То есть до девятой очередь не доходила физически. Второе, и это интереснее: reload вообще никто не делал. Файл пролежал изменённым трое суток, а постмастер всё это время работал по конфигу, прочитанному при последнем рестарте после обновления платформы месяц назад. Оба факта видно в одном запросе — pg_hba_file_rules показывает файл с диска, и там строка уже была, а в логе аутентификации за три дня не было ни одного отказа.
$ psql -c "SELECT rule_number, line_number, address, netmask, auth_method FROM pg_hba_file_rules WHERE address LIKE '192.168.10.%';"
rule_number | line_number | address | netmask | auth_method
-------------+-------------+---------------+-----------------+---------------
5 | 96 | 192.168.10.0 | 255.255.255.0 | scram-sha-256
9 | 118 | 192.168.10.77 | 255.255.255.255 | rejectТретье — и вот это уже про то, о чём почти никогда не думают. Даже после исправления порядка и корректного reload сессия сотрудника осталась жива. В pg_stat_activity висело соединение с backend_start девятидневной давности: DBeaver держал коннект открытым, ноутбук подрядчика стоял в гостевой комнате персонала и не выключался, а pg_hba проверяется только в момент установления соединения. Правило нового подключения не пускало — но старое работало как ни в чём не бывало.
Итог по работам занял двадцать минут. Подняли reject в верхнюю часть файла, сразу после блока local. Отдельно проверили, что строка для сервера 1С (он ходит на 127.0.0.1 и ::1) осталась выше запретов и учётная запись 1С не задета. Сделали pg_ctl reload и убедились по логу, что файл принят. Прибили живые сессии с этого адреса через pg_terminate_backend. Сменили пароль роли — потому что запрет по IP снимается сменой сети, а скомпрометированный пароль остаётся скомпрометированным. И в конец файла добавили явный host all all all reject, чтобы неучтённые адреса получали внятный отказ в логе, а не неявное «no pg_hba.conf entry». За месяц наблюдения — три попытки с того же ноутбука, все в логе, все отбиты.
- Порядок строк — reject был ниже разрешения на подсеть.
- Конфиг не перечитывали: правка лежала на диске трое суток без эффекта.
- Живая сессия пережила исправление правил: pg_hba работает только на новых подключениях.
- Пароль роли не меняли — запрет по IP обходится сменой рабочего места.
- Не было явной финальной строки reject, поэтому отказы не были видны в логе как отказы.
Шесть причин, по которым правило «не сработало», кроме порядка
Порядок строк — самая частая, но далеко не единственная причина. Разберу остальные, с которыми сталкиваюсь регулярно, в порядке убывания частоты.
Пулер посередине. Если между клиентами и базой стоит PgBouncer или Odyssey, сервер видит адрес пулера, а не адрес человека. Все правила про 192.168.10.77 в pg_hba бесполезны по определению: с точки зрения PostgreSQL к нему подключается 127.0.0.1 или адрес хоста пулера. Запрещать нужно там же, где пулер принимает соединения, — в pgbouncer.ini и его собственном auth-файле, либо на уровне фаервола. Диагностируется мгновенно: SELECT DISTINCT client_addr FROM pg_stat_activity показывает один-два адреса вместо десятков.
IPv4 против IPv6 и local против host. Документация не оставляет свободы толкования: «An entry given in IPv4 format will match only IPv4 connections, and an entry given in IPv6 format will match only IPv6 connections, even if the represented address is in the IPv4-in-IPv6 range». Запретили 127.0.0.1/32 — а клиент пришёл на ::1/128 и попал в следующее правило. Отдельная классика: человек ходит через SSH-туннель, и для сервера он вообще не 192.168.10.77, а локальный loopback. Запрет по его «настоящему» адресу в такой схеме не значит ничего.
Тип подключения уже, чем кажется. hostnossl не совпадёт с TLS-соединением, hostssl — с открытым, replication в колонке базы — это отдельная сущность: значение all матчит все базы, но не матчит запрос физической репликации. Если вы закрываете доступ и не закрыли отдельно строку с replication, реплика или pg_basebackup спокойно пройдут мимо вашего запрета.
include_dir и порядок файлов. Начиная с 16-й версии в pg_hba.conf работают директивы include, include_if_exists и include_dir. Содержимое подключается ровно на месте директивы, а файлы внутри каталога обрабатываются «in file name order (according to C locale rules, i.e., numbers before letters, and uppercase letters before lowercase ones)». Отсюда два подвоха разом: если строка include_dir стоит в конце основного файла, то всё, что вы аккуратно разложили по conf.d, окажется ниже общих разрешений; а внутри каталога 10-app.conf будет прочитан раньше 9-deny.conf, потому что сравниваются строки, а не числа, и запрет из «девятки» окажется ниже разрешений. Префиксы делайте двузначными и с нуля.
Не тот кластер и не тот файл. Два инстанса на 5432 и 5433, тестовый и боевой каталоги данных, конфиг из системы конфигурации, который на следующем прогоне возвращает файл к шаблону. Последнее особенно обидно: правило живёт до ближайшего запуска Ansible или оператора Kubernetes. В сообществе Percona ровно на этом ломались люди, у которых оператор дописывал пользовательские правила в конец файла и не давал поставить их выше собственных строк по умолчанию — при первом совпадении такие reject не достигаются никогда.
- PgBouncer/Odyssey: сервер видит адрес пулера — фильтруйте на пулере.
- IPv4-запись не матчит IPv6-подключение и наоборот; ::1 и 127.0.0.1 — разные строки.
- SSH-туннель превращает удалённого клиента в локального.
- `all` в колонке database не покрывает физическую репликацию.
- include_dir подставляется на месте директивы; имена файлов сортируются по локали C.
- Автоматизация (Ansible, оператор в Kubernetes) может вернуть файл к шаблону и стереть правку.
Как я строю pg_hba.conf, чтобы порядок не приходилось помнить
Держать порядок правил в голове — плохая стратегия: файл живёт годами, правят его разные люди, и рано или поздно кто-то допишет строку в конец. Поэтому я задаю структуру, в которой место новой строки определяется не аккуратностью автора, а именем файла. Схема простая: пять блоков сверху вниз, каждый в своём файле, подключение через include_dir в самом начале основного pg_hba.conf.
# /etc/postgresql/18/main/pg_hba.conf
# ЕДИНСТВЕННОЕ содержимое основного файла:
include_dir 'hba.d'
# hba.d/00-local.conf — доступ администратора с самого сервера
# hba.d/10-deny.conf — адресные запреты, всегда выигрывают
# hba.d/30-replication.conf— реплики и pg_basebackup
# hba.d/50-app.conf — сервер 1С, пулер, сервисные роли
# hba.d/70-people.conf — живые пользователи и их подсети
# hba.d/99-default.conf — финальный host all all all rejectСодержимое блоков выглядит так. В 00-local.conf оставляю себе local all postgres peer — это страховка от самоблокировки, её нельзя убирать даже «на время». В 10-deny.conf лежат все reject: уволенные, скомпрометированные машины, гостевые сегменты. В 50-app.conf адреса сервера 1С и пулера с узкими масками /32 и конкретными именами баз и ролей, без all. В 70-people.conf — подсети сотрудников. В 99-default.conf ровно одна строка отказа.
# hba.d/10-deny.conf
host all all 192.168.10.77/32 reject
host all all 192.168.90.0/24 reject
# hba.d/50-app.conf
host all usr1cv8 127.0.0.1/32 scram-sha-256
host all usr1cv8 ::1/128 scram-sha-256
hostssl all +ro_analytics 192.168.10.0/24 scram-sha-256
hostssl "/^unf_(dev|test)$" all 192.168.20.0/24 scram-sha-256
# hba.d/99-default.conf
host all all all rejectДве возможности, которыми стоит пользоваться и о которых почти не знают. Первая: группы через плюс. Запись +ro_analytics матчит любую роль, которая прямо или косвенно входит в эту группу, — вы управляете доступом через GRANT, а pg_hba больше не трогаете. Вторая: регулярные выражения в колонках базы и пользователя, доступные с 16-й версии — «Regular expression patterns are prefixed with a slash». Одна строка вида "/^unf_(dev|test)$" заменяет десяток. Кавычки вокруг регулярного выражения в документации используются в примерах и нужны, когда в шаблоне есть запятая, пробел или решётка. А вот группу через плюс в кавычки не берите: закавыченное значение теряет специальный смысл, и "+ro_analytics" будет искать роль с плюсом в имени.
Скажу честно про спорный момент. Финальную строку host all all all reject часть коллег считает лишней: если правило не совпало, доступ и так закрыт. Формально верно. Я всё равно её ставлю, потому что явный отказ пишется в лог иначе, чем неявный, и при разборе инцидента вы за секунду отличаете «правило отработало» от «правило не нашлось». Это стоит одной строки.
- include_dir — первой директивой основного файла, а не последней.
- Двузначные числовые префиксы с нуля: 00, 10, 30, 50, 70, 99.
- Блок reject всегда выше блоков разрешений.
- Группы через `+role` вместо перечисления пользователей.
- Регулярные выражения через `/` для семейств баз (PostgreSQL 16 и новее).
- Обязательная страховочная строка `local all postgres peer` в самом верху.
Живые соединения: почему правило есть, а человек работает
pg_hba проверяется один раз — в момент установления соединения. Всё, что уже подключено, живёт по правилам, действовавшим на момент коннекта, и переживёт любое количество reload. Клиенты вроде DBeaver, pgAdmin и обвязки на JDBC держат соединение открытым сутками, а пул сервера 1С — вообще постоянно. Поэтому «правило не сработало» очень часто означает «правило работает, но у человека сессия с прошлой недели».
Порядок действий важен и обратный ему не работает. Сначала правим файл и делаем reload, только потом рвём сессии. Если сделать наоборот, клиент переподключится по старым правилам за долю секунды и вы получите ту же сессию с новым PID.
-- кто именно висит с проблемного адреса
SELECT pid, usename, datname, client_addr, backend_start, state
FROM pg_stat_activity
WHERE client_addr = '192.168.10.77'
ORDER BY backend_start;
-- рвём (после reload, не до него)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE client_addr = '192.168.10.77'
AND pid <> pg_backend_pid();Если задача не «закрыть один адрес», а «отключить человека совсем», работайте с ролью, а не с адресом. ALTER ROLE ivanov NOLOGIN; запрещает новые входы независимо от того, откуда человек придёт, ALTER ROLE ivanov CONNECTION LIMIT 0; даёт похожий эффект, а смена пароля обесценивает всё, что сохранено в клиентах. Ни одна из этих команд, подчеркну, не рвёт уже открытые сессии — их всё равно придётся прибить руками.
- Порядок: правка файла → reload → проверка логов → terminate сессий.
- `pg_terminate_backend` работает без рестарта и не влияет на другие соединения.
- `ALTER ROLE ... NOLOGIN` надёжнее IP-запрета, если цель — конкретный человек.
- Смена пароля роли обязательна, если пароль мог быть сохранён в клиенте.
- Пул сервера 1С после terminate переподключится сам — приложение этого обычно не заметит.
scram-sha-256 против md5, тексты ошибок и особенности 1С
Вторая половина строки pg_hba — метод аутентификации, и здесь тоже много путаницы. С PostgreSQL 14 параметр password_encryption по умолчанию равен scram-sha-256 (раньше был md5), то есть все пароли, заданные через CREATE ROLE или ALTER ROLE на свежем кластере, хранятся как SCRAM-хеш. Метод md5 в pg_hba при этом не ломает вход: документация прямо говорит, что если в строке указан md5, а пароль роли хранится в SCRAM, сервер автоматически выберет SCRAM. Обратное не работает: строка scram-sha-256 не пустит роль, у которой пароль до сих пор лежит MD5-хешем после pg_upgrade со старой версии.
Поддержка MD5-паролей объявлена устаревшей и будет удалена в одной из будущих версий; в 18-й ветке появился параметр md5_password_warnings (по умолчанию on), который выдаёт WARNING при установке MD5-пароля. Переход по документации выглядит так: убедиться, что клиентские библиотеки поддерживают SCRAM, выставить password_encryption = 'scram-sha-256', заново задать пароли всем ролям и только потом поменять метод в pg_hba.conf.
-- у кого пароль всё ещё в MD5 (нужен суперпользователь)
SELECT rolname, left(rolpassword, 14) AS hash_prefix
FROM pg_authid
WHERE rolpassword LIKE 'md5%';
SHOW password_encryption;
-- после смены пароля хеш начнётся с SCRAM-SHA-256$
ALTER ROLE usr1cv8 PASSWORD '...';Тексты ошибок сразу подсказывают, где искать. FATAL: no pg_hba.conf entry for host "…", user "…", database "…" означает, что не совпала ни одна строка — подключение упало мимо всех правил. pg_hba.conf rejects connection for host … — совпала строка с reject, запрет сработал. password authentication failed for user — строка найдена, метод выбран, не подошёл пароль (или у роли MD5-хеш при методе scram-sha-256). Именно по этой разнице я отличаю «правило отработало» от «правило не нашлось». Помните, что клиенту сервер сообщает меньше, чем пишет в собственный лог, так что разбор всегда начинается с журнала сервера.
Теперь про 1С. Сервер 1С ходит в PostgreSQL под одной ролью, и именно он, а не пользователи, фигурирует в pg_hba. Если сервер 1С и СУБД на одной машине, как в хостеле из разбора, ему нужны строки на 127.0.0.1/32 и ::1/128 — платформа может подключаться по любому из адресов. Колонку базы для роли 1С я держу как all или регулярное выражение по префиксу: базы создаются из консоли кластера 1С, и строка с конкретным именем сломает создание новой информационной базы. Пользователи 1С в pg_hba не видны вообще, поэтому уволенному сотруднику закрывают доступ в самой 1С, а pg_hba защищает только от прямых подключений в обход платформы — через DBeaver, psql или Excel по ODBC.
- С PostgreSQL 14 `password_encryption` по умолчанию `scram-sha-256`.
- Метод `md5` в pg_hba сам переключается на SCRAM, если пароль роли хранится в SCRAM; наоборот — нет.
- После pg_upgrade со старых версий проверьте `pg_authid` на MD5-хеши до смены метода.
- «no pg_hba.conf entry» — ни одна строка не совпала; «pg_hba.conf rejects connection» — совпал reject.
- Роль сервера 1С — строки и на 127.0.0.1/32, и на ::1/128, выше любых запретов.
- Перед переводом роли 1С на scram-sha-256 проверьте на тестовой базе, что ваша сборка платформы и PostgreSQL подключается с SCRAM-паролем.
Чек-лист и приоритеты: что делать сразу, а на что можно забить
Если у вас сейчас открытый вопрос с доступом, я бы делал в таком порядке. Первое — убрать из файла строки с адресом all и методом trust, если они есть; это единственная по-настоящему срочная позиция. Второе — проверить listen_addresses и фаервол: pg_hba не заменяет ни того, ни другого, и правильная схема — сначала не пустить пакет до порта 5432, а уже потом разбираться с правилами внутри СУБД. Третье — привести файл к структуре «запреты сверху, разрешения снизу, финальный reject». Четвёртое — обновиться до текущего минорного релиза: на сентябрь 2026 года в 18-й ветке это 18.6, и сообщество прямо рекомендует всегда работать на актуальном миноре.
На что можно спокойно забить, чтобы не тратить время. Имена хостов в колонке адреса вместо IP — красиво в теории, на практике каждое подключение стоит двух резолверных запросов, прямого и обратного, и любая заминка DNS превращается в задержку логина. Держите IP и подсети. Экзотика вроде sameuser и samerole нужна на shared-хостингах и почти никогда — во внутреннем контуре небольшой компании вроде хостела на полтора десятка мест. Ручное перечисление пользователей в pg_hba тоже не нужно, если умеете пользоваться группами через плюс.
И про преувеличенные риски. Правка pg_hba.conf на боевой базе — операция обратимая и недорогая: битый файл не роняет сервер, reload не рвёт сессии, а откат — это возврат прежнего файла и повторный reload. Единственная реальная опасность — закрыть доступ самому себе, и она снимается одной строкой local для postgres и открытой заранее второй сессией psql. Я делаю такие правки на работающих кластерах в рабочее время много лет и ни разу не получил простоя из-за самой правки.
Финальная сводка, которую можно повесить рядом с файлом: правило выбирается первое совпавшее; провал аутентификации не даёт второго шанса; изменения на Linux применяются только после SIGHUP; на Windows — сразу для новых подключений; ошибки разбора видны через error в pg_hba_file_rules; открытые сессии живут по старым правилам.
- Убрать `trust` и широкие маски — сразу.
- Закрыть порт фаерволом и сузить listen_addresses — сразу.
- Перестроить порядок: reject наверх, широкие allow вниз, финальный reject в конец.
- Обновиться на текущий минорный релиз ветки (18.6 на сентябрь 2026).
- Включить log_connections и раз в квартал просматривать pg_hba_file_rules на предмет error.
- Не использовать hostname в колонке адреса и не плодить перечисления пользователей.
Частые вопросы
Почему строка reject, дописанная в конец pg_hba.conf, не блокирует подключение?
Потому что PostgreSQL берёт первую строку, совпавшую по типу подключения, адресу клиента, базе и пользователю, и дальше не смотрит. Если выше уже стоит разрешающее правило на подсеть или на all, до вашего reject очередь не доходит. Точность правила и длина маски не имеют значения — значение имеет только позиция строки в файле. Перенесите reject выше разрешения и сделайте reload.
Нужно ли перезапускать PostgreSQL после правки pg_hba.conf?
Нет. На Unix достаточно перечитать конфигурацию: pg_ctl reload, SELECT pg_reload_conf() или kill -HUP по PID постмастера. На Microsoft Windows сигнал не нужен вообще — изменения применяются сразу к последующим новым подключениям. Рестарт кластера ради pg_hba — лишний простой и холодный кэш.
Разорвёт ли reload уже открытые соединения?
Нет, и это главная причина, почему кажется, что правило не работает. pg_hba проверяется только в момент установления соединения; всё, что подключено, живёт по прежним правилам сколько угодно долго. Сначала правьте файл и делайте reload, затем находите сессии в pg_stat_activity по client_addr и рвите их через pg_terminate_backend.
Как проверить, какой файл читает сервер и нет ли в нём ошибок?
Выполните SHOW hba_file — сервер сам назовёт путь, это надёжнее поиска по файловой системе, особенно при нескольких кластерах на машине. Затем посмотрите SELECT rule_number, line_number, address, auth_method, error FROM pg_hba_file_rules. Строки с непустым error не разобрались и в аутентификации не участвуют, а rule_number показывает реальный порядок просмотра.
Я запретил конкретный IP, но человек всё равно подключается. Что ещё проверить?
Проверьте, какой адрес видит сам сервер: SELECT DISTINCT client_addr FROM pg_stat_activity. Если между клиентами и базой стоит PgBouncer или Odyssey, сервер видит адрес пулера и ваши правила по адресу человека бессмысленны. То же самое даёт SSH-туннель — клиент приходит с loopback. И помните про IPv4 против IPv6: запись 127.0.0.1/32 не совпадёт с подключением на ::1.
Действует ли reject на суперпользователя postgres?
Да, полностью. reject отклоняет подключение безусловно, никаких исключений для суперпользователя нет. Именно поэтому первой строкой в файле я всегда держу local-доступ для postgres по методу peer: это страховка от ситуации, когда широкий reject наверху закрывает доступ вам самим, а других открытых сессий не осталось.
Как работает include_dir и почему мои файлы из conf.d не влияют на порядок?
include_dir подставляет содержимое каталога ровно на месте самой директивы. Если она стоит в конце основного pg_hba.conf, все ваши правила окажутся ниже общих разрешений. Внутри каталога подключаются только файлы, не начинающиеся с точки и оканчивающиеся на .conf, а их порядок определяется именем в локали C: цифры перед буквами, заглавные перед строчными. Используйте двузначные префиксы 00, 10, 50, 99.
Чем отличаются ошибки «no pg_hba.conf entry for host» и «pg_hba.conf rejects connection»?
Первая значит, что подключение не совпало ни с одной строкой файла, вторая — что совпала строка с методом reject. Если вы ждали запрета, а видите «no entry», значит, ваш reject не совпал по типу, адресу, базе или пользователю — например, клиент пришёл по IPv6 или через hostssl-правило. Если видите «password authentication failed», строка найдена и не подошёл пароль.
Нужно ли менять md5 на scram-sha-256 в pg_hba.conf для базы 1С?
Желательно, но в правильном порядке. С PostgreSQL 14 новые пароли и так хранятся в SCRAM, а метод md5 в таком случае сам использует SCRAM. Проблема возникает, когда после обновления у роли остался MD5-хеш, а метод уже scram-sha-256: вход ломается. Задайте пароль роли 1С заново, проверьте хеш в pg_authid, убедитесь в подключении на тестовой базе и только потом меняйте метод и делайте reload.
Источники
- PostgreSQL 18 Documentation, 20.1. The pg_hba.conf File — Раздел о формате записей, порядке просмотра, отсутствии fall-through, директивах include/include_if_exists/include_dir, применении изменений по SIGHUP и поведении на Windows. https://www.postgresql.org/docs/18/auth-pg-hba-conf.html
- PostgreSQL 18 Documentation, 53.10. pg_hba_file_rules — Полный список колонок представления: rule_number, file_name, line_number, type, database, user_name, address, netmask, auth_method, options, error; доступ по умолчанию только суперпользователю. https://www.postgresql.org/docs/18/view-pg-hba-file-rules.html
- PostgreSQL 18 Documentation, 20.5. Password Authentication и 19.3.3 password_encryption — Автоматический выбор SCRAM при методе md5, депрекация MD5-паролей, порядок миграции md5→scram-sha-256; дефолт password_encryption = scram-sha-256. https://www.postgresql.org/docs/18/auth-password.html ; https://www.postgresql.org/docs/18/runtime-config-connection.html
- PostgreSQL 14 Release Notes — Пункт о смене значения по умолчанию password_encryption с md5 на scram-sha-256. https://www.postgresql.org/docs/release/14.0/
- PostgreSQL 18 Documentation, 20.15. Authentication Problems — Типовые сообщения: «no pg_hba.conf entry for host», «password authentication failed for user». https://www.postgresql.org/docs/18/client-authentication-problems.html
- PostgreSQL 16 Release Notes — Пункты «Allow include files in pg_hba.conf and pg_ident.conf» (include, include_if_exists, include_dir) и «Add support for regular expression matching on database and role entries in pg_hba.conf» — обе возможности присутствуют и в 18-й ветке. https://www.postgresql.org/docs/release/16.0/
- PostgreSQL 18.0 Release Notes — Добавление метода аутентификации oauth в pg_hba.conf, параметр oauth_validator_libraries и флаг сборки --with-libcurl. https://www.postgresql.org/docs/release/18.0/
- PostgreSQL Versioning Policy — Поддерживаемые ветки и минорные релизы: 18.6 (первый релиз ветки 25.09.2025, окончание поддержки 14.11.2030), рекомендация всегда работать на актуальном миноре. https://www.postgresql.org/support/versioning/
- Percona Community Forum, «Line order v2.2 pg_hba.conf» — Обсуждение: в Percona Operator for PostgreSQL 2.2 пользовательские правила дописываются в конец pg_hba.conf, первые записи оператор держит за собой, поэтому поставить reject выше них нельзя; модератор ссылается на тикет в JIRA. https://forums.percona.com/t/line-order-v2-2-pg-hba-conf/24026
