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

Желание попробовать OpenVPN в конфигурации "VPN over I2P" не случайно: в i2pd работа с TCP-туннелями сделана на порядок лучше, чем с UDP, а wireguard работает как раз по этому протоколу (по UDP). При стандартном использовании 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 = 1
outbound.length = 0
outbound.quantity = 1
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

systemctl daemon-reload
systemctl restart i2pd

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

apt install lynx
lynx 127.0.0.1:7070

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

netns-exec i2pd_ns lynx 127.0.0.1:7070

Наконец, конфигурируем туннель 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
tcp-nodelay
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.

Экспириенс

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

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

Один из замеров на канале с честными 200Мб/сек (значения всегда прыгают)
Один из замеров на канале с честными 200Мб/сек (значения всегда прыгают)

Комментарии (21)


  1. aborouhin
    30.12.2024 16:19

    К результатам спидтеста неплохо бы добавить информацию о том, какова скорость канала без VPN и с другими вариантами VPN на том же канале (скажем, OpenVPN без I2P, WG с I2P). По моему ощущению, OpenVPN через TCP - это те ещё тормоза, даже если его ни во что не упаковывать, и чем больше latency до удалённого хоста - тем всё печальнее в геометрической прогрессии. А VPN, в отличие от прокси, подразумевает не только "бытовое использование десктопной системы"...


    1. pureacetone Автор
      30.12.2024 16:19

      Спасибо за комментарий. Дополнил скрин замера скорости комментарием "Один из замеров на канале с честными 200Мб/сек (значения всегда прыгают)".

      VPN, в отличие от прокси, подразумевает не только "бытовое использование десктопной системы"

      не спорю; я описал для чего использую VPN именно я в домашних условиях и в описанном случае; это действительно только "бытовое использование десктопной системы"


    1. interrupt
      30.12.2024 16:19

      Большая проблема при тунелировании через TCP - алгоритм Нагла, особенно в сочетании с отложенным ACK (а обычно это так и получается). Если выставить TCP_NODELAY на сокете, то уже будет сильно лучше. Применительно к openvpn кажется надо tcp-nodelay написать в конфиг.


      1. pureacetone Автор
        30.12.2024 16:19

        Очень дельное замечание, благодарю. Добавил в клиентский конфиг.


  1. rPman
    30.12.2024 16:19

    Я правильно понимаю что i2p запускался с дефолтным конфигом? И при этом такая конструкция дает десятки мегабит скорости? Удивительно! Лично я смотрел на i2p с десяток лет назад, и скорости там были дай бог мегабит... это получается сеть i2p живет и в качестве ее нод выступают ноды на мощных каналах?


    1. pureacetone Автор
      30.12.2024 16:19

      Верно, i2pd с дефолтным конфигом. Конфигурируются только туннели (клиентский и серверный).

      У I2P два разных клиента сети. Я пишу о том, который на C++ (i2pd). Обычно же люди, гугля, приходят к Java-реализации. На ней я описанное не пробовал, но во многих других тестах "Java-роутер" дает сильные просадки по скорости и по потреблению ресурсов.

      в качестве нод I2P выступают ноды на мощных каналах

      Узлы I2P практически невозможно модерировать, поэтому сеть полна разношерстными серверами, среди которых хорошие VPS в датацентрах и подкроватные полухромые сервачки на USB-модемах и одноплатниках.

      Весьма хорошее качество туннелей во многом обусловлено их "шириной" (quantity). Можно представить туннель как логический канал связи, состоящий из физических нитей. Количество таких нитей - параметр quantity. Трафик делится между всеми нитями, что в некоторой степени нивелирует "хромые" узлы в туннеле, не давая им единолично заткнуть сообщение конечных участников в минимум.

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


      1. sim2q
        30.12.2024 16:19

        и подкроватные полухромые сервачки на USB-модемах и одноплатниках.

        сначала вроде и обидно, а потом.... Вместе мы сила!)


        1. pureacetone Автор
          30.12.2024 16:19

          Конечно! Каждый роутер в сети - часть ее общей мощности.

          В I2P есть базовые маханизмы для идентификации, чтобы при построении туннелей можно было выбирать узлы, которые наверняка дадут хороший перформанс: флаг доступности (доступен ли извне или сидит за NAT) и лимит скорости для транзитных узлов, установленный оператором в конфиге. Все это не гарантирует хороший физический канал, но дает надежду.


  1. JBFW
    30.12.2024 16:19

    Пока почитаю подробнее - но сразу возник вопрос: если связность и защиту обеспечивает i2p - зачем там вообще OpenVPN?
    Простого TCP соединения с PPP в нем недостаточно разве?


    1. pureacetone Автор
      30.12.2024 16:19

      Спасибо за комментарий. Я думал про pptp, ведь там данные бегают поверх TCP-сокета, но уперся в необходимость PPP-соединения, то есть линка на канальном уровне, который ниже TCP/UDP. Увы, i2pd не предоставляет туннели канального уровня.

      Можете подсказать (или развить) ход вашей мысли?


      1. JBFW
        30.12.2024 16:19

        Канал организуется непосредственно на уровне tcp соединения, вот точно так же как по модемной линии, с помощью pppd и netcat

        В канале поднимается PPP ну а дальше все как во времена аналоговых модемов.

        Более того, я сам это когда-то делал, и с теми же целями, только задача была в проходе по 443 порту сквозь файрвол. Но конфиг давно утерян, а восстановить сходу вчера у меня не получилось )

        В интернетах же, такое впечатление, это знание давно забыто и похоронено. Найденное с использованием pptp это другое, по сути похожее, но другое.


        1. JBFW
          30.12.2024 16:19

          Вот кажется, там на серверной стороне pppd запускался на порту через inetd, a на клиентской уже через netcat. Очень уж давно это было, надо на выходных попробовать)

          А то есть ещё SLIP, тоже может, но это не пробовал никогда


        1. MAXH0
          30.12.2024 16:19

          Интересная идея... Если получится, напишите статью?


          1. JBFW
            30.12.2024 16:19

            Напишу ))

            Пока вот получилось:
            На обоих сторонах обнуляем /etc/ppp/options (временно, чтобы не мешало)
            На серверной запускаем например так:

            pppd nodetach persist 192.168.50.1:192.168.50.2 pty "nc -l 5005"

            На локальной так (туннель на 127.0.0.1 утановлен):

            pppd nodetach persist pty "nc 127.0.0.1 5005"

            И оно работает:

            local IP address 192.168.50.2
            remote IP address 192.168.50.1

            ping 192.168.50.1
            PING 192.168.50.1 (192.168.50.1) 56(84) bytes of data.
            64 bytes from 192.168.50.1: icmp_seq=1 ttl=64 time=59.2 ms
            64 bytes from 192.168.50.1: icmp_seq=2 ttl=64 time=50.4 ms

            Только довести до ума, засунув настройки в /etc/ppp/options


            1. pureacetone Автор
              30.12.2024 16:19

              Элегантно и просто! Говоря о PPTP, упустил из виду PPP over TCP, и уперся в тупик. Жду вашу статью с конфигами)


  1. mltk
    30.12.2024 16:19

    В каких случаях это может быть нужно?

    1. Ваш сервер забанен РКНом по IP

    2. Используемый между вами и вашим сервером впн-протокол забанен РКНом по сигнатурам.

    И всё. Верно?


    1. JBFW
      30.12.2024 16:19

      Почему сразу РКН и почему именно это вас так заинтересовало? )

      У нас сейчас санкций больше чем у Ирана, на некоторые технические сайты отсюда просто больше не пускают. Это важнее РКН или чьих-то опасений.


      1. mltk
        30.12.2024 16:19

        Ну замените эти три буквы на "провайдер/регулятор". Я в курсе, что в разных странах/у разных провайдеров есть свои, иногда весьма вольные и даже законодательно не подкреплённые особенности и ограничения.

        По сути-то есть ответ на мой вопрос?


        1. JBFW
          30.12.2024 16:19

          По сути это "проброс порта", похожий в чем-то на явление квантовой запутанности:

          Зная адрес второй точки внутри сети i2p можно пробросить этот порт через пространство сети на любое расстояние, понятия не имея где именно эта вторая точка находится.

          А уж как именно использовать этот порт - дело того, кто его пробрасывает.
          Можно подключить там к примеру веб-систему, чтобы подключаться к своему любимому домашнему серверу из внешнего мира, не имея белых IP-адресов.


  1. kujoro
    30.12.2024 16:19

    Мне пока что хватает твоего тора, через yggdrasil, даже юпупчик посмотреть можно :-)


  1. VenbergV
    30.12.2024 16:19

    В openvpn пару лет уже можно использовать fingerprint вместо центра сертификации.
    Вот есть пример на эту тему.