Skip to content

Commit

Permalink
libct: document initConfig and friends
Browse files Browse the repository at this point in the history
This is one of the dark corners of runc / libcontainer, so let's shed
some light on it.

initConfig is a structure which is filled in [mostly] by newInitConfig,
and one of its hidden aspects is it contains a process config which is
the result of merge between the container and the process configs.

Let's document how all this happens, where the fields are coming from,
which one has a preference, and how it all works.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
  • Loading branch information
kolyshkin committed Jan 8, 2025
1 parent 81b1317 commit fc7190c
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 36 deletions.
9 changes: 9 additions & 0 deletions libcontainer/container_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,9 @@ func (c *Container) newSetnsProcess(p *Process, cmd *exec.Cmd, comm *processComm
}

func (c *Container) newInitConfig(process *Process) *initConfig {
// Set initial properties. For those properties that exist
// both in the container config and the process, use the ones
// from the container config first, and override them later.
cfg := &initConfig{
Config: c.config,
Args: process.Args,
Expand All @@ -701,6 +704,9 @@ func (c *Container) newInitConfig(process *Process) *initConfig {
ConsoleWidth: process.ConsoleWidth,
ConsoleHeight: process.ConsoleHeight,
}

// Overwrite config properties with ones from process.

if process.NoNewPrivileges != nil {
cfg.NoNewPrivileges = *process.NoNewPrivileges
}
Expand All @@ -713,6 +719,9 @@ func (c *Container) newInitConfig(process *Process) *initConfig {
if len(process.Rlimits) > 0 {
cfg.Rlimits = process.Rlimits
}

// Set misc properties.

if cgroups.IsCgroup2UnifiedMode() {
cfg.Cgroup2Path = c.cgroupManager.Path("")
}
Expand Down
72 changes: 50 additions & 22 deletions libcontainer/init_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,57 @@ type network struct {
TempVethPeerName string `json:"temp_veth_peer_name"`
}

// initConfig is used for transferring parameters from Exec() to Init()
// initConfig is used for transferring parameters from Exec() to Init().
// It contains:
// - original container config;
// - some [Process] properties;
// - set of properties merged from the container config ([configs.Config])
// and the process ([Process]);
// - some properties that come from the container.
//
// When adding new fields, please make sure they go into the relevant section.
type initConfig struct {
Args []string `json:"args"`
Env []string `json:"env"`
Cwd string `json:"cwd"`
Capabilities *configs.Capabilities `json:"capabilities"`
ProcessLabel string `json:"process_label"`
AppArmorProfile string `json:"apparmor_profile"`
NoNewPrivileges bool `json:"no_new_privileges"`
User string `json:"user"`
AdditionalGroups []string `json:"additional_groups"`
Config *configs.Config `json:"config"`
Networks []*network `json:"network"`
PassedFilesCount int `json:"passed_files_count"`
ContainerID string `json:"containerid"`
Rlimits []configs.Rlimit `json:"rlimits"`
CreateConsole bool `json:"create_console"`
ConsoleWidth uint16 `json:"console_width"`
ConsoleHeight uint16 `json:"console_height"`
RootlessEUID bool `json:"rootless_euid,omitempty"`
RootlessCgroups bool `json:"rootless_cgroups,omitempty"`
SpecState *specs.State `json:"spec_state,omitempty"`
Cgroup2Path string `json:"cgroup2_path,omitempty"`
// Config is the original container config.
Config *configs.Config `json:"config"`

// Properties that are unique to and come from [Process].

Args []string `json:"args"`
Env []string `json:"env"`
User string `json:"user"`
AdditionalGroups []string `json:"additional_groups"`
Cwd string `json:"cwd"`
CreateConsole bool `json:"create_console"`
ConsoleWidth uint16 `json:"console_width"`
ConsoleHeight uint16 `json:"console_height"`
PassedFilesCount int `json:"passed_files_count"`

// Properties that exists both in the container config and the process,
// as merged by [Container.newInitConfig] (process properties has preference).

AppArmorProfile string `json:"apparmor_profile"`
Capabilities *configs.Capabilities `json:"capabilities"`
NoNewPrivileges bool `json:"no_new_privileges"`
ProcessLabel string `json:"process_label"`
Rlimits []configs.Rlimit `json:"rlimits"`

// Properties that only exist in container config.
// FIXME: they are also passed in Config above.

RootlessEUID bool `json:"rootless_euid,omitempty"`
RootlessCgroups bool `json:"rootless_cgroups,omitempty"`

// Miscellaneous properties, filled in by [Container.newInitConfig]
// unless documented otherwise.

ContainerID string `json:"containerid"`
Cgroup2Path string `json:"cgroup2_path,omitempty"`

// Networks is filled in from container config by [initProcess.createNetworkInterfaces].
Networks []*network `json:"network"`

// SpecState is filled in by [initProcess.Start].
SpecState *specs.State `json:"spec_state,omitempty"`
}

// Init is part of "runc init" implementation.
Expand Down
40 changes: 26 additions & 14 deletions libcontainer/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ type processOperations interface {
pid() int
}

// Process specifies the configuration and IO for a process inside
// a container.
// Process specifies the configuration and IO for a process inside a container.
//
// Some properties here are also available in container config [configs.Config],
// in which case these properties have a preference over container config.
type Process struct {
// The command to be run followed by any arguments.
Args []string
Expand All @@ -37,41 +39,51 @@ type Process struct {
// Cwd will change the processes current working directory inside the container's rootfs.
Cwd string

// Stdin is a pointer to a reader which provides the standard input stream.
// Stdin is a reader which provides the standard input stream.
Stdin io.Reader

// Stdout is a pointer to a writer which receives the standard output stream.
// Stdout is a writer which receives the standard output stream.
Stdout io.Writer

// Stderr is a pointer to a writer which receives the standard error stream.
// Stderr is a writer which receives the standard error stream.
Stderr io.Writer

// ExtraFiles specifies additional open files to be inherited by the container
// ExtraFiles specifies additional open files to be inherited by the container.
ExtraFiles []*os.File

// open handles to cloned binaries -- see dmz.CloneSelfExe for more details
// Open handles to cloned binaries -- see dmz.CloneSelfExe for more details.
clonedExes []*os.File

// Initial sizings for the console
// Initial size for the console.
ConsoleWidth uint16
ConsoleHeight uint16

// 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 specify the capabilities to keep when executing the process inside the container.
// All capabilities not specified will be dropped from the processes capability mask.
//
// If not nil, overrides the [configs.Config.Capabilities].
Capabilities *configs.Capabilities

// AppArmorProfile specifies the profile to apply to the process and is
// changed at the time the process is execed
// changed at the time the process is executed.
//
// If not empty, overrides the [configs.Config.AppArmorProfile].
AppArmorProfile string

// Label specifies the label to apply to the process. It is commonly used by selinux
// Label specifies the label to apply to the process. It is commonly used by selinux.
//
// If not empty, overrides the [configs.Config.ProcessLabel].
Label string

// NoNewPrivileges controls whether processes can gain additional privileges.
//
// If not nil, overrides the [configs.Config.NoNewPrivileges].
NoNewPrivileges *bool

// Rlimits specifies the resource limits, such as max open files, to set in the container
// If Rlimits are not set, the container will inherit rlimits from the parent process
// Rlimits specifies the resource limits, such as max open files, to set for the process.
// If unset, the container will inherit rlimits from the parent process.
//
// If not empty, overrides the [configs.Config.Rlimit].
Rlimits []configs.Rlimit

// ConsoleSocket provides the masterfd console.
Expand Down

0 comments on commit fc7190c

Please sign in to comment.