Kernel
External link:
chroot
- Впервые в Minix и UNIX Version 7 (released 1979)
- В Linux этот syscall - функция ядра kernel API function.
> mkdir -p new-root/{bin,lib64}
> cp /bin/bash new-root/bin
> cp /lib64/{ld-linux-x86-64.so*,libc.so*,libdl.so.2,libreadline.so*,libtinfo.so*} new-root/lib64
> sudo chroot new-root
chroot - утилита, которая предназначена для изоляции файловой среды приложения. Создана в Minix 1.7. Для процессов и ОЗУ не подходит, но вдохновила создание Namespaces в Linux позднее.
Пример работы
Для работы bash в новой среде chroot необходимо внести его копию в папку jail:
mkdir $HOME/jail
mkdip -p $HOME/jail/bin
cp -v /bin/bash $HOME/jail/bin
cp -v /bin/ls $HOME/jail/bin
Далее нужно увидеть зависимости и перенести их:
ldd /bin/bash
ldd /bin/ls
Либо, гораздо проще перенести разом все библиотеки:
cp -a /usr jail/
cp -a /lib jail/
cp -a /lib64 jail/
Далее, заход в окружение:
sudo chroot $HOME/jail /bin/bash
bash-5.0# ls
bin lib lib64 usr
#### Побег из chroot
```c
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
mkdir(".out", 0755); // нужны права root в контейнере
chroot(".out");
chdir("../../../../../"); // относительный путь за пределы корня
chroot(".");
return execl("/bin/bash", "-i", NULL);
}
- Only privileged processes with the capability CAP_SYS_CHROOT are able to call chroot.
- Modern systems use pivot_mount (calling process must have the CAP_SYS_ADMIN capability). - has the benefit of putting the old mounts into a separate directory on calling.
Linux Namespaces
- Задача: обернуть системные ресурсы в уровень абстракции;
- Introduced in Linux 2.4.19 (2002), became “container ready” in 3.8 in 2013 with the introduction of the user namespace;
- Seven distinct namespaces implemented: mnt, pid, net, ipc, uts, user, cgroup; time and syslog introduced in 2016;
- функция clone . Создаёт дочерние процессы. Unlike
fork(2)
, theclone(2)
API allows the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers. You can pass different namespace flags toclone(2)
to create new namespaces for the child process.
unshare(2) - отсоединение частей контекста выполнения процесса.
setns(2)
позволяет запрашивающему процессу присоединяться в разные namespaces.
proc - Besides the available syscalls, the proc
filesystem populates additional namespace related files. Since Linux 3.8, each file in /proc/$PID/ns
is a “magic“ link which can be used as a handle for performing operations (like setns(2)
) to the referenced namespace.
> ls -Gg /proc/self/ns/
total 0
lrwxrwxrwx 1 0 Feb 6 18:32 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 0 Feb 6 18:32 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 0 Feb 6 18:32 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 0 Feb 6 18:32 net -> 'net:[4026532008]'
lrwxrwxrwx 1 0 Feb 6 18:32 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 0 Feb 6 18:32 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 0 Feb 6 18:32 user -> 'user:[4026531837]'
lrwxrwxrwx 1 0 Feb 6 18:32 uts -> 'uts:[4026531838]'
mnt namespace
Ввели в 2002 первым, ещё не знали, что понадобится много разных, потому обозвали флаг клонирования CLONE_NEWNS, что не соответствует флагам других namespaces. С помощью mnt в Linux можно изолировать группу точек монтирования для групп процессов.
> sudo unshare -m
# mkdir mount-dir
# mount -n -o size=10m -t tmpfs tmpfs mount-dir
# df mount-dir
We have a successfully mounted tmpfs, which is not available on the host system level:
> ls mount-dir
> grep mount-dir /proc/mounts
The actual memory being used for the mount point is laying in an abstraction layer called Virtual File System (VFS), which is part of the kernel and where every other filesystem is based on.
> grep mount-dir /proc/$(pgrep -u root bash)/mountinfo
Можно создавать на лету гибкие файловые системы на лету. Mounts can have different flavors (shared, slave, private, unbindable), which is best explained within the shared subtree documentation of the Linux kernel.
uts namespace (UNIX Time-sharing System)
Ввели в 2006 в Linux 2.6.19. Можно отсоединить домен и имя хоста от системы.
> sudo unshare -u
# hostname
nb
# hostname new-hostname
# hostname
new-hostname
And if we look at the system level nothing has changed, hooray:
exit
> hostname
nb
ipc namespace
Ввели в 2006 в Linux 2.6.19. Можно изолировать связи между процессами. Например, общую память (shared memory = SHM) между процессами. Два процесса будут использовать 1 идентификатор для общей памяти, но при этом писать в 2 разных региона памяти.
pid namespace (Process ID)
Ввели в 2008 в Linux 2.6.24. Возможность для процессов иметь одинаковые PID в разных namespace. У одного процесса могут быть 2 PID: один внутри namespace, а второй вовне его - на хост системе. Можно делать вложенные namespace, и тогда PID у 1 процесса будет больше. Первый процесс в namespace получается PID=1 и привилегии init-процесса.
> sudo unshare -fp --mount-proc
# ps aux
Флаг --mount-proc
нужен чтобы переподключить proc filesystem из нового namespace. Иначе PID в namespace будут не видны.
net namespace (Network)
Ввели в 2009 в Linux 2.6.29 для виртуализации сетей. Каждая сеть имеет свои свойства в разделе /proc/net
. При создании нового namespace он содержит только loopback интерфейсы. Создадим:
> sudo unshare -n
# ip l
# ip a
- Каждый интерфейс (физ или вирт) присутствует единожды в каждом namespace. Интерфейсы можно перемещать между namespace;
- Каждый namespace имеет свой набор ip, таблицу маршрутизации, список сокетов, таблицу отслеживания соединений, МЭ и т.д. ресурсы;
- Удаление net namespace разрушает все вирт интерфейсы и перемещает оттуда все физические.
Применение: создание SDN через пары виртуальных интерфейсов. Один конец пары подключается к bridge, а другой конец - к целевому контейнеру. Так работают CNI типа Flannel.
Создадим новый net namepsace:
> sudo ip netns add mynet
> sudo ip netns list
mynet
Когда команда ip создаёт network namespace, она создаёт it will create a bind mount for it under /var/run/netns
too. This allows the namespace to persist even when no processes are running within it.
> sudo ip netns exec mynet ip l
> sudo ip netns exec mynet ping 127.0.0.1
The network seems down, let’s bring it up:
> sudo ip netns exec mynet ip link set dev lo up
> sudo ip netns exec mynet ping 127.0.0.1
Let’s create a veth pair which should allow communication later on:
> sudo ip link add veth0 type veth peer name veth1
> sudo ip link show type veth
Both interfaces are automatically connected, which means that packets sent to veth0
will be received by veth1
and vice versa. Now we associate one end of the veth pair to our network namespace:
> sudo ip link set veth1 netns mynet
> ip link show type veth
Добавляем адреса ip:
> sudo ip netns exec mynet ip addr add 172.2.0.1/24 dev veth1
> sudo ip netns exec mynet ip link set dev veth1 up
> sudo ip addr add 172.2.0.2/24 dev veth0
> sudo ip link set dev veth0 up
Теперь можно связываться в обе стороны:
> ping -c1 172.2.0.1
> sudo ip netns exec mynet ping -c1 172.2.0.2
It works, but we wouldn’t have any internet access from the network namespace. We would need a network bridge or something similar for that and a default route from the namespace.
user namespace
Ввели в 2012-2013 в Linux 3.5-3.8 для изоляции пользователей, групп пользователей. Пользователь получает разные ID внутри и вовне namespace, а также разные привилегии.
> id -u
1000
> unshare -U
> whoami
nobody
После создания namespace, файлы /proc/$PID/{u,g}id_map
раскрывают соответствия user+groupID и PID. Эти файлы пишутся лишь единожды для определения соответствий.
> cat /proc/$PID/uid_map
0 1000 1
cgroups
Ввели в 2008 в Linux 2.6.24 для квотирования и далее переделали капитально в 2016 в Linux 4.6 - ввели cgroups namespace.
Cgroup version check:
stat -fc %T /sys/fs/cgroup/
For cgroup v2, the output is cgroup2fs
.
For cgroup v1, the output is tmpfs.
cgroups memory limit
Cgroups memory.limit_in_bytes was deprecated because it is prone to race condition: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#deprecated-v1-core-features
Использовать memory.max (in bytes)! https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#namespace
Система выдаёт список ограничений. Поменяем ограничения памяти для этой cgroup. Также отключим swap, чтобы реализация сработала:
unshare -c # unshare cgroupns in some cgroup
cat /proc/self/cgroup
sudo mkdir /sys/fs/cgroup/demo
cd /sys/fs/cgroup/demo/
sudo su
echo 100000000 > memory.max
echo 0 > memory.swap.max
cat /proc/self/cgroup
echo 0 > cgroup.procs
cat /proc/self/cgroup
После того как установлено ограничение в 100Mb памяти ОЗУ, напишем приложение, которое забирает память больше чем положенные 100Mb (в случае отсутствия ограничений приложение закрывается при занятии 200Mb):
fn main() {
let mut vec = vec![];
let max_switch: usize = 20; // запасное ограничение =200Mb
let mut memcount: usize;
loop {
vec.extend_from_slice(&[1u8; 10_000_000]);
memcount = vec.len() / 10_000_000;
println!("{}0 MB", memcount);
if memcount > max_switch {
break;
}
}
println!("Program terminated by MAX MEM = {}0 Mb", memcount);
}
Если его запустить, то увидим, что PID будет убит из-за ограничений памяти:
# rustc memory.rs
# ./memory
10 MB
20 MB
30 MB
40 MB
50 MB
60 MB
70 MB
80 MB
90 MB
Killed
Составление пространств имен
Можно составлять пространства имён вместе, чтобы они делили 1 сетевой интерфейс. Так работают k8s Pods. Создадим новое пространство имён с изолированным PID:
> sudo unshare -fp --mount-proc
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.6 18688 6904 pts/0 S 23:36 0:00 -bash
root 39 0.0 0.1 35480 1836 pts/0 R+ 23:36 0:00 ps aux
Вызов ядра setns
с приложением-обёрткой nsenter
теперь можно использовать для присоединения к пространству имён. Для этого нужно понять, в какое пространство мы хотим присоединиться:
> export PID=$(pgrep -u root bash)
> sudo ls -l /proc/$PID/ns
Теперь присоединяемся с помощью nsenter
:
> sudo nsenter --pid=/proc/$PID/ns/pid unshare --mount-proc
# ps aux
root 1 0.1 0.0 10804 8840 pts/1 S+ 14:25 0:00 -bash
root 48 3.9 0.0 10804 8796 pts/3 S 14:26 0:00 -bash
root 88 0.0 0.0 7700 3760 pts/3 R+ 14:26 0:00 ps aux
Своё приложение, создающее контейнер
https://brianshih1.github.io/mini-container/preface.html
runc
Система сборки и запуска контейнеров:
> sudo runc run -b bundle container
Можно исследовать, что runc создал mnt, uts, ipc, pid и net:
> sudo lsns | grep bash
4026532499 mnt 1 6409 root /bin/bash
4026532500 uts 1 6409 root /bin/bash
4026532504 ipc 1 6409 root /bin/bash
4026532505 pid 1 6409 root /bin/bash
4026532511 net 1 6409 root /bin/bash