Fix userns=auto failures in rootless mode

This commit fixes three related issues that prevented userns=auto from
working correctly in rootless containers:

1. Fix setgroups() error with userns=auto
   When using userns=auto, supplementary GIDs from the container image
   were being passed to setgroups() before user namespace mappings were
   allocated by storage, causing EINVAL errors. Added a check in
   container_internal_common.go to skip supplementary groups when
   AutoUserNs is enabled but GIDMap isn't populated yet.

2. Fix ID mapping allocation bug in storage.go
   CreateContainerStorage was returning empty UID/GID mappings from the
   input options parameter instead of the allocated mappings from the
   storage container object, causing containers to fail with "readlink:
   No such file or directory" errors. Changed the return statement to
   use container.UIDMap and container.GIDMap.

3. Add workaround for containers/storage v1.51.0 bug
   The vendored storage library has a bug in parseMountedFiles where
   groupFile path is incorrectly used as a directory path. Set an
   explicit Size=65536 in AutoUserNsOpts to bypass the buggy code path.

With these fixes, all userns=auto tests in 170-run-userns.bats now pass
in rootless mode.

Signed-off-by: Lokesh Mandvekar <lsm5@redhat.com>
This commit is contained in:
Lokesh Mandvekar 2025-12-19 18:21:04 -05:00
parent 141e6cddfa
commit ca1a0db4e7
No known key found for this signature in database
GPG key ID: 1C1EDD7CC7C3A0DD
3 changed files with 19 additions and 3 deletions

View file

@ -399,6 +399,15 @@ func (c *Container) setupStorageMapping(dest, from *storage.IDMappingOptions) {
overrides := c.getUserOverrides()
dest.AutoUserNsOpts.PasswdFile = overrides.ContainerEtcPasswdPath
dest.AutoUserNsOpts.GroupFile = overrides.ContainerEtcGroupPath
// Work around containers/storage v1.51.0 bug in parseMountedFiles where
// groupFile path is incorrectly defaulted. Set an explicit size to avoid
// the buggy code path. This is fixed in newer versions of storage.
// A size of 65536 is reasonable for most containers.
if dest.AutoUserNsOpts.Size == 0 {
dest.AutoUserNsOpts.Size = 65536
}
if c.config.User != "" {
initialSize := uint32(0)
parts := strings.Split(c.config.User, ":")

View file

@ -457,6 +457,11 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
var gidMappings []idtools.IDMap
switch {
case c.config.IDMappings.AutoUserNs && len(c.config.IDMappings.GIDMap) == 0:
// With userns=auto, the GID mappings are allocated later by storage,
// so we can't determine which supplementary GIDs will be available.
// Skip adding them to avoid setgroups() errors.
gidMappings = []idtools.IDMap{}
case len(c.config.IDMappings.GIDMap) > 0:
gidMappings = c.config.IDMappings.GIDMap
case rootless.IsRootless():
@ -488,7 +493,9 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if isGIDAvailable {
g.AddProcessAdditionalGid(uint32(gid))
} else {
logrus.Warnf("Additional gid=%d is not present in the user namespace, skip setting it", gid)
if len(gidMappings) > 0 {
logrus.Warnf("Additional gid=%d is not present in the user namespace, skip setting it", gid)
}
}
}
}

View file

@ -164,8 +164,8 @@ func (r *storageService) CreateContainerStorage(ctx context.Context, systemConte
logrus.Debugf("Container %q has run directory %q", container.ID, containerRunDir)
return ContainerInfo{
UIDMap: options.UIDMap,
GIDMap: options.GIDMap,
UIDMap: container.UIDMap,
GIDMap: container.GIDMap,
Dir: containerDir,
RunDir: containerRunDir,
Config: imageConfig,