WireGuard® — это чрезвычайно простой, но при этом быстрый и современный VPN, использующий передовую криптографию. Он стремится быть быстрее, проще, легче и полезнее IPsec, избегая при этом огромной головной боли. WireGuard значительно производительнее OpenVPN. WireGuard разработан как VPN общего назначения для работы как на встраиваемых интерфейсах, так и на суперкомпьютерах, подходя для множества различных сценариев. Изначально выпущенный для ядра Linux, теперь он кроссплатформенный (Windows, macOS, BSD, iOS, Android) и широко развертываемый. В настоящее время находится в активной разработке, но уже сейчас его можно считать самым безопасным, простым в использовании и самым простым VPN-решением в индустрии.
Простой и удобный в использовании
WireGuard стремится быть таким же простым в настройке и развертывании, как SSH. VPN-соединение устанавливается простым обменом открытыми ключами — точно так же, как при обмене SSH-ключами — а всё остальное прозрачно обрабатывается WireGuard. Он даже способен роумить между IP-адресами, как Mosh. Нет необходимости управлять соединениями, беспокоиться о состоянии, управлять демонами или беспокоиться о том, что находится под капотом. WireGuard предоставляет чрезвычайно простой, но мощный интерфейс.
WireGuard разработан с учётом простоты реализации и лёгкости. Он предназначен для простой реализации в очень небольшом количестве строк кода и лёгкого аудита на наличие уязвимостей безопасности. По сравнению с гигантами вроде *Swan/IPsec или OpenVPN/OpenSSL, аудит огромных кодовых баз которых является непосильной задачей даже для больших команд экспертов по безопасности, WireGuard предназначен для всесторонней проверки отдельными специалистами.
Высокая производительность
Сочетание чрезвычайно высокоскоростных криптографических примитивов и того факта, что WireGuard работает внутри ядра Linux, означает, что безопасная сеть может быть очень высокоскоростной. Он подходит как для небольших встраиваемых устройств, таких как смартфоны, так и для полностью загруженных магистральных маршрутизаторов.
Хорошо определённый и тщательно продуманный
WireGuard является результатом длительного и тщательно продуманного академического процесса, результатом которого стал технический документ — академическая исследовательская работа, которая чётко определяет протокол и тщательные соображения, лежащие в основе каждого решения.
Концептуальный обзор WireGuard — быстрого, современного и безопасного VPN-туннеля
Концептуальный обзор
Если вы хотите получить общее концептуальное представление о том, что такое WireGuard, читайте дальше. Затем вы можете перейти к установке и прочитать инструкции по быстрому запуску о том, как его использовать.
Если вы заинтересованы во внутреннем устройстве, вас может заинтересовать краткое описание протокола или вы можете углубиться, прочитав технический документ, который более подробно описывает протокол, криптографию и основы. Если вы намерены реализовать WireGuard для новой платформы, пожалуйста, прочитайте заметки о кросс-платформенной разработке.
WireGuard безопасно инкапсулирует IP-пакеты через UDP. Вы добавляете интерфейс WireGuard, настраиваете его с вашим приватным ключом и открытыми ключами ваших пиров, а затем отправляете через него пакеты. Все вопросы распространения ключей и принудительной настройки находятся вне области WireGuard; эти вопросы гораздо лучше оставить другим уровням, чтобы не получить раздувание IKE или OpenVPN. Напротив, он больше имитирует модель SSH и Mosh: обе стороны имеют открытые ключи друг друга, а затем они просто могут начать обмениваться пакетами через интерфейс.
Простой сетевой интерфейс
WireGuard работает путём добавления сетевого интерфейса (или нескольких), например eth0 или wlan0, называемого wg0 (или wg1, wg2, wg3 и т.д.). Этот сетевой интерфейс затем можно настраивать обычным образом с помощью ifconfig(8) или ip-address(8), добавлять и удалять маршруты с помощью route(8) или ip-route(8) и так далее со всеми обычными сетевыми утилитами. Специфические для WireGuard аспекты интерфейса настраиваются с помощью инструмента wg(8). Этот интерфейс действует как туннельный интерфейс.
WireGuard связывает IP-адреса туннеля с открытыми ключами и удалёнными конечными точками. Когда интерфейс отправляет пакет пиру, он делает следующее:
Этот пакет предназначен для 192.168.30.8. Какой это пир? Позвольте мне посмотреть… Хорошо, это для пира ABCDEFGH. (Или если он не предназначен ни для одного настроенного пира — отбросить пакет.)
Зашифровать весь IP-пакет, используя открытый ключ пира ABCDEFGH.
Какова удалённая конечная точка пира ABCDEFGH? Позвольте мне посмотреть… Хорошо, конечная точка — UDP-порт 53133 на хосте 216.58.211.110.
Отправить зашифрованные байты из шага 2 через Интернет на 216.58.211.110:53133 через UDP.
Когда интерфейс получает пакет, происходит следующее:
Я только что получил пакет с UDP-порта 7361 от хоста 98.139.183.24. Давайте расшифруем его!
Он успешно расшифрован и аутентифицирован для пира LMNOPQRS. Хорошо, давайте запомним, что последняя конечная точка пира LMNOPQRS в Интернете — 98.139.183.24:7361 через UDP.
После расшифровки обычный текстовый пакет пришёл с 192.168.43.89. Разрешено ли пиру LMNOPQRS отправлять нам пакеты с адреса 192.168.43.89?
Если да — принять пакет на интерфейсе. Если нет — отбросить его.
За кулисами происходит многое для обеспечения надлежащей конфиденциальности, подлинности и совершенной прямой секретности с использованием современной криптографии.
Криптоключевая маршрутизация
В основе WireGuard лежит концепция, называемая Криптоключевой маршрутизацией, которая работает путём связывания открытых ключей со списком IP-адресов туннеля, которым разрешено находиться внутри туннеля. Каждый сетевой интерфейс имеет приватный ключ и список пиров. Каждый пир имеет открытый ключ. Открытые ключи короткие и простые и используются пирами для аутентификации друг друга. Их можно передавать для использования в конфигурационных файлах любым внешним методом, подобно тому, как можно отправить свой открытый ключ SSH другу для доступа к серверу оболочки.
Например, серверный компьютер может иметь такую конфигурацию:
Инструкции по установке WireGuard на различные операционные системы и устройства
Обзор
WireGuard доступен для широкого спектра операционных систем и платформ. Ниже приведены инструкции по установке для наиболее популярных систем. Если ваша система не указана в списке, вы также можете легко скомпилировать WireGuard из исходного кода.
После установки модуль ядра и утилиты будут готовы к использованию. Модуль WireGuard автоматически загружается при создании интерфейса.
Проверка установки:
wg --version
modinfo wireguard
Установка на сервере Fedora
В Fedora WireGuard доступен в официальных репозиториях. Для установки требуются утилиты пользовательского пространства, так как модуль ядра встроен в ядро Linux начиная с версии 5.6.
Fedora 32 и новее (с ядром 5.6+):
sudo dnf install wireguard-tools
Fedora 31 и более ранние версии:
Для старых версий Fedora, где модуль ядра не встроен, необходимо установить модуль отдельно:
Настройка автоматической загрузки модуля (для ядер < 5.6):
sudo modprobe wireguard
echo"wireguard"| sudo tee /etc/modules-load.d/wireguard.conf
Для Fedora 32 и новее модуль WireGuard встроен в ядро Linux, поэтому дополнительная установка модуля не требуется.
Установка на рабочей станции Fedora
На рабочей станции Fedora процесс установки аналогичен серверной версии. Однако для рабочей станции может потребоваться графический интерфейс для управления VPN-подключениями.
Установка базовых инструментов:
sudo dnf install wireguard-tools
Установка GUI-менеджера (опционально):
Для управления WireGuard через графический интерфейс можно установить NetworkManager:
WireGuard использует ключи в формате base64. Их можно сгенерировать с помощью утилиты wg(8):
$ umask077$ wg genkey > privatekey
Это создаст файл privatekey с новым приватным ключом.
Затем вы можете получить публичный ключ из приватного:
$ wg pubkey < privatekey > publickey
Это прочитает privatekey из stdin и запишет соответствующий публичный ключ в publickey.
Конечно, вы можете сделать всё за один раз:
$ wg genkey | tee privatekey | wg pubkey > publickey
Постоянство при обходе NAT и файерволов
По умолчанию WireGuard старается быть максимально тихим, когда не используется; это не болтливый протокол. В основном он передаёт данные только тогда, когда пир хочет отправить пакеты. Когда его не просят отправлять пакеты, он перестаёт отправлять их до следующего запроса. В большинстве конфигураций это работает хорошо.
Однако, когда пир находится за NAT или файерволом, он может захотеть получать входящие пакеты, даже если не отправляет их. Поскольку NAT и stateful-файерволы отслеживают “соединения”, если пир за NAT или файерволом хочет получать входящие пакеты, он должен поддерживать актуальность NAT/файервола, периодически отправляя keepalive-пакеты. Это называется постоянные keepalive.
Когда эта опция включена, keepalive-пакет отправляется на конечную точку сервера каждые interval секунд. Разумный интервал, который работает с большим количеством файерволов — 25 секунд. Установка значения 0 отключает функцию (это значение по умолчанию, так как большинству пользователей она не нужна и делает WireGuard немного более болтливым).
Эта функция может быть задана добавлением поля PersistentKeepalive = в конфигурации пира или установкой persistent-keepalive в командной строке:
Если вам не нужна эта функция, не включайте её. Но если вы за NAT или файерволом и хотите получать входящие соединения через долгое время после того, как сетевой трафик затих, эта опция сохранит “соединение” открытым в глазах NAT.
Демо-сервер
После установки WireGuard, если вы хотите попробовать отправить несколько пакетов через WireGuard, вы можете использовать для тестирования скрипт из contrib/ncat-client-server/client.sh:
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0## Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.set -e
[[$UID==0]]||{echo"You must be root to run this.";exit 1;}exec 3<>/dev/tcp/demo.wireguard.com/42912
privatekey="$(wg genkey)"wg pubkey <<<"$privatekey" >&3IFS=: read -r status server_pubkey server_port internal_ip <&3[[$status== OK ]]ip link del dev wg0 2>/dev/null ||trueip link add dev wg0 type wireguard
wg set wg0 private-key <(echo"$privatekey") peer "$server_pubkey" allowed-ips 0.0.0.0/0 endpoint "demo.wireguard.com:$server_port" persistent-keepalive 25ip address add "$internal_ip"/24 dev wg0
ip link set up dev wg0
if["$1"=="default-route"];thenhost="$(wg show wg0 endpoints | sed -n 's/.*\t\(.*\):.*/\1/p')" ip route add $(ip route get $host| sed '/ via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/{s/^\(.* via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/}'| head -n 1) 2>/dev/null ||true ip route add 0/1 dev wg0
ip route add 128/1 dev wg0
fi
Это автоматически настроит интерфейс wg0 через очень небезопасный транспорт, который подходит только для демонстрационных целей. Затем вы можете попробовать загрузить скрытый сайт или отправить пинги:
$ chromium http://192.168.4.1
$ ping 192.168.4.1
Если вы хотите перенаправить ваш интернет-трафик, вы можете запустить его так:
Подключаясь к этому серверу, вы соглашаетесь с тем, что не будете использовать его в злонамеренных или незаконных целях и что ваш трафик может отслеживаться.
Отладочная информация
Если вы используете модуль ядра Linux и ваше ядро поддерживает динамическую отладку, вы можете получить полезный вывод во время выполнения, включив динамическую отладку для модуля:
Настроить автоматическое переподключение и мониторинг
Если у вас возникли проблемы, обратитесь к разделу известных ограничений или посетите IRC-канал #wireguard на Libera.Chat для получения помощи.
4 - wg-quick — руководство
Руководство по wg-quick — утилита для простой настройки интерфейсов WireGuard
ИМЯ
wg-quick — простая настройка интерфейса WireGuard
СИНТАКСИС
wg-quick [ up | down | save | strip ][ CONFIG_FILE | INTERFACE ]
ОПИСАНИЕ
Это чрезвычайно простой скрипт для быстрого поднятия интерфейса WireGuard, подходящий для нескольких распространённых сценариев использования.
Используйте up для добавления и настройки интерфейса, и down для его отключения и удаления. Команда up добавляет интерфейс WireGuard, поднимает его с указанными IP-адресами, настраивает MTU и маршруты, и при необходимости выполняет предварительные/последующие скрипты. Команда down при необходимости сохраняет текущую конфигурацию, удаляет интерфейс WireGuard и при необходимости выполняет предварительные/последующие скрипты. Команда save сохраняет конфигурацию существующего интерфейса без его отключения. Используйте strip для вывода конфигурационного файла без специфичных для wg-quick опций, подходящего для использования с wg(8).
CONFIG_FILE — это конфигурационный файл, имя которого соответствует имени интерфейса с расширением .conf. В противном случае INTERFACE — это имя интерфейса, конфигурация для которого ищется сначала в /etc/wireguard/INTERFACE.conf, а затем в путях поиска, специфичных для дистрибутива.
Вообще говоря, эта утилита представляет собой простой скрипт-обёртку для вызовов wg(8) и ip(8) для настройки интерфейса WireGuard. Она предназначена для пользователей с простыми потребностями. Пользователям с более сложными требованиями настоятельно рекомендуется использовать более специализированные инструменты, более полноценные сетевые менеджеры или просто использовать wg(8) и ip(8) как обычно.
КОНФИГУРАЦИЯ
Конфигурационный файл добавляет несколько дополнительных параметров к формату, используемому wg(8), для настройки дополнительных атрибутов интерфейса. Он обрабатывает значения, которые понимает, а остальные передаёт напрямую wg(8) для дальнейшей обработки.
Инструмент определяет все маршруты из списка разрешённых IP-адресов пиров и автоматически добавляет их в системную таблицу маршрутизации. Если один из этих маршрутов является маршрутом по умолчанию (0.0.0.0/0 или ::/0), он использует ip-rule(8) для обработки переопределения шлюза по умолчанию.
Конфигурационный файл будет передан напрямую подкоманде setconfwg(8), за исключением следующих дополнений в секции [Interface], которые обрабатываются этим инструментом:
Параметры секции Interface
Address — разделённый запятыми список IP-адресов (v4 или v6) (опционально с масками CIDR), которые будут назначены интерфейсу. Может быть указан несколько раз.
DNS — разделённый запятыми список IP-адресов (v4 или v6), которые будут установлены как DNS-серверы интерфейса, или не-IP-имя хоста для установки в качестве доменов поиска DNS. Может быть указан несколько раз. При поднятии интерфейса выполняется resolvconf -a tun.INTERFACE -m 0 -x, а при отключении — resolvconf -d tun.INTERFACE. Если эти вызовы resolvconf(8) нежелательны, вместо них можно использовать ключи PostUp и PostDown ниже.
MTU — если не указано, MTU автоматически определяется из адресов конечных точек или системного маршрута по умолчанию, что обычно является разумным выбором. Однако для ручного указания MTU это значение может быть задано явно.
Table — управляет таблицей маршрутизации, в которую добавляются маршруты. Есть два специальных значения: off полностью отключает создание маршрутов, а auto (по умолчанию) добавляет маршруты в таблицу по умолчанию и включает специальную обработку маршрутов по умолчанию.
PreUp, PostUp, PreDown, PostDown — фрагменты скриптов, которые будут выполнены bash(1) до/после настройки/отключения интерфейса, чаще всего используются для настройки пользовательских DNS-опций или правил файервола. Специальная строка %i заменяется на имя интерфейса. Каждый из них может быть указан несколько раз, в этом случае команды выполняются по порядку.
SaveConfig — если установлено в true, конфигурация сохраняется из текущего состояния интерфейса при его отключении. Любые изменения, внесённые в конфигурационный файл до удаления интерфейса, будут перезаписаны.
Имена интерфейсов
Рекомендуемые имена интерфейсов включают wg0, wgvpn0 или даже wgmgmtlan0. Однако число в конце необязательно, и действительно любое имя в формате [a-zA-Z0-9_=+.-]{1,15} будет работать. Так что даже имена интерфейсов, соответствующие географическим местоположениям, например cincinnati, nyc или paris, подойдут, если это по какой-то причине желательно.
ПРИМЕРЫ
Эти примеры используют тот же синтаксис, что и для wg(8), более полное описание можно найти там. Жирные строки ниже обозначают опции, расширяющие wg(8).
Пример 1: Клиент для VPN-шлюза
Следующий пример может использоваться для подключения в качестве клиента к VPN-шлюзу для туннелирования всего трафика:
Поле Address добавлено для настройки адреса интерфейса. Поле DNS указывает, что DNS-сервер для интерфейса должен быть настроен через resolvconf(8). Запись разрешённых IP-адресов пира подразумевает, что этот интерфейс должен быть настроен как шлюз по умолчанию, что этот скрипт и делает.
Пример 2: “Выключатель” (kill-switch)
Развивая предыдущий пример, можно реализовать так называемый “выключатель” для предотвращения потока незашифрованных пакетов через интерфейсы, отличные от WireGuard, добавив следующие строки PostUp и PreDown в секцию [Interface]:
PostUp=iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECTPreDown=iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
Поля PostUp и PreDown добавлены для указания команды iptables(8), которая при использовании с интерфейсами, имеющими пир с 0.0.0.0/0 в AllowedIPs, работает вместе с использованием fwmark в wg-quick для отбрасывания всех пакетов, которые либо не выходят из туннеля зашифрованными, либо не проходят через сам туннель. (Обратите внимание, что это продолжает пропускать большую часть DHCP-трафика, поскольку большинство DHCP-клиентов используют сокеты PF_PACKET, которые обходят Netfilter.) При использовании IPv6 могут быть добавлены дополнительные аналогичные строки с использованием ip6tables(8).
Пример 3: Хранение ключей в зашифрованном виде
Или, возможно, желательно хранить приватные ключи в зашифрованном виде, например, с использованием pass(1):
PreUp=wg set %i private-key <(pass WireGuard/private-keys/%i)
Пример 4: Сервер с несколькими пирами
Для использования на сервере следующий более сложный пример включает несколько пиров:
Обратите внимание на две строки Address вверху и что SaveConfig установлен в true, указывая, что конфигурационный файл должен быть сохранён при отключении, используя текущее состояние интерфейса.
Пример 5: Политика маршрутизации
Комбинация полей Table, PostUp и PreDown может использоваться для политики маршрутизации. Например, следующее может использоваться для отправки SSH-трафика (TCP-порт 22) через туннель:
wg-quick был написан Джейсоном А. Доненфельдом (Jason A. Donenfeld). Для обновлений и дополнительной информации доступна проектная страница во Всемирной паутине.
КРАТКИЙ СПРАВОЧНИК
Основные команды
Команда
Описание
wg-quick up INTERFACE
Поднять интерфейс
wg-quick down INTERFACE
Остановить интерфейс
wg-quick save INTERFACE
Сохранить конфигурацию
wg-quick strip INTERFACE
Вывести конфигурацию без wg-quick опций
Параметры конфигурации
Параметр
Описание
Пример
Address
IP-адреса интерфейса
10.0.0.1/24
DNS
DNS-серверы
8.8.8.8
MTU
Максимальный размер пакета
1420
Table
Таблица маршрутизации
auto, off, 1234
PreUp
Команда перед поднятием
iptables ...
PostUp
Команда после поднятия
iptables ...
PreDown
Команда перед остановкой
iptables ...
PostDown
Команда после остановки
iptables ...
SaveConfig
Сохранять конфигурацию
true / false
Примечание: Все команды в PreUp, PostUp, PreDown и PostDown выполняются через bash(1), поэтому вы можете использовать все возможности оболочки, включая подстановку команд и переменные.
Предупреждение: Будьте осторожны с использованием SaveConfig = true. Любые изменения, внесённые в конфигурационный файл вручную, могут быть перезаписаны при остановке интерфейса.
5 - wg — руководство
Руководство по wg — утилита для настройки и получения конфигурации интерфейсов WireGuard
ИМЯ
wg — установка и получение конфигурации интерфейсов WireGuard
СИНТАКСИС
wg [ КОМАНДА ][ ОПЦИИ ]... [ АРГУМЕНТЫ ]...
ОПИСАНИЕ
wg — это утилита конфигурации для получения и установки параметров туннельных интерфейсов WireGuard. Сами интерфейсы могут быть добавлены и удалены с помощью ip-link(8), а их IP-адреса и таблицы маршрутизации могут быть настроены с помощью ip-address(8) и ip-route(8). Утилита wg предоставляет серию подкоманд для изменения специфичных для WireGuard аспектов интерфейсов.
Если КОМАНДА не указана, по умолчанию используется show. Подкоманды, принимающие ИНТЕРФЕЙС, должны получать интерфейс WireGuard.
КОМАНДЫ
show
wg show { <interface> | all | interfaces }[public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | persistent-keepalive | transfer | dump]
Показывает текущую конфигурацию WireGuard и информацию о состоянии указанного интерфейса. Если интерфейс не указан, по умолчанию используется all. Если указано interfaces, выводит список всех интерфейсов WireGuard, по одному на строку, и завершает работу. Если после указания интерфейса не указаны опции, выводит список всех атрибутов в удобном для терминала формате. В противном случае выводит указанную информацию, сгруппированную переносами строк и табуляцией, предназначенную для использования в скриптах. При таком выводе, если указано all, первым полем для всех категорий информации является имя интерфейса. Если указано dump, выводится несколько строк; первая содержит в порядке, разделённом табуляцией: private-key, public-key, listen-port, fwmark. Последующие строки выводятся для каждого пира и содержат в порядке, разделённом табуляцией: public-key, preshared-key, endpoint, allowed-ips, latest-handshake, transfer-rx, transfer-tx, persistent-keepalive.
Примеры:
# Показать всю информацию об интерфейсе wg0wg show wg0
# Показать только публичный ключwg show wg0 public-key
# Показать все интерфейсы в формате для скриптовwg show all dump
# Список всех интерфейсовwg show interfaces
wg set <interface> [listen-port <port>][fwmark <fwmark>][private-key <file-path>][peer <base64-public-key> [remove][preshared-key <file-path>][endpoint <ip>:<port>][persistent-keepalive <interval seconds>][allowed-ips [+|-]<ip1>/<cidr1>[,[+|-]<ip2>/<cidr2>]...]]...
Устанавливает значения конфигурации для указанного интерфейса. Может быть указано несколько пиров, и если для пира указан аргумент remove, этот пир удаляется, а не настраивается.
Если listen-port не указан или установлен в 0, порт будет выбран случайно при поднятии интерфейса. И private-key, и preshared-key должны быть файлами, поскольку аргументы командной строки не считаются приватными в большинстве систем, но если вы используете bash(1), вы можете безопасно передать строку, указав в качестве private-key или preshared-key выражение: < (echo PRIVATEKEYSTRING). Если указан /dev/null или другой пустой файл в качестве имени файла для private-key или preshared-key, ключ удаляется из устройства.
Использование preshared-key необязательно и может быть опущено; оно добавляет дополнительный уровень симметричной криптографии к уже существующей криптографии с открытым ключом для устойчивости к квантовым атакам.
Если allowed-ips указан, но значение является пустой строкой, все разрешённые IP-адреса удаляются у пира. По умолчанию allowed-ips заменяет разрешённые IP-адреса пира. Если перед любым из IP-адресов указан + или -, обновление является инкрементальным; IP-адреса с префиксом + или без префикса добавляются к разрешённым IP-адресам пира, если их нет, а IP-адреса с префиксом - удаляются, если присутствуют.
Использование persistent-keepalive необязательно и по умолчанию отключено; установка в 0 или “off” отключает его. В противном случае он представляет собой интервал в секундах от 1 до 65535 включительно, как часто отправлять аутентифицированный пустой пакет пиру для поддержания актуальности stateful-файервола или NAT-маппинга. Например, если интерфейс очень редко отправляет трафик, но может в любое время получать трафик от пира и находится за NAT, интерфейс может выиграть от постоянного интервала keepalive в 25 секунд; однако большинству пользователей это не понадобится.
Использование fwmark необязательно и по умолчанию отключено; установка в 0 или “off” отключает его. В противном случае это 32-битная fwmark для исходящих пакетов, которая может быть указана в шестнадцатеричном формате с префиксом “0x”.
Примеры:
# Установить порт и приватный ключwg set wg0 listen-port 51820 private-key /etc/wireguard/private.key
# Добавить пираwg set wg0 peer xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=\
endpoint 192.95.5.67:1234 \
allowed-ips 10.192.122.3/32
# Удалить пираwg set wg0 peer xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg= remove
# Инкрементальное добавление IP-адресовwg set wg0 peer xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=\
allowed-ips +192.168.1.0/24
setconf
wg setconf <interface> <configuration-filename>
Устанавливает текущую конфигурацию интерфейса в содержимое configuration-filename, которое должно быть в формате, описанном в разделе ФОРМАТ КОНФИГУРАЦИОННОГО ФАЙЛА.
Пример:
wg setconf wg0 /etc/wireguard/wg0.conf
addconf
wg addconf <interface> <configuration-filename>
Добавляет содержимое configuration-filename, которое должно быть в формате, описанном в разделе ФОРМАТ КОНФИГУРАЦИОННОГО ФАЙЛА, к текущей конфигурации интерфейса.
Пример:
wg addconf wg0 /etc/wireguard/peer.conf
syncconf
wg syncconf <interface> <configuration-filename>
Как setconf, но сначала считывает существующую конфигурацию и вносит только те изменения, которые явно отличаются между конфигурационным файлом и интерфейсом. Это гораздо менее эффективно, чем setconf, но имеет преимущество, не нарушая текущие сессии пиров. Содержимое configuration-filename должно быть в формате, описанном в разделе ФОРМАТ КОНФИГУРАЦИОННОГО ФАЙЛА.
Пример:
wg syncconf wg0 /etc/wireguard/wg0.conf
genkey
wg genkey
Генерирует случайный приватный ключ в формате base64 и выводит его в стандартный вывод.
Пример:
wg genkey > private.key
genpsk
wg genpsk
Генерирует случайный предварительный общий ключ в формате base64 и выводит его в стандартный вывод.
Пример:
wg genpsk > preshared.key
pubkey
wg pubkey
Вычисляет публичный ключ и выводит его в формате base64 в стандартный вывод из соответствующего приватного ключа (сгенерированного с помощью genkey), переданного в формате base64 в стандартный ввод.
Пример:
wg pubkey < private.key > public.key
Приватный ключ и соответствующий публичный ключ могут быть сгенерированы одновременно:
umask077wg genkey | tee private.key | wg pubkey > public.key
help
wg help
Показывает сообщение об использовании.
ФОРМАТ КОНФИГУРАЦИОННОГО ФАЙЛА
Формат конфигурационного файла основан на INI. Есть два раздела верхнего уровня — Interface и Peer. Может быть указано несколько разделов Peer, но только один раздел Interface.
Секция Interface
Секция Interface может содержать следующие поля:
Поле
Описание
Обязательность
PrivateKey
Приватный ключ в формате base64, сгенерированный wg genkey
Обязательно
ListenPort
16-битный порт для прослушивания. Необязательно; если не указан, выбирается случайно
Опционально
FwMark
32-битная fwmark для исходящих пакетов. Если установлено в 0 или “off”, опция отключена. Может быть указана в шестнадцатеричном формате с префиксом “0x”
Опционально
Секция Peer
Секции Peer могут содержать следующие поля:
Поле
Описание
Обязательность
PublicKey
Публичный ключ в формате base64, вычисленный wg pubkey из приватного ключа и обычно передаваемый вне полосы автору конфигурационного файла
Обязательно
PresharedKey
Предварительный общий ключ в формате base64, сгенерированный wg genpsk. Необязательно, может быть опущено. Добавляет дополнительный уровень симметричной криптографии для устойчивости к квантовым атакам
Опционально
AllowedIPs
Разделённый запятыми список IP-адресов (v4 или v6) с масками CIDR, с которых разрешён входящий трафик для этого пира и на который направляется исходящий трафик. Может быть указан несколько раз
Обязательно
Endpoint
Конечная точка — IP-адрес или имя хоста, за которым следует двоеточие и номер порта. Эта конечная точка будет автоматически обновляться до последнего IP-адреса и порта источника правильно аутентифицированных пакетов от пира
Опционально
PersistentKeepalive
Интервал в секундах от 1 до 65535 включительно, как часто отправлять аутентифицированный пустой пакет пиру для поддержания stateful-файервола или NAT-маппинга. Если установлено в 0 или “off”, опция отключена
Опционально
Пример конфигурационного файла
Этот пример может использоваться как модель для написания конфигурационных файлов, следуя INI-подобному синтаксису. Символы после и включая # считаются комментариями и игнорируются.
Примечание: Для IPv6-адресов в поле Endpoint используется квадратные скобки: [2607:5300:60:6b0::c05f:543]:2468
ОТЛАДОЧНАЯ ИНФОРМАЦИЯ
Иногда полезно иметь информацию о текущем состоянии туннеля.
Linux (модуль ядра)
При использовании модуля ядра Linux на ядре, поддерживающем динамическую отладку, отладочная информация может быть записана в dmesg(1) запуском от root:
На OpenBSD и FreeBSD отладочная информация может быть записана в dmesg(1) для конкретного интерфейса с помощью ifconfig(1):
# ifconfig wg0 debug
Пользовательские реализации
В пользовательских реализациях принято устанавливать переменную окружения LOG_LEVEL в verbose:
exportLOG_LEVEL=verbose
ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ
WG_COLOR_MODE
Управляет цветным выводом:
always — всегда выводить ANSI-цвета
never — никогда не выводить ANSI-цвета
auto (или не установлено) — выводить цвета только при записи в TTY
Пример:
exportWG_COLOR_MODE=never
WG_HIDE_KEYS
Управляет отображением ключей:
never — показывать приватные и предварительные общие ключи
always (или не установлено) — показывать ключи как “(hidden)”
Пример:
exportWG_HIDE_KEYS=never
WG_ENDPOINT_RESOLUTION_RETRIES
Управляет повторными попытками разрешения DNS:
Если установлено целое число или infinity, разрешение DNS для конечной точки каждого пира будет повторяться указанное количество раз для непостоянных ошибок с увеличивающейся задержкой между попытками
wg был написан Джейсоном А. Доненфельдом (Jason A. Donenfeld). Для обновлений и дополнительной информации доступна проектная страница во Всемирной паутине.
6 - Компиляция из исходного кода
Пошаговое руководство по компиляции WireGuard из исходного кода для Ubuntu и Fedora
Компиляция модуля ядра из исходного кода
В этом руководстве вы узнаете, как скомпилировать WireGuard из исходного кода для Ubuntu и Fedora. Это может быть полезно, если вы используете нестандартное ядро, хотите получить самую свежую версию или если ваш дистрибутив не предоставляет готовые пакеты.
Требования: Вам понадобится gcc ≥ 4.7 и заголовочные файлы вашего ядра, расположенные в правильном месте, для успешной компиляции.
WireGuard требует Linux ≥ 3.10 со следующими опциями конфигурации, которые, вероятно, уже настроены в вашем ядре:
Опция
Описание
CONFIG_NET
Базовая поддержка сети
CONFIG_INET
Базовая поддержка IP
CONFIG_NET_UDP_TUNNEL
Отправка и получение UDP-пакетов
CONFIG_CRYPTO_ALGAPI
Криптографический API для crypto_xor
CONFIG_CRYPTO_MANAGER
Менеджер криптографических алгоритмов
Некоторые, но не все, из этих опций напрямую соответствуют записям в menuconfig. Для включения этих опций выберите следующие пункты в menuconfig:
[*] Networking support (NET) -->
Networking options -->
[*] TCP/IP networking (INET)
[*] IP: Foo (IP protocols) over UDP (NET_FOU)
[*] Cryptographic API (CRYPTO) -->
[*] Cryptographic algorithm manager (CRYPTO_MANAGER)
При сборке как внешнего модуля, вероятно, также потребуется установить CONFIG_UNUSED_SYMBOLS.
Сборка напрямую в дереве ядра
Если вы хотите собрать WireGuard как модуль или встроенный напрямую из дерева ядра, вы можете использовать скрипт create-patch.sh, который создаёт патч для добавления WireGuard непосредственно в дерево, или скрипт jury-rig.sh, который связывает исходный каталог WireGuard с деревом ядра:
Метод 1: Использование патча
cd /usr/src/linux
~/wireguard-linux-compat/kernel-tree-scripts/create-patch.sh | patch -p1
Подробное описание протокола WireGuard, криптографических примитивов и обмена ключами
Более подробная информация доступна в техническом документе. Для краткого ознакомления читайте далее.
Криптографические примитивы
В WireGuard используются следующие протоколы и примитивы:
ChaCha20 для симметричного шифрования, аутентифицированного с помощью Poly1305, с использованием AEAD-конструкции из RFC7539
Curve25519 для ECDH (обмена ключами по эллиптическим кривым)
BLAKE2s для хеширования и ключевого хеширования, описанный в RFC7693
SipHash24 для ключей хеш-таблиц
HKDF для производной ключей, как описано в RFC5869
Эти примитивы выбраны за их высокую производительность и криптографическую стойкость. Например, ChaCha20-Poly1305 обеспечивает скорость шифрования, сравнимую с AES, но без аппаратного ускорения, что делает его идеальным для встраиваемых устройств. Curve25519 считается одним из самых безопасных и быстрых алгоритмов для обмена ключами.
Протокол без установки соединения
Любой безопасный протокол требует сохранения некоторого состояния, поэтому существует начальное очень простое рукопожатие, которое устанавливает симметричные ключи для передачи данных. Это рукопожатие происходит каждые несколько минут для обеспечения вращения ключей и совершенной прямой секретности (Perfect Forward Secrecy — PFS). PFS означает, что даже если долговременный приватный ключ будет скомпрометирован, это не позволит расшифровать ранее перехваченные сессии.
Рукопожатие выполняется на основе времени, а не содержимого предыдущих пакетов, поскольку спроектировано для корректной обработки потери пакетов. Это важно, потому что в сетях с высокой загрузкой или нестабильным соединением пакеты могут теряться, и протокол должен продолжать работать без сбоев.
Используется умный механизм пульса (pulse mechanism), чтобы гарантировать, что последние ключи и рукопожатия актуальны, с повторным согласованием при необходимости. Этот механизм автоматически обнаруживает, когда рукопожатия устарели, и инициирует новые. Используется отдельная очередь пакетов на хост, чтобы минимизировать потерю пакетов во время рукопожатий, обеспечивая стабильную производительность для всех клиентов.
Другими словами, вы поднимаете устройство, и всё остальное обрабатывается автоматически. Вам не нужно беспокоиться о переподключении, отключении или переинициализации. WireGuard сам управляет всем жизненным циклом соединения.
Таймеры протокола
В протоколе используются следующие таймеры:
REKEY_TIMEOUT — повторная попытка рукопожатия выполняется через REKEY_TIMEOUT + jitter мс, если ответ не был получен, где jitter — случайное значение от 0 до 333 мс. Случайная задержка предотвращает синхронизацию попыток от множества клиентов.
KEEPALIVE — если пакет был получен от данного пира, но мы не отправили ему пакет в течение KEEPALIVE мс, мы отправляем пустой пакет. Это поддерживает NAT-маппинг активным.
Если мы отправили пакет данному пиру, но не получили пакет от него в течение KEEPALIVE + REKEY_TIMEOUT мс, мы инициируем новое рукопожатие.
Все эфемерные приватные ключи и симметричные сессионные ключи обнуляются после REJECT_AFTER_TIME * 3 мс, если новые ключи не были обменены. Это гарантирует, что старые ключи не будут случайно сохранены в памяти.
После отправки пакета, если количество отправленных пакетов с использованием этого ключа превышает REKEY_AFTER_MESSAGES, мы инициируем новое рукопожатие. Это ограничивает количество данных, зашифрованных одним ключом.
После отправки пакета, если отправитель был первоначальным инициатором рукопожатия и текущий сессионный ключ старше REKEY_AFTER_TIME мс, мы инициируем новое рукопожатие. Если отправитель был первоначальным ответчиком, мы не инициируем новое рукопожатие после REKEY_AFTER_TIME мс, как это делает инициатор.
После получения пакета, если получатель был первоначальным инициатором рукопожатия и текущий сессионный ключ старше REKEY_AFTER_TIME - KEEPALIVE_TIMEOUT - REKEY_TIMEOUT мс, мы инициируем новое рукопожатие.
Рукопожатия инициируются только один раз в REKEY_TIMEOUT мс, с применением строгого ограничения скорости.
Пакеты отбрасываются, если счётчик сессии больше REJECT_AFTER_MESSAGES или если его ключ старше REJECT_AFTER_TIME мс.
После REKEY_ATTEMPT_TIME мс попыток инициировать новое рукопожатие, попытки прекращаются, и очищаются все существующие пакеты, поставленные в очередь для отправки. Если пакет явно поставлен в очередь для отправки, этот таймер сбрасывается.
В будущем планируется настроить REKEY_TIMEOUT для использования экспоненциальной задержки (exponential back-off), что повысит эффективность при больших нагрузках.
Ключевое подтверждение
После завершения рукопожатия, с сообщением от инициатора к ответчику и затем обратно от ответчика к инициатору, инициатор может отправлять зашифрованные пакеты сессии, но ответчик не может. Ответчик обязан дождаться получения зашифрованного пакета сессии от инициатора для подтверждения ключа. Это требование обеспечивает подтверждение того, что инициатор действительно получил ключи и может использовать их.
До получения первого пакета с новым ключом ответчик должен либо поставить пакеты в очередь для отправки позже, либо использовать предыдущую сессию, если она существует и действительна. Поэтому после получения ответа от ответчика, если у инициатора нет немедленно готовых к отправке пакетов данных, он должен отправить пустой пакет для подтверждения ключа.
Пример: Представьте, что клиент устанавливает VPN-соединение с сервером. Клиент отправляет рукопожатие, сервер отвечает, и после этого клиент отправляет первый зашифрованный пакет данных. Только после получения этого пакета сервер начинает использовать новый ключ для ответов. Если клиенту нечего отправлять сразу, он отправляет пустой пакет, чтобы сервер знал, что ключ подтверждён.
Обмен ключами и пакеты данных
WireGuard использует рукопожатие Noise_IK из Noise Protocol Framework, основанное на работах CurveCP, NaCL, KEA+, SIGMA, FHMQV и HOMQV. Все эти протоколы внесли вклад в создание надёжного и безопасного механизма обмена ключами. Все пакеты передаются через UDP, что обеспечивает низкую задержку и высокую производительность.
Свойства обмена ключами
Обмен ключами обладает следующими свойствами:
Избегает компрометации ключей с подменой личности — даже если злоумышленник получит доступ к долговременному ключу одной стороны, он не сможет выдать себя за другую сторону.
Избегает атак повторного воспроизведения — каждый пакет содержит уникальные данные, предотвращающие его повторное использование.
Обеспечивает совершенную прямую секретность (PFS) — компрометация долговременных ключей не раскрывает содержимое прошлых сессий.
Достигает “AKE Security” — обеспечивает безопасность аутентифицированного обмена ключами в соответствии с современными криптографическими стандартами.
Обеспечивает скрытие идентичности — информация о сторонах, участвующих в обмене, защищена от пассивного наблюдения.
Предварительный общий ключ (PSK)
Если требуется дополнительный уровень симметричной криптографии (например, для устойчивости к квантовым атакам), WireGuard поддерживает опциональный предварительный общий ключ (Pre-Shared Key), который смешивается с криптографией с открытым ключом. Это обеспечивает дополнительный уровень защиты: даже если кто-то сможет взломать асимметричную криптографию с помощью квантового компьютера, PSK останется барьером.
Когда режим PSK не используется, значение предварительного общего ключа считается строкой из 32 нулевых байт. Таким образом, протокол всегда работает одинаково, независимо от использования PSK.
Используемые функции
В протоколе используются следующие криптографические функции:
DH(private key, public key) — умножение точек Curve25519, возвращает 32 байта общего секрета. Это основной механизм обмена ключами.
DH_GENERATE() — генерация случайного приватного ключа Curve25519 (32 байта). Каждый раз генерируется новый ключ для обеспечения PFS.
RAND(len) — возвращает len случайных байт. Используется для генерации nonce и других случайных значений.
AEAD(key, counter, plain text, auth text) — ChaCha20Poly1305 AEAD, как указано в RFC7539, с nonce, состоящим из 32 бит нулей, за которыми следует 64-битное значение counter в little-endian. Обеспечивает одновременно шифрование и аутентификацию.
XAEAD(key, nonce, plain text, auth text) — XChaCha20Poly1305 AEAD со случайным 24-байтовым nonce. Используется для cookie-пакетов.
AEAD_LEN(plain len) — plain len + 16, где 16 байт — размер тега аутентификации.
HMAC(key, input) — HMAC-Blake2s(key, input, 32), возвращает 32 байта. Используется для создания ключей.
MAC(key, input) — Keyed-Blake2s(key, input, 16), возвращает 16 байт. Используется для создания MAC (Message Authentication Code).
HASH(input) — Blake2s(input, 32), возвращает 32 байта. Используется для хеширования.
TAI64N() — TAI64N-метка текущего времени (12 байт). Используется для защиты от атак повторного воспроизведения.
CONSTRUCTION — UTF-8 значение Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s (37 байт). Идентифицирует используемую конструкцию Noise.
LABEL_MAC1 — UTF-8 значение mac1---- (8 байт). Используется для маркировки MAC1.
LABEL_COOKIE — UTF-8 значение cookie-- (8 байт). Используется для маркировки cookie.
Структура пакетов
Первое сообщение: Инициатор → Ответчик
Инициатор отправляет это сообщение:
msg = handshake_initiation {
u8 message_type // Всегда 1
u8 reserved_zero[3] // Зарезервировано для будущего использования
u32 sender_index // Индекс отправителя
u8 unencrypted_ephemeral[32] // Эфемерный публичный ключ (не зашифрован)
u8 encrypted_static[AEAD_LEN(32)] // Зашифрованный статический публичный ключ
u8 encrypted_timestamp[AEAD_LEN(12)] // Зашифрованная метка времени
u8 mac1[16] // Первый MAC
u8 mac2[16] // Второй MAC (cookie)
}
Пример: Клиент (инициатор) отправляет это сообщение серверу (ответчику), чтобы начать установку VPN-соединения. Поле unencrypted_ephemeral содержит эфемерный публичный ключ, который не шифруется, чтобы сервер мог его использовать для вычисления общего секрета. Поля encrypted_static и encrypted_timestamp зашифрованы и содержат долговременный публичный ключ клиента и текущее время для защиты от повторных атак.
Второе сообщение: Ответчик → Инициатор
Ответчик отправляет это сообщение после обработки первого сообщения:
msg = handshake_response {
u8 message_type // Всегда 2
u8 reserved_zero[3] // Зарезервировано
u32 sender_index // Индекс отправителя
u32 receiver_index // Индекс получателя (из первого сообщения)
u8 unencrypted_ephemeral[32] // Эфемерный публичный ключ
u8 encrypted_nothing[AEAD_LEN(0)] // Зашифрованное пустое сообщение
u8 mac1[16] // Первый MAC
u8 mac2[16] // Второй MAC (cookie)
}
Пример: Сервер (ответчик) отвечает клиенту своим эфемерным публичным ключом. Поле receiver_index содержит индекс, полученный от клиента, что позволяет клиенту сопоставить ответ с правильной сессией. Зашифрованное пустое сообщение (encrypted_nothing) служит для подтверждения того, что сервер правильно вычислил ключи.
Пакеты данных
После завершения рукопожатия инициатор и ответчик обмениваются пакетами данных:
Пример: После установления ключей клиент отправляет серверу зашифрованный IP-пакет. Поле counter увеличивается с каждым пакетом, предотвращая повторное использование nonce. Сами данные (encrypted_encapsulated_packet) содержат зашифрованный IP-пакет, который сервер расшифровывает и передаёт в сеть.
Производные ключей данных
После обмена двумя сообщениями ключи вычисляются инициатором и ответчиком для отправки и получения данных:
Затем все предыдущие цепочки ключей, эфемерные ключи и хеши обнуляются. Это гарантирует, что даже если злоумышленник получит доступ к памяти, он не сможет восстановить предыдущие ключи.
Объяснение: Процесс использует HKDF (HMAC-based Key Derivation Function) для создания независимых ключей для каждого направления. temp1 — это начальный ключевой материал, из которого через серию HMAC-операций создаются sending_key (ключ для отправки) и receiving_key (ключ для приёма). Счётчики инициализируются нулём и увеличиваются с каждым пакетом.
Защита от DoS-атак
WireGuard требует аутентификации в первом сообщении рукопожатия, что не требует выделения состояния на сервере для потенциально неаутентифицированных сообщений. Это критически важно для защиты от DoS-атак: злоумышленник не может заставить сервер выделять ресурсы для каждого поддельного пакета.
Сервер вообще не отвечает неавторизованному клиенту; он молчит и невидим. Это делает WireGuard устойчивым к сканированию портов и DoS-атакам.
Защита от атак повторного воспроизведения
Однако это создаёт проблему: аутентификация в первом пакете всегда уязвима для атак повторного воспроизведения. Злоумышленник может воспроизвести начальные сообщения рукопожатия, чтобы заставить сервер перегенерировать свой эфемерный ключ, тем самым разорвав соединение легитимного клиента (хотя это не влияет на безопасность сообщений).
Для защиты используется TAI64N-метка времени в первом сообщении. Сервер отслеживает наибольшую полученную метку времени для каждого клиента и отбрасывает пакеты с метками, меньшими или равными предыдущей.
Пример: Клиент отправляет рукопожатие с меткой времени 2024-01-01 12:00:00.000. Сервер запоминает эту метку. Если злоумышленник попытается воспроизвести это же рукопожатие через час, сервер отбросит его, так как метка времени меньше или равна уже полученной.
Если сервер перезапускается и теряет это состояние, это не проблема: более ранний пакет может быть воспроизведён, но он не может нарушить текущие сессии, так как сервер только что перезапустился. После переподключения клиенты будут использовать более новые метки времени, делая предыдущие недействительными.
Cookie-пакеты
Вычисление функции DH() требует значительных вычислительных ресурсов. Для защиты от атак на исчерпание CPU сервер может не обрабатывать сообщения рукопожатия, а вместо этого отвечать cookie-пакетом, если он находится под нагрузкой.
Чтобы сервер оставался молчаливым, если не получает действительный пакет, все сообщения должны содержать MAC, который комбинирует публичный ключ получателя и опционально PSK в качестве ключа MAC. Когда сервер находится под нагрузкой, он принимает только пакеты, которые дополнительно имеют второй MAC предыдущих байтов сообщения, использующий cookie в качестве ключа MAC.
Cookie истекают через две минуты и представляют собой MAC IP-адреса отправителя с использованием меняющегося (каждые две минуты) секрета сервера в качестве ключа MAC. Это позволяет доказать владение IP-адресом, который затем может быть правильно ограничен по скорости.
Сервер, вычислив эти MAC и сравнив их с полученными в сообщении, должен отклонять сообщения с недействительным msg.mac1 и при нагрузке — сообщения с недействительным msg.mac2.
Cookie-пакет
Как упоминалось выше, когда получено сообщение с действительным msg.mac1, но msg.mac2 равен нулю или недействителен, и сервер находится под нагрузкой, сервер может отправить cookie-пакет:
Пример: Злоумышленник пытается инициировать множество рукопожатий с сервера под нагрузкой. Сервер не выполняет дорогостоящие вычисления DH для каждого, а вместо этого отправляет cookie-пакет. Клиент должен расшифровать cookie и отправить его обратно в следующем рукопожатии, доказывая, что он может получить пакеты по указанному IP-адресу.
Защита от повторного использования nonce
Nonce никогда не повторяются. Используется 64-битный счётчик, который не может быть уменьшен. Это означает, что каждый пакет имеет уникальный nonce, что критически важно для безопасности ChaCha20-Poly1305.
UDP иногда доставляет сообщения в неправильном порядке. Для этого используется скользящее окно, в котором отслеживается наибольший полученный счётчик и окно примерно из 2000 предыдущих значений, проверяемых после верификации тега аутентификации. Это позволяет принимать пакеты, пришедшие не по порядку, но не позволяет повторно использовать уже принятые nonce.
Пример: Клиент отправляет пакеты с счётчиками 1, 2, 3, 4, 5. Пакет 4 приходит первым, затем пакет 2, затем пакет 5. Скользящее окно позволяет принять все эти пакеты, так как их счётчики находятся в допустимом диапазоне. Если злоумышленник попытается повторно отправить пакет с счётчиком 2, он будет отброшен, так как этот счётчик уже был принят.
DiffServ
При работе с IP-пакетами WireGuard учитывает следующие аспекты DiffServ:
DSCP рукопожатий: 0x88 (AF41) — эти пакеты имеют наивысший приоритет и наименьшую вероятность отбрасывания, так как они необходимы для управления туннелем. Это гарантирует, что даже при перегрузке сети рукопожатия будут доставлены.
DSCP данных: 0 — значение DSCP из внутреннего пакета никогда не копируется во внешний пакет. Это предотвращает утечку информации о типе трафика, проходящего через зашифрованный туннель.
ECN (Explicit Congestion Notification): Бит ECN копируется между внутренним и внешним пакетами в соответствии с логикой, описанной в RFC6040. Это позволяет сохранять информацию о перегрузке сети внутри туннеля.
Пример: Если внутри туннеля передаётся VoIP-трафик с высоким приоритетом, внешние пакеты WireGuard не будут показывать этот приоритет. Внешний наблюдатель увидит только обычные UDP-пакеты без информации о их содержимом.
Схема протокола
sequenceDiagram
participant I as Инициатор
participant R as Ответчик
Note over I,R: 1. Инициация рукопожатия
I->>R: message_type=1, ephemeral, encrypted_static, timestamp
Note over R: Проверка MAC, дешифрование<br/>Генерация ephemeral ключа
Note over I,R: 2. Ответ
R->>I: message_type=2, ephemeral, encrypted_nothing
Note over I: Проверка, дешифрование<br/>Производная ключей
Note over I,R: 3. Передача данных
I->>R: message_type=4, encrypted_data
Note over R: Дешифрование данными ключами
Формальная верификация протокола WireGuard, криптографии и реализации
Формальная верификация
WireGuard прошёл все виды формальной верификации, охватывающие аспекты криптографии, протокола и реализации. Ниже приведены подробности различных усилий по верификации.
Что такое формальная верификация? Это процесс математического доказательства того, что система соответствует своей спецификации и свободна от определённых классов ошибок. В контексте криптографических протоколов это означает доказательство того, что протокол безопасен против различных моделей атакующего.
Символическая верификация протокола с использованием Tamarin
Протокол WireGuard, описанный в техническом документе и основанный на Noise, был формально верифицирован в символической модели с использованием Tamarin. Это означает, что существует математическое доказательство безопасности протокола WireGuard.
Что такое символическая верификация? Символическая верификация рассматривает криптографические примитивы как “чёрные ящики” с идеальными свойствами. Она проверяет, что протокол логически корректен, при условии, что используемые криптографические примитивы безопасны. Это позволяет обнаруживать логические ошибки в протоколе, такие как неправильная последовательность сообщений или утечка информации.
Протокол был верифицирован на наличие следующих свойств безопасности:
Свойство
Описание
Значение
Корректность
Протокол всегда завершается успешно при правильном выполнении
Гарантирует, что при отсутствии атак протокол работает как ожидается
Надёжное согласование ключей и аутентичность
Стороны уверены, что обмениваются ключами именно с той стороной, с которой намеревались
Предотвращает атаки “человек посередине” (MITM)
Устойчивость к компрометации ключей с подменой личности
Даже если долговременный ключ скомпрометирован, атакующий не может выдать себя за другую сторону
Защищает от ситуаций, когда злоумышленник получает доступ к приватному ключу
Устойчивость к атакам неизвестного общего ключа
Сторона не может быть обманута, чтобы использовать ключ, которого она не ожидает
Предотвращает ситуации, когда атакующий заставляет две стороны использовать разные ключи
Секретность ключа
Ключ остаётся известным только участвующим сторонам
Базовая гарантия конфиденциальности
Совершенная прямая секретность
Компрометация долговременных ключей не раскрывает содержимое прошлых сессий
Защищает прошлый трафик даже при компрометации ключей в будущем
Уникальность сессии
Каждая сессия уникальна и не может быть спутана с другой
Предотвращает смешивание сессий
Скрытие идентичности
Личности сторон защищены от пассивного наблюдения
Злоумышленник не может узнать, кто общается с кем
Это совместная работа Джейсона Доненфельда (Jason Donenfeld) и Кевина Милнера (Kevin Milner).
Результаты широко обсуждаются в документе по формальной верификации WireGuard. Этот документ является черновиком, но основные результаты уже представлены.
Maude — система для написания и выполнения спецификаций
Пример использования: Исследователь может запустить модель Tamarin для проверки нового свойства безопасности или для подтверждения существующих результатов. Модель генерирует доказательства, которые могут быть проверены математически.
Вычислительное доказательство протокола в модели eCK
При попытке построить вычислительное доказательство WireGuard в модели eCK (extended Canetti-Krawczyk) оказалось, что протокол WireGuard не вписывается аккуратно в традиционную модель eCK, потому что сообщение подтверждения ключа является частью транспортного уровня. Это техническая деталь, связанная с тем, как моделируются этапы протокола.
Что такое модель eCK? Это одна из самых сильных моделей безопасности для протоколов согласования ключей. Она учитывает широкий спектр атак, включая компрометацию эфемерных и долговременных ключей в различных комбинациях.
Поэтому в этом документе доказывается вариант протокола WireGuard, который морально эквивалентен реальному протоколу, давая очень сильный результат.
Что такое “морально эквивалентный”? Это означает, что вариант протокола имеет ту же логическую структуру и свойства безопасности, но адаптирован для соответствия формальным требованиям модели eCK. Доказательство для варианта применимо и к реальному протоколу.
Это совместная работа Бенджамина Даулинга (Benjamin Dowling) и Кеннета Г. Патерсона (Kenneth G. Paterson).
Вычислительное доказательство протокола в модели ACCE
Эта диссертация строит механизированное криптографическое доказательство всего протокола WireGuard, включая сообщения транспортных данных, в вычислительной модели, подобной ACCE (Authenticated and Confidential Channel Establishment), с использованием CryptoVerif.
Что такое модель ACCE? Это модель для протоколов, которые устанавливают аутентифицированные и конфиденциальные каналы. Она учитывает как установление ключей, так и последующую передачу данных, что делает её особенно подходящей для WireGuard.
Что такое CryptoVerif? Это инструмент для автоматического доказательства безопасности криптографических протоколов в вычислительной модели. В отличие от символической верификации, он учитывает реальные криптографические примитивы и их математические свойства.
Доказанные свойства:
Свойство
Описание
Корректность
Протокол работает как ожидается
Секретность сообщений
Сообщения не могут быть прочитаны атакующим
Совершенная прямая секретность
Прошлые сообщения защищены при компрометации ключей
Взаимная аутентификация
Обе стороны уверены в личности друг друга
Устойчивость к компрометации ключей с подменой личности
Защита при компрометации ключей
Устойчивость к атакам неизвестного общего ключа
Защита от смешивания ключей
Устойчивость к повторному воспроизведению первого сообщения протокола
Первое сообщение не может быть повторно использовано
Символическая верификация протокола с использованием ProVerif
Проект Noise Explorer стремится формально верифицировать все шаблоны протокола Noise путём генерации моделей ProVerif.
Что такое ProVerif? Это популярный инструмент для автоматической верификации криптографических протоколов в символической модели. Он может автоматически находить атаки или доказывать свойства безопасности.
Модель, относящаяся к WireGuard, — это модель IK от Noise Explorer, которая может быть подключена к ProVerif для генерации доказательств различных свойств.
Что такое шаблон IK в Noise? Это конкретный шаблон рукопожатия, используемый WireGuard. Он определяет, в каком порядке отправляются ключи и как вычисляются общие секреты.
Это работа Надима Кобейсси (Nadim Kobeissi) и Картикеяна Баргавана (Karthikeyan Bhargavan).
WireGuard использует 64-битную реализацию умножения скаляра Curve25519 из HACL*. Кривая специфицирована в F*, что позволяет доказывать свойства стратегий реализации. KreMLin преобразует её в верифицированный C-код.
Что такое HACL?* Это библиотека криптографических примитивов, написанных на F* и верифицированных по безопасности и корректности. Она обеспечивает математические гарантии того, что реализация Curve25519 свободна от ошибок и уязвимостей.
Что такое F?* Это функциональный язык программирования с зависимыми типами, который позволяет выражать и доказывать свойства программ. Он используется для написания спецификаций криптографических примитивов.
Что такое KreMLin? Это компилятор, который преобразует код F* в верифицированный C-код, сохраняя все доказательства свойств.
HACL* — совместная работа Жана Карима Зинзиндууэ (Jean Karim Zinzindohoué), Картикеяна Баргавана (Karthikeyan Bhargavan), Джонатана Протценко (Jonathan Protzenko) и Бенджамина Беурдуше (Benjamin Beurdouche).
WireGuard также использует 32-битную реализацию умножения скаляра Curve25519 из Fiat-Crypto. Кривая специфицирована в Coq, что позволяет доказывать свойства стратегий реализации и генерировать верифицированный C-код.
Что такое Fiat-Crypto? Это проект по генерации верифицированного криптографического кода из формальных спецификаций в Coq. Он автоматически генерирует реализации, которые математически доказано корректны.
Что такое Coq? Это интерактивная система доказательства теорем, которая позволяет писать математические спецификации и доказывать их свойства. Она широко используется для формальной верификации программного обеспечения.
Преимущество 32-битной реализации: Она оптимизирована для устройств с 32-битными процессорами, таких как старые смартфоны или встраиваемые системы, что делает WireGuard доступным для более широкого спектра устройств.
Fiat-Crypto — совместная работа Андреса Эрбсена (Andres Erbsen), Джейд Филипум (Jade Philipoom), Джейсона Гросса (Jason Gross), Роберта Слоана (Robert Sloan) и Адама Члипала (Adam Chlipala).
Формальная верификация обеспечивает уровень уверенности, который невозможно достичь только тестированием или экспертной оценкой. Вот почему это критически важно для WireGuard:
Математические гарантии — Верификация даёт математическое доказательство того, что протокол безопасен против определённых классов атак. Это не просто “вероятно безопасно” — это “доказано безопасно”.
Обнаружение тонких ошибок — Некоторые уязвимости могут быть обнаружены только формальными методами. Например, атаки на протоколы, которые полагаются на тонкие логические ошибки.
Независимая проверка — Модели с открытым исходным кодом позволяют любому исследователю независимо проверить результаты, что повышает доверие к протоколу.
Документирование предположений — Верификация явно документирует предположения безопасности, на которых основан протокол, что помогает при его использовании и анализе.
Устойчивость к будущим атакам — Доказательства безопасности в различных моделях (символической, вычислительной) дают уверенность, что протокол устойчив к широкому спектру атакующих стратегий.
Обзор уровней верификации
graph TD
A[Формальная верификация WireGuard] --> B[Символическая верификация]
A --> C[Вычислительная верификация]
A --> D[Верификация реализации]
B --> B1[Tamarin]
B --> B2[ProVerif]
C --> C1[eCK модель]
C --> C2[ACCE модель]
D --> D1[HACL* 64-bit]
D --> D2[Fiat-Crypto 32-bit]
style A fill:#88171a,color:#fff
style B fill:#2c3e50,color:#fff
style C fill:#2c3e50,color:#fff
style D fill:#2c3e50,color:#fff
Описание пользовательской реализации WireGuard для кросс-платформенной работы и интерфейса конфигурации
Кроссплатформенная пользовательская реализация
Хотя WireGuard изначально был разработан для ядра Linux для максимальной производительности, он может работать в пользовательском пространстве с использованием отдельной реализации. В настоящее время wireguard-go вполне функционален, а wireguard-rs находится в разработке.
Что такое пользовательская реализация? В отличие от реализации в ядре, которая работает на уровне операционной системы, пользовательская реализация работает как обычное приложение. Это позволяет запускать WireGuard на платформах, где нет доступа к ядру, или когда требуется более простая интеграция с приложениями.
В любой момент документации, когда вы видите ip link add wg0 type wireguard, вы можете вместо этого написать wireguard-go wg0. Всё остальное должно быть идентичным.
Пример: На Windows или macOS, где нет прямой поддержки в ядре, вы запускаете wireguard-go wg0, и он создаёт виртуальный сетевой интерфейс, эмулируя поведение ядерной версии.
Чтобы предотвратить фрагментацию и обеспечить совместимость, все пользовательские реализации должны соответствовать одному и тому же протоколу и спецификации, имея точно такое же поведение, как и оригинальная реализация для ядра Linux. Кроме того, они должны соответствовать следующему интерфейсу конфигурации.
Интерфейс командной строки
Пользовательская реализация должна иметь следующий очень ограниченный интерфейс командной строки:
# userspace-wg [-f/--foreground] INTERFACE-NAME
Объяснение параметров:
-f или --foreground — опциональный флаг, который заставляет процесс оставаться на переднем плане (не демонизироваться). Это полезно для отладки и при запуске из системных менеджеров.
INTERFACE-NAME — имя создаваемого интерфейса (например, wg0).
Например, реализация на Go будет вызываться следующим образом для создания интерфейса wg0:
# wireguard-go wg0
Что происходит при запуске? Выполнение этой команды создаёт виртуальное TUN-устройство с именем wg0 и затем демонизируется (переходит в фоновый режим). После успешной демонизации и поднятия интерфейса создаётся /var/run/wireguard/wg0.sock (или /run/wireguard/wg0.sock в зависимости от платформы) — это UNIX-сокет домена, работающий в потоковом режиме.
Что такое TUN-устройство? Это виртуальный сетевой интерфейс, который работает на уровне IP. Приложения могут читать и писать в него IP-пакеты, как если бы они работали с обычным сетевым интерфейсом. WireGuard использует TUN для получения и отправки зашифрованных пакетов.
Что такое UNIX-сокет? Это механизм межпроцессного взаимодействия (IPC), который позволяет процессам обмениваться данными. В данном случае он используется для того, чтобы утилита wg(8) могла управлять процессом WireGuard.
На Windows используются те же семантики с двунаправленным именованным каналом (named pipe) в \\.\pipe\WireGuard\wg0. Именованные каналы — это аналог UNIX-сокетов в Windows.
Использование wg(8) для конфигурации
Инструмент wg(8) используется для настройки интерфейса, что обеспечивает полное единообразие интерфейсов конфигурации во всех реализациях.
Почему это важно? Пользователи и скрипты могут использовать одни и те же команды для управления WireGuard независимо от того, работает ли он в ядре или в пользовательском пространстве. Это значительно упрощает автоматизацию и написание документации.
Инструмент wg(8) ищет интерфейсы в /var/run/wireguard/*.sock (или /run/wireguard/*.sock). Пользовательские реализации должны корректно завершать работу в ответ на SIGINT/SIGTERM, удаление TUN-интерфейса или удаление файла UNIX-сокета.
Как это работает:
Вы запускаете wireguard-go wg0 — создаётся процесс и сокет
Вы запускаете wg setconf wg0 config.conf — утилита wg(8) находит сокет и отправляет команду
Процесс WireGuard получает команду через сокет и применяет конфигурацию
Всё прозрачно работает как с ядерной версией
Инструмент wg(8) подключается к этим сокетам и отправляет и получает следующий текстовый протокол.
Протокол конфигурации
Реализация WireGuard должна отвечать на две команды: get и set, обе версии 1 на момент написания.
Что такое версия протокола? Это позволяет в будущем добавлять новые функции без нарушения совместимости. Номер версии указывает, какой формат команд и ответов используется.
Команда get
wg(8) отправляет команду get, которая выглядит так:
get=1
{empty line}
Объяснение: Команда get запрашивает текущую конфигурацию и статус интерфейса. Пустая строка в конце указывает на завершение команды.
Пользовательская реализация отвечает на команду get так:
Объяснение ответа: После всех ключей и значений добавляется errno=0 (означает “успех”) и пустая строка. Если произошла ошибка, errno будет содержать код ошибки.
Команда set
wg(8) отправляет команду set, которая выглядит так:
Объяснение: Команда set отправляет новую конфигурацию. В отличие от get, она содержит ключи и значения для применения.
Пользовательская реализация отвечает на команду set так:
errno=0
{empty line}
Объяснение: Ответ на set проще — только код ошибки и пустая строка.
Если произошла ошибка, errno — соответствующее целое число из errno.h (стандартные коды ошибок POSIX).
Примечание: Обратите внимание на пустую строку в конце команд get и set, а также в конце каждого соответствующего ответа от wg(8). Это необходимо для правильного разбора протокола.
Ключи и значения
Уровень интерфейса
Эти ключи применяются ко всему интерфейсу:
Ключ
Описание
Формат значения
Примечания
private_key
Приватный ключ интерфейса
Шестнадцатеричная строка (в нижнем регистре)
Все нули = удалить ключ
listen_port
Порт для прослушивания
Десятичное целое число
fwmark
Метка для исходящих пакетов
Десятичное целое число
0 = удалить fwmark
replace_peers=true
Заменить всех пиров
Только ключ
Последующие пиры заменяют существующих
Пример использования replace_peers: Если у вас уже есть 5 пиров, и вы отправляете set с replace_peers=true и 3 новыми пирами, старые 5 пиров будут удалены, а останутся только 3 новых.
Уровень пира
Эти ключи применяются к конкретному пиру и следуют после ключа public_key:
Ключ
Описание
Формат значения
Примечания
public_key
Публичный ключ пира
Шестнадцатеричная строка (в нижнем регистре)
Начинает блок пира
remove=true
Удалить пира
Только ключ
Применяется к предыдущему пиру
update_only=true
Обновить только существующего
Только ключ
Ошибка, если пир не существует
preshared_key
Предварительный общий ключ
Шестнадцатеричная строка
Все нули = удалить ключ
endpoint
Конечная точка
IP:port или [IP]:port
Для IPv6 используются квадратные скобки
persistent_keepalive_interval
Интервал keepalive
Десятичное целое число
0 = отключить
replace_allowed_ips=true
Заменить все разрешённые IP
Только ключ
Заменяет список, а не добавляет
allowed_ip
Разрешённый IP-адрес
IP/cidr
Может быть указан несколько раз
rx_bytes
Получено байт
Десятичное целое число
Только для get
tx_bytes
Отправлено байт
Десятичное целое число
Только для get
last_handshake_time_sec
Время рукопожатия (сек)
Десятичное целое число
Только для get, время Unix
last_handshake_time_nsec
Время рукопожатия (нс)
Десятичное целое число
Только для get, время Unix
protocol_version
Версия протокола
1
Обычно не используется
Важные замечания о ключах
Порядок важен: Все ключи уровня интерфейса должны идти до ключей уровня пира. Это логично, так как сначала определяются общие параметры интерфейса, а затем параметры отдельных пиров.
Обработка allowed_ip: Если одинаковое значение allowed_ip уже существует у другого пира, запись IP будет удалена у того пира и добавлена к текущему. Это обеспечивает уникальность разрешённых IP-адресов.
Формат ключей: Все ключи в шестнадцатеричном формате должны быть в нижнем регистре. Это стандарт для криптографических ключей в WireGuard.
replace_peers=true — все старые пиры будут удалены
fwmark=0 — fwmark отключён
Добавляются 3 пира, последний (e818...) помечен remove=true для удаления
У каждого пира используется replace_allowed_ips=true для замены списков разрешённых IP
Пользовательская реализация WireGuard отвечает:
errno=0
{empty line}
Успех: Код ошибки 0 означает, что конфигурация успешно применена.
Практические примеры использования
Настройка интерфейса через командную строку
# Запуск пользовательской реализации# wireguard-go wg0# Настройка с использованием wg(8)# wg set wg0 private-key /etc/wireguard/private.key listen-port 51820# wg set wg0 peer xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg= allowed-ips 10.0.0.2/32 endpoint 192.95.5.69:51820# ip address add dev wg0 10.0.0.1/24# ip link set up dev wg0
Использование с конфигурационным файлом
# Создаём конфигурационный файл /etc/wireguard/wg0.conf# wg setconf wg0 /etc/wireguard/wg0.conf# wg-quick up wg0
Получение статуса
# wg show wg0
Преимущества пользовательской реализации
Кроссплатформенность — работает на Windows, macOS, BSD и других системах без поддержки в ядре
Простота отладки — ошибки легче отслеживать в пользовательском пространстве
Гибкость — можно интегрировать в приложения как библиотеку
Безопасность — изоляция от ядра может быть преимуществом в некоторых сценариях
Недостатки пользовательской реализации
Производительность — обычно медленнее, чем реализация в ядре
Дополнительные накладные расходы — копирование данных между ядром и пользовательским пространством
Потребление памяти — больше использования памяти для хранения состояния
Интеграция WireGuard с сетевыми пространствами имён Linux для контейнеризации и маршрутизации трафика
Интеграция с маршрутизацией и сетевыми пространствами имён
Как и все сетевые интерфейсы Linux, WireGuard интегрируется в инфраструктуру сетевых пространств имён (network namespaces). Это означает, что администратор может иметь несколько полностью разных сетевых подсистем и выбирать, какие интерфейсы находятся в каждом из них.
Что такое сетевые пространства имён? Это механизм ядра Linux, который позволяет изолировать сетевые стеки. Каждое пространство имён имеет свои собственные сетевые интерфейсы, таблицы маршрутизации, правила файервола и сокеты. Это основа для контейнеризации (Docker, LXC и другие).
WireGuard делает кое-что довольно интересное. Когда интерфейс WireGuard создаётся (с помощью ip link add wg0 type wireguard), он запоминает пространство имён, в котором был создан: «Я был создан в пространстве имён A». Позже WireGuard может быть перемещён в новые пространства имён («Я перемещаюсь в пространство имён B»), но он всё равно будет помнить, что произошёл из пространства имён A.
Почему это важно? WireGuard использует UDP-сокет для фактической отправки и получения зашифрованных пакетов. Этот сокет всегда живёт в пространстве имён A — исходном пространстве имён рождения. Это обеспечивает очень интересные возможности. А именно, вы можете создать интерфейс WireGuard в одном пространстве имён (A), переместить его в другое (B) и отправлять незашифрованные пакеты из пространства имён B, которые будут отправляться зашифрованными через UDP-сокет в пространстве имён A.
Примечание: Та же техника доступна для пользовательских интерфейсов на основе TUN: создайте файловый дескриптор сокета в одном пространстве имён, затем переключитесь в другое пространство имён, сохранив файловый дескриптор из предыдущего пространства имён открытым.
Это открывает очень интересные возможности.
Обычная контейнеризация
Наиболее очевидное использование этого — предоставить контейнерам (например, Docker-контейнерам) интерфейс WireGuard в качестве единственного интерфейса.
Пример: Внутри контейнера видно только loopback-интерфейс и интерфейс WireGuard:
container # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
17: wg0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1423 qdisc noqueue state UNKNOWN group default qlen 1
link/none
inet 192.168.4.33/32 scope global wg0
valid_lft forever preferred_lft forever
Что это значит? Единственный способ доступа к сети — через wg0, интерфейс WireGuard. Весь трафик из контейнера автоматически шифруется и отправляется через VPN. Контейнер даже не знает о существовании физических сетевых интерфейсов хоста.
Настройка контейнера с WireGuard
Шаг 1: Создание сетевого пространства имён
Сначала создаём сетевое пространство имён с именем «container»:
# ip netns add container
Шаг 2: Создание интерфейса WireGuard
Создаём интерфейс WireGuard в исходном пространстве имён (init):
# ip link add wg0 type wireguard
Шаг 3: Перемещение интерфейса
Перемещаем интерфейс в новое пространство имён:
# ip link set wg0 netns container
Шаг 4: Настройка интерфейса
Теперь настраиваем wg0 как обычно, но указываем его новое пространство имён:
# ip -n container addr add 192.168.4.33/32 dev wg0# ip netns exec container wg setconf wg0 /etc/wireguard/wg0.conf# ip -n container link set wg0 up# ip -n container route add default dev wg0
Результат: Теперь единственный способ доступа к сетевым ресурсам для «container» — через интерфейс WireGuard.
Для пользователей Docker: Вы можете указать PID Docker-процесса вместо имени сетевого пространства имён, чтобы использовать сетевое пространство имён, которое Docker уже создал для своего контейнера:
# ip link set wg0 netns 879
Маршрутизация всего трафика
Менее очевидное, но чрезвычайно мощное использование — применение этой характеристики WireGuard для перенаправления всего вашего обычного интернет-трафика через WireGuard.
Проблема: Как заставить весь трафик идти через VPN, но при этом позволить самому VPN-соединению использовать обычный интернет?
Классические решения
Классические решения основаны на различных конфигурациях таблиц маршрутизации. Для всех них нам нужно установить явный маршрут для фактической конечной точки WireGuard.
Пример: Предположим, конечная точка WireGuard — demo.wireguard.com, которая на момент написания резолвится в 163.172.161.0. Предположим также, что мы обычно подключаемся к интернету через eth0 со шлюзом 192.168.1.1.
Замена маршрута по умолчанию
Самая простая техника — просто заменить маршрут по умолчанию, но добавить явное правило для конечной точки WireGuard:
# ip route del default# ip route add default dev wg0# ip route add 163.172.161.0/32 via 192.168.1.1 dev eth0
Преимущества: Просто и понятно.
Недостатки: DHCP-демоны и другие программы любят отменять то, что мы только что сделали.
Переопределение маршрута по умолчанию
Вместо замены маршрута по умолчанию мы можем переопределить его двумя более специфичными правилами, которые в сумме дают маршрут по умолчанию, но совпадают раньше:
# ip route add 0.0.0.0/1 dev wg0# ip route add 128.0.0.0/1 dev wg0# ip route add 163.172.161.0/32 via 192.168.1.1 dev eth0
Объяснение: 0.0.0.0/1 + 128.0.0.0/1 = 0.0.0.0/0 (весь IPv4-диапазон). Эти маршруты имеют более высокий приоритет, чем маршрут по умолчанию, поэтому они используются для всего трафика, кроме явно исключённого.
Недостатки: Когда eth0 поднимается и опускается, явный маршрут для demo.wireguard.com забывается.
Маршрутизация на основе правил
Некоторые предпочитают использовать маршрутизацию на основе правил и несколько таблиц маршрутизации:
# ip rule add to 163.172.161.0 lookup main pref 30# ip rule add to all lookup 80 pref 40# ip route add default dev wg0 table 80
Объяснение: Правила определяют, какую таблицу маршрутизации использовать для каждого пакета. Для IP-адреса конечной точки используется основная таблица, для всего остального — таблица 80.
Недостатки: Нужно добавлять явные правила для конечных точек, нет очистки при удалении интерфейса, более сложные правила маршрутизации нужно дублировать.
Улучшенная маршрутизация на основе правил
Предыдущее решение требует знания явного IP-адреса конечной точки, который должен быть исключён из туннеля. Но конечные точки WireGuard могут роумить, что означает, что это правило может устареть.
Решение: Устанавливаем fwmark на все пакеты, выходящие из UDP-сокета WireGuard, которые затем будут исключены из туннеля:
# wg set wg0 fwmark 1234# ip route add default dev wg0 table 2468# ip rule add not fwmark 1234 table 2468# ip rule add table main suppress_prefixlength 0
Объяснение:
Устанавливаем fwmark на интерфейсе
Добавляем маршрут по умолчанию в альтернативную таблицу маршрутизации
Указываем, что пакеты без fwmark должны использовать эту альтернативную таблицу
Добавляем удобство для доступа к локальной сети
Это техника, используемая инструментом wg-quick(8).
Новое решение с пространствами имён
Оказывается, мы можем маршрутизировать весь интернет-трафик через WireGuard, используя сетевые пространства имён, а не классические хаки с таблицами маршрутизации.
Основная идея: Перемещаем интерфейсы, подключающиеся к интернету (например, eth0 или wlan0), в пространство имён «physical», а интерфейс WireGuard делаем единственным интерфейсом в пространстве имён «init».
Пошаговая настройка
Шаг 1: Создание физического пространства имён
# ip netns add physical
Шаг 2: Перемещение физических интерфейсов
Перемещаем eth0 и wlan0 в пространство имён «physical»:
# ip link set eth0 netns physical# iw phy phy0 set netns name physical
Примечание: Беспроводные устройства должны быть перемещены с помощью iw путём указания физического устройства phy0.
Теперь эти интерфейсы находятся в пространстве имён «physical», а в пространстве имён «init» интерфейсов нет:
# ip -n physical link1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000 link/ether ab:cd:ef:g1:23:45 brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000 link/ether 01:23:45:67:89:ab brd ff:ff:ff:ff:ff:ff
# ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
Шаг 3: Создание интерфейса WireGuard в физическом пространстве
# ip -n physical link add wg0 type wireguard
Пространство имён рождения wg0 теперь — «physical», что означает, что UDP-сокеты с зашифрованными данными будут привязаны к устройствам eth0 и wlan0.
Шаг 4: Перемещение интерфейса в init
Перемещаем wg0 в пространство имён «init». Он всё равно будет помнить своё место рождения для сокетов:
# ip -n physical link set wg0 netns 1
Мы указываем «1» как пространство имён «init», потому что это PID первого процесса в системе.
Теперь в пространстве имён «init» есть устройство wg0:
# ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
17: wg0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1423 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1 link/none
Шаг 5: Настройка физических устройств
Настраиваем физические устройства с помощью обычных инструментов, но запускаем их внутри пространства имён «physical»:
# ip netns exec physical dhcpcd wlan0# ip netns exec physical wpa_supplicant -iwlan0 -c/etc/wpa_supplicant/wpa_supplicant.conf# ip -n physical addr add 192.168.12.52/24 dev eth0
Шаг 6: Настройка WireGuard
Настраиваем интерфейс wg0 как обычно и устанавливаем его как маршрут по умолчанию:
# wg setconf wg0 /etc/wireguard/wg0.conf# ip addr add 10.2.4.5/32 dev wg0# ip link set wg0 up# ip route add default dev wg0
Результат: Все обычные процессы в системе маршрутизируют свои пакеты через пространство имён «init», которое содержит только интерфейс wg0 и его маршруты. Однако wg0 имеет UDP-сокет, живущий в пространстве имён «physical», что означает, что он будет отправлять трафик через eth0 или wlan0. Обычные процессы даже не знают о существовании eth0 или wlan0, за исключением dhcpcd и wpa_supplicant, которые были запущены внутри пространства имён «physical».
Выполнение команд в физическом пространстве
Иногда вам может понадобиться открыть веб-страницу или быстро сделать что-то, используя пространство имён «physical». Например, вы можете планировать маршрутизировать весь трафик через WireGuard, но кофейня, в которой вы сидите, требует аутентификации через веб-сайт перед предоставлением реального интернет-соединения.
$ physexec chromium # Открыть Chromium в физическом пространстве для аутентификации
После входа в сеть кофейни запускайте браузер как обычно:
$ chromium # Весь трафик защищён WireGuard
Пример скрипта
Следующий пример скрипта можно сохранить как /usr/local/bin/wgphys и использовать для команд wgphys up, wgphys down и wgphys exec:
#!/bin/bash
set -ex
[[$UID !=0]]&&exec sudo -E "$(readlink -f "$0")""$@"up(){ killall wpa_supplicant dhcpcd ||true ip netns add physical
ip -n physical link add wgvpn0 type wireguard
ip -n physical link set wgvpn0 netns 1 wg setconf wgvpn0 /etc/wireguard/wgvpn0.conf
ip addr add 192.168.4.33/32 dev wgvpn0
ip link set eth0 down
ip link set wlan0 down
ip link set eth0 netns physical
iw phy phy0 set netns name physical
ip netns exec physical dhcpcd -b eth0
ip netns exec physical dhcpcd -b wlan0
ip netns exec physical wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant-wlan0.conf -iwlan0
ip link set wgvpn0 up
ip route add default dev wgvpn0
}down(){ killall wpa_supplicant dhcpcd ||true ip -n physical link set eth0 down
ip -n physical link set wlan0 down
ip -n physical link set eth0 netns 1 ip netns exec physical iw phy phy0 set netns 1 ip link del wgvpn0
ip netns del physical
dhcpcd -b eth0
dhcpcd -b wlan0
wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant-wlan0.conf -iwlan0
}execi(){exec ip netns exec physical sudo -E -u \#${SUDO_UID:-$(id -u)} -g \#${SUDO_GID:-$(id -g)} -- "$@"}command="$1"shiftcase"$command" in
up) up "$@";; down) down "$@";;exec) execi "$@";; *)echo"Usage: $0 up|down|exec" >&2;exit1;;esac
Демонстрация работы скрипта:
Сравнение решений
Решение
Преимущества
Недостатки
Замена default
Простота
DHCP отменяет изменения
Переопределение default
Не заменяет default
Маршрут забывается при переподключении
Правила (rule-based)
Разделение таблиц
Нужно знать IP конечной точки
Улучшенные правила (fwmark)
Устойчив к роумингу
Сложнее для понимания
Пространства имён
Полная изоляция, чистота
Требует настройки пространств имён
Преимущества решения с пространствами имён
Полная изоляция — обычные процессы не видят физические интерфейсы
Автоматическая маршрутизация — весь трафик идёт через WireGuard без сложных правил
Прозрачность — не нужно добавлять явные маршруты для конечных точек
Чистота — нет хаков с таблицами маршрутизации
Гибкость — можно запускать отдельные процессы в физическом пространстве
Как встроить WireGuard в пользовательские приложения на различных платформах
Встраивание WireGuard в пользовательские приложения
Клиентские приложения проекта WireGuard были спроектированы с максимальной возможностью повторного использования, что позволяет создавать пользовательские приложения, использующие WireGuard. Ситуация несколько отличается на разных платформах, и эта страница пытается обобщить то, что доступно в проекте.
Важно: Если вы системный администратор, который просто пытается написать скрипты для существующих клиентов WireGuard, эта страница не для вас. Вместо этого ознакомьтесь с документацией по инструментам wg(8) и wg-quick(8), а также с руководством по корпоративному управлению для Windows.
Зачем встраивать WireGuard?
Встраивание WireGuard в приложения может быть полезно для:
VPN-клиентов — создание собственного VPN-приложения с брендированным интерфейсом
Систем управления — интеграция VPN в панели управления и оркестраторы
Сетевых утилит — добавление безопасного туннелирования в существующие инструменты
IoT-устройств — встраивание безопасного канала связи в嵌入式 системы
Корпоративных решений — создание проприетарных решений поверх WireGuard
Платформо-специфичные решения
Windows: embeddable-dll-service
Что это? Библиотека, которая позволяет создавать полноценные автономные Windows-сервисы, встраивающие WireGuard.
Для кого: Для разработчиков Windows-приложений, которые хотят добавить VPN-функциональность без написания низкоуровневого кода.
Как это работает: Библиотека предоставляет простой API для создания и управления WireGuard-туннелями из любого Windows-приложения. Она управляет всем циклом жизни VPN-соединения.
Пример использования:
// Псевдокод для понимания концепцииvarwgService=newWireGuardService();wgService.SetConfig(configFile);wgService.Start();// ... работа с VPNwgService.Stop();
Преимущества:
Полноценная интеграция с Windows Service Manager
Автоматическое управление жизненным циклом
Встроенная обработка ошибок и восстановление
Простой API
Windows: WireGuardNT
Что это? Более низкоуровневая реализация WireGuard для Windows, чем embeddable-dll-service.
Для кого: Для разработчиков, которым нужен максимальный контроль над WireGuard на Windows, или для создания альтернативных реализаций.
Важное предупреждение: Настоятельно рекомендуется использовать embeddable-dll-service, а не WireGuardNT напрямую, так как первый использует второй внутри себя. WireGuardNT предоставляет более низкоуровневый API.
Что делает WireGuardNT:
Реализует драйвер ядра для WireGuard в Windows
Обеспечивает высокую производительность
Предоставляет низкоуровневый интерфейс управления
Когда использовать WireGuardNT:
Создание альтернативных менеджеров WireGuard
Исследовательские проекты
Интеграция с нестандартными системами управления
macOS и iOS: WireGuardKit
Что это? Библиотека для интеграции WireGuard в приложения для macOS и iOS.
Где найти:WireGuardKit из репозитория wireguard-apple
Для кого: Для разработчиков приложений для экосистемы Apple.
Как это работает: WireGuardKit предоставляет Swift-интерфейс для управления WireGuard-туннелями. Он интегрируется с сетевыми расширениями (Network Extensions) Apple для создания полноценных VPN-приложений.
Пример использования (Swift):
importWireGuardKit// Создание туннеляlettunnel=WireGuardTunnel()tunnel.configuration=configtunnel.start{errorinifleterror=error{print("Ошибка запуска: \(error)")}else{print("Туннель запущен")}}
Поддерживаемые платформы:
macOS
iOS
iPadOS
Преимущества:
Интеграция с Apple Network Extension API
Поддержка Swift Package Manager (SPM)
Нативная производительность
Полная поддержка мобильных функций
Android: com.wireguard.android:tunnel
Что это? Библиотека для встраивания WireGuard в Android-приложения.
importcom.wireguard.android.backend.Backendimportcom.wireguard.config.Config// Создание бэкенда
valbackend=Backend()// Настройка и запуск туннеля
valconfig=Config.Builder().setPrivateKey(privateKey).addPeer(peerConfig).build()backend.setState(tunnelName,config,State.UP)
Возможности:
Интеграция с Android VPN Service API
Поддержка всех версий Android
Оптимизация для мобильных устройств
Обработка изменений сети
Linux: embeddable-wg-library
Что это? Однобиблиотечная C-библиотека для взаимодействия с WireGuard через ядро Linux.
Для кого: Для разработчиков на C/C++ в среде Linux, которым нужен программный контроль над WireGuard.
Особенности:
Один C-файл для включения в проект
Простой API для управления WireGuard
Использует системные вызовы ядра напрямую
Минимальные зависимости
Пример использования (C):
// Псевдокод для понимания
#include"wg.h"// Создание интерфейса
wg_interface_t*wg=wg_create("wg0");wg_set_private_key(wg,private_key);wg_add_peer(wg,public_key,allowed_ips,endpoint);wg_set_up(wg);
Linux/BSD/Darwin: wgctrl-go
Что это? Проект для создания WireGuard-конфигураций и управления ими из Go.
Для кого: Для разработчиков на Go, создающих приложения для Linux, BSD и macOS.
Пример использования (Go):
import"github.com/WireGuard/wgctrl"// Создание клиентаclient,err:=wgctrl.New()iferr!=nil{log.Fatal(err)}deferclient.Close()// Создание конфигурацииcfg:=wgtypes.Config{PrivateKey:privateKey,ListenPort:&port,Peers:[]wgtypes.PeerConfig{...},}// Применение конфигурацииerr=client.ConfigureDevice("wg0",cfg)
Поддерживаемые платформы:
Linux (через netlink)
BSD (через ioctl)
macOS (через userspace)
Преимущества:
Единый API для нескольких платформ
Поддержка всех функций WireGuard
Хорошая документация и примеры
Активная поддержка
Linux: NetworkManager, Systemd, connman
Что это? Полноценная поддержка WireGuard в популярных сетевых менеджерах Linux.
Для кого: Для разработчиков, предпочитающих использовать существующие инфраструктуры, а не создавать собственные.
NetworkManager
NetworkManager имеет встроенную поддержку WireGuard. Управление осуществляется через DBus API.
Пример (через nmcli, для скриптов):
nmcli connection add type wireguard ifname wg0 con-name wg0
nmcli connection modify wg0 wireguard.private-key /path/to/key
nmcli connection up wg0
Systemd
Systemd также поддерживает WireGuard через сетевые конфигурации.
Connman поддерживает WireGuard через свои конфигурационные файлы.
Сравнение решений по платформам
Платформа
Решение
Уровень
Язык
Сложность
Windows
embeddable-dll-service
Высокий
C#/C++/любой
Низкая
Windows
WireGuardNT
Низкий
C/C++
Высокая
macOS/iOS
WireGuardKit
Высокий
Swift
Низкая
Android
com.wireguard.android:tunnel
Высокий
Kotlin/Java
Низкая
Linux
embeddable-wg-library
Средний
C/C++
Средняя
Linux/BSD/Darwin
wgctrl-go
Средний
Go
Средняя
Linux
NetworkManager/Systemd/Connman
Высокий
Любой
Низкая
Руководство по выбору решения
Для Windows:
В большинстве случаев: Используйте embeddable-dll-service
Для экспериментов: Можно использовать WireGuardNT
Для Apple (macOS/iOS):
Всегда используйте: WireGuardKit
Для Android:
Всегда используйте: com.wireguard.android:tunnel
Для Linux:
Если вы используете Go: wgctrl-go
Если вы используете C/C++: embeddable-wg-library
Если вы используете существующие сетевые менеджеры: NetworkManager/Systemd/Connman
Для скриптов: Используйте wg(8) и wg-quick(8)
Для кроссплатформенных приложений:
Используйте соответствующее решение для каждой платформы
Или используйте wgctrl-go (для Linux, BSD, macOS) и отдельные решения для Windows/Android
Примеры интеграции
Создание простого VPN-клиента на Windows
# Использование embeddable-dll-service$wgService=New-Object-ComObjectWireGuard.Service$wgService.Install("MyVPN")$wgService.SetConfig("C:\Configs\myvpn.conf")$wgService.Start()
Интеграция в мобильное приложение (iOS)
// Использование WireGuardKitimportWireGuardKitclassVPNManager{lettunnel=WireGuardTunnel()funcstartVPN(){letconfig=try!Config(fromFile:"config.conf")tunnel.configuration=configtunnel.start()}}
Интеграция в веб-приложение (Linux)
// Использование wgctrl-gopackagemainimport("github.com/WireGuard/wgctrl""github.com/WireGuard/wgctrl/wgtypes")funcsetupVPN()error{client,_:=wgctrl.New()deferclient.Close()key,_:=wgtypes.GeneratePrivateKey()config:=wgtypes.Config{PrivateKey:&key,ListenPort:ptr(51820),}returnclient.ConfigureDevice("wg0",config)}funcptr[Tany](vT)*T{return&v}
Рекомендации по безопасности при встраивании
Хранение ключей: Всегда храните приватные ключи в защищённом месте (Keychain, Secure Storage, TPM)
Проверка конфигураций: Валидируйте все входящие конфигурации перед применением
Логирование: Ведите журнал действий для аудита
Обновления: Следите за обновлениями WireGuard и включайте их в свои приложения
Тестирование: Тщательно тестируйте интеграцию на всех целевых платформах