UN NewsWHO releases first global guidelines on child obesity as cases surgeESPN📈 Ovechkin's retirement announcement sparks major ticket price hikePunchBarcelona hails Messi’s remarkable career with ArgentinaThe Jerusalem PostHamam al-Hammami hired by flydubai as part of airline's mass hiring push - reportBollywood HungamaGuneet Monga Kapoor-led Women in Film India and Google Flow join hands to enable AI-powered filmmaking for new creative possibilitiesInquirerDTI maps halal calamansi supply chain in Oriental MindoroCapital FMPharmacists told to be on alert as Kenya confirms imported Ebola caseZDF heuteAktuelle Pressemitteilungen des ZDFCollider‘Mistborn’ Movie Script Is Officially Finished as Brandon Sanderson Reveals Next Step [Exclusive]SDP EspectáculosReseña de Carrie: una buena actualización de Prime Video, pero ¿dónde quedó el horror?Daily MaverickDANCE REVIEW: Elysium — 4 dances of bliss and seduction, transcendence and joy from Cape Ballet AfricaGIGAZINEChatGPTが生成したマンガに「実在するマンガ家の署名」が含まれているとの指摘
The Daily Newsstand · Free, Always
Wednesday, October 7, 2026

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

Translate
Сетевая архитектура стенда. Источник: Авторская схема (Excalidraw)

Сетевая архитектура стенда. Источник: Авторская схема (Excalidraw)

❯ Вступление

В этой статье будет разбираться построение многосегментного 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

  1. WAN (172.16.0.0/24): Внешний канал. Связывает наш шлюз с upstream-router.

  2. LAN (10.10.10.0/24): Внутренняя пользовательская сеть (lan-workstation и lan-dns-server).

  3. DMZ (10.10.20.0/24): Демилитаризованная зона для публичных сервисов (dmz-web-server). Это сегмент сети для публичных сервисов, к которым нужен доступ извне. Его специально держат отдельно от внутренней сети, чтобы если сервер взломают, злоумышленник не попал сразу в LAN.

  4. 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

Схема прохождения сетевых настроек от Vagrant до IP-адресов. Источник: Авторская схема (Excalidraw)

Схема прохождения сетевых настроек от Vagrant до IP-адресов. Источник: Авторская схема (Excalidraw)

На этом этапе зададим 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 активируется на двух ключевых машинах:

  1. linux-gateway — передаёт пакеты между внешним каналом WAN и внутренними сегментами (LAN, DMZ, MGMT).

  2. 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

Правила в цепочке идут в определённом порядке, и это важно:

  1. ct state established,related accept — сначала пропускаются пакеты уже открытых сессий. Это быстро, потому что не нужно проверять каждое новое соединение.

  2. ip saddr 10.10.10.0/24 ip daddr 10.10.20.10 tcp dport 80 ct state new accept — затем проверяются условия для новых HTTP-соединений.

  3. 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

Схема SNAT. Источник: Авторская схема (Excalidraw)

Схема SNAT. Источник: Авторская схема (Excalidraw)

На этом этапе мы организуем выход приватной локальной сети (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. Источник: Авторская схема (Excalidraw)

Схема DNAT. Источник: Авторская схема (Excalidraw)

На этом этапе настраиваем 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

Лого dnsmasq. Источник

Лого dnsmasq. Источник

На этом этапе поднимаем 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‑канале ↩

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.