Port knocking и не только
Прочитав по диагонали статью гражданина @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.
Велосипеды, конечно, хорошо, но давайте читать документацию: