
Использую Linux Debian Bookworm.
Проблема
Я хочу заблокировать все входящие соединения с моим сервером из определенных стран.
Редактировать
Как кто-то указал в комментариях, мне действительно не следует делать это с iptables, поскольку это остановит мой сервер, как только список заблокированных IP-адресов вырастет до неуправляемого размера (например, тысячи IP-адресов). Вместо этого мне следует использовать ipset
(для блокировки целых диапазонов). Проблема в том, что это зависит от списков, которые мне нужно загружать на регулярной основе. Я хочу что-то, что я устанавливаю один раз (предпочтительно).
Как же мне остановить подключения из определенных стран?Трафик (а иногда и атаки методом подбора) из Китая и России выходит из-под контроля. Они регулярно ставят мой сервер на колени. Они не придерживаются файла robots.txt и очень агрессивно сканируют сайт (сотнями, иногда тысячами подключений одновременно). Мне действительно нужно это прекратить. По моим подсчетам, более 90% всего трафика, который обрабатывает мой сервер, вредоносный/неправильно сформированный.
Редактировать 2
Я нашел несколько онлайн-уроков (как этот) как это сделать, но, похоже, эти руководства довольно старые. Кроме того, для этого требуется загрузка и компиляция/установка некоторых модулей. Я беспокоюсь, что это может сломаться в будущем (при обновлении/модернизации других пакетов или даже дистрибутива) или может стать угрозой безопасности в будущем. Я поддерживаю свой сервер в актуальном состоянии (регулярно запуская apt-get update / apt-get upgrade / apt-get dist-upgrade).
Исходное сообщение
Я нашел в сети скрипт, который (в двух словах) получает список IP-адресов установленных соединений каждую секунду, затем использует geoiplookup для получения страны IP-адреса. Если страна есть в списке стран, которые вы хотели бы заблокировать, он добавляет IP-адрес в фильтр вашего брандмауэра с помощью iptables. Я внес несколько небольших изменений в скрипт (сделав его более читабельным и потому что мне не было интересно, чтобы этот скрипт отправлял мне письмо каждый раз, когда он блокирует IP-адрес). Затем этот скрипт запускается каждую минуту через crontab:
#!/bin/bash
# Script found on lautenbacher.io"
end=$((SECONDS+55))
while [ $SECONDS -lt $end ]; do
echo $SECONDS
netstat -ant | egrep ':.*ESTABLISHED' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c > testcc.txt
sed 's/^[ t]*//' -i testcc.txt
sed '/^$/d' -i testcc.txt
while read c d; do
if [[ $c > "0" ]]; then
bGF1dGVuYmFjaGVyLmlv=$(geoiplookup $d | awk -v ip="$d" '{FS=" "} {if($4 == "RU," || $4 == "CN," || $4 == "BLR,") {print 1}}')
if [[ $bGF1dGVuYmFjaGVyLmlv = "1" ]]; then
echo "running geolookup"
echo checking ip $d
geoiplookup $d
isCloudflare=0
# The original script made an exception for CLOUDFLARE. I don't want that exception
# bGF1dGVuYmFjaGVyLmlX=$(whois $d)
# if [[ "$bGF1dGVuYmFjaGVyLmlX" == *"CLOUDFLARE "* ]]; then
# isCloudflare=1
# echo Cloudflare detected
# fi
if [[ "$isCloudflare" != 1 ]]; then
echo try to add ip $d to blocklist
sudo iptables -I INPUT -s $d -j DROP
### I would like to sever the connection here ###
fi
fi
fi
done < testcc.txt
sleep 1
:
done
Кстати, я понятия не имею, почему автор скрипта использует такие странные имена переменных («bGF1dGVuYmFjaGVyLmlv»).
В любом случае, проблема в том, что всякий раз, когда он находит соединение с IP-адресом из запрещенной страны, он запускается
sudo iptables -I INPUT -s $d -j DROP
на ip-адресе. Это все нормально, но большинство этих соединений довольно устойчивы, то есть через секунду тот же ip-адрес находится и снова добавляется с помощью iptables. Я не уверен, что происходит (создает ли это дублирующие правила?), но я хотел бы немедленно разорвать соединение после того, как ip-адрес был добавлен с помощью iptables.
Итак, вкратце: как я могу завершить все соединения с IP-адресом?
решение1
Уже несколько лет ядро Linux имеет API для принудительного уничтожения существующих IP-сокетов независимо от их состояния. Это можно сделать с помощьюss
Команда и синтаксис ее фильтра:
-K
,--kill
Пытается принудительно закрыть сокеты. Эта опция отображает сокеты, которые успешно закрыты, и молча пропускает сокеты, которые ядро не поддерживает закрытие. Поддерживает только сокеты IPv4 и IPv6.
Вы можете заменить:
### I would like to sever the connection here ###
с:
ss --kill -n dst = "$d"
Theфильтр( dst = ...
) необходимо, поскольку команда уничтожит любой отображаемый сокет. Без фильтра это было бы более чем запрошено. Локальный процесс может получить в противном случае редкую ошибку на своем сокете, например ECONNABORTED
. Удаленный узел, вероятно, будет уведомлен с помощью TCP RST, если только правило OUTPUT также (временно) не будет добавлено для предотвращения этого.
См. также этот вопрос/ответ (где я дал ответ):Отключите ВСЕ TCP-соединения (УСТАНОВЛЕННЫЕ,СВЯЗАННЫЕ) в Ubuntu, где OP пытается защитить от атак с помощьюipset(а также имеет проблемы с устаревшими соединениями и межсетевым экраном с отслеживанием состояния).