Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.11] add support for seccomp flags #57

Draft
wants to merge 9 commits into
base: rhaos-4.11
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ jobs:
- uses: actions/checkout@v3
- name: vars
run: |
echo 'VERSION=v0.7.2' >> $GITHUB_ENV
echo 'VERSION=v0.8.0' >> $GITHUB_ENV
echo 'BASEURL=https://github.com/koalaman/shellcheck/releases/download' >> $GITHUB_ENV
echo 'SHA256SUM=12ee2e0b90a3d1e9cae24ac9b2838be66b48573cb2c8e8f3c566b959df6f050c' >> $GITHUB_ENV
echo 'SHA256SUM=f4bce23c11c3919c1b20bcb0f206f6b44c44e26f2bc95f8aa708716095fa0651' >> $GITHUB_ENV
echo ~/bin >> $GITHUB_PATH
- name: install shellcheck
run: |
Expand Down
10 changes: 6 additions & 4 deletions features.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ var featuresCommand = cli.Command{

if seccomp.Enabled {
feat.Linux.Seccomp = &features.Seccomp{
Enabled: &tru,
Actions: seccomp.KnownActions(),
Operators: seccomp.KnownOperators(),
Archs: seccomp.KnownArchs(),
Enabled: &tru,
Actions: seccomp.KnownActions(),
Operators: seccomp.KnownOperators(),
Archs: seccomp.KnownArchs(),
KnownFlags: seccomp.KnownFlags(),
SupportedFlags: seccomp.SupportedFlags(),
}
major, minor, patch := seccomp.Version()
feat.Annotations[features.AnnotationLibseccompVersion] = fmt.Sprintf("%d.%d.%d", major, minor, patch)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/godbus/dbus/v5 v5.0.6
github.com/moby/sys/mountinfo v0.5.0
github.com/mrunalp/fileutils v0.5.0
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b
github.com/opencontainers/selinux v1.10.0
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646
github.com/sirupsen/logrus v1.8.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b h1:udwtfS44rxYE/ViMLchHQBjfE60GZSB1arY7BFbyxLs=
github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
13 changes: 7 additions & 6 deletions libcontainer/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ type IDMap struct {
// for syscalls. Additional architectures can be added by specifying them in
// Architectures.
type Seccomp struct {
DefaultAction Action `json:"default_action"`
Architectures []string `json:"architectures"`
Syscalls []*Syscall `json:"syscalls"`
DefaultErrnoRet *uint `json:"default_errno_ret"`
ListenerPath string `json:"listener_path,omitempty"`
ListenerMetadata string `json:"listener_metadata,omitempty"`
DefaultAction Action `json:"default_action"`
Architectures []string `json:"architectures"`
Flags []specs.LinuxSeccompFlag `json:"flags"`
Syscalls []*Syscall `json:"syscalls"`
DefaultErrnoRet *uint `json:"default_errno_ret"`
ListenerPath string `json:"listener_path,omitempty"`
ListenerMetadata string `json:"listener_metadata,omitempty"`
}

// Action is taken upon rule match in Seccomp
Expand Down
33 changes: 33 additions & 0 deletions libcontainer/seccomp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"sort"

"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
)

var operators = map[string]configs.Operator{
Expand Down Expand Up @@ -110,3 +111,35 @@ func ConvertStringToArch(in string) (string, error) {
}
return "", fmt.Errorf("string %s is not a valid arch for seccomp", in)
}

// List of flags known to this version of runc.
var flags = []string{
"SECCOMP_FILTER_FLAG_TSYNC",
string(specs.LinuxSeccompFlagSpecAllow),
string(specs.LinuxSeccompFlagLog),
}

// KnownFlags returns the list of the known filter flags.
// Used by `runc features`.
func KnownFlags() []string {
return flags
}

// SupportedFlags returns the list of the supported filter flags.
// This list may be a subset of one returned by KnownFlags due to
// some flags not supported by the current kernel and/or libseccomp.
// Used by `runc features`.
func SupportedFlags() []string {
if !Enabled {
return nil
}

var res []string
for _, flag := range flags {
if FlagSupported(specs.LinuxSeccompFlag(flag)) == nil {
res = append(res, flag)
}
}

return res
}
17 changes: 15 additions & 2 deletions libcontainer/seccomp/patchbpf/enosys_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ const uintptr_t C_SET_MODE_FILTER = SECCOMP_SET_MODE_FILTER;
#endif
const uintptr_t C_FILTER_FLAG_LOG = SECCOMP_FILTER_FLAG_LOG;

#ifndef SECCOMP_FILTER_FLAG_SPEC_ALLOW
# define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2)
#endif
const uintptr_t C_FILTER_FLAG_SPEC_ALLOW = SECCOMP_FILTER_FLAG_SPEC_ALLOW;

#ifndef SECCOMP_FILTER_FLAG_NEW_LISTENER
# define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
#endif
Expand Down Expand Up @@ -629,8 +634,13 @@ func filterFlags(config *configs.Seccomp, filter *libseccomp.ScmpFilter) (flags
flags |= uint(C.C_FILTER_FLAG_LOG)
}
}

// TODO: Support seccomp flags not yet added to libseccomp-golang...
if apiLevel >= 4 {
if ssb, err := filter.GetSSB(); err != nil {
return 0, false, fmt.Errorf("unable to fetch SECCOMP_FILTER_FLAG_SPEC_ALLOW bit: %w", err)
} else if ssb {
flags |= uint(C.C_FILTER_FLAG_SPEC_ALLOW)
}
}

for _, call := range config.Syscalls {
if call.Action == configs.Notify {
Expand All @@ -643,6 +653,9 @@ func filterFlags(config *configs.Seccomp, filter *libseccomp.ScmpFilter) (flags
}

func sysSeccompSetFilter(flags uint, filter []unix.SockFilter) (fd int, err error) {
// This debug output is validated in tests/integration/seccomp.bats
// by the SECCOMP_FILTER_FLAG_* test.
logrus.Debugf("seccomp filter flags: %d", flags)
fprog := unix.SockFprog{
Len: uint16(len(filter)),
Filter: &filter[0],
Expand Down
69 changes: 69 additions & 0 deletions libcontainer/seccomp/seccomp_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/seccomp/patchbpf"
"github.com/opencontainers/runtime-spec/specs-go"
)

var (
Expand Down Expand Up @@ -86,6 +87,13 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
}
}

// Add extra flags.
for _, flag := range config.Flags {
if err := setFlag(filter, flag); err != nil {
return -1, err
}
}

// Unset no new privs bit
if err := filter.SetNoNewPrivsBit(false); err != nil {
return -1, fmt.Errorf("error setting no new privileges: %w", err)
Expand All @@ -110,6 +118,67 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
return seccompFd, nil
}

type unknownFlagError struct {
flag specs.LinuxSeccompFlag
}

func (e *unknownFlagError) Error() string {
return "seccomp flag " + string(e.flag) + " is not known to runc"
}

func setFlag(filter *libseccomp.ScmpFilter, flag specs.LinuxSeccompFlag) error {
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.
return nil
case specs.LinuxSeccompFlagLog:
if err := filter.SetLogBit(true); err != nil {
return fmt.Errorf("error adding log flag to seccomp filter: %w", err)
}
return nil
case specs.LinuxSeccompFlagSpecAllow:
if err := filter.SetSSB(true); err != nil {
return fmt.Errorf("error adding SSB flag to seccomp filter: %w", err)
}
return nil
}
// NOTE when adding more flags above, do not forget to also:
// - add new flags to `flags` array in config.go;
// - add new flags to tests/integration/seccomp.bats flags test;
// - modify func filterFlags in patchbpf/ accordingly.

return &unknownFlagError{flag: flag}
}

// FlagSupported checks if the flag is known to runc and supported by
// currently used libseccomp and kernel (i.e. it can be set).
func FlagSupported(flag specs.LinuxSeccompFlag) error {
filter := &libseccomp.ScmpFilter{}
err := setFlag(filter, flag)

// For flags we don't know, setFlag returns unknownFlagError.
var uf *unknownFlagError
if errors.As(err, &uf) {
return err
}
// For flags that are known to runc and libseccomp-golang but can not
// be applied because either libseccomp or the kernel is too old,
// seccomp.VersionError is returned.
var verErr *libseccomp.VersionError
if errors.As(err, &verErr) {
// Not supported by libseccomp or the kernel.
return err
}

// All other flags are known and supported.
return nil
}

// Convert Libcontainer Action to Libseccomp ScmpAction
func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
switch act {
Expand Down
6 changes: 6 additions & 0 deletions libcontainer/seccomp/seccomp_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"

"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
)

var ErrSeccompNotEnabled = errors.New("seccomp: config provided but seccomp not supported")
Expand All @@ -19,6 +20,11 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
return -1, nil
}

// FlagSupported tells if a provided seccomp flag is supported.
func FlagSupported(_ specs.LinuxSeccompFlag) error {
return ErrSeccompNotEnabled
}

// Version returns major, minor, and micro.
func Version() (uint, uint, uint) {
return 0, 0, 0
Expand Down
24 changes: 19 additions & 5 deletions libcontainer/specconv/spec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1015,14 +1015,28 @@ 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.
if config.Flags == nil {
// No flags are set explicitly (not even the empty set);
// set the default of specs.LinuxSeccompFlagSpecAllow,
// if it is supported by the libseccomp and the kernel.
if err := seccomp.FlagSupported(specs.LinuxSeccompFlagSpecAllow); err == nil {
newConfig.Flags = []specs.LinuxSeccompFlag{specs.LinuxSeccompFlagSpecAllow}
}
} else {
// Fail early if some flags are unknown or unsupported.
for _, flag := range config.Flags {
if err := seccomp.FlagSupported(flag); err != nil {
return nil, err
}
newConfig.Flags = append(newConfig.Flags, flag)
}
}

if len(config.Architectures) > 0 {
newConfig.Architectures = []string{}
for _, arch := range config.Architectures {
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/events.bats
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ function teardown() {
teardown_bundle
}

# shellcheck disable=SC2030
@test "events --stats" {
# XXX: currently cgroups require root containers.
requires root
Expand Down Expand Up @@ -38,6 +39,7 @@ function test_events() {
fi

runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
# shellcheck disable=SC2031
[ "$status" -eq 0 ]

# Spawn two subshels:
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ TESTDATA="${INTEGRATION_ROOT}/testdata"
# Kernel version
KERNEL_VERSION="$(uname -r)"
KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
KERNEL_MINOR="${KERNEL_VERSION#$KERNEL_MAJOR.}"
KERNEL_MINOR="${KERNEL_VERSION#"$KERNEL_MAJOR".}"
KERNEL_MINOR="${KERNEL_MINOR%%.*}"

ARCH=$(uname -m)
Expand Down
Loading