Перейти к содержимому
Namespaces

Namespaces

Коротко

Механизмы ядра Linux для изоляции и ограничения процессов. На них построены контейнеры (Docker, LXC, Podman).

МеханизмЧто делает
NamespacesИзоляция ресурсов (сеть, PID, FS, users)
CgroupsЛимиты CPU, памяти, I/O
CapabilitiesГранулярные привилегии вместо полного root
SeccompФильтрация системных вызовов
AppArmor/SELinuxMandatory Access Control - доступ к файлам/сети
pivot_rootСмена корневой файловой системы

Контейнер = namespaces + cgroups + capabilities + seccomp + MAC + pivot_root.

Namespaces

8 типов namespaces (Linux 6.1+):

NamespaceClone FlagЧто изолирует
pidCLONE_NEWPIDProcess IDs - процессы не видят PID из других ns
netCLONE_NEWNETСетевой стек: интерфейсы, IP, routes, iptables, сокеты
mntCLONE_NEWNSMount points - своя файловая система
userCLONE_NEWUSERUID/GID mapping - root внутри != root снаружи
utsCLONE_NEWUTSHostname и domain name
ipcCLONE_NEWIPCSystem V IPC, POSIX message queues, shared memory
cgroupCLONE_NEWCGROUPИзолированный view cgroup hierarchy
timeCLONE_NEWTIMEBoot и monotonic clocks (с kernel 5.6)

Команды

Посмотреть namespaces процесса:

1
ls -la /proc/$$/ns/
lrwxrwxrwx 1 root root 0 Dec 25 12:00 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Dec 25 12:00 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Dec 25 12:00 mnt -> 'mnt:[4026531841]'
lrwxrwxrwx 1 root root 0 Dec 25 12:00 net -> 'net:[4026531840]'
lrwxrwxrwx 1 root root 0 Dec 25 12:00 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Dec 25 12:00 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Dec 25 12:00 uts -> 'uts:[4026531838]'

Список всех namespaces в системе:

1
lsns

Создать процесс в новом namespace:

1
2
3
4
5
6
# новый network + uts namespace
unshare --net --uts /bin/bash

# проверить - пустой сетевой стек
ip link
# только lo

Войти в namespace другого процесса:

1
2
3
4
5
# войти в net namespace процесса 1234
nsenter -t 1234 -n ip addr

# войти во все namespaces контейнера
nsenter -t $(docker inspect -f '{{.State.Pid}}' container_name) -a

Network Namespace - пример

Создание изолированной сети между двумя netns:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# создать два netns
ip netns add ns1
ip netns add ns2

# создать veth pair
ip link add veth1 type veth peer name veth2

# переместить интерфейсы в namespaces
ip link set veth1 netns ns1
ip link set veth2 netns ns2

# настроить IP
ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns1 ip link set lo up

ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth2
ip netns exec ns2 ip link set veth2 up
ip netns exec ns2 ip link set lo up

# проверить связность
ip netns exec ns1 ping 10.0.0.2

Удаление:

1
2
ip netns del ns1
ip netns del ns2

Cgroups

Cgroups (control groups) - лимитирование и учет ресурсов для группы процессов.

cgroups v1 - отдельная иерархия на каждый контроллер (устаревший) cgroups v2 - единая иерархия, делегирование non-root пользователям (современный)

Основные контроллеры:

КонтроллерЧто лимитирует
cpuCPU time, веса, квоты
memoryRAM, swap, OOM killer
ioDisk I/O bandwidth, IOPS
pidsМаксимальное число процессов
cpusetПривязка к конкретным CPU/NUMA

Команды cgroups v2

Проверить что используется v2:

1
2
mount | grep cgroup
# cgroup2 on /sys/fs/cgroup type cgroup2 ...

Посмотреть доступные контроллеры:

1
2
cat /sys/fs/cgroup/cgroup.controllers
# cpuset cpu io memory pids

Создать cgroup и добавить процесс:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# создать группу
mkdir /sys/fs/cgroup/mygroup

# включить контроллеры для дочерних групп
echo "+cpu +memory +io" > /sys/fs/cgroup/cgroup.subtree_control

# лимит памяти 100MB
echo 100M > /sys/fs/cgroup/mygroup/memory.max

# лимит CPU 50%
echo "50000 100000" > /sys/fs/cgroup/mygroup/cpu.max

# добавить процесс
echo $$ > /sys/fs/cgroup/mygroup/cgroup.procs

Посмотреть текущее использование:

1
2
cat /sys/fs/cgroup/mygroup/memory.current
cat /sys/fs/cgroup/mygroup/cpu.stat

systemd и cgroups

systemd использует cgroups для управления сервисами.

Посмотреть дерево cgroups:

1
systemd-cgls

Лимиты для сервиса:

1
2
3
4
5
# /etc/systemd/system/myservice.service.d/limits.conf
[Service]
MemoryMax=512M
CPUQuota=50%
IOWeight=100

Применить:

1
2
systemctl daemon-reload
systemctl restart myservice

Capabilities

Linux capabilities - разбиение привилегий root на отдельные единицы. Вместо “все или ничего” процесс получает только нужные привилегии.

Основные capabilities:

CapabilityЧто разрешает
CAP_NET_ADMINНастройка сети: IP, routes, iptables
CAP_NET_BIND_SERVICEBind на порты < 1024
CAP_NET_RAWRAW/PACKET сокеты
CAP_SYS_ADMIN“Новый root” - mount, namespaces, много всего
CAP_SYS_PTRACEptrace любого процесса
CAP_CHOWNСмена владельца файлов
CAP_DAC_OVERRIDEИгнорировать права доступа к файлам
CAP_SETUID/SETGIDСмена UID/GID

Docker по умолчанию дает контейнеру ~14 capabilities и дропает опасные (SYS_ADMIN, NET_ADMIN).

Посмотреть capabilities процесса:

1
2
3
4
5
6
7
8
# текущий процесс
capsh --print

# конкретный процесс
getpcaps $$

# файла
getcap /usr/bin/ping

Управление в Docker:

1
2
3
4
5
# убрать все и добавить только нужные
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

# добавить capability
docker run --cap-add=NET_ADMIN alpine ip link

Seccomp

Seccomp (Secure Computing) - фильтрация системных вызовов на уровне ядра. Процесс может вызывать только разрешенные syscalls.

Режимы:

  • strict - только read/write/exit/sigreturn (почти бесполезен)
  • filter (BPF) - гибкие правила через BPF программу

Docker по умолчанию блокирует ~44 syscalls: mount, reboot, setns, swapon и др.

Посмотреть seccomp статус процесса:

1
2
3
grep Seccomp /proc/$$/status
# Seccomp:        0  - отключен
# Seccomp:        2  - filter mode

Docker с кастомным профилем:

1
2
3
4
5
# использовать свой профиль
docker run --security-opt seccomp=/path/to/profile.json alpine

# отключить seccomp (небезопасно)
docker run --security-opt seccomp=unconfined alpine

Пример профиля (разрешить все кроме mount):

1
2
3
4
5
6
7
8
9
{
  "defaultAction": "SCMP_ACT_ALLOW",
  "syscalls": [
    {
      "names": ["mount", "umount2"],
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}

AppArmor

AppArmor - Mandatory Access Control (MAC) для Debian/Ubuntu. Профили ограничивают доступ процесса к файлам, сети, capabilities.

Режимы профилей:

  • enforce - блокирует нарушения
  • complain - только логирует

Статус:

1
aa-status

Docker автоматически применяет профиль docker-default к контейнерам.

1
2
3
4
5
# свой профиль
docker run --security-opt apparmor=my-profile alpine

# отключить (небезопасно)
docker run --security-opt apparmor=unconfined alpine

Пример профиля /etc/apparmor.d/my-container:

#include <tunables/global>

profile my-container flags=(attach_disconnected) {
  #include <abstractions/base>

  # разрешить чтение
  /etc/passwd r,
  /app/** r,

  # запретить запись в /etc
  deny /etc/** w,

  # разрешить сеть
  network inet tcp,
}

Загрузить профиль:

1
apparmor_parser -r /etc/apparmor.d/my-container

SELinux

SELinux - MAC для RHEL/CentOS/Fedora. Использует метки (labels) на процессах и ресурсах.

Режимы:

  • enforcing - блокирует нарушения
  • permissive - только логирует
  • disabled - отключен
1
2
3
4
5
6
# текущий режим
getenforce

# временно переключить
setenforce 0  # permissive
setenforce 1  # enforcing

Контекст безопасности (метка):

1
2
3
4
5
6
# файла
ls -Z /etc/passwd
# system_u:object_r:passwd_file_t:s0

# процесса
ps -Z

Docker и SELinux:

1
2
3
4
5
6
7
8
# включить разделение контейнеров
docker run --security-opt label=type:container_t alpine

# отключить SELinux для контейнера
docker run --security-opt label=disable alpine

# разрешить доступ к volume
docker run -v /data:/data:Z alpine  # :Z перемаркирует файлы

pivot_root

pivot_root - смена корневой файловой системы. Контейнеры используют вместо chroot (более безопасен).

Отличие от chroot:

  • chroot меняет только / для процесса, старый root доступен
  • pivot_root меняет root для всего mount namespace, старый root можно отмонтировать
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# подготовка нового root
mkdir /newroot
mount --bind /path/to/rootfs /newroot

# pivot
cd /newroot
mkdir old_root
pivot_root . old_root

# отмонтировать старый root
umount -l /old_root
rmdir /old_root

В контейнерах это делает runtime (runc, crun) автоматически.

Проверка

Посмотреть все механизмы изоляции процесса:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# namespaces
ls -la /proc/$$/ns/

# cgroup
cat /proc/$$/cgroup

# capabilities
getpcaps $$

# seccomp
grep Seccomp /proc/$$/status

# SELinux контекст (если включен)
ps -Z -p $$

# AppArmor профиль (если включен)
cat /proc/$$/attr/current

Для контейнера Docker:

1
2
3
4
5
6
7
8
# PID контейнера на хосте
PID=$(docker inspect -f '{{.State.Pid}}' container_name)

# проверить все
ls -la /proc/$PID/ns/
cat /proc/$PID/cgroup
getpcaps $PID
grep Seccomp /proc/$PID/status

Ссылки

See also