Сначала, о преимуществах WireGuard и обратной стороне медали, о сложностях, которые возникают вместе с преимуществами. WireGuard достаточно молодая реализация туннеля между двумя точками, как протокол передачи используется UDP. Именно поэтому и еще потому, что сама реализация в Linux выполнена в виде модуля ядра, тесты показывают порядочное преимущество в скорости, по отношению к конкурентам. Пиринговой сетью это считать нельзя, хотя конечные узлы и называются Peer. Суть пиринговой сети не только в том, что бы можно было соединить два произвольных узла. Конечно же на данном инструментарии, можно построить весьма замысловатую сетевую инфраструктуру, но у меня такой цели не стоит. Мы рассмотрим подключение к серверному узлу и выход посредством него в интернет.
Очевидно, что если два узла, сервер и один клиент имеют белые IP, в этом случае не должно возникать никаких сложностей в настройке. Но часто клиенты находятся за NAT и в этом случае при настройке сервера необходимо указывать совсем не тот IP адрес, который выдан/прописан на устройстве. В этом отношении, может помочь STUN протокол. Этот протокол, часто используется при работе VoIP, особенно, когда оба клиента находятся за NAT. Но в нашем случае он тоже поможет. Судя из информации в википедии, STUN работает не со всеми типами NAT, в одном из 4 типов NAT (симметричный) клиент может иметь другой IP, чем тот, что может быть определен снаружи с помощью STUN клиента.
Клиенты STUN, существуют на всех популярных операционных системах, кроме iOS. Под эту операционную систему, STUN клиент мне найти не удалось. Пример я приведу для macOS. Для начала, необходимо установить сам STUN клиент.
$ brew install stunman
В интернете существует масса STUN серверов и не сложно в поиске найти любой сервер для теста. Я буду использовать stun.ekiga.net. Для теста необходимо будет использовать локальный порт, тот который мы будем использовать для настройки:
$ stunclient --localport 51820 stun.ekiga.net
При удачном тесте мы получим, примерно такой вывод:
Binding test: success
Local address: 192.168.88.23:51820
Mapped address: 82.207.27.3:51820
Mapped address это именно тот IP, который необходимо будет использовать при настройке сервера. На самом деле, это тот IP адрес, который в моем случае выдаст любой сервис по определению внешнего IP. Поэтому можно воспользоваться вашим любимым сервисом по определению IP, но конечно, стоит учитывать, что этот тест будет косвенным и гарантии никакой нет, что все будет работать так как задумывалось.
Для подключения, со стороны клиента, для сервера, необходимо предоставить: IP, порт и публичный ключ. Для настройки на стороне клиента, необходим точно такой же список предоставленный со стороны сервера. Далее я предложу варианты конфигов, если вы будете использовать их у себя в качестве примеров, рекомендуется перегенерировать ключи.
На Linux, это можно сделать из командной строки, на macOS — через UI официального клиента.
$ wg genkey | tee privatekey | wg pubkey > publickey
В данном случае по месту вызова приватный ключ будет создан в файле privatekey, публичный в в publickey соответственно.
Первым рассмотрим клиентский конфиг:
# В начале секция описывающая локальный интерфейс
[Interface]
# Приватный ключ должен оставаться в секрете
PrivateKey = YPuKo2QXndQ2Vc3S/y90oKT7AJ0Swhq/HWKiF7GwS04=
# Порт нужно согласовать и использовать на стороне сервера
ListenPort = 51820
# Это IP адрес локальной сети, по которому сервер вас будет идентифицировать и
# выпускать в сеть
Address = 10.8.0.2/24
# DNS может быть несколько, их можно перечислить через запятую
DNS = 8.8.8.8
# Далее информация о сервере
[Peer]
# Это публичный ключ, который получен от сервера
PublicKey = nFjDIkgsAh1RMZuaCJ+AKs7JmbMxxthhZ0POjUSTvkc=
# Следующая опция может вызвать споры, так как условно верным является указание
# IP адреса сервера, но так как кроме всего прочего эта настройка автоматически
# клиентом прописывается в таблицу маршрутизации, то это самый простой способ
# перенаправить весь трафик в WireGuard интерфейс. IP может быть несколько,
# перечисляются через запятую.
AllowedIPs = 0.0.0.0/0
# Это IP и порт сервера
Endpoint = 46.101.122.130:51820
# Существуют 2 подхода. Первый - быть как можно тише, тогда эту настройку нужно убрать,
# но тогда и AllowedIPs в текущем варианте настройки нужно изменить и вручную
# настраивать таблицу маршрутизации. Второй - посылать пакеты для поддержания
# соединения, в документации рекомендуется - каждые 25 секунд.
PersistentKeepalive = 25
Теперь пришло время серверного конфига:
# В начале секция описывающая локальный интерфейс
[Interface]
# IP адрес сервера в локальной сети
Address = 10.8.0.1/24
# Порт со стороны сервера
ListenPort = 51820
# Приватный ключ, должен сохраняться в строжайшем секрете
PrivateKey = MNnxOy79xtXtSQ3UySWtdlOMbG7ff9dXGjeSTPEByn8=
# Далее 2 обработчика, в момент поднятия и опускания wg0 интерфейса
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Далее секция описывающая клиента
[Peer]
# Это публичный ключ, полученный от клиента
PublicKey = TdRtYd6XXI+ynPDXU6FF5TT3L5t/YlQVZswr2xsou34=
# Далее согласованный IP клиента, если он не будет совпадать,
# туннель не будет функционировать
AllowedIPs = 10.8.0.2/32
# Внешний IP клиента, как раз тот, что мы получили через STUN клиент
EndPoint = 82.207.27.3:51820
При использовании данных конфигов как примеров, рекомендуется удалить комментарии на русском языке, работа сервера и клиента в случае оставленных комментариев — не гарантируется.
Для настройки я пользовался следующими ресурсами: официальным сайтом, ArchWiki и вот этим туториалом.
В конце, я хотел бы также прояснить пару возможных вопросов:
1. Можно ли на сервере сделать несколько секций одинаковых Peer, которые будут отличаться только EndPoint?
Да можно, и это даже может быть полезно в том случае, когда вы пользуетесь сервисом из разных мест, к примеру, на работе и дома. Но проблемы могут возникнуть если такие Peer, выйдут в онлайн одновременно, в этом случае будет конфликт IP адресов.
2. Какой внешний IP и порт будет определяться по STUN протоколу для нескольких клиентов за NAT?
Один и тот же для всех клиентов, он будет одинаковый. Будет ли это проблемой? Это все зависит от настроек вашего провайдера/роутера, но принципиально проблемы возникать не должны, так как роутер должен широковещательно ретранслировать UDP пакеты внутри сети по маске локальной сети, соответственно те принимающие стороны, которые смогут расшифровать пакеты, должны их успешно принимать. Мы проводили тесты, на нашем оборудовании, тесты прошли успешно.
Целью статьи не было написать полный туториал как настроить WireGuard, это не сложно сделать воспользовавшись официальной документацией. Мы хотели показать, как WireGuard может работать за NAT.
Если вы хотите попробовать WireGuard в деле, вы можете обратиться к нам, у нас есть доступ в тестовом режиме.
UPD:
Как справедливо заметил хабражитель aborouhin, достаточно только одно белого IP для того что бы канал передачи данных работал без проблем. Соотвественно EndPoint для Peer в серверном конфиге можно не указывать и это облегчает настройку. А описаный мануал, может быть полезен, если оба узла находятся за NAT.
Комментарии (10)
mihmig
25.12.2019 06:231. «согласованный IP клиента» — это тот адрес клиента, что будет у клиента при успешном подключении? Т.е. не DHCP а вот так жёстко в настройках?
А что будет при одновременно повторном подключении того же клиента?
2. Внешний IP клиента — это если клиент подключается всегда со статического внешнего IP (даже если через NAT)?
А как быть с мобильными клиентами?yavdoshenko Автор
25.12.2019 09:591. Верно, в конфиге настройка статическая. При одновременном подключении будет конфликт. Если не делать привязки по ip, то в этом случае с одними кредами можно подключиться неконтролируемо большее количество раз.
2. Мобильные клиенты ничем не отличаются от не мобильных, они так же могут быть за NAT, и могут иметь белый IP, разницы в этом никакой. Внешний IP — это тот IP, который имеет узел, который выпускает в интернет (роутер или шлюз провайдера), он может и не иметь статического IP.
aborouhin
А зачем в серверном конфиге вообще указывать Endpoint клиента?
yavdoshenko Автор
Разделение на сервер и клиент весьма условное. Протокол разрабатывается с учетом того, что все узлы — это Peer. Поэтому если не указать Peer клиента, то условный сервер не будет знать куда отправлять данные. Строго говоря, в таком случае инициатором передачи данных может быть любой из узлов. Также нужно не забывать, что транспорт реализован на UDP и это выдвигает некоторые требования.
aborouhin
У меня уже скоро год как WireGuard на дюжине хостов (офис/дом/облака). И я ответственно заявляю, что если Endpoint указан в секции Peer только с одной стороны (условный клиент), то это нисколько не мешает другой стороне (условный сервер) "знать, куда отправлять данные".
Так что если хотя бы у одной из сторон белый IP — то WireGuard прекрасно работает и без упражнений с STUN.
yavdoshenko Автор
Проверил, все верно, правда достаточно только одного белого IP сервера, что бы все без проблем работало. У меня возникли подозрения, на счет проблем с производительностью в этом случае, специально полез смотреть исходники WireGuard, но по моему скромному мнению никаких проблем быть не должно. Поэтому STUN, может быть полезен, в том случае если оба хоста спрятаны за NAT.
aborouhin
И даже в этом случае Ваше решение не будет полезно, потому что NAT не обязан выдавать нашему Wireguard'у каждый раз один и тот же порт на внешнем IP. А раз мы вообще занимаемся вот этим всем вместо того, чтобы просто пробросить порт, то, вероятно, машинка, делающая NAT, нами не контролируется, — а значит и изменение её внешнего IP нельзя исключить.
Поэтому если обе стороны туннеля за NAT — не обойтись без обмена информацией об актуальных параметрах STUN через какую-то третью сторону. Тут как раз недавно про это аж два поста было (1, 2).
vanyaindigo
Согласен, какой-то огород нагородили лишний)
fougasse
Какие требования выдвигает UDP?
yavdoshenko Автор
Я имел в виду только то что отправка UDP, производится с обыкновенным IP заголовком, где указывается IP и порт принимающей стороны, так же IP и порт отправляющей стороны.