Задумал я как-то настроить себе сетевой роутер под Linux-машиной. Чтобы через неё гнался весь трафик, а остальные машины получали бы адрес по DHCP и DNS запросы также обрабатывались бы этой машиной. Начал гуглить. Изначально мысль была просто настроить Netplan, но по ходу настройки я столкнулся с тем, что iptables всё-равно настраивать пришлось. Сложных DNS запросов мне делать не нужно, сеть будет состоять максимум из 10 - 15 машин, но DNS-сервер всё-равно нужен - периодически будет появляться один-два сетевых HTTP-сервера. Это - испытательный стенд, и не хотелось бы "гадить" в сеть. Гуглил я долго, и информацию пришлось собирать буквально по крупицам - где-то описано, как настраивать Netplan, где-то - iptables. С dnsmasq-ом тоже не всё было гладко - после полной настройки я выяснил, что все DNS запросы из сети обрабатываются, а с хоста - нет. Прошу обратить на это внимание - в статье будет момент об этом.

Но по итогу получил результат, который при re-тесте я настроил минут за 20! В тот день я задержался на работе, а до вечера хотелось ещё раз протестировать идею, чтобы - если будут замечания - подкорректировать материал перед тем, как публиковать статью. Но решение получилось настолько элегантным, что для сети из 10-20 машин лучше и не придумаешь: весь DNS настраивается в одном файле. Если у вас два интерфейса и две сети - большего и не нужно! Да, можно настроить netplan - но это будет чуть более громоздкое решение, и iptables всё-равно настраивать.

Несколько раз протестировал на Debian-12. Решение полностью работоспособное. Я - только "студент" пока, так что комментарии, замечания и конструктивная критика принимается и даже приветствуется.

А, если кому-то эта идея будет полезна - я только рад! Пользуйтесь!

Одно замечание. Я привык настройки машины делать из под рута. Не лучшая практика, но для такого случая допустимо. Если вы будете делать из под локального пользователя, то стоит использовать sudo.


Для настройки собственного роутера нам понадобится:

  1. Сервер с двумя и более сетевыми интерфейсами (один из них уже должен быть настроен для выхода в интернет - выходит за рамки данной заметки)

  2. В качестве сетевого менеджера я буду использовать network interfaces (по умолчанию в Debian)

  3. Включить пересылку пакетов между интерфейсами (по умолчанию отключена в Linux)

  4. Настроить nftables (мы будем использовать собственный DNS сервер с DHCP, поэтому перенаправление на другой сервер в nftables делать не будем)

  5. Собственный DNS сервер (мы будем использовать dnsmasq)

 


1. Проверка настроек сетевых интерфейсов:

а. Проверяем, какие сетевые интерфейсы у нас есть:

ip a    

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 
state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever

2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 
qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:1b:df:b1 brd ff:ff:ff:ff:ff:ff 
inet 10.0.2.15/24 metric 100 brd 10.0.2.255 scope global dynamic enp0s3 
valid_lft 80772sec preferred_lft 80772sec
inet6 fd00::a00:27ff:fe1b:dfb1/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 86161sec preferred_lft 14161sec
inet6 fe80::a00:27ff:fe1b:dfb1/64 scope link
valid_lft forever preferred_lft forever

3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
qdisc fq_codel state UP group default qlen 1000 
link/ether 08:00:27:81:ff:ba brd ff:ff:ff:ff:ff:ff
inet 192.168.10.1/24 brd 192.168.10.255 scope global enp0s8
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe81:ffba/64 scope link
valid_lft forever preferred_lft forever

Значит, у нас есть интерфейсы enp0s3  и enp0s8, причём enp0s3  - выходит в интернет.

 


2. Проверяем настройки сети:

cat /etc/nework/interfaces

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface

allow-hotplug enp0s3
iface enp0s3 inet dhcp
    dns-nameservers 127.0.0.1

# This is an autoconfigured IPv6 interface
# iface enp0s3 inet6 auto

allow-hotplug enp0s8
iface enp0s8 inet static
    address 192.168.10.1
    netmask 255.255.255.0
    dns-nameservers 127.0.0.1

Сеть у нас настроена, но по тому, как её настраивать в интернете много информации.

Мы поняли главное: локальная сеть у нас работает на сети 192.168.10.0/24 и наша машина в локальной сети - 192.168.10.1 . Какие сервера использовать для DNS - тут не принципиально - мы будем настраивать их дальше. А, вот, имя сети и имя машины - очень важны - впоследствии именно она будет использоваться как шлюз.

 


3. Включаем в ядре бондинг:

а. Скачиваем, если он ещё не скачан -  nftables:


apt install nftables - y

systemctl start nftables
systemctl enable nftables

 

б. Делаем первичную настройку nftables:


# Создаем таблицу и цепочки
nft add table inet nat
nft add chain inet nat postrouting { type nat hook postrouting priority 100\; }

# Правило для DNS (в фильтрующей цепочке)
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0\; }
nft add rule inet filter input udp dport 53 accept

# Правило MASQUERADE
nft add rule inet nat postrouting oif "enp0s3" masquerade

 ... и сохраняем её.

nft list ruleset >> /etc/nftables.conf

в. Устанавливаем modprobe iptable_nat:

modprobe iptable_nat
echo 1 > /proc/sys/net/ipv4/ip_forward    

 

г. Сохраним настройки модулей:

vim /etc/modules    
# Добавляем эту строку
iptable_nat    

 

д. Сохраним настройки трафика:

vim /etc/sysctl.conf    
# Снимаем комментрарий с этой строки
net.ipv4.ip_forward=1    

 


3. Тестируем связь с другой машины в локальной сети:

  • Проверяем и - при необходимости - настраиваем сеть (у нас пока DHCP не поднят, поэтому - только статика):


ip a        

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 
state UNKNOWN group default qlen 1000 
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever 
inet6 ::1/128 scope host noprefixroute 
valid_lft forever preferred_lft forever

2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
qdisc fq_codel state UP group default qlen 1000 
link/ether 08:00:27:63:46:3b brd ff:ff:ff:ff:ff:ff 
inet 192.168.10.2/24 brd 192.168.10.255 scope global enp0s3
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe63:463b/64 scope link
valid_lft forever preferred_lft forever  

 

  • Пингуем наш шлюз:


ping 192.168.10.1

PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=1.45 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.923 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=1.15 ms
64 bytes from 192.168.10.1: icmp_seq=4 ttl=64 time=1.12 ms
64 bytes from 192.168.10.1: icmp_seq=5 ttl=64 time=1.02 ms
64 bytes from 192.168.10.1: icmp_seq=6 ttl=64 time=0.958 ms
64 bytes from 192.168.10.1: icmp_seq=7 ttl=64 time=0.964 ms
--- 192.168.10.1 ping statistics ---
7 packets transmitted, 7 received, 0% packet loss, time 6005ms
rtt min/avg/max/mdev = 0.923/1.084/1.447/0.168 ms    
  • Пингуем интернет - DNS у нас пока не настроен, поэтому только по ip:

ping 8.8.8.8

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=254 time=55.3 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=254 time=25.3 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=254 time=25.5 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=254 time=25.1 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=254 time=25.4 ms
64 bytes from 8.8.8.8: icmp_seq=6 ttl=254 time=65.0 ms
64 bytes from 8.8.8.8: icmp_seq=7 ttl=254 time=87.4 ms
64 bytes from 8.8.8.8: icmp_seq=8 ttl=254 time=25.0 ms
64 bytes from 8.8.8.8: icmp_seq=9 ttl=254 time=28.8 ms
--- 8.8.8.8 ping statistics ---
9 packets transmitted, 9 received, 0% packet loss, time 8008ms
rtt min/avg/max/mdev = 24.952/40.314/87.449/21.920 ms    
  • Перезагружаем шлюз и снова проверяем пинг со второй машины, чтобы проверить, что все настройки сохранились корректно.

 


5. Устанавливаем и настраиваем dnsmasq:

а. Устанавливаем сам dnsmasq:


apt install dnsmasq
systemctl enable dnsmasq
systemctl start dnsmasq   

После установки или старта сервиса мы можем увидеть ошибку:

failed to create listening socket for port 53: Address already in use    

Как правило, она связана с тем, что на компьютере работает сервис systemd-resolved, который занял порт 53. Чтобы это исправить, отключаем его:

systemctl disable systemd-resolved --now    

У меня при настройке появилась проблема: при пинге по доменному имени внутри сети всё работало, а вот с локальной машины - хотя я и отключил systemd-resolved - пинг не шёл. Нашёл несложное решение:


# /etc/resolv.conf прописываем:
nameserver 127.0.0.1


# и запрещаем менять его
chattr +i /etc/resolv.conf
 

б. Конфигурируем dnsmasq.conf:


cp /etc/dnsmasc.conf /etc/dnsmasq.conf.backup
vim /etc/dnsmasq.conf
    
# По умолчянию Linux слушает DNS в systemd-resolv,
#    Мы его отключили,
#    Поэтому - чтобы убрать ошибку, с этим связанную,
#     устанавливаем этот параметр
no-resolv

# Настраиваем адреса, на которых сервер будет слушать DNS запрсы
#     Настраиваем на 127.0.0.1, чтобы сервер работал со своими же запросами
listen-address=127.0.0.1

# Настраиваем интерфейсы, на которых будет работать сервис (нужны оба!)
interface=enp0s8,enp0s3

# Настройка DHCP
dhcp-range=192.168.10.100,192.168.10.254,255.255.255.0,24h

# Настройка внешних DNS серверов, куда будут
#     отправляться запросы на неизвестные сервера
server=8.8.8.8
all-servers

# Настройка внутренних доменных имён
address=/example_domain.com/192.168.10.1

# Настраиваем логи
log-queries
log-facility=/var/log/dnsmasq.log

Соответственно, поле address задаёт доменные имена для локальной сети.

в. Перезагружаем dnsmasq:

systemctl restart dnsmasq    

 


6. Снова тестируем работу со второй машины:

а. Проверяем DHCP:

vim /etc/network/interfaces    

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
source /etc/network/interfaces.d/*
# The loopback network interface

auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug enp0s3

iface enp0s3 inet dhcp

# This is an autoconfigured IPv6 interface
# iface enp0s3 inet6 auto    

б.  Перезапускаем сеть:

  systemctl restart networkiing    

в. Проверяем, что ip-адрес присвоился:


ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 
state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 
qdisc fq_codel state UP group default qlen 1000 
link/ether 08:00:27:bf:4c:a6 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.111/24 brd 192.168.10.255 scope global dynamic enp0s3
valid_lft 86370sec preferred_lft 86370sec 
inet6 fe80::a00:27ff:febf:4ca6/64 scope link
valid_lft forever preferred_lft forever    

г. Снова пингуем сеть:


ping 192.168.10.1

PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=1.82 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=1.12 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=12.5 ms
64 bytes from 192.168.10.1: icmp_seq=4 ttl=64 time=1.17 ms
64 bytes from 192.168.10.1: icmp_seq=5 ttl=64 time=0.905 ms
--- 192.168.10.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 5242ms
rtt min/avg/max/mdev = 0.905/3.493/12.452/4.489 ms    

ping 8.8.8.8

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=254 time=25.9 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=254 time=24.8 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=254 time=25.2 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=254 time=26.3 ms
64 bytes from 8.8.8.8: icmp_seq=5 ttl=254 time=28.0 ms
64 bytes from 8.8.8.8: icmp_seq=6 ttl=254 time=27.9 ms
--- 8.8.8.8 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5745ms
rtt min/avg/max/mdev = 24.839/26.349/27.951/1.212 ms   

ping ya.ru

PING ya.ru (77.88.44.242) 56(84) bytes of data.
64 bytes from ya.ru (77.88.44.242): icmp_seq=1 ttl=254 time=14.5 ms
64 bytes from ya.ru (77.88.44.242): icmp_seq=2 ttl=254 time=13.2 ms
64 bytes from ya.ru (77.88.44.242): icmp_seq=3 ttl=254 time=14.6 ms
64 bytes from ya.ru (77.88.44.242): icmp_seq=4 ttl=254 time=14.9 ms
64 bytes from ya.ru (77.88.44.242): icmp_seq=5 ttl=254 time=15.4 ms
64 bytes from ya.ru (77.88.44.242): icmp_seq=6 ttl=254 time=14.5 ms
--- ya.ru ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 6228ms
rtt min/avg/max/mdev = 13.187/14.517/15.367/0.662 ms

nslookup ya.ru

Server:		192.168.10.1
Address:	192.168.10.1#53

Non-authoritative answer:
Name:	ya.ru
Address: 77.88.55.242
Name:	ya.ru
Address: 5.255.255.242
Name:	ya.ru
Address: 77.88.44.242
Name:	ya.ru
Address: 2a02:6b8::2:242    

Мы видим наш сервер как первый в каскаде DNS запросов


7. Тестируем локальный DNS:

а. Проверяем, что DNS работает на локальной машине:


ping example_domain.com

PING example_domain.com (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=2 ttl=64 time=0.061 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=3 ttl=64 time=0.055 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=4 ttl=64 time=0.202 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=5 ttl=64 time=0.057 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=6 ttl=64 time=0.055 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=7 ttl=64 time=0.057 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=8 ttl=64 time=0.060 ms

--- example_domain.com ping statistics ---
8 packets transmitted, 8 received, 0% packet loss, time 7012ms
rtt min/avg/max/mdev = 0.043/0.073/0.202/0.048 ms

б. Проверяем локальный DNS из сети:


ping example_domain.com

PING example_domain.com (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=1 ttl=64 time=2.03 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=2 ttl=64 time=0.883 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=3 ttl=64 time=1.03 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=4 ttl=64 time=1.05 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=5 ttl=64 time=1.08 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=6 ttl=64 time=0.778 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=7 ttl=64 time=1.13 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=8 ttl=64 time=0.992 ms

--- example_domain.com ping statistics ---
8 packets transmitted, 8 received, 0% packet loss, time 7011ms
rtt min/avg/max/mdev = 0.778/1.122/2.029/0.358 ms

б. Проверяем доменную зону:


ping my_app.example_domain.com

PING my_app.example_domain.com (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=1 ttl=64 time=2.02 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=2 ttl=64 time=0.904 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=3 ttl=64 time=1.10 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=4 ttl=64 time=0.889 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=5 ttl=64 time=1.20 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=6 ttl=64 time=0.907 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=7 ttl=64 time=1.01 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=8 ttl=64 time=0.892 ms
64 bytes from 192.168.10.1 (192.168.10.1): icmp_seq=9 ttl=64 time=1.10 ms

--- my_app.example_domain.com ping statistics ---
9 packets transmitted, 9 received, 0% packet loss, time 8009ms
rtt min/avg/max/mdev = 0.889/1.112/2.020/0.337 ms

Мы настроили шлюз!