From 1ecf851ddb8a48977427eabd3bf1929eac3fedfd Mon Sep 17 00:00:00 2001 From: dvorst <87502756+dvorst@users.noreply.github.com> Date: Thu, 30 Oct 2025 21:26:06 +0100 Subject: [PATCH 1/2] Fix: Rootless Podman-in-Podman on WSL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes: #27411 Adjust SUB_UID and SUB_GID ranges to support running rootless Podman inside a rootless run Podman container. By default, a new user is assigned the following sub-ID ranges: SUB_UID_MIN=100000, SUB_GID_MIN=100000, SUB_UID_COUNT=65536, SUB_GID_COUNT=65536 This means the user’s sub-UID and sub-GID ranges are 100000–165535. When the container is run rootless with the user defined below, ID mappings occur as follows: - Container ID 0 (root) maps to user ID 1000 on the host (which is the user created below). - Container IDs 1–65536 map to IDs 100000–165535 on host (the subid range previously mentioned). If a new user is created inside this container (to build containers for example), it will attempt to use the default sub-ID range (100000–165535). However, this exceeds the container’s available ID mapping, since only IDs up to 65536 are mapped. This causes nested rootless Podman to fail. To enable container-in-container builds, the sub-ID ranges for the user must be large enough to provide at least 65536 usable IDs. A minimum SUB_UID_COUNT and SUB_GID_COUNT of 165536 is required, but 200000 is used here to provide additional margin. Signed-off-by: dvorst <87502756+dvorst@users.noreply.github.com> --- pkg/machine/wsl/declares.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/machine/wsl/declares.go b/pkg/machine/wsl/declares.go index aed26c998a9..efd655b20f8 100644 --- a/pkg/machine/wsl/declares.go +++ b/pkg/machine/wsl/declares.go @@ -38,6 +38,8 @@ ln -fs /dev/null /etc/systemd/system/console-getty.service ln -fs /dev/null /etc/systemd/system/systemd-oomd.socket mkdir -p /etc/systemd/system/systemd-sysusers.service.d/ echo CREATE_MAIL_SPOOL=no >> /etc/default/useradd +sed -ir 's/SUB_UID_COUNT.*/SUB_UID_COUNT 200000/' /etc/login.defs +sed -ir 's/SUB_GID_COUNT.*/SUB_GID_COUNT 200000/' /etc/login.defs adduser -m [USER] -G wheel mkdir -p /home/[USER]/.config/systemd/[USER]/ chown [USER]:[USER] /home/[USER]/.config From 67371a5ee4e1eb86d91514e359d7cf0469606afb Mon Sep 17 00:00:00 2001 From: dvorst <87502756+dvorst@users.noreply.github.com> Date: Mon, 3 Nov 2025 19:05:36 +0100 Subject: [PATCH 2/2] Test: init subid range check for rootless PinP in pkg/machine/e2e/init_test.go Signed-off-by: dvorst <87502756+dvorst@users.noreply.github.com> --- pkg/machine/e2e/init_test.go | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/pkg/machine/e2e/init_test.go b/pkg/machine/e2e/init_test.go index 132fbd9fd5e..6936952af16 100644 --- a/pkg/machine/e2e/init_test.go +++ b/pkg/machine/e2e/init_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "runtime" "strconv" "strings" @@ -124,6 +125,62 @@ var _ = Describe("podman machine init", func() { Expect(repeatSession.errorToString()).To(ContainSubstring(fmt.Sprintf("Error: machine %q already exists", mb.names[0]))) }) + It("init subid range check for rootless PinP", func() { + /* By default, a new user is assigned the following sub-ID ranges (see manual useradd): + * SUB_UID_MIN=100000, SUB_GID_MIN=100000, SUB_UID_COUNT=65536, SUB_GID_COUNT=65536 + * This means the default sub-UID and sub-GID ranges are 100000–165535. + * + * When the container is run rootless by the user in WSL, ID mappings occur as follows: + * Container ID 0 (root) maps to user ID on the host. + * Container IDs 1–65536 map to IDs 100000–165535 on host (range previously mentioned). + * + * If a new user is created inside the container and used to build containers with + * (rootless PinP), it will attempt to use the default sub-ID range (100000–165535). Given + * the mapping, this means that the host must at least have a SUB_UID_COUNT and + * SUB_GID_COUNT of 165536. Since 165536 would only allow rootless PinP for the first + * user (with ID 1000), the check is run against a count of 166536 (=165536+1000) as to + * provide additional margin. + */ + + count_min := 166536 + i := new(initMachine) + name := randomString() + session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath)).run() + Expect(err).ToNot(HaveOccurred()) + Expect(session).To(Exit(0)) + + // obtain the users subid range from the machine + ssh := new(sshMachine) + sshSession, err := mb.setName(name).setCmd(ssh.withSSHCommand([]string{"cat", "/etc/subuid", "/etc/subgid"})).run() + Expect(err).ToNot(HaveOccurred()) + if sshSession.ExitCode() != 0 { + Fail(fmt.Sprintf("SSH session failed with exit code %d\nstdout:\n%s\nstderr:\n%s", + sshSession.ExitCode(), + sshSession.outputToString(), + sshSession.errorToString(), + )) + } + Expect(sshSession).To(Exit(0)) + output := strings.TrimSpace(sshSession.outputToString()) + + // subuid and subgid have the format 'USER:SUB_ID_MIN:SUB_ID_COUNT', for example + // 'user:100000:65536', only count is of interest. + re := regexp.MustCompile(`(?m):(\d+)$`) + counts := re.FindAllStringSubmatch(output, -1) + + // A user must exist in order to run podman rootless, a line in both subuid and subgid + // should exist for it, so 2 lines in total. + Expect(len(counts)).To(BeNumerically(">=", 2), "expected at least 1 user/line in /etc/subuid and /etc/subgid each, got %d", len(counts)) + + // Verify the count. At the moment only 1 user is created in the machine. If multiple users + // are ever created, this will check that all users have a sufficient subid range. + for _, count := range counts { + n, err := strconv.Atoi(count[1]) + Expect(err).ToNot(HaveOccurred()) + Expect(n).To(BeNumerically(">=", count_min), "expected last number %d to be >= %d", n, count_min) + } + }) + It("run playbook", func() { str := randomString()