diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 74933e9be..7fd4eb399 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -14,8 +14,8 @@ }, { "ImportPath": "github.com/opencontainers/specs", - "Comment": "v0.1.1-56-g346a898", - "Rev": "346a898ba89a7450ef30c08910db8756111a9fcc" + "Comment": "v0.3.0", + "Rev": "25cbfc427ba6154016f33c7ed1b8ed43b8b7b7ed" }, { "ImportPath": "github.com/syndtr/gocapability/capability", diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/.travis.yml b/Godeps/_workspace/src/github.com/opencontainers/specs/.travis.yml index 64b57bfcb..5e1b19df0 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/.travis.yml +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/.travis.yml @@ -1,6 +1,6 @@ language: go go: - - 1.5.1 + - 1.5.3 - 1.4.3 - 1.3.3 diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/MAINTAINERS b/Godeps/_workspace/src/github.com/opencontainers/specs/MAINTAINERS index 48e6ee130..ac88dd51a 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/MAINTAINERS +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/MAINTAINERS @@ -1,9 +1,9 @@ Michael Crosby (@crosbymichael) Alexander Morozov (@LK4D4) -Rohit Jnagal (@rjnagal) -Victor Marmol (@vmarmol) +Vishnu Kannan (@vishh) Mrunal Patel (@mrunalp) Vincent Batts (@vbatts) Daniel, Dao Quang Minh (@dqminh) Brandon Philips (@philips) Tianon Gravi (@tianon) +Qiang Huang (@hqhq) diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/README.md b/Godeps/_workspace/src/github.com/opencontainers/specs/README.md index 185807193..bfff859e5 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/README.md +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/README.md @@ -6,27 +6,40 @@ Table of Contents - [Container Principles](principles.md) +- [Specification Style](style.md) - [Filesystem Bundle](bundle.md) - Configuration - - [Container Configuration](config.md) - - [Container Configuration (Linux-specific)](config-linux.md) - - [Runtime Configuration](runtime-config.md) - - [Runtime Configuration (Linux-specific)](runtime-config-linux.md) + - [General](config.md) + - [Linux-specific](config-linux.md) - [Runtime and Lifecycle](runtime.md) - [Linux Specific Runtime](runtime-linux.md) - [Implementations](implementations.md) +- [Glossary](glossary.md) + +In the specifications in the above table of contents, the keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119) (Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997). # Use Cases To provide context for users the following section gives example use cases for each part of the spec. -### Filesystem Bundle & Configuration +#### Application Bundle Builders + +Application bundle builders can create a [bundle](bundle.md) directory that includes all of the files required for launching an application as a container. +The bundle contains an OCI [configuration file](config.md) where the builder can specify host-independent details such as [which executable to launch](config.md#process-configuration) and host-specific settings such as [mount](config.md#mounts) locations, [hook](config.md#hooks) paths, Linux [namespaces](config-linux.md#namespaces) and [cgroups](config-linux.md#control-groups). +Because the configuration includes host-specific settings, application bundle directories copied between two hosts may require configuration adjustments. + +#### Hook Developers + +[Hook](config.md#hooks) developers can extend the functionality of an OCI-compliant runtime by hooking into a container's lifecycle with an external application. +Example use cases include sophisticated network configuration, volume garbage collection, etc. + +#### Runtime Developers -- A user can create a root filesystem and configuration, with low-level OS and host specific details, and launch it as a container under an Open Container runtime. +Runtime developers can build runtime implementations that run OCI-compliant bundles and container configuration, containing low-level OS and host specific details, on a particular platform. -## Releases +# Releases -There is a loose [Road Map](https://github.com/opencontainers/specs/wiki/RoadMap:) on the wiki. +There is a loose [Road Map](./ROADMAP.md). During the `0.x` series of OCI releases we make no backwards compatibility guarantees and intend to break the schema during this series. # Contributing diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/bundle.md b/Godeps/_workspace/src/github.com/opencontainers/specs/bundle.md index eff9bf2f4..5b1925b2e 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/bundle.md +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/bundle.md @@ -8,20 +8,17 @@ See also [OS X application bundles](http://en.wikipedia.org/wiki/Bundle_%28OS_X% The definition of a bundle is only concerned with how a container, and its configuration data, are stored on a local file system so that it can be consumed by a compliant runtime. A Standard Container bundle contains all the information needed to load and run a container. -This includes the following three artifacts which MUST all reside in the same directory on the local filesystem: +This includes the following artifacts which MUST all reside in the same directory on the local filesystem: -1. `config.json` : immutable, host independent configuration. -This REQUIRED file, which MUST be named `config.json`, contains settings that are host independent and application specific such as security permissions, environment variables and arguments. +1. `config.json` : contains configuration data. +This REQUIRED file, which MUST be named `config.json`. +When the bundle is packaged up for distribution, this file MUST be included. See [`config.json`](config.md) for more details. -2. `runtime.json` : mutable, host dependent configuration. -This REQUIRED file, which MUST be named `runtime.json`, contains settings that are host specific such as memory limits, local device access and mount sources. -The goal is that the bundle can be moved as a unit to another runtime and run the same application if `runtime.json` is reconfigured. -See [`runtime.json`](runtime-config.md) for more details. - -3. A directory representing the root filesystem of the container. +2. A directory representing the root filesystem of the container. While the name of this REQUIRED directory may be arbitrary, users should consider using a conventional name, such as `rootfs`. +When the bundle is packaged up for distribution, this directory MUST be included. This directory MUST be referenced from within the `config.json` file. -While these three artifacts MUST all be present in a single directory on the local filesytem, that directory itself is not part of the bundle. +While these artifacts MUST all be present in a single directory on the local filesystem, that directory itself is not part of the bundle. In other words, a tar archive of a *bundle* will have these artifacts at the root of the archive, not nested within a top-level directory. diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/config-linux.md b/Godeps/_workspace/src/github.com/opencontainers/specs/config-linux.md index 883dabac4..f7723c74c 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/config-linux.md +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/config-linux.md @@ -16,8 +16,73 @@ Valid values are the strings for capabilities defined in [the man page](http://m ] ``` +## Default File Systems + +The Linux ABI includes both syscalls and several special file paths. +Applications expecting a Linux environment will very likely expect these files paths to be setup correctly. + +The following filesystems MUST be made available in each application's filesystem + +| Path | Type | +| -------- | ------ | +| /proc | [procfs](https://www.kernel.org/doc/Documentation/filesystems/proc.txt) | +| /sys | [sysfs](https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt) | +| /dev/pts | [devpts](https://www.kernel.org/doc/Documentation/filesystems/devpts.txt) | +| /dev/shm | [tmpfs](https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt) | + +## Namespaces + +A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. +Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes. +For more information, see [the man page](http://man7.org/linux/man-pages/man7/namespaces.7.html). + +Namespaces are specified as an array of entries inside the `namespaces` root field. +The following parameters can be specified to setup namespaces: + +* **`type`** *(string, required)* - namespace type. The following namespaces types are supported: + * **`pid`** processes inside the container will only be able to see other processes inside the same container + * **`network`** the container will have its own network stack + * **`mount`** the container will have an isolated mount table + * **`ipc`** processes inside the container will only be able to communicate to other processes inside the same container via system level IPC + * **`uts`** the container will be able to have its own hostname and domain name + * **`user`** the container will be able to remap user and group IDs from the host to local users and groups within the container + +* **`path`** *(string, optional)* - path to namespace file + +If a path is specified, that particular file is used to join that type of namespace. +Also, when a path is specified, a runtime MUST assume that the setup for that particular namespace has already been done and error out if the config specifies anything else related to that namespace. + +###### Example + +```json + "namespaces": [ + { + "type": "pid", + "path": "/proc/1234/ns/pid" + }, + { + "type": "network", + "path": "/var/run/netns/neta" + }, + { + "type": "mount" + }, + { + "type": "ipc" + }, + { + "type": "uts" + }, + { + "type": "user" + } + ] +``` + ## User namespace mappings +###### Example + ```json "uidMappings": [ { @@ -40,24 +105,505 @@ The mappings represent how the bundle `rootfs` expects the user namespace to be *hostID* is the starting uid/gid on the host to be mapped to *containerID* which is the starting uid/gid in the container and *size* refers to the number of ids to be mapped. There is a limit of 5 mappings which is the Linux kernel hard limit. -## Default Devices and File Systems +## Devices -The Linux ABI includes both syscalls and several special file paths. -Applications expecting a Linux environment will very likely expect these files paths to be setup correctly. +`devices` is an array specifying the list of devices that MUST be available in the container. +The runtime may supply them however it likes (with [mknod][mknod.2], by bind mounting from the runtime mount namespace, etc.). + +The following parameters can be specified: + +* **`type`** *(char, required)* - type of device: `c`, `b`, `u` or `p`. + More info in [mknod(1)][mknod.1]. +* **`path`** *(string, required)* - full path to device inside container. +* **`major, minor`** *(int64, required unless **`type`** is `p`)* - [major, minor numbers][devices] for the device. +* **`fileMode`** *(uint32, optional)* - file mode for the device. + You can also control access to devices [with cgroups](#device-whitelist). +* **`uid`** *(uint32, optional)* - id of device owner. +* **`gid`** *(uint32, optional)* - id of device group. + +###### Example + +```json + "devices": [ + { + "path": "/dev/fuse", + "type": "c", + "major": 10, + "minor": 229, + "fileMode": 0666, + "uid": 0, + "gid": 0 + }, + { + "path": "/dev/sda", + "type": "b", + "major": 8, + "minor": 0, + "fileMode": 0660, + "uid": 0, + "gid": 0 + } + ] +``` + +###### Default Devices + +In addition to any devices configured with this setting, the runtime MUST also supply: + +* [`/dev/null`][null.4] +* [`/dev/zero`][zero.4] +* [`/dev/full`][full.4] +* [`/dev/random`][random.4] +* [`/dev/urandom`][random.4] +* [`/dev/tty`][tty.4] +* [`/dev/console`][console.4] +* [`/dev/ptmx`][pts.4]. + A [bind-mount or symlink of the container's `/dev/pts/ptmx`][devpts]. + +## Control groups + +Also known as cgroups, they are used to restrict resource usage for a container and handle device access. +cgroups provide controls to restrict cpu, memory, IO, pids and network for the container. +For more information, see the [kernel cgroups documentation][cgroup-v1]. + +The path to the cgroups can be specified in the Spec via `cgroupsPath`. +`cgroupsPath` is expected to be relative to the cgroups mount point. +If `cgroupsPath` is not specified, implementations can define the default cgroup path. +Implementations of the Spec can choose to name cgroups in any manner. +The Spec does not include naming schema for cgroups. +The Spec does not support [split hierarchy][cgroup-v2]. +The cgroups will be created if they don't exist. + +###### Example + +```json + "cgroupsPath": "/myRuntime/myContainer" +``` + +`cgroupsPath` can be used to either control the cgroups hierarchy for containers or to run a new process in an existing container. + +You can configure a container's cgroups via the `resources` field of the Linux configuration. +Do not specify `resources` unless limits have to be updated. +For example, to run a new process in an existing container without updating limits, `resources` need not be specified. + +#### Device whitelist + +`devices` is an array of entries to control the [device whitelist][cgroup-v1-devices]. +The runtime MUST apply entries in the listed order. + +The following parameters can be specified: + +* **`allow`** *(boolean, required)* - whether the entry is allowed or denied. +* **`type`** *(char, optional)* - type of device: `a` (all), `c` (char), or `b` (block). + `null` or unset values mean "all", mapping to `a`. +* **`major, minor`** *(int64, optional)* - [major, minor numbers][devices] for the device. + `null` or unset values mean "all", mapping to [`*` in the filesystem API][cgroup-v1-devices]. +* **`access`** *(string, optional)* - cgroup permissions for device. + A composition of `r` (read), `w` (write), and `m` (mknod). + +###### Example + +```json + "devices": [ + { + "allow": false, + "access": "rwm" + }, + { + "allow": true, + "type": "c", + "major": 10, + "minor": 229, + "access": "rw" + }, + { + "allow": true, + "type": "b", + "major": 8, + "minor": 0, + "access": "r" + } + ] +``` + +#### Disable out-of-memory killer + +`disableOOMKiller` contains a boolean (`true` or `false`) that enables or disables the Out of Memory killer for a cgroup. +If enabled (`false`), tasks that attempt to consume more memory than they are allowed are immediately killed by the OOM killer. +The OOM killer is enabled by default in every cgroup using the `memory` subsystem. +To disable it, specify a value of `true`. +For more information, see [the memory cgroup man page][cgroup-v1-memory]. + +* **`disableOOMKiller`** *(bool, optional)* - enables or disables the OOM killer + +###### Example + +```json + "disableOOMKiller": false +``` + +#### Set oom_score_adj + +`oomScoreAdj` sets heuristic regarding how the process is evaluated by the kernel during memory pressure. +For more information, see [the proc filesystem documentation section 3.1](https://www.kernel.org/doc/Documentation/filesystems/proc.txt). +This is a kernel/system level setting, where as `disableOOMKiller` is scoped for a memory cgroup. +For more information on how these two settings work together, see [the memory cgroup documentation section 10. OOM Contol][cgroup-v1-memory]. + +* **`oomScoreAdj`** *(int, optional)* - adjust the oom-killer score + +###### Example + +```json + "oomScoreAdj": 0 +``` + +#### Memory + +`memory` represents the cgroup subsystem `memory` and it's used to set limits on the container's memory usage. +For more information, see [the memory cgroup man page][cgroup-v1-memory]. + +The following parameters can be specified to setup the controller: + +* **`limit`** *(uint64, optional)* - sets limit of memory usage + +* **`reservation`** *(uint64, optional)* - sets soft limit of memory usage + +* **`swap`** *(uint64, optional)* - sets limit of memory+Swap usage + +* **`kernel`** *(uint64, optional)* - sets hard limit for kernel memory + +* **`kernelTCP`** *(uint64, optional)* - sets hard limit for kernel memory in tcp using + +* **`swappiness`** *(uint64, optional)* - sets swappiness parameter of vmscan (See sysctl's vm.swappiness) + +###### Example + +```json + "memory": { + "limit": 0, + "reservation": 0, + "swap": 0, + "kernel": 0, + "kernelTCP": 0, + "swappiness": 0 + } +``` + +#### CPU + +`cpu` represents the cgroup subsystems `cpu` and `cpusets`. +For more information, see [the cpusets cgroup man page][cgroup-v1-cpusets]. + +The following parameters can be specified to setup the controller: + +* **`shares`** *(uint64, optional)* - specifies a relative share of CPU time available to the tasks in a cgroup + +* **`quota`** *(uint64, optional)* - specifies the total amount of time in microseconds for which all tasks in a cgroup can run during one period (as defined by **`period`** below) + +* **`period`** *(uint64, optional)* - specifies a period of time in microseconds for how regularly a cgroup's access to CPU resources should be reallocated (CFS scheduler only) + +* **`realtimeRuntime`** *(uint64, optional)* - specifies a period of time in microseconds for the longest continuous period in which the tasks in a cgroup have access to CPU resources + +* **`realtimePeriod`** *(uint64, optional)* - same as **`period`** but applies to realtime scheduler only + +* **`cpus`** *(string, optional)* - list of CPUs the container will run in + +* **`mems`** *(string, optional)* - list of Memory Nodes the container will run in + +###### Example + +```json + "cpu": { + "shares": 0, + "quota": 0, + "period": 0, + "realtimeRuntime": 0, + "realtimePeriod": 0, + "cpus": "", + "mems": "" + } +``` + +#### Block IO Controller + +`blockIO` represents the cgroup subsystem `blkio` which implements the block io controller. +For more information, see [the kernel cgroups documentation about blkio][cgroup-v1-blkio]. + +The following parameters can be specified to setup the controller: + +* **`blkioWeight`** *(uint16, optional)* - specifies per-cgroup weight. This is default weight of the group on all devices until and unless overridden by per-device rules. The range is from 10 to 1000. + +* **`blkioLeafWeight`** *(uint16, optional)* - equivalents of `blkioWeight` for the purpose of deciding how much weight tasks in the given cgroup has while competing with the cgroup's child cgroups. The range is from 10 to 1000. + +* **`blkioWeightDevice`** *(array, optional)* - specifies the list of devices which will be bandwidth rate limited. The following parameters can be specified per-device: + * **`major, minor`** *(int64, required)* - major, minor numbers for device. More info in `man mknod`. + * **`weight`** *(uint16, optional)* - bandwidth rate for the device, range is from 10 to 1000 + * **`leafWeight`** *(uint16, optional)* - bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only + + You must specify at least one of `weight` or `leafWeight` in a given entry, and can specify both. + +* **`blkioThrottleReadBpsDevice`**, **`blkioThrottleWriteBpsDevice`**, **`blkioThrottleReadIOPSDevice`**, **`blkioThrottleWriteIOPSDevice`** *(array, optional)* - specify the list of devices which will be IO rate limited. The following parameters can be specified per-device: + * **`major, minor`** *(int64, required)* - major, minor numbers for device. More info in `man mknod`. + * **`rate`** *(uint64, required)* - IO rate limit for the device + +###### Example + +```json + "blockIO": { + "blkioWeight": 0, + "blkioLeafWeight": 0, + "blkioWeightDevice": [ + { + "major": 8, + "minor": 0, + "weight": 500, + "leafWeight": 300 + }, + { + "major": 8, + "minor": 16, + "weight": 500 + } + ], + "blkioThrottleReadBpsDevice": [ + { + "major": 8, + "minor": 0, + "rate": 600 + } + ], + "blkioThrottleWriteIOPSDevice": [ + { + "major": 8, + "minor": 16, + "rate": 300 + } + ] + } +``` + +#### Huge page limits + +`hugepageLimits` represents the `hugetlb` controller which allows to limit the +HugeTLB usage per control group and enforces the controller limit during page fault. +For more information, see the [kernel cgroups documentation about HugeTLB][cgroup-v1-hugetlb]. + +`hugepageLimits` is an array of entries, each having the following structure: + +* **`pageSize`** *(string, required)* - hugepage size + +* **`limit`** *(uint64, required)* - limit in bytes of *hugepagesize* HugeTLB usage + +###### Example + +```json + "hugepageLimits": [ + { + "pageSize": "2MB", + "limit": 9223372036854771712 + } + ] +``` + +#### Network + +`network` represents the cgroup subsystems `net_cls` and `net_prio`. +For more information, see [the net\_cls cgroup man page][cgroup-v1-net-cls] and [the net\_prio cgroup man page][cgroup-v1-net-prio]. + +The following parameters can be specified to setup these cgroup controllers: + +* **`classID`** *(uint32, optional)* - is the network class identifier the cgroup's network packets will be tagged with + +* **`priorities`** *(array, optional)* - specifies a list of objects of the priorities assigned to traffic originating from +processes in the group and egressing the system on various interfaces. The following parameters can be specified per-priority: + * **`name`** *(string, required)* - interface name + * **`priority`** *(uint32, required)* - priority applied to the interface + +###### Example + +```json + "network": { + "classID": 1048577, + "priorities": [ + { + "name": "eth0", + "priority": 500 + }, + { + "name": "eth1", + "priority": 1000 + } + ] + } +``` + +#### PIDs + +`pids` represents the cgroup subsystem `pids`. +For more information, see [the pids cgroup man page][cgroup-v1-pids]. + +The following paramters can be specified to setup the controller: + +* **`limit`** *(int64, required)* - specifies the maximum number of tasks in the cgroup + +###### Example + +```json + "pids": { + "limit": 32771 + } +``` + +## Sysctl + +sysctl allows kernel parameters to be modified at runtime for the container. +For more information, see [the man page](http://man7.org/linux/man-pages/man8/sysctl.8.html) + +###### Example + +```json + "sysctl": { + "net.ipv4.ip_forward": "1", + "net.core.somaxconn": "256" + } +``` + +## Rlimits + +rlimits allow setting resource limits. +`type` is a string with a value from those defined in [the man page](http://man7.org/linux/man-pages/man2/setrlimit.2.html). +The kernel enforces the `soft` limit for a resource while the `hard` limit acts as a ceiling for that value that could be set by an unprivileged process. + +###### Example + +```json + "rlimits": [ + { + "type": "RLIMIT_NPROC", + "soft": 1024, + "hard": 102400 + } + ] +``` + +## SELinux process label + +SELinux process label specifies the label with which the processes in a container are run. +For more information about SELinux, see [Selinux documentation](http://selinuxproject.org/page/Main_Page) + +###### Example + +```json + "selinuxProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c124,c675" +``` + +## Apparmor profile + +Apparmor profile specifies the name of the apparmor profile that will be used for the container. +For more information about Apparmor, see [Apparmor documentation](https://wiki.ubuntu.com/AppArmor) + +###### Example + +```json + "apparmorProfile": "acme_secure_profile" +``` + +## seccomp + +Seccomp provides application sandboxing mechanism in the Linux kernel. +Seccomp configuration allows one to configure actions to take for matched syscalls and furthermore also allows matching on values passed as arguments to syscalls. +For more information about Seccomp, see [Seccomp kernel documentation](https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt) +The actions, architectures, and operators are strings that match the definitions in seccomp.h from [libseccomp](https://github.com/seccomp/libseccomp) and are translated to corresponding values. +A valid list of constants as of Libseccomp v2.2.3 is contained below. + +Architecture Constants +* `SCMP_ARCH_X86` +* `SCMP_ARCH_X86_64` +* `SCMP_ARCH_X32` +* `SCMP_ARCH_ARM` +* `SCMP_ARCH_AARCH64` +* `SCMP_ARCH_MIPS` +* `SCMP_ARCH_MIPS64` +* `SCMP_ARCH_MIPS64N32` +* `SCMP_ARCH_MIPSEL` +* `SCMP_ARCH_MIPSEL64` +* `SCMP_ARCH_MIPSEL64N32` + +Action Constants: +* `SCMP_ACT_KILL` +* `SCMP_ACT_TRAP` +* `SCMP_ACT_ERRNO` +* `SCMP_ACT_TRACE` +* `SCMP_ACT_ALLOW` + +Operator Constants: +* `SCMP_CMP_NE` +* `SCMP_CMP_LT` +* `SCMP_CMP_LE` +* `SCMP_CMP_EQ` +* `SCMP_CMP_GE` +* `SCMP_CMP_GT` +* `SCMP_CMP_MASKED_EQ` + +###### Example + +```json + "seccomp": { + "defaultAction": "SCMP_ACT_ALLOW", + "architectures": [ + "SCMP_ARCH_X86" + ], + "syscalls": [ + { + "name": "getcwd", + "action": "SCMP_ACT_ERRNO" + } + ] + } +``` + +## Rootfs Mount Propagation + +rootfsPropagation sets the rootfs's mount propagation. +Its value is either slave, private, or shared. +[The kernel doc](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) has more information about mount propagation. + +###### Example + +```json + "rootfsPropagation": "slave", +``` + +## No new privileges + +Setting `noNewPrivileges` to true prevents the processes in the container from gaining additional privileges. +[The kernel doc](https://www.kernel.org/doc/Documentation/prctl/no_new_privs.txt) has more information on how this is achieved using a prctl system call. + +###### Example + +```json + "noNewPrivileges": true, +``` + +[cgroup-v1]: https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt +[cgroup-v1-blkio]: https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt +[cgroup-v1-cpusets]: https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt +[cgroup-v1-devices]: https://www.kernel.org/doc/Documentation/cgroup-v1/devices.txt +[cgroup-v1-hugetlb]: https://www.kernel.org/doc/Documentation/cgroup-v1/hugetlb.txt +[cgroup-v1-memory]: https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt +[cgroup-v1-net-cls]: https://www.kernel.org/doc/Documentation/cgroup-v1/net_cls.txt +[cgroup-v1-net-prio]: https://www.kernel.org/doc/Documentation/cgroup-v1/net_prio.txt +[cgroup-v1-pids]: https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt +[cgroup-v2]: https://www.kernel.org/doc/Documentation/cgroup-v2.txt +[devices]: https://www.kernel.org/doc/Documentation/devices.txt +[devpts]: https://www.kernel.org/doc/Documentation/filesystems/devpts.txt -The following devices and filesystems MUST be made available in each application's filesystem - -| Path | Type | Notes | -| ------------ | ------ | ------- | -| /proc | [procfs](https://www.kernel.org/doc/Documentation/filesystems/proc.txt) | | -| /sys | [sysfs](https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt) | | -| /dev/null | [device](http://man7.org/linux/man-pages/man4/null.4.html) | | -| /dev/zero | [device](http://man7.org/linux/man-pages/man4/zero.4.html) | | -| /dev/full | [device](http://man7.org/linux/man-pages/man4/full.4.html) | | -| /dev/random | [device](http://man7.org/linux/man-pages/man4/random.4.html) | | -| /dev/urandom | [device](http://man7.org/linux/man-pages/man4/random.4.html) | | -| /dev/tty | [device](http://man7.org/linux/man-pages/man4/tty.4.html) | | -| /dev/console | [device](http://man7.org/linux/man-pages/man4/console.4.html) | | -| /dev/pts | [devpts](https://www.kernel.org/doc/Documentation/filesystems/devpts.txt) | | -| /dev/ptmx | [device](https://www.kernel.org/doc/Documentation/filesystems/devpts.txt) | Bind-mount or symlink of /dev/pts/ptmx | -| /dev/shm | [tmpfs](https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt) | | +[mknod.1]: http://man7.org/linux/man-pages/man1/mknod.1.html +[mknod.2]: http://man7.org/linux/man-pages/man2/mknod.2.html +[console.4]: http://man7.org/linux/man-pages/man4/console.4.html +[full.4]: http://man7.org/linux/man-pages/man4/full.4.html +[null.4]: http://man7.org/linux/man-pages/man4/null.4.html +[pts.4]: http://man7.org/linux/man-pages/man4/pts.4.html +[random.4]: http://man7.org/linux/man-pages/man4/random.4.html +[tty.4]: http://man7.org/linux/man-pages/man4/tty.4.html +[zero.4]: http://man7.org/linux/man-pages/man4/zero.4.html diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/config.go b/Godeps/_workspace/src/github.com/opencontainers/specs/config.go index c2d987500..7861c5638 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/config.go +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/config.go @@ -1,10 +1,11 @@ package specs // Spec is the base configuration for the container. It specifies platform -// independent configuration. +// independent configuration. This information must be included when the +// bundle is packaged for distribution. type Spec struct { // Version is the version of the specification that is supported. - Version string `json:"version"` + Version string `json:"ociVersion"` // Platform is the host information for OS and Arch. Platform Platform `json:"platform"` // Process is the container's main process. @@ -12,9 +13,11 @@ type Spec struct { // Root is the root information for the container's filesystem. Root Root `json:"root"` // Hostname is the container's host name. - Hostname string `json:"hostname"` + Hostname string `json:"hostname,omitempty"` // Mounts profile configuration for adding mounts to the container's filesystem. - Mounts []MountPoint `json:"mounts"` + Mounts []Mount `json:"mounts"` + // Hooks are the commands run at various lifecycle events of the container. + Hooks Hooks `json:"hooks"` } // Process contains information to start a specific application inside the container. @@ -26,7 +29,7 @@ type Process struct { // Args specifies the binary and arguments for the application to execute. Args []string `json:"args"` // Env populates the process environment for the process. - Env []string `json:"env"` + Env []string `json:"env,omitempty"` // Cwd is the current working directory for the process and must be // relative to the container's root. Cwd string `json:"cwd"` @@ -49,22 +52,33 @@ type Platform struct { Arch string `json:"arch"` } -// MountPoint describes a directory that may be fullfilled by a mount in the runtime.json. -type MountPoint struct { - // Name is a unique descriptive identifier for this mount point. - Name string `json:"name"` - // Path specifies the path of the mount. The path and child directories MUST exist, a runtime MUST NOT create directories automatically to a mount point. - Path string `json:"path"` +// Mount specifies a mount for a container. +type Mount struct { + // Destination is the path where the mount will be placed relative to the container's root. The path and child directories MUST exist, a runtime MUST NOT create directories automatically to a mount point. + Destination string `json:"destination"` + // Type specifies the mount kind. + Type string `json:"type"` + // Source specifies the source path of the mount. In the case of bind mounts on + // linux based systems this would be the file on the host. + Source string `json:"source"` + // Options are fstab style mount options. + Options []string `json:"options,omitempty"` } -// State holds information about the runtime state of the container. -type State struct { - // Version is the version of the specification that is supported. - Version string `json:"version"` - // ID is the container ID - ID string `json:"id"` - // Pid is the process id for the container's main process. - Pid int `json:"pid"` - // BundlePath is the path to the container's bundle directory. - BundlePath string `json:"bundlePath"` +// Hook specifies a command that is run at a particular event in the lifecycle of a container +type Hook struct { + Path string `json:"path"` + Args []string `json:"args,omitempty"` + Env []string `json:"env,omitempty"` +} + +// Hooks for container setup and teardown +type Hooks struct { + // Prestart is a list of hooks to be run before the container process is executed. + // On Linux, they are run after the container namespaces are created. + Prestart []Hook `json:"prestart,omitempty"` + // Poststart is a list of hooks to be run after the container process is started. + Poststart []Hook `json:"poststart,omitempty"` + // Poststop is a list of hooks to be run after the container process exits. + Poststop []Hook `json:"poststop,omitempty"` } diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/config.md b/Godeps/_workspace/src/github.com/opencontainers/specs/config.md index 4e98d2996..884fe32cb 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/config.md +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/config.md @@ -8,14 +8,17 @@ This includes the process to run, environment variables to inject, sandboxing fe Below is a detailed description of each field defined in the configuration format. -## Manifest version +## Specification version -* **`version`** (string, required) must be in [SemVer v2.0.0](http://semver.org/spec/v2.0.0.html) format and specifies the version of the OCF specification with which the container bundle complies. The Open Container spec follows semantic versioning and retains forward and backward compatibility within major versions. For example, if an implementation is compliant with version 1.0.1 of the spec, it is compatible with the complete 1.x series. +* **`ociVersion`** (string, required) must be in [SemVer v2.0.0](http://semver.org/spec/v2.0.0.html) format and specifies the version of the OpenContainer specification with which the bundle complies. +The OpenContainer spec follows semantic versioning and retains forward and backward compatibility within major versions. +For example, if an implementation is compliant with version 1.0.1 of the spec, it is compatible with the complete 1.x series. +NOTE that there is no guarantee for forward or backward compatibility for version 0.x. *Example* ```json - "version": "0.1.0" + "ociVersion": "0.1.0" ``` ## Root Configuration @@ -34,42 +37,56 @@ Each container has exactly one *root filesystem*, specified in the *root* object } ``` -## Mount Points +## Mounts You can add array of mount points inside container as `mounts`. -Each record in this array must have configuration in [runtime config](runtime-config.md#mount-configuration). The runtime MUST mount entries in the listed order. +The parameters are similar to the ones in [the Linux mount system call](http://man7.org/linux/man-pages/man2/mount.2.html). -* **`name`** (string, required) Name of mount point. Used for config lookup. -* **`path`** (string, required) Destination of mount point: path inside container. +* **`destination`** (string, required) Destination of mount point: path inside container. +* **`type`** (string, required) Linux, *filesystemtype* argument supported by the kernel are listed in */proc/filesystems* (e.g., "minix", "ext2", "ext3", "jfs", "xfs", "reiserfs", "msdos", "proc", "nfs", "iso9660"). Windows: ntfs +* **`source`** (string, required) a device name, but can also be a directory name or a dummy. Windows, the volume name that is the target of the mount point. \\?\Volume\{GUID}\ (on Windows source is called target) +* **`options`** (list of strings, optional) in the fstab format [https://wiki.archlinux.org/index.php/Fstab](https://wiki.archlinux.org/index.php/Fstab). -*Example* +### Linux Example ```json "mounts": [ { - "name": "proc", - "path": "/proc" - }, - { - "name": "dev", - "path": "/dev" - }, - { - "name": "devpts", - "path": "/dev/pts" + "destination": "/tmp", + "type": "tmpfs", + "source": "tmpfs", + "options": ["nosuid","strictatime","mode=755","size=65536k"] }, { - "name": "data", - "path": "/data" + "destination": "/data", + "type": "bind", + "source": "/volumes/testing", + "options": ["rbind","rw"] + } +] +``` + +### Windows Example + +```json +"mounts": [ + "myfancymountpoint": { + "destination": "C:\\Users\\crosbymichael\\My Fancy Mount Point\\", + "type": "ntfs", + "source": "\\\\?\\Volume\\{2eca078d-5cbc-43d3-aff8-7e8511f60d0e}\\", + "options": [] } ] ``` +See links for details about [mountvol](http://ss64.com/nt/mountvol.html) and [SetVolumeMountPoint](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365561(v=vs.85).aspx) in Windows. + + ## Process configuration * **`terminal`** (bool, optional) specifies whether you want a terminal attached to that process. Defaults to false. -* **`cwd`** (string, optional) is the working directory that will be set for the executable. +* **`cwd`** (string, required) is the working directory that will be set for the executable. This value MUST be an absolute path. * **`env`** (array of strings, optional) contains a list of variables that will be set in the process's environment prior to execution. Elements in the array are specified as Strings in the form "KEY=value". The left hand side must consist solely of letters, digits, and underscores `_` as outlined in [IEEE Std 1003.1-2001](http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html). * **`args`** (string, required) executable to launch and any flags as an array. The executable is the first element and must be available at the given path inside of the rootfs. If the executable path is not an absolute path then the search $PATH is interpreted to find the executable. @@ -104,7 +121,7 @@ For Linux-based systems the user structure has the following fields: ## Hostname -* **`hostname`** (string, optional) as it is accessible to processes running inside. +* **`hostname`** (string, optional) as it is accessible to processes running inside. On Linux, you can only set this if your bundle creates a new [UTS namespace][uts-namespace]. *Example* @@ -126,3 +143,73 @@ For Linux-based systems the user structure has the following fields: Interpretation of the platform section of the JSON file is used to find which platform-specific sections may be available in the document. For example, if `os` is set to `linux`, then a JSON object conforming to the [Linux-specific schema](config-linux.md) SHOULD be found at the key `linux` in the `config.json`. + +## Hooks + +Lifecycle hooks allow custom events for different points in a container's runtime. +Presently there are `Prestart`, `Poststart` and `Poststop`. + +* [`Prestart`](#prestart) is a list of hooks to be run before the container process is executed +* [`Poststart`](#poststart) is a list of hooks to be run immediately after the container process is started +* [`Poststop`](#poststop) is a list of hooks to be run after the container process exits + +Hooks allow one to run code before/after various lifecycle events of the container. +Hooks MUST be called in the listed order. +The state of the container is passed to the hooks over stdin, so the hooks could get the information they need to do their work. + +Hook paths are absolute and are executed from the host's filesystem. + +### Prestart + +The pre-start hooks are called after the container process is spawned, but before the user supplied command is executed. +They are called after the container namespaces are created on Linux, so they provide an opportunity to customize the container. +In Linux, for e.g., the network namespace could be configured in this hook. + +If a hook returns a non-zero exit code, then an error including the exit code and the stderr is returned to the caller and the container is torn down. + +### Poststart + +The post-start hooks are called after the user process is started. +For example this hook can notify user that real process is spawned. + +If a hook returns a non-zero exit code, then an error is logged and the remaining hooks are executed. + +### Poststop + +The post-stop hooks are called after the container process is stopped. +Cleanup or debugging could be performed in such a hook. +If a hook returns a non-zero exit code, then an error is logged and the remaining hooks are executed. + +*Example* + +```json + "hooks" : { + "prestart": [ + { + "path": "/usr/bin/fix-mounts", + "args": ["fix-mounts", "arg1", "arg2"], + "env": [ "key1=value1"] + }, + { + "path": "/usr/bin/setup-network" + } + ], + "poststart": [ + { + "path": "/usr/bin/notify-start" + } + ], + "poststop": [ + { + "path": "/usr/sbin/cleanup.sh", + "args": ["cleanup.sh", "-f"] + } + ] + } +``` + +`path` is required for a hook. +`args` and `env` are optional. +The semantics are the same as `Path`, `Args` and `Env` in [golang Cmd](https://golang.org/pkg/os/exec/#Cmd). + +[uts-namespace]: http://man7.org/linux/man-pages/man7/namespaces.7.html diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/config_linux.go b/Godeps/_workspace/src/github.com/opencontainers/specs/config_linux.go index b9915538a..72027112f 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/config_linux.go +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/config_linux.go @@ -1,5 +1,10 @@ package specs +import "os" + +// LinuxStateDirectory holds the container's state information +const LinuxStateDirectory = "/run/opencontainer/containers" + // LinuxSpec is the full specification for linux containers. type LinuxSpec struct { Spec @@ -11,6 +16,35 @@ type LinuxSpec struct { type Linux struct { // Capabilities are linux capabilities that are kept for the container. Capabilities []string `json:"capabilities"` + // UIDMapping specifies user mappings for supporting user namespaces on linux. + UIDMappings []IDMapping `json:"uidMappings,omitempty"` + // GIDMapping specifies group mappings for supporting user namespaces on linux. + GIDMappings []IDMapping `json:"gidMappings,omitempty"` + // Rlimits specifies rlimit options to apply to the container's process. + Rlimits []Rlimit `json:"rlimits,omitempty"` + // Sysctl are a set of key value pairs that are set for the container on start + Sysctl map[string]string `json:"sysctl,omitempty"` + // Resources contain cgroup information for handling resource constraints + // for the container + Resources *Resources `json:"resources,omitempty"` + // CgroupsPath specifies the path to cgroups that are created and/or joined by the container. + // The path is expected to be relative to the cgroups mountpoint. + // If resources are specified, the cgroups at CgroupsPath will be updated based on resources. + CgroupsPath *string `json:"cgroupsPath,omitempty"` + // Namespaces contains the namespaces that are created and/or joined by the container + Namespaces []Namespace `json:"namespaces"` + // Devices are a list of device nodes that are created for the container + Devices []Device `json:"devices"` + // ApparmorProfile specified the apparmor profile for the container. + ApparmorProfile string `json:"apparmorProfile"` + // SelinuxProcessLabel specifies the selinux context that the container process is run as. + SelinuxProcessLabel string `json:"selinuxProcessLabel"` + // Seccomp specifies the seccomp security settings for the container. + Seccomp Seccomp `json:"seccomp"` + // RootfsPropagation is the rootfs mount propagation mode for the container. + RootfsPropagation string `json:"rootfsPropagation,omitempty"` + // NoNewPrivileges controls whether additional privileges could be gained by processes in the container. + NoNewPrivileges bool `json:"noNewPrivileges,omitempty"` } // User specifies linux specific user and group information for the container's @@ -21,5 +55,279 @@ type User struct { // GID is the group id. GID uint32 `json:"gid"` // AdditionalGids are additional group ids set for the container's process. - AdditionalGids []uint32 `json:"additionalGids"` + AdditionalGids []uint32 `json:"additionalGids,omitempty"` +} + +// Namespace is the configuration for a linux namespace +type Namespace struct { + // Type is the type of Linux namespace + Type NamespaceType `json:"type"` + // Path is a path to an existing namespace persisted on disk that can be joined + // and is of the same type + Path string `json:"path,omitempty"` +} + +// NamespaceType is one of the linux namespaces +type NamespaceType string + +const ( + // PIDNamespace for isolating process IDs + PIDNamespace NamespaceType = "pid" + // NetworkNamespace for isolating network devices, stacks, ports, etc + NetworkNamespace = "network" + // MountNamespace for isolating mount points + MountNamespace = "mount" + // IPCNamespace for isolating System V IPC, POSIX message queues + IPCNamespace = "ipc" + // UTSNamespace for isolating hostname and NIS domain name + UTSNamespace = "uts" + // UserNamespace for isolating user and group IDs + UserNamespace = "user" +) + +// IDMapping specifies UID/GID mappings +type IDMapping struct { + // HostID is the UID/GID of the host user or group + HostID uint32 `json:"hostID"` + // ContainerID is the UID/GID of the container's user or group + ContainerID uint32 `json:"containerID"` + // Size is the length of the range of IDs mapped between the two namespaces + Size uint32 `json:"size"` +} + +// Rlimit type and restrictions +type Rlimit struct { + // Type of the rlimit to set + Type string `json:"type"` + // Hard is the hard limit for the specified type + Hard uint64 `json:"hard"` + // Soft is the soft limit for the specified type + Soft uint64 `json:"soft"` +} + +// HugepageLimit structure corresponds to limiting kernel hugepages +type HugepageLimit struct { + // Pagesize is the hugepage size + Pagesize *string `json:"pageSize,omitempty"` + // Limit is the limit of "hugepagesize" hugetlb usage + Limit *uint64 `json:"limit,omitempty"` +} + +// InterfacePriority for network interfaces +type InterfacePriority struct { + // Name is the name of the network interface + Name string `json:"name"` + // Priority for the interface + Priority uint32 `json:"priority"` +} + +// blockIODevice holds major:minor format supported in blkio cgroup +type blockIODevice struct { + // Major is the device's major number. + Major int64 `json:"major"` + // Minor is the device's minor number. + Minor int64 `json:"minor"` +} + +// WeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice +type WeightDevice struct { + blockIODevice + // Weight is the bandwidth rate for the device, range is from 10 to 1000 + Weight *uint16 `json:"weight,omitempty"` + // LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only + LeafWeight *uint16 `json:"leafWeight,omitempty"` +} + +// ThrottleDevice struct holds a `major:minor rate_per_second` pair +type ThrottleDevice struct { + blockIODevice + // Rate is the IO rate limit per cgroup per device + Rate *uint64 `json:"rate,omitempty"` +} + +// BlockIO for Linux cgroup 'blkio' resource management +type BlockIO struct { + // Specifies per cgroup weight, range is from 10 to 1000 + Weight *uint16 `json:"blkioWeight,omitempty"` + // Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only + LeafWeight *uint16 `json:"blkioLeafWeight,omitempty"` + // Weight per cgroup per device, can override BlkioWeight + WeightDevice []WeightDevice `json:"blkioWeightDevice,omitempty"` + // IO read rate limit per cgroup per device, bytes per second + ThrottleReadBpsDevice []ThrottleDevice `json:"blkioThrottleReadBpsDevice,omitempty"` + // IO write rate limit per cgroup per device, bytes per second + ThrottleWriteBpsDevice []ThrottleDevice `json:"blkioThrottleWriteBpsDevice,omitempty"` + // IO read rate limit per cgroup per device, IO per second + ThrottleReadIOPSDevice []ThrottleDevice `json:"blkioThrottleReadIOPSDevice,omitempty"` + // IO write rate limit per cgroup per device, IO per second + ThrottleWriteIOPSDevice []ThrottleDevice `json:"blkioThrottleWriteIOPSDevice,omitempty"` +} + +// Memory for Linux cgroup 'memory' resource management +type Memory struct { + // Memory limit (in bytes). + Limit *uint64 `json:"limit,omitempty"` + // Memory reservation or soft_limit (in bytes). + Reservation *uint64 `json:"reservation,omitempty"` + // Total memory limit (memory + swap). + Swap *uint64 `json:"swap,omitempty"` + // Kernel memory limit (in bytes). + Kernel *uint64 `json:"kernel,omitempty"` + // Kernel memory limit for tcp (in bytes) + KernelTCP *uint64 `json:"kernelTCP"` + // How aggressive the kernel will swap memory pages. Range from 0 to 100. + Swappiness *uint64 `json:"swappiness,omitempty"` +} + +// CPU for Linux cgroup 'cpu' resource management +type CPU struct { + // CPU shares (relative weight (ratio) vs. other cgroups with cpu shares). + Shares *uint64 `json:"shares,omitempty"` + // CPU hardcap limit (in usecs). Allowed cpu time in a given period. + Quota *uint64 `json:"quota,omitempty"` + // CPU period to be used for hardcapping (in usecs). + Period *uint64 `json:"period,omitempty"` + // How much time realtime scheduling may use (in usecs). + RealtimeRuntime *uint64 `json:"realtimeRuntime,omitempty"` + // CPU period to be used for realtime scheduling (in usecs). + RealtimePeriod *uint64 `json:"realtimePeriod,omitempty"` + // CPUs to use within the cpuset. Default is to use any CPU available. + Cpus *string `json:"cpus,omitempty"` + // List of memory nodes in the cpuset. Default is to use any available memory node. + Mems *string `json:"mems,omitempty"` +} + +// Pids for Linux cgroup 'pids' resource management (Linux 4.3) +type Pids struct { + // Maximum number of PIDs. Default is "no limit". + Limit *int64 `json:"limit,omitempty"` +} + +// Network identification and priority configuration +type Network struct { + // Set class identifier for container's network packets + ClassID *uint32 `json:"classID"` + // Set priority of network traffic for container + Priorities []InterfacePriority `json:"priorities,omitempty"` +} + +// Resources has container runtime resource constraints +type Resources struct { + // Devices are a list of device rules for the whitelist controller + Devices []DeviceCgroup `json:"devices"` + // DisableOOMKiller disables the OOM killer for out of memory conditions + DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"` + // Specify an oom_score_adj for the container. + OOMScoreAdj *int `json:"oomScoreAdj,omitempty"` + // Memory restriction configuration + Memory *Memory `json:"memory,omitempty"` + // CPU resource restriction configuration + CPU *CPU `json:"cpu,omitempty"` + // Task resource restriction configuration. + Pids *Pids `json:"pids,omitempty"` + // BlockIO restriction configuration + BlockIO *BlockIO `json:"blockIO,omitempty"` + // Hugetlb limit (in bytes) + HugepageLimits []HugepageLimit `json:"hugepageLimits,omitempty"` + // Network restriction configuration + Network *Network `json:"network,omitempty"` +} + +// Device represents the mknod information for a Linux special device file +type Device struct { + // Path to the device. + Path string `json:"path"` + // Device type, block, char, etc. + Type rune `json:"type"` + // Major is the device's major number. + Major int64 `json:"major"` + // Minor is the device's minor number. + Minor int64 `json:"minor"` + // FileMode permission bits for the device. + FileMode *os.FileMode `json:"fileMode,omitempty"` + // UID of the device. + UID *uint32 `json:"uid,omitempty"` + // Gid of the device. + GID *uint32 `json:"gid,omitempty"` +} + +// DeviceCgroup represents a device rule for the whitelist controller +type DeviceCgroup struct { + // Allow or deny + Allow bool `json:"allow"` + // Device type, block, char, etc. + Type *rune `json:"type,omitempty"` + // Major is the device's major number. + Major *int64 `json:"major,omitempty"` + // Minor is the device's minor number. + Minor *int64 `json:"minor,omitempty"` + // Cgroup access permissions format, rwm. + Access *string `json:"access,omitempty"` +} + +// Seccomp represents syscall restrictions +type Seccomp struct { + DefaultAction Action `json:"defaultAction"` + Architectures []Arch `json:"architectures"` + Syscalls []Syscall `json:"syscalls,omitempty"` +} + +// Arch used for additional architectures +type Arch string + +// Additional architectures permitted to be used for system calls +// By default only the native architecture of the kernel is permitted +const ( + ArchX86 Arch = "SCMP_ARCH_X86" + ArchX86_64 Arch = "SCMP_ARCH_X86_64" + ArchX32 Arch = "SCMP_ARCH_X32" + ArchARM Arch = "SCMP_ARCH_ARM" + ArchAARCH64 Arch = "SCMP_ARCH_AARCH64" + ArchMIPS Arch = "SCMP_ARCH_MIPS" + ArchMIPS64 Arch = "SCMP_ARCH_MIPS64" + ArchMIPS64N32 Arch = "SCMP_ARCH_MIPS64N32" + ArchMIPSEL Arch = "SCMP_ARCH_MIPSEL" + ArchMIPSEL64 Arch = "SCMP_ARCH_MIPSEL64" + ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32" +) + +// Action taken upon Seccomp rule match +type Action string + +// Define actions for Seccomp rules +const ( + ActKill Action = "SCMP_ACT_KILL" + ActTrap Action = "SCMP_ACT_TRAP" + ActErrno Action = "SCMP_ACT_ERRNO" + ActTrace Action = "SCMP_ACT_TRACE" + ActAllow Action = "SCMP_ACT_ALLOW" +) + +// Operator used to match syscall arguments in Seccomp +type Operator string + +// Define operators for syscall arguments in Seccomp +const ( + OpNotEqual Operator = "SCMP_CMP_NE" + OpLessThan Operator = "SCMP_CMP_LT" + OpLessEqual Operator = "SCMP_CMP_LE" + OpEqualTo Operator = "SCMP_CMP_EQ" + OpGreaterEqual Operator = "SCMP_CMP_GE" + OpGreaterThan Operator = "SCMP_CMP_GT" + OpMaskedEqual Operator = "SCMP_CMP_MASKED_EQ" +) + +// Arg used for matching specific syscall arguments in Seccomp +type Arg struct { + Index uint `json:"index"` + Value uint64 `json:"value"` + ValueTwo uint64 `json:"valueTwo"` + Op Operator `json:"op"` +} + +// Syscall is used to match a syscall in Seccomp +type Syscall struct { + Name string `json:"name"` + Action Action `json:"action"` + Args []Arg `json:"args,omitempty"` } diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/implementations.md b/Godeps/_workspace/src/github.com/opencontainers/specs/implementations.md index b9401e5b2..5cd16b987 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/implementations.md +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/implementations.md @@ -11,6 +11,11 @@ If you know of any associated projects that are not listed here, please file a p * [hyperhq/runv](https://github.com/hyperhq/runv) - Hypervisor-based runtime for OCI +## Bundle authoring + +* [kunalkushwaha/octool](https://github.com/kunalkushwaha/octool) - A config linter and validator. +* [mrunalp/ocitools](https://github.com/mrunalp/ocitools) - A config generator. + ## Testing * [huawei-openlab/oct](https://github.com/huawei-openlab/oct) - Open Container Testing framework for OCI configuration and runtime diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime-config-linux.md b/Godeps/_workspace/src/github.com/opencontainers/specs/runtime-config-linux.md deleted file mode 100644 index 838e94e1e..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime-config-linux.md +++ /dev/null @@ -1,379 +0,0 @@ -# Linux-specific Runtime Configuration - -## Namespaces - -A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. -Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes. -For more information, see [the man page](http://man7.org/linux/man-pages/man7/namespaces.7.html). - -Namespaces are specified in the spec as an array of entries. -Each entry has a type field with possible values described below and an optional path element. -If a path is specified, that particular file is used to join that type of namespace. -Also, when a path is specified, a runtime MUST assume that the setup for that particular namespace has already been done and error out if the config specifies anything else related to that namespace. - -```json - "namespaces": [ - { - "type": "pid", - "path": "/proc/1234/ns/pid" - }, - { - "type": "network", - "path": "/var/run/netns/neta" - }, - { - "type": "mount" - }, - { - "type": "ipc" - }, - { - "type": "uts" - }, - { - "type": "user" - } - ] -``` - -#### Namespace types - -* **`pid`** processes inside the container will only be able to see other processes inside the same container. -* **`network`** the container will have its own network stack. -* **`mount`** the container will have an isolated mount table. -* **`ipc`** processes inside the container will only be able to communicate to other processes inside the same -container via system level IPC. -* **`uts`** the container will be able to have its own hostname and domain name. -* **`user`** the container will be able to remap user and group IDs from the host to local users and groups -within the container. - -## Devices - -Devices is an array specifying the list of devices to be created in the container. -Next parameters can be specified: - -* **`type`** - type of device: `c`, `b`, `u` or `p`. More info in `man mknod` -* **`path`** - full path to device inside container -* **`major, minor`** - major, minor numbers for device. More info in `man mknod`. - There is special value: `-1`, which means `*` for `device` - cgroup setup. -* **`permissions`** - cgroup permissions for device. A composition of `r` - (read), `w` (write), and `m` (mknod). -* **`fileMode`** - file mode for device file -* **`uid`** - uid of device owner -* **`gid`** - gid of device owner - -```json - "devices": [ - { - "path": "/dev/random", - "type": "c", - "major": 1, - "minor": 8, - "permissions": "rwm", - "fileMode": 0666, - "uid": 0, - "gid": 0 - }, - { - "path": "/dev/urandom", - "type": "c", - "major": 1, - "minor": 9, - "permissions": "rwm", - "fileMode": 0666, - "uid": 0, - "gid": 0 - }, - { - "path": "/dev/null", - "type": "c", - "major": 1, - "minor": 3, - "permissions": "rwm", - "fileMode": 0666, - "uid": 0, - "gid": 0 - }, - { - "path": "/dev/zero", - "type": "c", - "major": 1, - "minor": 5, - "permissions": "rwm", - "fileMode": 0666, - "uid": 0, - "gid": 0 - }, - { - "path": "/dev/tty", - "type": "c", - "major": 5, - "minor": 0, - "permissions": "rwm", - "fileMode": 0666, - "uid": 0, - "gid": 0 - }, - { - "path": "/dev/full", - "type": "c", - "major": 1, - "minor": 7, - "permissions": "rwm", - "fileMode": 0666, - "uid": 0, - "gid": 0 - } - ] -``` - -## Control groups - -Also known as cgroups, they are used to restrict resource usage for a container and handle device access. -cgroups provide controls to restrict cpu, memory, IO, pids and network for the container. -For more information, see the [kernel cgroups documentation](https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt). - -The path to the cgroups can be specified in the Spec via `cgroupsPath`. -`cgroupsPath` is expected to be relative to the cgroups mount point. -If not specified, cgroups will be created under '/'. -Implementations of the Spec can choose to name cgroups in any manner. -The Spec does not include naming schema for cgroups. -The Spec does not support [split hierarchy](https://www.kernel.org/doc/Documentation/cgroups/unified-hierarchy.txt). -The cgroups will be created if they don't exist. - -```json - "cgroupsPath": "/myRuntime/myContainer" -``` - -`cgroupsPath` can be used to either control the cgroups hierarchy for containers or to run a new process in an existing container. - -You can configure a container's cgroups via the `resources` field of the Linux configuration. -Do not specify `resources` unless limits have to be updated. -For example, to run a new process in an existing container without updating limits, `resources` need not be specified. - -#### Disable out-of-memory killer - -```json - "disableOOMKiller": false -``` - -#### Memory - -```json - "memory": { - "limit": 0, - "reservation": 0, - "swap": 0, - "kernel": 0, - "swappiness": -1 - } -``` - -#### CPU - -```json - "cpu": { - "shares": 0, - "quota": 0, - "period": 0, - "realtimeRuntime": 0, - "realtimePeriod": 0, - "cpus": "", - "mems": "" - } -``` - -#### Block IO Controller - -`blockIO` represents the cgroup subsystem `blkio` which implements the block io controller. -For more information, see the [kernel cgroups documentation about `blkio`](https://www.kernel.org/doc/Documentation/cgroups/blkio-controller.txt). - -The following parameters can be specified to setup the block io controller: - -* **`blkioWeight`** *(uint16, optional)* - specifies per-cgroup weight. This is default weight of the group on all devices until and unless overridden by per-device rules. The range is from 10 to 1000. - -* **`blkioLeafWeight`** *(uint16, optional)* - equivalents of `blkioWeight` for the purpose of deciding how much weight tasks in the given cgroup has while competing with the cgroup's child cgroups. The range is from 10 to 1000. - -* **`blkioWeightDevice`** *(array, optional)* - specifies the list of devices which will be bandwidth rate limited. The following parameters can be specified per-device: - * **`major, minor`** *(int64, required)* - major, minor numbers for device. More info in `man mknod`. - * **`weight`** *(uint16, optional)* - bandwidth rate for the device, range is from 10 to 1000. - * **`leafWeight`** *(uint16, optional)* - bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only. - - You must specify at least one of `weight` or `leafWeight` in a given entry, and can specify both. - -* **`blkioThrottleReadBpsDevice`**, **`blkioThrottleWriteBpsDevice`**, **`blkioThrottleReadIOPSDevice`**, **`blkioThrottleWriteIOPSDevice`** *(array, optional)* - specify the list of devices which will be IO rate limited. The following parameters can be specified per-device: - * **`major, minor`** *(int64, required)* - major, minor numbers for device. More info in `man mknod`. - * **`rate`** *(uint64, required)* - IO rate limit for the device - -###### Example - -```json - "blockIO": { - "blkioWeight": 0, - "blkioLeafWeight": 0, - "blkioWeightDevice": [ - { - "major": 8, - "minor": 0, - "weight": 500, - "leafWeight": 300 - }, - { - "major": 8, - "minor": 16, - "weight": 500 - } - ], - "blkioThrottleReadBpsDevice": [ - { - "major": 8, - "minor": 0, - "rate": 600 - } - ], - "blkioThrottleWriteIOPSDevice": [ - { - "major": 8, - "minor": 16, - "rate": 300 - } - ] - } -``` - -#### Huge page limits - -```json - "hugepageLimits": [ - { - "pageSize": "2MB", - "limit": 9223372036854771712 - } - ] -``` - -#### Network - -```json - "network": { - "classId": "ClassId", - "priorities": [ - { - "name": "eth0", - "priority": 500 - }, - { - "name": "eth1", - "priority": 1000 - } - ] - } -``` - -## Sysctl - -sysctl allows kernel parameters to be modified at runtime for the container. -For more information, see [the man page](http://man7.org/linux/man-pages/man8/sysctl.8.html) - -```json - "sysctl": { - "net.ipv4.ip_forward": "1", - "net.core.somaxconn": "256" - } -``` - -## Rlimits - -rlimits allow setting resource limits. -`type` is a string with a value from those defined in [the man page](http://man7.org/linux/man-pages/man2/setrlimit.2.html). -The kernel enforces the `soft` limit for a resource while the `hard` limit acts as a ceiling for that value that could be set by an unprivileged process. - -```json - "rlimits": [ - { - "type": "RLIMIT_NPROC", - "soft": 1024, - "hard": 102400 - } - ] -``` - -## SELinux process label - -SELinux process label specifies the label with which the processes in a container are run. -For more information about SELinux, see [Selinux documentation](http://selinuxproject.org/page/Main_Page) -```json - "selinuxProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c124,c675" -``` - -## Apparmor profile - -Apparmor profile specifies the name of the apparmor profile that will be used for the container. -For more information about Apparmor, see [Apparmor documentation](https://wiki.ubuntu.com/AppArmor) - -```json - "apparmorProfile": "acme_secure_profile" -``` - -## seccomp - -Seccomp provides application sandboxing mechanism in the Linux kernel. -Seccomp configuration allows one to configure actions to take for matched syscalls and furthermore also allows matching on values passed as arguments to syscalls. -For more information about Seccomp, see [Seccomp kernel documentation](https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt) -The actions, architectures, and operators are strings that match the definitions in seccomp.h from [libseccomp](https://github.com/seccomp/libseccomp) and are translated to corresponding values. -A valid list of constants as of Libseccomp v2.2.3 is contained below. - -Architecture Constants -* `SCMP_ARCH_X86` -* `SCMP_ARCH_X86_64` -* `SCMP_ARCH_X32` -* `SCMP_ARCH_ARM` -* `SCMP_ARCH_AARCH64` -* `SCMP_ARCH_MIPS` -* `SCMP_ARCH_MIPS64` -* `SCMP_ARCH_MIPS64N32` -* `SCMP_ARCH_MIPSEL` -* `SCMP_ARCH_MIPSEL64` -* `SCMP_ARCH_MIPSEL64N32` - -Action Constants: -* `SCMP_ACT_KILL` -* `SCMP_ACT_TRAP` -* `SCMP_ACT_ERRNO` -* `SCMP_ACT_TRACE` -* `SCMP_ACT_ALLOW` - -Operator Constants: -* `SCMP_CMP_NE` -* `SCMP_CMP_LT` -* `SCMP_CMP_LE` -* `SCMP_CMP_EQ` -* `SCMP_CMP_GE` -* `SCMP_CMP_GT` -* `SCMP_CMP_MASKED_EQ` - -```json - "seccomp": { - "defaultAction": "SCMP_ACT_ALLOW", - "architectures": [ - "SCMP_ARCH_X86" - ], - "syscalls": [ - { - "name": "getcwd", - "action": "SCMP_ACT_ERRNO" - } - ] - } -``` - -## Rootfs Mount Propagation - -rootfsPropagation sets the rootfs's mount propagation. -Its value is either slave, private, or shared. -[The kernel doc](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) has more information about mount propagation. - -```json - "rootfsPropagation": "slave", -``` diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime-config.md b/Godeps/_workspace/src/github.com/opencontainers/specs/runtime-config.md deleted file mode 100644 index 0536350dc..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime-config.md +++ /dev/null @@ -1,121 +0,0 @@ -# Runtime Configuration - -## Hooks - -Lifecycle hooks allow custom events for different points in a container's runtime. -Presently there are `Prestart`, `Poststart` and `Poststop`. - -* [`Prestart`](#pre-start) is a list of hooks to be run before the container process is executed -* [`Poststart`](#post-start) is a list of hooks to be run immediately after the container process is started -* [`Poststop`](#post-stop) is a list of hooks to be run after the container process exits - -Hooks allow one to run code before/after various lifecycle events of the container. -Hooks MUST be called in the listed order. -The state of the container is passed to the hooks over stdin, so the hooks could get the information they need to do their work. - -Hook paths are absolute and are executed from the host's filesystem. - -### Pre-start - -The pre-start hooks are called after the container process is spawned, but before the user supplied command is executed. -They are called after the container namespaces are created on Linux, so they provide an opportunity to customize the container. -In Linux, for e.g., the network namespace could be configured in this hook. - -If a hook returns a non-zero exit code, then an error including the exit code and the stderr is returned to the caller and the container is torn down. - -### Post-start - -The post-start hooks are called after the user process is started. -For example this hook can notify user that real process is spawned. - -If a hook returns a non-zero exit code, then an error is logged and the remaining hooks are executed. - -### Post-stop - -The post-stop hooks are called after the container process is stopped. -Cleanup or debugging could be performed in such a hook. -If a hook returns a non-zero exit code, then an error is logged and the remaining hooks are executed. - -*Example* - -```json - "hooks" : { - "prestart": [ - { - "path": "/usr/bin/fix-mounts", - "args": ["arg1", "arg2"], - "env": [ "key1=value1"] - }, - { - "path": "/usr/bin/setup-network" - } - ], - "poststart": [ - { - "path": "/usr/bin/notify-start" - } - ], - "poststop": [ - { - "path": "/usr/sbin/cleanup.sh", - "args": ["-f"] - } - ] - } -``` - -`path` is required for a hook. -`args` and `env` are optional. - -## Mount Configuration - -Additional filesystems can be declared as "mounts", specified in the *mounts* object. -Keys in this object are names of mount points from portable config. -Values are objects with configuration of mount points. -The parameters are similar to the ones in [the Linux mount system call](http://man7.org/linux/man-pages/man2/mount.2.html). -Only [mounts from the portable config](config.md#mount-points) will be mounted. - -* **`type`** (string, required) Linux, *filesystemtype* argument supported by the kernel are listed in */proc/filesystems* (e.g., "minix", "ext2", "ext3", "jfs", "xfs", "reiserfs", "msdos", "proc", "nfs", "iso9660"). Windows: ntfs -* **`source`** (string, required) a device name, but can also be a directory name or a dummy. Windows, the volume name that is the target of the mount point. \\?\Volume\{GUID}\ (on Windows source is called target) -* **`options`** (list of strings, optional) in the fstab format [https://wiki.archlinux.org/index.php/Fstab](https://wiki.archlinux.org/index.php/Fstab). - -*Example (Linux)* - -```json -"mounts": { - "proc": { - "type": "proc", - "source": "proc", - "options": [] - }, - "dev": { - "type": "tmpfs", - "source": "tmpfs", - "options": ["nosuid","strictatime","mode=755","size=65536k"] - }, - "devpts": { - "type": "devpts", - "source": "devpts", - "options": ["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"] - }, - "data": { - "type": "bind", - "source": "/volumes/testing", - "options": ["rbind","rw"] - } -} -``` - -*Example (Windows)* - -```json -"mounts": { - "myfancymountpoint": { - "type": "ntfs", - "source": "\\\\?\\Volume\\{2eca078d-5cbc-43d3-aff8-7e8511f60d0e}\\", - "options": [] - } -} -``` - -See links for details about [mountvol](http://ss64.com/nt/mountvol.html) and [SetVolumeMountPoint](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365561(v=vs.85).aspx) in Windows. diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime.md b/Godeps/_workspace/src/github.com/opencontainers/specs/runtime.md index fdaf96519..12d6c94cf 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime.md +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/runtime.md @@ -27,30 +27,33 @@ This is provided so that consumers can find the container's configuration and ro ```json { + "version": "0.2.0", "id": "oc-container", "pid": 4422, - "root": "/containers/redis" + "bundlePath": "/containers/redis" } ``` ## Lifecycle - -### Create - -Creates the container: file system, namespaces, cgroups, capabilities. - -### Start (process) - -Runs a process in a container. -Can be invoked several times. - -### Stop (process) - -Not sure we need that from runc cli. -Process is killed from the outside. - -This event needs to be captured by runc to run onstop event handlers. +The lifecycle describes the timeline of events that happen from when a container is created to when it ceases to exist. + +1. OCI compliant runtime is invoked by passing the bundle path as argument. +2. The container's runtime environment is created according to the configuration in [`config.json`](config.md). + Any updates to `config.json` after container is running do not affect the container. +3. The container's state.json file is written to the filesystem. +4. The prestart hooks are invoked by the runtime. + If any prestart hook fails, then the container is stopped and the lifecycle continues at step 8. +5. The user specified process is executed in the container. +6. The poststart hooks are invoked by the runtime. + If any poststart hook fails, then the container is stopped and the lifecycle continues at step 8. +7. Additional actions such as pausing the container, resuming the container or signaling the container may be performed using the runtime interface. + The container could also error out or crash. +8. The container is destroyed by undoing the steps performed during create phase (step 2). +9. The poststop hooks are invoked by the runtime and errors, if any, are logged. +10. The state.json file associated with the container is removed and the return code of the container's user specified process is returned or logged. + +Note: The lifecycle is a WIP and it will evolve as we have more use cases and more information on the viability of a separate create phase. ## Hooks -See [runtime configuration for hooks](./runtime-config.md) +See [runtime configuration for hooks](./config.md#hooks) diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime_config.go b/Godeps/_workspace/src/github.com/opencontainers/specs/runtime_config.go deleted file mode 100644 index 21ed20b00..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime_config.go +++ /dev/null @@ -1,40 +0,0 @@ -package specs - -// RuntimeSpec is the generic runtime state information on a running container -type RuntimeSpec struct { - // Mounts is a mapping of names to mount configurations. - // Which mounts will be mounted and where should be chosen with MountPoints - // in Spec. - Mounts map[string]Mount `json:"mounts"` - // Hooks are the commands run at various lifecycle events of the container. - Hooks Hooks `json:"hooks"` -} - -// Hook specifies a command that is run at a particular event in the lifecycle of a container -type Hook struct { - Path string `json:"path"` - Args []string `json:"args"` - Env []string `json:"env"` -} - -// Hooks for container setup and teardown -type Hooks struct { - // Prestart is a list of hooks to be run before the container process is executed. - // On Linux, they are run after the container namespaces are created. - Prestart []Hook `json:"prestart"` - // Poststart is a list of hooks to be run after the container process is started. - Poststart []Hook `json:"poststart"` - // Poststop is a list of hooks to be run after the container process exits. - Poststop []Hook `json:"poststop"` -} - -// Mount specifies a mount for a container -type Mount struct { - // Type specifies the mount kind. - Type string `json:"type"` - // Source specifies the source path of the mount. In the case of bind mounts on - // linux based systems this would be the file on the host. - Source string `json:"source"` - // Options are fstab style mount options. - Options []string `json:"options"` -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime_config_linux.go b/Godeps/_workspace/src/github.com/opencontainers/specs/runtime_config_linux.go deleted file mode 100644 index cd55f7c9d..000000000 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/runtime_config_linux.go +++ /dev/null @@ -1,297 +0,0 @@ -package specs - -import "os" - -// LinuxStateDirectory holds the container's state information -const LinuxStateDirectory = "/run/opencontainer/containers" - -// LinuxRuntimeSpec is the full specification for linux containers. -type LinuxRuntimeSpec struct { - RuntimeSpec - // LinuxRuntime is platform specific configuration for linux based containers. - Linux LinuxRuntime `json:"linux"` -} - -// LinuxRuntime hosts the Linux-only runtime information -type LinuxRuntime struct { - // UIDMapping specifies user mappings for supporting user namespaces on linux. - UIDMappings []IDMapping `json:"uidMappings"` - // GIDMapping specifies group mappings for supporting user namespaces on linux. - GIDMappings []IDMapping `json:"gidMappings"` - // Rlimits specifies rlimit options to apply to the container's process. - Rlimits []Rlimit `json:"rlimits"` - // Sysctl are a set of key value pairs that are set for the container on start - Sysctl map[string]string `json:"sysctl"` - // Resources contain cgroup information for handling resource constraints - // for the container - Resources *Resources `json:"resources"` - // CgroupsPath specifies the path to cgroups that are created and/or joined by the container. - // The path is expected to be relative to the cgroups mountpoint. - // If resources are specified, the cgroups at CgroupsPath will be updated based on resources. - CgroupsPath string `json:"cgroupsPath"` - // Namespaces contains the namespaces that are created and/or joined by the container - Namespaces []Namespace `json:"namespaces"` - // Devices are a list of device nodes that are created and enabled for the container - Devices []Device `json:"devices"` - // ApparmorProfile specified the apparmor profile for the container. - ApparmorProfile string `json:"apparmorProfile"` - // SelinuxProcessLabel specifies the selinux context that the container process is run as. - SelinuxProcessLabel string `json:"selinuxProcessLabel"` - // Seccomp specifies the seccomp security settings for the container. - Seccomp Seccomp `json:"seccomp"` - // RootfsPropagation is the rootfs mount propagation mode for the container - RootfsPropagation string `json:"rootfsPropagation"` -} - -// Namespace is the configuration for a linux namespace -type Namespace struct { - // Type is the type of Linux namespace - Type NamespaceType `json:"type"` - // Path is a path to an existing namespace persisted on disk that can be joined - // and is of the same type - Path string `json:"path"` -} - -// NamespaceType is one of the linux namespaces -type NamespaceType string - -const ( - // PIDNamespace for isolating process IDs - PIDNamespace NamespaceType = "pid" - // NetworkNamespace for isolating network devices, stacks, ports, etc - NetworkNamespace = "network" - // MountNamespace for isolating mount points - MountNamespace = "mount" - // IPCNamespace for isolating System V IPC, POSIX message queues - IPCNamespace = "ipc" - // UTSNamespace for isolating hostname and NIS domain name - UTSNamespace = "uts" - // UserNamespace for isolating user and group IDs - UserNamespace = "user" -) - -// IDMapping specifies UID/GID mappings -type IDMapping struct { - // HostID is the UID/GID of the host user or group - HostID uint32 `json:"hostID"` - // ContainerID is the UID/GID of the container's user or group - ContainerID uint32 `json:"containerID"` - // Size is the length of the range of IDs mapped between the two namespaces - Size uint32 `json:"size"` -} - -// Rlimit type and restrictions -type Rlimit struct { - // Type of the rlimit to set - Type string `json:"type"` - // Hard is the hard limit for the specified type - Hard uint64 `json:"hard"` - // Soft is the soft limit for the specified type - Soft uint64 `json:"soft"` -} - -// HugepageLimit structure corresponds to limiting kernel hugepages -type HugepageLimit struct { - // Pagesize is the hugepage size - Pagesize string `json:"pageSize"` - // Limit is the limit of "hugepagesize" hugetlb usage - Limit uint64 `json:"limit"` -} - -// InterfacePriority for network interfaces -type InterfacePriority struct { - // Name is the name of the network interface - Name string `json:"name"` - // Priority for the interface - Priority int64 `json:"priority"` -} - -// blockIODevice holds major:minor format supported in blkio cgroup -type blockIODevice struct { - // Major is the device's major number. - Major int64 `json:"major"` - // Minor is the device's minor number. - Minor int64 `json:"minor"` -} - -// WeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice -type WeightDevice struct { - blockIODevice - // Weight is the bandwidth rate for the device, range is from 10 to 1000 - Weight uint16 `json:"weight"` - // LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only - LeafWeight uint16 `json:"leafWeight"` -} - -// ThrottleDevice struct holds a `major:minor rate_per_second` pair -type ThrottleDevice struct { - blockIODevice - // Rate is the IO rate limit per cgroup per device - Rate uint64 `json:"rate"` -} - -// BlockIO for Linux cgroup 'blkio' resource management -type BlockIO struct { - // Specifies per cgroup weight, range is from 10 to 1000 - Weight uint16 `json:"blkioWeight"` - // Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only - LeafWeight uint16 `json:"blkioLeafWeight"` - // Weight per cgroup per device, can override BlkioWeight - WeightDevice []*WeightDevice `json:"blkioWeightDevice"` - // IO read rate limit per cgroup per device, bytes per second - ThrottleReadBpsDevice []*ThrottleDevice `json:"blkioThrottleReadBpsDevice"` - // IO write rate limit per cgroup per device, bytes per second - ThrottleWriteBpsDevice []*ThrottleDevice `json:"blkioThrottleWriteBpsDevice"` - // IO read rate limit per cgroup per device, IO per second - ThrottleReadIOPSDevice []*ThrottleDevice `json:"blkioThrottleReadIOPSDevice"` - // IO write rate limit per cgroup per device, IO per second - ThrottleWriteIOPSDevice []*ThrottleDevice `json:"blkioThrottleWriteIOPSDevice"` -} - -// Memory for Linux cgroup 'memory' resource management -type Memory struct { - // Memory limit (in bytes) - Limit int64 `json:"limit"` - // Memory reservation or soft_limit (in bytes) - Reservation int64 `json:"reservation"` - // Total memory usage (memory + swap); set `-1' to disable swap - Swap int64 `json:"swap"` - // Kernel memory limit (in bytes) - Kernel int64 `json:"kernel"` - // How aggressive the kernel will swap memory pages. Range from 0 to 100. Set -1 to use system default - Swappiness int64 `json:"swappiness"` -} - -// CPU for Linux cgroup 'cpu' resource management -type CPU struct { - // CPU shares (relative weight vs. other cgroups with cpu shares) - Shares int64 `json:"shares"` - // CPU hardcap limit (in usecs). Allowed cpu time in a given period - Quota int64 `json:"quota"` - // CPU period to be used for hardcapping (in usecs). 0 to use system default - Period int64 `json:"period"` - // How many time CPU will use in realtime scheduling (in usecs) - RealtimeRuntime int64 `json:"realtimeRuntime"` - // CPU period to be used for realtime scheduling (in usecs) - RealtimePeriod int64 `json:"realtimePeriod"` - // CPU to use within the cpuset - Cpus string `json:"cpus"` - // MEM to use within the cpuset - Mems string `json:"mems"` -} - -// Pids for Linux cgroup 'pids' resource management (Linux 4.3) -type Pids struct { - // Maximum number of PIDs. A value < 0 implies "no limit". - Limit int64 `json:"limit"` -} - -// Network identification and priority configuration -type Network struct { - // Set class identifier for container's network packets - ClassID string `json:"classId"` - // Set priority of network traffic for container - Priorities []InterfacePriority `json:"priorities"` -} - -// Resources has container runtime resource constraints -type Resources struct { - // DisableOOMKiller disables the OOM killer for out of memory conditions - DisableOOMKiller bool `json:"disableOOMKiller"` - // Memory restriction configuration - Memory Memory `json:"memory"` - // CPU resource restriction configuration - CPU CPU `json:"cpu"` - // Task resource restriction configuration. - Pids Pids `json:"pids"` - // BlockIO restriction configuration - BlockIO BlockIO `json:"blockIO"` - // Hugetlb limit (in bytes) - HugepageLimits []HugepageLimit `json:"hugepageLimits"` - // Network restriction configuration - Network Network `json:"network"` -} - -// Device represents the information on a Linux special device file -type Device struct { - // Path to the device. - Path string `json:"path"` - // Device type, block, char, etc. - Type rune `json:"type"` - // Major is the device's major number. - Major int64 `json:"major"` - // Minor is the device's minor number. - Minor int64 `json:"minor"` - // Cgroup permissions format, rwm. - Permissions string `json:"permissions"` - // FileMode permission bits for the device. - FileMode os.FileMode `json:"fileMode"` - // UID of the device. - UID uint32 `json:"uid"` - // Gid of the device. - GID uint32 `json:"gid"` -} - -// Seccomp represents syscall restrictions -type Seccomp struct { - DefaultAction Action `json:"defaultAction"` - Architectures []Arch `json:"architectures"` - Syscalls []*Syscall `json:"syscalls"` -} - -// Additional architectures permitted to be used for system calls -// By default only the native architecture of the kernel is permitted -type Arch string - -const ( - ArchX86 Arch = "SCMP_ARCH_X86" - ArchX86_64 Arch = "SCMP_ARCH_X86_64" - ArchX32 Arch = "SCMP_ARCH_X32" - ArchARM Arch = "SCMP_ARCH_ARM" - ArchAARCH64 Arch = "SCMP_ARCH_AARCH64" - ArchMIPS Arch = "SCMP_ARCH_MIPS" - ArchMIPS64 Arch = "SCMP_ARCH_MIPS64" - ArchMIPS64N32 Arch = "SCMP_ARCH_MIPS64N32" - ArchMIPSEL Arch = "SCMP_ARCH_MIPSEL" - ArchMIPSEL64 Arch = "SCMP_ARCH_MIPSEL64" - ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32" -) - -// Action taken upon Seccomp rule match -type Action string - -const ( - ActKill Action = "SCMP_ACT_KILL" - ActTrap Action = "SCMP_ACT_TRAP" - ActErrno Action = "SCMP_ACT_ERRNO" - ActTrace Action = "SCMP_ACT_TRACE" - ActAllow Action = "SCMP_ACT_ALLOW" -) - -// Operator used to match syscall arguments in Seccomp -type Operator string - -const ( - OpNotEqual Operator = "SCMP_CMP_NE" - OpLessThan Operator = "SCMP_CMP_LT" - OpLessEqual Operator = "SCMP_CMP_LE" - OpEqualTo Operator = "SCMP_CMP_EQ" - OpGreaterEqual Operator = "SCMP_CMP_GE" - OpGreaterThan Operator = "SCMP_CMP_GT" - OpMaskedEqual Operator = "SCMP_CMP_MASKED_EQ" -) - -// Arg used for matching specific syscall arguments in Seccomp -type Arg struct { - Index uint `json:"index"` - Value uint64 `json:"value"` - ValueTwo uint64 `json:"valueTwo"` - Op Operator `json:"op"` -} - -// Syscall is used to match a syscall in Seccomp -type Syscall struct { - Name string `json:"name"` - Action Action `json:"action"` - Args []*Arg `json:"args"` -} diff --git a/Godeps/_workspace/src/github.com/opencontainers/specs/version.go b/Godeps/_workspace/src/github.com/opencontainers/specs/version.go index c2d1e29c6..f236a693e 100644 --- a/Godeps/_workspace/src/github.com/opencontainers/specs/version.go +++ b/Godeps/_workspace/src/github.com/opencontainers/specs/version.go @@ -6,10 +6,13 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 0 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 2 + VersionMinor = 3 // VersionPatch is for backwards-compatible bug fixes VersionPatch = 0 + + // VersionDev indicates development branch. Releases will be empty string. + VersionDev = "" ) // Version is the specification version that the package types support. -var Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) +var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev) diff --git a/README.md b/README.md index 09a534e20..6c7e97675 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,22 @@ OPTIONS: Arg2_index/Arg2_value/Arg2_valuetwo/Arg2_op ``` +Validating OCI bundle +------------------------------------------ + +``` +# ocitools bvalidate --help +NAME: + bvalidate - validate a OCI bundle + +USAGE: + command bvalidate [command options] [arguments...] + +OPTIONS: + --path path to a bundle + +``` + Testing OCI runtimes ------------------------------------------ diff --git a/bvalidate.go b/bvalidate.go new file mode 100644 index 000000000..ba61177e5 --- /dev/null +++ b/bvalidate.go @@ -0,0 +1,379 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "path" + "reflect" + "regexp" + "strings" + + "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/opencontainers/specs" +) + +var bundleValidateFlags = []cli.Flag{ + cli.StringFlag{Name: "path", Usage: "path to a bundle"}, +} + +var ( + defaultRlimits = []string{ + "RLIMIT_CPU", + "RLIMIT_FSIZE", + "RLIMIT_DATA", + "RLIMIT_STACK", + "RLIMIT_CORE", + "RLIMIT_RSS", + "RLIMIT_NPROC", + "RLIMIT_NOFILE", + "RLIMIT_MEMLOCK", + "RLIMIT_AS", + "RLIMIT_LOCKS", + "RLIMIT_SIGPENDING", + "RLIMIT_MSGQUEUE", + "RLIMIT_NICE", + "RLIMIT_RTPRIO", + "RLIMIT_RTTIME", + } +) + +var bundleValidateCommand = cli.Command{ + Name: "bvalidate", + Usage: "validate a OCI bundle", + Flags: bundleValidateFlags, + Action: func(context *cli.Context) { + inputPath := context.String("path") + if inputPath == "" { + logrus.Fatalf("Bundle path shouldn't be empty") + } + + if _, err := os.Stat(inputPath); err != nil { + logrus.Fatal(err) + } + + sf, err := os.Open(path.Join(inputPath, "config.json")) + if err != nil { + logrus.Fatal(err) + } + + defer sf.Close() + + var spec specs.LinuxSpec + if err = json.NewDecoder(sf).Decode(&spec); err != nil { + logrus.Fatal(err) + } else { + if spec.Platform.OS != "linux" { + logrus.Fatalf("Operation system '%s' of the bundle is not supported yet.", spec.Platform.OS) + } + } + + rootfsPath := path.Join(inputPath, spec.Root.Path) + if fi, err := os.Stat(rootfsPath); err != nil { + logrus.Fatalf("Cannot find the rootfs: %v", rootfsPath) + } else if !fi.IsDir() { + logrus.Fatalf("Rootfs: %v is not a directory.", spec.Root.Path) + } + bundleValidate(spec, rootfsPath) + logrus.Infof("Bundle validation succeeded.") + }, +} + +func bundleValidate(spec specs.LinuxSpec, rootfs string) { + CheckMandatoryField(spec) + CheckSemVer(spec.Version) + CheckMounts(spec.Mounts, rootfs) + CheckLinux(spec.Linux, rootfs) +} + +func CheckSemVer(version string) { + re, _ := regexp.Compile("^(\\d+)?\\.(\\d+)?\\.(\\d+)?$") + if ok := re.Match([]byte(version)); !ok { + logrus.Fatalf("%s is not a valid version format, please read 'SemVer v2.0.0'", version) + } +} + +func CheckMounts(mounts []specs.Mount, rootfs string) { + for _, mount := range mounts { + rootfsPath := path.Join(rootfs, mount.Destination) + if fi, err := os.Stat(rootfsPath); err != nil { + logrus.Fatalf("Cannot find the mount point: %v", rootfsPath) + } else if !fi.IsDir() { + logrus.Fatalf("Mount point: %v is not a directory.", rootfsPath) + } + } +} + +//Linux only +func CheckLinux(spec specs.Linux, rootfs string) { + for index := 0; index < len(spec.Capabilities); index++ { + capability := spec.Capabilities[index] + if !capValid(capability) { + logrus.Fatalf("%s is not valid, man capabilities(7)", spec.Capabilities[index]) + } + } + + if len(spec.UIDMappings) > 5 { + logrus.Fatalf("Only 5 UID mappings are allowed (linux kernel restriction).") + } + if len(spec.GIDMappings) > 5 { + logrus.Fatalf("Only 5 GID mappings are allowed (linux kernel restriction).") + } + + for index := 0; index < len(spec.Rlimits); index++ { + if !rlimitValid(spec.Rlimits[index].Type) { + logrus.Fatalf("Rlimit %s is invalid.", spec.Rlimits[index]) + } + } + + for index := 0; index < len(spec.Namespaces); index++ { + if !namespaceValid(spec.Namespaces[index]) { + logrus.Fatalf("Namespace %s is invalid.", spec.Namespaces[index]) + } + } + + for index := 0; index < len(spec.Devices); index++ { + if !deviceValid(spec.Devices[index]) { + logrus.Fatalf("Device %s is invalid.", spec.Devices[index].Path) + } + } + + if len(spec.ApparmorProfile) > 0 { + profilePath := path.Join(rootfs, "/etc/apparmor.d", spec.ApparmorProfile) + _, err := os.Stat(profilePath) + if err != nil { + logrus.Fatal(err) + } + } + + CheckSeccomp(spec.Seccomp) + + switch spec.RootfsPropagation { + case "": + case "private": + case "rprivate": + case "slave": + case "rslave": + case "shared": + case "rshared": + default: + logrus.Fatalf("rootfs-propagation must be empty or one of private|rprivate|slave|rslave|shared|rshared") + } +} + +func CheckSeccomp(s specs.Seccomp) { + if !seccompActionValid(s.DefaultAction) { + logrus.Fatalf("Seccomp.DefaultAction is invalid.") + } + for index := 0; index < len(s.Syscalls); index++ { + if !syscallValid(s.Syscalls[index]) { + logrus.Fatalf("Syscall action is invalid.") + } + } + for index := 0; index < len(s.Architectures); index++ { + switch s.Architectures[index] { + case specs.ArchX86: + case specs.ArchX86_64: + case specs.ArchX32: + case specs.ArchARM: + case specs.ArchAARCH64: + case specs.ArchMIPS: + case specs.ArchMIPS64: + case specs.ArchMIPS64N32: + case specs.ArchMIPSEL: + case specs.ArchMIPSEL64: + case specs.ArchMIPSEL64N32: + default: + logrus.Fatalf("Seccomp.Architecture [%s] is invalid", s.Architectures[index]) + } + } +} + +func capValid(capability string) bool { + for _, val := range defaultCaps { + if val == capability { + return true + } + } + return false +} + +func rlimitValid(rlimit string) bool { + for _, val := range defaultRlimits { + if val == rlimit { + return true + } + } + return false +} + +func namespaceValid(ns specs.Namespace) bool { + switch ns.Type { + case specs.PIDNamespace: + case specs.NetworkNamespace: + case specs.MountNamespace: + case specs.IPCNamespace: + case specs.UTSNamespace: + case specs.UserNamespace: + default: + return false + } + return true +} + +func deviceValid(d specs.Device) bool { + switch d.Type { + case 'b': + case 'c': + case 'u': + if d.Major <= 0 { + return false + } + if d.Minor <= 0 { + return false + } + case 'p': + if d.Major > 0 || d.Minor > 0 { + return false + } + default: + return false + } + return true +} + +func seccompActionValid(secc specs.Action) bool { + switch secc { + case "": + case specs.ActKill: + case specs.ActTrap: + case specs.ActErrno: + case specs.ActTrace: + case specs.ActAllow: + default: + return false + } + return true +} + +func syscallValid(s specs.Syscall) bool { + if !seccompActionValid(s.Action) { + return false + } + for index := 0; index < len(s.Args); index++ { + arg := s.Args[index] + switch arg.Op { + case specs.OpNotEqual: + case specs.OpLessEqual: + case specs.OpEqualTo: + case specs.OpGreaterEqual: + case specs.OpGreaterThan: + case specs.OpMaskedEqual: + default: + return false + } + } + return true +} + +func isStruct(t reflect.Type) bool { + return t.Kind() == reflect.Struct +} + +func isStructPtr(t reflect.Type) bool { + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, parent string) ([]string, bool) { + var msgs []string + mandatory := !strings.Contains(tagField.Tag.Get("json"), "omitempty") + switch field.Kind() { + case reflect.Ptr: + if mandatory && field.IsNil() == true { + msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) + return msgs, false + } + case reflect.String: + if mandatory && (field.Len() == 0) { + msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) + return msgs, false + } + case reflect.Slice: + if mandatory && (field.Len() == 0) { + msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) + return msgs, false + } + valid := true + for index := 0; index < field.Len(); index++ { + mValue := field.Index(index) + if mValue.CanInterface() { + if ms, ok := checkMandatory(mValue.Interface()); !ok { + msgs = append(msgs, ms...) + valid = false + } + } + } + return msgs, valid + case reflect.Map: + if mandatory && ((field.IsNil() == true) || (field.Len() == 0)) { + msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) + return msgs, false + } + valid := true + keys := field.MapKeys() + for index := 0; index < len(keys); index++ { + mValue := field.MapIndex(keys[index]) + if mValue.CanInterface() { + if ms, ok := checkMandatory(mValue.Interface()); !ok { + msgs = append(msgs, ms...) + valid = false + } + } + } + return msgs, valid + default: + } + + return nil, true +} + +func checkMandatory(obj interface{}) (msgs []string, valid bool) { + objT := reflect.TypeOf(obj) + objV := reflect.ValueOf(obj) + if isStructPtr(objT) { + objT = objT.Elem() + objV = objV.Elem() + } else if !isStruct(objT) { + return nil, true + } + + valid = true + for i := 0; i < objT.NumField(); i++ { + t := objT.Field(i).Type + if isStructPtr(t) && objV.Field(i).IsNil() { + if !strings.Contains(objT.Field(i).Tag.Get("json"), "omitempty") { + msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty", objT.Name(), objT.Field(i).Name)) + valid = false + } + } else if (isStruct(t) || isStructPtr(t)) && objV.Field(i).CanInterface() { + if ms, ok := checkMandatory(objV.Field(i).Interface()); !ok { + msgs = append(msgs, ms...) + valid = false + } + } else { + if ms, ok := checkMandatoryUnit(objV.Field(i), objT.Field(i), objT.Name()); !ok { + msgs = append(msgs, ms...) + valid = false + } + } + + } + return msgs, valid +} + +func CheckMandatoryField(obj interface{}) { + if msgs, valid := checkMandatory(obj); !valid { + logrus.Fatalf("Mandatory information missing: %s.", msgs) + } +} diff --git a/cmd/runtimetest/main.go b/cmd/runtimetest/main.go index f1a1c6043..d12f351dd 100644 --- a/cmd/runtimetest/main.go +++ b/cmd/runtimetest/main.go @@ -15,37 +15,25 @@ import ( "github.com/syndtr/gocapability/capability" ) -type validation func(*specs.LinuxSpec, *specs.LinuxRuntimeSpec) error +type validation func(*specs.LinuxSpec) error -func loadSpecConfig() (spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, err error) { +func loadSpecConfig() (spec *specs.LinuxSpec, err error) { cPath := "config.json" cf, err := os.Open(cPath) if err != nil { if os.IsNotExist(err) { - return nil, nil, fmt.Errorf("config.json not found") + return nil, fmt.Errorf("config.json not found") } } defer cf.Close() - rPath := "runtime.json" - rf, err := os.Open(rPath) - if err != nil { - if os.IsNotExist(err) { - return nil, nil, fmt.Errorf("runtime.json not found") - } - } - defer rf.Close() - if err = json.NewDecoder(cf).Decode(&spec); err != nil { return } - if err = json.NewDecoder(rf).Decode(&rspec); err != nil { - return - } - return spec, rspec, nil + return spec, nil } -func validateProcess(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error { +func validateProcess(spec *specs.LinuxSpec) error { fmt.Println("validating container process") uid := os.Getuid() if uint32(uid) != spec.Process.User.UID { @@ -110,7 +98,7 @@ func validateProcess(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error return nil } -func validateCapabilities(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error { +func validateCapabilities(spec *specs.LinuxSpec) error { fmt.Println("validating capabilities") capabilityMap := make(map[string]capability.Cap) expectedCaps := make(map[capability.Cap]bool) @@ -152,7 +140,7 @@ func validateCapabilities(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) return nil } -func validateHostname(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error { +func validateHostname(spec *specs.LinuxSpec) error { fmt.Println("validating hostname") hostname, err := os.Hostname() if err != nil { @@ -164,9 +152,9 @@ func validateHostname(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) erro return nil } -func validateRlimits(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error { +func validateRlimits(spec *specs.LinuxSpec) error { fmt.Println("validating rlimits") - for _, r := range rspec.Linux.Rlimits { + for _, r := range spec.Linux.Rlimits { rl, err := strToRlimit(r.Type) if err != nil { return err @@ -187,9 +175,9 @@ func validateRlimits(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error return nil } -func validateSysctls(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error { +func validateSysctls(spec *specs.LinuxSpec) error { fmt.Println("validating sysctls") - for k, v := range rspec.Linux.Sysctl { + for k, v := range spec.Linux.Sysctl { keyPath := filepath.Join("/proc/sys", strings.Replace(k, ".", "/", -1)) vBytes, err := ioutil.ReadFile(keyPath) if err != nil { @@ -204,7 +192,7 @@ func validateSysctls(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) error } func main() { - spec, rspec, err := loadSpecConfig() + spec, err := loadSpecConfig() if err != nil { logrus.Fatalf("Failed to load configuration: %q", err) } @@ -218,7 +206,7 @@ func main() { } for _, v := range validations { - if err := v(spec, rspec); err != nil { + if err := v(spec); err != nil { logrus.Fatalf("Validation failed: %q", err) } } diff --git a/generate.go b/generate.go index 404e1fe97..1aac0f0bc 100644 --- a/generate.go +++ b/generate.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "path/filepath" "runtime" "strconv" "strings" @@ -75,13 +74,12 @@ var generateCommand = cli.Command{ Usage: "generate a OCI spec file", Flags: generateFlags, Action: func(context *cli.Context) { - spec, rspec := getDefaultTemplate() - err := modify(&spec, &rspec, context) + spec := getDefaultTemplate() + err := modify(&spec, context) if err != nil { logrus.Fatal(err) } cName := "config.json" - rName := "runtime.json" data, err := json.MarshalIndent(&spec, "", "\t") if err != nil { logrus.Fatal(err) @@ -89,27 +87,20 @@ var generateCommand = cli.Command{ if err := ioutil.WriteFile(cName, data, 0666); err != nil { logrus.Fatal(err) } - rdata, err := json.MarshalIndent(&rspec, "", "\t") - if err != nil { - logrus.Fatal(err) - } - if err := ioutil.WriteFile(rName, rdata, 0666); err != nil { - logrus.Fatal(err) - } }, } -func modify(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func modify(spec *specs.LinuxSpec, context *cli.Context) error { spec.Root.Path = context.String("rootfs") spec.Root.Readonly = context.Bool("read-only") spec.Hostname = context.String("hostname") spec.Process.User.UID = uint32(context.Int("uid")) spec.Process.User.GID = uint32(context.Int("gid")) - rspec.Linux.SelinuxProcessLabel = context.String("selinux-label") + spec.Linux.SelinuxProcessLabel = context.String("selinux-label") spec.Platform.OS = context.String("os") spec.Platform.Arch = context.String("arch") spec.Process.Cwd = context.String("cwd") - rspec.Linux.ApparmorProfile = context.String("apparmor") + spec.Linux.ApparmorProfile = context.String("apparmor") for i, a := range context.StringSlice("args") { if a != "" { @@ -137,36 +128,37 @@ func modify(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.C } } - if err := setupCapabilities(spec, rspec, context); err != nil { + if err := setupCapabilities(spec, context); err != nil { return err } - setupNamespaces(spec, rspec, context) - if err := addTmpfsMounts(spec, rspec, context); err != nil { + + setupNamespaces(spec, context) + if err := addTmpfsMounts(spec, context); err != nil { return err } - if err := mountCgroups(spec, rspec, context); err != nil { + if err := mountCgroups(spec, context); err != nil { return err } - if err := addBindMounts(spec, rspec, context); err != nil { + if err := addBindMounts(spec, context); err != nil { return err } - if err := addHooks(spec, rspec, context); err != nil { + if err := addHooks(spec, context); err != nil { return err } - if err := addRootPropagation(spec, rspec, context); err != nil { + if err := addRootPropagation(spec, context); err != nil { return err } - if err := addIDMappings(spec, rspec, context); err != nil { + if err := addIDMappings(spec, context); err != nil { return err } - if err := addSeccomp(spec, rspec, context); err != nil { + if err := addSeccomp(spec, context); err != nil { return err } return nil } -func addSeccompDefault(rspec *specs.LinuxRuntimeSpec, sdefault string) error { +func addSeccompDefault(spec *specs.LinuxSpec, sdefault string) error { switch sdefault { case "": case "SCMP_ACT_KILL": @@ -179,11 +171,11 @@ func addSeccompDefault(rspec *specs.LinuxRuntimeSpec, sdefault string) error { "SCMP_ACT_KILL|SCMP_ACT_TRAP|SCMP_ACT_ERRNO|SCMP_ACT_TRACE|" + "SCMP_ACT_ALLOW") } - rspec.Linux.Seccomp.DefaultAction = specs.Action(sdefault) + spec.Linux.Seccomp.DefaultAction = specs.Action(sdefault) return nil } -func addSeccompArch(rspec *specs.LinuxRuntimeSpec, sArch []string) error { +func addSeccompArch(spec *specs.LinuxSpec, sArch []string) error { for _, archs := range sArch { switch archs { case "": @@ -205,13 +197,13 @@ func addSeccompArch(rspec *specs.LinuxRuntimeSpec, sArch []string) error { "SCMP_ARCH_MIPS64N32|SCMP_ARCH_MIPSEL|SCMP_ARCH_MIPSEL64|" + "SCMP_ARCH_MIPSEL64N32") } - rspec.Linux.Seccomp.Architectures = append(rspec.Linux.Seccomp.Architectures, specs.Arch(archs)) + spec.Linux.Seccomp.Architectures = append(spec.Linux.Seccomp.Architectures, specs.Arch(archs)) } return nil } -func addSeccompSyscall(rspec *specs.LinuxRuntimeSpec, sSyscall []string) error { +func addSeccompSyscall(spec *specs.LinuxSpec, sSyscall []string) error { for _, syscalls := range sSyscall { syscall := strings.Split(syscalls, ":") if len(syscall) == 3 { @@ -230,7 +222,7 @@ func addSeccompSyscall(rspec *specs.LinuxRuntimeSpec, sSyscall []string) error { } action := specs.Action(syscall[1]) - var Args []*specs.Arg + var Args []specs.Arg if strings.EqualFold(syscall[2], "") { Args = nil } else { @@ -267,7 +259,7 @@ func addSeccompSyscall(rspec *specs.LinuxRuntimeSpec, sSyscall []string) error { ValueTwo: uint64(value2), Op: op, } - Args = append(Args, &Arg) + Args = append(Args, Arg) } else { return fmt.Errorf("seccomp-sysctl args error: %s", argsstru) } @@ -279,7 +271,7 @@ func addSeccompSyscall(rspec *specs.LinuxRuntimeSpec, sSyscall []string) error { Action: action, Args: Args, } - rspec.Linux.Seccomp.Syscalls = append(rspec.Linux.Seccomp.Syscalls, &syscallstruct) + spec.Linux.Seccomp.Syscalls = append(spec.Linux.Seccomp.Syscalls, syscallstruct) } else { return fmt.Errorf("seccomp sysctl must consist of 3 parameters") } @@ -288,20 +280,20 @@ func addSeccompSyscall(rspec *specs.LinuxRuntimeSpec, sSyscall []string) error { return nil } -func addSeccomp(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func addSeccomp(spec *specs.LinuxSpec, context *cli.Context) error { sd := context.String("seccomp-default") sa := context.StringSlice("seccomp-arch") ss := context.StringSlice("seccomp-syscalls") // Set the DefaultAction of seccomp - err := addSeccompDefault(rspec, sd) + err := addSeccompDefault(spec, sd) if err != nil { return err } // Add the additional architectures permitted to be used for system calls - err = addSeccompArch(rspec, sa) + err = addSeccompArch(spec, sa) if err != nil { return err } @@ -309,7 +301,7 @@ func addSeccomp(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *c // Set syscall restrict in Seccomp // The format of input syscall string is Name:Action:Args[1],Args[2],...,Args[n] // The format of Args string is Index/Value/ValueTwo/Operator,and is parsed by function parseArgs() - err = addSeccompSyscall(rspec, ss) + err = addSeccompSyscall(spec, ss) if err != nil { return err } @@ -356,7 +348,7 @@ func parseArgs(args2parse string) ([]*specs.Arg, error) { return Args, nil } -func addIDMappings(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func addIDMappings(spec *specs.LinuxSpec, context *cli.Context) error { for _, uidms := range context.StringSlice("uidmappings") { idm := strings.Split(uidms, ":") if len(idm) == 3 { @@ -371,7 +363,7 @@ func addIDMappings(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context ContainerID: uint32(cid), Size: uint32(size), } - rspec.Linux.UIDMappings = append(rspec.Linux.UIDMappings, uidmapping) + spec.Linux.UIDMappings = append(spec.Linux.UIDMappings, uidmapping) } else { return fmt.Errorf("uidmappings error: %s", uidms) } @@ -391,20 +383,20 @@ func addIDMappings(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context ContainerID: uint32(cid), Size: uint32(size), } - rspec.Linux.GIDMappings = append(rspec.Linux.GIDMappings, gidmapping) + spec.Linux.GIDMappings = append(spec.Linux.GIDMappings, gidmapping) } else { return fmt.Errorf("gidmappings error: %s", gidms) } } if len(context.StringSlice("uidmappings")) > 0 || len(context.StringSlice("gidmappings")) > 0 { - rspec.Linux.Namespaces = append(rspec.Linux.Namespaces, specs.Namespace{Type: "user"}) + spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.Namespace{Type: "user"}) } return nil } -func addRootPropagation(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func addRootPropagation(spec *specs.LinuxSpec, context *cli.Context) error { rp := context.String("root-propagation") switch rp { case "": @@ -417,11 +409,11 @@ func addRootPropagation(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, co default: return fmt.Errorf("rootfs-propagation must be empty or one of private|rprivate|slave|rslave|shared|rshared") } - rspec.Linux.RootfsPropagation = rp + spec.Linux.RootfsPropagation = rp return nil } -func addHooks(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func addHooks(spec *specs.LinuxSpec, context *cli.Context) error { for _, pre := range context.StringSlice("prestart") { parts := strings.Split(pre, ":") args := []string{} @@ -429,7 +421,7 @@ func addHooks(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli if len(parts) > 1 { args = parts[1:] } - rspec.Hooks.Prestart = append(rspec.Hooks.Prestart, specs.Hook{Path: path, Args: args}) + spec.Hooks.Prestart = append(spec.Hooks.Prestart, specs.Hook{Path: path, Args: args}) } for _, post := range context.StringSlice("poststop") { parts := strings.Split(post, ":") @@ -438,7 +430,7 @@ func addHooks(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli if len(parts) > 1 { args = parts[1:] } - rspec.Hooks.Poststop = append(rspec.Hooks.Poststop, specs.Hook{Path: path, Args: args}) + spec.Hooks.Poststop = append(spec.Hooks.Poststop, specs.Hook{Path: path, Args: args}) } for _, poststart := range context.StringSlice("poststart") { parts := strings.Split(poststart, ":") @@ -447,27 +439,25 @@ func addHooks(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli if len(parts) > 1 { args = parts[1:] } - rspec.Hooks.Poststart = append(rspec.Hooks.Poststart, specs.Hook{Path: path, Args: args}) + spec.Hooks.Poststart = append(spec.Hooks.Poststart, specs.Hook{Path: path, Args: args}) } return nil } -func addTmpfsMounts(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { + +func addTmpfsMounts(spec *specs.LinuxSpec, context *cli.Context) error { for _, dest := range context.StringSlice("tmpfs") { - name := filepath.Base(dest) - mntName := fmt.Sprintf("%stmpfs", name) - mnt := specs.MountPoint{Name: mntName, Path: dest} - spec.Mounts = append(spec.Mounts, mnt) - rmnt := specs.Mount{ - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "nodev", "mode=755"}, + mnt := specs.Mount{ + Destination: dest, + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "nodev", "mode=755"}, } - rspec.Mounts[mntName] = rmnt + spec.Mounts = append(spec.Mounts, mnt) } return nil } -func mountCgroups(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func mountCgroups(spec *specs.LinuxSpec, context *cli.Context) error { mountCgroupOption := context.String("mount-cgroups") switch mountCgroupOption { case "ro": @@ -478,17 +468,18 @@ func mountCgroups(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context return fmt.Errorf("--mount-cgroups should be one of (ro,rw,no)") } - spec.Mounts = append(spec.Mounts, specs.MountPoint{Name: "cgroup", Path: "/sys/fs/cgroup"}) - rspec.Mounts["cgroup"] = specs.Mount{ - Type: "cgroup", - Source: "cgroup", - Options: []string{"nosuid", "noexec", "nodev", "relatime", mountCgroupOption}, + mnt := specs.Mount{ + Destination: "/sys/fs/cgroup", + Type: "cgroup", + Source: "cgroup", + Options: []string{"nosuid", "noexec", "nodev", "relatime", mountCgroupOption}, } + spec.Mounts = append(spec.Mounts, mnt) return nil } -func addBindMounts(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func addBindMounts(spec *specs.LinuxSpec, context *cli.Context) error { for _, b := range context.StringSlice("bind") { var source, dest string options := "ro" @@ -501,20 +492,19 @@ func addBindMounts(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context default: return fmt.Errorf("--bind should have format src:dest:[options]") } - name := filepath.Base(source) - mntName := fmt.Sprintf("%sbind", name) - spec.Mounts = append(spec.Mounts, specs.MountPoint{Name: mntName, Path: dest}) defaultOptions := []string{"bind"} - rspec.Mounts[mntName] = specs.Mount{ - Type: "bind", - Source: source, - Options: append(defaultOptions, options), + mnt := specs.Mount{ + Destination: dest, + Type: "bind", + Source: source, + Options: append(defaultOptions, options), } + spec.Mounts = append(spec.Mounts, mnt) } return nil } -func setupCapabilities(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) error { +func setupCapabilities(spec *specs.LinuxSpec, context *cli.Context) error { var finalCapList []string // Add all capabilities in privileged mode. @@ -590,7 +580,7 @@ func mapStrToNamespace(ns string, path string) specs.Namespace { return specs.Namespace{} } -func setupNamespaces(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, context *cli.Context) { +func setupNamespaces(spec *specs.LinuxSpec, context *cli.Context) { namespaces := []string{"network", "pid", "mount", "ipc", "uts"} var linuxNs []specs.Namespace for _, nsName := range namespaces { @@ -601,10 +591,10 @@ func setupNamespaces(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, conte ns := mapStrToNamespace(nsName, nsPath) linuxNs = append(linuxNs, ns) } - rspec.Linux.Namespaces = linuxNs + spec.Linux.Namespaces = linuxNs } -func getDefaultTemplate() (specs.LinuxSpec, specs.LinuxRuntimeSpec) { +func getDefaultTemplate() specs.LinuxSpec { spec := specs.LinuxSpec{ Spec: specs.Spec{ Version: specs.Version, @@ -629,30 +619,30 @@ func getDefaultTemplate() (specs.LinuxSpec, specs.LinuxRuntimeSpec) { Cwd: "/", }, Hostname: "shell", - Mounts: []specs.MountPoint{ - { - Name: "proc", - Path: "/proc", - }, - { - Name: "dev", - Path: "/dev", - }, + Mounts: []specs.Mount{ { - Name: "devpts", - Path: "/dev/pts", + Destination: "/proc", + Type: "proc", + Source: "proc", + Options: nil, }, { - Name: "shm", - Path: "/dev/shm", + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, }, { - Name: "mqueue", - Path: "/dev/mqueue", + Destination: "/dev/shm", + Type: "tmpfs", + Source: "shm", + Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, }, { - Name: "sysfs", - Path: "/sys", + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev"}, }, }, }, @@ -673,44 +663,6 @@ func getDefaultTemplate() (specs.LinuxSpec, specs.LinuxRuntimeSpec) { "CAP_KILL", "CAP_AUDIT_WRITE", }, - }, - } - rspec := specs.LinuxRuntimeSpec{ - RuntimeSpec: specs.RuntimeSpec{ - Mounts: map[string]specs.Mount{ - "proc": { - Type: "proc", - Source: "proc", - Options: nil, - }, - "dev": { - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - "devpts": { - Type: "devpts", - Source: "devpts", - Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, - }, - "shm": { - Type: "tmpfs", - Source: "shm", - Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, - }, - "mqueue": { - Type: "mqueue", - Source: "mqueue", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - "sysfs": { - Type: "sysfs", - Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - }, - }, - Linux: specs.LinuxRuntime{ Namespaces: []specs.Namespace{ { Type: "pid", @@ -737,72 +689,44 @@ func getDefaultTemplate() (specs.LinuxSpec, specs.LinuxRuntimeSpec) { }, Devices: []specs.Device{ { - Type: 'c', - Path: "/dev/null", - Major: 1, - Minor: 3, - Permissions: "rwm", - FileMode: 0666, - UID: 0, - GID: 0, + Type: 'c', + Path: "/dev/null", + Major: 1, + Minor: 3, }, { - Type: 'c', - Path: "/dev/random", - Major: 1, - Minor: 8, - Permissions: "rwm", - FileMode: 0666, - UID: 0, - GID: 0, + Type: 'c', + Path: "/dev/random", + Major: 1, + Minor: 8, }, { - Type: 'c', - Path: "/dev/full", - Major: 1, - Minor: 7, - Permissions: "rwm", - FileMode: 0666, - UID: 0, - GID: 0, + Type: 'c', + Path: "/dev/full", + Major: 1, + Minor: 7, }, { - Type: 'c', - Path: "/dev/tty", - Major: 5, - Minor: 0, - Permissions: "rwm", - FileMode: 0666, - UID: 0, - GID: 0, + Type: 'c', + Path: "/dev/tty", + Major: 5, + Minor: 0, }, { - Type: 'c', - Path: "/dev/zero", - Major: 1, - Minor: 5, - Permissions: "rwm", - FileMode: 0666, - UID: 0, - GID: 0, + Type: 'c', + Path: "/dev/zero", + Major: 1, + Minor: 5, }, { - Type: 'c', - Path: "/dev/urandom", - Major: 1, - Minor: 9, - Permissions: "rwm", - FileMode: 0666, - UID: 0, - GID: 0, - }, - }, - Resources: &specs.Resources{ - Memory: specs.Memory{ - Swappiness: -1, + Type: 'c', + Path: "/dev/urandom", + Major: 1, + Minor: 9, }, }, }, } - return spec, rspec + + return spec } diff --git a/main.go b/main.go index 7e29644e1..642a394cd 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ func main() { app.Commands = []cli.Command{ generateCommand, + bundleValidateCommand, } if err := app.Run(os.Args); err != nil {