OpenVPN & i2pd: VPN через I2P (часть 2)

В предыдущей статье я описал как сконфигурировать wireguard для работы через I2P. Эту статью можно считать копией с тем отличием, что будем использовать OpenVPN.

Желание попробовать OpenVPN в конфигурации «VPN over I2P» не случайно: в i2pd работа с TCP-туннелями сделана на порядок лучше, чем с UDP, а wireguard работает как раз по этому протоколу. При стандартном использовании wireguard намного привлекательнее древнего OpenVPN, но в контексте i2pd и работы с TCP-туннелями, OpenVPN показал отличные результаты: несколько суток стабильной работы с просмотром видео и прочими бытовыми нагрузками не выявили ни единого мертвого затыка (чего нельзя сказать про конфиг с wireguard, к сожалению).

Проблема реализации в i2pd объясняется просто: Purple I2P — некоммерческий свободный проект, поэтому маловостребованные UDP-туннели имеют старую и очень несовершенную реализацию в коде, и в ближайшем будущем вряд ли кто-то проведет над этой частью приложения существенную работу. Если все-таки проведет, считайте приоритетной конфигурацию с wireguard.

По сути: о чём речь

В этой статье вы узнаете как подключиться к серверу OpenVPN через I2P, завернув в VPN весь нужный трафик устройства, при этом приложение I2P (i2pd) будет работать штатно через домашнего интернет-провайдера. Представленная конфигурация описана для Linux-систем, в частности для дистрибутивов, основанных на Debian.

Если вы хорошо знакомы с возможностями сетевой конфигурации Linux, вряд ли узнаете что-то принципиально новое.

Сервер

Команды приведены от рута (без sudo).

Устанавливаем openvpn:

apt install openvpn

OpenVPN в сравнении с wireguard имеет очень муторную настройку с ручной генерацией сертификатов и прочей ботвы про систему открытых и закрытых ключей. Вместо всего этого, в статье я продемонстрирую простой пример со статичным ключом, поддержка которого будет удалена в OpenVPN 2.7. Настройка безопасности OpenVPN избыточна, т.к. транспортную безопасность нам обеспечит протокол I2P. Если у вас уже существующая инфраструктура OpenVPN или версия 2.7+, вам необходимо адаптировать описанную логику самостоятельно.

Создаем конфиг по пути /etc/openvpn/server/i2pserver.conf:

dev tun
proto tcp-server
port 5005
local 127.0.0.1
ifconfig 10.20.25.1 10.20.25.2
cipher AES-128-CBC
data-ciphers AES-128-CBC
secret static.key

Генерируем статичный ключ и ложим рядом с конфигом (в /etc/openvpn/server):

openvpn --genkey secret static.key

Поднимаем туннель и добавляем его в автозагрузку:

systemctl start openvpn-server@i2pserver
systemctl enable openvpn-server@i2pserver

Включаем форвардинг трафика. В файле /etc/sysctl.conf раскомментируем строку net.ipv4.ip_forward = 1 (если такой строки нет, просто добавьте ее в начало файла). Применяем изменения командой

sysctl -p

Если видите ошибку о том, что команда не найдена, воспользуйтесь whereis sysctl и вызовите утилиту по полному пути /usr/sbin/sysctl.

Чтобы система умела не только пересылать пакеты, но и делать это корректно между разными сетями, включаем маскарадинг (на Debian 12 в качестве фаервола по умолчанию используется nftables). В конец файла /etc/nftables.conf добавляем

table ip nat {
    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
        iifname "tun0" masquerade
    }
}

Обратите внимание, что эта конфигурация включает маскарадинг только для запросов, которые приходят через интерфейс OpenVPN (tun0). Чтобы включить маскарадинг во всех направлениях, оставьте в строке только слово masquerade.

На свежей системе nftables.conf примет такой вид:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority filter;
        }
        chain forward {
                type filter hook forward priority filter;
        }
        chain output {
                type filter hook output priority filter;
        }
}

table ip nat {
    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
        iifname "tun0" masquerade
    }
}

После изменения конфига, перезапускам nftables:

systemtl restart nftables

Базовая настройка системы в качестве VPN-сервера завершена. Теперь переходим к экзотической части.

Устанавливаем i2pd по гайду из доки:

apt-get install apt-transport-https gpg
wget -q -O - https://repo.i2pd.xyz/.help/add_repo | bash -s -
apt-get update
apt-get install i2pd

Создаем серверный туннель для приема подключений через сеть I2P. Файл /etc/i2pd/tunnels.conf очищаем от лишнего и приводим к следующему виду:

[OPENVPN]
type = server
address = 127.0.0.1
host = 127.0.0.1
port = 5005
inport = 5005
inbound.length = 0
inbound.quantity = 16
outbound.length = 0
outbound.quantity = 16
keys = openvpn.dat

Обратите внимание: приведена конфигурация с туннелями нулевой длины. Это значит, что любой человек или бот, который знает I2P-адрес VPN-туннеля, с легкостью определит сервер, на котором он хостится, т.к. концы входящего и исходящего туннелей будут иметь один постоянный IP-адрес.

Если по каким-то причинам вам надо скрыть сервер от вымышленного наблюдателя, установите длину туннелей в один транзитный узел и увеличьте их «ширину», используя эти значения:

inbound.length = 1
inbound.quantity = 16
outbound.length = 1
outbound.quantity = 16

Подробнее про туннели I2P можете почитать тут.

Перезапускаем i2pd и на всякий случай дублируем автозапуск сервиса, который при установке уже был добавлен туда автоматически:

systemctl restart i2pd
systemctl enable i2pd

Последнее, что нам требуется на сервере — узнать I2P-адрес нашего туннеля. Самый простой способ — зайти в веб-интерфейс i2pd. Я покажу как это делается через консольный веб-браузер:

apt install lynx
lynx 127.0.0.1:7070

Веб-консоль i2pd в браузере lynx

Веб-консоль i2pd в браузере lynx

Навигация стрелками на клавиатуре. Выбираем пункт «I2P tunnels» и оттуда копируем адрес *.b32.i2p под заголовком из конфига (OPENVPN в примере выше).

Клиент

Сначала устанавливаем i2pd и OpenVPN аналогично тому, как это было сделано на сервере. Конфиги пока что не трогаем.

Если сейчас запустить VPN с маршрутом 0.0.0.0/0, то есть в качестве сетевого шлюза системы, i2pd вместе с другими приложениями будет ходить через туннель OpenVPN. Нюанс в том, что нам надо завернуть трафик OpenVPN в i2pd, а остальные приложения пускать через OpenVPN. Вопрос!

Если VPN маршрутизирует какую-то частную сеть, проблем не будет. Но разберем случай с VPN в качестве основного шлюза. После прочтения вы поймете как упростить эту инструкцию, опустив все танцы у костра, если ваш случай именно про маршрутизацию одной приватной подсети.

Попытки разрешить через фаервол какие-то отдельные порты или адреса для прямого выхода i2pd через домашнего провайдера упрутся в тупик: I2P-роутер общается с огромным количеством случайных адресов и по разным портам. Даже протоколы разные: полноценно используются TCP и UDP.

Решение в том, чтобы организовать изолированное сетевое пространство имен, создать там виртуальный сетевой интерфейс, который будет ходить через физический сетевой адаптер в интернет через домашнего провайдера без всяких VPN. Через специальную виртуальную сеть мы вытащим туннель i2pd в основное сетевое пространство ОС и подключим к нему клиент OpenVPN. Чтобы вся система была максимально секьюрна, в основном сетевом пространстве имен на сетевом интерфейсе приколотим статический локальный адрес и намеренно не зададим настройки шлюза.

Схема VPN через I2P на Linux (OpenVPN)

Схема VPN через I2P на Linux (OpenVPN)

Постарался на схеме изобразить суть, чтобы помимо конфигурационных строчек была почва для простого понимания. Ниже те самые строчки, которые надо скопировать и положить в файлик setup_network.sh в любом удобном месте.

Обратите внимание, что в переменной IP_ADDRESS надо указать свободный адрес вашей локальной сети — с ним будет создан изолированный сетевой интерфейс в пространстве имен i2pd, который станет равноценным участником вашей локалки помимо основного интерфейса.

#!/bin/bash
# acetone, 2024

# Set your default gateway settings
INTERFACE="eth0"
IP_ADDRESS="192.168.0.99/24"
GATEWAY="192.168.0.1"

# Nothing below this line should be changed unless you know what you are doing!

# Create i2pd network namespace
ip netns add i2pd_ns
ip netns exec i2pd_ns ip link set lo up

# Create macvlan interface (gateway for i2pd_ns)
ip link add macvlan0 link $INTERFACE type macvlan mode bridge
ip link set macvlan0 netns i2pd_ns

# Activate the macvlan interface in the i2pd namespace
ip netns exec i2pd_ns ip link set macvlan0 up

# Configuring the IP address and route for i2pd_ns
ip netns exec i2pd_ns ip addr add $IP_ADDRESS dev macvlan0
ip netns exec i2pd_ns ip route add default via $GATEWAY dev macvlan0

# Create virtual interfaces for i2pd to communicate with the main system
ip link add bri2pd_external type veth peer name bri2pd_internal
ip link set bri2pd_external up
ip link set bri2pd_internal netns i2pd_ns up
ip addr add 10.10.10.1/30 dev bri2pd_external
ip netns exec i2pd_ns ip addr add 10.10.10.2/30 dev bri2pd_internal

Запускаем скрипт, чтобы создать в системе изолированную сеть и прочие прелести (от рута).

chmod +x ./setup_network.sh
./setup_network.sh

Я использую systemd-сервис, чтобы скрипт отрабатывал всегда при старте системы и без моего участия. Если хотите также, создайте файл /etc/systemd/system/setup-network-ns.service со следующим содержимым (исправьте строку ExecStart):

[Unit]
Description=Setup Network Namespace
After=network.target
Wants=network.target

[Service]
Type=oneshot
User=root
ExecStart=/path/to/your/setup_network.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Когда файл сервиса создан, добавляем его в автозагрузку:

systemctl enable setup-network-ns

Сетевое пространство имен создано. Чтобы мы смогли без прав суперпользователя и ввода пароля запускать в нем приложения, поставим утилиту netns-exec:

apt install git build-essential
git clone --recursive https://github.com/freeacetone/netns-exec
cd netns-exec
make
make install

Финишная прямая! Конфигурируем i2pd и OpenVPN.

В /etc/i2pd/tunnels.conf убираем лишнее и создаем туннель до сервера (замените значение в строке destination):

[OPENVPN]
type = client
address = 10.10.10.2
host = 10.10.10.2
port = 5005
destination = ***.b32.i2p
destinationport = 5005
inbound.length = 1
inbound.quantity = 16
outbound.length = 1
outbound.quantity = 16
keys = transient-openvpn

Приведен конфиг с длиной туннелей в 1 транзитный узел. Это значит, что ваше устройство не будет обращаться напрямую на IP-адрес сервера. Всегда будут рандомные транзитные узлы. Если хотите подключаться напрямую, замените значения на

inbound.length = 0
inbound.quantity = 1
outbound.length = 0
outbound.quantity = 1

Теперь нужно внести небольшое изменение в файл сервиса i2pd, чтобы i2pd запускался в изолированном сетевом пространстве. В файле /lib/systemd/system/i2pd.service исправляем строку ExecStart, добавляя в ее начало /usr/local/bin/netns-exec i2pd_ns

e176d5f138ead042223a52e163ad73d4.png

systemctl daemon-reload
systemctl restart i2pd

Если все было сделано верно, i2pd работает в отдельной сети и уже предоставляет туннель до VPN сервера. Работу в отдельной сети легко проверить через консольный браузер:

apt install lynx
lynx 127.0.0.1:7070

a93ba45e9c3926d1932ef60861c31992.png

Ошибка — это правильно! i2pd работает в изолированном пространстве имен. Если запустить браузер в том же пространстве (i2pd_ns), все будет ок:

netns-exec i2pd_ns lynx 127.0.0.1:7070

73df5f96a3f1c504d91194f6612c4338.png

Наконец, конфигурируем туннель OpenVPN. Создаем файл /etc/openvpn/client/i2pclient.conf, вставляем в него клиентский конфиг:

remote 10.10.10.2 5005
proto tcp-client
dev tun
resolv-retry infinite
ifconfig 10.20.25.2 10.20.25.1
nobind
cipher AES-128-CBC
data-ciphers AES-128-CBC
redirect-gateway def1
redirect-gateway ipv6
secret static.key

Скопируйте с сервера static.key и положите рядом с конфигом в /etc/openvpn/client .

Готово! Поднимаем туннель OpenVPN и добавляем его в автозагрузку:

systemctl start openvpn-client@i2pclient
systemctl enable openvpn-client@i2pclient

Важное примечание

Чтобы настроить киллсвитч (предотвращение утечки пакетов при отключенном VPN), после завершения всех действий, когда убедитесь, что VPN работает, задайте статические настройки вашего основного сетевого интерфейса, убрав gateway.

736ab251321a9af4ad71b76243bb028d.png

Экспириенс

783faae5730750db5871b9cea11f6903.jpg

Эта статья не появилась, если бы качество связи OpenVPN+I2P не обрадовало меня так сильно, как это случилось. В отличие от конфигурации с UDP-туннелями, за несколько суток нон-стоп использования описанной конфигурации я ни разу не столкнулся с затыками или критическим падением скорости, при том, что тестирование проводилось не на нулевых туннелях, а на тех, что в статье (сервер — 0, клиент — 1), то есть живой тест с транзитными узлами между мной и сервером, что скрывает от наблюдателя сам факт подключения к IP-адресу сервера.

Я скачал пару тяжелых файлов через BitTorrent, провел несколько часов за просмотром видео в качестве FullHD на Youtube и ни в чем себе не отказывал при бытовом использовании десктопной системы. Вердикт: синтетические тесты скорости сильно слабже голого соединения, но на практике юзабельно на 100%.

a7b91e288953051ee4814a71d69c119b.png

Habrahabr.ru прочитано 2206 раз