Дисклеймер. Автор статьи никоим образом не призывает к чему-либо, что противоречит законодательству РФ и других благословенных государств. Юзкейс этой статьи родился в быту: был я, значит, в командировке в одной из стран ближайшего (дружественного) зарубежья, а там wireguard почему-то плохо работал, а без него связь с офисом в Москве встала — удалёнка не работает. Вот и пришлось изобретать...
О чём речь
В этой статье вы узнаете как подключиться к VPN-серверу wireguard через I2P, завернув в VPN весь нужный трафик устройства, при этом приложение I2P (i2pd) будет работать штатно через домашнего интернет-провайдера. Представленная конфигурация описана для Linux-систем, в частности для дистрибутивов, основанных на Debian (с другими принципиальных проблем тоже быть не должно).
Описанный механизм может быть легко перенесен на похожие случаи с другими приложениями, в том числе без использования сети I2P. Если вы хорошо знакомы с возможностями сетевой конфигурации Linux, вряд ли узнаете что-то принципиально новое.
UPDATE: смотрите вторую часть. Там то же самое, но с OpenVPN вместо wireguard; связка с OpenVPN дает лучший перформанс за счет использования TCP-туннелей вместо UDP.
Сервер
Команды приведены от рута (без sudo).
Устанавливаем wireguard:
apt install wireguardКонфигурируем wireguard в качестве принимающего сервера. Чтобы не заниматься консольными танцами, можно воспользоваться онлайн-генератором конфигов. Обратите внимание на CIDR (адрес сети), чтобы он не конфликтовал с другими вашими сетями. В примере я использую 10.20.25.0/24. Серверную часть ложим в /etc/wireguard/wg0.conf (строки PostUp и PostDown выбрасываем, это лишнее). Также не забудьте сохранить клиентский конфиг.
В моем случае серверный конфиг рассчитан на одного клиента и выглядит примерно так (можете использовать, если сеть не конфликтует с другими вашими сетями, и не забудьте подставить ключи):
[Interface]
Address = 10.20.25.1/24
ListenPort = 51820
PrivateKey = ***
[Peer]
PublicKey = ***
AllowedIPs = 10.20.25.2/32Wireguard не имеет настройки для биндинга на конкретный адрес, поэтому слушает на всех сетевых интерфейсах. Если это вас не устраивает, воспользуйтесь фаерволом.
Поднимаем интерфейс wireguard и добавляем его в автозагрузку:
systemctl start wg-quick@wg0
systemctl enable wg-quick@wg0Включаем форвардинг трафика. В файле /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 "wg0" masquerade
}
}Обратите внимание, что эта конфигурация включает маскарадинг только для запросов, которые приходят через интерфейс wireguard (wg0). Чтобы включить маскарадинг во всех направлениях, оставьте в строке только слово 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 "wg0" 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 очищаем от лишнего и приводим к следующему виду:
[WG-TUNNEL]
type = udpserver
address = 127.0.0.1
host = 127.0.0.1
port = 51820
inport = 51820
inbound.length = 0
inbound.quantity = 1
outbound.length = 0
outbound.quantity = 1
keys = wg-tun.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
Навигация стрелками на клавиатуре. Выбираем пункт "I2P tunnels" и оттуда копируем адрес *.b32.i2p под заголовком из конфига (WG-TUNNEL в примере выше).
Клиент
Сначала устанавливаем i2pd и wireguard аналогично тому, как это было сделано на сервере. Конфиги пока что не трогаем.
Если сейчас запустить VPN с маршрутом 0.0.0.0/0, то есть в качестве сетевого шлюза системы, i2pd вместе с другими приложениями будет ходить через туннель wireguard. Нюанс в том, что нам надо завернуть трафик wireguard в i2pd, а остальные приложения пускать через WG. Вопрос!
Если VPN маршрутизирует какую-то частную сеть, проблем не будет. Но разберем случай с VPN в качестве основного шлюза. После прочтения вы поймете как упростить эту инструкцию, опустив все танцы у костра, если ваш случай именно про маршрутизацию одной приватной подсети.
Попытки разрешить через фаервол какие-то отдельные порты или адреса для прямого выхода i2pd через домашнего провайдера упрутся в тупик: I2P-роутер общается с огромным количеством случайных адресов и по разным портам. Даже протоколы разные: полноценно используются TCP и UDP.
Решение в том, чтобы организовать изолированное сетевое пространство имен, создать там виртуальный сетевой интерфейс, который будет ходить через физический сетевой адаптер в интернет через домашнего провайдера без всяких VPN. Через специальную виртуальную сеть мы вытащим туннель i2pd в основное сетевое пространство ОС и подключим к нему клиент wireguard. Чтобы вся система была максимально секьюрна, в основном сетевом пространстве имен на сетевом интерфейс�� приколотим статический локальный адрес и намеренно не зададим настройки шлюза. Матёрый killswitch получится!

Постарался на схеме изобразить суть, чтобы помимо конфигурационных строчек была почва для простого понимания. Ниже те самые строчки, которые надо скопировать и положить в файлик 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 и wireguard.
В /etc/i2pd/tunnels.conf убираем лишнее и создаем туннель до сервера (з��мените значение в строке destination):
[WG-CLIENT]
type = udpclient
address = 10.10.10.2
host = 10.10.10.2
port = 51820
destination = ***.b32.i2p
destinationport = 51820
inbound.length = 1
inbound.quantity = 16
outbound.length = 1
outbound.quantity = 16
keys = transient-vpnПриведен конфиг с длиной туннелей в 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
Наконец, конфигурируем туннель wireguard. Создаем файл /etc/wireguard/wg0.conf, вставляем в него клиентский конфиг (подставьте ключи и замените значение Address в зависимости от конфига на сервере):
[Interface]
PrivateKey = ***
Address = 10.20.25.2/24
DNS = 94.140.14.14
[Peer]
PublicKey = ***
Endpoint = 10.10.10.2:51820
AllowedIPs = 0.0.0.0/0, ::/0В AllowedIPs важно вставлять IPv6 маршрут по умолчанию (::/0) даже если сервер VPN его не поддерживает — страховка от того, что IPv6 потечет мимо VPN.
Директива "DNS" не будет работать, если в системе не установлен resolvconf. Установить его просто:
apt install resolvconf Готово! Поднимаем туннель wireguard и добавляем его в автозагрузку:
systemctl start wg-quick@wg0
systemctl enable wg-quick@wg0Важные примечания
1. Чтобы настроить киллсвитч (предотвращение утечки пакетов при отключенном VPN), после завершения всех действий, когда убедитесь, что VPN работает, задайте статические настройки вашего основного сетевого и��терфейса, убрав gateway.

2. Проконтролируйте размеры UDP буферов с системе на сервере и на клиенте. Для хорошей работы приложений, завязанных на UDP и большом потоке данных, это критический (и, зачастую, неочевидный) нюанс!
Проверить максимальный размер буферов можно командами:
sysctl net.ipv4.udp_wmem_max
sysctl net.ipv4.udp_rmem_maxВ Debian 12 я обнаружил 4096 и 4096 . Рекомендуется значительно больше. Например, 16777216 . После установки подобных значений стабильность VPN-туннеля ощутимо повысилась.
Чтобы изменить размеры буферов, вставьте в начало файла /etc/sysctl.conf эти строки:
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384Затем примените изменения:
sysctl -pЭкспириенс

Саспенс — это слово почему-то просится быть первым после "экспириенс" в данном контексте.
Несколько замеров на загрузку большого файла через wget на кабеле 100Мб/сек показали, что при туннелях нулевой длины скорость падает примерно в два-четыре раза с редкими возможными скачками до 80% от реальной пропускной способности канала (верю в светлое будущее, где новые релизы i2pd улучшат показатель). При туннелях с ненулевой длиной стабильный замер практически невозможен: промежуточные узлы — кот в мешке. Видео грузятся и ладно. Стабильным такое подключение назвать нельзя, но и нерабочим — тоже, и это главное. VPN через I2P, Карл!
Возможны ли случайные утечки трафика? При приведенной конфигурации — нет. Сетевое пространство имен для i2pd изолировано от основной системы на уровне ядра ОС, при этом в доступном для пользователя пространстве нет маршрутов по умолчанию кроме VPN-туннеля. Если wireguard по каким-то причинам упадет, ПК останется оффлайн даже при том, что i2pd продолжит штатную работу.
Высокотехнологичный костыль красивее родной ноги.
