114 lines
3.6 KiB
Go
114 lines
3.6 KiB
Go
package specconv
|
|
|
|
import (
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/system"
|
|
"github.com/opencontainers/runc/libcontainer/user"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// ToRootless converts spec to be compatible with "rootless" runc.
|
|
// * Adds userns (Note: since we are already in userns, ideally we should not need to do this. runc-side issue is tracked at https://github.com/opencontainers/runc/issues/1837)
|
|
// * Fix up mount flags (same as above)
|
|
// * Replace /sys with bind-mount (FIXME: we don't need to do this if netns is unshared)
|
|
func ToRootless(spec *specs.Spec) error {
|
|
if !system.RunningInUserNS() {
|
|
return errors.New("needs to be in user namespace")
|
|
}
|
|
uidMap, err := user.CurrentProcessUIDMap()
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
gidMap, err := user.CurrentProcessUIDMap()
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
return toRootless(spec, uidMap, gidMap)
|
|
}
|
|
|
|
// toRootless was forked from github.com/opencontainers/runc/libcontainer/specconv
|
|
func toRootless(spec *specs.Spec, uidMap, gidMap []user.IDMap) error {
|
|
if err := configureUserNS(spec, uidMap, gidMap); err != nil {
|
|
return err
|
|
}
|
|
if err := configureMounts(spec); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Remove cgroup settings.
|
|
spec.Linux.Resources = nil
|
|
spec.Linux.CgroupsPath = ""
|
|
return nil
|
|
}
|
|
|
|
// configureUserNS add suserns and the current ID map to the spec.
|
|
// Since we are already in userns, ideally we should not need to add userns.
|
|
// However, currently rootless runc always requires userns to be added.
|
|
// https://github.com/opencontainers/runc/issues/1837
|
|
func configureUserNS(spec *specs.Spec, uidMap, gidMap []user.IDMap) error {
|
|
spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{
|
|
Type: specs.UserNamespace,
|
|
})
|
|
|
|
sort.Slice(uidMap, func(i, j int) bool { return uidMap[i].ID < uidMap[j].ID })
|
|
uNextContainerID := int64(0)
|
|
for _, u := range uidMap {
|
|
spec.Linux.UIDMappings = append(spec.Linux.UIDMappings,
|
|
specs.LinuxIDMapping{
|
|
HostID: uint32(u.ID),
|
|
ContainerID: uint32(uNextContainerID),
|
|
Size: uint32(u.Count),
|
|
})
|
|
uNextContainerID += int64(u.Count)
|
|
}
|
|
sort.Slice(gidMap, func(i, j int) bool { return gidMap[i].ID < gidMap[j].ID })
|
|
gNextContainerID := int64(0)
|
|
for _, g := range gidMap {
|
|
spec.Linux.GIDMappings = append(spec.Linux.GIDMappings,
|
|
specs.LinuxIDMapping{
|
|
HostID: uint32(g.ID),
|
|
ContainerID: uint32(gNextContainerID),
|
|
Size: uint32(g.Count),
|
|
})
|
|
gNextContainerID += int64(g.Count)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func configureMounts(spec *specs.Spec) error {
|
|
var mounts []specs.Mount
|
|
for _, mount := range spec.Mounts {
|
|
// Ignore all mounts that are under /sys, because we add /sys later.
|
|
if strings.HasPrefix(mount.Destination, "/sys") {
|
|
continue
|
|
}
|
|
|
|
// Remove all gid= and uid= mappings.
|
|
// Since we are already in userns, ideally we should not need to do this.
|
|
// https://github.com/opencontainers/runc/issues/1837
|
|
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, because we can't mount /sys unless we have netns.
|
|
// TODO: keep original /sys mount when we have netns.
|
|
mounts = append(mounts, specs.Mount{
|
|
Source: "/sys",
|
|
Destination: "/sys",
|
|
Type: "none",
|
|
Options: []string{"rbind", "nosuid", "noexec", "nodev", "ro"},
|
|
})
|
|
spec.Mounts = mounts
|
|
return nil
|
|
}
|