Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add runc features command #3296

Merged
merged 1 commit into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to mention here that the output is subject to change, which will be slightly more visible than the changelog. But we can add that later.

`,
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"
)