Add runc features command

Fix issue 3274

See `types/features/features.go`.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
Akihiro Suda
2021-11-29 19:34:31 +09:00
parent 6ff042023c
commit 520702dac5
9 changed files with 301 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
package main
import (
"encoding/json"
"fmt"
"github.com/opencontainers/runc/libcontainer/capabilities"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/runc/libcontainer/specconv"
"github.com/opencontainers/runc/types/features"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli"
)
var featuresCommand = cli.Command{
Name: "features",
Usage: "show the enabled features",
ArgsUsage: "",
Description: `Show the enabled features.
The result is parsable as a JSON.
See https://pkg.go.dev/github.com/opencontainers/runc/types/features for the type definition.
`,
Action: func(context *cli.Context) error {
if err := checkArgs(context, 0, exactArgs); err != nil {
return err
}
tru := true
feat := features.Features{
OCIVersionMin: "1.0.0",
OCIVersionMax: specs.Version,
Annotations: map[string]string{
features.AnnotationRuncVersion: version,
features.AnnotationRuncCommit: gitCommit,
features.AnnotationRuncCheckpointEnabled: "true",
},
Hooks: configs.KnownHookNames(),
MountOptions: specconv.KnownMountOptions(),
Linux: &features.Linux{
Namespaces: specconv.KnownNamespaces(),
Capabilities: capabilities.KnownCapabilities(),
Cgroup: &features.Cgroup{
V1: &tru,
V2: &tru,
Systemd: &tru,
SystemdUser: &tru,
},
Apparmor: &features.Apparmor{
Enabled: &tru,
},
Selinux: &features.Selinux{
Enabled: &tru,
},
},
}
if seccomp.Enabled {
feat.Linux.Seccomp = &features.Seccomp{
Enabled: &tru,
Actions: seccomp.KnownActions(),
Operators: seccomp.KnownOperators(),
Archs: seccomp.KnownArchs(),
}
major, minor, patch := seccomp.Version()
feat.Annotations[features.AnnotationLibseccompVersion] = fmt.Sprintf("%d.%d.%d", major, minor, patch)
}
enc := json.NewEncoder(context.App.Writer)
enc.SetIndent("", " ")
return enc.Encode(feat)
},
}
+11
View File
@@ -35,6 +35,17 @@ func init() {
}
}
// KnownCapabilities returns the list of the known capabilities.
// Used by `runc features`.
func KnownCapabilities() []string {
list := capability.List()
res := make([]string, len(list))
for i, c := range list {
res[i] = "CAP_" + strings.ToUpper(c.String())
}
return res
}
// New creates a new Caps from the given Capabilities config. Unknown Capabilities
// or Capabilities that are unavailable in the current environment are ignored,
// printing a warning instead.
+13
View File
@@ -251,6 +251,19 @@ const (
Poststop HookName = "poststop"
)
// KnownHookNames returns the known hook names.
// Used by `runc features`.
func KnownHookNames() []string {
return []string{
string(Prestart), // deprecated
string(CreateRuntime),
string(CreateContainer),
string(StartContainer),
string(Poststart),
string(Poststop),
}
}
type Capabilities struct {
// Bounding is the set of capabilities checked by the kernel.
Bounding []string
+34
View File
@@ -2,6 +2,7 @@ package seccomp
import (
"fmt"
"sort"
"github.com/opencontainers/runc/libcontainer/configs"
)
@@ -16,6 +17,17 @@ var operators = map[string]configs.Operator{
"SCMP_CMP_MASKED_EQ": configs.MaskEqualTo,
}
// KnownOperators returns the list of the known operations.
// Used by `runc features`.
func KnownOperators() []string {
var res []string
for k := range operators {
res = append(res, k)
}
sort.Strings(res)
return res
}
var actions = map[string]configs.Action{
"SCMP_ACT_KILL": configs.Kill,
"SCMP_ACT_ERRNO": configs.Errno,
@@ -26,6 +38,17 @@ var actions = map[string]configs.Action{
"SCMP_ACT_NOTIFY": configs.Notify,
}
// KnownActions returns the list of the known actions.
// Used by `runc features`.
func KnownActions() []string {
var res []string
for k := range actions {
res = append(res, k)
}
sort.Strings(res)
return res
}
var archs = map[string]string{
"SCMP_ARCH_X86": "x86",
"SCMP_ARCH_X86_64": "amd64",
@@ -45,6 +68,17 @@ var archs = map[string]string{
"SCMP_ARCH_S390X": "s390x",
}
// KnownArchs returns the list of the known archs.
// Used by `runc features`.
func KnownArchs() []string {
var res []string
for k := range archs {
res = append(res, k)
}
sort.Strings(res)
return res
}
// ConvertStringToOperator converts a string into a Seccomp comparison operator.
// Comparison operators use the names they are assigned by Libseccomp's header.
// Attempting to convert a string that is not a valid operator results in an
+3
View File
@@ -265,3 +265,6 @@ func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall, defAct libs
func Version() (uint, uint, uint) {
return libseccomp.GetLibraryVersion()
}
// Enabled is true if seccomp support is compiled in.
const Enabled = true
@@ -23,3 +23,6 @@ func InitSeccomp(config *configs.Seccomp) (int, error) {
func Version() (uint, uint, uint) {
return 0, 0, 0
}
// Enabled is true if seccomp support is compiled in.
const Enabled = false
+33
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
@@ -105,6 +106,38 @@ func initMaps() {
})
}
// KnownNamespaces returns the list of the known namespaces.
// Used by `runc features`.
func KnownNamespaces() []string {
initMaps()
var res []string
for k := range namespaceMapping {
res = append(res, string(k))
}
sort.Strings(res)
return res
}
// KnownMountOptions returns the list of the known mount options.
// Used by `runc features`.
func KnownMountOptions() []string {
initMaps()
var res []string
for k := range mountFlags {
res = append(res, k)
}
for k := range mountPropagationMapping {
if k != "" {
res = append(res, k)
}
}
for k := range extensionFlags {
res = append(res, k)
}
sort.Strings(res)
return res
}
// AllowedDevices is the set of devices which are automatically included for
// all containers.
//
+1
View File
@@ -132,6 +132,7 @@ func main() {
startCommand,
stateCommand,
updateCommand,
featuresCommand,
}
app.Before = func(context *cli.Context) error {
if !context.IsSet("root") && xdgRuntimeDir != "" {
+129
View File
@@ -0,0 +1,129 @@
// Package features provides the JSON structure that is printed by `runc features` (since runc v1.1.0).
package features
// Features represents the supported features of the runtime.
type Features struct {
// OCIVersionMin is the minimum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.0".
OCIVersionMin string `json:"ociVersionMin,omitempty"`
// OCIVersionMax is the maximum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.2-dev".
OCIVersionMax string `json:"ociVersionMax,omitempty"`
// Hooks is the list of the recognized hook names, e.g., "createRuntime".
// Nil value means "unknown", not "no support for any hook".
Hooks []string `json:"hooks,omitempty"`
// MountOptions is the list of the recognized mount options, e.g., "ro".
// Nil value means "unknown", not "no support for any mount option".
MountOptions []string `json:"mountOptions,omitempty"`
// Linux is specific to Linux.
Linux *Linux `json:"linux,omitempty"`
// Annotations contains implementation-specific annotation strings,
// such as the implementation version, and third-party extensions.
Annotations map[string]string `json:"annotations,omitempty"`
}
// Linux is specific to Linux.
type Linux struct {
// Namespaces is the list of the recognized namespaces, e.g., "mount".
// Nil value means "unknown", not "no support for any namespace".
Namespaces []string `json:"namespaces,omitempty"`
// Capabilities is the list of the recognized capabilities , e.g., "CAP_SYS_ADMIN".
// Nil value means "unknown", not "no support for any capability".
Capabilities []string `json:"capabilities,omitempty"`
Cgroup *Cgroup `json:"cgroup,omitempty"`
Seccomp *Seccomp `json:"seccomp,omitempty"`
Apparmor *Apparmor `json:"apparmor,omitempty"`
Selinux *Selinux `json:"selinux,omitempty"`
}
// Seccomp represents the "seccomp" field.
type Seccomp struct {
// Enabled is true if seccomp support is compiled in.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
// Actions is the list of the recognized actions, e.g., "SCMP_ACT_NOTIFY".
// Nil value means "unknown", not "no support for any action".
Actions []string `json:"actions,omitempty"`
// Operators is the list of the recognized actions, e.g., "SCMP_CMP_NE".
// Nil value means "unknown", not "no support for any operator".
Operators []string `json:"operators,omitempty"`
// Operators 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"`
}
// Apparmor represents the "apparmor" field.
type Apparmor struct {
// Enabled is true if AppArmor support is compiled in.
// Unrelated to whether the host supports AppArmor or not.
// Nil value means "unknown", not "false".
// Always true in the current version of runc.
Enabled *bool `json:"enabled,omitempty"`
}
// Selinux represents the "selinux" field.
type Selinux struct {
// Enabled is true if SELinux support is compiled in.
// Unrelated to whether the host supports SELinux or not.
// Nil value means "unknown", not "false".
// Always true in the current version of runc.
Enabled *bool `json:"enabled,omitempty"`
}
// Cgroup represents the "cgroup" field.
type Cgroup struct {
// V1 represents whether Cgroup v1 support is compiled in.
// Unrelated to whether the host uses cgroup v1 or not.
// Nil value means "unknown", not "false".
// Always true in the current version of runc.
V1 *bool `json:"v1,omitempty"`
// V2 represents whether Cgroup v2 support is compiled in.
// Unrelated to whether the host uses cgroup v2 or not.
// Nil value means "unknown", not "false".
// Always true in the current version of runc.
V2 *bool `json:"v2,omitempty"`
// Systemd represents whether systemd-cgroup support is compiled in.
// Unrelated to whether the host uses systemd or not.
// Nil value means "unknown", not "false".
// Always true in the current version of runc.
Systemd *bool `json:"systemd,omitempty"`
// SystemdUser represents whether user-scoped systemd-cgroup support is compiled in.
// Unrelated to whether the host uses systemd or not.
// Nil value means "unknown", not "false".
// Always true in the current version of runc.
SystemdUser *bool `json:"systemdUser,omitempty"`
}
const (
// 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".
// Third party implementations such as crun and runsc MAY use this annotation to report the most compatible runc version,
// however, parsing this annotation value is discouraged.
AnnotationRuncVersion = "org.opencontainers.runc.version"
// AnnotationRuncCommit corresponds to the output of `git describe --dirty --long --always` in the runc repo.
// Third party implementations such as crun and runsc SHOULD NOT use this annotation, as their repo is different from the runc repo.
// Parsing this annotation value is discouraged.
AnnotationRuncCommit = "org.opencontainers.runc.commit"
// AnnotationRuncCheckpointEnabled is set to "true" if CRIU-based checkpointing is supported.
// Unrelated to whether the host supports CRIU or not.
// Always set to "true" in the current version of runc.
// This is defined as an annotation because checkpointing is a runc-specific feature that is not defined in the OCI Runtime Spec.
// Third party implementations such as crun and runsc MAY use this annotation.
AnnotationRuncCheckpointEnabled = "org.opencontainers.runc.checkpoint.enabled"
// AnnotationLibseccompVersion is the version of libseccomp, e.g., "2.5.1".
// Note that the runtime MAY support seccomp even when this annotation is not present.
AnnotationLibseccompVersion = "io.github.seccomp.libseccomp.version"
)