Настройка Wireguard VPN сервера с маршрутизацией¶
Проблематика:¶
Есть два VPS сервера:
- в Швеции(1cpu, 1G, 10G): для доступа к ресурсам, заблокированным для пользователей РФ
- в России(2cpu, 2G, 25G): для доступа к домашним ресурсам, потому что домашний сервер за NAT, а арендовать выделенный ip адрес у провайдера по цене vps - не логично
Если на каждом VPS поднимать wireguard сервер, будет крайне неудобно в плане переключения на ноутбуке когда находишься вне дома, когда нужно воспользоваться теми или иными ресурсами.
Задача:¶
Необходимо сделать так, чтобы при одном настроенном wireguard подключении на ноутбуке были доступны все внутренние ресурсы wireguard, а весь остальной трафик направлялся через заграничного провайдера.
Описание:¶
У нас есть ноутбук с запущенным клиентом WireGuard, Endpoint A, с которого мы хотим получить доступ в Internet(на схеме).
Чтобы WireGuard-трафик мог дойти от Endpoint A до Интернета в этом сценарии, ему нужно пройти два перехода: один через wg_hub, Host B; и второй через wg_gate Host C. Хотя мы и хотим использовать Host C в качестве шлюза в Internet для Endpoint A, мы этого не хотим для Host B.
ENDPOINT A¶
На Endpoint A, когда сеть WireGuard активна, мы хотим отправлять весь Интернет-трафик через Host B, поэтому мы настраиваем AllowedIPs = 0.0.0.0/0 для Host B в конфигурации WireGuard Endpoint A:
[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Address = 10.0.1.3/32
DNS = 10.100.1.2, 10.100.1.1
[Peer]
PublicKey = PublicKeyHost-B
AllowedIPs = 0.0.0.0/0
Endpoint = HOSTNAME-B:PORT-B
PersistentKeepalive = 20
Если мы хотим использовать DNS, который нам выдает например мобильный оператор - просто убираем строку DNS = ...
ENDPOINT B¶
Теперь нужно сделать так, чтобы:
- весь трафик, который приходит на наш wireguard интерфейс wg0 отправлялся на host C
- трафик предназначенный для 10.100.1.0.24 (Home-Lan) направлялся в host D
Поэтому нужно настроить
- для хоста A: AllowedIPs = 10.0.1.3/32
- для хоста C: AllowedIPs=0.0.0.0/0
- для хоста D: AllowedIPs = 10.0.1.2/32, 10.100.1.0/24
Поскольку мы не хотим, чтобы весь трафик хоста B отправлялся на хост C, а мы хотим отправлять только трафик, который forward через wireguard сеть - поэтому - сконфигурируем маршруты для этого wireguard интерфейса, для этого используем custom routing table (через указание в настройках Table=123 для wg интерфейса, Table = 123 в конфиге wg0.conf указывает что маршруты будут добавляться в таблицу маршрутизации с идентификатором 123 для этого интерфейса WireGuard). По умолчанию маршруты добавляются в таблицу по умолчанию main с идентификатором 0. Когда WireGuard интерфейс активируется, он автоматически добавляет маршруты на основе AllowedIPs https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
- используем PreUp команду добавления правила маршрутизации, которое указывает хосту использовать эту таблицу только для трафика приходящего из wireguard интерфейса (
ip rule add iif wg0 table 123 priority 456
) - если перед этим правилом не добавить
PreUp = ip rule add to 172.31.0.0/16 table main priority 444
, то изнутри контейнеров не будет доступа до 10.100.1.2 например, потому что без этого правила пакеты будут уходить через main а возвращаться на table 123 и уходить на 10.0.1.100, а не внутрь докер сети, поэтому мы и добавляем это правило перед ip rule add iif wg0 table 123 priority 456 Не забываем добавить для каждого PreUp - PostDown
Для того чтобы хост B стал доступен (пинг его или SSH например) через wireguard соединение с одной или нескольких точек wg, вместо того, чтобы просто перенаправлять трафик через него - также необходимо добавить один или несколько маршрутов в основную таблицу маршрутизации хоста B, которая позволяет ему знать, как маршрутизировать свой собственный трафик в сеть WireGuard. Потому что маршруты теперь добавляются в table 123, которая только для iff wg0, а в main их нет.
Но если хост В в пределах сети до которой нужно открыть маршрутом, то можно в интерфейсе wg расширить маску до 24 и оставить только маршрут PostUp = ip route add 10.100.1.0/24 dev wg0 то есть если Address = 10.0.1.1/32 то нужно указать PostUp = ip route add 10.0.1.0/24 dev wg0 а если Address =10.0.1.1/24 то не должен быть PostUp = ip route add 10.0.1.0/24 dev wg0 иначе будет ошибка[Interface]
PrivateKey = {{ wg_server_privkey }}
Address = {{ wg_ip }}/{{ wg_mask_bits }}
ListenPort = {{ wg_server_port }}
{% if wg_hub %}
Table = 123
# IPv4 forwarding & routing
PreUp = sysctl -w net.ipv4.ip_forward=1
PreUp = ip rule add iif wg0 table 123 priority 456
PostDown = ip rule del iif wg0 table 123 priority 456
PostUp = ip route add 10.0.1.0/24 dev wg0
PostUp = ip route add 10.100.1.0/24 dev wg0
# PostDown = ip route del 10.100.1.0/24 dev wg0
{% endif %}
default dev wg0 scope link
. Он указывает, что весь исходящий трафик должен направляться через интерфейс wg0
Т.е вспоминаем, на хосте А был задан маршрут по умолчанию, который повел весь трафик в wg, а на хосте B этот трафик принялся на входящем порту и далее был снова задан маршрут по умолчанию, который дальше повел весь трафик в wg(так как приоритет таблицы маршрутизации 123 есть 456 < 32766(для таблицы main), поэтому маршрут по умолчанию wg а не в таблице main )
Как же ядро выбирает, в какую таблицу отправлять пакеты? Все логично – для этого есть правила.
В нашем случае:
$ ip rule list
0: from all lookup local
456: from all iif wg0 lookup 123
32766: from all lookup main
32767: from all lookup default
ENDPOINT C¶
На wg_gate (host C) Добавим PersistentKeepalive=25 , чтобы - пробивать отверстия через NAT-файрвол "pokes a hole through the NAT firewall" (некоторые NAT-файерволы могут закрывать соединения из-за отсутствия активности трафика. Отправка пустых пакетов (keepalive) помогает сохранять состояние открытого порта на NAT-файрволе, что позволяет данным узлам поддерживать активное соединение.) - eсли между двумя узлами нет активного обмена данными (например, если нет регулярной передачи реального трафика), keepalive-пакеты гарантируют, что соединение остается открытым и готовым к передаче данных в любое время
Весь трафик, который приходит на wireguard маркируем и потом в postrouting для не wg0 интерфейса делаем маскарадинг.
- name: Insert PostUp and PostDown in wg0.conf
blockinfile:
path: /etc/wireguard/wg0.conf
insertafter: '^(DNS|ListenPort|Address).*'
block: |
{% if wg_gate == true %}
# IPv4 forwarding
PreUp = sysctl -w net.ipv4.ip_forward=1
# IPv4 masquerading
PreUp = iptables -t mangle -A PREROUTING -i wg0 -j MARK --set-mark 0x30
PreUp = iptables -t nat -A POSTROUTING ! -o wg0 -m mark --mark 0x30 -j MASQUERADE
PostDown = iptables -t mangle -D PREROUTING -i wg0 -j MARK --set-mark 0x30
PostDown = iptables -t nat -D POSTROUTING ! -o wg0 -m mark --mark 0x30 -j MASQUERADE
{% endif %}
Проверка¶
На хосте А запустим и увидим редирект
$ curl google.com
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
...
Debugging¶
The netstat-nat command display the natted connections on a Linux iptable firewall:
netstat-nat -n
To display SNAT connections, run:
netstat-nat -S
To display DNAT connections, type:
netstat-nat -D
Please note that you may get the following message on the latest version of Linux:
Could not read info about connections from the kernel, make sure netfilter is enabled in kernel or by modules.
Then use the conntrack command:
sudo conntrack -L # List/dump
sudo conntrack -L -n # Filter source NAT connections
sudo conntrack -L -g # Filter destination NAT connections
sudo conntrack -L -j # Filter any NAT connection
Особенно важно в туннелях учитывать фрагментацию пакетов TCP при передаче данных через сети с разным MTU, таких как WireGuard, где дополнительные заголовки могут уменьшать эффективный MTU. Вот шаги для правильной настройки MSS:
1. Делаем ``tcpdump -i wg0``
После начальной успешной передачи, сервер продолжает отправлять данные, но клиент перестает отвечать на них.
Сервер многократно пытается отправить те же данные:
- Определить mtu
Linux ping -s 1300 -M do 10.0.1.100
MacOS ping -D -s 1300 10.0.1.100
При 1301 пинг уже не идет без фрагметации, поэтому MTU=1300+8+20=1328 Как определить оптимальный размер MTU
-
Уменьшаем mtu на клиентах wg
ip link set wg0 mtu 1328
-
Можем посмотреть на пути уменьшает ли кто-то mtu
apt install iputils-tracepath && tracepath $IP