Прочитав по диагонали статью гражданина @Winseven«ICMP открывашка портов для сервера», я сдержался. Все-таки велосипединг — это весело. Но вчитавшись, я опешил. Зачем запускать отдельное приложение для отслеживания нужных пакетов? Правильно ли, что достаточно один раз попасть пальцем в небо, чтобы порт был открыт? По мне, как-то не по фэншую.
Душа все это не вынесла, и я решился на статью.
Какие инструменты будут использоваться?
Мне привычнее иметь дело с iptables. Он нужен для всего: запрета пакетов, добавления адресов в списки и прочих файрвольных штучек.
Для составления списков используем ipset.
Принцип работы:
Пользователь посылает серию специальных пакетов нужному серверу.
Сервер, получив первый правильный пакет, заносит кандидата в первый список.
Сервер, получив второй правильный пакет, переносит кандидата во второй список, при условии нахождения его в первом.
Несколько итераций с переносом кандидата по спискам.
Сервер, получив последний правильный пакет, переносит кандидата в список разрешенных подключений, при условии нахождения в предпоследнем списке.
Для примера рассмотрю комбинацию из трех пакетов ICMP (Ping) разного размера (999, 1028 и 500 байтов).
Для начала создам все необходимые списки:
sudo ipset create knock_allow hash:net,iface timeout 60
sudo ipset create knock_step_1 hash:ip timeout 2
sudo ipset create knock_step_2 hash:ip timeout 2
Если долго смотреть на команды выше, можно заметить параметр timeout. Согласно документации, этот параметр отвечает за то, сколько времени в секундах запись будет присутствовать в списке. То есть для данного решения у пользователя будет минута на подключение по ssh.
Теперь пишу правила. Файрвол должен быть чистым:
sudo iptables -N INPUT_NEW
sudo iptables -N PORTKNOCKING
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate NEW -j INPUT_NEW
sudo iptables -A INPUT_NEW -j PORTKNOCKING
sudo iptables -A INPUT_NEW -p tcp -m tcp --dport 22 -m set --match-set knock_allow src,src -j ACCEPT
sudo iptables -A PORTKNOCKING -p icmp --icmp-type 8 -m connbytes --connbytes 500:500 --connbytes-mode bytes --connbytes-dir original -m set --match-set knock_step_2 src -j SET --add-set knock_allow src,src --exist
sudo iptables -A PORTKNOCKING -p icmp --icmp-type 8 -m connbytes --connbytes 1028:1028 --connbytes-mode bytes --connbytes-dir original -m set --match-set knock_step_1 src -j SET --add-set knock_step_2 src
sudo iptables -A PORTKNOCKING -p icmp --icmp-type 8 -m connbytes --connbytes 999:999 --connbytes-mode bytes --connbytes-dir original -j SET --add-set knock_step_1 src
Что я накостылял?
Запретил все пакеты, у которых состояние INVALID.
Разрешил все установленные соединения (ESTABLISHED).
RELATED добавил для тех парней, которые не сильно знают, что это, а по попе получать не хотят.
Все новые соединения я обрабатываю особо: сначала пропускаю через Port knocking, а затем все разрешенные пакеты разрешаю.
Почему я рассматриваю списки с начала в конец? Потому что при прохождении правил, таргет SET не прекращает прохождение по списку правил файрвола. Это значит, что послав один пакет, можно (гипотетически) сразу попасть во все списки. В данном примере, конечно, не получится, но все равно лучше смотреть с конца в начало.
Собственно, вся магия!
Как проверить?
Для начала установим политику по умолчанию в DROP (помните, что это может сломать ваааааще все!) Так что делайте с осторожностью и имейте возможность физического доступа к серверу:
sudo iptables -P INPUT DROP
Затем на виндах делаем следущее:
ping -l 971 -w 100 -n 1 mysrv.com; ping -l 1000 -w 100 -n 1 mysrv.com; ping -l 472 -w 100 -n 1 mysrv.com; ssh mysrv.com
На удивление, все работает, даже если порты закрыты =) Но почему размер пакета указан не такой, какой указан в настройке файрвола? Потому что добавляется 28 бит заголовков пакета.
Сочинение выше — базис. На его основе можно делать:
Кнокеры не только по ICMP, но и по TCP, UDP и прочие фантазии.
Кнокеры из сложных рукопожатий, включая промежутки тишины.
Примитивный Fail2Ban. Я рисовал отдельный чайник для тех, кто в списке банов, где в 75% ты попадёшь на TARPIT, в 20% — на DROP и в 5% — на REJECT.
Помимо этого, правила можно улучшить:
Проверять, что перестук не начался сначала, дойдя до середины.
Игнорировать перестуки, если пользователь и так в нужном списке.
Но мне чуть-чуть лень =)
P.S.
Велосипеды, конечно, хорошо, но давайте читать документацию:
Комментарии (23)
up40k
29.06.2022 14:39Это значит, что послав один пакет, можно (гипотетически) сразу попасть во все списки.
Вы сравниваете размер и нахождение в предыдущем сете. Размер реального пакета удовлетворяет только одной из записей в таблице. Сколько бы из них и в каком порядке вы ни тестировали. Какая разница, в каком порядке располагать записи? Каждый пакет будет прописывать адресанта в свой сет.
ZakharovAV Автор
29.06.2022 14:42только если адресант есть в предыдущем. то есть послав пакет размером 1028 раньше 999 в 3 список попасть не получится. Как я и говорил, если бы было 3 записи размером 999, то первый же пакет записал адресанта во все списки. А это - не зер гут
up40k
29.06.2022 14:52только если адресант есть в предыдущем. то есть послав пакет размером 1028 раньше 999 в 3 список попасть не получится.
Ага, так и задумано ведь.
Как я и говорил, если бы было 3 записи размером 999, то первый же пакет записал адресанта во все списки.
Не говорили :) при одинаковом размере да, только обратная проверка сетов работает (как единственное условие сравнения при всегда прямом прохождении ip-таблиц).
ZakharovAV Автор
29.06.2022 14:53А вот и говорил =)))
"Почему я рассматриваю списки с начала в конец? Потому что при прохождении правил, таргет SET не прекращает прохождение по списку правил файрвола. Это значит, что послав один пакет, можно (гипотетически) сразу попасть во все списки. В данном примере, конечно, не получится, но все равно лучше смотреть с конца в начало."up40k
29.06.2022 15:03+1Простите за занудство, но вы привели идеальный пример и не указали крайние случаи :) Гипотетическая область значений подразумевает явное их указание, имхо. Хотя бы для исключения таких вопросов, как мой.
ZakharovAV Автор
29.06.2022 15:05+1Согласен. Впредь буду постараться яснее проецировать мысли на бумагу
Tarakanator
29.06.2022 14:46+1Я так понял это было указано для общего случая, а не конкретного.
К примеру у меня какое-то время fail2ban работал так: при попытке доступа IP попадает в список подозрительных, а если подозрительный IP попадает в fail2ban правило, то уже банится.
И второе правило должно быть до первого.
titbit
29.06.2022 15:41+2Так ведь человеку посередине достаточно подслушать на какие порты отправляются пакеты и повторить атаку. Тут надо хитрее, список портов должен зависеть от чего-то глобального, например от времени. Схема будет сложнее, зато безопаснее.
ZakharovAV Автор
29.06.2022 15:49С нетерпением жду ваш вариант. Мне он уже по нраву =)))
avelor
29.06.2022 17:53+1немного наркомании - брать OTP, и генерить скриптом правила айпитаблей, отсекая например 1 и 5 число в 6-символьном OTP. оно и будет портом для пастука (например) %)
GennPen
29.06.2022 17:31Тут надо хитрее, список портов должен зависеть от чего-то глобального, например от времени. Схема будет сложнее, зато безопаснее.
Можно пойти дальше, если есть IPv6 /64 подсеть, то еще и IPv6 адрес генерировать в зависимости от времени, например раз в минуту менять на новый.
savostin
Неделя port knocking на Хабре! Ждем следующую статью "Я тут почитал предыдущие, ну нельзя же так!" Вот за это и любим наш Хабр ;)
Yaris
Может, это классическая схема с "... захожу под другим именем и пишу заведомо неправильный ответ" :-)
Winseven
Да, так и есть)))
ZakharovAV Автор
Тогда если завтра выйдет пост про реализацию PortKnocking через NFQueue, то это буду либо я, либо наши сеньоры =))
Шутка.
snp
Думаю, простого комментария по этой теме хватит :)
Winseven
Вы что?! Нельзя использовать PortKnocker, используйте VPN, Nginx... и вообще желательно отключить фаервол)))