• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья "Купание" в пространствах имен Linux — часть 1

вавилонец

CPU register
Пользователь
Регистрация
17.06.2021
Сообщения
1 116
Реакции
1 265
День добрый всем любителям готового перевода! Сегодня мы окунемся в пространство имен Linux. Все кто не взял спасательные круги прошу купаться на свой страх и риск. Бульк!
Изоляция процессов — ключевой компонент контейнеров. Одним из ключевых базовых механизмов являются пространства имён . Мы изучим, что они из себя представляют и как они работают, чтобы создать собственный изолированный контейнер и лучше понять каждую его часть.

Что такое пространства имен?


Пространства имен — это функция ядра Linux, выпущенная в версии ядра 2.6.24. в 2008 году. Они предоставляют процессам собственное системное представление , таким образом происходит изоляция процессов друг от друга. Другими словами, Пространства имен определяют набор ресурсов, которые может использовать процесс ( Вы не может взаимодействовать с чем-то, что вы не можете видеть ). На высоком уровне, они позволяют выполнять разделение глобальных ресурсов операционной системы: такие как точки монтирования, сетевой стек и межпроцессное взаимодействие. Мощная сторона пространств имен заключается в том, что они ограничивают доступ к системные ресурсы, при этом запущенный процесс не знает об ограничения. В типичном стиле Linux они представлены в виде файлов. в /proc/<pid>/ns .

Код:
cryptonite@cryptonite:~ $ echo $$
4622
cryptonite@cryptonite:~ $ ls /proc/$$/ns -al
total 0
dr-x--x--x 2 cryptonite cryptonite 0 Jun 29 15:00 .
dr-xr-xr-x 9 cryptonite cryptonite 0 Jun 29 13:13 ..
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 net -> 'net:[4026532008]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 time -> 'time:[4026531834]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 user -> 'user:[4026531837]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:00 uts -> 'uts:[4026531838]'

Когда мы создаем новый процесс, все пространства имен наследуются как от родительского процесса.

Код:
# inception
cryptonite@cryptonite:~ $ /bin/zsh
# father PID verification
╭─cryptonite@cryptonite ~
╰─$ ps -efj  | grep $$
crypton+   13560    4622   13560    4622  1 15:07 pts/1    00:00:02 /bin/zsh
╭─cryptonite@cryptonite ~
╰─$ ls /proc/$$/ns -al
total 0
dr-x--x--x 2 cryptonite cryptonite 0 Jun 29 15:10 .
dr-xr-xr-x 9 cryptonite cryptonite 0 Jun 29 15:07 ..
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 net -> 'net:[4026532008]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 time -> 'time:[4026531834]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 user -> 'user:[4026531837]'
lrwxrwxrwx 1 cryptonite cryptonite 0 Jun 29 15:10 uts -> 'uts:[4026531838]'

Пространства имен создаются с помощью clone с одним из следующих аргументов:
  • CLONE_NEWNS — создать новое "примонтированное" пространство имен;
  • CLONE_NEWUTS — создать новое пространство имен UTS;
  • CLONE_NEWIPC — создать новое пространство имен IPC;
  • CLONE_NEWPID — создать новое пространство имен PID;
  • CLONE_NEWNET — создать новое пространство имен NET;
  • CLONE_NEWUSER — создать новое пространство имен USR;
  • CLONE_NEWCGROUP — создать новое пространство имен контрольной группы.
Пространства имен также можно создавать с помощью unshare. Разница между CLONE и UNSHARE в том, что clone порождает новый процесс внутри нового набора пространств имен, а unshare перемещает текущий процесс внутри нового набора пространств имен (отменяет совместное использование).

Зачем использовать пространства имен?​

Если представить пространства имен в виде ящиков для процессов , содержащих некоторые абстрагированные глобальные системные ресурсы, очень удобно с этими ящиками то, что вы можете добавлять и удалять вещи одной коробки, и это не повлияет содержимое других. Или, если процесс A в ящике (множество пространства имен) "утонул" и решает "утопить" всю файловую систему или сетевой стек в этой "луже", это не повлияет на работоспособность ресурсов, предоставленные другому процессу B, помещеному в другой ящик. Более того, пространства имен могут обеспечить отличную изоляцию, позволяя процесс A и B для совместного использования некоторых системных ресурсов (например, совместное использование примонтированных ресурсов или сетевой стек). Пространства имен часто используются, когда определенному процессу не доверяют. Код должен выполняться на данной машине без ущерба для хоста операционной системы. Платформы для соревнований по программированию, такие как Hackerrank , Codeforces , Rootme используют окружения с пространством имен для безопасного выполнения и проверки кода участников, не подвергая риску их серверы. PaaS (платформа как услуга) таких поставщиков, как Google Cloud Engine использует пространство имен среды для запуска нескольких пользовательских служб (например, веб-серверов, баз данных) на том же оборудовании без возможности вмешательства этих Сервисов. Таким образом, пространства имен также можно рассматривать как полезные для эффективного совместного использование ресурсов . Другие облачные технологии, такие как Docker или LXC, также используют пространства имен как средство изоляции процессов. Эти технологии ставят процессы операционной системы в изолированных средах, называют контейнеры . Например, запуск процессов в контейнерах Docker например, запускаютих на виртуальных машинах. Разница между контейнерами и виртуальными машины заключается в том, что контейнеры совместно используют ядро ОС хоста и используют его напрямую, что делает их значительно легче, чем виртуальные машины, поскольку нет аппаратной эмуляции. Это увеличение общей производительности происходит в основном благодаря использованию пространств имен, которые напрямую интегрированы в ядро Линукс. Однако есть некоторые реализации очень легких ВМ.

Типы пространств имен​

В текущей стабильной версии ядра Linux 5.7 есть семь различных пространства имен:
  • Пространство имен PID : изоляция дерева системных процессов;
  • Пространство имен NET : изоляция сетевого стека хоста;
  • Пространство имен MNT : изоляция точек монтирования файловой системы хоста;
  • Пространство имен UTS : изоляция имени хоста;
  • Пространство имен IPC : изоляция для утилит межпроцессного взаимодействия (общие сегменты, семафоры);
  • Пространство имен USER : изоляция идентификаторов пользователей системы;
  • Пространство имен CGROUP : изоляция виртуальной файловой системы cgroup хоста.
Пространства имен являются атрибутами для каждого процесса. Каждый процесс может воспринимать не более одного пространства имен . Иными словами, в любой момент любой процесс P принадлежит ровно одному экземпляру каждого пространства имен. Например когда данный процесс хочет обновить таблицу маршрутов в системе, Ядро показывает ему копию таблицы маршрутов пространства имен, к которому оно принадлежит в этот момент. Если процесс запрашивает свой идентификатор в системе, ядро ответит идентификатором процесса в его текущем пространстве имен(в случае вложенного пространства имен). Мы подробно рассмотрим каждое пространство имен, чтобы понять, какие мехинзмы и процессы в операционной системе обеспечивают помогают им. Понимание этого поможет нам найти то, что под капотом современных контейнерных технологий.

Пространство имен PID

Исторически ядро Linux поддерживало единое дерево процессов. Древовидная структура данных содержит ссылку на каждый текущий процесс работает в иерархии родитель-потомок. Он также перечисляет все запущенные процессы в ОС. Эта структура сохраняется в так называемом procfs , которая является свойством работающей системы (т.е. присутствует только во время работы ОС). Эта структура позволяет процессам с достаточными привилегиями для подключения к другим процессам, проверки, общайтесь и/или завершать их. Он также содержит информацию о корневом каталоге процесса, его текущий рабочий каталог, открытые файловые дескрипторы, адреса виртуальной памяти, доступные точки монтирования и т.д.

Код:
# an example of the procfs structure
cryptonite@cryptonite:~ $ls /proc/1/
   arch_status     coredump_filter      gid_map     mounts          pagemap         setgroups   task
   attr            cpu_resctrl_groups   io          mountstats      patch_state     smaps       timens_offsets
   cgroup          environ              map_files   numa_maps       root            stat        uid_map
   clear_refs      exe                  maps        oom_adj         sched           statm
...
# an example of the process tree structure
cryptonite@cryptonite:~ $pstree | head -n 20
systemd-+-ModemManager---2*[{ModemManager}]
        |-NetworkManager---2*[{NetworkManager}]
        |-accounts-daemon---2*[{accounts-daemon}]
        |-acpid
        |-avahi-daemon---avahi-daemon
        |-bluetoothd
        |-boltd---2*[{boltd}]
        |-colord---2*[{colord}]
        |-containerd---17*[{containerd}]


При загрузке системы первый процесс запускающийся в большинстве современных ОС Linux - это systemd (системный демон), который находится на корневом узле. Его родителем является PID=0, который является невидимым процессом в ОС . Этот процесс отвечает за запуск других сервисов/демонов, которые представлены его дочерними элементами и необходимы для нормального функционирования ОС. Эти процессы будут иметь PID> 1 и уникальный PID в древовидной структуре .
С введением пространства имен Process (или пространства имен PID ) стало возможным делать вложенные деревья процессов. Это позволяет обрабатывать другие чем systemd (PID=1), чтобы воспринимать себя как корневой процесс перемещаясь по вершине поддерева, таким образом получая PID=1 в этом поддереве. Все процессы в одном и том же поддереве также получат идентификаторы, относящиеся к пространство имен процессов. Это также означает, что некоторые процессы могут в конечном итоге иметь несколько идентификаторов в зависимости от количества пространств имен процессов, в которых они находятся. Тем не менее, в каждом пространстве имен не более одного процесса может иметь данный PID (уникальное значение узла в дереве процессов становится свойством для каждого пространства имён). Это связано с тем, что отношения между процессами в пространстве имен корневого процесса остаются нетронутыми. Или сказать другими словами, процесс в новом пространстве имен PID по-прежнему привязан к своему родителю, поэтому будучи частью своего родительского пространства имен PID. Эти отношения между всеми процессами можно увидеть в пространстве имён корневого процесса, но во вложенном пространстве имен процессов они не видны. Это означает, что процесс в вложенном пространстве имен процессов не может взаимодействовать со своим родителем или любым другим процессом в пространстве имен родительского процесса. Это связано с тем, что, будучи поверх нового пространства имен PID процесс воспринимает свой PID как 1, а перед процессом с PID=1 нет другого процесса.

1653377159110.png

В ядре Linux PID представлен в виде структуры. Внутри мы можем также найдите пространства имен, частью которых является процесс, в виде массива идентификаторов upid структуры.

Код:
struct upid {
    int nr;  /* the pid value */
    struct pid_namespace *ns;       /* the namespace this value
                                    * is visible in */
    struct hlist_node pid_chain; /* hash chain for faster search of PIDS in the given namespace*/
};

struct pid {
    atomic_t count; /* reference counter */
    struct hlist_head tasks[PIDTYPE_MAX]; /* lists of tasks */
    struct rcu_head rcu;
    int level;              // number of upids
    struct upid numbers[0];  // array of pid namespaces
};

Чтобы создать новый процесс внутри пространства имен PID, необходимо вызвать метод clone() со специальным флагом CLONE_NEWPID. В то время как другие пространства имен, обсуждаемые ниже, также могут быть созданы с помощью unshare() , пространство имен PID может быть создано только во время создания нового процесса с помощью clone() или fork() .

Давайте рассмотрим это:
Код:
# Let's start a process in a new pid namespace;
cryptonite@cryptonite:~ $sudo unshare --pid  /bin/bash
bash: fork: Cannot allocate memory     [1]
root@cryptonite:/home/cryptonite# ls
bash: fork: Cannot allocate memory     [1]

Что случилось? Кажется, что оболочка застряла между двумя пространствами имён. Это связано с тем, что unshare не входит в новое пространство имен после выполнения execve(). Это желаемое поведение ядра Linux. Текущий процесс «unshare» вызывает unshare , создающий новое пространство имен pid, но текущий Процесс «unshare» не находится в новом пространстве имен pid. Процесс B создает новое пространство имен, но сам процесс B не будет помещен в новое пространство имен, в новое пространство будут помещены только подпроцессы процесса B. После создания пространства имен unshare программа выполнит /bin/bash . Затем /bin/bash разветвит несколько новых подпроцессов для выполнения некоторых заданий. Эти подпроцессы будут иметь PID относительно нового пространства имен, и когда эти процессы завершатся, они оставят пространство имен без PID=1. Ядро Linux, например, не может иметь пространства имен PID без процесса с PID=1 внутри. Так как пространство имен остается пустым, ядро отключит некоторые механизмы которые связаны с выделением PID внутри этого пространства имен, таким образом приводит к ошибке. Эта ошибка хорошо задокументирована, если вы поищите в Интернете. Вместо этого мы должны поручить программе unshare выделить новый процесс после создания пространства имен. Тогда этот новый процесс будет иметь PID=1 и выполнит нашу программу. Таким образом, когда подпроцессы /bin/bash выходят из пространства имен по-прежнему будут иметь процесс с PID=1.

Код:
cryptonite@cryptonite:~ $sudo unshare --pid --fork  /bin/bash
root@cryptonite:/home/cryptonite# echo $$
1
root@cryptonite:/home/cryptonite# ps
    PID TTY          TIME CMD
   7239 pts/0    00:00:00 sudo
   7240 pts/0    00:00:00 unshare
   7241 pts/0    00:00:00 bash
   7250 pts/0    00:00:00 ps

Но почему наша оболочка не имеет PID 1, когда мы используем ps? И почему мы все еще видим процесс из корневого пространства имен? Программа ps использует систему procfs для получения информации о текущем процессе в системе. Эта файловая система смонтирована в каталог /proc. Однако в новом пространстве имен эта точка монтирования описывает процессы из корневого пространства имен PID. Есть два способа избежать этого:

Код:
# creating a new mount namespace and mounting a new procfs inside
cryptonite@cryptonite:~ $sudo unshare --pid --fork --mount /bin/bash
root@cryptonite:/home/cryptonite# mount -t proc proc /proc
root@cryptonite:/home/cryptonite# ps
    PID TTY          TIME CMD
      1 pts/2    00:00:00 bash
      9 pts/2    00:00:00 ps

# Or use the unshare wrapper with the --mount-proc flag
# which does the same
cryptonite@cryptonite:~ $sudo unshare --fork --pid --mount-proc  /bin/bash
root@cryptonite:/home/cryptonite# ps
    PID TTY          TIME CMD
      1 pts/1    00:00:00 bash
      8 pts/1    00:00:00 ps

Как мы упоминали ранее, процесс может иметь несколько идентификаторов в зависимости от количество пространств имен, в которых находится процесс. Давайте теперь проверим различные PID оболочки, вложенной в два пространства имен.

Код:
╭cryptonite@cryptonite:~ $sudo unshare --fork --pid --mount-proc  /bin/bash
# this process has PID 4700 in the root PID namespace
root@cryptonite:/home/cryptonite# unshare --fork --pid --mount-proc /bin/bash
root@cryptonite:/home/cryptonite# ps
    PID TTY          TIME CMD
      1 pts/1    00:00:00 bash
      8 pts/1    00:00:00 ps

# Let's inspect the different PIDs
cryptonite@cryptonite:~ $sudo nsenter --target 4700 --pid --mount
cryptonite# ps -aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0  18476  4000 pts/0    S    21:11   0:00 /bin/bash
root           9  0.2  0.0  21152  5644 pts/1    S    21:15   0:00 -zsh # me
root          14  0.0  0.0  20972  4636 pts/0    S    21:15   0:00 sudo unshare
root          15  0.0  0.0  16720   520 pts/0    S    21:15   0:00 unshare -fp -
root          11  0.0  0.0  18476  3836 pts/0    S+   21:15   0:00 /bin/bash # nested shell
root          24  0.0  0.0  20324  3520 pts/1    R+   21:15   0:00 ps -aux
# the PID viewed from within the first PID namespace is 11

# Let's see its PID in the root PID namespace
cryptonite@cryptonite:~ $ps aux | grep /bin/bash
....
root       13512  0.0  0.0  18476  4036 pts/1    S+   14:44   0:00 /bin/bash
# believe me it's that process ;)

# All this info can be found in the procfs
cryptonite@cryptonite:~ $cat /proc/13152/status | grep -i NSpid
NSpid:  13512   11  1
# PID in the root namespace = 13512
# PID in the first nested namespace = 11
# pid in the second nested namespace = 1

Хорошо, после того, как мы увидели виртуализацию с точки зрения идентификаторов, давайте посмотрим если есть реальная изоляция в плане взаимодействия с другими процессами в ОС.

Код:
# process is run with effective UID=0 (root) and it can normally kill any other process in the OS
root@cryptonite:/home/cryptonite# kill 3
# nothing happens, because there is no process 3 in the current namespace

Мы видим, что процесс не может взаимодействовать с процессом вне его текущего пространства имен (Вы не можете коснуться того, чего не видите, Помните?).


Подводя итог о пространстве имен PID :
  • Процессы внутри пространства имен видят (взаимодействуют) только с процессами в том же пространстве имен PID (изоляция);
  • Каждое пространство имен PID имеет собственную нумерацию, начинающуюся с 1;
  • Эта нумерация уникальна для каждого пространства имен PID. Если PID 1 исчезает, то удаляется всё пространство имен;
  • Пространства имен могут быть вложенными;
  • В конечном итоге процесс имеет несколько PID (когда пространства имен вложены друг в друга);
  • Все ps-подобные команды используют procfs монтирование файловой системы

Пространство имен NET

Сетевое пространство имен ограничивает представление процесса хост-сети. Это позволяет процессу иметь свое собственное отделение от сетевого стека хоста (набор сетевых интерфейсов, правила маршрутизации, набор перехватчиков netfilter). Давайте рассмотрим как это работает:

Код:
# root net namespace
cryptonite@cryptonite:~ $ip link  # network interfaces
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s31f6: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000
    link/ether 8c:16:45:54:8b:65 brd ff:ff:ff:ff:ff:ff
.....
cryptonite@cryptonite:~ $ip route # routing rules
default via 192.168.2.1 dev wlp3s0 proto dhcp metric 600
10.0.0.0/16 via 10.0.1.230 dev tun0 proto static metric 50
....
cryptonite@cryptonite:~ $sudo iptables --list-rules # firewall rules
-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
-N DOCKER
.....

Давайте теперь создадим новое сетевое пространство имен и проверим сеть.

Код:
cryptonite@cryptonite:~ $sudo unshare --net /bin/bash
root@cryptonite:/home/cryptonite# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
root@cryptonite:/home/cryptonite# ip route
Error: ipv4: FIB table does not exist.
Dump terminated
root@cryptonite:/home/cryptonite# iptables --list-rules
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

Мы видим, что весь сетевой стек процесса изменился. Есть только loop-интерфейс, который тоже не работает. Другими словами, этот процесс недоступен по сети. Но это проблема, не так ли? Зачем нам практически изолированный сетевой стек, если мы не можем общаться через него?

1653378877757.png


Как обычно, мы хотим иметь возможность каким-то образом общаться с данным процессом, мы должны обеспечить способ подключения различных сетевых пространств имен.

Соединение пары пространств имен​

Чтобы сделать процесс в новом сетевом пространстве имен доступным из другого сетевого пространства имен, необходима пара виртуальных интерфейсов. Эти два виртуальных интерфейса идут с виртуальным кабелем (как труба Linux). Итак, если мы хотим соединить пространства имен (скажем, N1) и еще одно (скажем, N2), которое мы должны поместить один из виртуальных интерфейсов в сетевом стеке N1 и другой в сетевом стеке N2.

1653379100652.png


Давайте построим функциональную сеть между различными сетями пространства имен! Важно отметить, что существует два типа сети пространства имен - именованные и анонимные. Сначала мы создадиим сеть namespace, а затем создадим пару виртуальных интерфейсов:

Код:
# create network namespace
cryptonite@cryptonite:~ $sudo ip netns add netnstest
# check if creation was successful
cryptonite@cryptonite:~ $ls /var/run/netns
netnstest
# check if we have the same configurations as before
cryptonite@cryptonite:~ $sudo nsenter --net=/var/run/netns/netnstest /bin/bash
root@cryptonite:/home/cryptonite# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# create the virtual network interface pair on
# the top of the network stack of the root namespace
cryptonite@cryptonite:~ $sudo ip link add veth0 type veth peer name ceth0
# check if the pair veth0-ceth0 was successfully created
cryptonite@cryptonite:~ $ip link | tail -n 4
8: ceth0@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether f6:1a:ee:9c:26:0c brd ff:ff:ff:ff:ff:ff
9: veth0@ceth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ae:4d:95:44:ab:39 brd ff:ff:ff:ff:ff:ff
# put one of the interfaces in the previously created network namespace
# and keep the other end in the root network namespace
cryptonite@cryptonite:~ $sudo ip link set ceth0 netns netnstest
cryptonite@cryptonite:~ $ip link
...
9: veth0@if8: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ae:4d:95:44:ab:39 brd ff:ff:ff:ff:ff:ff link-netns netnstest
# one of the interfaces vanished
# turn on the interface and assign it an IP
cryptonite@cryptonite:~ $sudo ip link set veth0 up
cryptonite@cryptonite:~ $sudo ip addr add 172.12.0.11/24 dev veth0

cryptonite@cryptonite:~ $sudo nsenter --net=/var/run/netns/netnstest /bin/bash
root@cryptonite:/home/cryptonite# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
8: ceth0@if9: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether f6:1a:ee:9c:26:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0
root@cryptonite:/home/cryptonite# ip link set lo up
root@cryptonite:/home/cryptonite# ip link set ceth0 up
root@cryptonite:/home/cryptonite# ip addr add 172.12.0.12/24 dev ceth0
root@cryptonite:/home/cryptonite# ip addr | grep ceth
8: ceth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    inet 172.12.0.12/24 scope global ceth0

Подключим виртуальные интерфейсы:

Код:
# inside the root namespace
cryptonite@cryptonite:~ $ping 172.12.0.12
PING 172.12.0.12 (172.12.0.12) 56(84) bytes of data.
64 bytes from 172.12.0.12: icmp_seq=1 ttl=64 time=0.125 ms
64 bytes from 172.12.0.12: icmp_seq=2 ttl=64 time=0.111 ms
...
# inside of the new net namespace
root@cryptonite:/home/cryptonite# tcpdump
17:18:17.534459 IP 172.12.0.11 > 172.12.0.12: ICMP echo request, id 2, seq 1, length 64
17:18:17.534479 IP 172.12.0.12 > 172.12.0.11: ICMP echo reply, id 2, seq 1, length 64
17:18:18.540407 IP 172.12.0.11 > 172.12.0.12: ICMP echo request, id 2, seq 2, length 64
....

# try the other way around
root@cryptonite:/home/cryptonite# ping 172.12.0.11
PING 172.12.0.11 (172.12.0.11) 56(84) bytes of data.
64 bytes from 172.12.0.11: icmp_seq=1 ttl=64 time=0.108 ms
...
# back to the root namespace
cryptonite@cryptonite:~ $sudo tcpdump -i veth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:22:27.999342 IP 172-12-0-12.lightspeed.sgnwmi.sbcglobal.net > 172-12-0-11.lightspeed.sgnwmi.sbcglobal.net: ICMP echo request, id 18572, seq 1, length 64
17:22:27.999417 IP 172-12-0-11.lightspeed.sgnwmi.sbcglobal.net > 172-12-0-12.lightspeed.sgnwmi.sbcglobal.net: ICMP echo reply, id 18572, seq 1, length 64
17:22:29.004480 IP 172-12-0-12.lightspeed.sgnwmi.sbcglobal.net > 172-12-0-11.lightspeed.sgnwmi.sbcglobal.net: ICMP echo request, id 18572, seq 2, length 64

Из фрагмента выше мы видим, как создать новую сеть пространства имен и соедините его с корневым пространством имен с помощью конвейера связи. Родительское пространство имен сохранило один из интерфейсов, а передал другой в дочернее пространство имен. Все, что входит в один из концов, выходит через другой конец, как настоящая сеть.



1653379990859.png



Мы увидели, как изолировать, виртуализировать и соединять сетевые стеки Linux. Обычно имея возможности виртуализации, мы хотели бы пойти дальше. и создайте виртуальную локальную сеть между процессами!

Подключение нескольких пространств имен (создание локальной сети)​

Для создания виртуальной локальной сети потребуется другая утилита виртуализации Linux - мост. Мост Linux ведет себя как реальный (уровень 2 (Ethernet) ) сетевой коммутатор — он пересылает пакеты между интерфейсами, подключеными к нему с помощью таблицы сопоставления MAC-адресов. Давайте создадим нашу виртуальнаю локальную сеть.

Код:
# all previous configurations were deleted
# creating a pair of namespaces
cryptonite@cryptonite:~ $sudo ip netns add netns_0
cryptonite@cryptonite:~ $sudo ip netns add netns_1
cryptonite@cryptonite:~ $tree /var/run/netns/
/var/run/netns/
├── netns_0
└── netns_1
...
cryptonite@cryptonite:~ $sudo ip link add veth0 type veth peer name ceth0
cryptonite@cryptonite:~ $sudo ip link add veth1 type veth peer name ceth1
cryptonite@cryptonite:~ $sudo ip link set veth1 up
cryptonite@cryptonite:~ $sudo ip link set veth0 up
cryptonite@cryptonite:~ $sudo ip link set ceth0 netns netns_0
cryptonite@cryptonite:~ $sudo ip link set ceth1 netns netns_1
# setup the first connected interface -> net_namespace=netns_0
cryptonite@cryptonite:~ $sudo ip netns exec netns_0 ip link set lo up
cryptonite@cryptonite:~ $sudo ip netns exec netns_0 ip link set ceth0 up
cryptonite@cryptonite:~ $sudo ip netns exec netns_0 ip addr add 192.168.1.20/24 dev ceth0

# setup the second connected interface -> netns_1
cryptonite@cryptonite:~ $sudo ip netns exec netns_1 ip link set lo up
cryptonite@cryptonite:~ $sudo ip netns exec netns_1 ip link set ceth1 up
cryptonite@cryptonite:~ $sudo ip netns exec netns_1 ip addr add 192.168.1.21/24 dev ceth1

# create the bridge
cryptonite@cryptonite:~ $sudo ip link add name br0 type bridge
# set an ip on the bridge and turn it up
# so that processes can reach the LAN through it
cryptonite@cryptonite:~ $ip addr add 192.168.1.11/24 brd + dev br0
cryptonite@cryptonite:~ $sudo ip link set br0 up
# connect the ends of the network namespaces in the
# root namespace to the bridge
cryptonite@cryptonite:~ $sudo ip link set veth0 master br0
cryptonite@cryptonite:~ $sudo ip link set veth1 master br0
# check if the bridge is the master of the two veths
cryptonite@cryptonite:~ $bridge link show br0
10: veth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2
12: veth1@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2

# allow forwarding by the bridge in the root net namespace
# in order to enable the interface to forward between the namespaces
# depending on the different iptables policy this step may be skipped
cryptonite@cryptonite:~ $iptables -A FORWARD -i br0 -j ACCEPT

# check the network connection netns_test1 -> netns_test0
cryptonite@cryptonite:~ $sudo ip netns exec netns_test1 ping  192.168.1.20
PING 192.168.1.20 (192.168.1.20) 56(84) bytes of data.
64 bytes from 192.168.1.20: icmp_seq=1 ttl=64 time=0.046 ms
...

# connectivity check root_namespace -> netns_0
cryptonite@cryptonite:~ $ip route
...
192.168.1.0/24 dev br0 proto kernel scope link src 192.168.1.11
...
cryptonite@cryptonite:~ $ping 192.168.1.20
PING 192.168.1.20 (192.168.1.20) 56(84) bytes of data.
64 bytes from 192.168.1.20: icmp_seq=1 ttl=64 time=0.150 ms
...

# check the network connection netns_test0 -> netns_test1
cryptonite@cryptonite:~ $sudo ip netns exec netns_test0 ping 192.168.1.21
PING 192.168.1.21 (192.168.1.21) 56(84) bytes of data.
64 bytes from 192.168.1.21: icmp_seq=1 ttl=64 time=0.040 ms

Оно работает! Очень важно то, что виртуальный интерфейс должен иметь разрешения на пересылку пакетов в текущем сетевом стеке. Чтобы не напутать с правилами iptables, можно повторить эту процедуру в отдельном сетевом пространстве имен, где таблица правил будет пустой по умолчанию. Давайте теперь подключим локальную сеть к Интернету!

Достижение внешнего мира​

Мы присвоили IP нашему мосту и можем пинговать его из сети пространства имен.

Код:
# try to reach the internet
cryptonite@cryptonite:~ $sudo ip netns exec netns_1 ping 8.8.8.8
ping: connect: Network is unreachable
cryptonite@cryptonite:~ $sudo ip netns exec netns_1 ip route
192.168.1.0/24 dev veth1 proto kernel scope link src 192.168.1.21

# no route for the host interface -> bridge is on Level 2 =>
# no ARP resolution and inter-networking
# We  can make the bridge the default gateway for both namespaces
# and let it forward all traffic to the upper network namespace
cryptonite@cryptonite:~ $sudo ip -all netns exec ip route add default via 192.168.1.11

# Did everything go smoothly?
cryptonite@cryptonite:~ $sudo ip -all netns exec ip route
netns: netns_1
default via 192.168.1.11 dev ceth1
192.168.1.0/24 dev ceth1 proto kernel scope link src 192.168.1.21

netns: netns_0
default via 192.168.1.11 dev ceth0
192.168.1.0/24 dev ceth0 proto kernel scope link src 192.168.1.20

# let's try again
cryptonite@cryptonite:~ $ip netns exec netns_0 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, ...

# One last thing -> the outside world doesn't know about our LAN nor do the host so we have to add one last rule
cryptonite@cryptonite:~ $iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE

cryptonite@cryptonite:~ $sudo ip netns exec netns_0 ping 8.8.8.8
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=61 time=11.5 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=61 time=12.5 ms
...

Обратите внимание, что iptables хоста должны быть хорошо настроены, иначе связи с интернетом не будет. Кроме того, без маски, пакеты будут покидать хост с его внутренним IP-адреса который известен только этого хоста, шлюз в локальной сети хоста не знает , как присоединиться к локальной сети моста.

Подводя итог о сетевом пространстве имен :
  • Процессы внутри заданного сетевого пространства имен получают свой собственный частный сетевой стек, включая сетевые интерфейсы, таблицы маршрутизации, правила iptables, сокеты (ss, netstat);
  • Соединение между сетевыми пространствами имен может быть выполнено с использованием двух виртуальных интерфейсов;
  • Связь между изолированными сетевыми стеками в одном и том же пространстве имен осуществляется с помощью моста;
  • Пространство имен NET можно использовать для имитации «коробки» процессов Linux, из которой лишь немногие из них смогут достичь внешнего мира (путем удаления шлюза хоста по умолчанию из правил маршрутизации некоторых пространств имен NET).

Выныривай, друг! Скоро 2 Часть.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх