Сети

Настройка роутера 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 и назначить 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 плохо работают с мостом (редко, но бывает). Если не сработает — см. раздел «Альтернатива».

Что сделать — шаги:

  1. Создать netdev для моста:
/etc/systemd/network/00-br0.netdev
[NetDev]
Name=br0
Kind=bridge
  1. Назначить IP на мост:
/etc/systemd/network/10-br0.network
[Match]
Name=br0

[Network]
Address=10.0.1.1/24
  1. Подключить 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)

  1. Оставить 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 — они не нужны.)

  1. Перезапуск 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/. Перед внесением — сделайте резервные копии текущих файлов.

  1. /etc/systemd/network/00-br0.netdev
[NetDev]
Name=br0
Kind=bridge
  1. /etc/systemd/network/10-br0.network
[Match]
Name=br0

[Network]
Address=10.0.1.1/24
  1. /etc/systemd/network/10-enp2s0f0.network
[Match]
Name=enp2s0f0

[Network]
Bridge=br0
  1. /etc/systemd/network/30-wlx002e2d304ea1.network
[Match]
Name=wlx002e2d304ea1

[Network]
Bridge=br0
  1. /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 — сделайте всё по шагам и сначала проверьте на локальной консоли. Ошибка при замене сетевых файлов может временно отрубить доступ.

Источники


Заключение

Для стабильной работы Debian роутера с несколькими интерфейсами и WiFi в той же подсети лучше всего объединить порты в мост br0 через systemd-networkd и держать IP 10.0.1.1 на мосту; nftables должен обеспечить форвард и masquerade на enp2s0f1. Удалите ошибочные [Route] для локальной подсети, включите ip_forward и скорректируйте rp_filter по необходимости — и маршрутизация заработает предсказуемо. Если мост невозможен по аппаратным причинам, используйте отдельную подсеть для WiFi + NAT.

Авторы
Проверено модерацией
Модерация