Namespace
Namespace简介
Linux Namespace 是 Linux 内核提供的一个功能,可以实现系统资源的隔离。目前共有六种namespace:
类型 | 系统调用参数 | 内核版本 |
---|---|---|
Mount Namespace | CLONE_NEWNS | 2.4.19 |
UTS Namespace | CLONE_NEWUTS | 2.6.19 |
IPC Namespace | CLONE_NEWIPC | 2.6.19 |
PID Namespace | CLONE_NEWPID | 2.6.24 |
Network Namespace | CLONE_NEWNET | 2.6.29 |
User Namespace | CLONE_NEWUSER | 3.8 |
可以通过三个系统调用的方式:
- clone, 创建新的进程和新的namespace,新创建的进程 attach 到新创建的 namespace
- unshare,不创建新的进程,创建新的 namespace 并把当前进程 attach 上
- setns, attach 进程到已有的 namespace 上
shell 也提供了一个和系统调用同名的 unshare 命令可以非常简单的创建 namespace。
1 | sudo unshare --fork --pid --mount-proc bash |
六种类型说明
UTS(UNIX Timesharing System) Namespace 可以用来隔离 nodename 和 domainname 两个系统标识。在 UTS Namespace 中,每个 Namespace 可以有自己的 hostname。
IPC(Interprocess Communication) Namespace 用来隔离 System V IPC 和 POSIX message queues。每一个 IPC Namespace 都有自己的 System V IPC 和 POSIX message queue。
PID(Process ID) Namespace 可以用来隔离进程 ID。同一个进程在不同的 PID Namespace 中可以拥有不同的 PID。在 Docker Container 中,使用
ps -ef
可以看到启动容器的进程 PID 为 1,但是在宿主机上,该进程却又有不同的 PID。Mount Namespace 用来隔离各个进程看到的挂载点视图。在不同的 Namespace 中,看到的挂载点文件系统层次是不一样的。在 Mount Namespace 中调用
mount
和unmount
仅仅会影响当前 Namespace 内的文件系统,而对全局文件系统是没有影响的。User Namespace 主要是隔离用户的用户组 ID。也就是说,一个进程的 User ID 和 Group ID 在 User Namespace 内外可以是不同的。比较常用的是,在宿主机上以一个非 root 用户运行创建一个 User Namespace,然后在 User Namespace 中被映射为了 root 用户。*这意味着这个进程在 User Namespace 中有 root 权限,但是在宿主机上却没有 root 权限。 *
Network Namespace 用来隔离网络设置、IP 地址和端口号等网络栈的 Namespace。Network Namespace 可以让每个容器拥有自己独立的网络设备,而且容器内的应用可以绑定到自己的端口,每个 Namespace 的端口都不会有冲突。在宿主机搭建网桥后,就能很方便地实现容器之间的通信。
Cgroups
简介
Linux Namespace 帮助进程隔离出自己的单独空间,而 Cgroups 则可以限制每个空间的大小。 Linux cgroups 的全称是 Linux Control Groups,它是 Linux 内核的特性,主要作用是限制、记录和隔离进程组(process groups)使用的物理资源(cpu、memory、IO 等)。
Cgroups 从设计之初使命就很明确,为进程提供资源控制,它主要的功能包括:
- 资源限制:限制进程使用的资源上限,比如最大内存、文件系统缓存使用限制
- 优先级控制:不同的组可以有不同的优先级,比如 CPU 使用和磁盘 IO 吞吐
- 审计:计算 group 的资源使用情况,可以用来计费
- 控制:挂起一组进程,或者重启一组进程
cgroups 核心概念
前面说过,cgroups 是用来对进程进行资源管理的,因此 cgroup 需要考虑如何抽象这两种概念:进程和资源,同时如何组织自己的结构。cgroups 中有几个非常重要的概念:
- task:任务,对应于系统中运行的一个实体,一般是指进程
- subsystem:子系统,具体的资源控制器(resource class 或者 resource controller),控制某个特定的资源使用。比如 CPU 子系统可以控制 CPU 时间,memory 子系统可以控制内存使用量
- cgroup:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略
- hierarchy:层级树,一系列 cgroup 组成的树形结构。每个节点都是一个 cgroup,cgroup 可以有多个子节点,子节点默认会继承父节点的属性。系统中可以有多个 hierarchy
虽然 cgroups 支持 hierarchy,允许不同的子资源挂到不同的目录,但是多个树之间有各种限制,增加了理解和维护的复杂性。在实际使用中,所有的子资源都会统一放到某个路径下(比如 ubuntu16.04 的 /sys/fs/cgroup/
),因此本文并不详细介绍多个树的情况,感兴趣的可以参考 RedHat 的这篇文档。
子资源系统(Resource Classes or SubSystem)
目前有下面这些资源子系统:
- Block IO(
blkio
):限制块设备(磁盘、SSD、USB 等)的 IO 速率 - CPU Set(
cpuset
):限制任务能运行在哪些 CPU 核上 - CPU Accounting(
cpuacct
):生成 cgroup 中任务使用 CPU 的报告 - CPU (
CPU
):限制调度器分配的 CPU 时间 - Devices (
devices
):允许或者拒绝 cgroup 中任务对设备的访问 - Freezer (
freezer
):挂起或者重启 cgroup 中的任务 - Memory (
memory
):限制 cgroup 中任务使用内存的量,并生成任务当前内存的使用情况报告 - Network Classifier(
net_cls
):为 cgroup 中的报文设置上特定的 classid 标志,这样 tc 等工具就能根据标记对网络进行配置 - Network Priority (
net_prio
):对每个网络接口设置报文的优先级 perf_event
:识别任务的 cgroup 成员,可以用来做性能分析
Hierarchy
Linux 进程之间组成一棵树的结构,每个进程(除了 init
根进程之外)都有一个父进程,子进程创建之后会继承父进程的一些属性(比如环境变量,打开的文件描述符等)。
和进程模型类似,只不过 cgroups 是一个森林结构。
使用 cgroups
cgroup 内核功能比较有趣的地方是它没有提供任何的系统调用接口,而是对 linux vfs 的一个实现,因此可以用类似文件系统的方式进行操作。
使用 cgroups 的方式有几种:
- 使用 cgroups 提供的虚拟文件系统,直接通过创建、读写和删除目录、文件来控制 cgroups
- 使用命令行工具,比如 libcgroup 包提供的 cgcreate、cgexec、cgclassify 命令
- 使用
rules engine daemon
提供的配置文件 - 当然,systemd、lxc、docker 这些封装了 cgroups 的软件也能让你通过它们定义的接口控制 cgroups 的内容
查看 cgroups 挂载信息
在 ubuntu 16.04 的机器上,cgroups 已经挂载到文件系统上了,可以通过 mount
命令查看:
1 | root@PEK1000251892:~# mount -t cgroup |
2 | cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) |
3 | cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) |
4 | cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) |
5 | cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb) |
6 | cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children) |
7 | cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) |
8 | cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event) |
9 | cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids) |
10 | cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) |
11 | cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) |
12 | cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) |
如果没有的话,也可以通过以下命令来把想要的 subsystem mount 到系统中:
1 | root@PEK1000251892:~# mount mount -t cgroup -o cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem |
上述命令表示把 cpu、cpuset、memory 三个子资源 mount 到 /cgroup/cpu_and_mem
目录下。
每个 cgroup 目录下面都会有描述该 cgroup 的文件,除了每个 cgroup 独特的资源控制文件,还有一些通用的文件:
tasks
:当前 cgroup 包含的任务(task)pid 列表,把某个进程的 pid 添加到这个文件中就等于把进程移到该 cgroupcgroup.procs
:当前 cgroup 中包含的 thread group 列表,使用逻辑和tasks
相同notify_on_release
:0 或者 1,是否在 cgroup 销毁的时候执行 notify。如果为 1,那么当这个 cgroup 最后一个任务离开时(退出或者迁移到其他 cgroup),并且最后一个子 cgroup 被删除时,系统会执行release_agent
中指定的命令release_agent
:需要执行的命令
创建 cgroup
创建 cgroup,可以直接用 mkdir
在对应的子资源中创建一个目录:
1 | root@PEK1000251892:~# mkdir /sys/fs/cgroup/cpu/mycgroup |
2 | root@PEK1000251892:~# ls /sys/fs/cgroup/cpu/mycgroup |
3 | cgroup.clone_children cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.stat tasks |
4 | cgroup.procs cpuacct.usage cpu.cfs_period_us cpu.shares notify_on_release |
上面命令在 cpu 子资源中创建了 mycgroup
,创建 cgroup 之后,目录中会自动创建需要的文件。
删除 cgroup
删除子资源,就是删除对应的目录:
1 | root@PEK1000251892:~# rmdir /sys/fs/cgroup/cpu/mycgroup/ |
删除之后,如果 tasks 文件中有进程,它们会自动迁移到父 cgroup 中。
设置 cgroup 参数
设置 group 的参数就是直接往特定的文件中写入特定格式的内容,比如要限制 cgroup 能够使用的 CPU 核数:
1 | root@PEK1000251892:~# echo 0-1 > /sys/fs/cgroup/cpuset/mycgroup/cpuset.cpus |
把进程加入到 cgroup
要把某个已经运行的进程加入到 cgroup,可以直接往需要的 cgroup tasks 文件中写入进程的 PID:
1 | root@PEK1000251892:~# echo 2358 > /sys/fs/cgroup/memory/mycgroup/tasks |
在 cgroup 中运行进程
如果想直接把进程运行在某个 cgroup,但是运行前还不知道进程的 Pid 应该怎么办呢?
我们可以利用 cgroup 的继承方式来实现,因为子进程会继承父进程的 cgroup,因此我们可以把当前 shell 加入到要想的 cgroup:
1 | root@PEK1000251892:~# echo $$ > /sys/fs/cgroup/cpu/mycgroup/tasks |
上面的方案有个缺陷,运行完之后原来的 shell 还在 cgroup 中。如果希望进程运行完不影响当前使用的 shell,可以另起一个临时的 shell:
1 | sh -c "echo \$$ > /sys/fs/cgroup/memory/mycgroup/tasks & & stress -m 1" |
把进程移动到 cgroup
如果想要把进程移动到另外一个 cgroup,只要使用 echo
把进程 PID 写入到 cgroup tasks 文件中即可,原来 cgroup tasks 文件会自动删除该进程。
Docker使用cgroup
默认情况下,Docker 启动一个容器后,会在 /sys/fs/cgroup
目录下的各个资源目录下生成以容器 ID 为名字的目录(group),比如:
1 | /sys/fs/cgroup/cpu/docker/03dd196f415276375f754d51ce29b418b170bd92d88c5e420d6901c32f93dc14 |
此时 cpu.cfs_quota_us 的内容为 -1,表示默认情况下并没有限制容器的 CPU 使用。在容器被 stopped 后,该目录被删除。
运行命令 docker run -d --name web41 --cpu-quota 25000 --cpu-period 100 --cpu-shares 30 training/webapp python app.py
启动一个新的容器,结果:
1 | root@devstack:/sys/fs/cgroup/cpu/docker/06bd180cd340f8288c18e8f0e01ade66d066058dd053ef46161eb682ab69ec24# cat cpu.cfs_quota_us |
2 | 25000 |
3 | root@devstack:/sys/fs/cgroup/cpu/docker/06bd180cd340f8288c18e8f0e01ade66d066058dd053ef46161eb682ab69ec24# cat tasks |
4 | 3704 |
5 | root@devstack:/sys/fs/cgroup/cpu/docker/06bd180cd340f8288c18e8f0e01ade66d066058dd053ef46161eb682ab69ec24# cat cpu.cfs_period_us |
6 | 2000 |
Docker 会将容器中的进程的 ID 加入到各个资源对应的 tasks 文件中。表示 Docker 也是以上面的机制来使用 cgroups 对容器的 CPU 使用进行限制。
相似地,可以通过 docker run
中 mem
相关的参数对容器的内存使用进行限制:
1 | --cpuset-mems string MEMs in which to allow execution (0-3, 0,1) |
2 | --kernel-memory string Kernel memory limit |
3 | -m, --memory string Memory limit |
4 | --memory-reservation string Memory soft limit |
5 | --memory-swap string Swap limit equal to memory plus swap: '-1' to enable unlimited swap |
6 | --memory-swappiness int Tune container memory swappiness (0 to 100) (default -1) |
比如docker run -d --name web42 --blkio-weight 100 --memory 10M --cpu-quota 25000 --cpu-period 2000 --cpu-shares 30 training/webapp python app.py
:
1 | root@devstack:/sys/fs/cgroup/memory/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410# cat memory.limit_in_bytes |
2 | 10485760 |
3 | root@devstack:/sys/fs/cgroup/blkio/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410# cat blkio.weight |
4 | 100 |
目前 docker 已经几乎支持了所有的 cgroups 资源,可以限制容器对包括 network,device,cpu 和 memory 在内的资源的使用,比如:
1 | root@devstack:/sys/fs/cgroup# find -iname ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
2 | ./net_prio/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
3 | ./net_cls/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
4 | ./systemd/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
5 | ./hugetlb/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
6 | ./perf_event/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
7 | ./blkio/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
8 | ./freezer/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
9 | ./devices/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
10 | ./memory/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
11 | ./cpuacct/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
12 | ./cpu/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
13 | ./cpuset/docker/ec8d850ebbabaf24df572cb5acd89a6e7a953fe5aa5d3c6a69c4532f92b57410 |
Capbilities
capbilities查看
在上一篇文章中介绍了capbilities了,实际是一种把root权限细分的技术,现在介绍下docker中的capbilities的使用。
列出系统支持的capability使用命令 capsh --print
,查看dockerd进程所有capbilities:
1 | root@PEK1000251892:~# cat /proc/`pidof dockerd`/status | grep Cap |
2 | CapInh: 0000000000000000 |
3 | CapPrm: 0000003fffffffff |
4 | CapEff: 0000003fffffffff |
5 | CapBnd: 0000003fffffffff |
6 | CapAmb: 0000000000000000 |
解码
1 | root@PEK1000251892:~# capsh --decode=0000003fffffffff |
2 | 0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,37 |
和getpcaps得到结果一样
1 | root@PEK1000251892:~# getpcaps `pidof dockerd` |
2 | Capabilities for `116548': = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,37+ep |
Docker默认capbilities
Docker 能调整容器中的进程针对主机上的 OS 功能的访问授权,这些功能访问授权称为 capability。 Docker 在创建容器时,会默认去除一组 capability,包括:
- SETPCAP: 修改进程的 capability
- SYS_MODULE: 插入/删除内核模块
- SYS_RAWIO: 修改内核内存
- SYS_PACCT: 配置进程的记账
- SYS_NICE: 修改进程的优先级
- SYS_RESOURCE: 覆盖资源的限制
- SYS_TIME: 修改系统时钟
- SYS_TTY_CONFIG: 配置 TTY 设备
- AUDIT_WRITE: 写审计日志
- AUDIT_CONTROL: 配置审计子系统
- MAC_OVERRIDE: 忽略内核 MAC 策略
- MAC_ADMIN: 配置 MAC 设置信息
- SYSLOG: 修改内核的 print 行为
- NET_ADMIN: 配置网络
- SYS_ADMIN: 表示系统管理的全部功能
Docker为了确保容器的安全,仅仅支持了其中的14项基本的 Capabilities,现在,我们不妨来看看这些 Capabilities 到底有哪些,它们分别是: CAP_CHOWN
、 CAP_DAC_OVERRIDE
、 CAP_FSETID
、 CAP_MKNOD
、 FOWNER
、 NET_RAW
、 SETGID
、 SETUID
、 SETFCAP
、 SETPCAP
、 NET_BIND_SERVICE
、 SYS_CHROOT
、 KILL
和 AUDIT_WRITE
。 在这 14 项中几乎没有一项涉及到系统管理权限,比如 Docker 容器的 root 用户不具备 CAP_SYS_ADMIN,磁盘限额操作、mount 操作、创建进程新命名空间等均无法实现;比如由于没有 CAP_NET_ADMIN,网络方面的配置管理也将受到管制。因此,默认情况下,Docker 容器中的 root 用户并没有以往我们想象得那么能力超群,Docker 依然对其存在限制,这样设计的出发点之一自然是安全。
添加容器的 capability 用 --cap-add
,去除容器的 capability 用 --cap-drop
。 Linux 文档中的所有 capability 名都是以 CAP_
开头的全部大写字母,但是这里用其不带前缀的小字版本。例子:
1 | $ docker run --rm -u nobody \ |
2 | ubuntu:latest \ |
3 | /bin/bash -c "capsh --print | grep net_raw" |
4 | |
5 | Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+i |
6 | Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap |
7 | |
8 | $ docker run --rm -u nobody \ |
9 | --cap-drop net_raw \ # drop NET_RAW capability |
10 | ubuntu:latest \ |
11 | /bin/bash -c "capsh --print | grep net_raw" # no output |
12 | |
13 | $ docker run --rm -u nobody \ |
14 | ubuntu:latest \ |
15 | /bin/bash -c "capsh --print | grep sys_admin" # no output |
16 | |
17 | $ docker run --rm -u nobody \ |
18 | --cap-add sys_admin \ |
19 | ubuntu:latest \ |
20 | /bin/bash -c "capsh --print | grep sys_admin" |
21 | |
22 | urrent: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_admin,cap_mknod,cap_audit_write,cap_setfcap+i |
23 | Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_admin,cap_mknod,cap_audit_write,cap_setfcap |
24 | hp |
seccomp
使用
Seccomp是Secure computing mode的缩写,它是Linux内核提供的一个操作,用于限制一个进程可以执行的系统调用。当然,我们需要有一个配置文件来指明进程到底可以执行哪些系统调用,不可以执行哪些系统调用。
在Docker中,它使用Seccomp来限制一个容器可以执行的系统调用。
默认情况下,Seccomp会禁止容器执行64位Linux系统的313个系统调用的44个。我找不到这个Seccomp的默认配置文件在哪,可能是写在了源码中。
我们下面来描述如何使用Seccomp。
1 | $ grep CONFIG_SECCOMP= /boot/config-$(uname -r) |
2 | CONFIG_SECCOMP=y |
首先,创建一个配置文件:/home/ygy/seccomp/profile.json,其内容为:
1 | { |
2 | "defaultAction": "SCMP_ACT_ALLOW", |
3 | "syscalls": [ |
4 | { |
5 | "name": "chmod", |
6 | "action": "SCMP_ACT_ERRNO" |
7 | } |
8 | ] |
9 | } |
在上面的这个配置文件中,默认情况下,我们允许容器执行全部的系统调用.但是,禁止它执行chmod这个系统调用.
然后,我们用这个Seccomp配置文件来启动一个Docker容器:
1 | docker run --rm -it --security-opt seccomp:/home/ygy/seccomp/profile.json busybox chmod 400 /etc/hosts |
结果如下:
1 | chmod: /etc/hosts: Operation not permitted |
使用docker inspect
查看容器详细信息,有如下输出:
1 | "SecurityOpt": [ |
2 | "seccomp: |
3 | {"defaultAction":"SCMP_ACT_ALLOW", |
4 | "syscalls":[{"name":"chmod","action":"SCMP_ACT_ERRNO"}]}" |
5 | ], |
我们也可以在run
一个容器的时候,通过--security-opt seccomp:unconfined
参数来允许容器执行全部的系统的调用。
1 | docker run --rm -it --security-opt seccomp:unconfined busybox chmod 400 /etc/hosts |
默认策略
docker seccomp使用白名单策机制,默认的策略列表: https://github.com/moby/moby/blob/master/profiles/seccomp/default.json
一些重要的系统调用如下:(不完整列表)
Syscall | Description |
---|---|
acct |
Accounting syscall which could let containers disable their own resource limits or process accounting. Also gated by CAP_SYS_PACCT . |
add_key |
Prevent containers from using the kernel keyring, which is not namespaced. |
bpf |
Deny loading potentially persistent bpf programs into kernel, already gated by CAP_SYS_ADMIN . |
clock_adjtime |
Time/date is not namespaced. Also gated by CAP_SYS_TIME . |
clock_settime |
Time/date is not namespaced. Also gated by CAP_SYS_TIME . |
clone |
Deny cloning new namespaces. Also gated by CAP_SYS_ADMIN for CLONE_* flags, except CLONE_USERNS . |
create_module |
Deny manipulation and functions on kernel modules. Obsolete. Also gated by CAP_SYS_MODULE . |
delete_module |
Deny manipulation and functions on kernel modules. Also gated by CAP_SYS_MODULE . |
finit_module |
Deny manipulation and functions on kernel modules. Also gated by CAP_SYS_MODULE . |
get_kernel_syms |
Deny retrieval of exported kernel and module symbols. Obsolete. |
get_mempolicy |
Syscall that modifies kernel memory and NUMA settings. Already gated by CAP_SYS_NICE . |
init_module |
Deny manipulation and functions on kernel modules. Also gated by CAP_SYS_MODULE . |
ioperm |
Prevent containers from modifying kernel I/O privilege levels. Already gated by CAP_SYS_RAWIO . |
iopl |
Prevent containers from modifying kernel I/O privilege levels. Already gated by CAP_SYS_RAWIO . |
kcmp |
Restrict process inspection capabilities, already blocked by dropping CAP_PTRACE . |
kexec_file_load |
Sister syscall of kexec_load that does the same thing, slightly different arguments. Also gated by CAP_SYS_BOOT . |
kexec_load |
Deny loading a new kernel for later execution. Also gated by CAP_SYS_BOOT . |
keyctl |
Prevent containers from using the kernel keyring, which is not namespaced. |
lookup_dcookie |
Tracing/profiling syscall, which could leak a lot of information on the host. Also gated by CAP_SYS_ADMIN . |
mbind |
Syscall that modifies kernel memory and NUMA settings. Already gated by CAP_SYS_NICE . |
mount |
Deny mounting, already gated by CAP_SYS_ADMIN . |
move_pages |
Syscall that modifies kernel memory and NUMA settings. |
name_to_handle_at |
Sister syscall to open_by_handle_at . Already gated by CAP_DAC_READ_SEARCH . |
nfsservctl |
Deny interaction with the kernel nfs daemon. Obsolete since Linux 3.1. |
open_by_handle_at |
Cause of an old container breakout. Also gated by CAP_DAC_READ_SEARCH . |
perf_event_open |
Tracing/profiling syscall, which could leak a lot of information on the host. |
personality |
Prevent container from enabling BSD emulation. Not inherently dangerous, but poorly tested, potential for a lot of kernel vulns. |
pivot_root |
Deny pivot_root , should be privileged operation. |
process_vm_readv |
Restrict process inspection capabilities, already blocked by dropping CAP_PTRACE . |
process_vm_writev |
Restrict process inspection capabilities, already blocked by dropping CAP_PTRACE . |
ptrace |
Tracing/profiling syscall, which could leak a lot of information on the host. Already blocked by dropping CAP_PTRACE . Blocked in Linux kernel versions before 4.8 to avoid seccomp bypass. |
query_module |
Deny manipulation and functions on kernel modules. Obsolete. |
quotactl |
Quota syscall which could let containers disable their own resource limits or process accounting. Also gated by CAP_SYS_ADMIN . |
reboot |
Don’t let containers reboot the host. Also gated by CAP_SYS_BOOT . |
request_key |
Prevent containers from using the kernel keyring, which is not namespaced. |
set_mempolicy |
Syscall that modifies kernel memory and NUMA settings. Already gated by CAP_SYS_NICE . |
setns |
Deny associating a thread with a namespace. Also gated by CAP_SYS_ADMIN . |
settimeofday |
Time/date is not namespaced. Also gated by CAP_SYS_TIME . |
stime |
Time/date is not namespaced. Also gated by CAP_SYS_TIME . |
swapon |
Deny start/stop swapping to file/device. Also gated by CAP_SYS_ADMIN . |
swapoff |
Deny start/stop swapping to file/device. Also gated by CAP_SYS_ADMIN . |
sysfs |
Obsolete syscall. |
_sysctl |
Obsolete, replaced by /proc/sys. |
umount |
Should be a privileged operation. Also gated by CAP_SYS_ADMIN . |
umount2 |
Should be a privileged operation. Also gated by CAP_SYS_ADMIN . |
unshare |
Deny cloning new namespaces for processes. Also gated by CAP_SYS_ADMIN , with the exception of unshare --user . |
uselib |
Older syscall related to shared libraries, unused for a long time. |
userfaultfd |
Userspace page fault handling, largely needed for process migration. |
ustat |
Obsolete syscall. |
vm86 |
In kernel x86 real mode virtual machine. Also gated by CAP_SYS_ADMIN . |
vm86old |
In kernel x86 real mode virtual machine. Also gated by CAP_SYS_ADMIN . |
不使用seccomp
1 | $ docker run --rm -it --security-opt seccomp=unconfined debian:jessie \ |
2 | unshare --map-root-user --user sh -c whoami |
AppArmor和Selinux
docker支持apparmor和selinux,默认不启用selinux,启用apparmor。 AppArmor 是一个 Linux 内核安全模块,可用于限制主机操作系统上运行的进程的功能。每个进程都可以拥有自己的安全配置文件。安全配置文件用来允许或禁止特定功能,例如网络访问或文件读/写/执行权限。
加载和卸载profiles
加载一个新的profile
1 | $ apparmor_parser -r -W /path/to/your_profile |
docker运行自定义的apparmor策略
1 | $ docker run --rm -it --security-opt apparmor=your_profile hello-world |
卸载一个
1 | $ apparmor_parser -R /path/to/profile |
默认策略
Docker默认策略生成模板 https://github.com/moby/moby/blob/master/profiles/apparmor/template.go
参考:
https://zhuanlan.zhihu.com/p/67586925
https://zhuanlan.zhihu.com/p/55099839
https://chuanleiguo.com/2018/08/05/Docker-Namespace-Cgroup/
https://cizixs.com/2017/08/25/linux-cgroup/
https://zhuanlan.zhihu.com/p/98920705
https://www.atjiang.com/limiting-risk-with-isolation-in-docker/