Что именно мы собираем и где чаще всего ломается
WireGuard сам по себе — это «просто интерфейс» (обычно wg0) и набор маршрутов, которые добавляются на основании AllowedIPs. А nftables — это фильтрация и NAT. Ошибки появляются на стыке: маршруты есть, туннель поднят, но трафик не ходит из-за forward, не работает NAT, или пакеты молча дропает rp_filter при асимметрии маршрутов (особенно когда включаем policy routing).
Ниже — практическая конфигурация для трёх самых частых задач:
- Клиенты WireGuard выходят в интернет через сервер (NAT
masqueradeна внешнем интерфейсе). - LAN to VPN: из локальной сети (или контейнерной сети) отправляем часть/весь трафик в VPN через
wg0. - Policy routing: часть трафика идёт через VPN, остальное — напрямую, причём разделение делается не только по подсети, но и по источнику/марке.
Будем опираться на nftables с таблицами inet (фильтрация) и ip/ip6 (NAT). Примеры подойдут для Debian/Ubuntu и большинства современных дистрибутивов.
Минимальные вводные: интерфейсы, адреса и sysctl
Для примеров примем такие обозначения:
eth0— внешний интерфейс сервера (интернет).wg0— WireGuard.- VPN-сеть:
10.7.0.0/24, сервер в ней10.7.0.1. - LAN (или внутренняя сеть, которую нужно отправлять в VPN):
192.168.50.0/24.
Сразу включим маршрутизацию и приведём rp_filter в режим, совместимый с policy routing (иначе при «нестандартном» обратном пути пакеты будут отбрасываться как спуфинг).
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.conf.default.rp_filter=2
sysctl -w net.ipv4.conf.wg0.rp_filter=2
Значение rp_filter=2 — «loose mode»: проверка источника менее строгая и не ломает асимметричные маршруты. Если policy routing не используете, можно оставить 1, но на практике на VPN-шлюзах чаще безопаснее и стабильнее именно 2 (при условии, что фильтрация и политики на firewall настроены корректно).
Проверьте, что интерфейс wg0 реально поднят и имеет адрес:
wg show
ip addr show dev wg0
ip route

WireGuard: как AllowedIPs влияет на маршруты и доступность сетей
AllowedIPs — это одновременно:
- список «разрешённых» подсетей для конкретного peer (что принимать из туннеля);
- и подсказка для роутинга (что отправлять в этот peer).
Типичная серверная конфигурация (упрощённо) может выглядеть так (показываю как текст; в реальности это файл /etc/wireguard/wg0.conf):
[Interface]
Address = 10.7.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
[Peer]
PublicKey = CLIENT1_PUBLIC_KEY
AllowedIPs = 10.7.0.2/32
На клиенте, если хотите «весь интернет через VPN» (full-tunnel), часто ставят AllowedIPs = 0.0.0.0/0. Если нужен split-tunnel — указывают только нужные подсети (например, доступ к офису/серверной сети). Типичная ошибка «туннель есть, а трафик не уходит» — нужная сеть не попала в AllowedIPs, и маршрута просто нет.
Для сценария lan to vpn (когда не клиент, а сервер/шлюз отправляет LAN в VPN) маршруты удобно делать вручную через ip route + ip rule. Это даёт гибкость, но требует аккуратности с nftables и rp_filter.
nftables: базовая фильтрация для WireGuard (input/forward)
Обычно на сервере нужно разрешить:
- входящий UDP на порт WireGuard (например,
51820/udp) на внешнем интерфейсе; - форвардинг между
wg0иeth0(или междуwg0и LAN-интерфейсом), если сервер работает как маршрутизатор; - обратный трафик (stateful).
Ниже — компактный, но рабочий каркас. Важно: политика по умолчанию drop, разрешаем только необходимое. Угловые скобки в примерах экранированы, чтобы конфиг можно было копировать в терминал без «сюрпризов» от редакторов.
nft -f /dev/stdin << 'EOF'
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
policy drop;
iif "lo" accept
ct state established,related accept
# SSH (пример)
tcp dport 22 accept
# WireGuard
udp dport 51820 accept
# ICMP полезен для диагностики MTU/PMTU
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
}
chain forward {
type filter hook forward priority 0;
policy drop;
ct state established,related accept
# Разрешаем трафик из VPN в интернет
iif "wg0" oif "eth0" accept
# Опционально: интернет в VPN (нужно только если вы реально публикуете сети за peer)
iif "eth0" oif "wg0" accept
}
chain output {
type filter hook output priority 0;
policy accept;
}
}
EOF
Ключевая точка здесь — цепочка forward. Очень частая ситуация: WireGuard поднят, маршруты есть, но forward по умолчанию drop (или вообще не настроен), и «lan to vpn» не работает.
NAT masquerade для WireGuard: когда нужен и где включать
NAT на сервере нужен, когда ваши VPN-клиенты выходят в интернет через этот сервер и в интернете о них «не знают». Самый типичный сценарий: клиент получает адрес 10.7.0.2, идёт на сайты, а внешняя сеть видит только публичный IP сервера.
В nftables NAT обычно делается в таблице ip nat (и отдельно ip6 nat для IPv6, если вы используете NAT66 — что встречается реже и требует отдельного проектирования).
nft -f /dev/stdin << 'EOF'
table ip nat {
chain prerouting {
type nat hook prerouting priority -100;
policy accept;
}
chain postrouting {
type nat hook postrouting priority 100;
policy accept;
# NAT для клиентов WireGuard, выходящих в интернет через eth0
oif "eth0" ip saddr 10.7.0.0/24 masquerade
}
}
EOF
Проверка, что NAT реально срабатывает:
nft list ruleset
conntrack -L | head
tcpdump -ni eth0 host 8.8.8.8
Если интернет с клиента не работает, чаще всего причина одна из трёх:
- нет правила
masquerade(или не тотoif); - пакеты блокируются в
forward; - на клиенте не настроен DNS (туннель есть, но имена не резолвятся).
Если у вас много исходящих IP или сложная логика SNAT (например, разные подсети клиентов должны выходить разными адресами), пригодится отдельный разбор: как контролировать SNAT на нескольких IP в nftables.
LAN to VPN: отправляем локальную сеть в туннель через policy routing
Сценарий: у вас есть хост-шлюз с LAN-интерфейсом (или просто подсеть, приходящая на него через другой роутер), и вы хотите, чтобы 192.168.50.0/24 ходила в интернет или в удалённые сети через WireGuard.
Можно «сломать» default route и отправить весь трафик самого сервера в VPN, но чаще нужно аккуратно: только LAN — в VPN, а сам сервер продолжает жить напрямую (обновления, мониторинг, SSH и т.д.). Для этого подходит policy routing: отдельная таблица маршрутизации плюс ip rule.
Шаг 1. Создаём отдельную таблицу маршрутов
Добавим таблицу, например 100. В современных системах можно не редактировать /etc/iproute2/rt_tables, а просто использовать номер. Но если вы ведёте инфраструктуру командой, имя таблицы повышает читаемость (это уже на ваш вкус).
Маршрут по умолчанию в этой таблице — через wg0:
ip route add default dev wg0 table 100
Если вам нужно отправлять в VPN не весь интернет, а конкретные подсети за удалённой стороной, вместо default добавляйте конкретные маршруты, например:
ip route add 10.20.0.0/16 dev wg0 table 100
Шаг 2. Правило ip rule: кто пользуется этой таблицей
Самая понятная политика: весь трафик с источником 192.168.50.0/24 маршрутизируем по таблице 100.
ip rule add from 192.168.50.0/24 lookup 100 priority 1000
Проверяем:
ip rule show
ip route show table 100
ip route get 1.1.1.1 from 192.168.50.10
Шаг 3. nftables: разрешаем форвардинг LAN → wg0 и делаем NAT там, где нужно
Дальше зависит от того, требуется ли скрывать LAN-адреса в VPN. Обычно да: удалённая сторона не знает вашу 192.168.50.0/24, либо вы не хотите анонсировать маршруты. Тогда делаем NAT на выходе в wg0.
Правила фильтрации (forward):
nft add rule inet filter forward iif "lan0" oif "wg0" accept
nft add rule inet filter forward iif "wg0" oif "lan0" ct state established,related accept
Где lan0 — ваш интерфейс в локальную сеть (например, ens19, br0, vlan50).
NAT (если надо маскарадинг LAN в VPN):
nft add rule ip nat postrouting oif "wg0" ip saddr 192.168.50.0/24 masquerade
Важно: NAT на wg0 нужен не всегда. Если вы контролируете обе стороны и можете прописать маршруты, корректнее (и прозрачнее для логов) обойтись без NAT: на удалённой стороне добавить маршрут к вашей LAN через peer WireGuard. Но в реальности часто быстрее именно masquerade.
Policy routing по маркировке: когда одной сети from lookup мало
Иногда нужно отправлять через VPN не всю подсеть, а только отдельные сервисы или группы хостов, либо трафик, который вы предварительно пометили (например, по порту/назначению). Тогда удобно использовать fwmark и отдельную таблицу маршрутизации.
Схема такая:
- nftables помечает пакеты меткой (
meta mark); ip ruleвыбирает таблицу поfwmark;- таблица отправляет трафик в
wg0.
Пример: пометим весь трафик из LAN к конкретной сети (например, 203.0.113.0/24) и отправим его через VPN.
nft add chain inet filter mangle_prerouting '{ type filter hook prerouting priority -150; policy accept; }'
nft add rule inet filter mangle_prerouting iif "lan0" ip daddr 203.0.113.0/24 meta mark set 0x66
ip rule add fwmark 0x66 lookup 100 priority 900
ip route add default dev wg0 table 100
Замечания по практике:
- При маркировке в сложных сценариях важно продумать обратный трафик (иногда нужна синхронизация меток через conntrack:
ct mark). - Проверяйте
ip route getс указанием источника на тестовом хосте (или в network namespace), прежде чем переносить в прод.

Диагностика: что смотреть, когда «не работает»
Набор команд, который обычно быстро локализует проблему между WireGuard, маршрутизацией и nftables.
1) WireGuard живой?
wg show
ip -s link show dev wg0
Если RX/TX счётчики на wg0 не растут — проблема до firewall: ключи, endpoint, NAT у клиента, блокировка UDP/51820.
2) Маршрут выбран тот, который вы ожидаете?
ip rule show
ip route show table main
ip route show table 100
ip route get 8.8.8.8 from 192.168.50.10
ip route get — лучший друг при policy routing: он показывает, через какой интерфейс и какой next-hop ядро реально отправит пакет.
3) nftables реально пропускает?
nft list ruleset
nft list chain inet filter forward
Если сомневаетесь, временно добавьте логирование на forward (аккуратно, чтобы не залить диск):
nft add rule inet filter forward limit rate 5/second log prefix "FW " flags all
Если вы ещё не уверены в своей «базе» по nftables, полезно держать под рукой материал про защитные приёмы на уровне firewall: SYNPROXY и практичный firewall на VDS.
4) rp_filter не режет ли обратный путь?
sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.wg0.rp_filter
journalctl -k | tail -n 200
При «странных» маршрутах (особенно при split-tunnel и нескольких uplink’ах) rp_filter=1 — частая причина неочевидных потерь. Для VPN-шлюза с policy routing обычно выбирают 2 и компенсируют риски строгим firewall и отсутствием лишних маршрутов.
Частые грабли: короткий чек-лист
- AllowedIPs не соответствует задумке: нет маршрута к нужной сети или, наоборот, случайно «утянули» весь интернет в VPN.
- Нет разрешения в
forward: WireGuard работает, но маршрутизатор «не маршрутизирует». - NAT стоит не там: для «клиенты в интернет» NAT нужен на внешнем интерфейсе; для «lan to vpn» — часто на
wg0. rp_filterв strict mode при policy routing: пакеты уходят одним путём, возвращаются другим — и отбрасываются.- MTU: если часть сайтов/протоколов «подвисает», проверьте PMTU, попробуйте уменьшить MTU на
wg0(часто 1420 или ниже в зависимости от оверхеда).
Как это аккуратно сделать в проде
Разделяйте изменения на слои и проверяйте по шагам:
- Поднимите WireGuard и убедитесь, что handshake идёт и пакеты ходят между адресами VPN.
- Добавьте маршрутизацию (
ip route/ip rule) и проверьтеip route get. - Только потом включайте nftables
forwardи NAT, контролируя счётчики/логи.
Если вы делаете VPN-шлюз на сервере, который также обслуживает продакшен (веб, базы, почта), держите под рукой консольный доступ и план отката: policy routing может неожиданно «увести» исходящие соединения и отрезать вас от сервера, если ошибиться с приоритетами ip rule или default route в таблицах.
Идеальный результат — когда вы можете словами объяснить: какие пакеты попадают в
wg0, какие правила разрешают форвардинг, где именно применяетсяmasquerade, и какая таблица маршрутизации выбирается поip rule. Тогда конфигурация становится не магией, а предсказуемой системой.
Итог
Связка WireGuard + nftables на Linux отлично работает и в full-tunnel, и в split-tunnel, и в роли VPN-шлюза для LAN — если правильно разложить ответственность:
- WireGuard отвечает за туннель и (частично) маршруты через
AllowedIPs; - nftables отвечает за
forwardи NAT (masquerade); - policy routing (
ip rule,ip routeи отдельные таблицы) отвечает за то, кто и куда маршрутизируется черезwg0без поломки остальных потоков.
Если нужно — в следующем материале можно разобрать сохранение маркировок через conntrack (ct mark), несколько peer’ов с разными таблицами, а также IPv6-стратегию (без NAT и с корректным firewall).


