Многосегментный Linux-маршрутизатор на Netplan и Nftables: DMZ, SNAT/DNAT, MGMT, диагностика и многое другое


❯ Вступление
В этой статье будет разбираться построение многосегментного Linux-маршрутизатора на базе Ubuntu 24.04, Netplan и nftables на основе моего лабораторного стенда. Стенд состоит из 7 виртуальных машин и нескольких изолированных сетевых сегментов: LAN, DMZ и MGMT, а также имитирует провайдерский маршрутизатор и внешнего клиента. Все виртуальные машины разворачиваются с помощью Vagrant и VirtualBox, в качестве альтернативы, если вам хочется, такую схему также можно развернуть на облачных серверах. Для этого подойдет хостинг Timeweb Cloud, в таком случае Vagrantfile писать будет не нужно.
Сам стенд можно посмотреть в моем GitHub. В папке docs/ есть команды с краткими комментариями по шагам, однако всё самое важное будет объясняться здесь.
Всего будет 12 этапов. Для каждого этапа я подготовил пошаговый разбор конфигураций, правил файрвола и команд диагностики. Мы с нуля настроим адресацию, включим IP forwarding, разберем работу stateful-фильтрации через conntrack, организуем SNAT/DNAT, развернем локальный DNS и изолируем критические сегменты и многое другое.
❯ Сегменты
Сеть разделяется на сегменты, каждый сегмент имеет свою подсеть и шлюз. Сначала пройдемся по терминам.
Сегмент сети — отдельная часть общей сети. Устройства внутри одного сегмента могут общаться напрямую, а трафик между разными сегментами проходит через маршрутизатор (какая-то конкретная точка, общая у сегментов, через которую идет трафик) и может контролироваться файрволом.
Сегментация (разделение сети) нужна, чтобы изолировать друг от друга разные по уровню доверия зоны: пользовательскую сеть, публичные сервисы, сеть управления и внешний мир.
Подсеть — это диапазон IP-адресов, объединённых общей маской. Например, запись 10.10.10.0/24 означает сеть с маской 255.255.255.0: в ней 256 адресов, из которых 254 можно назначить устройствам. Все узлы одной подсети видят друг друга напрямую, без маршрутизации.
Шлюз — это IP-адрес маршрутизатора в данной подсети. Когда устройство хочет отправить пакет в другую подсеть или во внешнюю сеть, оно отправляет его на шлюз. Шлюз решает, куда передать пакет дальше. В нашем стенде роль шлюзов выполняют linux-gateway и upstream-router.
Для конкретного этого стенда можно построить такую таблицу:
Сегмент | Подсеть | Шлюз |
|---|---|---|
lab-internet | 198.51.100.0/24 | 198.51.100.1 |
lab-wan | 172.16.0.0/24 | 172.16.0.1 |
lab-lan | 10.10.10.0/24 | 10.10.10.1 |
lab-dmz | 10.10.20.0/24 | 10.10.20.1 |
lab-mgmt | 10.10.30.0/24 | 10.10.30.1 |
WAN (172.16.0.0/24): Внешний канал. Связывает наш шлюз с upstream-router.
LAN (10.10.10.0/24): Внутренняя пользовательская сеть (lan-workstation и lan-dns-server).
DMZ (10.10.20.0/24): Демилитаризованная зона для публичных сервисов (dmz-web-server). Это сегмент сети для публичных сервисов, к которым нужен доступ извне. Его специально держат отдельно от внутренней сети, чтобы если сервер взломают, злоумышленник не попал сразу в LAN.
MGMT (10.10.30.0/24): Защищённая сеть управления (mgmt-workstation). Защищённый сегмент для администраторов и управления оборудованием. В MGMT стоит mgmt-workstation, и только из неё разрешён SSH/ICMP к шлюзу, LAN и DMZ
❯ Узлы
Узел — это отдельное устройство в сети: компьютер, сервер и т.д.
В нашем стенде каждый узел — это отдельная виртуальная машина (VM), созданная в VirtualBox. У каждой VM есть имя (hostname), набор сетевых интерфейсов и своя роль
Для конкретного этого стенда можно построить такую таблицу:
VM | Роль |
|---|---|
upstream-router | Имитация провайдера |
linux-gateway | Основной шлюз + firewall |
lan-workstation | Рабочая станция в LAN |
lan-dns-server | DNS-сервер в LAN |
dmz-web-server | Nginx в DMZ |
mgmt-workstation | Админская станция |
external-client | Внешний клиент |
❯ Этап 1: Подготовка Vagrant и интерфейсов

Кратко про то, что такое Vagrant и про некоторые особенные для этого стенда настройки.
Vagrant — инструмент для управления виртуальными машинами через конфигурационный файл. Вместо ручного создания VM в VirtualBox мы описываем весь стенд в Vagrantfile: базовый образ, ресурсы и сети.
virtualbox__intnet — создаёт изолированную внутреннюю сеть VirtualBox. Она соединяет только виртуальные машины между собой и не выходит в реальную сеть. Разные имена (lab-lan, lab-dmz) — разные сегменты.
auto_config: false — запрещает Vagrant автоматически настраивать этот интерфейс. То есть интерфейсы будут созданы, но они будут без привязки к какому-то адресу. IP-адреса мы назначим сами через Netplan на следующем этапе (обычно выключать не нужно, но для настройки «с нуля» решил сделать так).
Здесь сам Vagrantfile
Vagrant.configure("2") do |config|
# Базовый образ Ubuntu 24.04 для всех виртуальных машин.
# box — имя образа, box_version — конкретная зафиксированная версия,
# чтобы стенд воспроизводился одинаково при каждом запуске.
config.vm.box = "cloud-image/ubuntu-24.04"
config.vm.box_version = "20260814.0.0"
# Внешний маршрутизатор: имитирует провайдера и соединяет WAN с внешней сетью
config.vm.define "upstream-router" do |vm|
vm.vm.hostname = "upstream"
# Сеть между upstream и gateway (сегмент lab-wan)
vm.vm.network "private_network",
virtualbox__intnet: "lab-wan",
auto_config: false
# Внешняя сеть, имитирующая Интернет (сегмент lab-internet)
vm.vm.network "private_network",
virtualbox__intnet: "lab-internet",
auto_config: false
# Ресурсы и настройки VirtualBox
vm.vm.provider "virtualbox" do |vb|
vb.name = "upstream"
vb.cpus = 2
vb.memory = 1024
# По идее настройка должна отключать gui, от нее был бы смысл, если бы вместо vboxvga вообще не было контроллера.
vb.gui = false
# Используется графический контроллер vboxvga
# Если не указывается, то используется vmsvga,
# но с vagrant у меня почему-то виртуалки в таком случае не запускаются
vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
end
end
# Главный Linux-маршрутизатор лаборатории: 4 лабораторных интерфейса
config.vm.define "linux-gateway" do |vm|
vm.vm.hostname = "gateway"
# WAN: связь с upstream
vm.vm.network "private_network",
virtualbox__intnet: "lab-wan",
auto_config: false
# LAN: внутренняя пользовательская сеть
vm.vm.network "private_network",
virtualbox__intnet: "lab-lan",
auto_config: false
# DMZ: сеть публичных сервисов
vm.vm.network "private_network",
virtualbox__intnet: "lab-dmz",
auto_config: false
# MGMT: отдельная сеть для администрирования
vm.vm.network "private_network",
virtualbox__intnet: "lab-mgmt",
auto_config: false
# Ресурсы и настройки VirtualBox
vm.vm.provider "virtualbox" do |vb|
vb.name = "gateway"
vb.cpus = 2
vb.memory = 1024
vb.gui = false
vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
end
end
# Рабочая станция пользователя в LAN
config.vm.define "lan-workstation" do |vm|
vm.vm.hostname = "workstation"
# Подключение только к LAN
vm.vm.network "private_network",
virtualbox__intnet: "lab-lan",
auto_config: false
vm.vm.provider "virtualbox" do |vb|
vb.name = "workstation"
vb.cpus = 2
vb.memory = 1024
vb.gui = false
vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
end
end
# DNS-сервер внутренней сети
config.vm.define "lan-dns-server" do |vm|
vm.vm.hostname = "dns"
# Подключение только к LAN
vm.vm.network "private_network",
virtualbox__intnet: "lab-lan",
auto_config: false
vm.vm.provider "virtualbox" do |vb|
vb.name = "dns"
vb.cpus = 2
vb.memory = 1024
vb.gui = false
vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
end
end
# Веб-сервер в DMZ
config.vm.define "dmz-web-server" do |vm|
vm.vm.hostname = "web"
# Подключение только к DMZ
vm.vm.network "private_network",
virtualbox__intnet: "lab-dmz",
auto_config: false
vm.vm.provider "virtualbox" do |vb|
vb.name = "web"
vb.cpus = 2
vb.memory = 1024
vb.gui = false
vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
end
end
# Административная рабочая станция
config.vm.define "mgmt-workstation" do |vm|
vm.vm.hostname = "admin"
# Подключение только к MGMT
vm.vm.network "private_network",
virtualbox__intnet: "lab-mgmt",
auto_config: false
vm.vm.provider "virtualbox" do |vb|
vb.name = "admin"
vb.cpus = 2
vb.memory = 1024
vb.gui = false
vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
end
end
# Внешний клиент, имитирующий пользователя из Интернета
config.vm.define "external-client" do |vm|
vm.vm.hostname = "external-client"
# Подключение только к внешней сети
vm.vm.network "private_network",
virtualbox__intnet: "lab-internet",
auto_config: false
vm.vm.provider "virtualbox" do |vb|
vb.name = "external-client"
vb.cpus = 2
vb.memory = 1024
vb.gui = false
vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
end
end
endТеперь с помощью vagrant up поднимаем машины.
Всё запустилось, теперь напишем цикл на bash, чтобы зайти на каждую машину и получить список интерфейсов.
# Перебираем все виртуальные машины стенда по очереди.
# Список соответствует именам VM из Vagrantfile.
for vm in upstream-router linux-gateway lan-workstation lan-dns-server \
dmz-web-server mgmt-workstation external-client; do
# Печатаем заголовок, чтобы понимать, к какой машине относится вывод.
echo "===== $vm ====="
# Заходим на VM по SSH и выполняем команду:
# ip -br link — краткий список сетевых интерфейсов и их состояний (UP/DOWN).
vagrant ssh "$vm" -c 'ip -br link'
doneПолучим такую карту интерфейсов:
VM | enp0s3 | enp0s8 | enp0s9 | enp0s10 | enp0s16 |
|---|---|---|---|---|---|
upstream-router | Vagrant NAT | lab-wan | lab-internet | — | — |
linux-gateway | Vagrant NAT | lab-wan | lab-lan | lab-dmz | lab-mgmt |
lan-workstation | Vagrant NAT | lab-lan | — | — | — |
lan-dns-server | Vagrant NAT | lab-lan | — | — | — |
dmz-web-server | Vagrant NAT | lab-dmz | — | — | — |
mgmt-workstation | Vagrant NAT | lab-mgmt | — | — | — |
external-client | Vagrant NAT | lab-internet | — | — | — |
На всех виртуальных машинах есть интерфейс enp0s3. Он обязателен для Vagrant, так как без него vagrant потеряет доступ к узлам. На него внимание не обращаем, он в сети участвовать не будет.
Также небольшая справка по поводу названий интерфейсов. Имена интерфейсов в Linux зависят от PCI-слота, в который VirtualBox подключила сетевую карту. Порядок сетей в Vagrantfile не связан с порядком имён внутри VM: четвёртый интерфейс может получить имя enp0s16, а не enp0s11. Поэтому мы смотрим список интерфейсов на каждой машине отдельно командой ip -br link.
❯ Этап 2: Назначение IP-адресов через Netplan

На этом этапе зададим IP-адреса интерфейсов. Его можно было бы пропустить, если бы в Vagrantfile не было auto_config: false настройки, но для наглядности, мне кажется, стоит этот этап тоже сделать ручками.
Конкретно в моем случае в /etc/netplan везде стандартный файл назывался 50-cloud-init.yaml. Он управляет только enp0s3. Чтобы ничего там не сломать, буду создавать отдельный 60-lab.yaml.
Для каждого сервера повторяется один общий блок кода команд. Отличается только имя при подключении по ssh и сам конфиг.
# Подключаемся к виртуальной машине linux-gateway по SSH
vagrant ssh linux-gateway
# Открываем файл конфигурации сети в редакторе nano (с правами root)
sudo nano /etc/netplan/60-lab.yaml
# Устанавливаем права доступа 600 (только владелец может читать и писать) —
# netplan требует такие права для файлов конфигурации из соображений безопасности
sudo chmod 600 /etc/netplan/60-lab.yaml
# Генерируем конфигурацию бэкендов (проверка синтаксиса YAML без применения)
sudo netplan generate
# Применяем сетевые настройки из конфигурации
sudo netplan apply
# Кратко показать интерфейсы и назначенные им IP-адреса
ip -br addr
# Показать таблицу маршрутизации: куда уходят пакеты и какой шлюз по умолчанию
ip routeНиже конфиги netplan для каждого отдельного сервера с пояснениями. Они все примерно одинаковые, с небольшими отличиями в назначенных IP-адресах.
Linux Gateway
network:
version: 2
ethernets:
enp0s8: # lab-wan (канал к upstream-router)
addresses:
- 172.16.0.2/24
enp0s9: # lab-lan (локальная сеть)
addresses:
- 10.10.10.1/24
enp0s10: # lab-dmz (демилитаризованная зона)
addresses:
- 10.10.20.1/24
enp0s16: # lab-mgmt (сеть управления)
addresses:
- 10.10.30.1/24После применения конфигурации интерфейсы получат IP-адреса 172.16.0.2, 10.10.10.1, 10.10.20.1, 10.10.30.1.
Upstream Router
network:
version: 2
ethernets:
enp0s8: # lab-wan
addresses:
- 172.16.0.1/24
enp0s9: # lab-internet
addresses:
- 198.51.100.1/24LAN Workstation
network:
version: 2
ethernets:
enp0s8: # lab-lan
addresses:
- 10.10.10.10/24LAN DNS Server
network:
version: 2
ethernets:
enp0s8: # lab-lan
addresses:
- 10.10.10.53/24DMZ Web Server
network:
version: 2
ethernets:
enp0s8: # lab-dmz
addresses:
- 10.10.20.10/24MGMT Workstation
network:
version: 2
ethernets:
enp0s8: # lab-mgmt
addresses:
- 10.10.30.10/24External Client
network:
version: 2
ethernets:
enp0s8: # lab-internet
addresses:
- 198.51.100.10/24После завершения настройки Netplan на всех узлах адресация в стенде распределилась следующим образом:
Узел | Назначенные IP-адреса |
|---|---|
linux-gateway | 172.16.0.2, 10.10.10.1, 10.10.20.1, 10.10.30.1 |
upstream-router | 172.16.0.1, 198.51.100.1 |
lan-workstation | 10.10.10.10 |
lan-dns-server | 10.10.10.53 |
dmz-web-server | 10.10.20.10 |
mgmt-workstation | 10.10.30.10 |
external-client | 198.51.100.10 |
❯ Этап 3: Маршрутизация
На этом шаге мы прописываем на всех виртуальных машинах маршруты и шлюзы по умолчанию. Пересылку пакетов (IP forwarding) и файрвол (nftables) пока не включаем — сначала нужно, чтобы каждый узел правильно понимал, куда отправлять трафик.
Нюанс работы с Vagrant NAT
Как я уже упоминал ранее, на всех виртуальных машинах по умолчанию присутствует default-маршрут через интерфейс enp0s3. Проблема в том, что в данный момент этот шлюз имеет приоритет, поэтому нужно сделать так, чтобы у него этого приоритета не было, и трафик через него не шел.
В netplan есть такая настройка metric. Чем меньше в ней число — тем выше приоритет, в enp0s3 значение равняется 100, поэтому, чтобы решить нашу проблему нужно поставить metric: 50 (к примеру). Эта настройка будет нужна на всех серверах, так как на них нужно будет прописать default-маршрут к шлюзу, кроме Linux Gateway и Upstream Router.
Теперь для каждого сервера пишем новый netplan конфиг, с дополнительными параметрами. Для удобного обновления файла /etc/netplan/60-lab.yaml используем утилиту tee:
Linux Gateway
# Подключаемся к главному шлюзу по SSH
vagrant ssh linux-gateway
# Создаем и записываем конфигурацию в /etc/netplan/60-lab.yaml
# Экранирование 'EOF' предотвращает подстановку переменных bash внутри документа
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия формата конфигурации Netplan
renderer: networkd # Использование бэкенда systemd-networkd для управления сетью
ethernets:
enp0s8: # Сетевой интерфейс сегмента lab-wan
addresses:
- 172.16.0.2/24 # IP-адрес linux-gateway в сегменте WAN
routes:
# Статический маршрут к внешней сети через upstream-router
- to: 198.51.100.0/24 # Целевая внешняя подсеть
via: 172.16.0.1 # IP-адрес следующего перехода (next-hop)
enp0s9: # Сетевой интерфейс сегмента lab-lan
addresses:
- 10.10.10.1/24 # IP-адрес шлюза для подсети LAN
enp0s10: # Сетевой интерфейс сегмента lab-dmz
addresses:
- 10.10.20.1/24 # IP-адрес шлюза для подсети DMZ
enp0s16: # Сетевой интерфейс сегмента lab-mgmt
addresses:
- 10.10.30.1/24 # IP-адрес шлюза для подсети MGMT
EOF
# Генерируем конфигурационные файлы и применяем настройки сети
sudo netplan generate && sudo netplan applyВ таблице маршрутизации (ip route) должны появиться маршруты к сетям LAN, DMZ, MGMT, WAN и статический маршрут 198.51.100.0/24 via 172.16.0.1.
Upstream Router
# Подключаемся к провайдерскому маршрутизатору по SSH
vagrant ssh upstream-router
# Записываем конфигурационный файл Netplan
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия спецификации Netplan
ethernets:
enp0s8: # Сетевой интерфейс сегмента lab-wan
addresses:
- 172.16.0.1/24 # IP-адрес внешнего маршрутизатора
routes:
# Статические маршруты к внутренним сетям лаборатории за linux-gateway
- to: 10.10.10.0/24 # Подсеть LAN
via: 172.16.0.2 # Адрес linux-gateway в сегменте WAN
- to: 10.10.20.0/24 # Подсеть DMZ
via: 172.16.0.2 # Адрес linux-gateway в сегменте WAN
- to: 10.10.30.0/24 # Подсеть MGMT
via: 172.16.0.2 # Адрес linux-gateway в сегменте WAN
enp0s9: # Сетевой интерфейс сегмента lab-internet
addresses:
- 198.51.100.1/24 # IP-адрес в симулируемом Интернете
EOF
# Проверяем синтаксис и активируем настройки
sudo netplan generate && sudo netplan applyUpstream Router должен знать, что все внутренние подсети стенда (10.10.10.0/24, 10.10.20.0/24, 10.10.30.0/24) находятся за главным шлюзом linux-gateway (172.16.0.2).
LAN Workstation
# Подключаемся к пользовательской рабочей станции
vagrant ssh lan-workstation
# Формируем файл конфигурации сети
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия формата Netplan
ethernets:
enp0s8: # Интерфейс подключения к сегменту lab-lan
addresses:
- 10.10.10.10/24 # IP-адрес хоста
routes:
# Маршрут по умолчанию через linux-gateway
- to: default # Обозначает любой назначенный трафик (0.0.0.0/0)
via: 10.10.10.1 # IP-адрес linux-gateway в сети LAN
metric: 50 # Высший приоритет по сравнению с DHCP (100)
EOF
# Генерация и активация новой сетевой конфигурации
sudo netplan generate && sudo netplan applyLAN DNS Server
# Подключаемся к DNS-серверу локальной сети
vagrant ssh lan-dns-server
# Записываем конфигурацию с маршрутом по умолчанию
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия формата Netplan
ethernets:
enp0s8: # Интерфейс подключения к сегменту lab-lan
addresses:
- 10.10.10.53/24 # IP-адрес DNS-сервера
routes:
# Маршрут по умолчанию через linux-gateway
- to: default # Весь внешне ориентированный трафик
via: 10.10.10.1 # IP-адрес linux-gateway в сети LAN
metric: 50 # Метрика для приоритета перед NAT-интерфейсом
EOF
# Генерация и активация сетевой конфигурации
sudo netplan generate && sudo netplan applyDMZ Web Server
# Подключаемся к веб-серверу DMZ
vagrant ssh dmz-web-server
# Формируем файл конфигурации Netplan
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия формата Netplan
ethernets:
enp0s8: # Интерфейс подключения к сегменту lab-dmz
addresses:
- 10.10.20.10/24 # IP-адрес веб-сервера
routes:
# Маршрут по умолчанию через linux-gateway
- to: default # Направление неуточненного трафика
via: 10.10.20.1 # IP-адрес linux-gateway в сети DMZ
metric: 50 # Приоритетная метрика
EOF
# Применение новых параметров сети
sudo netplan generate && sudo netplan applyMGMT Workstation
# Подключаемся к административной станции
vagrant ssh mgmt-workstation
# Записываем конфигурацию сети
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия формата Netplan
ethernets:
enp0s8: # Интерфейс подключения к сегменту lab-mgmt
addresses:
- 10.10.30.10/24 # IP-адрес рабочей станции
routes:
# Маршрут по умолчанию через linux-gateway
- to: default # Направление трафика по умолчанию
via: 10.10.30.1 # IP-адрес linux-gateway в сети MGMT
metric: 50 # Приоритетная метрика
EOF
# Генерация и активация сетевой конфигурации
sudo netplan generate && sudo netplan applyExternal Client
# Подключаемся к внешнему клиенту
vagrant ssh external-client
# Записываем конфигурацию сети
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия формата Netplan
ethernets:
enp0s8: # Интерфейс подключения к сегменту lab-internet
addresses:
- 198.51.100.10/24 # IP-адрес клиента
routes:
# Маршрут по умолчанию через upstream-router
- to: default # Весь исходящий трафик
via: 198.51.100.1 # IP-адрес upstream-router в интернет-сегменте
metric: 50 # Приоритетная метрика
EOF
# Применение настроек сети
sudo netplan generate && sudo netplan applyТеперь все узлы стенда имеют настроенный маршрут по умолчанию.
Linux Gateway знает путь к внешней сети 198.51.100.0/24 через Upstream Router.
Upstream Router знает пути ко всем внутренним сегментам (10.10.10.0/24, 10.10.20.0/24, 10.10.30.0/24) через Linux Gateway.
Пересылка пакетов (IP forwarding) и правила сетевого экрана (nftables) ещё не активированы, поэтому трафик между сегментами пока не передаётся.
❯ Этап 4: IP forwarding
Включение пересылки пакетов (IP forwarding) является обязательным условием для превращения узла Linux в маршрутизатор. Без этой функции ядро Linux отбрасывает пакеты, адресованные другим узлам, из-за чего трафик не может пройти дальше одного хопа.
Хоп — это один переход пакета от одного сетевого устройства к другому по пути к цели. Каждый маршрутизатор, через который проходит пакет, — это отдельный хоп.
На нашем стенде IP forwarding активируется на двух ключевых машинах:
linux-gateway — передаёт пакеты между внешним каналом WAN и внутренними сегментами (LAN, DMZ, MGMT).
upstream-router — передаёт пакеты между внешним сегментом (lab-internet) и каналом связи со шлюзом (lab-wan).
Файервол на данном этапе сознательно не настраивается. Это позволяет проверить связность и убедиться в правильности маршрутизации перед созданием ограничений безопасности.
Linux Gateway
# Подключаемся по SSH к главному шлюзу
vagrant ssh linux-gateway
# Записываем параметр включения IP forwarding в постоянный конфигурационный файл sysctl
sudo tee /etc/sysctl.d/99-ipforward.conf > /dev/null <<'EOF'
# Активация пересылки IPv4-пакетов между сетевыми интерфейсами на уровне ядра
net.ipv4.ip_forward=1
EOF
# Перезагружаем и применяем настройки sysctl из всех конфигурационных файлов в системе
sudo sysctl --system
# Проверяем, что параметр пересылки пакетов активирован (ожидается: net.ipv4.ip_forward = 1)
sysctl net.ipv4.ip_forwardUpstream Router
# Подключаемся по SSH к провайдерскому маршрутизатору
vagrant ssh upstream-router
# Записываем параметр включения IP forwarding в конфигурацию sysctl
sudo tee /etc/sysctl.d/99-ipforward.conf > /dev/null <<'EOF'
# Разрешаем ядру транзитировать IPv4-пакеты между интерфейсами enp0s8 и enp0s9
net.ipv4.ip_forward=1
EOF
# Применяем измененные параметры ядра
sudo sysctl --system
# Проверяем текущий статус функции пересылки пакетов
sysctl net.ipv4.ip_forwardПосле включения пересылки проверяем доступность подключенных шлюзов с соседних виртуальных машин:
# Проверка доступности провайдера (upstream) с внешнего клиента
vagrant ssh external-client -c 'ping -c 3 198.51.100.1'
# Проверка доступности главного шлюза (linux-gateway) с провайдерского маршрутизатора
vagrant ssh upstream-router -c 'ping -c 3 172.16.0.2'
# Проверка доступности всех внутренних узлов и внешнего маршрутизатора с linux-gateway
vagrant ssh linux-gateway -c \
'ping -c 3 10.10.10.10 && \
ping -c 3 10.10.20.10 && \
ping -c 3 10.10.30.10 && \
ping -c 3 172.16.0.1'Так как сетевой экран ещё не активирован, пакеты должны проходить между любыми сегментами:
# Проверка доступности DMZ из внешней сети (External Client -> DMZ Web Server)
vagrant ssh external-client -c 'ping -c 3 10.10.20.10'
# Проверка доступности DMZ из локальной сети (LAN Workstation -> DMZ Web Server)
vagrant ssh lan-workstation -c 'ping -c 3 10.10.20.10'
# Проверка доступности LAN и MGMT из демилитаризованной зоны (DMZ Web Server -> LAN / MGMT)
vagrant ssh dmz-web-server -c \
'ping -c 3 10.10.10.10 && ping -c 3 10.10.30.10'Все ICMP-запросы должны успешно проходить. Это подтверждает правильность настройки таблицы маршрутизации и включения IP forwarding на обоих роутерах.
❯ Этап 5: веб-сервис в DMZ

На данном этапе разворачивается веб-сервер (Nginx) в зоне dmz-web-server и проводятся дополнительные проверки до включения межсетевого экрана.
Подключаемся к узлу, обновляем индекс пакетов и устанавливаем Nginx:
# Подключаемся по SSH к серверу демилитаризованной зоны
vagrant ssh dmz-web-server
# Обновляем списки пакетов apt в системе
sudo apt update
# Устанавливаем веб-сервер Nginx в автоматическом режиме
sudo apt install -y nginx
# Проверяем текущий статус службы Nginx (без открытия пейджера)
sudo systemctl status nginx --no-pager
# Проверяем, что Nginx успешно прослушивает 80-й порт на всех IPv4/IPv6 интерфейсах
ss -tulpn | grep ':80'Выполняем HTTP-запросы к веб-серверу из различных сегментов стенда:
# Запрос к веб-серверу DMZ со станции внутренней локальной сети (LAN Workstation -> DMZ Web Server)
vagrant ssh lan-workstation -c 'curl http://10.10.20.10'
# Запрос к веб-серверу DMZ с внешнего клиента (External Client -> DMZ Web Server)
vagrant ssh external-client -c 'curl http://10.10.20.10'Оба запроса должны вернуть стандартный приветственный HTML от Nginx.
❯ Этап 6: Базовый nftables
На данном этапе разворачивается и настраивается фильтрация пакетов nftables на главном шлюзе linux-gateway. Мы переходим от открытой сети к контролю трафика с политикой по умолчанию DROP.
Чтобы не потерять доступ к виртуальным машинам через vagrant ssh, в цепочке input явно разрешается трафик через технический enp0s3. Также на этом этапе настраивается доступ для административной сети MGMT (SSH и ICMP на сам шлюз).
Формируем конфигурационный файл /etc/nftables.conf на главном шлюзе:
Набор правил Linux Gateway Nftables
# Подключаемся по SSH к главному шлюзу
vagrant ssh linux-gateway
# Обновляем индексы пакетов apt
sudo apt update
# Устанавливаем межсетевой экран nftables
sudo apt install -y nftables
sudo tee /etc/nftables.conf > /dev/null <<'EOF'
# Очищаем текущий набор правил nftables перед загрузкой новых
flush ruleset
# Создаем таблицу для IPv4-пакетов с именем "filter"
table ip filter {
# Цепочка обработки входящего трафика, адресованного самому шлюзу
chain input {
# Подключаем хук input с приоритетом filter и жесткой политикой DROP
type filter hook input priority filter; policy drop;
# Разрешаем весь локальный трафик на loopback-интерфейсе
iifname "lo" accept
# Stateful-фильтрация: разрешаем входящий трафик для уже установленных и связанных соединений
ct state established,related accept
# Разрешаем SSH-подключения (порт 22) через служебный Vagrant NAT для работы vagrant ssh
iifname "enp0s3" tcp dport 22 accept
# Разрешаем узлам административной сети (MGMT) SSH-доступ к самому шлюзу
ip saddr 10.10.30.0/24 tcp dport 22 accept
# Разрешаем узлам административной сети (MGMT) отправлять ICMP Echo-Request (ping) на шлюз
ip saddr 10.10.30.0/24 icmp type echo-request accept
}
# Цепочка обработки транзитного трафика, проходящего ЧЕРЕЗ шлюз
chain forward {
# Подключаем хук forward с приоритетом filter и жесткой политикой DROP
type filter hook forward priority filter; policy drop;
# На данном шаге транзитный трафик полностью заблокирован — правила разрешения отсутствуют
}
# Цепочка обработки исходящего трафика, генерируемого самим шлюзом
chain output {
# Подключаем хук output с приоритетом filter и разрешающей политикой ACCEPT
type filter hook output priority filter; policy accept;
}
}
EOF
# Синтаксическая проверка файла конфигурации (флаг -c проверяет синтаксис без применения)
sudo nft -c -f /etc/nftables.conf
# Загрузка правил в ядро
sudo nft -f /etc/nftables.conf
# Вывод текущего активного набора правил nftables
sudo nft list ruleset
# Включаем автозагрузку службы nftables при старте системы и перезапускаем её
sudo systemctl enable nftables
sudo systemctl restart nftables
# Проверяем статус работы службы nftables (ожидаем значение active)
sudo systemctl status nftables --no-pagerИз-за смены политики по умолчанию в цепочке FORWARD на DROP, транзитный трафик между всеми сегментами теперь полностью заблокирован.
Новые входящие подключения к самому шлюзу разрешены только в трёх случаях:
Vagrant → gateway:22 — служебное SSH-подключение через enp0s3. Это нужно, чтобы работала команда vagrant ssh linux-gateway. Без этого правила мы потеряли бы управление шлюзом.
MGMT → gateway:22 — администратор (10.10.30.0/24) может зайти на шлюз по SSH.
MGMT → gateway (ICMP) — с той же админской станции можно пинговать шлюз.
Проверяем блокировку транзитного трафика и сохранение доступности шлюза при подключении напрямую:
# 1. Запросы, которые должны блокироваться (таймаут):
# Запрос со станции внешнего клиента к веб-серверу в DMZ
vagrant ssh external-client -c 'curl --connect-timeout 3 http://10.10.20.10'
# Запрос из локальной сети (LAN) к веб-серверу в DMZ
vagrant ssh lan-workstation -c 'curl --connect-timeout 3 http://10.10.20.10'
# Пинг с рабочей станции LAN до локального шлюза (ICMP не разрешён для LAN в цепочке input)
vagrant ssh lan-workstation -c 'ping -c 3 10.10.10.1'
# 2. Проверки, которые должны проходить:
# Пинг с админской станции MGMT до шлюза в сети управления
vagrant ssh mgmt-workstation -c 'ping -c 3 10.10.30.1'
# Проверка доступности SSH-порта 22 на шлюзе из сети MGMT
vagrant ssh mgmt-workstation -c 'nc -vz 10.10.30.1 22'❯ Этап 7: Stateful-фильтрация и conntrack
Stateful-фильтрация — это когда файрвол помнит, какие соединения уже открыты. Новое соединение разрешается правилом ct state new, а ответные пакеты пропускаются автоматически правилом ct state established,related accept. За это отвечает подсистема conntrack: она ведёт таблицу всех активных сессий.
Благодаря этому не нужно вручную открывать обратное направление — достаточно разрешить инициацию, а ответный трафик пройдёт сам.
Нужно добавить stateful-правила в цепочку FORWARD:
# Подключаемся по SSH к главному шлюзу
vagrant ssh linux-gateway
# Добавляем правило для автоматического пропуска обратного трафика уже установленных сессий
sudo nft add rule ip filter forward ct state established,related accept
# Проверяем текущую цепочку forward в таблице filter
sudo nft list chain ip filter forwardВ цепочке FORWARD отобразится примерно следующее:
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
}Теперь точечно разрешаем пользователям из локальной сети (10.10.10.0/24) инициировать новые HTTP-соединения к веб-серверу в DMZ (10.10.20.10:80):
# Добавляем правило разрешения инициации (ct state new) TCP-трафика на 80 порт веб-сервера
sudo nft add rule ip filter forward \
ip saddr 10.10.10.0/24 \
ip daddr 10.10.20.10 \
tcp dport 80 \
ct state new \
accept
# Проверяем итоговый порядок правил в цепочке forward
sudo nft list chain ip filter forwardПравила в цепочке идут в определённом порядке, и это важно:
ct state established,related accept — сначала пропускаются пакеты уже открытых сессий. Это быстро, потому что не нужно проверять каждое новое соединение.
ip saddr 10.10.10.0/24 ip daddr 10.10.20.10 tcp dport 80 ct state new accept — затем проверяются условия для новых HTTP-соединений.
policy drop — всё остальное, что не подошло под эти правила, блокируется.
Наблюдение за таблицей conntrack
Для наглядного отслеживания того, как ядро Linux отслеживает состояния сетевых сессий, установим утилиту conntrack и запустим мониторинг событий в реальном времени:
# Устанавливаем утилиту работы с таблицей соединений на linux-gateway
sudo apt install -y conntrack
# Запускаем отслеживание событий conntrack в режиме реального времени
sudo conntrack -EПри выполнении HTTP-запроса с lan-workstation в окне мониторинга отобразится жизненный цикл TCP-соединения:
[NEW] tcp 6 120 SYN_SENT src=10.10.10.10 dst=10.10.20.10 sport=... dport=80
[UPDATE] tcp 6 60 SYN_RECV src=10.10.10.10 dst=10.10.20.10 sport=... dport=80
[UPDATE] tcp 6 432000 ESTABLISHED src=10.10.10.10 dst=10.10.20.10 sport=... dport=80 [ASSURED]
[UPDATE] tcp 6 120 FIN_WAIT src=10.10.10.10 dst=10.10.20.10 sport=... dport=80
[UPDATE] tcp 6 30 LAST_ACK src=10.10.10.10 dst=10.10.20.10 sport=... dport=80
[UPDATE] tcp 6 120 TIME_WAIT src=10.10.10.10 dst=10.10.20.10 sport=... dport=80Ответные пакеты от веб-сервера к рабочей станции проходят через шлюз благодаря правилу ct state established,related — отдельно разрешать направление DMZ → LAN не нужно. Stateful-фильтрация работает.
❯ Этап 8: SNAT для LAN → External

На этом этапе мы организуем выход приватной локальной сети (LAN) во внешнюю сеть (lab-internet) через механизмы трансляции сетевых адресов — SNAT (Source Network Address Translation).
Логика работы SNAT
Вся приватная подсеть 10.10.10.0/24 будет выходить во внешний мир, подменяя свой исходный IP-адрес на WAN-адрес шлюза 172.16.0.2 (интерфейс enp0s8 на linux-gateway).
На upstream-router уже есть маршрут обратно в LAN через 172.16.0.2. Формально пакеты могли бы ходить и без SNAT. Но SNAT нужен, чтобы сымитировать реальную схему: вся приватная сеть выходит во внешний сегмент через один адрес шлюза.
Фиксация текущего рабочего ruleset
Перед созданием новых таблиц и цепочек экспортируем текущие активные правила в файл конфигурации /etc/nftables.conf, чтобы сохранить все ранее настроенные блокировки и правила фильтрации:
# Подключаемся по SSH к главному шлюзу
vagrant ssh linux-gateway
# Дамп активного правил с предварительной очисткой во временный файл
sudo sh -c 'echo "flush ruleset"; nft list ruleset' > /tmp/nftables.conf
# Замещаем постоянный файл конфигурации и устанавливаем безопасные права доступа
sudo mv /tmp/nftables.conf /etc/nftables.conf
sudo chmod 600 /etc/nftables.conf
# Проверяем синтаксис обновленного файла конфигурации
sudo nft -c -f /etc/nftables.confРазрешение инициации трафика LAN → External в FORWARD
Пакет сначала проходит фильтрацию в цепочке forward, и только потом выполняется SNAT. Разрешаем новые соединения из LAN во внешнюю сеть 198.51.100.0/24
# Разрешаем прохождение новых сессий (ct state new) из подсети LAN в подсеть lab-internet
sudo nft add rule ip filter forward \
ip saddr 10.10.10.0/24 \
ip daddr 198.51.100.0/24 \
ct state new \
acceptНастройка таблицы NAT и цепочки postrouting
Создаём отдельную таблицу nat для IPv4 и цепочку postrouting, которая перехватывает пакеты перед их отправкой в сетевой интерфейс:
# Создаём таблицу для правил трансляции адресов
sudo nft add table ip nat
# Создаём цепочку postrouting с привязкой к хуку postrouting и приоритетом srcnat (100)
sudo nft 'add chain ip nat postrouting { type nat hook postrouting priority srcnat; policy accept; }'Добавление правила SNAT
Добавляем правило, которое выполняет подмену исходного IP-адреса для всех пакетов из сети 10.10.10.0/24, уходящих через внешний интерфейс enp0s8:
# Выполняем SNAT (замену IP отправителя на 172.16.0.2) при выходе через интерфейс enp0s8
sudo nft add rule ip nat postrouting \
oifname "enp0s8" \
ip saddr 10.10.10.0/24 \
ip daddr 198.51.100.0/24 \
snat to 172.16.0.2
# Проверяем сформированные правила в таблице nat
sudo nft list table ip natПроверка работы с помощью tcpdump
Для проверки корректности подмены IP-адресов запускаем дамп сетевого трафика на виртуальной машине external-client и отправляем ICMP-пакеты с lan-workstation:
На external-client запускаем прослушивание ICMP-трафика:
# Запускаем перехват ICMP-пакетов на интерфейсе enp0s8 без разрешения DNS-имён sudo tcpdump -ni enp0s8 icmp
На lan-workstation отправляем тестовый запрос:
# Отправляем 3 ICMP-пакета на внешний адрес 198.51.100.10 ping -c 3 198.51.100.10
В консоли external-client отображается, что входящие пакеты поступают с адреса 172.16.0.2 (внешний интерфейс шлюза), а не с реального приватного адреса 10.10.10.10:
11:10:50 IP 172.16.0.2 > 198.51.100.10: ICMP echo request
11:10:50 IP 198.51.100.10 > 172.16.0.2: ICMP echo replyСохраняем полную конфигурацию файрвола:
# Записываем полный действующий набор правил в файл /etc/nftables.conf
sudo nft list ruleset | sudo tee /etc/nftables.conf > /dev/null
# Выставляем корректные права доступа на файл
sudo chmod 600 /etc/nftables.conf
# Проверяем синтаксическую целостность итогового файла
sudo nft -c -f /etc/nftables.conf
# Перезапускаем службу для проверки корректности загрузки при старте
sudo systemctl restart nftables❯ Этап 9: DNAT для External → DMZ

На этом этапе настраиваем DNAT, чтобы внешние клиенты из 198.51.100.0/24 могли обращаться к веб-серверу в DMZ (10.10.20.10:80) через шлюз linux-gateway.
Как пакет проходит через Netfilter:
PREROUTING — здесь выполняется DNAT: адрес назначения меняется до того, как ядро решит, куда маршрутизировать пакет.
FORWARD — здесь файрвол проверяет, можно ли пропустить пакет дальше. Важно: адрес назначения уже изменён DNAT.
POSTROUTING — здесь выполняется SNAT: адрес источника меняется перед отправкой пакета в интерфейс.
Настройка цепочки prerouting в таблице NAT
Подключаемся к главному шлюзу и создаём цепочку prerouting с привязкой к хуку prerouting и приоритетом dstnat (-100):
# Подключаемся по SSH к главному шлюзу
vagrant ssh linux-gateway
# Создаём цепочку prerouting для обработки входящих пакетов до маршрутизации
sudo nft 'add chain ip nat prerouting { type nat hook prerouting priority dstnat; policy accept; }'
# Проверяем структуру таблицы nat
sudo nft list table ip natДобавление правила DNAT
Добавляем правило DNAT: входящий TCP-трафик на 80-й порт внешнего интерфейса enp0s8 перенаправляется на веб-сервер в DMZ (10.10.20.10:80).
# Разрешаем прохождение новых сессий (ct state new) с внешнего интерфейса enp0s8 на интерфейс DMZ enp0s10 к веб-серверу
sudo nft add rule ip filter forward \
iifname "enp0s8" \
oifname "enp0s10" \
ip saddr 198.51.100.0/24 \
ip daddr 10.10.20.10 \
tcp dport 80 \
ct state new \
acceptПроверка работы DNAT и диагностика
С внешнего клиента обращаемся к адресу шлюза в сети lab-wan:
# Запрос с external-client на внешний адрес шлюза. Должна открыться стандартная страница Nginx.
vagrant ssh external-client -c 'curl --connect-timeout 5 http://172.16.0.2'Убедимся, что адрес назначения действительно меняется. Запускаем tcpdump на двух интерфейсах шлюза в разных терминалах:
На внешнем интерфейсе (enp0s8):
# Перехватываем HTTP-трафик на внешнем интерфейсе enp0s8 sudo tcpdump -ni enp0s8 tcp port 80На интерфейсе DMZ (enp0s10):
# Смотрим HTTP-трафик на интерфейсе DMZ sudo tcpdump -ni enp0s10 tcp port 80
Отправляем запрос с external-client. В первом окне увидим пакет 198.51.100.10 -> 172.16.0.2:80, во втором — тот же пакет, но уже с адресом 198.51.100.10 -> 10.10.20.10:80. Это подтверждает, что DNAT работает: адрес назначения подменяется на внутренний IP веб-сервера.
Фиксируем набор правил:
# Сохраняем итоговый набор правил nftables в конфигурационный файл /etc/nftables.conf
sudo nft list ruleset | sudo tee /etc/nftables.conf > /dev/null
# Выставляем правильные права доступа на файл
sudo chmod 600 /etc/nftables.conf
# Проверяем синтаксис файла конфигурации
sudo nft -c -f /etc/nftables.conf
# Перезапускаем службу nftables для проверки загрузки правил при старте
sudo systemctl restart nftables❯ Этап 10: Правила сегмента DMZ
На этом этапе настраиваем правила для трафика из DMZ. Работает принцип наименьших привилегий: разрешаем только то, что действительно нужно.
Что разрешено и что запрещено для DMZ
DMZ → LAN — запрещено.
DMZ → MGMT — запрещено.
DMZ → DNS (порт 53) — разрешено.
DMZ → External (порты 80, 443) — разрешено.
Отдельные запрещающие правила для LAN и MGMT писать не нужно: в цепочке FORWARD уже стоит политика drop, поэтому всё, что не разрешено явно, блокируется автоматически.
Разрешение доступа DMZ к локальному DNS-серверу
Веб-серверу в DMZ нужно уметь разрешать доменные имена через внутренний DNS (10.10.10.53). DNS работает и по UDP, и по TCP на 53-м порту, поэтому открываем оба протокола:
# Подключаемся по SSH к главному шлюзу
vagrant ssh linux-gateway
# Разрешаем новые UDP-соединения из подсети DMZ к локальному DNS-серверу
sudo nft add rule ip filter forward \
iifname "enp0s10" \
oifname "enp0s9" \
ip saddr 10.10.20.0/24 \
ip daddr 10.10.10.53 \
udp dport 53 \
ct state new \
accept
# Разрешаем новые TCP-соединения из подсети DMZ к локальному DNS-серверу
sudo nft add rule ip filter forward \
iifname "enp0s10" \
oifname "enp0s9" \
ip saddr 10.10.20.0/24 \
ip daddr 10.10.10.53 \
tcp dport 53 \
ct state new \
acceptРазрешение доступа DMZ в внешний мир (HTTP/HTTPS)
Разрешаем серверам из DMZ обращаться во внешнюю сеть (198.51.100.0/24) по протоколам HTTP (порт 80) и HTTPS (порт 443):
# Разрешаем исходящий HTTP-трафик (порт 80) из DMZ во внешнюю сеть
sudo nft add rule ip filter forward \
iifname "enp0s10" \
oifname "enp0s8" \
ip saddr 10.10.20.0/24 \
ip daddr 198.51.100.0/24 \
tcp dport 80 \
ct state new \
accept
# Разрешаем исходящий HTTPS-трафик (порт 443) из DMZ во внешнюю сеть
sudo nft add rule ip filter forward \
iifname "enp0s10" \
oifname "enp0s8" \
ip saddr 10.10.20.0/24 \
ip daddr 198.51.100.0/24 \
tcp dport 443 \
ct state new \
acceptТестирование изоляции и разрешенного доступа
Попытка достучаться из DMZ до рабочей станции в LAN должна приводить к таймауту:
# Проверка пинга из DMZ в LAN
vagrant ssh dmz-web-server -c 'ping -c 3 10.10.10.10'
# Проверка доступности SSH-порта на рабочей станции из DMZ
vagrant ssh dmz-web-server -c 'nc -vz -w 3 10.10.10.10 22'Проверка разрешенного доступа (DMZ → External:80):
На external-client:
# Запуск простого HTTP-сервера Python на порту 80 sudo python3 -m http.server 80 --bind 198.51.100.10С dmz-web-server:
# Запрос к внешнему серверу из зоны DMZ curl http://198.51.100.10
Запрос должен успешно завершиться ответом от сервера.
Сохраняем конфигурацию через nft как делали ранее.
❯ Этап 11: Доступ из MGMT-сегмента
На этом этапе настраиваем правила для административной сети (10.10.30.0/24). Из неё должен быть полный доступ к внутренним сегментам, но обратный доступ в MGMT должен быть закрыт.
Что разрешено для MGMT
MGMT → Gateway — SSH и ICMP (уже настроено в цепочке INPUT).
MGMT → LAN — SSH (порт 22) и ICMP.
MGMT → DMZ — SSH (порт 22) и ICMP.
Новые соединения из LAN и DMZ в MGMT запрещены. Ответный трафик в рамках соединений, созданных из MGMT, разрешается через ct state established,related.
Разрешаем MGMT → LAN
Администраторы из 10.10.30.0/24 (интерфейс enp0s16) должны иметь доступ по SSH и ICMP ко всем хостам в LAN (10.10.10.0/24, интерфейс enp0s9):
# Подключаемся по SSH к главному шлюзу
vagrant ssh linux-gateway
# Разрешаем новые SSH-соединения (порт 22) из сети MGMT в подсеть LAN
sudo nft add rule ip filter forward \
iifname "enp0s16" \
oifname "enp0s9" \
ip saddr 10.10.30.0/24 \
ip daddr 10.10.10.0/24 \
tcp dport 22 \
ct state new \
accept
# Разрешаем отправку ICMP Echo-Request (ping) из сети MGMT в подсеть LAN
sudo nft add rule ip filter forward \
iifname "enp0s16" \
oifname "enp0s9" \
ip saddr 10.10.30.0/24 \
ip daddr 10.10.10.0/24 \
icmp type echo-request \
ct state new \
acceptРазрешение доступа MGMT → DMZ
Точно так же разрешаем административной станции доступ к серверам в DMZ (10.10.20.0/24, интерфейс enp0s10):
# Разрешаем новые SSH-соединения (порт 22) из сети MGMT в подсеть DMZ
sudo nft add rule ip filter forward \
iifname "enp0s16" \
oifname "enp0s10" \
ip saddr 10.10.30.0/24 \
ip daddr 10.10.20.0/24 \
tcp dport 22 \
ct state new \
accept
# Разрешаем отправку ICMP Echo-Request (ping) из сети MGMT в подсеть DMZ
sudo nft add rule ip filter forward \
iifname "enp0s16" \
oifname "enp0s10" \
ip saddr 10.10.30.0/24 \
ip daddr 10.10.20.0/24 \
icmp type echo-request \
ct state new \
acceptСохраняем nft конфигурацию как это делали ранее.
❯ Этап 12: DNS-сервер в LAN

На этом этапе поднимаем DNS-сервер на lan-dns-server с помощью dnsmasq. Он будет разрешать локальные имена внутри стенда.
Установка dnsmasq
Подключаемся к выделенному серверу DNS-службы, обновляем списки пакетов и устанавливаем dnsmasq:
# Подключаемся по SSH к серверу внутренней DNS-службы
vagrant ssh lan-dns-server
# Обновляем индексы репозиториев apt
sudo apt update
# Устанавливаем легкий DNS-сервер dnsmasq в автоматическом режиме
sudo apt install -y dnsmasqКонфигурация dnsmasq
Создаём конфигурационный файл для описания локальной зоны и параметров прослушивания сетевых интерфейсов:
# Создаём и заполняем файл конфигурации /etc/dnsmasq.d/lab.conf
sudo tee /etc/dnsmasq.d/lab.conf > /dev/null <<'EOF'
# Прослушивать запросы только на интерфейсе локальной сети (enp0s8)
interface=enp0s8
# Привязываться строго к указанному IP-адресу DNS-сервера
listen-address=10.10.10.53
bind-interfaces
# Использовать стандартный 53-й порт для DNS-запросов
port=53
# Игнорировать системный файл /etc/resolv.conf (автономный режим без редиректа во внешнюю сеть)
no-resolv
# Статические локальные записи для лаборатории
address=/web.lab/10.10.20.10
address=/gateway.lab/10.10.10.1
EOFПримечание: Внешние DNS-серверы здесь не указаны специально — зона изолированная и работает только для внутренних нужд стенда.
Запускаем службу и проверяем работу:
# Перезапускаем службу dnsmasq и добавляем её в автозагрузку системы
sudo systemctl restart dnsmasq
sudo systemctl enable dnsmasq
# Проверяем текущий статус работы службы
sudo systemctl status dnsmasq --no-pager
# Проверяем, что служба успешно слушает 53-й порт (UDP/TCP)
sudo ss -lntup | grep ':53'Выполняем тестовые запросы напрямую к созданному DNS-серверу с помощью dig:
# Запрос A-записи для домена web.lab
dig @10.10.10.53 web.lab +short
# Запрос A-записи для домена gateway.lab
dig @10.10.10.53 gateway.lab +shortОжидаемые ответы: 10.10.20.10 для web.lab и 10.10.10.1 для gateway.lab.
Настройка клиента: dmz-web-server
Настраиваем веб-сервер в DMZ, чтобы он использовал локальный DNS-сервер (10.10.10.53) как основной:
# Подключаемся по SSH к веб-серверу в DMZ
vagrant ssh dmz-web-server
# Записываем конфигурацию Netplan с указанием nameserver
sudo tee /etc/netplan/60-lab.yaml > /dev/null <<'EOF'
network:
version: 2 # Версия формата конфигурации Netplan
ethernets:
enp0s8:
addresses:
- 10.10.20.10/24 # IP-адрес веб-сервера в DMZ
routes:
- to: default
via: 10.10.20.1 # Шлюз по умолчанию в сегменте DMZ
metric: 50 # Приоритетная метрика перед DHCP
nameservers:
addresses:
- 10.10.10.53 # IP-адрес нашего локального DNS-сервера в LAN
EOF
# Генерация и применение новых сетевых настроек
sudo netplan generate
sudo netplan applyПроверяем конфигурацию резолвера на клиенте:
# Проверяем статус DNS-клиента systemd-resolved
resolvectl status enp0s8В выводе параметра должны отображаться назначенные DNS сервера: 10.10.10.53.
Проверка резолвинга на клиенте и сквозных запросов
Проверяем разрешение имён и доступность по доменному имени с dmz-web-server:
# Запросы резолвинга через systemd-resolved
resolvectl query web.lab
resolvectl query gateway.lab
# Прямые запросы к DNS-серверу
dig @10.10.10.53 web.lab +short
# Проверка HTTP-запроса по доменному имени
curl http://web.lab❯ Заключение
В рамках лабораторного стенда мы настроили статическую маршрутизацию, активировали IP forwarding, внедрили stateful-фильтрацию через conntrack, реализовали SNAT и DNAT, настроили правила безопасности для DMZ и сегмента управления (MGMT), а также развернули локальный DNS-сервер.
Полный исходный код проекта с готовыми конфигурационными файлами и пошаговыми инструкциями доступен в репозитории canntstand/network-tools-practice.
Если вам интересна тема сетевой архитектуры, администрирования Linux и DevOps, делюсь своими практическими заметками и опытом в Telegram-канале My Tech Notes.
Может быть интересно:

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram‑канале ↩
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.