mirror of
https://github.com/opencontainers/runc.git
synced 2025-10-04 23:23:09 +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 {
|
if seccomp.Enabled {
|
||||||
feat.Linux.Seccomp = &features.Seccomp{
|
feat.Linux.Seccomp = &features.Seccomp{
|
||||||
Enabled: &tru,
|
Enabled: &tru,
|
||||||
Actions: seccomp.KnownActions(),
|
Actions: seccomp.KnownActions(),
|
||||||
Operators: seccomp.KnownOperators(),
|
Operators: seccomp.KnownOperators(),
|
||||||
Archs: seccomp.KnownArchs(),
|
Archs: seccomp.KnownArchs(),
|
||||||
|
KnownFlags: seccomp.KnownFlags(),
|
||||||
|
SupportedFlags: seccomp.SupportedFlags(),
|
||||||
}
|
}
|
||||||
major, minor, patch := seccomp.Version()
|
major, minor, patch := seccomp.Version()
|
||||||
feat.Annotations[features.AnnotationLibseccompVersion] = fmt.Sprintf("%d.%d.%d", major, minor, patch)
|
feat.Annotations[features.AnnotationLibseccompVersion] = fmt.Sprintf("%d.%d.%d", major, minor, patch)
|
||||||
|
@@ -5,8 +5,13 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"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{
|
var operators = map[string]configs.Operator{
|
||||||
"SCMP_CMP_NE": configs.NotEqualTo,
|
"SCMP_CMP_NE": configs.NotEqualTo,
|
||||||
"SCMP_CMP_LT": configs.LessThan,
|
"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)
|
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)
|
flags |= uint(C.C_FILTER_FLAG_SPEC_ALLOW)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// XXX: add newly supported filter flags above this line.
|
||||||
|
|
||||||
for _, call := range config.Syscalls {
|
for _, call := range config.Syscalls {
|
||||||
if call.Action == configs.Notify {
|
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 {
|
for _, flag := range config.Flags {
|
||||||
switch flag {
|
if err := setFlag(filter, flag); err != nil {
|
||||||
case "SECCOMP_FILTER_FLAG_TSYNC":
|
return -1, err
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +132,67 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
|
|||||||
return seccompFd, nil
|
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
|
// Convert Libcontainer Action to Libseccomp ScmpAction
|
||||||
func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
|
func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
|
||||||
switch act {
|
switch act {
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrSeccompNotEnabled = errors.New("seccomp: config provided but seccomp not supported")
|
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
|
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.
|
// Version returns major, minor, and micro.
|
||||||
func Version() (uint, uint, uint) {
|
func Version() (uint, uint, uint) {
|
||||||
return 0, 0, 0
|
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
|
// The list of flags defined in runtime-spec is a subset of the flags
|
||||||
// in the seccomp() syscall
|
// in the seccomp() syscall
|
||||||
for _, flag := range config.Flags {
|
for _, flag := range config.Flags {
|
||||||
switch flag {
|
if err := seccomp.FlagSupported(flag); err != nil {
|
||||||
case "SECCOMP_FILTER_FLAG_TSYNC":
|
return nil, err
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
newConfig.Flags = append(newConfig.Flags, flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.Architectures) > 0 {
|
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".
|
// Archs is the list of the recognized archs, e.g., "SCMP_ARCH_X86_64".
|
||||||
// Nil value means "unknown", not "no support for any arch".
|
// Nil value means "unknown", not "no support for any arch".
|
||||||
Archs []string `json:"archs,omitempty"`
|
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.
|
// Apparmor represents the "apparmor" field.
|
||||||
|
Reference in New Issue
Block a user