Linux Capability
Linux 内核中的 Capabilities 特性用于划分特权集,以便进程可以只分配 “执行特定功能” 的特权。
在引入此特性前,如果进程需要使用网络,则必须使用 root 来运行,通常是 sudo 或者添加 suid,那么普通用户在使用 ping 时,ping 就可以运行任何特权。引入 Capabilities 特性后,可以通过给 ping 应用添加 CAP_NET_RAW 特权集,使其具有使用网络的特权集,而不具备其他特权集。缺省 ping 具有 cap_net_admin 和 cap_net_raw 特权集
命令setcap和getcap可以设置和查看文件的capabilities。因为文件的capabilities存储在文件的扩展属性中(xattr),目前ext2,ext3,ext4和xfs等常用文件系统都支持扩展属性。常规cp命令不会复制文件capabilities,cp -a可以支持拷贝文件的capabilities。如文件系统没有设置xattr属性那么无论getcap,getfattr是看不到文件的capabilities属性的也可以函数setxattr()和getxattr()的security.capability字段是可以设置和查看文件的capabilities。
# getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p
在 Linux 中的 Capabilities 是通过 extended attributes 中的 security 命名空间实现的, selinux 也是一样
# getfattr -d -m "^security\\." /bin/ping
getfattr: Removing leading '/' from absolute path names
# file: bin/ping
security.capability=0sAAAAAgAgAAAAAAAAAAAAAAAAAAA=
security.selinux="system_u:object_r:ping_exec_t:s0"
可以通过查看进程 / proc/xxxx/status,来检查进程的特权集,并通过 capsh 来解码得到具体的特权集。
# ps -ef | grep dockerd | grep -v grep
root 1159 1 1 May26 ? 01:02:41 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
# cat /proc/1159/status | grep Cap
CapInh: 0000000000000000 #可以继承的CAP(i)
CapPrm: 0000003fffffffff #可以使用的CAP(p)
CapEff: 0000003fffffffff #使用的CAP(e)
CapBnd: 0000003fffffffff #进程特有
CapAmb: 0000000000000000 #进程特有
可以看到权限一共分为5组, 分别是:
- Inheritable: 当前线程可以被继承的权限
- Permitted: 当前线程可被允许的权限
- Effective: 当前线程真正生效的权限, 内核在执行时会去根据这个权限来判断是否允许对于的操
- Bounding: 权限边界
- Ambient: 暂时未使用到,后面在添加
补充
Capabilities 的主要思想在于分割 root 用户的特权,即将 root 的特权分割成不同的能力,每种能力代表一定的特权操作。
例如:能力CAP_SYS_MODULE表示用户能够加载 (或卸载) 内核模块的特权操作,而CAP_SETUID表示用户能够修改进程用户身份的特权操作。在 Capbilities 中系统将根据进程拥有的能力来进行特权操作的访问控制。
在Capilities中,只有进程和可执行文件才具有能力,每个进程拥有三组能力集,分别称为cap_effective, cap_inheritable, cap_permitted(分别简记为: PE,PI,PP)。
其中cap_permitted表示进程所拥有的最大能力集, cap_effective表示进程当前可用的能力集,可以看做是cap_permitted的一个子集, 而cap_inheitable则表示进程可以传递给其子进程的能力集。
系统根据进程的cap_effective能力集进行访问控制,cap_effective为cap_permitted的子集,进程可以通过取消cap_effective中的某些能力来放弃进程的一些特权。
可执行文件也拥有三组能力集,对应于进程的三组能力集,分别称为cap_effective, cap_allowed 和 cap_forced(分别简记为 FE,FI,FP), 其中cap_allowed表示程序运行时可从原进程的cap_inheritable中集成的能力集,cap_forced表示运行文件时必须拥有才能完成其服务的能力集, 而cap_effective则表示文件开始运行时可以使用的能力。
使用 capsh 可以翻译出每个 BIT 的含义,3=0011 表示 2 个 bit,f=1111 表示 4 个 bit,一共 2+4+4+4+4+4+4+4+4+4=38bit,从后先前,每个 bit 代表一种特权,一共 38 种特权集.
# capsh --decode=0000003fffffffff
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,cap_audit_read
docker Capability
Docker 也支持 Capabilities , 在运行容器的时候可以通过指定 --privileded 参数来开启容器的所有特权,可以通过 --cap-add 和 --cap-drop 这两个参数来调整.后台运行一个容器 busybox,通过查看进程,可以发现容器默认只有 14 种特权集相关特权级参考
补充:
CAP_CHOWN: 修改文件属主的权限
CAP_DAC_OVERRIDE: 忽略文件的 DAC 访问限制
CAP_DAC_READ_SEARCH: 忽略文件读及目录搜索的 DAC 访问限制
CAP_FOWNER: 忽略文件属主 ID 必须和进程用户 ID 相匹配的限制
CAP_FSETID: 允许设置文件的 setuid 位
CAP_KILL: 允许对不属于自己的进程发送信号
CAP_SETGID: 允许改变进程的组 ID
CAP_SETUID: 允许改变进程的用户 ID
CAP_SETPCAP: 允许向其他进程转移能力以及删除其他进程的能力
CAP_LINUX_IMMUTABLE: 允许修改文件的 IMMUTABLE 和 APPEND 属性标志
CAP_NET_BIND_SERVICE: 允许绑定到小于 1024 的端口
CAP_NET_BROADCAST: 允许网络广播和多播访问
CAP_NET_ADMIN: 允许执行网络管理任务
CAP_NET_RAW: 允许使用原始套接字
CAP_IPC_LOCK: 允许锁定共享内存片段
CAP_IPC_OWNER: 忽略 IPC 所有权检查
CAP_SYS_MODULE: 允许插入和删除内核模块
CAP_SYS_RAWIO: 允许直接访问 / devport,/dev/mem,/dev/kmem 及原始块设备
CAP_SYS_CHROOT: 允许使用 chroot() 系统调用
CAP_SYS_PTRACE: 允许跟踪任何进程
CAP_SYS_PACCT: 允许执行进程的 BSD 式审计
CAP_SYS_ADMIN: 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等
CAP_SYS_BOOT: 允许重新启动系统
CAP_SYS_NICE: 允许提升优先级及设置其他进程的优先级
CAP_SYS_RESOURCE: 忽略资源限制
CAP_SYS_TIME: 允许改变系统时钟
CAP_SYS_TTY_CONFIG: 允许配置 TTY 设备
CAP_MKNOD: 允许使用 mknod() 系统调用
CAP_LEASE: 允许修改文件锁的 FL_LEASE 标志
相关操作验证:
默认无cap_sys_nice特权功能的情况:
在默认无cap_sys_nice能里的情况下启动容器docker run --name test1 -td busybox /bin/httpd -f
在主机侧执行ps -ef | grep httpd | grep -v grep , 获取容器进程内pid
root 50496 50472 0 10:42 pts/0 00:00:00 /bin/httpd -f
在主机侧查看该进程所拥有的能力:$ cat /proc/50496/status|grep Cap
CapInh: 0000000000000000
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
主机侧通过capsh来解码得到具体的特权集(默认情况下是14个特权)。无cap_sys_nice特权。
$ capsh --decode=00000000a80425fb
0x00000000a80425fb=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
进入容器内部docker exec -it test1 /bin/sh 并执行以下命令:
/ # renice -n -9 1
renice: setpriority: Permission denied
添加cap_sys_nice特权功能的情况:
通过--cap-add=cap_sys_nice添加特权并启动容器docker run --name test2 -td --cap-add=cap_sys_nice busybox /bin/httpd -f
在主机侧执行ps -ef | grep httpd | grep -v grep , 获取容器进程内pid
root 26805 26765 0 11:01 pts/0 00:00:00 /bin/httpd -f
在主机侧查看该进程所拥有的能力:cat /proc/26805/status|grep -i cap
CapInh: 0000000000000000
CapPrm: 00000000a88425fb
CapEff: 00000000a88425fb
CapBnd: 00000000a88425fb
CapAmb: 0000000000000000
主机侧通过capsh来解码得到具体的特权集。存在cap_sys_nice特权。
$ capsh --decode=00000000a88425fb
0x00000000a88425fb=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_nice,cap_mknod,cap_audit_write,cap_setfcap
进入容器内部docker exec -it test2 /bin/sh 并执行以下命令,正常执行:
/ # renice -n -9 -p 1
添加所有特权的情况下--cap-add=ALL:
通过--cap-add=ALL添加所有特权并启动容器docker run --name test3 -td --cap-add=ALL busybox /bin/httpd -f
在主机侧执行ps -ef | grep httpd | grep -v grep , 获取容器进程内pid
$ ps -ef | grep httpd | grep -v grep
root 1390 1365 2 13:55 pts/0 00:00:00 /bin/httpd -f
在主机侧查看该进程所拥有的能力:cat /proc/1390/status|grep -i cap
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
主机侧通过capsh来解码得到具体的特权集(38个特权)。
$ capsh --decode=0000003fffffffff
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,cap_audit_read
进入容器内部docker exec -it test3 /bin/sh 并执行以下命令,正常执行:
/ # renice -n -9 -p 1
特权集--cap-drop和--cap-add是在基础的14种特权集的基础上先减后加的。使用--cap-drop=ALL --cap-add=cap_net_bind_service,则只有cap_net_bind_service 特权集.
通过--cap-drop=ALL删除所有特权并只添加一个特权--cap-add=cap_net_bind_service,启动容器docker run --name test3 -td --cap-drop=ALL --cap-add=cap_net_bind_service busybox /bin/sh
在主机侧执行 ps -ef |grep cid(容器id) ,获取容器进程,并获取容器子进程。
$ ps -ef |grep 53920 |grep -v grep
root 53920 1 0 14:52 ? 00:00:00 /usr/bin/containerd-shimxxxxxx -address /run/containerd/containerd.sock
root 53938 53920 0 14:52 ? 00:00:00 /bin/sh
在主机侧查看该子进程所拥有的能力:cat /proc/53938/status|grep -i cap
$ cat /proc/53938/status|grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000400
CapEff: 0000000000000400
CapBnd: 0000000000000400
CapAmb: 0000000000000000
主机侧通过capsh来解码得到具体的特权集。
$ capsh --decode=0000000000000400
0x0000000000000400=cap_net_bind_service
进入容器内部docker exec -it test3 /bin/sh 并执行以下命令:
/ # renice -n -9 -p 1
renice: setpriority: Permission denied
删除cap_mknod默认特权功能的情况:
通过--cap-drop=cap_mknod添加特权并启动容器docker run --name test4 -td --cap-add=cap_sys_nice busybox /bin/sh
在主机侧执行 ps -ef |grep cid(容器id) ,获取容器进程,并获取容器子进程。
$ ps -ef |grep 63158|grep -v grep
root 63158 1 0 16:30 ? 00:00:00 /usr/bin/containerdxxxxxx -address /run/containerd/containerd.sock
root 63179 63158 0 16:30 ? 00:00:00 /bin/sh
在主机侧查看该子进程所拥有的能力:cat /proc/63179/status|grep -i cap
CapInh: 0000000000000000
CapPrm: 00000000a00425fb
CapEff: 00000000a00425fb
CapBnd: 00000000a00425fb
CapAmb: 0000000000000000
主机侧通过capsh来解码得到具体的特权集(默认情况下是14个特权级,现在为13个)。
$ capsh --decode=00000000a00425fb
0x00000000a00425fb=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_audit_write,cap_setfcap
进入容器内部docker exec -it test4 /bin/sh 并执行以下命令:
/ # mknod /tmp/sda b 8 0 && echo ok
mknod: /tmp/sda: Operation not permitted