rootless: add autogenerated rootless config from `runc spec`

Since this is a runC-specific feature, this belongs here over in
opencontainers/ocitools (which is for generic OCI runtimes).

In addition, we don't create a new network namespace. This is because
currently if you want to set up a veth bridge you need CAP_NET_ADMIN in
both network namespaces' pinned user namespace to create the necessary
interfaces in each network namespace.

Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
Aleksa Sarai 2016-05-09 21:26:11 +10:00
parent 76aeaf8181
commit d04cbc49d2
No known key found for this signature in database
GPG Key ID: 9E18AA267DDB8DB4
3 changed files with 85 additions and 29 deletions

View File

@ -1,16 +1,18 @@
package specconv
import (
"os"
"runtime"
"strings"
"github.com/opencontainers/runtime-spec/specs-go"
)
func sPtr(s string) *string { return &s }
// ExampleSpec returns an example spec file, with many options set so a user
// can see what a standard spec file looks like.
func ExampleSpec() *specs.Spec {
// Example returns an example spec file, with many options set so a user can
// see what a standard spec file looks like.
func Example() *specs.Spec {
return &specs.Spec{
Version: specs.Version,
Platform: specs.Platform{
@ -158,3 +160,68 @@ func ExampleSpec() *specs.Spec {
},
}
}
// ExampleRootless returns an example spec file that works with rootless
// containers. It's essentially a modified version of the specfile from
// Example().
func ToRootless(spec *specs.Spec) {
var namespaces []specs.LinuxNamespace
// Remove networkns from the spec.
for _, ns := range spec.Linux.Namespaces {
switch ns.Type {
case specs.NetworkNamespace, specs.UserNamespace:
// Do nothing.
default:
namespaces = append(namespaces, ns)
}
}
// Add userns to the spec.
namespaces = append(namespaces, specs.LinuxNamespace{
Type: specs.UserNamespace,
})
spec.Linux.Namespaces = namespaces
// Add mappings for the current user.
spec.Linux.UIDMappings = []specs.LinuxIDMapping{{
HostID: uint32(os.Geteuid()),
ContainerID: 0,
Size: 1,
}}
spec.Linux.GIDMappings = []specs.LinuxIDMapping{{
HostID: uint32(os.Getegid()),
ContainerID: 0,
Size: 1,
}}
// Fix up mounts.
var mounts []specs.Mount
for _, mount := range spec.Mounts {
// Ignore all mounts that are under /sys.
if strings.HasPrefix(mount.Destination, "/sys") {
continue
}
// Remove all gid= and uid= mappings.
var options []string
for _, option := range mount.Options {
if !strings.HasPrefix(option, "gid=") && !strings.HasPrefix(option, "uid=") {
options = append(options, option)
}
}
mount.Options = options
mounts = append(mounts, mount)
}
// Add the sysfs mount as an rbind.
mounts = append(mounts, specs.Mount{
Source: "/sys",
Destination: "/sys",
Type: "none",
Options: []string{"rbind", "nosuid", "noexec", "nodev", "ro"},
})
spec.Mounts = mounts
// Remove cgroup settings.
spec.Linux.Resources = nil
}

View File

@ -3,7 +3,6 @@
package specconv
import (
"os"
"testing"
"github.com/opencontainers/runc/libcontainer/configs/validate"
@ -53,8 +52,9 @@ func TestLinuxCgroupsPathNotSpecified(t *testing.T) {
}
func TestSpecconvExampleValidate(t *testing.T) {
spec := ExampleSpec()
spec := Example()
spec.Root.Path = "/"
opts := &CreateOpts{
CgroupName: "ContainerID",
UseSystemdCgroup: false,
@ -97,29 +97,9 @@ func TestDupNamespaces(t *testing.T) {
}
func TestRootlessSpecconvValidate(t *testing.T) {
spec := &specs.Spec{
Linux: specs.Linux{
Namespaces: []specs.Namespace{
{
Type: specs.UserNamespace,
},
},
UIDMappings: []specs.IDMapping{
{
HostID: uint32(os.Geteuid()),
ContainerID: 0,
Size: 1,
},
},
GIDMappings: []specs.IDMapping{
{
HostID: uint32(os.Getegid()),
ContainerID: 0,
Size: 1,
},
},
},
}
spec := Example()
spec.Root.Path = "/"
ToRootless(spec)
opts := &CreateOpts{
CgroupName: "ContainerID",

11
spec.go
View File

@ -64,12 +64,21 @@ container on your host.`,
Value: "",
Usage: "path to the root of the bundle directory",
},
cli.BoolFlag{
Name: "rootless",
Usage: "generate a configuration for a rootless container",
},
},
Action: func(context *cli.Context) error {
if err := checkArgs(context, 0, exactArgs); err != nil {
return err
}
spec := specconv.ExampleSpec()
spec := specconv.Example()
rootless := context.Bool("rootless")
if rootless {
specconv.ToRootless(spec)
}
checkNoFile := func(name string) error {
_, err := os.Stat(name)