Хакер - Пингвинья тропа. Простые рецепты для выборочного шифрования трафика в Linux
Источник: t.me/Bureau121
Содержание статьи
- Немного теории
- Что есть что
- Коротко о двух вариантах
- Реализация
- Вариант 1: клиент VPN на машине пользователя
- Вариант 2: клиент VPN на маршрутизаторе
- IPSet
- Автоматизация после сбоев/перезагрузок
- Заключение
Сегодня я расскажу о методике обхода блокировок в Linux с маскировкой трафика и покажу несколько скриптов для автоматизации этого. Их можно без труда перенести на Raspberry Pi, чтобы сделать умный маршрутизатор.
Linux предоставляет огромный набор функций для маршрутизации и инструментов ее конфигурирования. Опытные сисадмины знают об этом и используют арсенал Linux на полную катушку. Но и многие даже продвинутые пользователи не догадываются, сколько удобства могут принести все эти замечательные возможности. Сегодня мы создадим таблицы маршрутизации и опишем правила прохода по ним, а также автоматизируем администрирование этих таблиц. Итак, наши творческие планы:
- определимся с тем, что нам требуется: установим необходимые пакеты и разберемся, зачем они нужны;
- изучим общий принцип работы связки;
- настроим защищенный канал VPN с использованием OpenVPN + stunnel;
- составим списки адресов и опишем области их применения;
- создадим скрипт для быстрого добавления домена или IP-адреса в списки IPSet с добавлением в таблицу маршрутизации и включением в правила перенаправления;
- используем SSH для предоставления безопасного канала связи в эти ваши интернеты.
Немного теории
Что нам понадобится, чтобы все работало, и желательно — комфортно? Само собой, iptables, куда же без него. Еще iproute2, он и позволит нам насоздавать кучу таблиц. IPSet потребуется для того, чтобы не городить огород из множества правил iptables.
Что есть что
- iptables — утилита командной строки. Базовое средство управления работой файрвола для ядер Linux.
- iproute2 — набор утилит для управления параметрами сетевых устройств в ядре Linux.
- IPSet — инструмент для работы со списками IP-адресов и сетевых портов в сетевом фильтре. Формирует список в специальном формате для передачи файрволу.
- stunnel — инструмент организации шифрованных соединений для клиентов или серверов, которые не поддерживают TLS или SSL. Stunnel перехватывает незашифрованные данные, которые должны были отправиться в сеть, и шифрует их. Программа работает как в Unix-системах, так и в Windows. В качестве шифрования использует OpenSSL для реализации базового протокола TLS и SSL.
- OpenVPN — VPN-сервер с поддержкой шифрования библиотекой OpenSSL. Клиентские части доступны практически на всех платформах. Умеет работать через прокси типа Socks, HTTP, через NAT и сетевые фильтры.
Про все эти утилиты можно отыскать много информации в интернете, причем с примерами настроек в самых разных вариантах. Мы станем использовать iptables для маркировки пакетов. У нас будут два варианта настройки. Первый — когда машина, на которой выполняется обход, сама подключена к VPN. Второй вариант — когда в сети находится узел (виртуалка, Raspberry Pi или любой другой хост с Linux), играющий роль маршрутизатора. Далее мы разберем эти варианты чуть подробнее.
Коротко о двух вариантах
Клиент и сервер будут устанавливать зашифрованный канал связи по 443-му порту (stunnel) и передавать внутри OpenVPN по 995-му порту. Снаружи это должно выглядеть как обычный HTTPS.
И вот тут можно реализовать две схемы подключения.
Вариант 1. На клиентской машине iptables будет маркировать пакеты неким флагом, если адреса в пакетах и списках IPSet совпадут, и передавать их для маршрутизации.
Далее помеченные флагом пакеты будут отправлены по заранее созданной таблице маршрутизации, остальные пойдут по маршруту, заданному по умолчанию. С помощью консоли или небольшого скрипта мы поправим этот список адресов. В зависимости от ситуации списков может быть несколько.
Вариант 2. В качестве клиента выступит хост в локальной сети (виртуалка, Raspberry Pi или какое-нибудь другое устройство). Он будет указан в качестве основного шлюза на компьютерах, с которых нужен доступ к ресурсам через VPN. Получив запрос для IP-адреса из списка, шлюз включит NAT и отправит такой трафик в VPN. Остальной трафик будет маршрутизироваться до шлюза по умолчанию без NAT.
Для Linux-систем мы можем оставить шлюз по умолчанию и установить у себя IPSet и iproute2, а потом настроить их аналогично настройкам «промежуточного» хоста-маршрутизатора. В этом случае на уровне клиента будет отбираться трафик по тому же списку IPSet. То есть то, что в списке, будет отправляться на промежуточный хост-маршрутизатор и далее в VPN. Остальное будет маршрутизироваться по умолчанию.
Реализация
Предположим, что где-то далеко в облаке у нас уже есть VPS-сервер с Ubuntu или Debian. В других дистрибутивах отличия будут, скорее всего, только в установке необходимых пакетов. Этот хост в нашей конфигурации будет использоваться в качестве сервера VPN. Рекомендаций, какой VPS лучше использовать, в интернетах полным-полно — на разный бюджет, с разными конфигурациями и условиями.
Устанавливаем на сервере OpenVPN, stunnel, git:
sudo apt install openvpn stunnel4 git
Далее воспользуемся готовым скриптом для настройки сервера OpenVPN. Можно, конечно, настроить все вручную, и я советую поступить именно так. Информацию о том, как правильно сконфигурировать OpenVPN, нетрудно отыскать в Сети. Но если нужно получить результат очень быстро и тебе лень возиться, то вот решение.
git clone https://github.com/Nyr/openvpn-install.git cd openvpn-install sudo ./openvpn-install.sh
Получаем скрипт установки и запускаем его. Он поможет нам создать пользовательские ключи и сертификаты, если нужно — изменить адресацию сети. Все очень доступно и понятно. На выходе для каждого пользователя мы получим один файл, внутри которого будет конфигурационный ключ и сертификаты (сервера и пользователя). Передаем этот файл на клиент.
Открываем конфигурационный файл сервера OpenVPN и правим его. Выбираем нужный порт — тот, который на сервере будет свободен для подключения. Порт лучше использовать более-менее неприметный (я выбрал 995-й, обычно почтовые порты оставляют открытыми), чтобы клиент мог гарантированно подключиться к VPN-серверу.
Вот как будет выглядеть наш конфиг:
port 443 proto tcp dev tun0 sndbuf 0 rcvbuf 0 ca ca.crt cert server.crt key server.key dh dh.pem auth SHA512 tls-auth ta.key 0 topology subnet server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt push "redirect-gateway def1 bypass-dhcp" push "dhcp-option DNS 208.67.222.222" push "dhcp-option DNS 208.67.220.220" push "route 10.8.0.0 255.255.255.0" route 192.168.31.0 255.255.255.0 keepalive 10 120 cipher AES-256-CBC user nobody group nogroup persist-key persist-tun status openvpn-status.log verb 3 crl-verify crl.pem
Следующим шагом настроим stunnel:
sudo nano /etc/stunnel/stunnel.conf output = /var/log/stunnel.log [openvpn] client = no accept = 443 connect = localhost:443 cert = /etc/stunnel/stunnelsrv.pems
Тут все просто:
- описываем, куда будут сохраняться логи;
- указываем название сервиса в произвольной форме;
- выбираем, в каком режиме работает устройство: сервера или ��лиента. В данном случае
client = no
указывает на режим сервера; - accept = 443 указывает порт, к которому будем подключаться снаружи (443-й порт выбран не просто так — в 99% случаев он всегда открыт, проще прикинуться обычным HTTPS, и нас не заметят даже при DPI).
Генерируем ключи и сертификаты для stunnel:
sudo openssl req -nodes -new -days 365 -newkey rsa:1024 -x509 -keyout stunnelsrv.pem -out stunnel.pem
На этом этапе важно помнить, что у нас на стороне сервера работают OpenVPN на порте 995, proto tcp, dev tun0 и stunnel на порте 443. Все остальные настройки — стандартные.
Переходим к клиенту:
sudo apt install iproute2 ipset stunnel4 git openvpn
Все, что нужно, поставили, теперь настраиваем stunnel на стороне клиента:
output = /var/log/stunnel.log [openvpn] client = yes accept = 127.0.0.1:995 connect = IP:443 cert = /etc/stunnel/stunnel.pem
Здесь мы указываем, куда выводить логи, режим клиента включен. С помощью директивы accept
мы говорим, на какой адрес и порт передавать соединение и куда, собственно, подключаться клиенту stunnel (опция connect
). Ну и какой сертификат использовать.
Передаем файл клиента OpenVPN с сервера. Проверяем настройки: используется порт 995, proto tcp, dev tun0. Очень важный момент: нам нужно объявить маршрут до нашего VPS через шлюз по умолчанию. Иначе получится, что stunnel установит соединение, потом подключится VPN и попытается все перенаправить в туннель, но stunnel уже не сможет работать, так как хост и порт будут недоступны. Также отключаем перенаправление всего трафика в VPN, проверяем адрес подключения и порт. Адрес VPN-сервера в конфиге будет 127.0.0.1 — то есть localhost
. Подразумевается, что stunnel уже пробросил нужный порт клиента. Должно получиться примерно следующее:
client dev tun proto tcp sndbuf 0 rcvbuf 0 remote 127.0.0.1 995 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server auth SHA512 cipher AES-256-CBC setenv opt block-outside-dns key-direction 1 #redirect-gateway def1 pull-filter ignore redirect-gateway #добавляем в конфиг OpenVPN клиента белый IP-адрес VPN-сервера route IP 255.255.255.255 net_gateway #ip route add table obhod default dev tun0 verb 3
Итак, на сервере и клиенте установлены и настроены OpenVPN и stunnel. Безопасное соединение установлено. Дальше будем рассматривать каждый вариант по очереди.
Вариант 1: клиент VPN на машине пользователя
Первым делом создадим список IP-адресов, который будем использовать далее:
sudo ipset -N vpn iphash
Добавим в него один адрес для тестов:
sudo ipset -A vpn 8.8.8.8
Теперь нам необходимо маркировать пакеты с адресами назначения, которые совпадают со списком.
sudo iptables - OUTOUT -t mangle -m set --match-set vpn dst -j MARK --set-mark (1 или 0x1)
В результате наших действий iptables при совпадении в адресе назначения и адресе из списка IPSet начнет маркировать такой трафик. Дальше создадим новую таблицу маршрутизации, для этого пропишем ее в файле /etc/iproute2/rt_tables
с очередностью выше, чем default (253)
:
252 vpn
Теперь надо создать правило и маршрут для маркированных пакетов.
sudo ip rule add table vpn prio 1000 fwmark (1 или 0x1) sudo ip route add table vpn dev tun0 default
Не забываем включить rp_filter
для обеспечения асимметричной маршрутизации и разрешаем переброс пакетов между интерфейсами, на случай если кому-то нужно будет предоставить доступ к закрытым ресурсам в локальной сети.
sudo sysctl net.ipv4.tcp_fwmark_accept=1 sudo sysctl net.ipv4.conf.all.rp_filter=2 sudo net.ipv4.ip_forward=1
Теперь включим NAT для трафика, который направляется в VPN.
sudo iptables -t nat -I POSTROUTING -o tun0 -j MASQUERADE
Если нужно предоставить доступ из локальной сети через свой VPN, правило iptables нужно немного подправить, а именно перенести в другую цепочку.
sudo iptables -I PREROUTING -t mangle -m set --match-set tovpn dst -j MARK --set-mark 0x1
В итоге у нас получается, что все адреса из списка IPSet VPN идут через безопасное соединение, остальные направляются через маршрут по умолчанию.
Вариант 2: клиент VPN на маршрутизаторе
Теперь рассмотрим случай, когда VPN-соединение у нас выполняет не клиентская машина, а некий узел в сети и все настроено на нем аналогично примеру выше. Тогда нам нужно либо указать этот узел в качестве шлюза по умолчанию, либо установить на клиентской машине IPSet и iproute2
и правильно ее настроить.
В этом случае сработают локальные IPSet и iproute2
и направят на промежуточный сервер-маршрутизатор пакет, а тот сверит адрес назначения со своим списком IPSet и, если обнаружит совпадение, отправит пакет в VPN, если совпадения не найдет — перешлет на шлюз по умолчанию.
Неудобство такого приема состоит в том, что нужно дважды вносить IP-адреса в списки. Сначала на сервере-маршрутизаторе, а второй раз — в свой локальный список IPSet. Зато это позволяет более гибко пользоваться маршрутизацией.
Стоит также отметить еще один вариант, о котором не говорилось выше. Можно установить на сервер-маршрутизатор VPN-клиент и stunnel, а потом убрать из конфига строку pull-filter ignore redirect-gateway
. Тогда весь полученный трафик будет завернут в VPN-туннель. Останется лишь локально вести IPSet для направления нужного трафика через разные шлюзы.
IPSet
Списки IPSet очень удобно использовать, если нужно быстро разрешить либо запретить входящие или исходящие соединения на сервер. Так, например, многие реализуют географическую фильтрацию: проще один раз занести все в списки и одним правилом запретить весь входящий трафик отовсюду, кроме этого списка. Например:
sudo iptables -A INPUT -m set --match-set !RU src -j DROP
Это правило заблокирует все входящие соединения, кроме адресов из списка ipset RU
.
IPSet, как видишь, удобный инструмент, который значительно сокращает таблицу правил iptables. В частности, хорошо бывает объединять в отдельные списки сети серверов, адреса админов, остальных пользователей и так далее. А потом уже разом запрещать доступ одним и разрешать другим.
Автоматизация после сбоев/перезагрузок
Чтобы не потерять все настроенные нами параметры, добавим в crontab периодическое сохранение правил IPSet и iptables.
sudo crontab -e # Каждую первую минуту каждого часа, то есть раз в час 1 * * * * /bin/bash root ipset save > /opt/ipset.rules 1 * * * * iptables-save root > /opt/firewall.rules
И в /etc/rc.local
добавим восстановление:
ipset restore < /opt/routers/ipset.rules iptables-restore < /opt/routers/iptables.rules
А теперь напишем скрипт, который будет обходить правила iprule
и iproute
и в случае, если чего-то не хватает, допишет недостающее. Скрипт можно сделать демоном, чтобы он постоянно висел в памяти и мониторил состояние системы, а можно запускать только при старте ОС. В целом второго варианта вполне достаточно.
Исходный код скрипта check ты найдешь в архиве по ссылке в конце статьи, а общий его смысл такой: проверяем таблицу маршрутизации и, если маршрута нет, добавляем его. Потом ту же проверку выполняем с правилом таблицы. Затем смотрим правило iptables, которое маркирует пакеты.
Напоследок напишем скрипт, который позволит нам более удобно добавлять IP-адреса в списки IPSet, чтобы каждый раз не ломиться в консоль. Этот скрипт выглядит так:
#!/bin/bash test=$(zenity --entry --title="Добавление адреса в ipset списки " --text="Введите адрес ip или домена") initip=$(dig +short "$test" -u 1.1.1.1) if [ ! -n "$initip" ]; then zenity --password --title="Введите пароль администратора" | sudo -S ipset -exist -A vpn "$test" echo "$test" >> /opt/iptovpn.txt else zenity --password --title="Введите пароль администратора" | sudo -S ipset -exist -A vpn "$initip" echo "$initip" >> /opt/iptovpn.txt fi sudo ipset save > /opt/ipset.rules
При запуске мы вводим имя домена или сразу IP-адрес. Скрипт переварит и то и другое, после чего и запихает IP в список IPSet.
Также в архиве ты найдешь скрипт add_in_file, который читает файл со списком доменов, получает IP и складывает в списки IPSet.
Заключение
Итак, что у нас получилось? А вот что. Мы подняли зашифрованный, «белый» с точки зрения провайдера канал HTTPS. Внутри у него ходит наш OpenVPN. Клиент заворачивает туда то, что нам нужно, и не отправляет того, что не нужно. В принципе, можно написать еще пару правил iptables, в которых указано, кому дозволено ходить по адресам из заданного списка, а кому нет. Можно указать источники запросов для большей безопасности и для того, чтобы туда, не дай бог, не залез любознательный пользователь. Это реализуется с помощью второго списка IPSet, где придется вбить доверенные хосты, которые будут пользоваться доступом.
При желании всю эту не слишком навороченную архитектуру можно развернуть на Raspberry Pi с «Линуксом» и таскать его с собой в качестве маленького маршрутизатора. Он где угодно обеспечит тебе надежное, безопасное и шифрованное соединение, до которого не доберется ни один товарищ майор.