Skip to content

Commit 520702d

Browse files
committed
Add runc features command
Fix issue 3274 See `types/features/features.go`. Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
1 parent 6ff0420 commit 520702d

File tree

9 files changed

+301
-0
lines changed

9 files changed

+301
-0
lines changed

features.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/opencontainers/runc/libcontainer/capabilities"
8+
"github.com/opencontainers/runc/libcontainer/configs"
9+
"github.com/opencontainers/runc/libcontainer/seccomp"
10+
"github.com/opencontainers/runc/libcontainer/specconv"
11+
"github.com/opencontainers/runc/types/features"
12+
"github.com/opencontainers/runtime-spec/specs-go"
13+
"github.com/urfave/cli"
14+
)
15+
16+
var featuresCommand = cli.Command{
17+
Name: "features",
18+
Usage: "show the enabled features",
19+
ArgsUsage: "",
20+
Description: `Show the enabled features.
21+
The result is parsable as a JSON.
22+
See https://pkg.go.dev/github.com/opencontainers/runc/types/features for the type definition.
23+
`,
24+
Action: func(context *cli.Context) error {
25+
if err := checkArgs(context, 0, exactArgs); err != nil {
26+
return err
27+
}
28+
29+
tru := true
30+
31+
feat := features.Features{
32+
OCIVersionMin: "1.0.0",
33+
OCIVersionMax: specs.Version,
34+
Annotations: map[string]string{
35+
features.AnnotationRuncVersion: version,
36+
features.AnnotationRuncCommit: gitCommit,
37+
features.AnnotationRuncCheckpointEnabled: "true",
38+
},
39+
Hooks: configs.KnownHookNames(),
40+
MountOptions: specconv.KnownMountOptions(),
41+
Linux: &features.Linux{
42+
Namespaces: specconv.KnownNamespaces(),
43+
Capabilities: capabilities.KnownCapabilities(),
44+
Cgroup: &features.Cgroup{
45+
V1: &tru,
46+
V2: &tru,
47+
Systemd: &tru,
48+
SystemdUser: &tru,
49+
},
50+
Apparmor: &features.Apparmor{
51+
Enabled: &tru,
52+
},
53+
Selinux: &features.Selinux{
54+
Enabled: &tru,
55+
},
56+
},
57+
}
58+
59+
if seccomp.Enabled {
60+
feat.Linux.Seccomp = &features.Seccomp{
61+
Enabled: &tru,
62+
Actions: seccomp.KnownActions(),
63+
Operators: seccomp.KnownOperators(),
64+
Archs: seccomp.KnownArchs(),
65+
}
66+
major, minor, patch := seccomp.Version()
67+
feat.Annotations[features.AnnotationLibseccompVersion] = fmt.Sprintf("%d.%d.%d", major, minor, patch)
68+
}
69+
70+
enc := json.NewEncoder(context.App.Writer)
71+
enc.SetIndent("", " ")
72+
return enc.Encode(feat)
73+
},
74+
}

libcontainer/capabilities/capabilities.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ func init() {
3535
}
3636
}
3737

38+
// KnownCapabilities returns the list of the known capabilities.
39+
// Used by `runc features`.
40+
func KnownCapabilities() []string {
41+
list := capability.List()
42+
res := make([]string, len(list))
43+
for i, c := range list {
44+
res[i] = "CAP_" + strings.ToUpper(c.String())
45+
}
46+
return res
47+
}
48+
3849
// New creates a new Caps from the given Capabilities config. Unknown Capabilities
3950
// or Capabilities that are unavailable in the current environment are ignored,
4051
// printing a warning instead.

libcontainer/configs/config.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,19 @@ const (
251251
Poststop HookName = "poststop"
252252
)
253253

254+
// KnownHookNames returns the known hook names.
255+
// Used by `runc features`.
256+
func KnownHookNames() []string {
257+
return []string{
258+
string(Prestart), // deprecated
259+
string(CreateRuntime),
260+
string(CreateContainer),
261+
string(StartContainer),
262+
string(Poststart),
263+
string(Poststop),
264+
}
265+
}
266+
254267
type Capabilities struct {
255268
// Bounding is the set of capabilities checked by the kernel.
256269
Bounding []string

libcontainer/seccomp/config.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package seccomp
22

33
import (
44
"fmt"
5+
"sort"
56

67
"github.com/opencontainers/runc/libcontainer/configs"
78
)
@@ -16,6 +17,17 @@ var operators = map[string]configs.Operator{
1617
"SCMP_CMP_MASKED_EQ": configs.MaskEqualTo,
1718
}
1819

20+
// KnownOperators returns the list of the known operations.
21+
// Used by `runc features`.
22+
func KnownOperators() []string {
23+
var res []string
24+
for k := range operators {
25+
res = append(res, k)
26+
}
27+
sort.Strings(res)
28+
return res
29+
}
30+
1931
var actions = map[string]configs.Action{
2032
"SCMP_ACT_KILL": configs.Kill,
2133
"SCMP_ACT_ERRNO": configs.Errno,
@@ -26,6 +38,17 @@ var actions = map[string]configs.Action{
2638
"SCMP_ACT_NOTIFY": configs.Notify,
2739
}
2840

41+
// KnownActions returns the list of the known actions.
42+
// Used by `runc features`.
43+
func KnownActions() []string {
44+
var res []string
45+
for k := range actions {
46+
res = append(res, k)
47+
}
48+
sort.Strings(res)
49+
return res
50+
}
51+
2952
var archs = map[string]string{
3053
"SCMP_ARCH_X86": "x86",
3154
"SCMP_ARCH_X86_64": "amd64",
@@ -45,6 +68,17 @@ var archs = map[string]string{
4568
"SCMP_ARCH_S390X": "s390x",
4669
}
4770

71+
// KnownArchs returns the list of the known archs.
72+
// Used by `runc features`.
73+
func KnownArchs() []string {
74+
var res []string
75+
for k := range archs {
76+
res = append(res, k)
77+
}
78+
sort.Strings(res)
79+
return res
80+
}
81+
4882
// ConvertStringToOperator converts a string into a Seccomp comparison operator.
4983
// Comparison operators use the names they are assigned by Libseccomp's header.
5084
// Attempting to convert a string that is not a valid operator results in an

libcontainer/seccomp/seccomp_linux.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,6 @@ func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall, defAct libs
265265
func Version() (uint, uint, uint) {
266266
return libseccomp.GetLibraryVersion()
267267
}
268+
269+
// Enabled is true if seccomp support is compiled in.
270+
const Enabled = true

libcontainer/seccomp/seccomp_unsupported.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
2323
func Version() (uint, uint, uint) {
2424
return 0, 0, 0
2525
}
26+
27+
// Enabled is true if seccomp support is compiled in.
28+
const Enabled = false

libcontainer/specconv/spec_linux.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"os"
99
"path/filepath"
10+
"sort"
1011
"strings"
1112
"sync"
1213
"time"
@@ -105,6 +106,38 @@ func initMaps() {
105106
})
106107
}
107108

109+
// KnownNamespaces returns the list of the known namespaces.
110+
// Used by `runc features`.
111+
func KnownNamespaces() []string {
112+
initMaps()
113+
var res []string
114+
for k := range namespaceMapping {
115+
res = append(res, string(k))
116+
}
117+
sort.Strings(res)
118+
return res
119+
}
120+
121+
// KnownMountOptions returns the list of the known mount options.
122+
// Used by `runc features`.
123+
func KnownMountOptions() []string {
124+
initMaps()
125+
var res []string
126+
for k := range mountFlags {
127+
res = append(res, k)
128+
}
129+
for k := range mountPropagationMapping {
130+
if k != "" {
131+
res = append(res, k)
132+
}
133+
}
134+
for k := range extensionFlags {
135+
res = append(res, k)
136+
}
137+
sort.Strings(res)
138+
return res
139+
}
140+
108141
// AllowedDevices is the set of devices which are automatically included for
109142
// all containers.
110143
//

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ func main() {
132132
startCommand,
133133
stateCommand,
134134
updateCommand,
135+
featuresCommand,
135136
}
136137
app.Before = func(context *cli.Context) error {
137138
if !context.IsSet("root") && xdgRuntimeDir != "" {

types/features/features.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Package features provides the JSON structure that is printed by `runc features` (since runc v1.1.0).
2+
package features
3+
4+
// Features represents the supported features of the runtime.
5+
type Features struct {
6+
// OCIVersionMin is the minimum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.0".
7+
OCIVersionMin string `json:"ociVersionMin,omitempty"`
8+
9+
// OCIVersionMax is the maximum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.2-dev".
10+
OCIVersionMax string `json:"ociVersionMax,omitempty"`
11+
12+
// Hooks is the list of the recognized hook names, e.g., "createRuntime".
13+
// Nil value means "unknown", not "no support for any hook".
14+
Hooks []string `json:"hooks,omitempty"`
15+
16+
// MountOptions is the list of the recognized mount options, e.g., "ro".
17+
// Nil value means "unknown", not "no support for any mount option".
18+
MountOptions []string `json:"mountOptions,omitempty"`
19+
20+
// Linux is specific to Linux.
21+
Linux *Linux `json:"linux,omitempty"`
22+
23+
// Annotations contains implementation-specific annotation strings,
24+
// such as the implementation version, and third-party extensions.
25+
Annotations map[string]string `json:"annotations,omitempty"`
26+
}
27+
28+
// Linux is specific to Linux.
29+
type Linux struct {
30+
// Namespaces is the list of the recognized namespaces, e.g., "mount".
31+
// Nil value means "unknown", not "no support for any namespace".
32+
Namespaces []string `json:"namespaces,omitempty"`
33+
34+
// Capabilities is the list of the recognized capabilities , e.g., "CAP_SYS_ADMIN".
35+
// Nil value means "unknown", not "no support for any capability".
36+
Capabilities []string `json:"capabilities,omitempty"`
37+
38+
Cgroup *Cgroup `json:"cgroup,omitempty"`
39+
Seccomp *Seccomp `json:"seccomp,omitempty"`
40+
Apparmor *Apparmor `json:"apparmor,omitempty"`
41+
Selinux *Selinux `json:"selinux,omitempty"`
42+
}
43+
44+
// Seccomp represents the "seccomp" field.
45+
type Seccomp struct {
46+
// Enabled is true if seccomp support is compiled in.
47+
// Nil value means "unknown", not "false".
48+
Enabled *bool `json:"enabled,omitempty"`
49+
50+
// Actions is the list of the recognized actions, e.g., "SCMP_ACT_NOTIFY".
51+
// Nil value means "unknown", not "no support for any action".
52+
Actions []string `json:"actions,omitempty"`
53+
54+
// Operators is the list of the recognized actions, e.g., "SCMP_CMP_NE".
55+
// Nil value means "unknown", not "no support for any operator".
56+
Operators []string `json:"operators,omitempty"`
57+
58+
// Operators is the list of the recognized archs, e.g., "SCMP_ARCH_X86_64".
59+
// Nil value means "unknown", not "no support for any arch".
60+
Archs []string `json:"archs,omitempty"`
61+
}
62+
63+
// Apparmor represents the "apparmor" field.
64+
type Apparmor struct {
65+
// Enabled is true if AppArmor support is compiled in.
66+
// Unrelated to whether the host supports AppArmor or not.
67+
// Nil value means "unknown", not "false".
68+
// Always true in the current version of runc.
69+
Enabled *bool `json:"enabled,omitempty"`
70+
}
71+
72+
// Selinux represents the "selinux" field.
73+
type Selinux struct {
74+
// Enabled is true if SELinux support is compiled in.
75+
// Unrelated to whether the host supports SELinux or not.
76+
// Nil value means "unknown", not "false".
77+
// Always true in the current version of runc.
78+
Enabled *bool `json:"enabled,omitempty"`
79+
}
80+
81+
// Cgroup represents the "cgroup" field.
82+
type Cgroup struct {
83+
// V1 represents whether Cgroup v1 support is compiled in.
84+
// Unrelated to whether the host uses cgroup v1 or not.
85+
// Nil value means "unknown", not "false".
86+
// Always true in the current version of runc.
87+
V1 *bool `json:"v1,omitempty"`
88+
89+
// V2 represents whether Cgroup v2 support is compiled in.
90+
// Unrelated to whether the host uses cgroup v2 or not.
91+
// Nil value means "unknown", not "false".
92+
// Always true in the current version of runc.
93+
V2 *bool `json:"v2,omitempty"`
94+
95+
// Systemd represents whether systemd-cgroup support is compiled in.
96+
// Unrelated to whether the host uses systemd or not.
97+
// Nil value means "unknown", not "false".
98+
// Always true in the current version of runc.
99+
Systemd *bool `json:"systemd,omitempty"`
100+
101+
// SystemdUser represents whether user-scoped systemd-cgroup support is compiled in.
102+
// Unrelated to whether the host uses systemd or not.
103+
// Nil value means "unknown", not "false".
104+
// Always true in the current version of runc.
105+
SystemdUser *bool `json:"systemdUser,omitempty"`
106+
}
107+
108+
const (
109+
// AnnotationRuncVersion represents the version of runc, e.g., "1.2.3", "1.2.3+dev", "1.2.3-rc.4.", "1.2.3-rc.4+dev".
110+
// Third party implementations such as crun and runsc MAY use this annotation to report the most compatible runc version,
111+
// however, parsing this annotation value is discouraged.
112+
AnnotationRuncVersion = "org.opencontainers.runc.version"
113+
114+
// AnnotationRuncCommit corresponds to the output of `git describe --dirty --long --always` in the runc repo.
115+
// Third party implementations such as crun and runsc SHOULD NOT use this annotation, as their repo is different from the runc repo.
116+
// Parsing this annotation value is discouraged.
117+
AnnotationRuncCommit = "org.opencontainers.runc.commit"
118+
119+
// AnnotationRuncCheckpointEnabled is set to "true" if CRIU-based checkpointing is supported.
120+
// Unrelated to whether the host supports CRIU or not.
121+
// Always set to "true" in the current version of runc.
122+
// This is defined as an annotation because checkpointing is a runc-specific feature that is not defined in the OCI Runtime Spec.
123+
// Third party implementations such as crun and runsc MAY use this annotation.
124+
AnnotationRuncCheckpointEnabled = "org.opencontainers.runc.checkpoint.enabled"
125+
126+
// AnnotationLibseccompVersion is the version of libseccomp, e.g., "2.5.1".
127+
// Note that the runtime MAY support seccomp even when this annotation is not present.
128+
AnnotationLibseccompVersion = "io.github.seccomp.libseccomp.version"
129+
)

0 commit comments

Comments
 (0)