Linux network namespace с bridge

Linux network namespace с bridge


Что такое network namespace в Linux?

Network namespace в Linux - это механизм, который позволяет создавать изолированные сетевые окружения внутри одной операционной системы. У каждого namespace - свои сетевые интерфейсы, IP-адреса, таблицы маршрутизации и правила firewall.

Bridge, в свою очередь, - это сетевое устройство, которое соединяет несколько сетевых интерфейсов и позволяет им общаться друг с другом. Он работает на канальном уровне (Layer 2) модели OSI.

В этой статье разберём три сценария использования namespace:

  • Прямое взаимодействие одного namespace с другим на том же хосте.
  • Общение между одним и несколькими namespace через Linux bridge - так коммуникацию организовать проще.
  • Выходим наружу! Namespace взаимодействует с внешним миром через Linux bridge.

Как организовать прямое взаимодействие между двумя namespace на одном хосте

Чтобы настроить network namespace с bridge в Linux, выполните следующие шаги.

Создаём namespace

ip netns add dev
ip netns add prod

# показать список namespace
ip netns show

Создаём виртуальную пару Ethernet (veth)

Теперь создадим виртуальный Ethernet-кабель для соединения namespace:

ip link add veth-dev type veth peer veth-prod

Подключаем veth к namespace

ip link set veth-dev netns dev
ip link set veth-prod netns prod

Почти готово. Осталось назначить IP-адреса в одной сети (/24) внутри каждого namespace:

ip netns exec dev ip addr add 192.168.10.1/24 dev veth-dev
ip netns exec dev ip link set veth-dev up

ip netns exec prod ip addr add 192.168.10.2/24 dev veth-prod
ip netns exec prod ip link set veth-prod up

Проверяем связь

Пингуем один namespace из другого:

ip netns exec dev ping 192.168.10.2
ip netns exec prod ping 192.168.10.1

Ниже - сводный скрипт, который создаёт два namespace, соединяет их и проверяет связь:

#!/bin/bash
set euxo -pipefail

ip netns add dev
ip netns add prod

# показать namespace
ip netns show

ip link add veth-dev type veth peer name veth-prod
ip link set veth-dev netns dev
ip link set veth-prod netns prod

ip -n dev addr add 192.168.10.1/24 dev veth-dev
ip -n prod addr add 192.168.10.2/24 dev veth-prod

ip -n dev link set veth-dev up
ip -n prod link set veth-prod up

ip netns exec prod ping 192.168.10.1

# Очистка
ip netns delete dev
ip netns delete prod

Пример вывода

Ниже - пример вывода, показывающий, что пакеты успешно передаются между двумя namespace:

root@ip-172-31-86-219:~$ sudo ip netns exec prod ping 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.022 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.032 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=0.033 ms
64 bytes from 192.168.10.1: icmp_seq=4 ttl=64 time=0.034 ms

Создаём network namespace с bridge в Linux

Давайте теперь соберём схему с bridge, чтобы несколько namespace могли общаться через общий L2-сегмент.

Создаём namespace

ip netns add dev
ip netns add prod

Создаём bridge

Используя network namespace и bridge, можно строить изолированные сетевые окружения в Linux и управлять их связностью так, как нужно.

Здесь команда ip link set dev v-net-0 up поднимает устройство bridge.

ip link add v-net-0 type bridge
ip link set dev v-net-0 up

Проверим, что bridge создан.

Проверка через bridge-utils

apt install bridge-utils
brctl show

Пример вывода:

root@ip-172-31-86-219:~# brctl show
bridge name     bridge id               STP enabled     interfaces
v-net-0         8000.4248a9c73e1f       no

Подключаем namespace к bridge

Теперь создадим veth-пары, чтобы подключить dev и prod к bridge v-net-0.

Создаём линк между namespace dev и bridge

ip link add veth-dev type veth peer name veth-dev-br

Создаём линк между namespace prod и bridge

ip link add veth-prod type veth peer name veth-prod-br

Настраиваем стороны линков

Для namespace dev

ip link set veth-dev netns dev
ip link set veth-dev-br master v-net-0

Для namespace prod

ip link set veth-prod netns prod
ip link set veth-prod-br master v-net-0

Назначаем IP-адреса

ip -n dev addr add 192.168.10.1/24 dev veth-dev
ip -n prod addr add 192.168.10.2/24 dev veth-prod

Поднимаем интерфейсы

Со стороны namespace:

ip -n dev link set veth-dev up
ip -n prod link set veth-prod up

Со стороны bridge:

ip link set veth-dev-br up
ip link set veth-prod-br up

Теперь можно проверить связность между namespace:

ip netns exec dev ping 192.168.10.2

Полный скрипт

#!/bin/bash
set euxo -pipefail

ip netns add dev
ip netns add prod

# show namespace
ip netns show

ip link add v-net-0 type bridge
ip link set dev v-net-0 up

ip link add veth-dev type veth peer name veth-dev-br
ip link add veth-prod type veth peer name veth-prod-br

ip link set veth-dev netns dev
ip link set veth-dev-br master v-net-0

ip link set veth-prod netns prod
ip link set veth-prod-br master v-net-0

ip -n dev addr add 192.168.20.1/24 dev veth-dev
ip -n prod addr add 192.168.20.2/24 dev veth-prod

ip -n dev link set veth-dev up
ip -n prod link set veth-prod up

ip link set veth-dev-br up
ip link set veth-prod-br up

# Cleanup
ip netns delete dev
ip netns delete prod
ip link set dev v-net-0 down
brctl delbr v-net-0

Теперь у вас есть network namespace с bridge. Любые сетевые интерфейсы, добавленные в bridge, смогут общаться друг с другом.

Чтобы настроить IP-адреса, маршрутизацию или правила файрвола внутри namespace, используйте ip netns exec для выполнения команд в его контексте.

Выходим наружу! Namespace общается с внешним миром через Linux bridge

До этого момента мы всё время общались внутри одной VM - machine1.

Но рано или поздно namespace понадобится доступ наружу. Например, prod/dev в machine1 хотят достучаться до machine2.

В чём проблема?

В VM у нас есть две сети:

  1. Сеть внутри namespace
  2. Физическая сеть VM (инфраструктурная сеть)
В примере используется простая схема с двумя namespace в подсети 192.168.10.0/24, но на практике это могут быть несколько подсетей и множество namespace.

В таких случаях чаще всего используют NAT.

Почему? Потому что маршрутизаторы вне VM не знают о сети namespace (192.168.10.0/24). И весь трафик с этих адресов просто будет отброшен.

Поэтому сеть namespace (192.168.10.0/24) нужно транслировать в инфраструктурную сеть VM - например, 10.10.10.0/24.

Разберём всё на нашем же примере.

Воссоздаём схему с двумя namespace и bridge

Всё это выполняется в одной VM - machine1:

ip netns add dev
ip netns add prod

# show namespace
ip netns show

ip link add v-net-0 type bridge
ip link set dev v-net-0 up

ip link add veth-dev type veth peer name veth-dev-br
ip link add veth-prod type veth peer name veth-prod-br

ip link set veth-dev netns dev
ip link set veth-dev-br master v-net-0

ip link set veth-prod netns prod
ip link set veth-prod-br master v-net-0

ip -n dev addr add 192.168.10.1/24 dev veth-dev
ip -n prod addr add 192.168.10.2/24 dev veth-prod

ip -n dev link set veth-dev up
ip -n prod link set veth-prod up

ip link set veth-dev-br up
ip link set veth-prod-br up

Пробуем достучаться до machine2

Попробуем пропинговать machine2 (IP 10.10.10.5) из namespace dev:

ip netns exec dev ping 10.10.10.5

Результат:

ping: connect: Network is unreachable

Это ожидаемо - у namespace нет маршрута в сеть 10.10.10.0/24.

Проверим таблицу маршрутизации:

apt install net-tools
ip netns exec dev route -n

Вывод:

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 veth-dev

Видим только локальную сеть namespace.

Добавляем gateway

Чтобы добавить маршрут, сначала нужно назначить gateway для подсети 192.168.10.0/24.

В нашем случае gateway задаём на Linux bridge (v-net-0) - туда приходят оба veth-интерфейса из dev и prod.

ip addr add 192.168.10.10/24 dev v-net-0

Теперь добавим маршрут в namespace dev:

ip -n dev route add 10.10.10.0/24 via 192.168.10.10

Проверяем маршруты

ip netns exec dev route -n

Вывод:

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.10.10.0      192.168.10.10   255.255.255.0   UG    0      0        0 veth-dev
192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 veth-dev

Пробуем снова:

ip netns exec dev ping 10.10.10.5

Теперь поведение другое. Ping всё равно не проходит, но ошибка уже не Network is unreachable.

Это означает, что namespace успешно отправляет пакет через veth-dev к нужной сети, однако проблема возникает уже дальше по цепочке.

На этом этапе пакет выходит из namespace и попадает на Linux bridge.

Bridge теперь выступает в роли gateway между сетью namespace и сетью machine1 (10.10.10.0/24).

То есть echo request уходит с 192.168.10.1 (IP namespace dev) на 10.10.10.5 (IP machine2).

Именно поэтому здесь и нужен NAT (о котором говорили в начале раздела).

Включаем NAT

Для NAT используется netfilter - часть сетевого стека Linux, которая позволяет фильтровать и модифицировать пакеты, а также выполнять трансляцию адресов и портов. Настраивается это через iptables.

В Linux есть 4 типа таблиц:

  1. Filter table - таблица по умолчанию. Если явно не указать таблицу, используется именно она.
  2. NAT table - используется для трансляции адресов и портов.
  3. Mangle table - для специфической модификации пакетов (например, изменение QoS-битов в TCP-заголовке).
  4. Raw table - для настройки исключений в обработке.

Добавляем правило NAT

Настроим правило в таблице nat:

iptables --table nat -A POSTROUTING -s 192.168.10.0/24 -j MASQUERADE
Обязательно убедитесь, что включён IPv4 forwarding:
echo 1 > /proc/sys/net/ipv4/ip_forward

Теперь проверяем:

ip netns exec dev ping 10.10.10.5 -c4

Вывод:

PING 10.10.10.5 (10.10.10.5) 56(84) bytes of data.
64 bytes from 10.10.10.5: icmp_seq=1 ttl=62 time=0.585 ms
64 bytes from 10.10.10.5: icmp_seq=2 ttl=62 time=0.651 ms
64 bytes from 10.10.10.5: icmp_seq=3 ttl=62 time=0.662 ms
64 bytes from 10.10.10.5: icmp_seq=4 ttl=62 time=0.838 ms
^C
--- 100.26.188.252 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4077ms
rtt min/avg/max/mdev = 0.585/0.684/0.838/0.083 ms

Если вы пингуете machine2, убедитесь, что в его security group во входящих правилах открыт ICMP (с machine1 или отовсюду).

Проверим, что NAT действительно работает

Мы добились связи между namespace dev на machine1 и machine2.

Но сможет ли namespace выйти вообще в любую внешнюю сеть?

Попробуем:

ip netns exec dev ping 8.8.8.8 -c4

Результат:

connect: Network is unreachable

Почему? Потому что в namespace dev нет маршрута в эту сеть.

Можно добавлять маршрут под каждую конкретную сеть, но это неудобно. Проще добавить default route.

Добавляем default route

ip -n dev route add default via 192.168.10.10

Проверяем таблицу маршрутизации:

ip netns exec dev route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.10.10   0.0.0.0         UG    0      0        0 veth-dev
10.10.10.0      192.168.10.10   255.255.248.0   UG    0      0        0 veth-dev
192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 veth-dev

Теперь снова пробуем:

ip netns exec dev ping 8.8.8.8 -c4
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=107 time=0.612 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=107 time=0.679 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=107 time=0.752 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=107 time=0.684 ms

Теперь всё работает - благодаря default route и включённому NAT.

Если нужно, чтобы prod namespace тоже мог пинговать machine2 или любые другие внешние адреса, его нужно настроить аналогично dev.


Report Page