Port knocking и не только

Прочитав по диагонали статью гражданина @Winseven«ICMP открывашка портов для сервера», я сдержался. Все-таки велосипединг — это весело. Но вчитавшись, я опешил. Зачем запускать отдельное приложение для отслеживания нужных пакетов? Правильно ли, что достаточно один раз попасть пальцем в небо, чтобы порт был открыт? По мне, как-то не по фэншую.

Душа все это не вынесла, и я решился на статью.

4e19983b8f439136ace30ab91634d484.png

Какие инструменты будут использоваться?

  1. Мне привычнее иметь дело с iptables. Он нужен для всего: запрета пакетов, добавления адресов в списки и прочих файрвольных штучек.

  2. Для составления списков используем 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

Что я накостылял?

  1. Запретил все пакеты, у которых состояние INVALID.

  2. Разрешил все установленные соединения (ESTABLISHED).

  3. RELATED добавил для тех парней, которые не сильно знают, что это, а по попе получать не хотят.

  4. Все новые соединения я обрабатываю особо: сначала пропускаю через 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.

Велосипеды, конечно, хорошо, но давайте читать документацию:

© Habrahabr.ru