From e3de08fba019d5c11995f7be68f77a16ac8ca190 Mon Sep 17 00:00:00 2001 From: Alban Crequy Date: Tue, 22 Feb 2022 15:58:46 +0100 Subject: [PATCH] seccomp: add support for flags List of seccomp flags defined in runtime-spec: * SECCOMP_FILTER_FLAG_TSYNC * SECCOMP_FILTER_FLAG_LOG * SECCOMP_FILTER_FLAG_SPEC_ALLOW Signed-off-by: Alban Crequy --- libcontainer/configs/config.go | 1 + libcontainer/seccomp/seccomp_linux.go | 23 ++++++++++++++++++++ libcontainer/specconv/spec_linux.go | 18 +++++++++++----- tests/integration/seccomp.bats | 31 +++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 7cf2fb65751..91a3bc3ecac 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -33,6 +33,7 @@ type IDMap struct { type Seccomp struct { DefaultAction Action `json:"default_action"` Architectures []string `json:"architectures"` + Flags []string `json:"flags"` Syscalls []*Syscall `json:"syscalls"` DefaultErrnoRet *uint `json:"default_errno_ret"` ListenerPath string `json:"listener_path,omitempty"` diff --git a/libcontainer/seccomp/seccomp_linux.go b/libcontainer/seccomp/seccomp_linux.go index 8c12af72be9..22258ffa77f 100644 --- a/libcontainer/seccomp/seccomp_linux.go +++ b/libcontainer/seccomp/seccomp_linux.go @@ -86,6 +86,29 @@ func InitSeccomp(config *configs.Seccomp) (int, error) { } } + // Add extra flags + for _, flag := range config.Flags { + switch flag { + case "SECCOMP_FILTER_FLAG_TSYNC": + // libseccomp-golang always use filterAttrTsync when + // possible so all goroutines will receive the same + // rules, so there is nothing to do. It does not make + // sense to apply the seccomp filter on only one + // thread; other threads will be terminated after exec + // anyway. + case "SECCOMP_FILTER_FLAG_LOG": + if err := filter.SetLogBit(true); err != nil { + return -1, fmt.Errorf("error adding log flag to seccomp filter: %w", err) + } + case "SECCOMP_FILTER_FLAG_SPEC_ALLOW": + if err := filter.SetSSB(true); err != nil { + return -1, fmt.Errorf("error adding SSB flag to seccomp filter: %w", err) + } + default: + return -1, fmt.Errorf("seccomp flags %q not yet supported by runc", flag) + } + } + // Unset no new privs bit if err := filter.SetNoNewPrivsBit(false); err != nil { return -1, fmt.Errorf("error setting no new privileges: %w", err) diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 7ff9f098d6a..4b7eb291a04 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -1016,14 +1016,22 @@ func SetupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) { return nil, nil } - // We don't currently support seccomp flags. - if len(config.Flags) != 0 { - return nil, errors.New("seccomp flags are not yet supported by runc") - } - newConfig := new(configs.Seccomp) newConfig.Syscalls = []*configs.Syscall{} + // The list of flags defined in runtime-spec is a subset of the flags + // in the seccomp() syscall + for _, flag := range config.Flags { + switch flag { + case "SECCOMP_FILTER_FLAG_TSYNC": + // Tsync can be silently ignored + case "SECCOMP_FILTER_FLAG_LOG", "SECCOMP_FILTER_FLAG_SPEC_ALLOW": + newConfig.Flags = append(newConfig.Flags, string(flag)) + default: + return nil, fmt.Errorf("seccomp flags %q not yet supported by runc", flag) + } + } + if len(config.Architectures) > 0 { newConfig.Architectures = []string{} for _, arch := range config.Architectures { diff --git a/tests/integration/seccomp.bats b/tests/integration/seccomp.bats index e81beca3357..d1cb6c2ce70 100644 --- a/tests/integration/seccomp.bats +++ b/tests/integration/seccomp.bats @@ -66,6 +66,37 @@ function teardown() { [[ "$output" == *"Network is down"* ]] } +@test "runc run [seccomp] (SECCOMP_FILTER_FLAG_*)" { + # Linux 4.14: SECCOMP_FILTER_FLAG_LOG + # Linux 4.17: SECCOMP_FILTER_FLAG_SPEC_ALLOW + requires_kernel 4.17 + SECCOMP_FILTER_FLAGS=( + '' # no flag + '"SECCOMP_FILTER_FLAG_LOG"' + '"SECCOMP_FILTER_FLAG_SPEC_ALLOW"' + '"SECCOMP_FILTER_FLAG_TSYNC"' + '"SECCOMP_FILTER_FLAG_LOG","SECCOMP_FILTER_FLAG_SPEC_ALLOW"' + '"SECCOMP_FILTER_FLAG_LOG","SECCOMP_FILTER_FLAG_TSYNC"' + '"SECCOMP_FILTER_FLAG_SPEC_ALLOW","SECCOMP_FILTER_FLAG_TSYNC"' + '"SECCOMP_FILTER_FLAG_LOG","SECCOMP_FILTER_FLAG_SPEC_ALLOW","SECCOMP_FILTER_FLAG_TSYNC"' + ) + for flags in "${SECCOMP_FILTER_FLAGS[@]}"; do + update_config ' .process.args = ["/bin/sh", "-c", "mkdir /dev/shm/foo"] + | .process.noNewPrivileges = false + | .linux.seccomp = { + "defaultAction":"SCMP_ACT_ALLOW", + "architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32"], + "flags":['"${flags}"'], + "syscalls":[{"names":["mkdir"], "action":"SCMP_ACT_ERRNO"}] + }' + + # This test checks that the log flag is accepted but does not check + runc run test_busybox + [ "$status" -ne 0 ] + [[ "$output" == *"mkdir:"*"/dev/shm/foo"*"Operation not permitted"* ]] + done +} + @test "runc run [seccomp] (SCMP_ACT_KILL)" { update_config ' .process.args = ["/bin/sh", "-c", "mkdir /dev/shm/foo"] | .process.noNewPrivileges = false