runc features: add seccomp filter flags

Amend runc features to print seccomp flags. Two set of flags are added:
 * known flags are those that this version of runc is aware of;
 * supported flags are those that can be set; normally, this is the same
   set as known flags, but due to older version of kernel and/or
   libseccomp, some known flags might be unsupported.

This commit also consolidates three different switch statements dealing
with flags into one, in func setFlag. A note is added to this function
telling what else to look for when adding new flags.

Unfortunately, it also adds a list of known flags, that should be
kept in sync with the switch statement.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Kir Kolyshkin
2022-09-02 12:20:54 -07:00
parent ab8480893e
commit 076745a40f
7 changed files with 127 additions and 31 deletions

View File

@@ -63,6 +63,8 @@ var featuresCommand = cli.Command{
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)

View File

@@ -5,8 +5,13 @@ import (
"sort"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
)
// flagTsync is recognized but ignored by runc, and it is not defined
// in the runtime-spec.
const flagTsync = "SECCOMP_FILTER_FLAG_TSYNC"
var operators = map[string]configs.Operator{
"SCMP_CMP_NE": configs.NotEqualTo,
"SCMP_CMP_LT": configs.LessThan,
@@ -111,3 +116,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{
flagTsync,
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
}

View File

@@ -643,6 +643,7 @@ func filterFlags(config *configs.Seccomp, filter *libseccomp.ScmpFilter) (flags
flags |= uint(C.C_FILTER_FLAG_SPEC_ALLOW)
}
}
// XXX: add newly supported filter flags above this line.
for _, call := range config.Syscalls {
if call.Action == configs.Notify {

View File

@@ -87,27 +87,10 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
}
}
// Add extra flags
// 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 specs.LinuxSeccompFlagLog:
if err := filter.SetLogBit(true); err != nil {
return -1, fmt.Errorf("error adding log flag to seccomp filter: %w", err)
}
case specs.LinuxSeccompFlagSpecAllow:
if err := filter.SetSSB(true); err != nil {
return -1, fmt.Errorf("error adding SSB flag to seccomp filter: %w", err)
}
// NOTE when adding more flags, make sure to also modify filterFlags in patchbpf.
default:
return -1, fmt.Errorf("seccomp flags %q not yet supported by runc", flag)
if err := setFlag(filter, flag); err != nil {
return -1, err
}
}
@@ -149,6 +132,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 flagTsync:
// 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` slice 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 {

View File

@@ -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")
@@ -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

View File

@@ -1026,14 +1026,10 @@ func SetupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) {
// 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 specs.LinuxSeccompFlagLog, specs.LinuxSeccompFlagSpecAllow:
newConfig.Flags = append(newConfig.Flags, flag)
default:
return nil, fmt.Errorf("seccomp flag %q not yet supported by runc", flag)
if err := seccomp.FlagSupported(flag); err != nil {
return nil, err
}
newConfig.Flags = append(newConfig.Flags, flag)
}
if len(config.Architectures) > 0 {

View File

@@ -60,6 +60,16 @@ type Seccomp struct {
// Archs is the list of the recognized archs, e.g., "SCMP_ARCH_X86_64".
// Nil value means "unknown", not "no support for any arch".
Archs []string `json:"archs,omitempty"`
// KnownFlags is the list of the recognized filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
// Nil value means "unknown", not "no flags are recognized".
KnownFlags []string `json:"knownFlags,omitempty"`
// SupportedFlags is the list of the supported filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
// This list may be a subset of KnownFlags due to some flags
// not supported by the current kernel and/or libseccomp.
// Nil value means "unknown", not "no flags are supported".
SupportedFlags []string `json:"supportedFlags,omitempty"`
}
// Apparmor represents the "apparmor" field.