Возникла необычная творческая задача, после решения которой я подумал, что мой опыт может пригодиться кому-то ещё.
Задача следующая: в сети имеется RDP-сервер (10.0.0.10), к которому подключаются клиенты. Так же на этих клиентах имеется доступ в Интернет. Требуется сделать так, чтобы при подключении к серверу RDP, доступа в Интернет у клиентов уже не было. А при отключении от RDP-сервера, доступ появлялся обратно. То есть работало одновременно только что-то одно.
Я, конечно, не очень люблю вмешиваться в сетевые пакеты. Работает NAT да и ладно, но в некоторых случаях приходится изворачиваться, чтобы пропустить какой-нибудь капризный сервис внутрь/наружу. Пока ещё не сталкивались с неприятными моментами в CryptoPro, а ведь некоторыем компьютерщикам приходится “разруливать” и такие: Проблема Джакарты вещи.
Моё решение не претендует на медаль, конечно, возможно я впоследствии его улучшу (надеюсь), но выполнено это было следующим образом:
- Настройка DNAT на шлюзе Интернета:
/sbin/iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 13389 -j DNAT --to-destination 10.0.0.10:3389
Это правило изменит IP назначения пакета, пришедшего на 13389-ый порт шлюза на соответствующий порт RDP-сервера; - Устанавливаем ipset – модуль для линуксового файрволла netfilter. Я качал пакеты и ставил.
- Прописываем запрещающее правило для внешки:
/sbin/iptables -A FORWARD -i eth0 -m set --match-set blocked dst -j DROP
Это правило будет отбрасывать пакеты, приходящие с внешнего интерфейса на адреса, перечисленные в списке blocked; - Пишем скрипт:
#!/bin/sh
IPFILE='/etc/ban_ip.txt'
IPFILE_OLD='/etc/ban_ip_old.txt'
TIMEOUT=10
while true
do
if [ -e $IPFILE ];
then
mv $IPFILE $IPFILE_OLD
fi
cat /proc/net/nf_conntrack | grep 'dport=13389' | awk '{print $7}' | cut -d'=' -f2 > $IPFILE
for i in `cat $IPFILE`
do
ipset -T blocked $i 0> /dev/null 2> /dev/null 1> /dev/null
if [ $? -ne 0 ];
then
ipset -A blocked $i
fi
done
for i in `diff --left-column --unchanged-group-format='' $IPFILE $IPFILE_OLD`
do
ipset -D blocked $i
done
sleep $TIMEOUT
done - Создаём новый список ip:
# ipset -N blocked iphash
- Запускаем наш скрипт на выполнение.
Что делает скрипт? Получает установленные соединения на порт RDP-сервера (перенаправляемый), выделяет из них IP-адреса. Затем добавляет эти адреса в список блокируемых. Если на следующей итерации адресов стало меньше, то исчезнувшие адреса удаляются из списка блокируемых.
Пока отлаживал работу со сниффером – многое удалось выяснить. Если у вас вдруг какие-то проблемы возникают (у меня часто не получалось), проследите, чтобы последовательность действий была такая:
- Клиент 10.0.0.2 посылает SYN-сегмент на 10.0.0.1:13389 (шлюз и порт перенаправления);
- Шлюз 10.0.0.1 перепосылает этот же SYN-сегмент на 3389 порт сервера;
- Сервер 10.0.0.10 получает SYN-сегмент на порт 3389;
- Сервер 10.0.0.10 отвечает SYN+ACK-сегментом на порт шлюза 10.0.0.1;
- Шлюз пересылает SYN+ACK-сегмент на порт клиента;
У меня проблемы возникали на разных этапах, чаще всего сервер посылал ответ SYN+ACK не на адрес шлюза, а напрямую клиенту, поэтому соединение сбрасывалось (RST) из-за несоответствия SEQ и ACK-последовательностей.
Кстати, данный подход можно использовать не только в моём сценарии, но и в некоторых других. Подобных решений я пока не встречал (может плохо искал, так что забирайте, применяйте, отписывайтесь!)
Скрипт конечно не идеален, планирую его улучшить, добавить логи, убрать мелкие косяки где-то подшлифовать (иногда дублируются записи + задержку уменьшить, да и уйти от файлов, оставив таблицы в памяти), но пока что работает.
Да, создание ipset набора нужно повесить в автозагрузку.
P.S. Некоторая задержка связана с не мгновенным разрывом соединения в connection tracking, таймер можно уменьшить, но пока не знаю, как это отразится на качестве работы сети в целом.
Comments: