Skip to content

Commit 8fc8e9e

Browse files
committed
Harden security check in openat2 syscall handler.
When sysbox traps openat2 and lowers security checks on it, ensure it only does this when the file is in fact on a sysbox-fs (FUSE) mount. Signed-off-by: Cesar Talledo <cesar.talledo@docker.com>
1 parent 0043004 commit 8fc8e9e

4 files changed

Lines changed: 36 additions & 15 deletions

File tree

domain/nsenter.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,12 @@ type GidInfoRespPayload struct {
291291
}
292292

293293
type Openat2SyscallPayload struct {
294-
Header NSenterMsgHeader `json:"header"`
295-
Path string `json:"path"`
296-
Flags uint64 `json:"flags"`
297-
Mode uint64 `json:"mode"`
298-
Resolve uint64 `json:"resolve"`
294+
Header NSenterMsgHeader `json:"header"`
295+
Path string `json:"path"`
296+
Flags uint64 `json:"flags"`
297+
Mode uint64 `json:"mode"`
298+
Resolve uint64 `json:"resolve"`
299+
CheckForSysboxfs bool `json:"verifyfs"`
299300
}
300301

301302
type Openat2RespPayload struct {

mount/service.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,12 @@ var SysfsMounts = []string{
4848
"/sys/module/nf_conntrack/parameters",
4949
}
5050

51-
// IsSysboxfsMount checks if the given path is at or under a sysbox-fs mount
51+
// IsSysboxfsMount checks if the given path is at or under a sysbox-fs mount.
52+
//
53+
// Note: this is just a simple string prefix check. A check for actual mountpoints
54+
// is done by the nsenter agent when handling openat2 requests (since it must be
55+
// done inside the container's mount ns).
5256
func IsSysboxfsMount(path string) bool {
53-
54-
// TODO: don't just check by path name; check that the given path is in fact
55-
// on a sysbox-fs managed mount. This likely requires dispatching an nsenter
56-
// agent to the container's mount ns to perform the check.
57-
5857
for _, mountPath := range ProcfsMounts {
5958
if strings.HasPrefix(path, mountPath+"/") || path == mountPath {
6059
return true

nsenter/event.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,26 @@ func (e *NSenterEvent) processOpenat2SyscallRequest(pipe *os.File) (int, error)
15791579

15801580
p := e.ReqMsg.Payload.(domain.Openat2SyscallPayload)
15811581

1582+
// If requested, verify that the target file resides on sysbox-fs
1583+
if p.CheckForSysboxfs {
1584+
var statfs unix.Statfs_t
1585+
if err := unix.Statfs(p.Path, &statfs); err != nil {
1586+
e.ResMsg = &domain.NSenterMessage{
1587+
Type: domain.ErrorResponse,
1588+
Payload: &fuse.IOerror{RcvError: fmt.Errorf("failed to stat filesystem for %s: %v", p.Path, err)},
1589+
}
1590+
return -1, nil
1591+
}
1592+
1593+
if statfs.Type != unix.FUSE_SUPER_MAGIC { // sysbox-fs is a FUSE filesystem
1594+
e.ResMsg = &domain.NSenterMessage{
1595+
Type: domain.ErrorResponse,
1596+
Payload: &fuse.IOerror{RcvError: fmt.Errorf("file %s is not on sysbox-fs", p.Path)},
1597+
}
1598+
return -1, nil
1599+
}
1600+
}
1601+
15821602
// Adjust nsenter personality (uid/gid and capabilities) to match
15831603
// the original process performing the syscall. This is needed to
15841604
// ensure proper permission checks when opening the file.

seccomp/openat2.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,11 @@ func (si *openat2SyscallInfo) processOpenat2() (*sysResponse, error) {
153153
Cwd: si.cwd,
154154
Capabilities: si.caps,
155155
},
156-
Path: fullPath,
157-
Flags: cleanFlags,
158-
Mode: si.mode,
159-
Resolve: cleanResolve,
156+
Path: fullPath,
157+
Flags: cleanFlags,
158+
Mode: si.mode,
159+
Resolve: cleanResolve,
160+
CheckForSysboxfs: true, // ensure the openat2 is opening a sysbox-fs managed file
160161
}
161162

162163
nss := t.service.nss

0 commit comments

Comments
 (0)