Skip to content

Commit 34b9f16

Browse files
Userns container in containers
Added a check on devices for hosts that are themselves usernamespaced containers. It is not possible for a non-root host process to set the devices.allow and devices.deny, and therefore this patch skips that for host processes which have a non-root uid_map. Signed-off-by: Abin Shahab <ashahab@altiscale.com>
1 parent 3be7f87 commit 34b9f16

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed

libcontainer/cgroups/fs/devices.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package fs
55
import (
66
"github.com/opencontainers/runc/libcontainer/cgroups"
77
"github.com/opencontainers/runc/libcontainer/configs"
8+
"github.com/opencontainers/runc/libcontainer/utils"
89
)
910

1011
type DevicesGroup struct {
@@ -26,6 +27,13 @@ func (s *DevicesGroup) Apply(d *data) error {
2627
}
2728

2829
func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
30+
isHostUserns, err := utils.IsHostUserns()
31+
if err != nil {
32+
return err
33+
}
34+
if isHostUserns {
35+
return nil
36+
}
2937
if !cgroup.AllowAllDevices {
3038
if err := writeFile(path, "devices.deny", "a"); err != nil {
3139
return err

libcontainer/rootfs_linux.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/opencontainers/runc/libcontainer/cgroups"
1919
"github.com/opencontainers/runc/libcontainer/configs"
2020
"github.com/opencontainers/runc/libcontainer/label"
21+
"github.com/opencontainers/runc/libcontainer/utils"
2122
)
2223

2324
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
@@ -370,7 +371,15 @@ func createDevices(config *configs.Config) error {
370371
for _, node := range config.Devices {
371372
// containers running in a user namespace are not allowed to mknod
372373
// devices so we can just bind mount it from the host.
373-
if err := createDeviceNode(config.Rootfs, node, config.Namespaces.Contains(configs.NEWUSER)); err != nil {
374+
isHostUserns, err := utils.IsHostUserns()
375+
if err != nil {
376+
return err
377+
}
378+
isContainerUserns := config.Namespaces.Contains(configs.NEWUSER)
379+
if err := createDeviceNode(
380+
config.Rootfs,
381+
node,
382+
isContainerUserns || isHostUserns); err != nil {
374383
syscall.Umask(oldMask)
375384
return err
376385
}

libcontainer/utils/utils.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ import (
44
"crypto/rand"
55
"encoding/hex"
66
"io"
7+
"io/ioutil"
78
"path/filepath"
9+
"regexp"
810
"syscall"
911
)
1012

1113
const (
1214
exitSignalOffset = 128
1315
)
1416

17+
var userns_regex, _ = regexp.Compile("0\\s+0\\s+\\d+")
18+
1519
// GenerateRandomName returns a new name joined with a prefix. This size
1620
// specified is used to truncate the randomly generated value
1721
func GenerateRandomName(prefix string, size int) (string, error) {
@@ -43,3 +47,18 @@ func ExitStatus(status syscall.WaitStatus) int {
4347
}
4448
return status.ExitStatus()
4549
}
50+
51+
//Checks if host itself usernamespaced, to allow for
52+
//containers in containers case
53+
func IsHostUserns() (bool, error) {
54+
//scan uid map. should never be more than 5 lines long
55+
dat, err := ioutil.ReadFile("/proc/self/uid_map")
56+
if err != nil {
57+
return false, err
58+
}
59+
if userns_regex.Match(dat) {
60+
return false, nil
61+
}
62+
63+
return true, nil
64+
}

0 commit comments

Comments
 (0)