IP-наборы пересмотрены
Уже есть ответ, упоминающий наборы IP. Тем не менее, он является скорее одномерным, поскольку в нем основное внимание уделяется повышению производительности по сравнению с классическими правилами, а также тому факту, что наборы IP-адресов смягчают проблему, возникающую при использовании большого количества отдельных IP-адресов, которые нельзя легко выразить как подсеть в нотации CIDR.
Обозначения, используемые ниже
Ибо ipset
я буду использовать обозначения, прочитанные ipset restore
и написанные ipset save
.
Соответственно для iptables
(и ip6tables
) правил я буду использовать обозначения, которые прочитаны iptables-restore
и написаны iptables-save
. Это сокращает нотацию и позволяет выделить потенциальные правила -4
только для IPv4 (с префиксом ) или только для IPv6 (с префиксом -6
).
В некоторых примерах мы перенаправим поток пакетов в другую цепочку. Предполагается, что цепочка существует в этой точке, поэтому строки для создания цепочек не создаются (не упоминается ни имя таблицы, ни команды COMMIT
в конце).
Расширенные наборы IP
Наборы IP могут сделать намного больше, чем было упомянуто в другом ответе, и вы обязательно должны прочитать документацию ipset(8)
по IP-наборам ( ) вместе с iptables-extensions(8)
этой краткой записью здесь.
Например , я буду в основном сосредоточены на три типа набора: hash:ip
, hash:net
и list:set
, но есть больше , чем те , и все они имеют действительные случаи использования.
Например, вы можете также сопоставлять номера портов, а не только IP-адреса .
Сохранение и восстановление наборов IP , как с iptables-save
иiptables-restore
Вы можете создавать декларации IP-наборов и импортировать их по трубопроводу ipset restore
. Если вы хотите сделать вашу команду более устойчивой к уже существующим записям, используйте ipset -exist restore
.
Если ваши правила находятся в файле, который default.set
вы будете использовать:
ipset -exist restore < default.set
Подобный файл может содержать записи в create
наборы и add
записи в них. Но, как правило, большинство команд из командной строки имеют соответствующую версию в файлах. Пример (создание набора DNS-серверов):
create dns4 hash:ip family inet
create dns6 hash:ip family inet6
# Google DNS servers
add dns4 8.8.8.8
add dns4 8.8.4.4
add dns6 2001:4860:4860::8888
add dns6 2001:4860:4860::8844
Здесь один набор создан для IPv4 ( dns4
) и один для IPv6 ( dns6
).
Тайм-ауты на наборах IP
Таймауты в наборах IP могут быть установлены по умолчанию для каждого набора, а также для каждой записи. Это очень полезно для сценариев, в которых вы хотите временно заблокировать кого-либо (например, для сканирования портов или попытки перебора вашего SSH-сервера).
Это работает следующим образом (по умолчанию при создании наборов IP):
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
Мы вернемся к этим конкретным наборам ниже и обоснованию того, почему они установлены так, как они есть.
Если вы хотите установить тайм-аут для определенного IP-адреса, вы можете просто сказать:
add ssh_dynblock4 1.2.3.4 timeout 7200
Блокировать IP 1.2.3.4 на два часа вместо (установить) полчаса по умолчанию.
Если вы посмотрите на это ipset save ssh_dynblock4
через некоторое время, вы увидите что-то вроде:
create ssh_dynblock4 hash:ip family inet hashsize 1024 maxelem 65536 timeout 1800
add ssh_dynblock4 1.2.3.4 timeout 6954
Тайм-аут предостережения
- таймауты - это особенность любого набора. Если набор не был создан с поддержкой тайм-аута, вы получите ошибку (например
Kernel error received: Unknown error -1
).
- Тайм-ауты даны в секундах. Например, используйте арифметические выражения Bash, чтобы получать от минут до секунд. Например:
sudo ipset add ssh_dynblock4 1.2.3.4 timeout $((120*60))
Проверка, существует ли запись в данном наборе IP
Внутри ваших скриптов может быть полезно посмотреть, существует ли уже запись. Это может быть достигнуто с помощью ipset test
которого возвращает ноль, если запись существует, и ненулевое в противном случае. Таким образом, обычные проверки могут быть применены в сценарии:
if ipset test dns4 8.8.8.8; then
echo "Google DNS is in the set"
fi
Тем не менее, во многих случаях вы предпочитаете использовать -exist
переключатель ipset
, чтобы он не жаловался на существующие записи.
Заполнение наборов IP из iptables
правил
Это, на мой взгляд, одна из самых страшных особенностей IP-комплектов. Вы можете не только сопоставлять записи набора IP, но также добавлять новые записи в существующий набор IP.
Например, в этом ответе на этот вопрос у вас есть:
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --update --seconds 15 -j DROP
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --set -j ACCEPT
... с целью ограничения скорости попыток подключения к SSH (TCP-порт 22). Используемый модуль recent
отслеживает последние попытки подключения. Однако вместо state
модуля я предпочитаю conntrack
модуль.
# Say on your input chain of the filter table you have
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
# Then inside the SSH chain you can
# 1. create an entry in the recent list on new connections
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
# 2. check whether 3 connection attempts were made within 2 minutes
# and if so add or update an entry in the ssh_dynblock4 IP set
-4 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock4 src --exist
-6 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock6 src --exist
# 3. last but not least reject the packets if the source IP is in our
# IP set
-4 -A SSH -m set --match-set ssh_dynblock4 src -j REJECT
-6 -A SSH -m set --match-set ssh_dynblock6 src -j REJECT
В этом случае я перенаправляю поток в SSH
цепочку так, что мне не нужно повторяться -p tcp --dport ssh
для каждого отдельного правила.
Чтобы повторить:
-m set
делает в iptables
курсе , что мы используем переключатели из set
модуля (который обрабатывает IP - наборы)
--match-set ssh_dynblock4 src
говорит, iptables
чтобы сопоставить адрес источника ( src
) с именованным набором ( ssh_dynblock4
)
- это соответствует
sudo ipset test ssh_dynblock4 $IP
(где $IP
содержит IP-адрес источника для пакета)
-j SET --add-set ssh_dynblock4 src --exist
добавляет или обновляет адрес источника ( src
) из пакета в набор IP ssh_dynblock4
. Если запись существует ( --exist
), она будет просто обновлена.
- это соответствует
sudo ipset -exist add ssh_dynblock4 $IP
(где $IP
содержит IP-адрес источника для пакета)
Если вы хотите сопоставить адрес назначения / цели вместо этого, вы бы использовали dst
вместо src
. Консультируйтесь с руководством для большего количества вариантов.
Наборы наборов
Наборы IP могут содержать другие наборы. Теперь, если вы проследовали до этой статьи, вы бы удивились, можно ли комбинировать наборы. И конечно это так. Для наборов IP сверху мы можем создать два совместных набора ssh_dynblock
и ssh_loggedon
соответственно содержать наборы только для IPv4 и IPv6:
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
# Sets of sets
create ssh_loggedon list:set
create ssh_dynblock list:set
# Populate the sets of sets
add ssh_loggedon ssh_loggedon4
add ssh_loggedon ssh_loggedon6
add ssh_dynblock ssh_dynblock4
add ssh_dynblock ssh_dynblock6
И следующий вопрос, который должен всплыть у вас в голове, заключается в том, позволяет ли это сопоставлять наборы IP и манипулировать ими в зависимости от версии IP.
И ответ на это звучит громко: ДА! (увы, это не было задокументировано явно в прошлый раз, когда я проверял)
Следовательно, правила из предыдущего раздела можно переписать так:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
что намного лаконичнее. И да, это проверено и работает, как шарм.
Собираем все вместе: SSH перебор
На моих серверах у меня есть сценарий, выполняемый как cron
задание, которое принимает несколько имен хостов и разрешает их в IP-адреса, а затем передает их в набор IP для «доверенных хостов». Идея состоит в том, что доверенные хосты получают больше попыток войти на сервер и не обязательно блокируются так долго, как кто-либо еще.
Наоборот, у меня есть целые страны, заблокированные от подключения к моему SSH-серверу, с (потенциальным) исключением доверенных хостов (т. Е. Порядок правил имеет значение).
Тем не менее, это оставлено в качестве упражнения для читателя. Здесь я хотел бы добавить изящное решение, которое будет использовать наборы, содержащиеся в ssh_loggedon
наборе, чтобы позволить последующим попыткам соединения проходить и не подвергаться такой же проверке, как другие пакеты.
Важно помнить время ожидания по умолчанию: 90 минут ssh_loggedon
и 30 минут для ssh_dynblock
следующих iptables
правил:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m set --match-set ssh_loggedon src -j ACCEPT
-A SSH -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
Теперь вы должны спросить себя, как подключающийся IP-адрес попадает в ssh_loggedon
подмножества. Так что читайте дальше ...
Бонус: добавление IP-адреса, с которым вы входите при входе по SSH
Если вы экспериментировали с sshrc
друзьями и знакомыми, вы узнали о его недостатках. Но PAM приходит на помощь. Модуль с именем pam_exec.so
позволяет нам вызывать скрипт во время входа в SSH в точке, где мы знаем, что пользователь допущен в.
В /etc/pam.d/sshd
ниже pam_env
и pam_selinux
записи добавьте следующую строку:
session optional pam_exec.so stdout /path/to/your/script
и убедитесь, что ваша версия скрипта ( /path/to/your/script
выше) существует и является исполняемой.
PAM использует переменные среды для передачи информации о происходящем, поэтому вы можете использовать простой скрипт, подобный следующему:
#!/bin/bash
# When called via pam_exec.so ...
SETNAME=ssh_loggedon
if [[ "$PAM_TYPE" == "open_session" ]] && [[ -n "$PAM_RHOST" ]]; then
[[ "x$PAM_RHOST" != "x${PAM_RHOST//:/}" ]] && SETNAME="${SETNAME}6" || SETNAME="${SETNAME}4"
ipset -exist add $SETNAME "$PAM_RHOST"
fi
К сожалению, ipset
утилита, похоже, не имеет встроенных умений netfilter. Таким образом, мы должны различать IPv4 и IPv6, установленные при добавлении нашей записи. В противном случае ipset
предположим, что мы хотим добавить другой набор к набору наборов вместо IP. И, конечно, вряд ли найдется набор, названный в честь IP :)
Поэтому мы проверяем :
IP-адрес и добавляем 6
имя набора в таком случае и в 4
противном случае.
Конец.