Так уж сложилось что в бесплатной версии PostgreSQL нет возможности блокировать пользователя после достижения определенного количества неудачных попыток аутентификации. Поэтому приходится использовать что-то другое для решения этой задачи.

Fail2ban

При использовании Fail2ban банить именно пользователя в PostgreSQL не получится, если только не использовать скрипты PostgreSQL для блокировки пользователя. Но тут встаёт вопрос с подключением к бд, и хранению пароля.

В моём же случае было достаточно просто блокировать подключения к PostgreSQL с IP-адреса, от которого были множественные неудачные подключения.

fail2ban postgresql block

Создаём jail

Jail включает в себя набор правил и фильтров, по которым и происходим блокировка.

Для создания выполняем следующее:

sudo vim /etc/fail2ban/jail.d/postgres.conf
[postgres-iplock]

enabled = true
#filter name
filter = postgres-iplock
#filter action
action = iptables-postgres
#path to logs
logpath = /var/lib/pgsql/12/data/log/postgresql-*.log
maxretry = 3
bantime = 380
usedns = raw

Создаём фильтр

Используя фильтр Fail2ban будет искать вхождение в лог файлах, путь к которым указан в переменной logpath.

sudo vim /etc/fail2ban/filter.d/postgres-iplock.conf
[Definition]
failregex = ^.*@<F-ID/> FATAL:  password authentication failed .*

Внимание ваш pattern для фильтра может отличатся, так как в PostgreSQL можно изменить стиль вывода логов, используя переменную log_line_prefix, в файле postgresql.conf. В моём случае значение переменной '%m [%p] %q%u@%d@%h '.

Создаём правило

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

Я же выбрал стандартный iptables только подредактировал под порт 5432.

sudo cp /etc/fail2ban/action.d/iptables.conf /etc/fail2ban/action.d/iptables-postgres.conf
sudo vim /etc/fail2ban/action.d/iptables-postgres.conf
[INCLUDES]

before = iptables-common.conf

[Definition]

actionstart = <iptables> -N f2b-<name>
              <iptables> -A f2b-<name> -j <returntype>
              <iptables> -I <chain> -p tcp --dport 5432 -j f2b-<name>

actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'

actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>

actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>

[Init]

Тестируем

Теперь, когда всё готово перезапускаем сервис fail2ban и смотрим логи на предмет срабатывания. Если сервис после перезапуска не стартовал возможно придётся немного поработать с selinux.

sudo systemctl restart fail2ban

Пытаемся неудачно залогинится на сервер PostgreSQL как минимум 3 раза. Если всё сделано правильно, то после 3 раза у нас просто будет недоступен порт 5432.

psql -U admin -d postgres

В файле логов находим следующие записи:

2022-10-07 16:41:31,773 fail2ban.filter         [1109]: INFO    [postgres-lockuser] Found 192.168.0.11 - 2022-10-07 16:41:31
2022-10-07 16:41:34,613 fail2ban.filter         [1109]: INFO    [postgres-lockuser] Found 192.168.0.11 - 2022-10-07 16:41:34
2022-10-07 16:41:37,023 fail2ban.filter         [1109]: INFO    [postgres-lockuser] Found 192.168.0.11 - 2022-10-07 16:41:36
2022-10-07 16:41:37,073 fail2ban.actions        [1109]: NOTICE  [postgres-lockuser] Ban 192.168.0.11
2022-10-07 16:47:56,173 fail2ban.actions        [1109]: NOTICE  [postgres-lockuser] Unban 192.168.0.11

Время, через которое IP-адрес будет разбанен задаётся в переменной bantime.

Итог

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

В общем решение всегда за вами подходит вам такой метод или нет.

Еще один минус в том, что fail2ban не умеет работать с ротацией логов. Т.е. если активный файл с логами создастся после запуска сервиса fail2ban он не поймёт, что логи в новом файле. Во избежание такой ситуации все активные логи надо писать в один файл и к нему применять какой-нибудь logrotate.