[lxc-devel] [lxd/master] idmap bugfixes and tests

stgraber on Github lxc-bot at linuxcontainers.org
Wed Nov 23 00:06:31 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20161123/ce21cb2d/attachment.bin>
-------------- next part --------------
From 15b100735b7a294bbf2939c1745b68108f6aa900 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Nov 2016 18:28:57 -0500
Subject: [PATCH 1/5] idmapset: Properly render "b" (uid/gid) entries
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 shared/idmapset_linux.go | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/shared/idmapset_linux.go b/shared/idmapset_linux.go
index 6851ce0..9ac0a56 100644
--- a/shared/idmapset_linux.go
+++ b/shared/idmapset_linux.go
@@ -24,11 +24,19 @@ type IdmapEntry struct {
 	Maprange int
 }
 
-func (e *IdmapEntry) ToLxcString() string {
+func (e *IdmapEntry) ToLxcString() []string {
+	if e.Isuid && e.Isgid {
+		return []string{
+			fmt.Sprintf("u %d %d %d", e.Nsid, e.Hostid, e.Maprange),
+			fmt.Sprintf("g %d %d %d", e.Nsid, e.Hostid, e.Maprange),
+		}
+	}
+
 	if e.Isuid {
-		return fmt.Sprintf("u %d %d %d", e.Nsid, e.Hostid, e.Maprange)
+		return []string{fmt.Sprintf("u %d %d %d", e.Nsid, e.Hostid, e.Maprange)}
 	}
-	return fmt.Sprintf("g %d %d %d", e.Nsid, e.Hostid, e.Maprange)
+
+	return []string{fmt.Sprintf("g %d %d %d", e.Nsid, e.Hostid, e.Maprange)}
 }
 
 func is_between(x, low, high int) bool {
@@ -250,7 +258,9 @@ func (m *IdmapSet) AddSafe(i IdmapEntry) error {
 func (m IdmapSet) ToLxcString() []string {
 	var lines []string
 	for _, e := range m.Idmap {
-		lines = append(lines, e.ToLxcString()+"\n")
+		for _, l := range e.ToLxcString() {
+			lines = append(lines, l+"\n")
+		}
 	}
 	return lines
 }

From c3eecd27de018a9ba559953360973da425887458 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Nov 2016 18:29:51 -0500
Subject: [PATCH 2/5] idmapset: Properly detect overlaps
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The existing code was considering a map of range "1" to be made of 2 IDs
rather than just one.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 shared/idmapset_linux.go | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/shared/idmapset_linux.go b/shared/idmapset_linux.go
index 9ac0a56..10ffd4f 100644
--- a/shared/idmapset_linux.go
+++ b/shared/idmapset_linux.go
@@ -63,21 +63,21 @@ func (e *IdmapEntry) HostidsIntersect(i IdmapEntry) bool {
 func (e *IdmapEntry) Intersects(i IdmapEntry) bool {
 	if (e.Isuid && i.Isuid) || (e.Isgid && i.Isgid) {
 		switch {
-		case is_between(e.Hostid, i.Hostid, i.Hostid+i.Maprange):
+		case is_between(e.Hostid, i.Hostid, i.Hostid+i.Maprange-1):
 			return true
-		case is_between(i.Hostid, e.Hostid, e.Hostid+e.Maprange):
+		case is_between(i.Hostid, e.Hostid, e.Hostid+e.Maprange-1):
 			return true
-		case is_between(e.Hostid+e.Maprange, i.Hostid, i.Hostid+i.Maprange):
+		case is_between(e.Hostid+e.Maprange-1, i.Hostid, i.Hostid+i.Maprange-1):
 			return true
-		case is_between(i.Hostid+i.Maprange, e.Hostid, e.Hostid+e.Maprange):
+		case is_between(i.Hostid+i.Maprange-1, e.Hostid, e.Hostid+e.Maprange-1):
 			return true
-		case is_between(e.Nsid, i.Nsid, i.Nsid+i.Maprange):
+		case is_between(e.Nsid, i.Nsid, i.Nsid+i.Maprange-1):
 			return true
-		case is_between(i.Nsid, e.Nsid, e.Nsid+e.Maprange):
+		case is_between(i.Nsid, e.Nsid, e.Nsid+e.Maprange-1):
 			return true
-		case is_between(e.Nsid+e.Maprange, i.Nsid, i.Nsid+i.Maprange):
+		case is_between(e.Nsid+e.Maprange-1, i.Nsid, i.Nsid+i.Maprange-1):
 			return true
-		case is_between(i.Nsid+i.Maprange, e.Nsid, e.Nsid+e.Maprange):
+		case is_between(i.Nsid+i.Maprange-1, e.Nsid, e.Nsid+e.Maprange-1):
 			return true
 		}
 	}
@@ -217,7 +217,7 @@ func (m *IdmapSet) AddSafe(i IdmapEntry) error {
 		}
 
 		if e.HostidsIntersect(i) {
-			return fmt.Errorf("can't map the same host UID twice")
+			return fmt.Errorf("can't map the same host ID twice")
 		}
 
 		added = true

From 89e65e0aa61d6a17a778b39c1048dd5f6c482b06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Nov 2016 18:30:42 -0500
Subject: [PATCH 3/5] idmap: Properly handle "both" entries
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

And also don't ignore errors coming from idmapset.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container_lxc.go | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index d26a2cb..ff7412f 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -491,19 +491,23 @@ func parseRawIdmap(value string) ([]shared.IdmapEntry, error) {
 		switch entries[0] {
 		case "both":
 			entry.Isuid = true
-			ret.AddSafe(entry)
-			ret.AddSafe(shared.IdmapEntry{
-				Isgid:    true,
-				Hostid:   entry.Hostid,
-				Nsid:     entry.Nsid,
-				Maprange: entry.Maprange,
-			})
+			entry.Isgid = true
+			err := ret.AddSafe(entry)
+			if err != nil {
+				return nil, err
+			}
 		case "uid":
 			entry.Isuid = true
-			ret.AddSafe(entry)
+			err := ret.AddSafe(entry)
+			if err != nil {
+				return nil, err
+			}
 		case "gid":
 			entry.Isgid = true
-			ret.AddSafe(entry)
+			err := ret.AddSafe(entry)
+			if err != nil {
+				return nil, err
+			}
 		default:
 			return nil, fmt.Errorf("invalid raw.idmap type %s", line)
 		}

From 49b70251a42f48aa6e43018c2a77a61704c61474 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Nov 2016 18:38:35 -0500
Subject: [PATCH 4/5] idmap: User provided ranges are inclusive
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container_lxc.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index ff7412f..e3bafaf 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -451,6 +451,7 @@ func parseRawIdmap(value string) ([]shared.IdmapEntry, error) {
 			}
 
 			size -= base
+			size += 1
 		}
 
 		return base, size, nil

From 49e4f584e0f9ced177a854584414ab7b7a25db2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Nov 2016 19:00:24 -0500
Subject: [PATCH 5/5] test: Test the idmap feature
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 test/main.sh         |   4 +
 test/suites/idmap.sh | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 216 insertions(+)
 create mode 100644 test/suites/idmap.sh

diff --git a/test/main.sh b/test/main.sh
index a29fd98..c6d8bac 100755
--- a/test/main.sh
+++ b/test/main.sh
@@ -479,6 +479,10 @@ echo "==> TEST: network"
 TEST_CURRENT=test_network
 test_network
 
+echo "==> TEST: idmap"
+TEST_CURRENT=test_idmap
+test_idmap
+
 echo "==> TEST: devlxd"
 TEST_CURRENT=test_devlxd
 test_devlxd
diff --git a/test/suites/idmap.sh b/test/suites/idmap.sh
new file mode 100644
index 0000000..44b2e6a
--- /dev/null
+++ b/test/suites/idmap.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+
+test_idmap() {
+  # Check that we have a big enough range for this test
+  if [ ! -e /etc/subuid ] && [ ! -e /etc/subgid ]; then
+    UIDs=1000000000
+    GIDs=1000000000
+    UID_BASE=1000000
+    GID_BASE=1000000
+  else
+    UIDs=0
+    GIDs=0
+    UID_BASE=0
+    GID_BASE=0
+    LARGEST_UIDs=0
+    LARGEST_GIDs=0
+
+    for entry in $(grep ^root: /etc/subuid); do
+      COUNT=$(echo ${entry} | cut -d: -f3)
+      UIDs=$((${UIDs}+${COUNT}))
+
+      if [ "${COUNT}" -gt "${LARGEST_UIDs}" ]; then
+        LARGEST_UIDs=${COUNT}
+        UID_BASE=$(echo ${entry} | cut -d: -f2)
+      fi
+    done
+
+    for entry in $(grep ^root: /etc/subgid); do
+      COUNT=$(echo ${entry} | cut -d: -f3)
+      GIDs=$((${GIDs}+${COUNT}))
+
+      if [ "${COUNT}" -gt "${LARGEST_GIDs}" ]; then
+        LARGEST_GIDs=${COUNT}
+        GID_BASE=$(echo ${entry} | cut -d: -f2)
+      fi
+    done
+  fi
+
+  if [ "${UIDs}" -lt 500000 ] || [ "${GIDs}" -lt 500000 ]; then
+    echo "==> SKIP: The idmap test requires at least 500000 uids and gids"
+    return
+  fi
+
+  # Setup daemon
+  ensure_import_testimage
+
+  # Check a normal, non-isolated container (full LXD id range)
+  lxc launch testimage idmap
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')" = "${UID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')" = "${GID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $3}')" = "${UIDs}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $3}')" = "${GIDs}" ]
+
+  # Convert container to isolated and confirm it's not using the first range
+  lxc config set idmap security.idmap.isolated true
+  lxc restart idmap --force
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')" = "$((${UID_BASE}+65536))" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')" = "$((${GID_BASE}+65536))" ]
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $3}')" = "65536" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $3}')" = "65536" ]
+
+  # Bump allocation size
+  lxc config set idmap security.idmap.size 100000
+  lxc restart idmap --force
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')" != "${UID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')" != "${GID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $3}')" = "100000" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $3}')" = "100000" ]
+
+  # Switch back to full LXD range
+  lxc config unset idmap security.idmap.isolated
+  lxc config unset idmap security.idmap.size
+  lxc restart idmap --force
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')" = "${UID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')" = "${GID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $3}')" = "${UIDs}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $3}')" = "${GIDs}" ]
+  lxc delete idmap --force
+
+  # Confirm id recycling
+  lxc launch testimage idmap -c security.idmap.isolated=true
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')" = "$((${UID_BASE}+65536))" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')" = "$((${GID_BASE}+65536))" ]
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $3}')" = "65536" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $3}')" = "65536" ]
+
+  # Copy and check that the base differs
+  lxc copy idmap idmap1
+  lxc start idmap1
+  [ "$(lxc exec idmap1 -- cat /proc/self/uid_map | awk '{print $2}')" = "$((${UID_BASE}+131072))" ]
+  [ "$(lxc exec idmap1 -- cat /proc/self/gid_map | awk '{print $2}')" = "$((${GID_BASE}+131072))" ]
+  [ "$(lxc exec idmap1 -- cat /proc/self/uid_map | awk '{print $3}')" = "65536" ]
+  [ "$(lxc exec idmap1 -- cat /proc/self/gid_map | awk '{print $3}')" = "65536" ]
+
+  # Validate non-overlapping maps
+  lxc exec idmap -- touch /a
+  ! lxc exec idmap -- chown 65536 /a
+  lxc exec idmap -- chown 65535 /a
+  PID_1=$(lxc info idmap | grep ^Pid | awk '{print $2}')
+  UID_1=$(stat -c '%u' "/proc/${PID_1}/root/a")
+
+  lxc exec idmap1 -- touch /a
+  PID_2=$(lxc info idmap1 | grep ^Pid | awk '{print $2}')
+  UID_2=$(stat -c '%u' "/proc/${PID_2}/root/a")
+
+  [ "${UID_1}" != "${UID_2}" ]
+  [ "${UID_2}" = "$((${UID_1}+1))" ]
+
+  # Check profile inheritance
+  lxc profile create idmap
+  lxc profile set idmap security.idmap.isolated true
+  lxc profile set idmap security.idmap.size 100000
+
+  lxc launch testimage idmap2
+  [ "$(lxc exec idmap2 -- cat /proc/self/uid_map | awk '{print $2}')" = "${UID_BASE}" ]
+  [ "$(lxc exec idmap2 -- cat /proc/self/gid_map | awk '{print $2}')" = "${GID_BASE}" ]
+  [ "$(lxc exec idmap2 -- cat /proc/self/uid_map | awk '{print $3}')" = "${UIDs}" ]
+  [ "$(lxc exec idmap2 -- cat /proc/self/gid_map | awk '{print $3}')" = "${GIDs}" ]
+
+  lxc profile add idmap idmap
+  lxc profile add idmap1 idmap
+  lxc profile add idmap2 idmap
+  lxc restart idmap idmap1 idmap2 --force
+  lxc launch testimage idmap3 -p default -p idmap
+
+  UID_1=$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')
+  GID_1=$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $2}')" != "${UID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $2}')" != "${GID_BASE}" ]
+  [ "$(lxc exec idmap -- cat /proc/self/uid_map | awk '{print $3}')" = "100000" ]
+  [ "$(lxc exec idmap -- cat /proc/self/gid_map | awk '{print $3}')" = "100000" ]
+
+  UID_2=$(lxc exec idmap1 -- cat /proc/self/uid_map | awk '{print $2}')
+  GID_2=$(lxc exec idmap1 -- cat /proc/self/gid_map | awk '{print $2}')
+  [ "$(lxc exec idmap1 -- cat /proc/self/uid_map | awk '{print $2}')" != "${UID_BASE}" ]
+  [ "$(lxc exec idmap1 -- cat /proc/self/gid_map | awk '{print $2}')" != "${GID_BASE}" ]
+  [ "$(lxc exec idmap1 -- cat /proc/self/uid_map | awk '{print $3}')" = "100000" ]
+  [ "$(lxc exec idmap1 -- cat /proc/self/gid_map | awk '{print $3}')" = "100000" ]
+
+  UID_3=$(lxc exec idmap2 -- cat /proc/self/uid_map | awk '{print $2}')
+  GID_3=$(lxc exec idmap2 -- cat /proc/self/gid_map | awk '{print $2}')
+  [ "$(lxc exec idmap2 -- cat /proc/self/uid_map | awk '{print $2}')" != "${UID_BASE}" ]
+  [ "$(lxc exec idmap2 -- cat /proc/self/gid_map | awk '{print $2}')" != "${GID_BASE}" ]
+  [ "$(lxc exec idmap2 -- cat /proc/self/uid_map | awk '{print $3}')" = "100000" ]
+  [ "$(lxc exec idmap2 -- cat /proc/self/gid_map | awk '{print $3}')" = "100000" ]
+
+  UID_4=$(lxc exec idmap3 -- cat /proc/self/uid_map | awk '{print $2}')
+  GID_4=$(lxc exec idmap3 -- cat /proc/self/gid_map | awk '{print $2}')
+  [ "$(lxc exec idmap3 -- cat /proc/self/uid_map | awk '{print $2}')" != "${UID_BASE}" ]
+  [ "$(lxc exec idmap3 -- cat /proc/self/gid_map | awk '{print $2}')" != "${GID_BASE}" ]
+  [ "$(lxc exec idmap3 -- cat /proc/self/uid_map | awk '{print $3}')" = "100000" ]
+  [ "$(lxc exec idmap3 -- cat /proc/self/gid_map | awk '{print $3}')" = "100000" ]
+
+  [ "${UID_1}" != "${UID_2}" ]
+  [ "${UID_1}" != "${UID_3}" ]
+  [ "${UID_1}" != "${UID_4}" ]
+  [ "${UID_2}" != "${UID_3}" ]
+  [ "${UID_2}" != "${UID_4}" ]
+  [ "${UID_3}" != "${UID_4}" ]
+
+  [ "${GID_1}" != "${GID_2}" ]
+  [ "${GID_1}" != "${GID_3}" ]
+  [ "${GID_1}" != "${GID_4}" ]
+  [ "${GID_2}" != "${GID_3}" ]
+  [ "${GID_2}" != "${GID_4}" ]
+  [ "${UID_3}" != "${UID_4}" ]
+
+  lxc delete idmap1 idmap2 idmap3 --force
+
+  # Test running out of ids
+  ! lxc launch testimage idmap1 -c security.idmap.isolated=true -c security.idmap.size=$((${UIDs}+1))
+
+  # Test raw id maps
+  (
+  cat << EOF
+uid ${UID_BASE} 1000000
+gid $((${GID_BASE}+1)) 1000000
+both $((${UID_BASE}+2)) 2000000
+EOF
+  ) | lxc config set idmap raw.idmap -
+  lxc restart idmap --force
+  PID=$(lxc info idmap | grep ^Pid | awk '{print $2}')
+
+  lxc exec idmap -- touch /a
+  lxc exec idmap -- chown 1000000:1000000 /a
+  [ "$(stat -c '%u:%g' "/proc/${PID}/root/a")" = "${UID_BASE}:$((${GID_BASE}+1))" ]
+
+  lxc exec idmap -- touch /b
+  lxc exec idmap -- chown 2000000:2000000 /b
+  [ "$(stat -c '%u:%g' "/proc/${PID}/root/b")" = "$((${UID_BASE}+2)):$((${GID_BASE}+2))" ]
+
+  # Test id ranges
+  (
+  cat << EOF
+uid $((${UID_BASE}+10))-$((${UID_BASE}+19)) 3000000-3000009
+gid $((${GID_BASE}+10))-$((${GID_BASE}+19)) 3000000-3000009
+both $((${GID_BASE}+20))-$((${GID_BASE}+29)) 4000000-4000009
+EOF
+  ) | lxc config set idmap raw.idmap -
+  lxc restart idmap --force
+  PID=$(lxc info idmap | grep ^Pid | awk '{print $2}')
+
+  lxc exec idmap -- touch /c
+  lxc exec idmap -- chown 3000009:3000009 /c
+  [ "$(stat -c '%u:%g' "/proc/${PID}/root/c")" = "$((${UID_BASE}+19)):$((${GID_BASE}+19))" ]
+
+  lxc exec idmap -- touch /d
+  lxc exec idmap -- chown 4000009:4000009 /d
+  [ "$(stat -c '%u:%g' "/proc/${PID}/root/d")" = "$((${UID_BASE}+29)):$((${GID_BASE}+29))" ]
+
+  lxc delete idmap --force
+}


More information about the lxc-devel mailing list