Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Deprecated

* `runc` option `--criu` is now ignored (with a warning), and the option will
be removed entirely in a future release. Users who need a non-standard
`criu` binary should rely on the standard way of looking up binaries in
`$PATH`. (#3316)

### Changed

* When Intel RDT feature is not available, its initialization is skipped,
Expand Down
3 changes: 1 addition & 2 deletions contrib/completions/bash/runc
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,11 @@ _runc_runc() {
--log
--log-format
--root
--criu
--rootless
"

case "$prev" in
--log | --root | --criu)
--log | --root)
case "$cur" in
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
'')
Expand Down
2 changes: 1 addition & 1 deletion delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"golang.org/x/sys/unix"
)

func killContainer(container libcontainer.Container) error {
func killContainer(container *libcontainer.Container) error {
_ = container.Signal(unix.SIGKILL, false)
for i := 0; i < 100; i++ {
time.Sleep(100 * time.Millisecond)
Expand Down
20 changes: 1 addition & 19 deletions init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package main
import (
"os"
"runtime"
"strconv"

"github.com/opencontainers/runc/libcontainer"
_ "github.com/opencontainers/runc/libcontainer/nsenter"
"github.com/sirupsen/logrus"
)

func init() {
Expand All @@ -17,23 +15,7 @@ func init() {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()

level, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_LOGLEVEL"))
if err != nil {
panic(err)
}

logPipeFd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_LOGPIPE"))
if err != nil {
panic(err)
}

logrus.SetLevel(logrus.Level(level))
logrus.SetOutput(os.NewFile(uintptr(logPipeFd), "logpipe"))
logrus.SetFormatter(new(logrus.JSONFormatter))
logrus.Debug("child process in init()")

factory, _ := libcontainer.New("")
if err := factory.StartInitialization(); err != nil {
if err := libcontainer.StartInitialization(); err != nil {
// as the error is sent back to the parent there is no need to log
// or write it to stderr because the parent process will handle this
os.Exit(1)
Expand Down
5 changes: 2 additions & 3 deletions libcontainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ func init() {
if len(os.Args) > 1 && os.Args[1] == "init" {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
factory, _ := libcontainer.New("")
if err := factory.StartInitialization(); err != nil {
if err := libcontainer.StartInitialization(); err != nil {
logrus.Fatal(err)
}
panic("--this line should have never been executed, congratulations--")
Expand All @@ -45,7 +44,7 @@ Then to create a container you first have to initialize an instance of a factory
that will handle the creation and initialization for a container.

```go
factory, err := libcontainer.New("/var/lib/container", libcontainer.Cgroupfs, libcontainer.InitArgs(os.Args[0], "init"))
factory, err := libcontainer.New("/var/lib/container")
if err != nil {
logrus.Fatal(err)
return
Expand Down
4 changes: 2 additions & 2 deletions libcontainer/capabilities/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"sort"
"strings"

"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/syndtr/gocapability/capability"
)
Expand Down Expand Up @@ -49,7 +49,7 @@ func KnownCapabilities() []string {
// 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.
func New(capConfig *configs.Capabilities) (*Caps, error) {
func New(capConfig *specs.LinuxCapabilities) (*Caps, error) {
var (
err error
c Caps
Expand Down
4 changes: 2 additions & 2 deletions libcontainer/capabilities/capabilities_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import (
"os"
"testing"

"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/syndtr/gocapability/capability"
)

func TestNew(t *testing.T) {
cs := []string{"CAP_CHOWN", "CAP_UNKNOWN", "CAP_UNKNOWN2"}
conf := configs.Capabilities{
conf := specs.LinuxCapabilities{
Bounding: cs,
Effective: cs,
Inheritable: cs,
Expand Down
18 changes: 1 addition & 17 deletions libcontainer/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ type Syscall struct {
Args []*Arg `json:"args"`
}

// TODO Windows. Many of these fields should be factored out into those parts
// which are common across platforms, and those which are platform specific.

// Config defines configuration options for executing a process inside a contained environment.
type Config struct {
// NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs
Expand Down Expand Up @@ -127,7 +124,7 @@ type Config struct {

// Capabilities specify the capabilities to keep when executing the process inside the container
// All capabilities not specified will be dropped from the processes capability mask
Capabilities *Capabilities `json:"capabilities"`
Capabilities *specs.LinuxCapabilities `json:"capabilities"`

// Networks specifies the container's network setup to be created
Networks []*Network `json:"networks"`
Expand Down Expand Up @@ -264,19 +261,6 @@ func KnownHookNames() []string {
}
}

type Capabilities struct {
// Bounding is the set of capabilities checked by the kernel.
Bounding []string
// Effective is the set of capabilities checked by the kernel.
Effective []string
// Inheritable is the capabilities preserved across execve.
Inheritable []string
// Permitted is the limiting superset for effective capabilities.
Permitted []string
// Ambient is the ambient set of capabilities that are kept.
Ambient []string
}
Comment on lines -267 to -278
Copy link
Member

Choose a reason for hiding this comment

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

I don't think it will cause any issues, but mentioninging it (just in case); the type is the same from a "go" perspective, but the runtime-spec type uses lowercase names (and omitempty) for the JSON representation; https://github.com/opencontainers/runtime-spec/blob/a3c33d663ebc56c4d35dbceaa447c7bf37f6fab3/specs-go/config.go#L65-L78

// LinuxCapabilities specifies the list of allowed capabilities that are kept for a process.
// http://man7.org/linux/man-pages/man7/capabilities.7.html
type LinuxCapabilities struct {
	// Bounding is the set of capabilities checked by the kernel.
	Bounding []string `json:"bounding,omitempty" platform:"linux"`
	// Effective is the set of capabilities checked by the kernel.
	Effective []string `json:"effective,omitempty" platform:"linux"`
	// Inheritable is the capabilities preserved across execve.
	Inheritable []string `json:"inheritable,omitempty" platform:"linux"`
	// Permitted is the limiting superset for effective capabilities.
	Permitted []string `json:"permitted,omitempty" platform:"linux"`
	// Ambient is the ambient set of capabilities that are kept.
	Ambient []string `json:"ambient,omitempty" platform:"linux"`
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, this might open the hell gates:

[root@kir-rhat runc]# jq < /run/runc/xxx/state.json | grep -A10 capab
    "capabilities": {
      "Bounding": [
        "CAP_AUDIT_WRITE",
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE"
      ],
      "Effective": [
        "CAP_AUDIT_WRITE",
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE"
      ],
[root@kir-rhat runc]# jq < /run/runc/xx344/state.json | grep -A10 capab
    "capabilities": {
      "bounding": [
        "CAP_AUDIT_WRITE",
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE"
      ],
      "effective": [
        "CAP_AUDIT_WRITE",
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE"
      ],

From what I can see, this is not used anywhere, and yet it might become problematic.

I guess I'll revert this.

Copy link
Member

Choose a reason for hiding this comment

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

If these files are only used by runc and/or only used by Go code, then it shouldn't (AFAICS) be an issue. json.Unmarshal() is case insensitive for keys (for better or worse 😅)

https://go.dev/play/p/9bo-nSvEARD

Details
package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

type Capabilities struct {
	// Bounding is the set of capabilities checked by the kernel.
	Bounding []string
	// Effective is the set of capabilities checked by the kernel.
	Effective []string
	// Inheritable is the capabilities preserved across execve.
	Inheritable []string
	// Permitted is the limiting superset for effective capabilities.
	Permitted []string
	// Ambient is the ambient set of capabilities that are kept.
	Ambient []string
}

type LinuxCapabilities struct {
	// Bounding is the set of capabilities checked by the kernel.
	Bounding []string `json:"bounding,omitempty" platform:"linux"`
	// Effective is the set of capabilities checked by the kernel.
	Effective []string `json:"effective,omitempty" platform:"linux"`
	// Inheritable is the capabilities preserved across execve.
	Inheritable []string `json:"inheritable,omitempty" platform:"linux"`
	// Permitted is the limiting superset for effective capabilities.
	Permitted []string `json:"permitted,omitempty" platform:"linux"`
	// Ambient is the ambient set of capabilities that are kept.
	Ambient []string `json:"ambient,omitempty" platform:"linux"`
}

type runc struct {
	Capabilities *Capabilities `json:"capabilities"`
}

type oci struct {
	Capabilities *LinuxCapabilities `json:"capabilities"`
}

const jsonLower = `
{
  "capabilities": {
    "bounding": [
      "CAP_AUDIT_WRITE",
      "CAP_KILL",
      "CAP_NET_BIND_SERVICE"
    ],
    "effective": [
      "CAP_AUDIT_WRITE",
      "CAP_KILL",
      "CAP_NET_BIND_SERVICE"
    ]
  }
}`

const jsonUpper = `
{
  "capabilities": {
    "Bounding": [
      "CAP_AUDIT_WRITE",
      "CAP_KILL",
      "CAP_NET_BIND_SERVICE"
    ],
    "Effective": [
      "CAP_AUDIT_WRITE",
      "CAP_KILL",
      "CAP_NET_BIND_SERVICE"
    ]
  }
}`

func main() {
	var (
		runcLower runc
		runcUpper runc
		ociLower  oci
		ociUpper  oci
	)

	if err := json.Unmarshal([]byte(jsonLower), &runcLower); err != nil {
		fmt.Println(err)
	}
	if err := json.Unmarshal([]byte(jsonUpper), &runcUpper); err != nil {
		fmt.Println(err)
	}
	if err := json.Unmarshal([]byte(jsonLower), &ociLower); err != nil {
		fmt.Println(err)
	}
	if err := json.Unmarshal([]byte(jsonUpper), &ociUpper); err != nil {
		fmt.Println(err)
	}

	if reflect.DeepEqual(runcLower, runcUpper) {
		fmt.Printf("SAME: %v <--> %v\n", runcLower.Capabilities, runcUpper.Capabilities)
	}
	if reflect.DeepEqual(ociLower, ociUpper) {
		fmt.Printf("SAME: %v <--> %v\n", ociLower.Capabilities, runcUpper.Capabilities)
	}
}


func (hooks HookList) RunHooks(state *specs.State) error {
for i, h := range hooks {
if err := h.Run(state); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions libcontainer/configs/validate/rootless.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
)

// rootlessEUID makes sure that the config can be applied when runc
// rootlessEUIDCheck makes sure that the config can be applied when runc
// is being executed as a non-root user (euid != 0) in the current user namespace.
func (v *ConfigValidator) rootlessEUID(config *configs.Config) error {
func rootlessEUIDCheck(config *configs.Config) error {
if !config.RootlessEUID {
return nil
}
Expand Down
40 changes: 14 additions & 26 deletions libcontainer/configs/validate/rootless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,34 @@ func rootlessEUIDConfig() *configs.Config {
}

func TestValidateRootlessEUID(t *testing.T) {
validator := New()

config := rootlessEUIDConfig()
if err := validator.Validate(config); err != nil {
if err := Validate(config); err != nil {
t.Errorf("Expected error to not occur: %+v", err)
}
}

/* rootlessEUIDMappings */

func TestValidateRootlessEUIDUserns(t *testing.T) {
validator := New()

config := rootlessEUIDConfig()
config.Namespaces = nil
if err := validator.Validate(config); err == nil {
if err := Validate(config); err == nil {
t.Errorf("Expected error to occur if user namespaces not set")
}
}

func TestValidateRootlessEUIDMappingUid(t *testing.T) {
validator := New()

config := rootlessEUIDConfig()
config.UidMappings = nil
if err := validator.Validate(config); err == nil {
if err := Validate(config); err == nil {
t.Errorf("Expected error to occur if no uid mappings provided")
}
}

func TestValidateNonZeroEUIDMappingGid(t *testing.T) {
validator := New()

config := rootlessEUIDConfig()
config.GidMappings = nil
if err := validator.Validate(config); err == nil {
if err := Validate(config); err == nil {
t.Errorf("Expected error to occur if no gid mappings provided")
}
}
Expand All @@ -78,8 +70,6 @@ func TestValidateNonZeroEUIDMappingGid(t *testing.T) {

func TestValidateRootlessEUIDMountUid(t *testing.T) {
config := rootlessEUIDConfig()
validator := New()

config.Mounts = []*configs.Mount{
{
Source: "devpts",
Expand All @@ -88,37 +78,35 @@ func TestValidateRootlessEUIDMountUid(t *testing.T) {
},
}

if err := validator.Validate(config); err != nil {
if err := Validate(config); err != nil {
t.Errorf("Expected error to not occur when uid= not set in mount options: %+v", err)
}

config.Mounts[0].Data = "uid=5"
if err := validator.Validate(config); err == nil {
if err := Validate(config); err == nil {
t.Errorf("Expected error to occur when setting uid=5 in mount options")
}

config.Mounts[0].Data = "uid=0"
if err := validator.Validate(config); err != nil {
if err := Validate(config); err != nil {
t.Errorf("Expected error to not occur when setting uid=0 in mount options: %+v", err)
}

config.Mounts[0].Data = "uid=2"
config.UidMappings[0].Size = 10
if err := validator.Validate(config); err != nil {
if err := Validate(config); err != nil {
t.Errorf("Expected error to not occur when setting uid=2 in mount options and UidMapping[0].size is 10")
}

config.Mounts[0].Data = "uid=20"
config.UidMappings[0].Size = 10
if err := validator.Validate(config); err == nil {
if err := Validate(config); err == nil {
t.Errorf("Expected error to occur when setting uid=20 in mount options and UidMapping[0].size is 10")
}
}

func TestValidateRootlessEUIDMountGid(t *testing.T) {
config := rootlessEUIDConfig()
validator := New()

config.Mounts = []*configs.Mount{
{
Source: "devpts",
Expand All @@ -127,29 +115,29 @@ func TestValidateRootlessEUIDMountGid(t *testing.T) {
},
}

if err := validator.Validate(config); err != nil {
if err := Validate(config); err != nil {
t.Errorf("Expected error to not occur when gid= not set in mount options: %+v", err)
}

config.Mounts[0].Data = "gid=5"
if err := validator.Validate(config); err == nil {
if err := Validate(config); err == nil {
t.Errorf("Expected error to occur when setting gid=5 in mount options")
}

config.Mounts[0].Data = "gid=0"
if err := validator.Validate(config); err != nil {
if err := Validate(config); err != nil {
t.Errorf("Expected error to not occur when setting gid=0 in mount options: %+v", err)
}

config.Mounts[0].Data = "gid=5"
config.GidMappings[0].Size = 10
if err := validator.Validate(config); err != nil {
if err := Validate(config); err != nil {
t.Errorf("Expected error to not occur when setting gid=5 in mount options and GidMapping[0].size is 10")
}

config.Mounts[0].Data = "gid=11"
config.GidMappings[0].Size = 10
if err := validator.Validate(config); err == nil {
if err := Validate(config); err == nil {
t.Errorf("Expected error to occur when setting gid=11 in mount options and GidMapping[0].size is 10")
}
}
Loading