В прошлой статье мы создали отдельный виртуальный коммутатор Hyper-V специально под наш стенд и заменили VPCS на Alpine.
В этой статье мы займемся настройкой Ubuntu и установим клиент OpenSSH на Alpine.
Думаю, что отдельно стоит объяснить, почему для доступа Alpine в интернет мы будем поднимать NAT именно на Ubuntu, а не включать в Hyper-V встроенный NAT для Internal-коммутатора. Технически это тоже сработало бы, но тогда весь смысл теряется.
Цель статьи – на практике разобраться в маршрутизации, NAT, DHCP и DNS средствами Linux, а не использовать готовые решения Windows.
Напомним схему стенда и таблицу с адресацией:

Устройство | Интерфейс | Коммутатор Hyper-V | IP-адрес | Роль |
|---|---|---|---|---|
Ubuntu 26.04 | eth0 | Default Switch | DHCP (как в первой статье) | Выход в интернет (WAN) |
Ubuntu 26.04 | eth1 (новый) | GNS3-Lab (Internal) | 192.168.50.1/24 | Шлюз, DHCP- и DNS-сервер стенда |
GNS3 VM | eth0 | Default Switch | DHCP | Управление GNS3 VM, интернет для самой ВМ (нужен, чтобы скачать образ Alpine из Docker Hub) |
GNS3 VM | eth1 (новый) | GNS3-Lab (Internal) | IP-адрес для работы стенда не потребуется | Точка входа топологии GNS3 в сеть стенда |
Alpine (узел в GNS3) | eth0 | — | 192.168.50.x/24 (DHCP или статически) | Первый узел стенда |
Хост Windows | vEthernet (GNS3-Lab) | — | DHCP от Ubuntu | Возможность достучаться до стенда прямо с хоста |
Как избежать петли маршрутизации в Windows
Почему мы начинаем с этого шага? Ubuntu будет работать не только как DHCP-сервер для узлов GNS3, но и выдавать им шлюз 192.168.50.1. Поскольку виртуальный интерфейс Windows также подключен к коммутатору GNS3-Lab, он получит от Ubuntu те же параметры, включая маршрут по умолчанию.
В результате Windows начнет отправлять интернет-трафик через Ubuntu:
Windows → Ubuntu → Default Switch → Windows
Получится петля маршрутизации, примерно как у меня:
PS C:\Users\jetcry> tracert 8.8.8.8 Трассировка маршрута к dns.google [8.8.8.8] с максимальным числом прыжков 30: 1 <1 мс * <1 мс 192.168.50.1 2 1 ms <1 мс <1 мс MSI-PRO.mshome.net [172.17.176.1] 3 * * * Превышен интервал ожидания для запроса. 4 <1 мс <1 мс <1 мс MSI-PRO.mshome.net [172.17.176.1] 5 * *
Есть два способа избежать петли маршрутизации. Рассмотрим их по очереди. Выберите любой из них. В своем стенде я использовал команду netsh.
1. Запрет на получение маршрута по умолчанию через DHCP
Оставим получение адреса по DHCP, но запретим Windows принимать маршрут по умолчанию через через vEthernet (GNS3-Lab):
Set-NetIPInterface ` -InterfaceAlias "vEthernet (GNS3-Lab)" ` -AddressFamily IPv4 ` -IgnoreDefaultRoutes Enabled
Параметр IgnoreDefaultRoutes Enabled позволяет получать адрес по DHCP, но запрещает динамически добавлять маршрут по умолчанию через этот интерфейс.
2. Назначение интерфейсу статического адреса
Воспользуемся консольной утилитой netsh. Общий синтаксис команды:
netsh interface ipv4 set address name="<имя интерфейса>" static <IP-адрес> <маска> <шлюз>
В моем случае:
netsh interface ipv4 set address name="vEthernet (GNS3-Lab)" static 192.168.50.2 255.255.255.0 none
Результат через ipconfig:
Адаптер Ethernet vEthernet (GNS3-Lab): DNS-суффикс подключения . . . . . : Локальный IPv6-адрес канала . . . : fe80::b832:7886:d6ba:6934%28 IPv4-адрес. . . . . . . . . . . . : 192.168.50.2 Маска подсети . . . . . . . . . . : 255.255.255.0 Основной шлюз. . . . . . . . . :
В обоих случаях Windows сохранит доступ к сети стенда, но не будет использовать Ubuntu как основной шлюз для выхода в интернет.
Третьим вариантом в PowerShell можно задать имя интерфейса через переменную и последовательно изменить его параметры. Такой способ будет длиннее, но удобнее для автоматизации и повторного использования в скриптах. Если тема интересна, можем разобрать подобную настройку в одной из следующих статей.
Переходим к настройке Ubuntu 26.04.
Ubuntu 26.04 как маршрутизатор стенда
Заходим на Ubuntu по SSH:
ssh <пользователь>@<адрес eth0>
Первым делом смотрим, как определился новый адаптер eth1:
jetcry@ubuntu26-04:~$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:02:64:0c brd ff:ff:ff:ff:ff:ff altname enx00155d02640c inet 172.24.183.109/20 metric 100 brd 172.24.191.255 scope global dynamic eth0 valid_lft 86224sec preferred_lft 86224sec inet6 fe80::215:5dff:fe02:640c/64 scope link proto kernel_ll valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:02:64:0f brd ff:ff:ff:ff:ff:ff altname enx00155d02640f inet6 fe80::215:5dff:fe02:640f/64 scope link proto kernel_ll valid_lft forever preferred_lft forever jetcry@ubuntu26-04:~$
Видим, что новый интерфейс пока еще без IP-адреса.
Настраиваем статический адрес через netplan
Ubuntu 26.04 использует Netplan версии 1.2 с рендерером systemd-networkd. Синтаксис YAML-файлов не изменился по сравнению с Ubuntu 24.04, но теперь Netplan проверяет права доступа к конфигурационным файлам, поэтому сразу устанавливаем для файла права 600.
Подробнее смотрите в обзоре сетевого стека 26.04 на ComputingForGeeks (на английском).
Создаем отдельный файл, чтобы не трогать конфигурацию eth0, которая осталась от установки:
sudo nano /etc/netplan/60-gns3-lab.yaml
Содержимое файла:
network: version: 2 renderer: networkd ethernets: eth1: dhcp4: false addresses: - 192.168.50.1/24
Выставляем права и применяем:
sudo chmod 600 /etc/netplan/60-gns3-lab.yaml sudo netplan apply
Проверяем:
ip -4 addr show eth1
Результат:
jetcry@ubuntu26-04:~$ ip -4 addr show eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 altname enx00155d02640f inet 192.168.50.1/24 brd 192.168.50.255 scope global eth1 valid_lft forever preferred_lft forever jetcry@ubuntu26-04:~$
Маршрут по умолчанию при этом остается через eth0 и Default Switch. Трогать его не нужно. Команда ip route покажет default via <адрес шлюза по DHCP> через eth0. В моем случае:
jetcry@ubuntu26-04:~$ ip route default via 172.24.176.1 dev eth0 proto dhcp src 172.24.183.109 metric 100 172.24.176.0/20 dev eth0 proto kernel scope link src 172.24.183.109 metric 100 172.24.176.1 dev eth0 proto dhcp scope link src 172.24.183.109 metric 100 192.168.50.0/24 dev eth1 proto kernel scope link src 192.168.50.1 jetcry@ubuntu26-04:~$
Обратите внимание, что сеть 192.168.50.0/24 появилась через eth1.
Включаем маршрутизацию пакетов
По умолчанию Linux не пересылает трафик между интерфейсами. Включаем IP forwarding и делаем его постоянным:
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-gns3-router.conf sudo sysctl --system
Фрагмент результата:
jetcry@ubuntu26-04:~$ echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-gns3-router.conf sudo sysctl --system net.ipv4.ip_forward = 1 * Applying /usr/lib/sysctl.d/10-apparmor.conf ... * Applying /usr/lib/sysctl.d/10-coredump-debian.conf ... ... net.ipv4.ip_forward = 1
Проверяем:
sysctl net.ipv4.ip_forward
Правильный результат:
net.ipv4.ip_forward = 1
Настраиваем NAT через nftables
Бэкенд iptables по умолчанию работает поверх nftables начиная с Ubuntu 20.10. Поэтому логично сразу писать правила на родном языке nft. Так рекомендует и официальная документация Ubuntu по nftables.
Обновляем пакеты и устанавливаем nftables:
sudo apt update && sudo apt install nftables -y
Редактируем основной конфиг:
sudo nano /etc/nftables.conf
Мой конфиг, можете им полностью заменить базовый:
#!/usr/sbin/nft -f flush ruleset table inet filter { chain input { type filter hook input priority filter; policy accept; } chain forward { type filter hook forward priority filter; policy accept; } chain output { type filter hook output priority filter; policy accept; } } table ip nat { chain postrouting { type nat hook postrouting priority srcnat; policy accept; oifname "eth0" ip saddr 192.168.50.0/24 masquerade } }
Это правило подменяет адрес источника у пакетов из подсети 192.168.50.0/24, которые уходят через eth0 в сторону интернета. Внешние узлы будут видеть их как трафик от самой Ubuntu.
Для лабораторного стенда цепочки
input/forwardоставлены с политикойaccept– это упрощает диагностику. В реальной сети для цепочекinputиforwardлучше использовать политикуdrop, явно разрешая только необходимый трафик.
Применяем и включаем автозапуск:
sudo nft -f /etc/nftables.conf sudo systemctl enable --now nftables.service
Результат:
jetcry@ubuntu26-04:~$ sudo nft -f /etc/nftables.conf sudo systemctl enable --now nftables.service Created symlink '/etc/systemd/system/sysinit.target.wants/nftables.service' → '/usr/lib/systemd/system/nftables.service'. jetcry@ubuntu26-04:~$
Проверяем, что правило загрузилось:
sudo nft list ruleset
Что Вы должны увидеть (самое важное):
jetcry@ubuntu26-04:~$ sudo nft list ruleset ... table ip nat { chain postrouting { type nat hook postrouting priority srcnat; policy accept; oifname "eth0" ip saddr 192.168.50.0/24 masquerade } } jetcry@ubuntu26-04:~$
Если таблицы nat в выводе нет – значит конфиг не применился. В этом случае проверьте синтаксис файла командой sudo nft -c -f /etc/nftables.conf (флаг -c только проверяет файл, не применяя его).
Поднимаем DHCP и DNS для стенда
Чтобы новым узлам в топологии не приходилось прописывать адреса руками, поставим dnsmasq, легкий DHCP/DNS-сервер. На мой взгляд, он как раз подходит для небольших сетей. Если интересно, вот официальная документация dnsmasq.
Устанавливаем:
sudo apt install dnsmasq -y
Создаем отдельный конфиг, не трогая /etc/dnsmasq.conf:
sudo nano /etc/dnsmasq.d/gns3-lab.conf
Вставляем следующие строки в файл:
interface=eth1 bind-interfaces dhcp-range=192.168.50.100,192.168.50.200,255.255.255.0,12h dhcp-option=3,192.168.50.1 dhcp-option=6,192.168.50.1 server=8.8.8.8 server=1.1.1.1
Разберем параметры из файла:
interface=eth1+bind-interfaces– dnsmasq слушает только на нашем внутреннем интерфейсе и не пытается занять порт 53 на всех адресах сразу. Это важно: на Ubuntu по умолчанию порт 53 на 127.0.0.53 занятsystemd-resolved, и безbind-interfacesвозможен конфликт;dhcp-range– пул адресов для узлов стенда;dhcp-option=3– адрес шлюза (сама Ubuntu);dhcp-option=6– DNS-сервер для клиентов (тоже сама Ubuntu, она форвардит запросы дальше);server=8.8.8.8/server=1.1.1.1– вышестоящие DNS-серверы, которым dnsmasq пересылает запросы, если не может ответить сам.
Перезапускаем и включаем автозапуск:
sudo systemctl restart dnsmasq sudo systemctl enable dnsmasq
Результат:
jetcry@ubuntu26-04:~$ sudo systemctl restart dnsmasq sudo systemctl enable dnsmasq Synchronizing state of dnsmasq.service with SysV service script with /usr/lib/systemd/systemd-sysv-install. Executing: /usr/lib/systemd/systemd-sysv-install enable dnsmasq jetcry@ubuntu26-04:~$
Проверяем, что служба слушает нужные порты:
sudo ss -lunp | grep dnsmasq
Должны увидеть:
jetcry@ubuntu26-04:~$ sudo ss -lunp | grep dnsmasq UNCONN 0 0 127.0.0.1:53 0.0.0.0:* users:(("dnsmasq",pid=3128,fd=8)) UNCONN 0 0 192.168.50.1:53 0.0.0.0:* users:(("dnsmasq",pid=3128,fd=6)) UNCONN 0 0 0.0.0.0%eth1:67 0.0.0.0:* users:(("dnsmasq",pid=3128,fd=4)) UNCONN 0 0 [::1]:53 [::]:* users:(("dnsmasq",pid=3128,fd=12)) UNCONN 0 0 [fe80::215:5dff:fe02:640f]%eth1:53 [::]:* users:(("dnsmasq",pid=3128,fd=10)) jetcry@ubuntu26-04:~$
Получение сетевых параметров Alpine
В GNS3 у нас создана следующая топология:

Перед получением параметров по DHCP зададим имя нашему узлу Alpine командой:
hostname alpine-pc-1
После этого вводим команду:
udhcpc -i eth0 -x hostname:alpine-pc-1 -n -q
Здесь:
hostname alpine-pc-1задает имя внутри Alpine;-x hostname:alpine-pc-1передает это имя DHCP-серверу;-nзавершает команду с ошибкой, если DHCP-сервер не ответил;-qзавершает DHCP-клиент после успешного получения аренды.Если не нужно передавать имя на DHCP-сервер, то базовая команда выглядит так:
udhcpc -i eth0
В моем случае:
/ # hostname alpine-pc-1 / # udhcpc -i eth0 -x hostname:alpine-pc-1 -n -q udhcpc: started, v1.37.0 udhcpc: broadcasting discover udhcpc: broadcasting select for 192.168.50.154, server 192.168.50.1 udhcpc: lease of 192.168.50.154 obtained from 192.168.50.1, lease time 43200 / #
Вижу, что Alpine получил адрес 192.168.50.154.
Проверяю содержимое файла с текущими DHCP-арендами dnsmasq:
cat /var/lib/misc/dnsmasq.leases
Здесь будут срок аренды, MAC-адрес, выданный IP-адрес и имя клиента:
jetcry@ubuntu26-04:~$ cat /var/lib/misc/dnsmasq.leases 1784011574 02:42:c1:0d:ea:00 192.168.50.154 alpine-pc-1 01:02:42:c1:0d:ea:00 1784010791 00:15:5d:02:64:10 192.168.50.158 MSI-PRO 01:00:15:5d:02:64:10 1784010547 00:15:5d:02:64:0e 192.168.50.156 gns3vm 01:00:15:5d:02:64:0e jetcry@ubuntu26-04:~$
Видим, что адреса получили MSI-PRO (мой физический хост), GNS3 VM и Alpine.
Можно воспользоваться еще одной командой для просмотра журнала выданных аренд в реальном времени:
sudo journalctl -u dnsmasq -f
В моем случае (фрагмент вывода):
jetcry@ubuntu26-04:~$ sudo journalctl -u dnsmasq -f ... Jul 13 18:46:14 ubuntu26-04 dnsmasq-dhcp[1489]: DHCPDISCOVER(eth1) 02:42:c1:0d:ea:00 Jul 13 18:46:14 ubuntu26-04 dnsmasq-dhcp[1489]: DHCPOFFER(eth1) 192.168.50.154 02:42:c1:0d:ea:00 Jul 13 18:46:14 ubuntu26-04 dnsmasq-dhcp[1489]: DHCPREQUEST(eth1) 192.168.50.154 02:42:c1:0d:ea:00 Jul 13 18:46:14 ubuntu26-04 dnsmasq-dhcp[1489]: DHCPACK(eth1) 192.168.50.154 02:42:c1:0d:ea:00 alpine-pc-1
Устанавливаем SSH-клиент на Alpine
Перед установкой проверим, что на Alpine работают маршрутизация, NAT и DNS:
/ # ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: seq=0 ttl=60 time=1.183 ms 64 bytes from 8.8.8.8: seq=1 ttl=60 time=0.721 ms ^C --- 8.8.8.8 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.721/0.952/1.183 ms / # ping google.com PING google.com (216.58.201.238): 56 data bytes 64 bytes from 216.58.201.238: seq=0 ttl=60 time=0.663 ms 64 bytes from 216.58.201.238: seq=1 ttl=60 time=1.204 ms ^C --- google.com ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.663/0.933/1.204 ms / #
Связь есть. Теперь обновим индексы репозиториев командой:
apk update
Затем установим клиент OpenSSH:
apk add openssh-client-default
Пакет openssh-client-default содержит обычный клиент OpenSSH без установки SSH-сервера.
Проверим установку командой:
ssh -V
Результат:
/ # ssh -V OpenSSH_10.3p1, OpenSSL 3.5.7 9 Jun 2026 / #
Подключимся к Ubuntu по SSH:
ssh jetcry@192.168.50.1
Подключение прошло штатно:
/ # ssh jetcry@192.168.50.1 The authenticity of host '192.168.50.1 (192.168.50.1)' can't be established. ED25519 key fingerprint is: SHA256:VKcj0f1SOhRKdKa3/LmbZTiC1MYEr/LUCBAnWvoikhI This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.50.1' (ED25519) to the list of known hosts. jetcry@192.168.50.1's password: Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-27-generic x86_64) ... jetcry@ubuntu26-04:~$
Отмечу, что Alpine в GNS3 работает как Docker-контейнер, поэтому установленные вручную пакеты после выключения GNS3 не сохранятся. То есть при следующем запуске GNS3 придется снова устанавливать openssh-client-default.
Итоги
Ubuntu теперь работает как полноценный шлюз для стенда. Она форвардит пакеты, подменяет адреса через NAT и раздает DHCP/DNS для устройств внутри GNS3.
Все это никак не влияет на ее интерфейс eth0 и подключение по SSH, которым мы пользовались в первой статье.
Также мы настроили получение адреса по DHCP на Alpine, установили клиент OpenSSH и удаленно подключились к нашей виртуальной Ubuntu 26.04.
В следующей статье настроим собственный образ Alpine со своими инструментами и подготовим его для дальнейших работ.

