Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 8 additions & 61 deletions cmd/oci-runtime-tool/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ var generateFlags = []cli.Flag{
cli.StringFlag{Name: "linux-selinux-label", Usage: "process selinux label"},
cli.StringSliceFlag{Name: "linux-sysctl", Usage: "add sysctl settings e.g net.ipv4.forward=1"},
cli.StringSliceFlag{Name: "linux-uidmappings", Usage: "add UIDMappings e.g HostID:ContainerID:Size"},
cli.StringSliceFlag{Name: "mount-bind", Usage: "bind mount directories src:dest[:options...]"},
cli.StringFlag{Name: "mount-cgroups", Value: "no", Usage: "mount cgroups (rw,ro,no)"},
cli.StringSliceFlag{Name: "mounts-add", Usage: "configures additional mounts inside container"},
cli.BoolFlag{Name: "mounts-remove-all", Usage: "remove all mounts inside container"},
cli.StringFlag{Name: "output", Usage: "output file (defaults to stdout)"},
cli.BoolFlag{Name: "privileged", Usage: "enable privileged container settings"},
cli.StringSliceFlag{Name: "process-cap-add-ambient", Usage: "add Linux ambient capabilities"},
Expand All @@ -108,7 +108,6 @@ var generateFlags = []cli.Flag{
cli.StringFlag{Name: "rootfs-path", Value: "rootfs", Usage: "path to the root filesystem"},
cli.BoolFlag{Name: "rootfs-readonly", Usage: "make the container's rootfs readonly"},
cli.StringFlag{Name: "template", Usage: "base template to use for creating the configuration"},
cli.StringSliceFlag{Name: "tmpfs", Usage: "mount tmpfs e.g. ContainerDIR[:OPTIONS...]"},
}

var generateCommand = cli.Command{
Expand Down Expand Up @@ -415,30 +414,17 @@ func setupSpec(g *generate.Generator, context *cli.Context) error {
g.AddOrReplaceLinuxNamespace("user", "")
}

if context.IsSet("tmpfs") {
tmpfsSlice := context.StringSlice("tmpfs")
for _, s := range tmpfsSlice {
dest, options, err := parseTmpfsMount(s)
if err != nil {
return err
}
g.AddTmpfsMount(dest, options)
}
}

mountCgroupOption := context.String("mount-cgroups")
if err := g.AddCgroupsMount(mountCgroupOption); err != nil {
return err
if context.IsSet("mounts-remove-all") {
g.ClearMounts()
}

if context.IsSet("mount-bind") {
binds := context.StringSlice("mount-bind")
for _, bind := range binds {
source, dest, options, err := parseBindMount(bind)
if context.IsSet("mounts-add") {
mounts := context.StringSlice("mounts-add")
for _, mount := range mounts {
err := g.AddMounts(mount)
if err != nil {
return err
}
g.AddBindMount(source, dest, options)
}
}

Expand Down Expand Up @@ -857,45 +843,6 @@ func parseNetworkPriority(np string) (string, int32, error) {
return parts[0], int32(priority), nil
}

func parseTmpfsMount(s string) (string, []string, error) {
var dest string
var options []string
var err error

parts := strings.Split(s, ":")
if len(parts) == 2 && parts[0] != "" {
dest = parts[0]
options = strings.Split(parts[1], ",")
} else if len(parts) == 1 {
dest = parts[0]
options = []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
} else {
err = fmt.Errorf("invalid -- tmpfs value: %s", s)
}

return dest, options, err
}

func parseBindMount(s string) (string, string, []string, error) {
var source, dest string
options := []string{}

bparts := strings.SplitN(s, ":", 3)
switch len(bparts) {
case 2:
source, dest = bparts[0], bparts[1]
case 3:
source, dest, options = bparts[0], bparts[1], strings.Split(bparts[2], ":")
default:
return source, dest, options, fmt.Errorf("--mount-bind should have format src:dest[:options...]")
}

if source == "" || dest == "" {
return source, dest, options, fmt.Errorf("--mount-bind should have format src:dest[:options...]")
}
return source, dest, options, nil
}

func parseRlimit(rlimit string) (string, uint64, uint64, error) {
parts := strings.Split(rlimit, ":")
if len(parts) != 3 || parts[0] == "" {
Expand Down
12 changes: 3 additions & 9 deletions completions/bash/oci-runtime-tool
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,7 @@ _oci-runtime-tool_generate() {
--linux-selinux-label
--linux-sysctl
--linux-uidmappings
--mount-bind
--mount-cgroups
--mounts-add
--output
--process-cap-add-ambient
--process-cap-add-bounding
Expand All @@ -382,7 +381,6 @@ _oci-runtime-tool_generate() {
--process-uid
--rootfs-path
--template
--tmpfs
"

local boolean_options="
Expand All @@ -392,6 +390,7 @@ _oci-runtime-tool_generate() {
--linux-namespace-remove-all
--linux-seccomp-only
--linux-seccomp-remove-all
--mounts-remove-all
--privileged
--process-cap-drop-all
--process-no-new-privileges
Expand Down Expand Up @@ -436,11 +435,6 @@ _oci-runtime-tool_generate() {
return
;;

--mount-cgroups)
COMPREPLY=( $( compgen -W "no ro rw" -- "$cur" ) )
return
;;

--process-cap-add-ambient|--process-cap-add-bounding|--process-cap-add-effective|--process-cap-add-inheritable|--process-cap-add-permitted|--process-cap-drop-ambient|--process-cap-drop-bounding|--process-cap-drop-effective|--process-cap-drop-inheritable|--process-cap-drop-permitted)
__oci-runtime-tool_complete_capabilities
return
Expand All @@ -462,7 +456,7 @@ _oci-runtime-tool_generate() {
return
;;

--rootfs-path|--tmpfs|--mount-bind|--process-cwd)
--rootfs-path|--process-cwd)
case "$cur" in
*:*)
# TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
Expand Down
65 changes: 11 additions & 54 deletions generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -874,69 +874,26 @@ func (g *Generator) AddPostStartHookTimeout(path string, timeout int) {
g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook)
}

// AddTmpfsMount adds a tmpfs mount into g.spec.Mounts.
func (g *Generator) AddTmpfsMount(dest string, options []string) {
mnt := rspec.Mount{
Destination: dest,
Type: "tmpfs",
Source: "tmpfs",
Options: options,
}

// AddMounts adds a mount into g.spec.Mounts.
func (g *Generator) AddMounts(mountObject string) error {
g.initSpec()
g.spec.Mounts = append(g.spec.Mounts, mnt)
}

// AddCgroupsMount adds a cgroup mount into g.spec.Mounts.
func (g *Generator) AddCgroupsMount(mountCgroupOption string) error {
switch mountCgroupOption {
case "ro":
case "rw":
case "no":
return nil
default:
return fmt.Errorf("--mount-cgroups should be one of (ro,rw,no)")
}

mnt := rspec.Mount{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"nosuid", "noexec", "nodev", "relatime", mountCgroupOption},
mnt := rspec.Mount{}
err := json.Unmarshal([]byte(mountObject), &mnt)
if err != nil {
return err
}
g.initSpec()
g.spec.Mounts = append(g.spec.Mounts, mnt)

return nil
}

// AddBindMount adds a bind mount into g.spec.Mounts.
func (g *Generator) AddBindMount(source, dest string, options []string) {
if len(options) == 0 {
options = []string{"rw"}
}

// We have to make sure that there is a bind option set, otherwise it won't
// be an actual bindmount.
foundBindOption := false
for _, opt := range options {
if opt == "bind" || opt == "rbind" {
foundBindOption = true
break
}
}
if !foundBindOption {
options = append(options, "bind")
}

mnt := rspec.Mount{
Destination: dest,
Type: "bind",
Source: source,
Options: options,
// ClearMounts clear g.spec.Mounts
func (g *Generator) ClearMounts() {
if g.spec == nil {
return
}
g.initSpec()
g.spec.Mounts = append(g.spec.Mounts, mnt)
g.spec.Mounts = []rspec.Mount{}
}

// SetupPrivileged sets up the privilege-related fields inside g.spec.
Expand Down
42 changes: 18 additions & 24 deletions man/oci-runtime-tool-generate.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,18 +283,20 @@ read the configuration from `config.json`.

Add UIDMappings e.g HostUID:ContainerID:Size. Implies **--user=**.

**--mount-bind**=*[[HOST-DIR:CONTAINER-DIR][:OPTIONS...]]*
Bind mount directories src:dest:(rw,ro) If you specify, ` --mount-bind
/HOST-DIR:/CONTAINER-DIR`, runc bind mounts `/HOST-DIR` in the host
to `/CONTAINER-DIR` in the OCI container. The `OPTIONS` are a colon
delimited list and can be any mount option support by the runtime such
as [rw|ro|rbind|bind|...]. The `HOST_DIR` and `CONTAINER-DIR` must be
absolute paths such as `/src/docs`. You can set the `ro` or `rw`
options to a bind-mount to mount it read-only or read-write mode,
respectively. By default, bind-mounts are mounted read-write.

**--mount-cgroups**=[rw|ro|no]
Mount cgroups. The default is *no*.
**--mounts-add**=[]
Configures additional mounts inside container.
This option can be specified multiple times.
For example,
A. Add tmpfs into container.
--mounts-add '{"destination": "/tmp","type": "tmpfs","source": "tmpfs","options": ["nosuid","strictatime","mode=755","size=65536k"]}'
B. Bind host directory into containeri.
--mounts-add '{"destination": "/data","type": "bind","source": "/volumes/testing","options": ["rbind","rw"]}'
C. mount for windows platform
--mount-add '{"destination": "C:\\folder-inside-container","source": "C:\\folder-on-host","options": ["ro"]}'

**--mounts-remove-all**=true|false
Remove all mounts inside the container. The default is *false*.
When specified with --mount-add, this option will be parsed first.

**--output**=PATH
Instead of writing the configuration JSON to stdout, write it to a
Expand Down Expand Up @@ -392,14 +394,6 @@ read the configuration from `config.json`.
Additional options will only adjust the relevant portions of your template.
Templates are not validated for correctness, so the user should ensure that they are correct.

**--tmpfs**=[] Create a tmpfs mount
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:

$ oci-runtime-tool generate -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image

This command mounts a `tmpfs` at `/tmp` within the container. The supported mount options are the same as the Linux default `mount` flags. If you do not specify any options, the systems uses the following options:
`rw,noexec,nosuid,nodev,size=65536k`.

# EXAMPLES

## Generating container in read-only mode
Expand All @@ -413,14 +407,14 @@ This protects the containers image from modification. Read only containers may
still need to write temporary data. The best way to handle this is to mount
tmpfs directories on /generate and /tmp.

$ oci-runtime-tool generate --rootfs-readonly --tmpfs /generate --tmpfs /tmp --tmpfs /run --rootfs-path /var/lib/containers/fedora --args bash
$ oci-runtime-tool generate --rootfs-readonly --mounts-add '{"destination": "/tmp","type": "tmpfs","source": "tmpfs","options": ["nosuid","strictatime","mode=755","size=65536k"]}' --mounts-add '{"destination": "/run","type": "tmpfs","source": "tmpfs","options": ["nosuid","strictatime","mode=755","size=65536k"]}' --rootfs-path /var/lib/containers/fedora --args bash

## Exposing log messages from the container to the host's log

If you want messages that are logged in your container to show up in the host's
syslog/journal then you should bind mount the /dev/log directory as follows.

$ oci-runtime-tool generate --mount-bind /dev/log:/dev/log --rootfs-path /var/lib/containers/fedora --args bash
$ oci-runtime-tool generate --mounts-add '{"destination": "/dev/log","type": "bind","source": "/dev/log","options": ["rbind","rw"]}' --rootfs-path /var/lib/containers/fedora --args bash

From inside the container you can test this by sending a message to the log.

Expand All @@ -440,13 +434,13 @@ To mount a host directory as a container volume, specify the absolute path to
the directory and the absolute path for the container directory separated by a
colon:

$ oci-runtime-tool generate --mount-bind /var/db:/data1 --rootfs-path /var/lib/containers/fedora --args bash
$ oci-runtime-tool generate --mounts-add '{"destination": "/var/db","type": "bind","source": "/data1","options": ["rbind","rw"]}' --rootfs-path /var/lib/containers/fedora --args bash

## Using SELinux

You can use SELinux to add security to the container. You must specify the process label to run the init process inside of the container using `--linux-selinux-label`.

$ oci-runtime-tool generate --mount-bind /var/db:/data1 --linux-selinux-label system_u:system_r:svirt_lxc_net_t:s0:c1,c2 --linux-mount-label system_u:object_r:svirt_sandbo x_file_t:s0:c1,c2 --rootfs-path /var/lib/containers/fedora --args bash
$ oci-runtime-tool generate --mounts-add '{"destination": "/var/db","type": "bind","source": "/data1","options": ["rbind","rw"]}' --linux-selinux-label system_u:system_r:svirt_lxc_net_t:s0:c1,c2 --linux-mount-label system_u:object_r:svirt_sandbo x_file_t:s0:c1,c2 --rootfs-path /var/lib/containers/fedora --args bash

Not in the above example we used a type of svirt_lxc_net_t and an MCS Label of s0:c1,c2. If you want to guarantee separation between containers, you need to make sure that each container gets launched with a different MCS Label pair.

Expand Down