Skip to content

Commit 90e49e9

Browse files
committed
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 <[email protected]>
1 parent a647a34 commit 90e49e9

File tree

6 files changed

+118
-31
lines changed

6 files changed

+118
-31
lines changed

features.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,12 @@ var featuresCommand = cli.Command{
5959

6060
if seccomp.Enabled {
6161
feat.Linux.Seccomp = &features.Seccomp{
62-
Enabled: &tru,
63-
Actions: seccomp.KnownActions(),
64-
Operators: seccomp.KnownOperators(),
65-
Archs: seccomp.KnownArchs(),
62+
Enabled: &tru,
63+
Actions: seccomp.KnownActions(),
64+
Operators: seccomp.KnownOperators(),
65+
Archs: seccomp.KnownArchs(),
66+
KnownFlags: seccomp.KnownFlags(),
67+
SupportedFlags: seccomp.SupportedFlags(),
6668
}
6769
major, minor, patch := seccomp.Version()
6870
feat.Annotations[features.AnnotationLibseccompVersion] = fmt.Sprintf("%d.%d.%d", major, minor, patch)

libcontainer/seccomp/config.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"sort"
66

77
"github.com/opencontainers/runc/libcontainer/configs"
8+
"github.com/opencontainers/runtime-spec/specs-go"
89
)
910

1011
var operators = map[string]configs.Operator{
@@ -111,3 +112,33 @@ func ConvertStringToArch(in string) (string, error) {
111112
}
112113
return "", fmt.Errorf("string %s is not a valid arch for seccomp", in)
113114
}
115+
116+
// List of flags known to this version of runc.
117+
var flags = []string{
118+
"SECCOMP_FILTER_FLAG_TSYNC",
119+
string(specs.LinuxSeccompFlagSpecAllow),
120+
string(specs.LinuxSeccompFlagLog),
121+
}
122+
123+
// KnownFlags returns the list of the known filter flags.
124+
// Used by `runc features`.
125+
func KnownFlags() []string {
126+
return flags
127+
}
128+
129+
// KnownFlags returns the list of the supported filter flags.
130+
// Used by `runc features`.
131+
func SupportedFlags() []string {
132+
if !Enabled {
133+
return nil
134+
}
135+
136+
var res []string
137+
for _, flag := range flags {
138+
if FlagSupported(specs.LinuxSeccompFlag(flag)) == nil {
139+
res = append(res, flag)
140+
}
141+
}
142+
143+
return res
144+
}

libcontainer/seccomp/seccomp_linux.go

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -87,27 +87,10 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
8787
}
8888
}
8989

90-
// Add extra flags
90+
// Add extra flags.
9191
for _, flag := range config.Flags {
92-
switch flag {
93-
case "SECCOMP_FILTER_FLAG_TSYNC":
94-
// libseccomp-golang always use filterAttrTsync when
95-
// possible so all goroutines will receive the same
96-
// rules, so there is nothing to do. It does not make
97-
// sense to apply the seccomp filter on only one
98-
// thread; other threads will be terminated after exec
99-
// anyway.
100-
case specs.LinuxSeccompFlagLog:
101-
if err := filter.SetLogBit(true); err != nil {
102-
return -1, fmt.Errorf("error adding log flag to seccomp filter: %w", err)
103-
}
104-
case specs.LinuxSeccompFlagSpecAllow:
105-
if err := filter.SetSSB(true); err != nil {
106-
return -1, fmt.Errorf("error adding SSB flag to seccomp filter: %w", err)
107-
}
108-
// NOTE when adding more flags, make sure to also modify filterFlags in patchbpf.
109-
default:
110-
return -1, fmt.Errorf("seccomp flags %q not yet supported by runc", flag)
92+
if err := setFlag(filter, flag); err != nil {
93+
return -1, err
11194
}
11295
}
11396

@@ -135,6 +118,67 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
135118
return seccompFd, nil
136119
}
137120

121+
type unknownFlagError struct {
122+
flag specs.LinuxSeccompFlag
123+
}
124+
125+
func (e *unknownFlagError) Error() string {
126+
return "seccomp flag " + string(e.flag) + " is not known to runc"
127+
}
128+
129+
func setFlag(filter *libseccomp.ScmpFilter, flag specs.LinuxSeccompFlag) error {
130+
switch flag {
131+
case "SECCOMP_FILTER_FLAG_TSYNC":
132+
// libseccomp-golang always use filterAttrTsync when
133+
// possible so all goroutines will receive the same
134+
// rules, so there is nothing to do. It does not make
135+
// sense to apply the seccomp filter on only one
136+
// thread; other threads will be terminated after exec
137+
// anyway.
138+
return nil
139+
case specs.LinuxSeccompFlagLog:
140+
if err := filter.SetLogBit(true); err != nil {
141+
return fmt.Errorf("error adding log flag to seccomp filter: %w", err)
142+
}
143+
return nil
144+
case specs.LinuxSeccompFlagSpecAllow:
145+
if err := filter.SetSSB(true); err != nil {
146+
return fmt.Errorf("error adding SSB flag to seccomp filter: %w", err)
147+
}
148+
return nil
149+
}
150+
// NOTE when adding more flags above, do not forget to also:
151+
// - add new flags to `flags` array in config.go;
152+
// - add new flags to tests/integration/seccomp.bats flags test;
153+
// - modify func filterFlags in patchbpf/ accordingly.
154+
155+
return &unknownFlagError{flag: flag}
156+
}
157+
158+
// FlagSupported checks if the flag is known to runc and supported by
159+
// currently used libseccomp and kernel (i.e. it can be set).
160+
func FlagSupported(flag specs.LinuxSeccompFlag) error {
161+
filter := &libseccomp.ScmpFilter{}
162+
err := setFlag(filter, flag)
163+
164+
// For flags we don't know, setFlag returns unknownFlagError.
165+
var uf *unknownFlagError
166+
if errors.As(err, &uf) {
167+
return err
168+
}
169+
// For flags that are known to runc and libseccomp-golang but can not
170+
// be applied because either libseccomp or the kernel is too old,
171+
// seccomp.VersionError is returned.
172+
var verErr *libseccomp.VersionError
173+
if errors.As(err, &verErr) {
174+
// Not supported by libseccomp or the kernel.
175+
return err
176+
}
177+
178+
// All other flags are known and supported.
179+
return nil
180+
}
181+
138182
// Convert Libcontainer Action to Libseccomp ScmpAction
139183
func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
140184
switch act {

libcontainer/seccomp/seccomp_unsupported.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88

99
"github.com/opencontainers/runc/libcontainer/configs"
10+
"github.com/opencontainers/runtime-spec/specs-go"
1011
)
1112

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

23+
// FlagSupported tells if a provided seccomp flag is supported.
24+
func FlagSupported(_ specs.LinuxSeccompFlag) error {
25+
return ErrSeccompNotEnabled
26+
}
27+
2228
// Version returns major, minor, and micro.
2329
func Version() (uint, uint, uint) {
2430
return 0, 0, 0

libcontainer/specconv/spec_linux.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,14 +1023,10 @@ func SetupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) {
10231023
// The list of flags defined in runtime-spec is a subset of the flags
10241024
// in the seccomp() syscall
10251025
for _, flag := range config.Flags {
1026-
switch flag {
1027-
case "SECCOMP_FILTER_FLAG_TSYNC":
1028-
// Tsync can be silently ignored
1029-
case specs.LinuxSeccompFlagLog, specs.LinuxSeccompFlagSpecAllow:
1030-
newConfig.Flags = append(newConfig.Flags, flag)
1031-
default:
1032-
return nil, fmt.Errorf("seccomp flag %q not yet supported by runc", flag)
1026+
if err := seccomp.FlagSupported(flag); err != nil {
1027+
return nil, err
10331028
}
1029+
newConfig.Flags = append(newConfig.Flags, flag)
10341030
}
10351031

10361032
if len(config.Architectures) > 0 {

types/features/features.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ type Seccomp struct {
6060
// Archs is the list of the recognized archs, e.g., "SCMP_ARCH_X86_64".
6161
// Nil value means "unknown", not "no support for any arch".
6262
Archs []string `json:"archs,omitempty"`
63+
64+
// KnownFlags is the list of the recognized filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
65+
// Nil value means "unknown", not "no support for any flag".
66+
KnownFlags []string `json:"known_flags,omitempty"`
67+
68+
// Flags is the list of the supported filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
69+
// Nil value means "unknown", not "no support for any flag".
70+
SupportedFlags []string `json:"supported_flags,omitempty"`
6371
}
6472

6573
// Apparmor represents the "apparmor" field.

0 commit comments

Comments
 (0)