WireGuard

Материал из ALT Linux Wiki

Запуск через systemd-networkd

Запускать ваергарда в связке с systemd-networkd под Альтом проще, чем в убунтоподобных дистрибутивах с их нетпланом.

Сервер

Дальнейшее можно делать на любом лине (будь то железный комп, виртуалка или контейнер) вне зависимости от роли — сервер это, рабочая лошадка или гейм-приставка.

Чтобы всё это управлялось не доп.утилитами, а непосредственно сервисом systemd-networkd, данный сервис следует установить и запустить (см. статью), а пакеты etcnet (касается только альта) — отодвинуть (см. ту же статью) либо снести:

# apt-get remove -y `rpm -qa --qf "%{NAME}\n" etcnet*`

Для начала сгенерим в файл пару ключей — приватный (для себя) и публичный (для тех, с кем предстоит туннелироваться):

PRIV=`wg genkey`; PUB=`echo $PRIV | wg pubkey`; printf "PrivateKey = $PRIV\nPublicKey  = $PUB\n"

PublicKey = <хэш нашего публичного ключа> из файла /tmp/wgkeys рассылаем нашим партнёрам по суровому бизнесу, а PrivateKey = <хэш нашего приватного ключа> задействуем в первом из двух следующих конфигов:

/etc/systemd/network/wg.netdev  
[NetDev]
Name       = wg
Kind       = wireguard

# Сервер
[WireGuard]
ListenPort = 51820
PrivateKey = <хэш нашего приватного ключа из файла /tmp/wgkeys>

# Клиент, которых можно наплодить ощутимо больше одного
[WireGuardPeer]
PublicKey  = <публичный ключ нашего партнёра (пира)>
AllowedIPs = 192.168.123.253/32,192.168.1.0/24
# Первый адрес — сам клиент, второй — его внутренняя сеть.
/etc/systemd/network/wg.network  
[Match]
Name        = wg

# Адрес сервера: как для взаимодействия с клиентами, так и им между собой
[Network]
Address     = 192.168.123.254/24

# Пример маршрутизации в сеть клиента из предыдущего конфига
[Route]
Destination = 192.168.1.0/24
Gateway     = 192.168.123.254
Важно! Простая аналогия для новичков:
  • частный ключ ключом и является, и беречь его следует пуще зеницы ока;
  • а вот публичный — скорее не ключ, а замóк, которым можно раскидываться направо-налево: расхватывайте, мол, люди добрые, цепляйте на свои двери! А я в них войду...

Клиенты

Чтоб не мучиться с созданием конфига каждому сотруднику, нуждающемуся в доступе к локалке извне, накостылил скрипт:

/usr/local/bin/wgadd  
#!/bin/bash

[ "$#" -gt 0 ] || {
    echo "Type new key name without spaces/oversymbols, and optionally - device type (mobile, mac, windows or linux)"
    exit
}

ChatID="-1001хххххххх"  # ИД чатика
Theme=1253              # Тема в чатике
Token="ид бота:его жетон"
URL="https://api.telegram.org/bot$Token/sendDocument"
WD=/etc/systemd/network/clients.wg
cd $WD
USR=$1                  # имя доменной учётки клиента
Dev=$2                  # дивайсина [windows|linux|mac|mobile]
Net=192.168.123         # туннельная сеть
Priv=`wg genkey`
Publ=`echo "$Priv" | wg pubkey`
# Вычисление последнего октета для нового адреса:
IP=$[`dir -1 *.conf | sort -V | sed 's|\..*||' | head -1`-1]

Content="[Peer]
PublicKey = <наш публичный ключ>
Endpoint = <IP или имя нашего сервера>:<порт>
AllowedIPs = <н.а.ш.а>/<локалка>

[Interface]
PrivateKey = $Priv
Address = $Net.$IP/24
DNS = н.а.ш.и, д.н.с.ы, наш.домен.зона"

[ "$Dev" ] && case "$Dev" in
    linux) # можно добавить правила фаервола — и не обязательно именно iptables
        Content+="
#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"
    ;;
    mobile) # создание кукарекода из сгенерённого конфига для отправки его мобильному клиенту
        echo "$Content" | qrencode -t ansiutf8 >$IP.$USR.qr
        cat $USR.qr
    ;;
esac

echo "$Content" >$IP.$USR.conf # создание и отправка конфига в тему чатика админов
[ -s "$IP.$USR.conf" ] && curl -s $URL -F "document=@$WD/$IP.$USR.conf" \
    -F "chat_id=$ChatID" -F "reply_to_message_id=$Theme" 2>&1 >/dev/null

NewDev="
[WireGuardPeer]
# $USR $Dev
AllowedIPs  = $Net.$IP/32
PublicKey   = $Publ"

echo "$NewDev" >>../wg.netdev && {  # добавление нового клиента в ДНС
    echo "$Net.$IP  $USR.wg" >>/etc/hosts
    systemctl daemon-reload
    systemctl reload dnsmasq
    systemctl restart systemd-networkd
}

Помимо занесения вновь сгенерированного клиента в конфиг ваергарда, он также добавляется в локальный ДНС-кэш (dnsmasq), с которого основные внутренние ДНСы запрашивают зону .wg — таким образом, адреса клиентов туннеля становятся видны клиентам локалки по имени.

Готовый конфиг отправляется в соответствующую тему админского телеграм-чата.

Список последних подключённых

Тоже скрипт:

/usr/local/bin/wgdump  
#!/bin/sh

SIZE=1
for NAME in `awk '!/^(#|$)/{print $2}' /etc/hosts`; do
    [ $SIZE -ge ${#NAME} ] || SIZE=${#NAME}
done
LINE=`for ((i=$SIZE;i>0;i--)); do printf "─"; done`
SPACE=`for ((i=$SIZE-10;i>0;i--)); do printf " "; done`
case $LANG in
    ru_RU.UTF-8)
        date +"$LINE┬────────── IP-адреса ──────────┐%d/%m,%T┌────────"
        echo "${SPACE}Абонент   │  туннельный   ┬   реальный    │  Подключено  │ Длится";;
    *)  date +"$LINE┬───────────────┬───────────────┐%d/%m,%T┌────────"
        echo "${SPACE}host.wg   │   Tunnel IP   │    Real IP    │  Hand-shake  │ Uptime";;
esac
echo "$LINE┼───────────────┼───────────────┼──────────────┼────────"
while read OUT; do
    [ "$OUT" ] || continue
    FULL=($OUT)
    HOST=`awk '/'${FULL[1]}'/{print $2}' /etc/hosts`
    [ "$HOST" ] || HOST="unknown"
    LONG=$[ `date +%s`-${FULL[2]}]
    UPTIME=$(date +%T -ud @$LONG)
    case $(($LONG >   180 && $LONG <  2400 ? 1 :
            $LONG >= 2400 && $LONG < 86400 ? 2 :
            $LONG >= 86400                 ? 3 : 0)) in
        (0) MARK=32     ;;  # зелёный
        (1) MARK="1;34" ;;  # жирно-синий
        (2) MARK=31     ;;  # красный
#       (3) MARK="1;30"     # жирно-серый
        (3) MARK=29         # серый
            [ $LANG == ru_RU.UTF-8 ] &&
                UPTIME=" сутки+" ||
                UPTIME="  days"
    esac
    printf "\033[${MARK}m%-${SIZE}s│%-15s│%-15s│$(date +"%d/%m,%T" -d@$[${FULL[2]}])$UPTIME\033[0m\n" $HOST ${FULL[1]} ${FULL[0]}
done <<<`wg show wg dump | awk '!/\(none\)\t\(none\)|off/{print $3","$4","$5}' | sed 's|/.*,|\t|;s|:.*,|\t|' | sort -rk3`
echo "$LINE┴───────────────┴───────────────┴──────────────┴────────"
Пример вывода  
[root@vpn ~]# wgdump
─────────────┬────────── IP-адреса ──────────┐24/04,15:31:23┌────────
   Абонент   │  туннельный   ┬   реальный    │  Подключено  │ Длится
─────────────┼───────────────┼───────────────┼──────────────┼────────
shop-48      │192.168.252.148│77.243.118.89  │24/04,15:31:21│00:00:02
...
shop-65      │192.168.252.165│91.185.60.39   │24/04,15:29:24│00:01:59
─────────────┴───────────────┴───────────────┴──────────────┴────────

Мониторинг

Для журналирования активности ядрёного модуля достаточно на [хосте / всех контейнеризаторах кластера] либо в виртуалке с ваергардом создать юнит:

/lib/systemd/system/wg-log.service  
[Unit]
Description = WireGuard events logging
ConditionPathExists = /sys/kernel/debug/dynamic_debug/control
ConditionPathIsDirectory = /sys/module/wireguard
After = network.target systemd-modules-load.service

[Service]
Type = oneshot
RemainAfterExit = yes
ExecStart = echo module wireguard +p >/sys/kernel/debug/dynamic_debug/control

[Install]
WantedBy = multi-user.target

И запустить его навсегда командой # systemctl enable --now wg-log

Отслеживать текучку по $ journalctl -kf , а статистику подключений в контейнере с ваергардом — посредством wg-logger.

Хотя последнего в репозитории нет, но...

  1. Бинарник собирается легко.
  2. Для запуска службой хватит примитивного юнита /lib/systemd/system/wg-logger.service:
    [Unit]
    Description = WireGuard logger daemon
    After = network-online.target
    
    [Service]
    ExecStart = /usr/local/sbin/%N -d
    
    [Install]
    WantedBy = multi-user.target
    
  3. Подключения пишутся в текстовый журнал /var/log/wg-logger/events.log, который легко парсится awk'ом.

Проброс VXLAN

...описан отдельно.


Ниже — плоды коллективного творчества.

Запуск через wg-quick

  1. Создать свободную пару частного/публичного ключей (см. скрипт wgenkeys выше):
    $ wg genkey >privatekey
    $ wg pubkey <privatekey >publickey
    
    В файле privatekey текущей папки окажется частный ключ, а в файле publickey — публичный.
  2. Установить wg-quick: # apt-get install wireguard-tools-wg-quick
  3. Завести в каталоге /etc/wireguard (при отсутствии такового — создать) конфиг, одноимённый поднимаемому интерфейсу (в данном случае — /etc/wireguard/wg0.conf):
    [Interface]
    PrivateKey = частный ключ клиента
    # IP-адрес клиента (маска всегда /32)
    Address = 172.16.1.2/32
    
    # Для клиента пиром является сервер (что логично и взаимно)
    [Peer]
    PublicKey = <публичный ключ сервера>
    # Подсети с другими доступными узлами, для которых клиент является маршрутизатором:
    AllowedIPs = 172.16.1.0/23, 192.168.1.0/24
    # Реальный IP-адрес сервера с номером порта:
    Endpoint = IP-адрес:51820
    
  4. В конфиге сервера все клиенты должны быть указаны аналогичным образом — в качестве пиров.
    Да, серверу тоже нужны публичные ключи клиентов.
  5. Запуск, останов и мониторинг соединения возможны только от рута:
    # systemctl start|stop|restart wg-quick@wg0
    # wg show
    

Запуск через NetworkManager из KDE

Принцип создания соединения аналогичен варианту для консоли: требуются те же данные, что и для конфиг-файла, но вводить их нужно в нескольких окнах.

Запускаем приложение "Параметры системы KDE5":

  • Alt+F2,
  • во всплывшем окне начинаем набирать systemsettings5,
  • Enter на появившемся пункте.

Выбираем в окне вкладку "Соединения" (группа "Сеть и связь"), "+" для создания нового.

Во всплывшем окне:

  • прокручиваем до группы "VPN-соединения",
  • выбираем "WireGuard",
  • нажимаем "Создать".

В окне "Новое соединение" вводим имя соединения (например, wg0, соответственно названию интерфейса). Окно открывается на вкладке "Интерфейс WireGuard", на ней в поле "личный ключ" вводим PrivateKey клиента и выбираем "Сохранить пароль только для этого пользователя (зашифрованный)".

Остальные поля можно оставить пустыми.

Далее — кнопка "Участники обмена…", во вновь появившемся окне ввести данные для пира (сервера):

  • в поле "Открытый ключ" — PublicKey сервера,
  • в поле "Разрешенные адреса IP" — AllowedIPs,
  • в поля "Адрес подключения" и "Порт подключения" — реальный (видимый из Интернета) IP-адрес сервера с номером порта;
  • поле "Интервал отправки пакетов keepalive" соответствует опции PersistentKeepalive конфига, но не сохраняется — что и не нужно: интервал задаёт сервер.

Остальные поля не нужны. Просто жмем OK.

Вкладка "IPv4":

  • метод "Вручную",
  • кнопка "+ Добавить",
  • вводим IP-адрес клиента и маску подсети (255.255.255.255 для одиночного адреса или 0.0.0.0 для шлюза),
  • галка "Для этого соединения требуется IPv4".

Вкладка "IPv6":

  • метод "Отключено" (если, конечно, не используете этот протокол).

Вкладка "Основные параметры":

  • снять галку "Все пользователи могут подключаться к данной сети" (так как мы всё равно выбрали хранить пароль только для этого пользователя),
  • остальное оставить как есть.

Жмем "Сохранить". Теперь соединение можно подключать и отключать из иконки сетевых соединений в системном лотке.

Чтобы увидеть результат настроек в едином конфиг-файле, можно использовать команду (замените wg0 на имя вашего соединения):

# wg showconf wg0

Соединение должно быть активно.

Обратная связь