linux seccomp
linux seccomp参考链接
https://blog.csdn.net/qq_44846324/article/details/121731640
http://kuring.me/post/linux-seccomp/
seccomp是secure computing mode的缩写,是Linux内核中的一个安全计算工具,机制用于限制应用程序可以使用的系统调用,增加系统的安全性。
在/proc/${pid}/status文件中的Seccomp字段可以看到进程的Seccomp
- 返回值0表示没有使用seccomp
- 返回值2表示使用了seccomp并处于SECCOMP_MODE_FILTER模式
prctl
seccomp有三种工作模式:
seccomp-disabled,seccomp-strict,seccomp-filter
seccomp-filter模式允许进程为传入的系统调用指定一个过滤器。Linux内核提供了两个系统调用,prctl()和seccomp()来设置seccomp过滤模式。
但是它们只能用于更改调用线程/进程的seccomp过滤器模式,不能设置其他进程的seccomp过滤器模式。
seccomp支持两种模式:
SECCOMP_MODE_STRICT和SECCOMP_MODE_FILTER在SECCOMP_MODE_STRICT模式下,进程不能使用read(2)、write(2)、_exit(2)和sigreturn(2)以外的其他系统调用。 在SECCOMP_MODE_FILTER模式下,可以利用Berkeley Packet Filter(BPF)配置哪些系统调用及它们的参数可以被进程使用。
使用prctl来设置程序的seccomp为strict模式,仅允许read、write、_exit和sigreturn四个系统调用
当调用seccomp白名单之外的系统调用时,程序会被kill
#include <stdio.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <unistd.h>
int main() {
printf("step 1: unrestricted\n");
// Enable filtering
prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
printf("step 2: only 'read', 'write', '_exit' and 'sigreturn' syscalls\n");
// Redirect stderr to stdout
dup2(1, 2);
printf("step 3: !! YOU SHOULD NOT SEE ME !!\n");
// Success (well, not so in this case...)
return 0;
}
输出结果
step 1: unrestricted
step 2: only 'read', 'write', '_exit' and 'sigreturn' syscalls
已杀死
基于BPF的seccomp
基于prctl系统调用的seccomp机制不够灵活,在linux 3.5之后引入了基于BPF的可定制的系统调用过滤功能
需要先安装依赖包:apt install libseccomp-dev
#include <stdio.h> /* printf */
#include <unistd.h> /* dup2: just for test */
#include <seccomp.h> /* libseccomp */
int main() {
printf("step 1: unrestricted\n");
// Init the filter
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_KILL); // default action: kill
// setup basic whitelist
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
// setup our rule
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup2), 2,
SCMP_A0(SCMP_CMP_EQ, 1),
SCMP_A1(SCMP_CMP_EQ, 2));
// build and load the filter
seccomp_load(ctx);
printf("step 2: only 'write' and dup2(1, 2) syscalls\n");
// Redirect stderr to stdout
dup2(1, 2);
printf("step 3: stderr redirected to stdout\n");
// Duplicate stderr to arbitrary fd
dup2(2, 42);
printf("step 4: !! YOU SHOULD NOT SEE ME !!\n");
// Success (well, not so in this case...)
return 0;
}
gcc b.c -o b -l seccomp
输出结果
step 1: unrestricted
step 2: only 'write' and dup2(1, 2) syscalls
step 3: stderr redirected to stdout
错误的系统调用 (核心已转储)
直接手写 BPF 代码过滤 system call稍有不慎就会引入 bug,导致一些 system call 没有被blacklist 掉。开源项目 Kafel 提供了解决方案。
Kafel 规定了一种更便于人理解的 policy file,并且提供了编译器,能把 policy file 编译成 BPF 代码
但是kafel生成的bpf代码是
BPF_STMT、BPF_JUMP这种类型的
docker中的应用
通过如下方式可以查看docker是否启用seccomp
$ sudo docker info --format "{{ .SecurityOptions }}"
[name=apparmor name=seccomp,profile=builtin]
关于docker的seccomp配置文件信息可以看官方文档https://docs.docker.com/engine/security/seccomp/
docker run --rm \
-it \
--security-opt seccomp=/path/to/seccomp/profile.json \
hello-world
k8s中的应用seccomp
https://zhuanlan.zhihu.com/p/578250080
eBPF-seccomp的缺点
https://zhuanlan.zhihu.com/p/580777000
linux capability
参考链接
https://www.xiexianbin.cn/linux/basic/linux-capabilities/index.html
http://kuring.me/post/capability/?highlight=capabilities
传统的unix权限模型将进程分为root用户进程(有效用户id为0)和普通用户进程。普通用户需要root权限的某些功能,通常通过setuid系统调用实现。但普通用户并不需要root的所有权限,可能仅仅需要修改系统时间的权限而已。这种粗放的权限管理方式势必会带来一定的安全隐患。
capability用于分割root用户的权限,将root的权限分割为不同的能力,每一种能力代表一定的特权操作。
例如CAP_SYS_MODULE用于表示用户加载内核模块的特权操作。根据进程具有的能力来进行特权操作的访问控制。
Capabilites作为线程的属性存在,虽然linux下又对进程和线程不怎么区分
这样一来,权限检查的过程就变成了:在执行特权操作时,如果线程的有效身份不是 root,就去检查其是否具有该特权操作所对应的 capabilities,并以此为依据,决定是否可以执行特权操作。
capabilities类别
| Capability 名称 | 说明 |
|---|---|
| CAP_AUDIT_CONTROL | 启用和禁用内核审计;改变审计过滤规则;检索审计状态和过滤规则 |
| CAP_AUDIT_READ | 允许通过 multicast netlink 套接字读取审计日志 |
| CAP_AUDIT_WRITE | 将记录写入内核审计日志 |
| CAP_BLOCK_SUSPEND | 使用可以阻止系统挂起的特性 |
| CAP_CHOWN | 修改文件所有者的权限 |
| CAP_DAC_OVERRIDE | 忽略文件的 DAC 访问限制 |
| CAP_DAC_READ_SEARCH | 忽略文件读及目录搜索的 DAC 访问限制 |
| CAP_FOWNER | 忽略文件属主 ID 必须和进程用户 ID 相匹配的限制 |
| CAP_FSETID | 允许设置文件的 setuid 位 |
| CAP_IPC_LOCK | 允许锁定共享内存片段 |
| CAP_IPC_OWNER | 忽略 IPC 所有权检查 |
| CAP_KILL | 允许对不属于自己的进程发送信号 |
| CAP_LEASE | 允许修改文件锁的 FL_LEASE 标志 |
| CAP_LINUX_IMMUTABLE | 允许修改文件的 IMMUTABLE 和 APPEND 属性标志 |
| CAP_MAC_ADMIN | 允许 MAC 配置或状态更改 |
| CAP_MAC_OVERRIDE | 覆盖 MAC(Mandatory Access Control) |
| CAP_MKNOD | 允许使用 mknod() 系统调用 |
| CAP_NET_ADMIN | 允许执行网络管理任务 |
| CAP_NET_BIND_SERVICE | 允许绑定到小于 1024 的端口 |
| CAP_NET_BROADCAST | 允许网络广播和多播访问 |
| CAP_NET_RAW | 允许使用原始套接字 |
| CAP_SETGID | 允许改变进程的 GID |
| CAP_SETFCAP | 允许为文件设置任意的 capabilities |
| CAP_SETPCAP | 参考capabilities man page |
| CAP_SETUID | 允许改变进程的 UID |
| CAP_SYS_ADMIN | 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等 |
| CAP_SYS_BOOT | 允许重新启动系统 |
| CAP_SYS_CHROOT | 允许使用 chroot() 系统调用 |
| CAP_SYS_MODULE | 允许插入和删除内核模块 |
| CAP_SYS_NICE | 允许提升优先级及设置其他进程的优先级 |
| CAP_SYS_PACCT | 允许执行进程的 BSD 式审计 |
| CAP_SYS_PTRACE | 允许跟踪任何进程 |
| CAP_SYS_RAWIO | 允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块设备 |
| CAP_SYS_RESOURCE | 忽略资源限制 |
| CAP_SYS_TIME | 允许改变系统时钟 |
| CAP_SYS_TTY_CONFIG | 允许配置 TTY 设备 |
| CAP_SYSLOG | 允许使用 syslog() 系统调用 |
| CAP_WAKE_ALARM | 允许触发一些能唤醒系统的东西(比如 CLOCK_BOOTTIME_ALARM 计时器) |
程序和文件的capabilities
-
每个进程拥有以下几组Capabilities set
- Effective:cap_effective: 进程当前可用的能力集
- Inheritable:cap_inheritable: 进程可以传递给子进程的能力集
- Permitted:cap_permitted: 进程可拥有的最大能力集
- Ambient:cap_ambient: Linux 4.3后引入的能力集
- Bounding:cap_bounding: 用于进一步限制能力的获取
-
对于可执行程序文件的Capabilities set
- Permitted
- Inheritable
- Effective:Effective 只是一个 bit。如果设置为开启,那么在执行 execve 函数后,Permitted 集合中新增的 capabilities 会自动出现在进程的 Effective 集合中。
capabilities命令使用示例
apt install libcap2-bin -y
相关命令:
getcap:获取指定文件的 Capabilitiessetcap:给文件设置 Capabilitiesgetpcaps用于获取进程所具有的能力。
aha@ubuntu20:~/桌面$ sudo getcap /usr/bin/chown
# 将chown命令授权给普通用户也具备更改文件owner的能力
# 其中eip分别代表cap_effective(e) cap_inheritable(i) cap_permitted(p)
aha@ubuntu20:~/桌面$ sudo setcap cap_chown=eip /usr/bin/chown
aha@ubuntu20:~/桌面$ sudo getcap /usr/bin/chown
/usr/bin/chown = cap_chown+eip
# 使用root创建测试文件
aha@ubuntu20:~/桌面$ sudo touch /tmp/a.txt
aha@ubuntu20:~/桌面$ echo "hello" > /tmp/a.txt
bash: /tmp/a.txt: 权限不够
# 普通用户也可以修改root用户创建文件的owner了(正常情况下chown需要sudo一下)
aha@ubuntu20:~/桌面$ chown aha:aha /tmp/a.txt
aha@ubuntu20:~/桌面$ echo "hello" > /tmp/a.txt
aha@ubuntu20:~/桌面$ cat /tmp/a.txt
hello
# 清除chown的能力
aha@ubuntu20:~/桌面$ sudo setcap -r /usr/bin/chown
aha@ubuntu20:~/桌面$ sudo getcap /usr/bin/chown
可以通过/proc/${pid}/status文件中的CapInh CapPrm CapEff CapBnd CapAmb来表示,每个字段为8个字节即64bit,每个比特表示一种能力,这几个字段存放在进程的内核数据结构task_struct中,由此可见capability的最小单位为线程,而不是进程。
# 查看进程的Cap
$ cat /proc/$$/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
使用 capsh 解码
$ capsh --decode=000001ffffffffff
WARNING: libcap needs an update (cap=40 should have a name).
0x000001ffffffffff=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,38,39,40
代码示例:设置进程capabilities
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#undef _POSIX_SOURCE
#include <sys/capability.h>
extern int errno;
void whoami(void){
printf("uid=%i euid=%i gid=%i\n", getuid(), geteuid(), getgid());
}
void listCaps(){
cap_t caps = cap_get_proc();
ssize_t y = 0;
printf("The process %d was give capabilities %s\n",(int) getpid(), cap_to_text(caps, &y));
fflush(0);
cap_free(caps);
}
int main(int argc, char **argv){
whoami();
//int stat = setuid(geteuid());
pid_t parentPid = getpid();
printf("parentPid: %d\n", parentPid);
if(!parentPid){
return 1;
}
cap_t caps = cap_init();
// 给进程增加5种能力
cap_value_t capList[5] ={ CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID,CAP_SETPCAP } ;
unsigned num_caps = 5;
cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);
cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);
cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);
if (cap_set_proc(caps)) {
perror("capset()");
return EXIT_FAILURE;
}
listCaps();
// 将进程的能力清除
printf("dropping caps\n");
cap_clear(caps); // resetting caps storage
if (cap_set_proc(caps)) {
perror("capset()");
return EXIT_FAILURE;
}
listCaps();
cap_free(caps);
return 0;
}
$ gcc a.c -o a -lcap
$ sudo ./a # 普通用户不能给进程设置能力
uid=0 euid=0 gid=0
parentPid: 30322
The process 30322 was give capabilities = cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw+eip
dropping caps
The process 30322 was give capabilities =
代码示例:查看进程capabilities
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#undef _POSIX_SOURCE
#include <sys/capability.h>
int main(){
struct __user_cap_header_struct cap_header_data;
cap_user_header_t cap_header = &cap_header_data;
struct __user_cap_data_struct cap_data_data;
cap_user_data_t cap_data = &cap_data_data;
cap_header->pid = getpid();
cap_header->version = _LINUX_CAPABILITY_VERSION_1;
if (capget(cap_header, cap_data) < 0) {
perror("Failed capget");
exit(1);
}
printf("Cap data 0x%x, 0x%x, 0x%x\n", cap_data->effective,cap_data->permitted, cap_data->inheritable);
return 0;
}
$ ./d # 普通用户默认情况下没有任何能力
Cap data 0x0, 0x0, 0x0
$ sudo ./d # root用户默认拥有所有的能力
Cap data 0xffffffff, 0xffffffff, 0x0
docker中使用capabilities
https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
$ docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container
Options:
...
--cap-add list Add Linux capabilities
--cap-drop list Drop Linux capabilities
...
--privileged Give extended privileges to this container
...
列出的Capabilities分两类
- 一是
Docker默认给容器添加的,我们可以通过--cap-drop去除其中一个或者多个 - 二是
Docker默认删除的,我们可以通过--cap-add添加其中一个或者多个
可以使用
--privileged赋予容器所有的capabilities
特权提升cap_sys_nice示例
容器缺省不支持cap_sys_nice,所以无法改变nice值
# docker exec -it test1 /bin/sh
/ # renice -n -9 1
renice: setpriority: Permission denied
通过--cap-add给容器增加cap_sys_nice特权集
# docker run --name test2 -td --cap-add=cap_sys_nice busybox /bin/httpd -f
# docker exec -it test2 /bin/sh
/ # renice -n -9 -p 1
/ #
在宿主机上查看nice值,发现已经修改为-9,test1的nice值还是0
# ps -eo "%p %c %n" | grep httpd
16371 httpd 0
21056 httpd -9
kubernetes使用capabilities
Kubernetes 配置 Capabilities 通过 spec.containers.sercurityContext.capabilities 配置 add 和 drop 配置
apiVersion: v1
kind: Pod
metadata:
name: p-1
spec:
containers:
- name: p-1
image: busybox
args:
- sleep
- "3600"
securityContext:
capabilities:
add:
- NET_ADMIN
drop:
- KILL