diff --git a/docs/yaml.md b/docs/yaml.md index 49b1017f3..ceac3e2e0 100644 --- a/docs/yaml.md +++ b/docs/yaml.md @@ -232,6 +232,21 @@ services: - CAP_DAC_OVERRIDE ``` +## `devices` + +To access the console, it's necessary to explicitly add a "device" definition, for example: + +``` +devices: +- path: "/dev/console" + type: c + major: 5 + minor: 1 + mode: 0666 +``` + +See the [the getty package](../pkg/getty/build.yml) for a more complete example +and see [runc](https://github.com/opencontainers/runc/commit/60e21ec26e15945259d4b1e790e8fd119ee86467) for context). ### Mount Options When mounting filesystem paths into a container - whether as part of `onboot` or `services` - there are several options of which you need to be aware. Using them properly is necessary for your containers to function properly. diff --git a/pkg/getty/build.yml b/pkg/getty/build.yml index d10af6834..4cb4238c7 100644 --- a/pkg/getty/build.yml +++ b/pkg/getty/build.yml @@ -14,5 +14,26 @@ config: - /var/lib/containerd:/var/lib/containerd - /dev:/dev - /sys:/sys + devices: + - path: "/dev/console" + type: c + major: 5 + minor: 1 + mode: 0666 + - path: "/dev/tty0" + type: c + major: 4 + minor: 0 + mode: 0666 + - path: "/dev/ttyS0" + type: c + major: 4 + minor: 64 + mode: 0666 + - path: "/dev/ttyAMA0" + type: c + major: 204 + minor: 64 + mode: 0666 capabilities: - all diff --git a/src/cmd/linuxkit/moby/config.go b/src/cmd/linuxkit/moby/config.go index 8ff39e890..8e49d06c0 100644 --- a/src/cmd/linuxkit/moby/config.go +++ b/src/cmd/linuxkit/moby/config.go @@ -2,6 +2,7 @@ package moby import ( "fmt" + "os" "sort" "strconv" "strings" @@ -70,6 +71,7 @@ type ImageConfig struct { Mounts *[]specs.Mount `yaml:"mounts,omitempty" json:"mounts,omitempty"` Binds *[]string `yaml:"binds,omitempty" json:"binds,omitempty"` BindsAdd *[]string `yaml:"binds.add,omitempty" json:"binds.add,omitempty"` + Devices *[]Device `yaml:"devices,omitempty" json:"devices,omitempty"` Tmpfs *[]string `yaml:"tmpfs,omitempty" json:"tmpfs,omitempty"` Command *[]string `yaml:"command,omitempty" json:"command,omitempty"` Env *[]string `yaml:"env,omitempty" json:"env,omitempty"` @@ -102,6 +104,15 @@ type ImageConfig struct { ref *reference.Spec } +// Device specifies a device to be exposed to the container. +type Device struct { + Path string `yaml:"path" json:"path"` + Type string `yaml:"type" json:"type"` + Major int64 `yaml:"major" json:"major"` + Minor int64 `yaml:"minor" json:"minor"` + Mode string `yaml:"mode,omitempty" json:"mode,omitempty"` +} + // Runtime is the type of config processed at runtime, not used to build the OCI spec type Runtime struct { Cgroups *[]string `yaml:"cgroups,omitempty" json:"cgroups,omitempty"` @@ -561,6 +572,17 @@ func assignResources(v1, v2 *specs.LinuxResources) specs.LinuxResources { return specs.LinuxResources{} } +// assignDevices does ordered overrides from Devices +func assignDevices(v1, v2 *[]Device) []Device { + if v2 != nil { + return *v2 + } + if v1 != nil { + return *v1 + } + return []Device{} +} + // assignRuntime does ordered overrides from Runtime func assignRuntime(v1, v2 *Runtime) Runtime { if v1 == nil { @@ -1021,6 +1043,25 @@ func ConfigToOCI(yaml *Image, config imagespec.ImageConfig, idMap map[string]uin resources := assignResources(label.Resources, yaml.Resources) + devices := assignDevices(label.Devices, yaml.Devices) + var linuxDevices []specs.LinuxDevice + for _, device := range devices { + mode, err := strconv.ParseInt(device.Mode, 8, 32) + if err != nil { + return oci, runtime, fmt.Errorf("Cannot parse device mode as octal value: %v", err) + } + fileMode := os.FileMode(mode) + linuxDevice := specs.LinuxDevice{ + Path: device.Path, + Type: device.Type, + Major: device.Major, + Minor: device.Minor, + FileMode: &fileMode, + } + linuxDevices = append(linuxDevices, linuxDevice) + resources.Devices = append(resources.Devices, deviceCgroup(linuxDevice)) + } + oci.Linux = &specs.Linux{ UIDMappings: assignMappings(label.UIDMappings, yaml.UIDMappings), GIDMappings: assignMappings(label.GIDMappings, yaml.GIDMappings), @@ -1028,7 +1069,7 @@ func ConfigToOCI(yaml *Image, config imagespec.ImageConfig, idMap map[string]uin Resources: &resources, CgroupsPath: assignString(label.CgroupsPath, yaml.CgroupsPath), Namespaces: namespaces, - // Devices + Devices: linuxDevices, // Seccomp RootfsPropagation: assignString(label.RootfsPropagation, yaml.RootfsPropagation), MaskedPaths: assignStrings(label.MaskedPaths, yaml.MaskedPaths), @@ -1041,3 +1082,13 @@ func ConfigToOCI(yaml *Image, config imagespec.ImageConfig, idMap map[string]uin return oci, runtime, nil } + +func deviceCgroup(device specs.LinuxDevice) specs.LinuxDeviceCgroup { + return specs.LinuxDeviceCgroup{ + Allow: true, + Type: device.Type, + Major: &device.Major, + Minor: &device.Minor, + Access: "rwm", + } +} diff --git a/src/cmd/linuxkit/moby/schema.go b/src/cmd/linuxkit/moby/schema.go index 06a52a414..3d651d87c 100644 --- a/src/cmd/linuxkit/moby/schema.go +++ b/src/cmd/linuxkit/moby/schema.go @@ -67,6 +67,21 @@ var schema = string(` "type": "array", "items": { "$ref": "#/definitions/mount" } }, + "device": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { "type": "string" }, + "type": { "type": "string" }, + "major": { "type": "integer" }, + "minor": { "type": "integer" }, + "mode": { "type": "string" } + } + }, + "devices": { + "type": "array", + "items": { "$ref": "#/definitions/device" } + }, "idmapping": { "type": "object", "additionalProperties": false, @@ -265,6 +280,7 @@ var schema = string(` "mounts": { "$ref": "#/definitions/mounts" }, "binds": { "$ref": "#/definitions/strings" }, "binds.add": { "$ref": "#/definitions/strings" }, + "devices": { "$ref": "#/definitions/devices" }, "tmpfs": { "$ref": "#/definitions/strings" }, "command": { "$ref": "#/definitions/strings" }, "env": { "$ref": "#/definitions/strings" },