Skip to content

Commit e712b35

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 cb64773 commit e712b35

File tree

7 files changed

+127
-31
lines changed

7 files changed

+127
-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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ import (
55
"sort"
66

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

11+
// flagTsync is recognized but ignored by runc, and it is not defined
12+
// in the runtime-spec.
13+
const flagTsync = "SECCOMP_FILTER_FLAG_TSYNC"
14+
1015
var operators = map[string]configs.Operator{
1116
"SCMP_CMP_NE": configs.NotEqualTo,
1217
"SCMP_CMP_LT": configs.LessThan,
@@ -111,3 +116,35 @@ func ConvertStringToArch(in string) (string, error) {
111116
}
112117
return "", fmt.Errorf("string %s is not a valid arch for seccomp", in)
113118
}
119+
120+
// List of flags known to this version of runc.
121+
var flags = []string{
122+
flagTsync,
123+
string(specs.LinuxSeccompFlagSpecAllow),
124+
string(specs.LinuxSeccompFlagLog),
125+
}
126+
127+
// KnownFlags returns the list of the known filter flags.
128+
// Used by `runc features`.
129+
func KnownFlags() []string {
130+
return flags
131+
}
132+
133+
// SupportedFlags returns the list of the supported filter flags.
134+
// This list may be a subset of one returned by KnownFlags due to
135+
// some flags not supported by the current kernel and/or libseccomp.
136+
// Used by `runc features`.
137+
func SupportedFlags() []string {
138+
if !Enabled {
139+
return nil
140+
}
141+
142+
var res []string
143+
for _, flag := range flags {
144+
if FlagSupported(specs.LinuxSeccompFlag(flag)) == nil {
145+
res = append(res, flag)
146+
}
147+
}
148+
149+
return res
150+
}

libcontainer/seccomp/patchbpf/enosys_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ func filterFlags(config *configs.Seccomp, filter *libseccomp.ScmpFilter) (flags
643643
flags |= uint(C.C_FILTER_FLAG_SPEC_ALLOW)
644644
}
645645
}
646+
// XXX: add newly supported filter flags above this line.
646647

647648
for _, call := range config.Syscalls {
648649
if call.Action == configs.Notify {

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

@@ -149,6 +132,67 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
149132
return seccompFd, nil
150133
}
151134

135+
type unknownFlagError struct {
136+
flag specs.LinuxSeccompFlag
137+
}
138+
139+
func (e *unknownFlagError) Error() string {
140+
return "seccomp flag " + string(e.flag) + " is not known to runc"
141+
}
142+
143+
func setFlag(filter *libseccomp.ScmpFilter, flag specs.LinuxSeccompFlag) error {
144+
switch flag {
145+
case flagTsync:
146+
// libseccomp-golang always use filterAttrTsync when
147+
// possible so all goroutines will receive the same
148+
// rules, so there is nothing to do. It does not make
149+
// sense to apply the seccomp filter on only one
150+
// thread; other threads will be terminated after exec
151+
// anyway.
152+
return nil
153+
case specs.LinuxSeccompFlagLog:
154+
if err := filter.SetLogBit(true); err != nil {
155+
return fmt.Errorf("error adding log flag to seccomp filter: %w", err)
156+
}
157+
return nil
158+
case specs.LinuxSeccompFlagSpecAllow:
159+
if err := filter.SetSSB(true); err != nil {
160+
return fmt.Errorf("error adding SSB flag to seccomp filter: %w", err)
161+
}
162+
return nil
163+
}
164+
// NOTE when adding more flags above, do not forget to also:
165+
// - add new flags to `flags` slice in config.go;
166+
// - add new flags to tests/integration/seccomp.bats flags test;
167+
// - modify func filterFlags in patchbpf/ accordingly.
168+
169+
return &unknownFlagError{flag: flag}
170+
}
171+
172+
// FlagSupported checks if the flag is known to runc and supported by
173+
// currently used libseccomp and kernel (i.e. it can be set).
174+
func FlagSupported(flag specs.LinuxSeccompFlag) error {
175+
filter := &libseccomp.ScmpFilter{}
176+
err := setFlag(filter, flag)
177+
178+
// For flags we don't know, setFlag returns unknownFlagError.
179+
var uf *unknownFlagError
180+
if errors.As(err, &uf) {
181+
return err
182+
}
183+
// For flags that are known to runc and libseccomp-golang but can not
184+
// be applied because either libseccomp or the kernel is too old,
185+
// seccomp.VersionError is returned.
186+
var verErr *libseccomp.VersionError
187+
if errors.As(err, &verErr) {
188+
// Not supported by libseccomp or the kernel.
189+
return err
190+
}
191+
192+
// All other flags are known and supported.
193+
return nil
194+
}
195+
152196
// Convert Libcontainer Action to Libseccomp ScmpAction
153197
func getAction(act configs.Action, errnoRet *uint) (libseccomp.ScmpAction, error) {
154198
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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ 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 flags are recognized".
66+
KnownFlags []string `json:"knownFlags,omitempty"`
67+
68+
// SupportedFlags is the list of the supported filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
69+
// This list may be a subset of KnownFlags due to some flags
70+
// not supported by the current kernel and/or libseccomp.
71+
// Nil value means "unknown", not "no flags are supported".
72+
SupportedFlags []string `json:"supportedFlags,omitempty"`
6373
}
6474

6575
// Apparmor represents the "apparmor" field.

0 commit comments

Comments
 (0)