Дорогие читатели, добрый день. На связи Николай Едомский, руководитель группы сетевых инженеров компании Единый ЦУПИС.
В предыдущих статьях мы рассмотрели теоретическую базу построения нашего комплексного решения IPsecHub+. Мы также обозначили требования к топологии, после чего при помощи различных решений закрывали одно требование за другим. Для сохранения структуры повествования предыдущих статьях я не стал включать разделы, содержащие непосредственно конфигурацию и фрагменты кода. А сегодня мы с вами займемся тем, что шаг за шагом настроим наш концентратор.
Как уже было упомянуто в предыдущих статьях, наше решение является мультивендорным. Его можно реализовать на платформах различных вендоров. Но мы рассмотрим практическую реализацию на основе Linux-машины.
Целевая схема
Linux-маршрутизация включает в себя очень много аспектов, и все они являются темами для отдельных статей, в том числе и на Хабре. Мы же пройдемся по основным этапам создания концентратора. Назовем это неким MVP, который вам уже предлагается донастроить самостоятельно. Основной упор практикума - это собрать базу команд и конфигураций, которые помогут сориентироваться в вопросе.
Нам предстоит поэтапно выстроить что-то такое:

Напомню, в namespace default мы организуем связность нашего концентратора с внешним миром. Отсюда же мы построим IPsec-туннель с удаленным филиалом. Вспомнить, какие задачи решает эскалаторная топология, можно здесь.
Обратите внимание, что в примере присутствует пересечение адресных пространств филиала и ЦОД. Подсеть 192.168.0.0/24 присутствует как в ЦОД, так и в филиале. В нашем практикуме мы организуем стыковку с проработкой этого пересечения.
И небольшая ремарка по поводу нейминга - VRF или namespace? На схемах я придерживаюсь классического названия сущностей - VRF, так как оно более универсально в разрезе сетевого администрирования. Но в практикуме мы будем оперировать понятием namespace, раз уж практикум посвящен целиком Linux. Эти понятия не равнозначны, но для реализации текущей функциональности в Linux нам достаточно будет применить namespace.
Связность концентратора с сетью Интернет
Хотя связность концентратора с сетью Интернет напрямую и не связана с темой цикла наших статей, я все же решил уделить этому вопросу внимание. Это важно хотя бы потому, что одна из главных наших целей - это фильтровать через межсетевой экран весь трафик, идущий к концентратору и от него.
А если ставить внешний интерфейс концентратора непосредственно в сегмент стыковки с оператором, то межсетевой экран в этом случае уже не будет фильтровать трафик из Интернета к концентратору. Вот как это выглядит:

Для решения этой проблемы можно запросить у оператора состыковаться на адресах из RFC1918, арендовать подсеть внешних адресов (4.4.4.0/28) и попросить их смаршрутизировать ее через наш "серый" СЕ-адрес (10.0.0.2). И уже внутри нашей зоны ответственности мы точечно можем распределять адреса из этой арендованной подсети по разным хостам. Вот как это выглядит (для упрощения в качестве СЕ я использую межсетевой экран):

Такая связка позволит:
Сэкономить «белые» адреса, не тратя их на broadcast, PE и net address.
Избежать выставления внешнего интерфейса концентратора "в мир" без фильтрации.
Избежать опасность DDoS на стыковочные адреса маршрутизаторов (они ведь у нас из RFC1918, не забываем).
Масштабировать аренду новых внешних подсетей, если необходимо. Нам просто нужно арендовать их у оператора и попросить смаршрутизировать все через тот же наш СЕ-адрес.
Если оператор не имеет возможности маршрутизировать арендованную подсеть через ваш СЕ, то аналогичную схему можно реализовать через proxy-arp. Наш СЕ-маршрутизатор будет отвечать на arp-запросы до тех внешних адресов, до которых у него самого есть маршруты внутрь нашей зоны ответственности. Но сэкономить адреса уже не получится. Предпочтительнее все же первый вариант.
Базовая конфигурация интерфейсов
Настройку концентратора мы начнем с конфигурации базовых интерфейсов - vlan и bond.
Давайте предположим, что для подключения сервера к коммутатору мы используем бонд с LACP. На основе этого бонда мы создадим vlan-интерфейсы, которые уже затем поместим в соответствующие namespace.
Настройку интерфейсов при помощи конфигурационного файла netplan можно представить следующим образом:
Скрытый текст
network:
version: 2
ethernets:
enp138s0f0: {}
enp138s0f1: {}
bonds:
bond0:
interfaces:
- enp138s0f0
- enp138s0f1
parameters:
mode: 802.3ad
lacp-rate: fast
transmit-hash-policy: layer3+4
vlans:
bond0.9:
id: 9
link: bond0
addresses:
- 172.16.1.2/24
bond0.10:
id: 10
link: bond0
dhcp4: no
bond0.11:
id: 11
link: bond0
dhcp4: no
Эту конфигурацию можно положить, например, в файл /etc/netplan/01-vlans.yml.
И конфигурация для loopback, можно положить в/etc/netplan/99-lo.yml.
Скрытый текст
network: version: 2 renderer: networkd ethernets:
lo:
addresses: - 4.4.4.4/32
Стоит отметить, что адреса следует настраивать при помощи netplan только в случае, если интерфейс находится в namespace default. Если интерфейс в последствии будет помещен в другой namespace, то его ip-адрес будет удален из настроек. К сожалению, netplan на 2025 год не поддерживает namespace, поэтому все связанные манипуляции мы будем выполнять в bash.
Итак, для того, чтобы погрузить vlan-интерфейсы в namespace, их сначала нужно создать:
ip netns add ipsecTo
ip netns add ipsecFrom
Вывести список существующих на машине namespace можно следующим образом:
ip netns
Удалить namespace:
ip netns delete <nsName>
Погрузим vlan-интерфейсы в соответствующие namespace (ipsecTo в данном случае):
ip link set bond0.10 netns ipsecTo
После этого можно добавить адрес и поднять интерфейс. Чтобы изменить ip-настройки интерфейса, находящегося в namespace, нам нужно будет выполнять все команды с определенной преамбулой - ip netns exec <nsName>. Это относится к любой системной команде, которая должна быть выполнена в рамках namespace (например, tcpdump). Делаем:
ip netns exec ipsecTo ip a add 172.16.2.2/24 dev bond0.10
ip netns exec ipsecTo ip link set bond0.10 up
Посмотреть ip-настройки всех интерфейсов, находящихся в namespace ipsecTo, можно командой:
ip netns exec ipsecTo ip a
На этом этапе у нас получится вот такая картина:

Для завершения базовых настроек нужно проделать аналогичные действия с vlan-интерфейсом bond1.11:
ip link set bond0.11 netns ipsecFrom
ip netns exec ipsecFrom ip a add 172.16.3.2/24 dev bond0.11
ip netns exec ipsecFrom ip link set bond0.11 up
В итоге получаем:

Стыковочный контур
После того, как мы завершили настройку базовой стыковки концентратора с межсетевым экраном, давайте приступим к настройке стыковочного контура. Он будет использоваться непосредственно для приема трафика с филиала.
Начнем с того, что добавим namespace, предназначенный для стыковки с внешним филиалом. В него будут приниматься необработанные маршруты до филиальных подсетей. Назовем его site1_ext.
ip netns add site1_ext
В этом namespace, напомню, мы будем прикрывать адреса филиала суррогатными адресами. Далее можно создать GRE-интерфейс и добавить его в namespace. Создание GRE возможно и через netplan, но я предпочитаю делать это в bash, так как настройка gre уже не входит в базовую конфигурацию сети концентратора, и я не вижу смысла выносить настройку GRE-интерфейса в netplan.
ip tunnel add gre_site1 mode gre local 4.4.4.4 remote 9.9.9.9 ttl 32
ip link set gre_site1 netns site1_ext
Затем настроим для него адреса и поднимем интерфейс:
ip netns exec site1_ext ip a add 172.16.4.1/30 dev gre_site1
ip netns exec site1_ext ip link set gre_site1 up
Вот что у нас получилось:

После этого можно приступить к созданию namespace site1_int. В этом namespace мы при помощи NAT прикроем адреса ЦОД перед тем, как они будут смаршрутизированы в филиал. Подробнее про необходимость каскада стыковочных namespace можно почитать здесь, раздел "Ситуация с конфликтами адресов".
ip netns add site1_int
Далее нам нужно обеспечить связность между namespace site1_int и site1_ext. Мы будем делать это при помощи veth-интерфейсов. Подробнее про них можно глянуть здесь. А сейчас давайте создадим их. Пара veth создается одной командой:
ip link add t_site1_ext type veth peer name t_site1_int
Представьте, что это два интерфейса, связанных виртуальным патчкордом (virtual Ethernet). Они будут находиться в виртуальной L2-связности друг с другом, даже будучи разнесенными в разные namespace.
И пару слов про нейминг интерфейсов. Имейте в виду, что в Linux имя сетевого интерфейса ограничено 12 символами, поэтому наш нейминг может быть несколько вычурным. А именно:
"t_site1_int"читаем как "to namespace site1_int", то есть интерфейс, ведущий из site1_ext до site1_int.
"t_site1_ext"читаем как "to namespace site1_ext". Ведет в namespace site1_ext из namespace site1_int.
Далее помещаем эти два интерфейса в соответствующие namespece:
ip link set t_site1_ext netns site1_int
ip link set t_site1_int netns site1_ext
И добавляем адреса на интерфейсы. Я использую /31 маску с целью упрощения подсчета стыковочных подсетей для veth-интерфейсов. Но можно и применять классическую /30 подсеть.
ip netns exec site1_int ip addr add 10.90.0.4/31 dev t_site1_ext
ip netns exec site1_int ip link set t_site1_ext up
ip netns exec site1_ext ip addr add 10.90.0.5/31 dev t_site1_int
ip netns exec site1_ext ip link set t_site1_int up
Несмотря на то, что эти два интерфейса находятся в разных namespace, связность между адресами10.90.2.1и 10.90.2.0сохраняется, так как они соединены тем самым виртуальным патчкордом.
Вот что получилось:

И давайте по аналогии создадим veth-интерфейсы, которые свяжут namespace "ipsecTo" с namespace "site1_int":
ip link add t_site1 type veth peer name f_fw_site1ip link set t_site1 netns ipsecToip link set f_fw_site1 netns site1_int
ip netns exec site1_int ip addr add 10.90.0.1/31 dev f_fw_site1ip netns exec site1_int ip link set f_fw_site1 up
ip netns exec ipsecTo ip addr add 10.90.0.0/31 dev t_site1ip netns exec ipsecTo ip link set t_site1 up
При этом:
"t_site1"читаем как "to namespace site1", то есть ведущий в namespace site1_int из namespace ipsecTo.
"f_fw_site1"читаем как "From firewall to namespace site1", то есть ведущий в namespace site1_int от межсетевого экрана.
И по аналогии обеспечим связность namespace ipsecFrom и site1_int:
ip link add f_site1 type veth peer name t_fw_site1ip link set f_site1 netns ipsecToip link set t_fw_site1 netns site1_int
ip netns exec site1_int ip addr add 10.90.1.0/31 dev f_site1ip netns exec site1_int ip link set f_site1 up
ip netns exec ipsecFrom ip addr add 10.90.1.1/31 dev t_fw_site1ip netns exec ipsecFrom ip link set t_fw_site1 up
При этом:
"
f_site1"читаем как "from namespace site1", то есть ведущий из namespace site1_int в namespace ipsecFrom, к межсетевому экрану.
"
t_fw_site1" читаем как "to firewall from namespace site1", то есть ведущий из namespace site1_int к межсетевому экрану в namespace ipsecFrom.
Итого:

NAT
После того, как мы подготовили namespace-сегментацию и создали все необходимые интерфейсы, нужно разрешить пересечение адресных пространств ЦОД и филиала.
Для начала спрячем за суррогатными адресами филиал. В сегменте ЦОД филиал будет представлен подсетью из RFC6598 - 100.64.0.0/24. При этом в филиале подсеть ЦОД мы будем представлять уже другой суррогатной подсетью - 100.65.0.0/24

Сначала прикроем трафик, идущий из филиала (для соединений из филиала):
ip netns exec site1_ext iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j NETMAP --to 100.64.0.0/24
Затем прокинем до реальной сети трафик, идущий на суррогат филиала (для соединений в филиал):
ip netns exec site1_ext iptables -t nat -A PREROUTING -d 100.64.0.0/24 -j NETMAP --to 192.168.0.0/24
Проделаем ту же процедуру, но уже для подсети ЦОД. Спрячем ее для филиала за суррогатом 100.65.0.0/24. Результирующие правила выполняем в namespace site1_int:
ip netns exec site1_int iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j NETMAP --to 100.65.0.0/24
ip netns exec site1_int iptables -t nat -A PREROUTING -d 100.65.0.0/24 -j NETMAP --to 192.168.0.0/24
При корректной настройке правил трафик из пересекающихся сетей никогда не соприкоснется, так как на выходе с namespace site_int или site_ext он всегда будет прикрыт суррогатом. Пересечение адресов мы таким образом разрешили.
Рассмотрим пример, где хост в филиале с адресом 192.168.0.1 желает пингануть адрес ЦОД, закрытый за суррогатом 100.65.0.200:
src_ip: 192.168.0.1, dst_ip: 100.65.0.200
Что мы увидим, если запустим tcpdump на интерфейсе t_site1_int в namespace site1_ext, то есть после отработки всех NAT-правил?
src_ip: 100.64.0.1, dst_ip: 100.65.0.200
А tcpdump на интерфейсе t_fw_site1 в namespace site1_int после отработки всех NAT-правил уже покажет вот это:
src_ip: 100.64.0.1, dst_ip: 192.168.0.200
Уважаемые коллеги, на этом первый этап нашего практикума завершен. В следующей статье мы настроим деймоны маршрутизации и шифрования.
Навигация по циклу:
ValdikSS
VRF и Namespace это разные сущности, и в Linux есть и то, и то. Вы настраиваете Namespace, это не VRF.
https://docs.kernel.org/networking/vrf.html
Gidral88 Автор
Да, согласен. Чуть поправлю в тексте.