
❯ Вступление
В этой статье будет разбираться построение многосегментного 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/24
LAN Workstation
network: version: 2 ethernets: enp0s8: # lab-lan addresses: - 10.10.10.10/24
LAN DNS Server
network: version: 2 ethernets: enp0s8: # lab-lan addresses: - 10.10.10.53/24
DMZ Web Server
network: version: 2 ethernets: enp0s8: # lab-dmz addresses: - 10.10.20.10/24
MGMT Workstation
network: version: 2 ethernets: enp0s8: # lab-mgmt addresses: - 10.10.30.10/24
External 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 apply
Upstream 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 apply
LAN 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 apply
DMZ 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 apply
MGMT 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 apply
External 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_forward
Upstream 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‑канале ↩