mirror of
https://github.com/opencontainers/runc.git
synced 2025-10-03 22:56:47 +08:00
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:
10
features.go
10
features.go
@@ -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)
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user