vArmor中的ptrace阻断功能实现分析
source link: https://blog.spoock.com/2023/09/07/eBPF-vArmor-ptrace/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
vArmor中的ptrace阻断功能实现分析
最近发现vArmor-ebpf
增加一个pstrace
的功能,学习其实现原理。具体参见 ptrace-confine
v_ptrace
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, u64); // rule: permissions + flags
__uint(max_entries, OUTER_MAP_ENTRIES_MAX);
} v_ptrace SEC(".maps");
static __always_inline u64 *get_ptrace_rule(u32 mnt_ns) {
return bpf_map_lookup_elem(&v_ptrace, &mnt_ns);
}
定义了v_ptrace
用来保存ptrace
的规则。
函数get_ptrace_rule
获取对应的命名空间的ptrace
的规则。
ptrace_access_check
SEC("lsm/ptrace_access_check")
int BPF_PROG(varmor_ptrace_access_check, struct task_struct *child, unsigned int mode) {
// Retrieve the current task
struct task_struct *current = (struct task_struct *)bpf_get_current_task();
u32 current_mnt_ns = get_task_mnt_ns_id(current);
u32 child_mnt_ns = get_task_mnt_ns_id(child);
// Whether the current task has ptrace access control rule
u64 *rule = get_ptrace_rule(current_mnt_ns);
if (rule != 0) {
DEBUG_PRINT("================ lsm/ptrace_access_check ================");
if (!ptrace_permission_check(current_mnt_ns, child_mnt_ns, *rule, (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ : AA_PTRACE_TRACE))
return -EPERM;
}
// Whether the child task has ptrace access control rule
// We allow tasks from the init mnt ns by default
rule = get_ptrace_rule(child_mnt_ns);
if (current_mnt_ns != init_mnt_ns && rule != 0) {
DEBUG_PRINT("================ lsm/ptrace_access_check ================");
if (!ptrace_permission_check(current_mnt_ns, child_mnt_ns, *rule, (mode & PTRACE_MODE_READ) ? AA_MAY_BE_READ : AA_MAY_BE_TRACED))
return -EPERM;
}
return 0;
}
有关ptrace_access_check
原函数的实现:
static int apparmor_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
struct aa_label *tracer, *tracee;
int error;
tracer = __begin_current_label_crit_section();
tracee = aa_get_task_label(child);
error = aa_may_ptrace(tracer, tracee,
(mode & PTRACE_MODE_READ) ? AA_PTRACE_READ
: AA_PTRACE_TRACE);
aa_put_label(tracee);
__end_current_label_crit_section(tracer);
return error;
}
针对mode
的值,以下是可能的选项及其含义:
PTRACE_MODE_READ
(0x01):表示读取另一个任务的状态。PTRACE_MODE_ATTACH
(0x02):表示附加到另一个任务。
所以通过(mode & PTRACE_MODE_READ) ? AA_PTRACE_READ : AA_PTRACE_TRACE)
,就可以知道当前pstrace
操作类型。
varmor_ptrace_access_check函数根据传入的ptrace访问规则和请求的权限,对当前任务和child任务的挂载命名空间进行比较,并根据规则中的标志位决定是否允许访问。这个函数用于在
ptrace_access_check函数中进行权限检查,以确定是否允许使用
ptrace`系统调用来追踪或读取另一个任务的状态。
所以关键实现是看ptrace_permission_check
函数具体实现。
ptrace_permission_check
#define PRECISE_MATCH 0x00000001
#define GREEDY_MATCH 0x00000002
static __always_inline bool ptrace_permission_check(u32 current_mnt_ns, u32 child_mnt_ns, u64 rule, u32 request_permission) {
DEBUG_PRINT("current task(mnt ns: %u) request the vArmor ptrace permission(0x%x) of child task(mnt ns: %u)",
current_mnt_ns, request_permission, child_mnt_ns);
u32 permissions = rule >> 32;
u32 flags = (u32)(rule & 0xffffffff);
if (permissions & request_permission) {
// deny all tasks
if (flags & GREEDY_MATCH) {
DEBUG_PRINT("access denied");
return false;
}
// only deny tasks outside the container
if (flags & PRECISE_MATCH && current_mnt_ns != child_mnt_ns) {
DEBUG_PRINT("access denied");
return false;
}
}
DEBUG_PRINT("access allowed");
return true;
}
通过
rule >> 32
和(u32)(rule & 0xffffffff)
,分别得到permissions
和flags
。permissions & request_permission
如果在permissions
中包含了request_permission
的权限,则进一步判断。- 如果标志位(
flags
)中包含了GREEDY_MATCH
,表示拒绝所有任务,函数返回false
表示访问被拒绝。 - 如果标志位中包含了
PRECISE_MATCH
,表示只拒绝容器外的任务,那么如果当前任务的挂载命名空间ID与child任务的挂载命名空间ID不相等,函数返回false
表示访问被拒绝。 - 如果以上判断都通过,表示访问被允许,函数返回
true
。
这个函数根据传入的ptrace访问规则和请求的权限,对当前任务和child任务的挂载命名空间进行比较,并根据规则中的标志位决定是否允许访问。这个函数用于在ptrace_access_check
函数中进行权限检查,以确定是否允许使用ptrace
系统调用来追踪或读取另一个任务的状态。
用户态实现
PtraceContent
在apis/varmor/v1beta1/armorprofile_types.go
中添加了相关的规则。
定义了PtraceContent
结构体,包含了Permissions
和Flags
,对应内核态判断标准得两个参数。
BpfContent
结构体是所有规则类型的合集,在其中增加了PtraceContent
结构体,后面就可以通过BpfContent
设置pstrace
的规则。
pkg/lsm/bpfenforcer/profile.go
将pstrace相关的规则添加到V_ptrace
规则列表中。
在添加规则时通过rule := uint64(bpfContent.Ptrace.)<<32 + uint64(bpfContent.Ptrace.Flags)
将Permissions
和Flags
合并成为rule
,这种方式和内核态中代码是对应的。
u32 permissions = rule >> 32;
u32 flags = (u32)(rule & 0xffffffff);
generateRawPtraceRule
internal/profile/bpf/bpf.go
中增加了generateRawPtraceRule()
函数方法。
这个函数是将varmor.PtraceRule
转换为varmor.BpfContent
格式。两者对应的格式是:
type PtraceRule struct {
// StrictMode is used to indicate whether to restrict ptrace permissions for all source and destination processes.
// If set to false, it restricts ptrace permissions only for processes in other containers.
// If set to true, it restricts ptrace permissions for all processes (except those within the init mnt namespace)
StrictMode bool `json:"strictMode,omitempty"`
// Permissions are used to indicate which ptrace-related permissions of the target container should be restricted.
// Available values: trace, traceby, read, readby.
//
// trace, traceby
// For "write" operations, or other operations that are more dangerous, such as: ptrace attaching (PTRACE_ATTACH) to
// another process or calling process_vm_writev(2).
// read, readby
// For "read" operations or other operations that are less dangerous, such as: get_robust_list(2); kcmp(2); reading
// /proc/pid/auxv, /proc/pid/environ, or /proc/pid/stat; or readlink(2) of a /proc/pid/ns/* file.
Permissions []string `json:"permissions,omitempty"`
}
type PtraceContent struct {
Permissions uint32 `json:"permissions"`
Flags uint32 `json:"flags"`
}
将permission
类型(trace
,read
,traceby
,readby
)转换为AaPtraceTrace
等类型的数字。
将rule
类型(StrictMode
和其他模式)转换为GreedyMatch
(0x00000002
)和PreciseMatch
(0x00000001
)
所以,当规则设定使用字符串表示,函数generateRawPtraceRule()
转换为对应的数字类型,方便后面转换为rule
。
org_varmorpolicies
crd.varmor.org_varmorpolicies.yaml
中也增加了相关的规则格式和说明
ptrace:
properties:
permissions:
description: "Permissions are used to indicate which
ptrace-related permissions of the target container
should be restricted. Available values: trace, traceby,
read, readby. \n trace, traceby For \"write\" operations,
or other operations that are more dangerous, such
as: ptrace attaching (PTRACE_ATTACH) to another
process or calling process_vm_writev(2). read, readby
For \"read\" operations or other operations that
are less dangerous, such as: get_robust_list(2);
kcmp(2); reading /proc/pid/auxv, /proc/pid/environ,
or /proc/pid/stat; or readlink(2) of a /proc/pid/ns/*
file."
items:
type: string
type: array
strictMode:
description: StrictMode is used to indicate whether
to restrict ptrace permissions for all source and
destination processes. If set to false, it restricts
ptrace permissions only for processes in other containers.
If set to true, it restricts ptrace permissions
for all processes (except those within the init
mnt namespace)
type: boolean
type: object
crd.varmor.org_varmorpolicies.yaml
定义了一个 Kubernetes 自定义资源定义(Custom Resource Definition,CRD),用于扩展 Kubernetes API 并引入名为 varmorpolicies.crd.varmor.org
的新资源类型。
properties
定义了两个属性,分别是permissions
和strictMode
permissions
是一个字符串数组类型,可选值包括trace, traceby,read, readbystrictMode
是布尔类型
这个配置的设置和前面在generateRawPtraceRule()
函数中解析PtraceRule
就对应上了。
通过 ptrace-confine中添加了ptrace
功能的相关实现,可以很好地了解在用户态对于规则的解析和运用。
https://github.com/bytedance/vArmor-ebpf/commit/b539731a641283dbb48ff1e7e569fe521b717d41
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK