Настройка роутера Debian 13: systemd-networkd, nftables, WiFi
Подробное руководство по настройке Debian 13 как роутера с systemd-networkd и nftables. Добавление WiFi в локальную сеть 10.0.1.0/24, устранение конфликтов маршрутов, примеры .network файлов, мост br0 и правила nftables для NAT и форварда.
Настройка трех сетевых интерфейсов в Debian 13 с systemd-networkd и nftables: добавление WiFi в локальную сеть с роутингом
Описание проблемы:
У меня сервер на Debian 13 работает как роутер: раздает интернет из сети провайдера (enp2s0f1) в локальную сеть (enp2s0f0). Хочу добавить USB WiFi-адаптер (wlx002e2d304ea1) в ту же локальную сеть (10.0.1.0/24), чтобы он тоже раздавал интернет по воздуху. При включении WiFi сервер теряет доступ в локальную сеть через enp2s0f0. Подозреваю проблему с маршрутами.
Конфигурация интерфейсов:
- enp2s0f0 (локальная сеть): IP 10.0.1.1/24
- enp2s0f1 (провайдер): IP 172.16.16.162/22, шлюз 172.16.16.1
- wlx002e2d304ea1 (WiFi): IP 10.0.1.10/24
Текущие файлы /etc/systemd/network/:
10-enp2s0f0.network:
[Match]
Name = enp2s0f0
[Network]
Address=10.0.1.1/24
[Route]
Gateway=10.0.1.1
Destination=10.0.0.0/24
Metric=100
Scope=link
20-enp2s0f1.network:
[Match]
Name = enp2s0f1
[Network]
Address=172.16.16.162/22
Gateway=172.16.16.1
DNS=77.88.8.8
DNS=77.88.8.1
[Route]
Gateway=172.16.16.1
Destination=0.0.0.0/0
Metric=0
30-wlx002e2d304ea1.network:
[Match]
Name = wlx002e2d304ea1
[Network]
Address=10.0.1.10/24
[Route]
Gateway=10.0.1.1
Destination=10.0.0.0/24
Metric=110
Scope=link
Вывод ifconfig:
enp2s0f0: flags=4163 mtu 1500
inet 10.0.1.1 netmask 255.255.255.0 broadcast 10.0.1.255
...
enp2s0f1: flags=4163 mtu 1500
inet 172.16.16.162 netmask 255.255.252.0 broadcast 172.16.19.255
...
wlx002e2d304ea1: flags=4163 mtu 1500
inet 10.0.1.10 netmask 255.255.255.0 broadcast 10.0.1.255
...
Вывод ip r:
default via 172.16.16.1 dev enp2s0f1 proto static
10.0.0.0/24 via 10.0.1.1 dev enp2s0f0 proto static scope link metric 100
10.0.1.0/24 dev enp2s0f0 proto kernel scope link src 10.0.1.1
10.0.1.0/24 dev wlx002e2d304ea1 proto kernel scope link src 10.0.1.10
172.16.16.0/22 dev enp2s0f1 proto kernel scope link src 172.16.16.162
Вопрос: Как правильно настроить маршруты в systemd-networkd (или nftables), чтобы все три интерфейса работали корректно? WiFi должен быть в той же подсети 10.0.1.0/24, роутить трафик через enp2s0f1, без конфликтов маршрутов? Приведите примеры конфигураций .network файлов и nftables правил для роутера.
Для Debian роутера оптимально объединить enp2s0f0 и wlx002e2d304ea1 в один L2‑мост (br0) и держать IP 10.0.1.1 на мосту: так systemd-networkd не создаёт конфликтующих «connected» маршрутов, а nftables выполняет NAT/фильтрацию и роутинг через enp2s0f1. Если мост невозможен, альтернативный путь — выделить отдельную подсеть для WiFi и настроить masquerade; остающиеся статические [Route] на локальные подсети лучше удалить (они создают ошибочные маршруты).
Содержание
- Короткое решение
- Почему возникает проблема с маршрутами
- Решение (рекомендуется): мост br0 с systemd-networkd
- Альтернатива: отдельная подсеть для WiFi и NAT (masquerade)
- Примеры конфигураций systemd-networkd (.netdev/.network)
- Пример /etc/nftables.conf — NAT и фильтрация для роутера
- Порядок запуска сервисов и полезные настройки (sysctl, systemd)
- Проверка и отладка — команды и что смотреть
- Источники
- Заключение
Короткое решение
Лучше всего сделать мост br0 и назначить 10.0.1.1 на сам мост; затем добавить enp2s0f0 и wlx002e2d304ea1 в мост. Уберите вручную добавленные [Route] для локальной подсети — они лишние и часто неправильно настроены. nftables настраивается на разрешение форварда между br0 и WAN (enp2s0f1) и — при необходимости — на маскарадинг исходящего трафика.
Почему возникает проблема с маршрутами
Когда у вас два интерфейса с адресами в одной подсети (10.0.1.1/24 и 10.0.1.10/24), ядро создаёт по одному «connected» маршруту на каждый интерфейс (как в вашем выводе). Получаются дублирующие записи вида:
- 10.0.1.0/24 dev enp2s0f0 …
- 10.0.1.0/24 dev wlx002e2d304ea1 …
Это порождает неоднозначность ARP/маршрутизации: пакеты могут отправляться на одно устройство, а ответы приходить через другое — и вы теряете связь с локальной сетью. Дополнительно ваши ручные [Route] секции (Destination=10.0.0.0/24 Gateway=10.0.1.1) выглядят некорректно и не нужны — они только усугубляют проблему. Подробнее о том, как systemd-networkd управляет маршрутами и зачем использовать Scope/маршрутные правила, описано в обсуждении по systemd-networkd и маршрутам: https://unix.stackexchange.com/questions/664975/systemd-networkd-routing-configuration-with-two-interfaces
Решение (рекомендуется): мост br0 с systemd-networkd
Идея простая: сделать единый L2‑сегмент — мост br0 — и назначить IP 10.0.1.1 на мост. Тогда в таблице маршрутов появится ровно одна запись 10.0.1.0/24 dev br0, конфликтов не будет, ARP работает корректно, клиенты WiFi — в той же подсети.
Плюсы:
- простая архитектура: WiFi и LAN — один сегмент;
- DHCP/ARP/броадкасты работают как ожидается;
- systemd-networkd управляет мостом корректно.
Минусы/ограничения:
- некоторые USB WiFi чипы/драйверы/режимы AP плохо работают с мостом (редко, но бывает). Если не сработает — см. раздел «Альтернатива».
Что сделать — шаги:
- Создать netdev для моста:
/etc/systemd/network/00-br0.netdev
[NetDev]
Name=br0
Kind=bridge
- Назначить IP на мост:
/etc/systemd/network/10-br0.network
[Match]
Name=br0
[Network]
Address=10.0.1.1/24
- Подключить enp2s0f0 и WiFi-порт к мосту (без адресов на самих интерфейсах):
/etc/systemd/network/10-enp2s0f0.network
[Match]
Name=enp2s0f0
[Network]
Bridge=br0
/etc/systemd/network/30-wlx002e2d304ea1.network
[Match]
Name=wlx002e2d304ea1
[Network]
Bridge=br0
(Для устойчивых имён лучше матчить по MAC: в [Match] можно использовать MACAddress=00:2e:2d:30:4e:a1)
- Оставить WAN (enp2s0f1) как есть — он даёт default route:
/etc/systemd/network/20-enp2s0f1.network
[Match]
Name=enp2s0f1
[Network]
Address=172.16.16.162/22
Gateway=172.16.16.1
DNS=77.88.8.8
DNS=77.88.8.1
(Уберите любые ваши [Route] для 10.x.x.x — они не нужны.)
- Перезапуск networkd и проверка (осторожно при удалённом доступе — заранее убедитесь, что есть консоль или отложенная команда для возврата):
sudo systemctl restart systemd-networkd
ip addr show br0
ip route
bridge link
Ожидаемый вывод маршрутов:
default via 172.16.16.1 dev enp2s0f1 proto static
10.0.1.0/24 dev br0 proto kernel scope link src 10.0.1.1
172.16.16.0/22 dev enp2s0f1 proto kernel scope link src 172.16.16.162
Если вы используете hostapd — привяжите интерфейс к мосту в hostapd.conf:
interface=wlx002e2d304ea1
bridge=br0
driver=nl80211
ssid=MySSID
wpa=2
wpa_passphrase=SuperSecret
Подробнее про то, как Debian и nftables работают в подобных сценариях, смотрите в официальной вики по nftables: https://wiki.debian.org/nftables
Альтернатива: отдельная подсеть для WiFi и NAT (masquerade)
Если по аппаратным причинам вы не можете сделать мост (драйвер WiFi не поддерживает нужный режим, или AP‑режим/bridge конфликтует), второй, надёжный вариант — выделить другую подсеть для WiFi (например 10.0.2.0/24) и настроить маскарадинг (NAT) на outbound через enp2s0f1. Тогда маршруты будут однозначны, и WiFi‑клиенты будут видеть Интернет через NAT.
Пример /etc/systemd/network/30-wlx002e2d304ea1.network для этого варианта:
[Match]
Name=wlx002e2d304ea1
[Network]
Address=10.0.2.1/24
Запустить DHCP-сервер для 10.0.2.0/24 (isc-dhcp-server, dnsmasq и т. п.) и в nftables включить masquerade для исходящего трафика (см. пример ниже). Этот подход прост и надёжен, но клиенты WiFi будут в отдельной подсети (не в 10.0.1.0/24).
Примеры конфигураций systemd-networkd (.netdev/.network)
Ниже — минимальные файлы, которые можно положить в /etc/systemd/network/. Перед внесением — сделайте резервные копии текущих файлов.
- /etc/systemd/network/00-br0.netdev
[NetDev]
Name=br0
Kind=bridge
- /etc/systemd/network/10-br0.network
[Match]
Name=br0
[Network]
Address=10.0.1.1/24
- /etc/systemd/network/10-enp2s0f0.network
[Match]
Name=enp2s0f0
[Network]
Bridge=br0
- /etc/systemd/network/30-wlx002e2d304ea1.network
[Match]
Name=wlx002e2d304ea1
[Network]
Bridge=br0
- /etc/systemd/network/20-enp2s0f1.network
[Match]
Name=enp2s0f1
[Network]
Address=172.16.16.162/22
Gateway=172.16.16.1
DNS=77.88.8.8
DNS=77.88.8.1
Комментарий: удалите из старых файлов секции [Route] для 10.0.0.0/24 / 10.0.1.0/24 — они не нужны и, в вашем примере, содержат явную ошибку (Destination=10.0.0.0/24 при работе в 10.0.1.0/24). Systemd/ядро создадут подключённый маршрут автоматически на br0.
Пример /etc/nftables.conf — NAT и фильтрация для роутера
Ниже — минимальный, безопасный набор правил: разрешить established/related, форвард между LAN и WAN, маскарадинг исходящего трафика через enp2s0f1.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iifname "lo" accept
# SSH из LAN
iifname "br0" tcp dport 22 accept
# ICMP
ip protocol icmp accept
# локальный трафик, сервисы на сервере — разрешите по необходимости
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
iifname "br0" oifname "enp2s0f1" accept
iifname "enp2s0f1" oifname "br0" accept
}
chain output {
type filter hook output priority 0; policy accept;
}
}
table ip nat {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname "enp2s0f1" masquerade
}
}
Сохраните как /etc/nftables.conf, затем:
sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables
Подробные примеры и рекомендации есть в Debian wiki по nftables: https://wiki.debian.org/nftables и в Debian Handbook о файрволах: https://debian-handbook.info/browse/hr-HR/stable/sect.firewall-packet-filtering.html
Порядок запуска сервисов и полезные настройки (sysctl, systemd)
- Чтобы nftables не загрузился слишком рано (и не закрыл доступ из‑за отсутствия интерфейсов), можно сделать override для unit:
sudo systemctl edit --full nftables.service
и добавить в секцию [Unit]:
Wants=network-online.target
After=network-online.target
Либо создать drop-in:
[Unit]
Wants=network-online.target
After=network-online.target
Это рекомендует практика — см. обсуждение порядка загрузки firewall после сети: https://bbs.archlinux.org/viewtopic.php?id=209520
- Включите пересылку пакетов и настройте rp_filter:
/etc/sysctl.d/99-router.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.br0.rp_filter = 0
net.ipv4.conf.enp2s0f1.rp_filter = 0
Затем:
sudo sysctl --system
Почему rp_filter=0? Reverse Path Filtering может «отбрасывать» пакеты при наличии нескольких интерфейсов/маршрутов; при мостовой конфигурации это обычно безопасно и устраняет потери пакетов. (Если вы предпочитаете более строгую конфигурацию — настройте rp_filter выборочно.)
- Если требуется дождаться реального «онлайн» состояния сети перед выполнением nftables/других сервисов, включите wait-online:
sudo systemctl enable --now systemd-networkd-wait-online.service
Осторожно: wait-online может задерживать загрузку, если какие‑то интерфейсы не приходят в состояние online.
Проверка и отладка — команды и что смотреть
- Состояние интерфейсов:
ip addr
bridge link
- Таблица маршрутов:
ip route show
Ожидается одна запись 10.0.1.0/24 dev br0
- Правила nftables:
sudo nft list ruleset
- Логи systemd-networkd и nftables:
journalctl -u systemd-networkd -f
journalctl -u nftables -f
- Если вы работаете по SSH — сделайте всё по шагам и сначала проверьте на локальной консоли. Ошибка при замене сетевых файлов может временно отрубить доступ.
Источники
- https://wiki.debian.org/nftables
- https://debian-handbook.info/browse/hr-HR/stable/sect.firewall-packet-filtering.html
- https://unix.stackexchange.com/questions/664975/systemd-networkd-routing-configuration-with-two-interfaces
- https://bbs.archlinux.org/viewtopic.php?id=209520
- https://bbs.archlinux.org/viewtopic.php?id=270165
Заключение
Для стабильной работы Debian роутера с несколькими интерфейсами и WiFi в той же подсети лучше всего объединить порты в мост br0 через systemd-networkd и держать IP 10.0.1.1 на мосту; nftables должен обеспечить форвард и masquerade на enp2s0f1. Удалите ошибочные [Route] для локальной подсети, включите ip_forward и скорректируйте rp_filter по необходимости — и маршрутизация заработает предсказуемо. Если мост невозможен по аппаратным причинам, используйте отдельную подсеть для WiFi + NAT.