Использование Wireguard клиентами с «серыми IP».
Вступление.
Официальный сайт:
https://www.wireguard.com/quickstart/
Справка:
man wg
Это простое и высокопроизводительное средство создания туннелей с шифрованием трафика. Wireguard включён в состав ядра Linux под лицензией GPLv2. Передача пакетов осуществляется по протоколу UDP.
Благодаря продвинутому протоколу передачи данных и UDP, обладает высокой эффективностью преодоления NAT, что позволяет прокладывать туннели между пользователями с «серыми IP» (частными, внутренними), кроме случаев использования провайдером Symmetric NAT. При такой конфигурации сети потребуется хотя бы один участник с «белым IP» (выделенным) или применять туннелирование трафика сервером ретрансляции (TURN), имеющим выделенный IP.
Совокупность свойств делает Wireguard очень удобным средством для множества задач: от развёртывания удалённого доступа к офисному серверу до игр по локальной сети без ретрансляции трафика через сервера третьих лиц, что повышает надёжность, уменьшает задержки и снижает поверхность атаки.
В данной статье рассмотрен простой вариант соединения двух клиентов с «серыми IP», не спрятанными за Symmetric NAT.
Подготовка к работе.
Установка.
Для начала работы необходимо установить пакет wireguard-tools:
sudo apt install wireguard-tools
Основной состав пакета:
wg — основная программа.
wg-quick — программа для упрощения развёртывания туннелей по заранее заготовленным конфигурационным файлам. По умолчанию конфигурационные файлы располагаются в /etc/wireguard/. Файлы можно разместить в любой желаемом месте, но настоятельно рекомендуется строго ограничить права на чтение и запись, так как файл содержит информацию о закрытом ключе и списки IP клиентов, а так же считывается с правами суперпользователя. Есть незамысловатое правило: то, с чем работает суперпользователь, должно принадлежать суперпользователю.
Нюансы конфигурации «сервера» и «клиента».
Основные.
- Разница между сервером и клиентом довольно условна. Она заключается в том, что «сервер» должен знать всех «клиентов», а «клиентам» достаточно знать только «сервер», но нет никакой проблемы в том, чтобы все знали друг о друге. Последнее позволяет пользователям связываться друг с другом напрямую.
- Имя интерфейса Wireguard должно быть одинаковым у «сервера» и «клиентов».
- Имя зависит от названия конфигурационного файла. Если имя конфигурационного файла wg0.conf, то у интерфейса будет wg0. В целях безопасности имеет смысл задать собственное имя, а не предлагаемое по умолчанию.
- Выбор имени определённым образом ограничен, но без проблем можно называть подобным образом: wg-myVPN.conf
- По умолчанию файлы конфигурации хранятся в /etc/wireguard/. На деле место размещения файла конфигурации не имеет значения, но настоятельно рекомендуется строго ограничить права на чтение и запись, так как файл содержит информацию о закрытом ключе и списки IP клиентов, а так же считывается с правами суперпользователя. Поэтому имеет смысл дать право на чтение, запись и выполнение только суперпользователю, а всем остальным запретить всё.
О конфигурациях NAT и механике преодоления.
При использовании «серых IP» для установки соединения между клиентами нужно «пробить» NAT. Возможность «пробития» сильно зависит от конфигурации NAT на стороне провайдера: https://en.wikipedia.org/wiki/Network_address_translation#Methods_of_translation
В данном случае рассмотрим вариант связи между клиентами с «серыми IP» и конфигурацией NAT провайдера, соответствующей Full-cone NAT, которая является самой простой для преодоления, но в качестве справки сначала несколько слов о Symmetric NAT.
Конфигурацию Symmetric NAT крайне проблематично преодолеть. Такой тип конфигурации повсеместно встречается у провайдеров мобильного интернета. Для преодоления потребуется участник сети Wireguard с выделенным IP (белым), через которого клиенты смогут соединяться между собой, так как прямое соединение между «серыми IP» при такой конфигурации крайне проблематично.
Пропуск трафика клиентов через участника сети с «белым IP» для установки соединения схож с использованием узла ретрансляции по протоколу TURN (для работы необходим сервер с выделенным IP).
Для пропуска трафика потребуется разрешить транзит пакетов по правилу FORWARD. Это можно сделать добавив строку net.ipv4.ip_forward=1 в файл /etc/sysctl.conf.
Для применения изменений:
sudo sysctl -p
Механика преодоления NAT заключается в следующем. Благодаря тому, что Wireguard использует UDP, роутер считает, что отправленный UDP-пакет по умолчанию достиг цели, сохраняет адрес и порт в таблице NAT в ожидании следующих пакетов, включая встречные. Тем самым это позволяет установить соединение, если пользователи знают свои реальные внешние IP (их получение описано далее).
Важным нюансом является то, что информация о UDP соединении хранится в таблице NAT ограниченное время, поэтому пользователи, желающие установить и поддерживать соединение между собой, должны посылать друг другу пакеты с небольшим интервалом (25 секунд и менее).
Определение внешнего IP с помощью STUN.
Ключевым условием преодоления NAT является точное определение внешнего IP и порта, которые провайдер выделяет пользователям для соединения с узлами в интернете. Для надёжного определения внешнего IP и порта необходимо использовать STUN-сервер, так как изнутри сети провайдера свой внешний IP не определить.
Для использования STUN-серверов по протоколу RFC 3489 можно использовать Python-программу pystun3.
Установка (с правами пользователя):
pip3 install pystun3
Пример использования pystun3:
pystun3 -H stun.nextcloud.com -P 3478 -p 51820
-H — указать адрес STUN-сервера. В данном случае это stun.nextcloud.com. Альтернативные адреса безопасных STUN-серверов: stun.ekiga.net, stun.sipgate.net.
-P — порт для соединения со STUN-сервером. Порт 3478 используется в качестве стандартного для обращения к STUN-серверу.
-p — порт, с которого будут отправлены пакеты в сторону STUN-сервера. В целях проверки выбрать порт, который планируется использовать для Wireguard. Стандартный порт Wireguard — 51820.
Вывод:
NAT Type: Full Cone
External IP: 80.235.76.174
External Port: 51820
Press any key to continue
Стоит сделать несколько попыток, чтобы точнее определить конфигурацию NAT.
В выводе указан внешний IP и порт, предоставленные провайдером для соединений с узлами в интернете:
- Совпадение указанного порта (51820) с внешним портом (External Port) указывает на высоковероятное использование конфигурации NAT вида Full Cone, которую Wireguard легко преодолевает.
- Если внешний порт отличается от указанного, то конфигурация соответствует Restric NAT, что заметно усложняет налаживание соединения между клиентами с «серыми IP». Важно учитывать, что провайдер предоставляет порт на ограниченное время. Если устойчивое соединение отсутствует, то через некоторое время (менее часа) провайдер сменит порт на другой. И так каждый раз. В этом основное усложнение установки соединения между клиентами с «серыми IP» для данного типа NAT. Используемый в Wireguard протокол и частота отправки пакетов с «рукопожатием» позволяют это преодолевать.
- Если порт (и/или IP) меняется при каждой попытке соединения со STUN-серверами, то, скорее всего, используется Symmetric NAT, что делает соединение между клиентами с «серыми IP» невозможным без применения ряда ухищрений.
Настройка конфигурации.
Генерация закрытого и открытого ключа.
Первым этапом настройки конфигурации является генерирование ключевой пары.
Примечание: Имена ключей могут быть любые.
Команда на генерацию представляет собой следующее:
umask 077 && wg genkey > private.key && wg pubkey < ./private.key > public.key
umask 077 — применить маску прав для файлов, которые будут созданы в этой сессии терминала. 077 — разрешить пользователю чтение, запись и выполнение, а группе и остальным — запретить всё.
wg genkey — генерирует закрытый ключ. Затем вывод, содержащий сгенерированный ключ, перенаправляется (>) в файл private.key (будет создан).
wg pubkey — генерирует открытый ключ. Ввод (<) из ранее созданного файла private.key, который содержит закрытый ключ, перенаправляется функции pubkey, которая из полученного закрытого ключа по алгоритму Base64 генерирует открытый ключ, затем поток вывода (>) перенаправляется в файл public.key.
По итогу получается два файла: private.key (закрытый ключ) и public.key (открытый ключ).
Альтернативный способ:
umask 077 && wg genkey | tee private.key | wg pubkey > public.key
tee — утилита запишет ключ из буфера в файл с указанным именем (файл будет создан).
wg pubkey — закрытый ключ, который всё ещё находится в буфере, передаётся утилите pubkey, которая из закрытого ключа по алгоритму Base64 генерирует открытый ключ. Затем поток вывода (>) перенаправляется в файл с указанным именем (файл будет создан).
В обоих случаях ключи окажутся в корне Домашнего каталога. Файлы можно поместить в любое подходящее место.
По желанию можно применить более строгий уровень прав на файлы ключей — только чтение:
chmod 400 ~/public.key ~/private.key
Конфигурация «сервера».
Примечание: В конфигурационных файлах поддерживаются комментарии. Комментарий должен начинаться с символа #.
Пример конфигурации с одним клиентом:
[Interface]
PrivateKey = WCF+wwg4y/B84p6Op6bclaEGWgSVBcy9LC+uevk6SWk= ListenPort = 51820 Address = 192.168.2.1/32 PostUp = iptables -A INPUT -i eno1 -p udp -s Внешний IP клиента -j ACCEPT PostDown = iptables -D INPUT -i eno1 -p udp -s Внешний IP клиента -j ACCEPT [Peer] PublicKey = DYCaPpX+8pi8ds/J9h2A+g79QZPI1qrSvvSwFNF/yAs= Endpoint = Внешний IP клиента и порт. AllowedIPs = 192.168.2.2/32 PersistentKeepalive = 25
Пример расположения файла: ~/wg0.conf
Описание:
[Interface] PrivateKey = Закрытый ключ сервера # Порт, через который будет осуществляться подключение и на котором будет ожидаться пакет-рукопожатие от клиента. Указывать обязательно, это особенно важно, если требуется пробить NAT, так как пакеты должны поступать и уходить с одинакового порта. По умолчанию 51820. ListenPort = 51820 # Адрес сервера в сети Wireguard. Указывать обязательно. Может быть любой подходящий для локальной сети. Маска подсети /32 оптимальная, иначе будет предупреждение Warning: AllowedIP has nonzero host part. Address = 192.168.2.1/32 # Активация (PostUp) и деактивация (PostDown) правила при включении и отключении интерфейса Wireguard. Правило необходимо для пропуска пакетов через сетевой экран. Расшифровка правила: на вход основного сетевого интерфейса (в примере — eno1) приходят пакеты; проверяется их протокол (udp), проверяется, что они именно с IP клиента из блока Peer; если всё сходится, пакеты пропускаются. Для получения имени основного сетевого интерфейса можно использовать команду: ip link PostUp = iptables -A INPUT -i eno1 -p udp -s Внешний IP клиента -j ACCEPT PostDown = iptables -D INPUT -i eno1 -p udp -s Внешний IP клиента -j ACCEPT # Каждый клиент описывается в отдельном блоке Peer. [Peer] PublicKey = Открытый ключ клиента # Для пробития NAT обязательно указать внешний IP клиента и порт аналогичный порту сервера(!). В данном случае это 51820. Для локальной сети IP и порт можно не указывать, так как клиенты будут подключаться к серверу по его адресу, а сервер будет проверять клиентов по соответствию открытого ключа. Endpoint = Внешний IP клиента и порт, который такой же, как у сервера — 51820. # Разрешённый адрес клиента в сети Wireguard. Указывать обязательно. Можно указать несколько через запятую. Чтобы разрешить любой адрес, нужно указать 0.0.0.0/0 AllowedIPs = 192.168.2.2/32 # Посылать пакеты для поддержания соединения. По умолчанию отправляются каждые 25 секунд. Важно для устойчивости соединения и пробития NAT. PersistentKeepalive = 25
Конфигурация «клиента».
Пример подключения к «серверу» без прочих «клиентов»:
[Interface]
PrivateKey = 86CQ3PdK8bG50ISbU0Ap2WKWA0jSI8zfIXXRbDTQl0Y=
ListenPort = 51820
Address = 192.168.2.2/32
PostUp = iptables -A INPUT -i eth1 -p udp -s Внешний IP сервера -j ACCEPT
PostDown = iptables -D INPUT -i eth1 -p udp -s Внешний IP сервера -j ACCEPT
[Peer]
PublicKey = rBZZuBXEyzmsqU/sJ+9f4sHD+0uDz0C5hRs5ZqPbyS8=
Endpoint = Внешний IP адрес сервера и порт
AllowedIPs = 192.168.2.1/32
PersistentKeepalive = 25
Название файла должно быть такое же, как на «сервере» ( в примере wg0.conf), а расположение может быть любым.
Описание:
[Interface] PrivateKey = Закрытый ключ клиента # Порт, на котором будет ожидаться встречный пакет-рукопожатие от сервера, если у того указан адрес клиента. Желательно указывать, если требуется пробить NAT. ListenPort = 51820 # IP адрес клиента в сети Wireguard должен быть таким же, какой указан в конфигурации сервера в блоке Peer. Address = 192.168.2.2/32 # Тот же тип разрешения на пропуск пакетов сетевым экраном, но в этот раз с внешнего IP сервера. Важно обратить внимание, что сетевое устройство на клиенте может иметь другое имя. В данном примере eth1. PostUp = iptables -A INPUT -i eth1 -p udp -s Внешний IP сервера -j ACCEPT PostDown = iptables -D INPUT -i eth1 -p udp -s Внешний IP сервера -j ACCEPT # В данном случае это «сервер», который на деле обычный клиент. [Peer] PublicKey = Открытый ключ сервера Endpoint = Внешний IP адрес сервера и порт, который такой же, как у сервера — 51820 # Адрес сервера в сети Wireguard. AllowedIPs = 192.168.2.1/32 # Посылать пакеты для поддержания соединения. PersistentKeepalive = 25
Запуск Wireguard.
Упрощённый запуск с помощью wg-quick.
Утилита wg-quick входит в состав пакет wireguard-tools, установка которого описана в начале статьи.
Утилита на основе информации из файла конфигурации создаёт сетевой интерфейс Wireguard и запускает его. Пример запуска конфигурации «сервера»:
sudo wg-quick up '/путь/до/wg0.conf'
Вывод после выполнения команды:
[#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip -4 address add 192.168.2.1/32 dev wg0 [#] ip link set mtu 1420 up dev wg0 [#] iptables -A INPUT -i eno1 -p udp -s Внешний IP клиента -j ACCEPT
В выводе перечислены команды, выполнение которых инициировала утилита wg-quick:
- Был создан сетевой интерфейс с именем wg0 и типом wireguard;
- Для него задействована конфигурация из файла wg0.conf;
- Присвоен IP, указанный в файле конфигурации;
- Установлено значение MTU;
- Для сетевого экрана применено правило, разрешающее прохождение пакетов с IP «клиента», указанного в файле конфигурации.
Имя интерфейса зависит от названия конфигурационного файла, в данном примере это wg0. У «сервера» и «клиента» интерфейсы должны называться одинаково.
Пример завершения работы интерфейса:
sudo wg-quick down '/путь/до/wg0.conf'
Вывод:
[#] ip link delete dev wg0 [#] iptables -D INPUT -i eno1 -p udp -s Внешний IP клиента -j ACCEPT
Были удалены интерфейс wg0 и правило для сетевого экрана, разрешающее прохождение пакетов с внешнего IP «клиента».
Так же можно включить создание и запуск интерфейса в виде автоматически запускаемой службы:
sudo systemctl enable wg-quick@wg0
Проверка работы интерфейса и соединения.
Вывести информацию о сетевых интерфейсах Wireguard:
sudo wg
sudo wg show wg0
sudo wg showconf wg0
ip addr show dev wg0
Проверка соединения к «серверу» по внутреннему IP созданной сети Wireguard (на примере IP ранее представленной конфигурации):
ping 192.168.2.1
Проверка соединения к «клиенту»:
ping 192.168.2.2
Отслеживать все пакеты, полученные или отправленные с указанного порта:
sudo tcpdump -n port <номер порта>
В лоб пропинговать весь диапазон IP, чтобы узнать IP всех участников сети:
echo 192.168.2.{1..254} | xargs -n1 -P0 ping -c1 | grep "bytes from"
Вывести доступный диапазон IP для указанного интерфейса:
ip route | grep wg0
После завершения работы интерфейса проверить правила iptables и убедиться, что правило на пропуск пакетов с внешнего IP было удалено:
sudo iptables -L
| grep 'Внешний IP клиента'
Вывод должен быть пустым.
Проблемы с установкой соединения.
Если эхо-запрос (ping) не достигает пользователя сети Wireguard, то первым делом необходимо проверить не блокируются ли пакеты сетевым экраном (со стороны сервера и клиента):
journalctl -k | grep "BLOCK" | tail -n 10
Если блокируются, значит правило настроено неправильно. Ошибка может содержаться в имени основного сетевого интерфейса или во внешнем IP.
Так же пакеты может уничтожать роутер пользователя. На нормальной прошивке можно изучить журнал событий на предмет наличия отброшенных пакетов или принудительно включить прослушивание конкретных портов. Проблема может оказаться в излишне параноидальных настройках безопасности. В крайнем случае может потребоваться применить DMZ и разрешить пропуск пакетов с внешнего IP подключающейся стороны, но это самый крайний вариант, так как значительно снижает уровень безопасности.
Если на роутере пользователя нет отброшенных пакетов с IP подключающейся стороны, то это может означать, что пакеты уничтожаются на роутере провайдера. Вероятно, неверно определён внешний IP или провайдер всё-таки использует Symmetric NAT. Во втором случае нет адекватных решений, кроме использования выделенного IP, включая вариант с ретрансляцией.