Skip to content

Commit b539731

Browse files
authored
Merge pull request #1 from bytedance/ptrace-confine
support ptrace enforcement
2 parents 01a8f43 + 9250ded commit b539731

File tree

8 files changed

+217
-40
lines changed

8 files changed

+217
-40
lines changed

pkg/bpfenforcer/bpf/enforcer.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@
1010
#include "file.h"
1111
#include "process.h"
1212
#include "network.h"
13+
#include "ptrace.h"
1314

1415
char __license[] SEC("license") = "GPL";
1516

17+
// Save the mnt ns id of init task
18+
volatile const u32 init_mnt_ns;
19+
1620
// Tail call map (program array) initialized with program pointers.
1721
struct {
1822
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
@@ -269,3 +273,30 @@ int BPF_PROG(varmor_socket_connect, struct socket *sock, struct sockaddr *addres
269273
// Iterate all rules in the inner map
270274
return iterate_net_inner_map(vnet_inner, address);
271275
}
276+
277+
SEC("lsm/ptrace_access_check")
278+
int BPF_PROG(varmor_ptrace_access_check, struct task_struct *child, unsigned int mode) {
279+
// Retrieve the current task
280+
struct task_struct *current = (struct task_struct *)bpf_get_current_task();
281+
u32 current_mnt_ns = get_task_mnt_ns_id(current);
282+
u32 child_mnt_ns = get_task_mnt_ns_id(child);
283+
284+
// Whether the current task has ptrace access control rule
285+
u64 *rule = get_ptrace_rule(current_mnt_ns);
286+
if (rule != 0) {
287+
DEBUG_PRINT("================ lsm/ptrace_access_check ================");
288+
if (!ptrace_permission_check(current_mnt_ns, child_mnt_ns, *rule, (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ : AA_PTRACE_TRACE))
289+
return -EPERM;
290+
}
291+
292+
// Whether the child task has ptrace access control rule
293+
// We allow tasks from the init mnt ns by default
294+
rule = get_ptrace_rule(child_mnt_ns);
295+
if (current_mnt_ns != init_mnt_ns && rule != 0) {
296+
DEBUG_PRINT("================ lsm/ptrace_access_check ================");
297+
if (!ptrace_permission_check(current_mnt_ns, child_mnt_ns, *rule, (mode & PTRACE_MODE_READ) ? AA_MAY_BE_READ : AA_MAY_BE_TRACED))
298+
return -EPERM;
299+
}
300+
301+
return 0;
302+
}

pkg/bpfenforcer/bpf/enforcer.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
#define FILE_PATH_PATTERN_SIZE_MAX 64
1616
#define BUFFER_MAX 4096*3
1717

18-
#define PRECISE_MATCH 0x1
19-
#define GREEDY_MATCH 0x2
20-
#define PREFIX_MATCH 0x4
21-
#define SUFFIX_MATCH 0x8
22-
#define CIDR_MATCH 0x00000020
23-
#define IPV4_MATCH 0x00000040
24-
#define IPV6_MATCH 0x00000080
25-
#define PORT_MATCH 0x00000100
18+
#define PRECISE_MATCH 0x00000001
19+
#define GREEDY_MATCH 0x00000002
20+
#define PREFIX_MATCH 0x00000004
21+
#define SUFFIX_MATCH 0x00000008
22+
#define CIDR_MATCH 0x00000020
23+
#define IPV4_MATCH 0x00000040
24+
#define IPV6_MATCH 0x00000080
25+
#define PORT_MATCH 0x00000100
2626

2727
#undef container_of
2828
#define container_of(ptr, type, member) \

pkg/bpfenforcer/bpf/perms.h

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,44 @@
55

66
// https://elixir.bootlin.com/linux/v5.10.178/source/tools/include/nolibc/nolibc.h#L446
77
/* fcntl / open */
8-
#define O_RDONLY 0
9-
#define O_WRONLY 1
10-
#define O_RDWR 2
11-
#define O_CREAT 0x40
12-
#define O_EXCL 0x80
13-
#define O_NOCTTY 0x100
14-
#define O_TRUNC 0x200
15-
#define O_APPEND 0x400
16-
#define O_NONBLOCK 0x800
17-
#define O_DIRECTORY 0x10000
8+
#define O_RDONLY 0x00000000
9+
#define O_WRONLY 0x00000001
10+
#define O_RDWR 0x00000002
11+
#define O_CREAT 0x00000040
12+
#define O_EXCL 0x00000080
13+
#define O_NOCTTY 0x00000100
14+
#define O_TRUNC 0x00000200
15+
#define O_APPEND 0x00000400
16+
#define O_NONBLOCK 0x00000800
17+
#define O_DIRECTORY 0x00010000
1818

1919
// https://elixir.bootlin.com/linux/v5.10.178/source/include/linux/fs.h#L95
20-
#define MAY_EXEC 0x00000001
21-
#define MAY_WRITE 0x00000002
22-
#define MAY_READ 0x00000004
23-
#define MAY_APPEND 0x00000008
24-
#define MAY_ACCESS 0x00000010
25-
#define MAY_OPEN 0x00000020
26-
#define MAY_CHDIR 0x00000040
27-
#define FMODE_READ 0x1
28-
#define FMODE_WRITE 0x2
20+
#define MAY_EXEC 0x00000001
21+
#define MAY_WRITE 0x00000002
22+
#define MAY_READ 0x00000004
23+
#define MAY_APPEND 0x00000008
24+
#define MAY_ACCESS 0x00000010
25+
#define MAY_OPEN 0x00000020
26+
#define MAY_CHDIR 0x00000040
27+
#define FMODE_READ 0x00000001
28+
#define FMODE_WRITE 0x00000002
2929

3030
// https://elixir.bootlin.com/linux/v5.10.178/source/security/apparmor/include/perms.h#L16
3131
// https://elixir.bootlin.com/linux/v5.10.178/source/security/apparmor/include/net.h#L72
32-
#define AA_MAY_EXEC MAY_EXEC
33-
#define AA_MAY_WRITE MAY_WRITE
34-
#define AA_MAY_READ MAY_READ
35-
#define AA_MAY_APPEND MAY_APPEND
36-
#define AA_MAY_CREATE 0x0010
37-
#define AA_MAY_RENAME 0x0080
38-
#define AA_MAY_LINK 0x00040000
32+
#define AA_MAY_EXEC MAY_EXEC
33+
#define AA_MAY_WRITE MAY_WRITE
34+
#define AA_MAY_READ MAY_READ
35+
#define AA_MAY_APPEND MAY_APPEND
36+
#define AA_MAY_CREATE 0x00000010
37+
#define AA_MAY_RENAME 0x00000080
38+
#define AA_MAY_LINK 0x00040000
39+
#define AA_PTRACE_TRACE MAY_WRITE
40+
#define AA_PTRACE_READ MAY_READ
41+
#define AA_MAY_BE_TRACED AA_MAY_APPEND
42+
#define AA_MAY_BE_READ AA_MAY_CREATE
43+
44+
// https://elixir.bootlin.com/linux/v5.10.178/source/include/linux/ptrace.h#L62
45+
#define PTRACE_MODE_READ 0x01
46+
#define PTRACE_MODE_ATTACH 0x02
3947

4048
#endif /* __PERMS_H */

pkg/bpfenforcer/bpf/ptrace.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright 2023 vArmor-ebpf Authors
3+
4+
#ifndef __PTRACE_H
5+
#define __PTRACE_H
6+
7+
#include "vmlinux.h"
8+
#include "bpf_helpers.h"
9+
#include "bpf_tracing.h"
10+
#include "bpf_core_read.h"
11+
#include "enforcer.h"
12+
#include "perms.h"
13+
14+
struct {
15+
__uint(type, BPF_MAP_TYPE_HASH);
16+
__type(key, u32);
17+
__type(value, u64); // rule: permissions + flags
18+
__uint(max_entries, OUTER_MAP_ENTRIES_MAX);
19+
} v_ptrace SEC(".maps");
20+
21+
static __always_inline u64 *get_ptrace_rule(u32 mnt_ns) {
22+
return bpf_map_lookup_elem(&v_ptrace, &mnt_ns);
23+
}
24+
25+
static __always_inline bool ptrace_permission_check(u32 current_mnt_ns, u32 child_mnt_ns, u64 rule, u32 request_permission) {
26+
DEBUG_PRINT("current task(mnt ns: %u) request the vArmor ptrace permission(0x%x) of child task(mnt ns: %u)",
27+
current_mnt_ns, request_permission, child_mnt_ns);
28+
29+
u32 permissions = rule >> 32;
30+
u32 flags = (u32)(rule & 0xffffffff);
31+
32+
if (permissions & request_permission) {
33+
// deny all tasks
34+
if (flags & GREEDY_MATCH) {
35+
DEBUG_PRINT("access denied");
36+
return false;
37+
}
38+
39+
// only deny tasks outside the container
40+
if (flags & PRECISE_MATCH && current_mnt_ns != child_mnt_ns) {
41+
DEBUG_PRINT("access denied");
42+
return false;
43+
}
44+
}
45+
46+
DEBUG_PRINT("access allowed");
47+
return true;
48+
}
49+
50+
#endif /* __PTRACE_H */

pkg/bpfenforcer/bpf_bpfel.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/bpfenforcer/bpf_bpfel.o

4.02 KB
Binary file not shown.

pkg/bpfenforcer/enforcer.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import "C"
2121
import (
2222
"fmt"
2323
"net"
24+
"os"
25+
"strconv"
2426
"strings"
2527

2628
"github.com/cilium/ebpf"
@@ -47,6 +49,10 @@ const (
4749
AA_MAY_WRITE = 0x00000002
4850
AA_MAY_READ = 0x00000004
4951
AA_MAY_APPEND = 0x00000008
52+
AA_PTRACE_TRACE = 0x00000002
53+
AA_PTRACE_READ = 0x00000004
54+
AA_MAY_BE_TRACED = 0x00000008
55+
AA_MAY_BE_READ = 0x00000010
5056
)
5157

5258
type bpfPathRule struct {
@@ -72,6 +78,7 @@ type BpfEnforcer struct {
7278
pathRenameLink link.Link
7379
bprmLink link.Link
7480
sockConnLink link.Link
81+
ptraceLink link.Link
7582
log logr.Logger
7683
}
7784

@@ -84,6 +91,27 @@ func NewBpfEnforcer(log logr.Logger) *BpfEnforcer {
8491
return &enforcer
8592
}
8693

94+
func readMntNsID(pid uint32) (uint32, error) {
95+
path := fmt.Sprintf("/proc/%d/ns/mnt", pid)
96+
realPath, err := os.Readlink(path)
97+
if err != nil {
98+
return 0, err
99+
}
100+
101+
index := strings.Index(realPath, "[")
102+
if index == -1 {
103+
return 0, fmt.Errorf(fmt.Sprintf("fatel error: can not parser mnt ns id from: %s", realPath))
104+
}
105+
106+
id := realPath[index+1 : len(realPath)-1]
107+
u64, err := strconv.ParseUint(id, 10, 32)
108+
if err != nil {
109+
return 0, fmt.Errorf(fmt.Sprintf("fatel error: can not transform mnt ns id (%s) to uint64 type", realPath))
110+
}
111+
112+
return uint32(u64), nil
113+
}
114+
87115
func (enforcer *BpfEnforcer) InitEBPF() error {
88116
// Allow the current process to lock memory for eBPF resources.
89117
enforcer.log.Info("remove memory lock")
@@ -124,6 +152,16 @@ func (enforcer *BpfEnforcer) InitEBPF() error {
124152
}
125153
collectionSpec.Maps["v_net_outer"].InnerMap = &netInnerMap
126154

155+
initMntNsId, err := readMntNsID(1)
156+
if err != nil {
157+
return err
158+
}
159+
160+
// Set the mnt ns id to the BPF program
161+
collectionSpec.RewriteConstants(map[string]interface{}{
162+
"init_mnt_ns": initMntNsId,
163+
})
164+
127165
// Load pre-compiled programs and maps into the kernel.
128166
enforcer.log.Info("load ebpf program and maps into the kernel")
129167
err = collectionSpec.LoadAndAssign(&enforcer.objs, nil)
@@ -202,6 +240,14 @@ func (enforcer *BpfEnforcer) StartEnforcing() error {
202240
}
203241
enforcer.sockConnLink = sockConnLink
204242

243+
ptraceLink, err := link.AttachLSM(link.LSMOptions{
244+
Program: enforcer.objs.VarmorPtraceAccessCheck,
245+
})
246+
if err != nil {
247+
return err
248+
}
249+
enforcer.ptraceLink = ptraceLink
250+
205251
enforcer.log.Info("start enforcing")
206252

207253
return nil
@@ -237,6 +283,10 @@ func (enforcer *BpfEnforcer) StopEnforcing() {
237283
if enforcer.sockConnLink != nil {
238284
enforcer.sockConnLink.Close()
239285
}
286+
287+
if enforcer.ptraceLink != nil {
288+
enforcer.ptraceLink.Close()
289+
}
240290
}
241291

242292
func (enforcer *BpfEnforcer) SetCapableMap(mntNsID uint32, capability uint64) error {
@@ -481,3 +531,15 @@ func (enforcer *BpfEnforcer) SetNetMap(mntNsID uint32, networkRule *bpfNetworkRu
481531
func (enforcer *BpfEnforcer) ClearNetMap(mntNsID uint32) error {
482532
return enforcer.objs.V_netOuter.Delete(&mntNsID)
483533
}
534+
535+
func newBpfPtraceRule(permissions uint32, flags uint32) uint64 {
536+
return uint64(permissions)<<32 + uint64(flags)
537+
}
538+
539+
func (enforcer *BpfEnforcer) SetPtraceMap(mntNsID uint32, ptraceRule uint64) error {
540+
return enforcer.objs.V_ptrace.Put(&mntNsID, &ptraceRule)
541+
}
542+
543+
func (enforcer *BpfEnforcer) ClearPtraceMap(mntNsID uint32) error {
544+
return enforcer.objs.V_ptrace.Delete(&mntNsID)
545+
}

0 commit comments

Comments
 (0)