Skip to content

Commit

Permalink
Add runc features command
Browse files Browse the repository at this point in the history
Fix issue 3274

See `types/features/features.go`.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed Nov 30, 2021
1 parent 6ff0420 commit 520702d
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 0 deletions.
74 changes: 74 additions & 0 deletions features.go
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions libcontainer/capabilities/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions libcontainer/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions libcontainer/seccomp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package seccomp

import (
"fmt"
"sort"

"github.com/opencontainers/runc/libcontainer/configs"
)
Expand All @@ -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,
Expand All @@ -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",
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions libcontainer/seccomp/seccomp_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions libcontainer/seccomp/seccomp_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 33 additions & 0 deletions libcontainer/specconv/spec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -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.
//
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func main() {
startCommand,
stateCommand,
updateCommand,
featuresCommand,
}
app.Before = func(context *cli.Context) error {
if !context.IsSet("root") && xdgRuntimeDir != "" {
Expand Down
129 changes: 129 additions & 0 deletions types/features/features.go
Original file line number Diff line number Diff line change
@@ -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"
)

0 comments on commit 520702d

Please sign in to comment.