WireGuard -это такая реализация VPN в пространстве ядра. Авторами заявляется, что этот туннель простой как апельсин и надёжный как швейцарский банк. (шутка)
Да и мне уже все уши им прожужжали, мол, OpenVPN - это прошлый век, а сейчас все на WireGuard переходят.
Ну я и решил попробовать поставить это дело на Debian 10, который у меня давно уже используется. И тут начались проблемы.
Первая проблема возникла уже на этапе установки. Из-за того, что WireGuard работает внутри ядра, для него собирается модуль (пакет "wireguard-dkms"). В Debian 10 используется ядро версии 4.19, в к котором ещё нет такого модуля из коробки, а ставить ядро версии 5.5 из backports я желанием не горел.
Пробуем ставить:
sudo apt install wireguard
Установка заканчивается неудачей. При сборке модуля возникла примерно такая ошибка:
In file included from <command-line>: /var/lib/dkms/wireguard/1.0.20200429/build/socket.c: In function 'send6': /var/lib/dkms/wireguard/1.0.20200429/build/compat/compat.h:102:42: error: 'const struct ipv6_stub' has no member named 'ipv6_dst_lookup'; did you mean 'ipv6_dst_lookup_flow'? #define ipv6_dst_lookup_flow(a, b, c, d) ipv6_dst_lookup(a, b, &dst, c) + (void *)0 ?: dst ^~~~~~~~~~~~~~~ /var/lib/dkms/wireguard/1.0.20200429/build/socket.c:145:20: note: in expansion of macro 'ipv6_dst_lookup_flow' dst = ipv6_stub->ipv6_dst_lookup_flow(sock_net(sock), sock, &fl, ^~~~~~~~~~~~~~~~~~~~
Из найденного в Google сообщения об ошибке я узнал,что подобное возникает из-за того, что при сборке модуля не учитываются особенности ядра Linux из состава Debian 10. Там же я нашёл ссылку на патч, который подобную ситуацию исправляет. Он ничего не изменяет в алгоритмах модуля, только добавляет дополнительные проверки при сборке.
Код патча:
diff --git a/src/compat/Kbuild.include b/src/compat/Kbuild.include index eb6b6a9..3cc842e 100644 --- a/src/compat/Kbuild.include +++ b/src/compat/Kbuild.include @@ -99,3 +99,7 @@ ifeq ($(CONFIG_X86_64),y) asflags-y += $(adx_instr) endif endif + +ifneq ($(shell grep -s -F "\#define LINUX_PACKAGE_ID \" Debian " "$(CURDIR)/include/generated/package.h"),) +ccflags-y += -DISDEBIAN +endif diff --git a/src/compat/compat.h b/src/compat/compat.h index 363e01d..60056b8 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -98,7 +98,7 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 83) #define ipv6_dst_lookup_flow(a, b, c, d) ipv6_dst_lookup_flow(b, c, d) -#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 5) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || (LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 18) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 119) && !defined(ISRHEL82)) +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 5) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || (LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 18) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) || (((!defined(ISDEBIAN) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 119)) || LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 118)) && !defined(ISRHEL82)) #define ipv6_dst_lookup_flow(a, b, c, d) ipv6_dst_lookup(a, b, &dst, c) + (void *)0 ?: dst #endifСохраняем патч в файл "~/wgfix.patch" и накладываем на исходные тексты:
cd /usr/src/wireguard-1.0.20200429/ sudo patch -p2 < ~/wgfix.patchЕсли все прошло без ошибок, то пробуем продолжить установку:
sudo apt install -f
На этот раз установка должна пройти до конца.
Я надеюсь, что в скором времени пакет обновят, чтобы этот патч был уже не нужен.
Теперь можно немного расслабиться, потому что самое сложное позади. Установленное добро надо как-то настроить, чтобы использовать. Делал я это аж двумя способами.
Способ первый. Использование wg-quick.
Вместе с WireGuard устанавливается программа wg-quick и одноимённый сервис для systemd. Для описания туннелей она использует conf-файлы из каталога "/etc/wireguard".
Попробуем создать туннель.
Сначала необходимо сгенерировать пары ключей (публичный и приватный) для сервера и клиента, а также preshared-ключ. Команды я взял из найденных в сети руководств:
# каталог для ключей mkdir ~/wgkeys cd ~/wgkeys # preshared-ключ wg genpsk > wg.preshared # ключи сервера wg genkey | tee server.priv | wg pubkey > server.pub # ключи клиента wg genkey | tee client.priv | wg pubkey > client.pubТеперь можно писать файл конфигурации "/etc/wireguard/wg0.conf". wg0 - это имя сетевого интерфейса, который будет создан при активации туннеля. По структуре файл очень похож на ini-файл.
# конфигурация интерфейса [Interface] # адрес интерфейса на стороне сервера # и маска всей виртуальной сети Address = 10.18.0.1/24 PrivateKey = содержимое файла server.priv # UDP-порт, на котором сервер будет принимать соединения ListenPort = 35535 # конфигурация удалённого клиента [Peer] PublicKey = содержимое файла client.pub PresharedKey = содержимое файла wg.preshared # адреса для проброса через туннель # в данном случае только адрес клиента AllowedIPs = 10.18.0.2/32
При этом секций "[Peer]" может быть насколько, если клиентов больше одного. Для каждой нужно будет указать свой ключ и адрес, при этом preshared-ключ может быть у всех одинаковый.
Теперь можно запускать серверную часть:
sudo systemctl enable wg-quick@wg0 sudo systemctl start wg-quick@wg0Настройка клиентской части почти не отличается от серверной:
# конфигурация интерфейса [Interface] # адрес интерфейса на стороне сервера # и маска всей виртуальной сети Address = 10.18.0.2/32 PrivateKey = содержимое файла client.priv # конфигурация удалённого сервера [Peer] PublicKey = содержимое файла server.pub PresharedKey = содержимое файла wg.preshared # адреса для проброса через туннель # в данном случае весь диапазон виртуальной сети AllowedIPs = 10.18.0.0/24 # Адрес сервера и порт для соединения из параметра ListenPort сервера Endpoint = внешний-адрес-сервера:порт PersistentKeepalive = 20
Туннель на клиенте запускается тем же способом, что и на сервере.
Способ второй. Использование systemd-networkd.
Если для управления сетью используется systemd-networkd, то для организация туннеля можно написать всего два юнита. У себя я сделал подобное для домашнего сервера, поэтому опишу только конфигурацию со стороны сервера.
Первый юнит - это описание сетевого устройства. У меня он называется "/etc/systemd/network/14-wg1.netdev".
[NetDev] Name = wg1 Kind = wireguard Description = wg server home [WireGuard] PrivateKey = приватный ключ сервера ListenPort = порт [WireGuardPeer] PublicKey = публичный ключ клиента AllowedIPs = 10.138.0.2/32 PresharedKey = preshared-ключВторой юнит - это описание сети. У меня он называется "/etc/systemd/network/14-wg1.network".
[Match] Name = wg1 [Network] # адрес интерфейса Address = 10.138.0.1/32 [Route] # маршрут Gateway = 10.138.0.1 Destination = 10.138.0.0/24После этого достаточно перезапустить сервис, чтобы появился новый интерфейс.
sudo systemctl restart systemd-networkdЕсли на сервере и клиенте всё правильно получилось сделать, то при просмотре состояния туннеля при активном соединении должна быть примерно такая информация:
sudo wg show wg0 interface: wg0 public key: публичный ключ сервера private key: (hidden) listening port: 35535 peer: публичный ключ клиента preshared key: (hidden) endpoint: внешний адрес и порт клиента allowed ips: 10.18.0.3/32 latest handshake: 1 minute, 6 seconds ago transfer: 23.24 KiB received, 3.71 KiB sent
То есть соединение между узлами есть, и трафик циркулирует в обе стороны.
Я пока не пробовал соединять сети через подобный туннель и не нашёл способа заставить работать подключение только по IPv4 без явного указания ip-адреса вместо доменного имени.
UPD: 10 мая 2020 года вышло обновление, для которого патч уже не требуется.