Скрипт управления списком IP-сетей в iptables/ipset для PostgreSQL

Содержание

Введение

На работе до сих пор на некоторых серверах удавалось обходиться без настройки фильтрации трафика. Сервисов установлено немного, у каждого есть собственные средства ограничения подключений. В частности, одними из таких серверов были серверы с PostgreSQL, на которых установлены только сервер SSH, Zabbix-агент, PostgreSQL и PgBouncer. Доступ к SSH можно ограничить с помощью файла /etc/hosts.allow, у Zabbix-агента можно указать списки IP-адресов в опции Server в файле конфигурации /etc/zabbix/zabbix_agentd.conf, ограничить доступ к PostgreSQL и PgBouncer можно при помощи файла /etc/postgresql/15/main/pg_hba.conf. Однако по мнению отдела информационной безопасности этого недостаточно и нужно не просто ограничить доступ к сервисам, но и вообще скрыть сам факт существования этих сервисов от посторонних. И это несмотря на то, что серверы имеют серые IP-адреса.

Задача прописывать доступ в файл /etc/postgresql/15/main/pg_hba.conf для пользователей БД при этом никуда не девается, но возникает необходимость синхронно менять ещё и списки IP-адресов в ipset, что чревато ошибками. Чтобы избежать ошибок, я решил вновь прибегнуть к подходу, описанному в статье Скрипты управления списком IP-адресов в iptables/ipset и ipfw/table, переработав скрипт для текущей задачи.

Установка пакетов

Первым делом установим необходимые пакеты:

# apt-get install netfilter-persistent iptables-persistent ipset-persistent iptables ipset gawk aggregate

Настройка скрипта

Создадим скрипт /usr/local/bin/ipset_auto.sh со следующим содержимым:

#!/bin/sh

GAWK="/usr/bin/gawk"
SORT="/usr/bin/sort"
UNIQ="/usr/bin/uniq"
IPSET="/sbin/ipset"
XARGS="/usr/bin/xargs"
AGG="/usr/bin/aggregate"

update()
{
  SET="$1"
  NEED_IPS="$2"

  CURRENT_IPS=`$IPSET list $SET | $GAWK '/^([0-9]{1,3}\.){3}[0-9]{1,3}(|\/[0-9]{1,2})$/ { if ($0 ~ "/") { print $0; } else { print $0 "/32"; } }'`

  DIFF_IPS=`(echo "$NEED_IPS" ; echo -n "$CURRENT_IPS") | $SORT | $UNIQ -u`
  ADD_IPS=`(echo "$NEED_IPS" ; echo -n "$DIFF_IPS") | $SORT | $UNIQ -d`
  DEL_IPS=`(echo "$CURRENT_IPS" ; echo -n "$DIFF_IPS") | $SORT | $UNIQ -d`

  if [ -n "$ADD_IPS" ]
  then
    echo "--- $SET add ---"
    echo "$ADD_IPS"
    echo "$ADD_IPS" | $XARGS -n1 $IPSET add $SET
  fi

  if [ -n "$DEL_IPS" ]
  then
    echo "--- $SET del ---"
    echo "$DEL_IPS"
    echo "$DEL_IPS" | $XARGS -n1 $IPSET del $SET
  fi
}

NEED_NETS=`$GAWK '/^host\s*/ && $4 != "::1/128" && $4 != "127.0.0.1/32" { print $4; }' /etc/postgresql/15/main/pg_hba.conf | $AGG -q`

if [ -z "$NEED_NETS" ]
then
  echo "Failed to prepare needed networks"
  exit
fi

update "postgres_auto" "$NEED_NETS"

Добавим скрипту права на выполнение:

# chmod +x /usr/local/bin/ipset_auto.sh

Как видно из скрипта, он обновляет в ipset список сетей под названием postgres_auto.

Для создания списка сетей postgres_auto в ipset можно воспользоваться командой:

# ipset create postgres_auto hash:net

Выполним скрипт вручную, чтобы наполнить список сетями:

# /usr/local/bin/ipset_auto.sh

Проверить содержимое списка можно с помощью такой команды:

# ipset list postgres_auto

Чтобы список обновлялся автоматически раз в минуту, запустим команду редактирования задач планировщика:

# crontab -e

И добавим в него строчку:

*   *   *   *   *   /usr/local/bin/ipset_auto.sh

Настройка iptables

Для создания правила в iptables, которое разрешит всем сетям из множества postgre_auto взаимодействовать с сервером PostgreSQL и его прокси PgBouncer, можно воспользоваться командой:

# iptables -A INPUT -p tcp -m set --match-set postgres_auto src -m multiport --dports 5432,6432 -j ACCEPT

В моём случае iptables ещё не был настроен и я поместил в файл /etc/iptables/rules.v4 примерно такие правила (реальный список правил не привожу для упрощения и по соображениям безопасности):

*filter
:INPUT DROP [99531963:5114253625]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [41237204127:25059350107297]
-A INPUT -s 127.0.0.1/32 -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -m icmp ! --icmp-type 5 -j ACCEPT
-A INPUT -s 88.88.88.88/32 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -s 99.99.99.99/32 -p tcp -m tcp --dport 10050 -j ACCEPT
-A INPUT -p tcp -m set --match-set postgres_auto src -m multiport --dports 5432,6432 -j ACCEPT
COMMIT

В приведённом выше примере IP-адрес 88.88.88.88 - это адрес, откуда к серверу разрешено подключаться по SSH, а IP-адрес 99.99.99.99 - адрес сервера Zabbix.

Для применения правил можно воспользоваться командой:

# netfilter-persistent reload