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:
parent
76aeaf8181
commit
d04cbc49d2
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
11
spec.go
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue