☰ Оглавление

Рort knocking

Условия

При реализации стучалки я отталкивался от таких условий:

Итого, какие я избрал правила стука:

Таким образом, клиент должен выполнить N-1 попытку подключения, на которые он гарантированно получит REJECT. Большинство ботов сдаются после одной-трёх попыток. Потом клиенту предоставляется только одна попытка подключиться. Если он не угадал пароль, то новая серия попыток будет доступна только по истечении некоторого времени.

Реализация port knocking средствами iptables

Вызываем port-knocking цепочку в подходящем месте как-то так.

iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j KNOCK

Не забываем, что в цепочку для стука должны попадать только SYN-пакеты.

Правила цепочки очень просты:

# создаём или обновляем запись в /proc/net/xt_recent/KNOCK_SSH
-A KNOCK -m recent --set --name KNOCK_SSH --rsource
# если за последние 60 секунд было более 10 попыток подключиться, то не пропускаем попытку
-A KNOCK -m recent --rcheck --seconds 60 --hitcount 11 --name KNOCK_SSH --rsource -j RETURN
# если за последние 60 секунд это попытка 10-я пропускаем подключение
-A KNOCK -m recent --rcheck --seconds 60 --hitcount 10 --name KNOCK_SSH --rsource -j ACCEPT
# в противном случае возвращаемся из цепочки

Внимание! Я предполагаю, что основная цепочка правил закрытая. То есть возврат из цепочки равносилен запрету входа.

Ну и не забудьте:

iptables -N KNOCK

А то будете иметь

iptables: No chain/target/match by that name.

Итого. Что мы получили

Первые 10 попыток подключиться будут безуспешными. Это отвадит большинство сканеров.

Если в порт стучат реже 10 раз в минуту, то у сканера не будет возможности подключиться.

Если в порт стучат чаще 10 раз в минуту, то у сканера будет только одна попытка. Для подбора пароля этого недостаточно.

Теоретически, сканер может стучаться ровно 10 раз в минуту, тогда он сможет выполнять проверку пароля раз в 6 секунд. Я считаю, что этого тоже недостаточно для успешной атаки.

Если ваша паранойя зашла дальше моей, то вы можете поменять цифры или добавить таблицы (аналогичные KNOCK_SSH) и цепочки (аналогичные KNOCK). Таким образом можно собрать сколь угодно сложную (в разумных пределах) машину состояний. Правда, при этом уже придётся помнить правильный «стук». Я считаю это слишком обременительным.

И ещё одно замечание: не ставьте hitcount больше 20, подробнее смотрите man iptables.

Полый пример iptables с порт-кнокингом

# фаервол закрыт
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# дополнительные цепочки для удобства
:KNOCK - [0:0]
:TCP - [0:0]
:TRUSTED - [0:0]
# пропускаем всё для установленных соединений
# (далее будет только обработка новых соединений)
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# пропускаем всё, что относится к локальному хосту
-A INPUT -i lo -j ACCEPT
# игнорируем весь сломанный трафик
-A INPUT -m state --state INVALID -j DROP
# пропускаем всё с доверенных IP
-A INPUT -j TRUSTED
# обрабатываем разные виды трафика (есть только TCP, но можно завести для чего угодно)
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m state --state NEW -j TCP
-A INPUT -p icmp -j ACCEPT
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable
# цепочка для обработки tcp
# 22 порт отправляем на анализ стука
-A TCP -p tcp -m tcp --dport 22 -j KNOCK
# торенты пропускаем
-A TCP -p tcp -m tcp --dport 6881:6889 -j ACCEPT
# цепочки для анализа стука
-A KNOCK -m recent --set --name KNOCK_SSH --rsource
-A KNOCK -m recent --rcheck --seconds 60 --hitcount 11 --name KNOCK_SSH --rsource -j RETURN
-A KNOCK -m recent --rcheck --seconds 60 --hitcount 10 --name KNOCK_SSH --rsource -j ACCEPT
# доверенные IP-адреса, пропускаемые без фильтрации
-A TRUSTED -s 194.67.3.229/32 -j ACCEPT

Отладка

Посмотреть только инициализации соединений:

tcpdump 'tcp[tcpflags] & tcp-syn != 0'

Посмотреть запомненные IP можно здесь /proc/net/xt_recent/KNOCK_SSH.

Мониторинг

Вы всегда можете добавить протоколирование в любое место. В этом нет ничего сложного. Но дополнительным удобным местом для мониторинга является файл /proc/net/xt_recent/KNOCK_SSH.

Единственное, что надо помнить, что время там указывается в jiffies.

jiffies — это uptime, выраженный в CONFIG_HZ. CONFIG_HZ задаётся при сборке ядра и в большинстве случаев равна 1000. То есть один тик jiffies равен 1/1000 секунды.

Некоторые системы собраны с CONFIG_HZ=100.

Для просмотра информации из xt_recent можно использовать какой-нибудь такой скрипт:

perl -e '
@h = sort {$b->[0] <=> $a->[0]}
map {m-src=(\S+).*last_seen:\s(\S+)-; [$2, $1]} <>;
$m = $h[0][0];
print join("\n",
map {sprintf("%6.2f %s", @$_)}
grep {$_->[0] < 30}
map {[($m - $_->[0])/1000/60/60, $_->[1]]} @h) . "\n";
' /proc/net/xt_recent/KNOCK_SSH

Он читает IP-адреса и даты последних обращений, пересчитывает даты в часы, сортирует от настоящего в прошлое, оставляет только последние 30 часов, форматирует и выводит. Отредактируйте по вкусу.