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

Содержание

Введение

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

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

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

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

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

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

Создадим скрипт /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"
MYSQL="/usr/bin/mysql"

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

  CURRENT_IPS=`$IPSET list $SET | $GAWK '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ { print $0; }'`

  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_IPS=`$MYSQL -N mysql <<END 2>/dev/null
SELECT host
FROM user
WHERE host LIKE '%.%.%.%'
GROUP BY host;
END
`
ERROR=$?
if [ $ERROR -ne 0 ]
then
  echo "Failed to execute SQL-query"
  exit
fi

update "mysql_auto" "$NEED_IPS"

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

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

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

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

# ipset create mysql_auto hash:ip

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

# /usr/local/bin/ipset_auto.sh

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

# ipset list mysql_auto

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

# crontab -e

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

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

Настройка iptables

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

# iptables -A INPUT -p tcp -m set --match-set mysql_auto src -m multiport --dports 3306 -j ACCEPT

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

*filter
:INPUT DROP [372:15672]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [21185:2417175]
-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 -p tcp -m set --match-set ssh src -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 nodes src -m multiport --dports 3306,4444,4567,4568 -j ACCEPT
-A INPUT -p tcp -m set --match-set mysql_auto src -m tcp --dport 3306 -j ACCEPT
-A INPUT -d 224.0.0.18/32 -p ah -m set --match-set nodes src -j ACCEPT
-A INPUT -d 224.0.0.18/32 -p vrrp -m set --match-set nodes src -j ACCEPT
COMMIT

В приведённом выше примере IP-адрес 99.99.99.99 - адрес сервера Zabbix. IP-адреса, имеющие доступ по SSH, нужно поместить в список ssh в ipset. Сервер в приведённом примере является частью кластера Galera, IP-адреса узлов, входящих в кластер, нужно поместить в список nodes в ipset.

Сохраним списки адресов в ipset в файл /etc/iptables/ipsets с помощью команды:

# ipset save > /etc/iptables/ipsets

Теперь для применения правил из файла /etc/iptables/rules.v4 можно воспользоваться командой:

# netfilter-persistent reload