December 21, 2019

Хакер - Пингвинья тропа. Простые рецепты для выборочного шифрования трафика в 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 с «Линуксом» и таскать его с собой в качестве маленького маршрутизатора. Он где угодно обеспечит тебе надежное, безопасное и шифрованное соединение, до которого не доберется ни один товарищ майор.

ПОДПИСАТЬСЯ - Бюро121