diff --git a/daemon/archive_tarcopyoptions.go b/daemon/archive_tarcopyoptions.go index 2e2141042e..42bd1ace2b 100644 --- a/daemon/archive_tarcopyoptions.go +++ b/daemon/archive_tarcopyoptions.go @@ -2,6 +2,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/idtools" ) // defaultTarCopyOptions is the setting that is used when unpacking an archive @@ -9,6 +10,6 @@ import ( func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions { return &archive.TarOptions{ NoOverwriteDirNonDir: noOverwriteDirNonDir, - IDMap: daemon.idMapping, + IDMap: idtools.FromUserIdentityMapping(daemon.idMapping), } } diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index cda101673d..dd8351dd57 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -23,6 +23,7 @@ import ( "github.com/docker/docker/pkg/process" "github.com/docker/docker/pkg/stringid" "github.com/moby/sys/mount" + "github.com/moby/sys/user" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "go.opentelemetry.io/otel" @@ -251,14 +252,14 @@ func (daemon *Daemon) setupIPCDirs(ctr *container.Container) error { fallthrough case ipcMode.IsShareable(): - rootIDs := daemon.idMapping.RootPair() + uid, gid := daemon.idMapping.RootPair() if !ctr.HasMountFor("/dev/shm") { shmPath, err := ctr.ShmResourcePath() if err != nil { return err } - if err := idtools.MkdirAllAndChown(shmPath, 0o700, rootIDs); err != nil { + if err := user.MkdirAllAndChown(shmPath, 0o700, uid, gid); err != nil { return err } @@ -266,7 +267,7 @@ func (daemon *Daemon) setupIPCDirs(ctr *container.Container) error { if err := unix.Mount("shm", shmPath, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), label.FormatMountLabel(shmproperty, ctr.GetMountLabel())); err != nil { return fmt.Errorf("mounting shm tmpfs: %s", err) } - if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil { + if err := os.Chown(shmPath, uid, gid); err != nil { return err } ctr.ShmPath = shmPath @@ -298,7 +299,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) } // retrieve possible remapped range start for root UID, GID - rootIDs := daemon.idMapping.RootPair() + ruid, rgid := daemon.idMapping.RootPair() for _, s := range ctr.SecretReferences { // TODO (ehazlett): use type switch when more are supported @@ -313,7 +314,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) if err != nil { return errors.Wrap(err, "error getting secret file path") } - if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0o700, rootIDs); err != nil { + if err := user.MkdirAllAndChown(filepath.Dir(fPath), 0o700, ruid, rgid); err != nil { return errors.Wrap(err, "error creating secret mount path") } @@ -338,7 +339,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) return err } - if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil { + if err := os.Chown(fPath, ruid+uid, rgid+gid); err != nil { return errors.Wrap(err, "error setting ownership for secret") } if err := os.Chmod(fPath, s.File.Mode); err != nil { @@ -364,7 +365,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) if err != nil { return errors.Wrap(err, "error getting config file path for container") } - if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0o700, rootIDs); err != nil { + if err := user.MkdirAllAndChown(filepath.Dir(fPath), 0o700, ruid, rgid); err != nil { return errors.Wrap(err, "error creating config mount path") } @@ -389,7 +390,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) return err } - if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil { + if err := os.Chown(fPath, ruid+uid, rgid+gid); err != nil { return errors.Wrap(err, "error setting ownership for config") } if err := os.Chmod(fPath, configRef.File.Mode); err != nil { @@ -404,18 +405,18 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) // In practice this is using a tmpfs mount and is used for both "configs" and "secrets" func (daemon *Daemon) createSecretsDir(ctr *container.Container) error { // retrieve possible remapped range start for root UID, GID - rootIDs := daemon.idMapping.RootPair() + uid, gid := daemon.idMapping.RootPair() dir, err := ctr.SecretMountPath() if err != nil { return errors.Wrap(err, "error getting container secrets dir") } // create tmpfs - if err := idtools.MkdirAllAndChown(dir, 0o700, rootIDs); err != nil { + if err := user.MkdirAllAndChown(dir, 0o700, uid, gid); err != nil { return errors.Wrap(err, "error creating secret local mount path") } - tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) + tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", uid, gid) if err := mount.Mount("tmpfs", dir, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil { return errors.Wrap(err, "unable to setup secret mount") } @@ -430,8 +431,8 @@ func (daemon *Daemon) remountSecretDir(ctr *container.Container) error { if err := label.Relabel(dir, ctr.MountLabel, false); err != nil { log.G(context.TODO()).WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label") } - rootIDs := daemon.idMapping.RootPair() - tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID) + uid, gid := daemon.idMapping.RootPair() + tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", uid, gid) // remount secrets ro if err := mount.Mount("tmpfs", dir, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil { diff --git a/daemon/create_unix.go b/daemon/create_unix.go index d91bf94fec..7853a9a5c6 100644 --- a/daemon/create_unix.go +++ b/daemon/create_unix.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/errdefs" "github.com/docker/docker/oci" + "github.com/docker/docker/pkg/idtools" volumemounts "github.com/docker/docker/volume/mounts" volumeopts "github.com/docker/docker/volume/service/opts" "github.com/opencontainers/selinux/go-selinux/label" @@ -27,8 +28,8 @@ func (daemon *Daemon) createContainerOSSpecificSettings(ctx context.Context, con } defer daemon.Unmount(container) - rootIDs := daemon.idMapping.RootPair() - if err := container.SetupWorkingDirectory(rootIDs); err != nil { + uid, gid := daemon.idMapping.RootPair() + if err := container.SetupWorkingDirectory(idtools.Identity{UID: uid, GID: gid}); err != nil { return err } @@ -107,8 +108,8 @@ func (daemon *Daemon) populateVolume(ctx context.Context, c *container.Container } return err } - - volumePath, cleanup, err := mnt.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), nil) + uid, gid := daemon.idMapping.RootPair() + volumePath, cleanup, err := mnt.Setup(ctx, c.MountLabel, idtools.Identity{UID: uid, GID: gid}, nil) if err != nil { if errdefs.IsNotFound(err) { return nil diff --git a/daemon/daemon.go b/daemon/daemon.go index 395f4cc351..50bb480d3b 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -75,6 +75,7 @@ import ( "github.com/moby/buildkit/util/grpcerrors" "github.com/moby/buildkit/util/tracing" "github.com/moby/locker" + "github.com/moby/sys/user" "github.com/moby/sys/userns" "github.com/pkg/errors" "go.etcd.io/bbolt" @@ -113,7 +114,7 @@ type Daemon struct { sysInfoOnce sync.Once sysInfo *sysinfo.SysInfo shutdown bool - idMapping idtools.IdentityMapping + idMapping user.IdentityMapping PluginStore *plugin.Store // TODO: remove pluginManager *plugin.Manager linkIndex *linkIndex @@ -791,7 +792,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S if err != nil { return nil, err } - rootIDs := idMapping.RootPair() + uid, gid := idMapping.RootPair() // set up the tmpDir to use a canonical path tmp, err := prepareTempDir(config.Root) @@ -878,10 +879,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } daemonRepo := filepath.Join(cfgStore.Root, "containers") - if err := idtools.MkdirAllAndChown(daemonRepo, 0o710, idtools.Identity{ - UID: idtools.CurrentIdentity().UID, - GID: rootIDs.GID, - }); err != nil { + if err := user.MkdirAllAndChown(daemonRepo, 0o710, os.Getuid(), gid); err != nil { return nil, err } @@ -999,7 +997,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } log.G(ctx).Debugf("Using default logging driver %s", d.defaultLogConfig.Type) - d.volumes, err = volumesservice.NewVolumeService(cfgStore.Root, d.PluginStore, rootIDs, d) + d.volumes, err = volumesservice.NewVolumeService(cfgStore.Root, d.PluginStore, idtools.Identity{UID: uid, GID: gid}, d) if err != nil { return nil, err } @@ -1074,15 +1072,15 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S RegistryHosts: d.RegistryHosts, Registry: d.registryService, EventsService: d.EventsService, - IDMapping: idMapping, - RefCountMounter: snapshotter.NewMounter(config.Root, driverName, idMapping), + IDMapping: idtools.FromUserIdentityMapping(idMapping), + RefCountMounter: snapshotter.NewMounter(config.Root, driverName, idtools.FromUserIdentityMapping(idMapping)), }) } else { layerStore, err := layer.NewStoreFromOptions(layer.StoreOptions{ Root: cfgStore.Root, GraphDriver: driverName, GraphDriverOptions: cfgStore.GraphOptions, - IDMapping: idMapping, + IDMapping: idtools.FromUserIdentityMapping(idMapping), }) if err != nil { return nil, err @@ -1423,7 +1421,7 @@ func prepareTempDir(rootDir string) (string, error) { } } } - return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0o700, idtools.CurrentIdentity()) + return tmpDir, user.MkdirAllAndChown(tmpDir, 0o700, os.Getuid(), os.Getegid()) } func (daemon *Daemon) setGenericResources(conf *config.Config) error { @@ -1545,7 +1543,8 @@ func CreateDaemonRoot(config *config.Config) error { if err != nil { return err } - return setupDaemonRoot(config, realRoot, idMapping.RootPair()) + uid, gid := idMapping.RootPair() + return setupDaemonRoot(config, realRoot, uid, gid) } // RemapContainerdNamespaces returns the right containerd namespaces to use: @@ -1561,16 +1560,16 @@ func RemapContainerdNamespaces(config *config.Config) (ns string, pluginNs strin if idMapping.Empty() { return config.ContainerdNamespace, config.ContainerdPluginNamespace, nil } - root := idMapping.RootPair() + uid, gid := idMapping.RootPair() ns = config.ContainerdNamespace if _, ok := config.ValuesSet["containerd-namespace"]; !ok { - ns = fmt.Sprintf("%s-%d.%d", config.ContainerdNamespace, root.UID, root.GID) + ns = fmt.Sprintf("%s-%d.%d", config.ContainerdNamespace, uid, gid) } pluginNs = config.ContainerdPluginNamespace if _, ok := config.ValuesSet["containerd-plugin-namespace"]; !ok { - pluginNs = fmt.Sprintf("%s-%d.%d", config.ContainerdPluginNamespace, root.UID, root.GID) + pluginNs = fmt.Sprintf("%s-%d.%d", config.ContainerdPluginNamespace, uid, gid) } return ns, pluginNs, nil @@ -1601,7 +1600,7 @@ func (daemon *Daemon) GetAttachmentStore() *network.AttachmentStore { // IdentityMapping returns uid/gid mapping or a SID (in the case of Windows) for the builder func (daemon *Daemon) IdentityMapping() idtools.IdentityMapping { - return daemon.idMapping + return idtools.FromUserIdentityMapping(daemon.idMapping) } // ImageService returns the Daemon's ImageService diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index b988b07a8e..a36bdee907 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -45,6 +45,7 @@ import ( "github.com/docker/docker/runconfig" volumemounts "github.com/docker/docker/volume/mounts" "github.com/moby/sys/mount" + "github.com/moby/sys/user" "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" @@ -1250,9 +1251,10 @@ func removeDefaultBridgeInterface() { } } -func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error { +func setupInitLayer(idMapping user.IdentityMapping) func(string) error { return func(initPath string) error { - return initlayer.Setup(initPath, idMapping.RootPair()) + uid, gid := idMapping.RootPair() + return initlayer.Setup(initPath, idtools.Identity{UID: uid, GID: gid}) } } @@ -1349,9 +1351,9 @@ func parseRemappedRoot(usergrp string) (string, string, error) { return username, groupname, nil } -func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) { +func setupRemappedRoot(config *config.Config) (user.IdentityMapping, error) { if runtime.GOOS != "linux" && config.RemappedRoot != "" { - return idtools.IdentityMapping{}, fmt.Errorf("User namespaces are only supported on Linux") + return user.IdentityMapping{}, fmt.Errorf("User namespaces are only supported on Linux") } // if the daemon was started with remapped root option, parse @@ -1359,13 +1361,13 @@ func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) { if config.RemappedRoot != "" { username, groupname, err := parseRemappedRoot(config.RemappedRoot) if err != nil { - return idtools.IdentityMapping{}, err + return user.IdentityMapping{}, err } if username == "root" { // Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op // effectively log.G(context.TODO()).Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF") - return idtools.IdentityMapping{}, nil + return user.IdentityMapping{}, nil } log.G(context.TODO()).Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s", username) // update remapped root setting now that we have resolved them to actual names @@ -1373,14 +1375,14 @@ func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) { mappings, err := usergroup.LoadIdentityMapping(username) if err != nil { - return idtools.IdentityMapping{}, errors.Wrap(err, "Can't create ID mappings") + return user.IdentityMapping{}, errors.Wrap(err, "Can't create ID mappings") } return mappings, nil } - return idtools.IdentityMapping{}, nil + return user.IdentityMapping{}, nil } -func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools.Identity) error { +func setupDaemonRoot(config *config.Config, rootDir string, uid, gid int) error { config.Root = rootDir // the docker root metadata directory needs to have execute permissions for all users (g+x,o+x) // so that syscalls executing as non-root, operating on subdirectories of the graph root @@ -1400,9 +1402,9 @@ func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools } } - id := idtools.Identity{UID: idtools.CurrentIdentity().UID, GID: remappedRoot.GID} + curuid := os.Getuid() // First make sure the current root dir has the correct perms. - if err := idtools.MkdirAllAndChown(config.Root, 0o710, id); err != nil { + if err := user.MkdirAllAndChown(config.Root, 0o710, curuid, gid); err != nil { return errors.Wrapf(err, "could not create or set daemon root permissions: %s", config.Root) } @@ -1411,10 +1413,10 @@ func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools // a new subdirectory with ownership set to the remapped uid/gid (so as to allow // `chdir()` to work for containers namespaced to that uid/gid) if config.RemappedRoot != "" { - config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", remappedRoot.UID, remappedRoot.GID)) + config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", uid, gid)) log.G(context.TODO()).Debugf("Creating user namespaced daemon root: %s", config.Root) // Create the root directory if it doesn't exist - if err := idtools.MkdirAllAndChown(config.Root, 0o710, id); err != nil { + if err := user.MkdirAllAndChown(config.Root, 0o710, curuid, gid); err != nil { return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) } // we also need to verify that any pre-existing directories in the path to @@ -1427,7 +1429,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools if dirPath == "/" { break } - if !canAccess(dirPath, remappedRoot) { + if !canAccess(dirPath, uid, gid) { return fmt.Errorf("a subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories", config.Root) } } @@ -1445,7 +1447,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools // Note: this is a very rudimentary check, and may not produce accurate results, // so should not be used for anything other than the current use, see: // https://github.com/moby/moby/issues/43724 -func canAccess(path string, pair idtools.Identity) bool { +func canAccess(path string, uid, gid int) bool { statInfo, err := os.Stat(path) if err != nil { return false @@ -1456,11 +1458,11 @@ func canAccess(path string, pair idtools.Identity) bool { return true } ssi := statInfo.Sys().(*syscall.Stat_t) - if ssi.Uid == uint32(pair.UID) && (perms&0o100 == 0o100) { + if ssi.Uid == uint32(uid) && (perms&0o100 == 0o100) { // owner access. return true } - if ssi.Gid == uint32(pair.GID) && (perms&0o010 == 0o010) { + if ssi.Gid == uint32(gid) && (perms&0o010 == 0o010) { // group access. return true } diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 5a099c1800..20d173c08c 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -24,10 +24,10 @@ import ( "github.com/docker/docker/libnetwork/netlabel" "github.com/docker/docker/libnetwork/options" "github.com/docker/docker/libnetwork/scope" - "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/parsers/operatingsystem" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" + "github.com/moby/sys/user" "github.com/pkg/errors" "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc/mgr" @@ -56,7 +56,7 @@ func (daemon *Daemon) parseSecurityOpt(daemonCfg *config.Config, securityOptions return nil } -func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error { +func setupInitLayer(idMapping user.IdentityMapping) func(string) error { return nil } @@ -462,11 +462,11 @@ func recursiveUnmount(_ string) error { return nil } -func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) { - return idtools.IdentityMapping{}, nil +func setupRemappedRoot(config *config.Config) (user.IdentityMapping, error) { + return user.IdentityMapping{}, nil } -func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error { +func setupDaemonRoot(config *config.Config, rootDir string, uid, gid int) error { config.Root = rootDir // Create the root directory if it doesn't exists if err := system.MkdirAllWithACL(config.Root, 0, system.SddlAdministratorsLocalSystem); err != nil { diff --git a/daemon/export.go b/daemon/export.go index d01057cd26..b3aaca8477 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" + "github.com/docker/docker/pkg/idtools" ) // ContainerExport writes the contents of the container to the given @@ -65,7 +66,7 @@ func (daemon *Daemon) containerExport(ctx context.Context, ctr *container.Contai archv, err := chrootarchive.Tar(basefs, &archive.TarOptions{ Compression: archive.Uncompressed, - IDMap: daemon.idMapping, + IDMap: idtools.FromUserIdentityMapping(daemon.idMapping), }, basefs) if err != nil { return err diff --git a/daemon/info.go b/daemon/info.go index 339d9664ef..ebc3fa280c 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -191,7 +191,7 @@ func (daemon *Daemon) fillSecurityOptions(v *system.Info, sysInfo *sysinfo.SysIn if selinux.GetEnabled() { securityOptions = append(securityOptions, "name=selinux") } - if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 { + if uid, gid := daemon.idMapping.RootPair(); uid != 0 || gid != 0 { securityOptions = append(securityOptions, "name=userns") } if Rootless(cfg) { diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 1529dee046..28c7543527 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -360,13 +360,13 @@ func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts { } } -func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping { +func specMapping(s []user.IDMap) []specs.LinuxIDMapping { var ids []specs.LinuxIDMapping for _, item := range s { ids = append(ids, specs.LinuxIDMapping{ - HostID: uint32(item.HostID), - ContainerID: uint32(item.ContainerID), - Size: uint32(item.Size), + HostID: uint32(item.ParentID), + ContainerID: uint32(item.ID), + Size: uint32(item.Count), }) } return ids @@ -712,7 +712,8 @@ func withCommonOptions(daemon *Daemon, daemonCfg *dconfig.Config, c *container.C Path: c.BaseFS, Readonly: c.HostConfig.ReadonlyRootfs, } - if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil { + uid, gid := daemon.idMapping.RootPair() + if err := c.SetupWorkingDirectory(idtools.Identity{UID: uid, GID: gid}); err != nil { return err } cwd := c.Config.WorkingDir diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index 0a04fe2369..05f4d56fea 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -14,6 +14,7 @@ import ( mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/container" "github.com/docker/docker/internal/cleanups" + "github.com/docker/docker/pkg/idtools" volumemounts "github.com/docker/docker/volume/mounts" "github.com/pkg/errors" ) @@ -61,7 +62,8 @@ func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ( return nil } - path, clean, err := m.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), checkfunc) + uid, gid := daemon.idMapping.RootPair() + path, clean, err := m.Setup(ctx, c.MountLabel, idtools.Identity{UID: uid, GID: gid}, checkfunc) if err != nil { return nil, nil, err } @@ -106,13 +108,13 @@ func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ( // if we are going to mount any of the network files from container // metadata, the ownership must be set properly for potential container // remapped root (user namespaces) - rootIDs := daemon.idMapping.RootPair() + uid, gid := daemon.idMapping.RootPair() for _, mnt := range netMounts { // we should only modify ownership of network files within our own container // metadata repository. If the user specifies a mount path external, it is // up to the user to make sure the file has proper ownership for userns if strings.Index(mnt.Source, daemon.repository) == 0 { - if err := os.Chown(mnt.Source, rootIDs.UID, rootIDs.GID); err != nil { + if err := os.Chown(mnt.Source, uid, gid); err != nil { return nil, nil, err } } diff --git a/daemon/workdir.go b/daemon/workdir.go index 4c0184998a..0e4885f54f 100644 --- a/daemon/workdir.go +++ b/daemon/workdir.go @@ -1,5 +1,7 @@ package daemon // import "github.com/docker/docker/daemon" +import "github.com/docker/docker/pkg/idtools" + // ContainerCreateWorkdir creates the working directory. This solves the // issue arising from https://github.com/docker/docker/issues/27545, // which was initially fixed by https://github.com/docker/docker/pull/27884. But that fix @@ -16,5 +18,6 @@ func (daemon *Daemon) ContainerCreateWorkdir(cID string) error { return err } defer daemon.Unmount(container) - return container.SetupWorkingDirectory(daemon.idMapping.RootPair()) + uid, gid := daemon.idMapping.RootPair() + return container.SetupWorkingDirectory(idtools.Identity{UID: uid, GID: gid}) } diff --git a/internal/usergroup/add_linux_test.go b/internal/usergroup/add_linux_test.go index 9308965d15..6e6cc5e430 100644 --- a/internal/usergroup/add_linux_test.go +++ b/internal/usergroup/add_linux_test.go @@ -7,7 +7,7 @@ import ( "syscall" "testing" - "github.com/docker/docker/pkg/idtools" + mobyuser "github.com/moby/sys/user" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/skip" @@ -29,14 +29,13 @@ func TestNewIDMappings(t *testing.T) { idMapping, err := LoadIdentityMapping(tempUser.Username) assert.Check(t, err) - rootUID, rootGID, err := idtools.GetRootUIDGID(idMapping.UIDMaps, idMapping.GIDMaps) - assert.Check(t, err) + rootUID, rootGID := idMapping.RootPair() dirName, err := os.MkdirTemp("", "mkdirall") assert.Check(t, err, "Couldn't create temp directory") defer os.RemoveAll(dirName) - err = idtools.MkdirAllAndChown(dirName, 0o700, idtools.Identity{UID: rootUID, GID: rootGID}) + err = mobyuser.MkdirAllAndChown(dirName, 0o700, rootUID, rootGID) assert.Check(t, err, "Couldn't change ownership of file path. Got error") cmd := exec.Command("ls", "-la", dirName) cmd.SysProcAttr = &syscall.SysProcAttr{ diff --git a/internal/usergroup/lookup_unix.go b/internal/usergroup/lookup_unix.go index 79e9535c04..4ac10ecc52 100644 --- a/internal/usergroup/lookup_unix.go +++ b/internal/usergroup/lookup_unix.go @@ -10,7 +10,6 @@ import ( "strconv" "syscall" - "github.com/docker/docker/pkg/idtools" "github.com/moby/sys/user" ) @@ -140,28 +139,28 @@ func getExitCode(err error) (int, error) { // LoadIdentityMapping takes a requested username and // using the data from /etc/sub{uid,gid} ranges, creates the // proper uid and gid remapping ranges for that user/group pair -func LoadIdentityMapping(name string) (idtools.IdentityMapping, error) { +func LoadIdentityMapping(name string) (user.IdentityMapping, error) { usr, err := LookupUser(name) if err != nil { - return idtools.IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err) + return user.IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err) } subuidRanges, err := lookupSubRangesFile("/etc/subuid", usr) if err != nil { - return idtools.IdentityMapping{}, err + return user.IdentityMapping{}, err } subgidRanges, err := lookupSubRangesFile("/etc/subgid", usr) if err != nil { - return idtools.IdentityMapping{}, err + return user.IdentityMapping{}, err } - return idtools.IdentityMapping{ + return user.IdentityMapping{ UIDMaps: subuidRanges, GIDMaps: subgidRanges, }, nil } -func lookupSubRangesFile(path string, usr user.User) ([]idtools.IDMap, error) { +func lookupSubRangesFile(path string, usr user.User) ([]user.IDMap, error) { uidstr := strconv.Itoa(usr.Uid) rangeList, err := user.ParseSubIDFileFilter(path, func(sid user.SubID) bool { return sid.Name == usr.Name || sid.Name == uidstr @@ -173,16 +172,16 @@ func lookupSubRangesFile(path string, usr user.User) ([]idtools.IDMap, error) { return nil, fmt.Errorf("no subuid ranges found for user %q", usr.Name) } - idMap := []idtools.IDMap{} + idMap := []user.IDMap{} - containerID := 0 + containerID := int64(0) for _, idrange := range rangeList { - idMap = append(idMap, idtools.IDMap{ - ContainerID: containerID, - HostID: int(idrange.SubID), - Size: int(idrange.Count), + idMap = append(idMap, user.IDMap{ + ID: containerID, + ParentID: idrange.SubID, + Count: idrange.Count, }) - containerID = containerID + int(idrange.Count) + containerID = containerID + idrange.Count } return idMap, nil } diff --git a/pkg/idtools/idtools.go b/pkg/idtools/idtools.go index 37b184cc62..dcb09c2377 100644 --- a/pkg/idtools/idtools.go +++ b/pkg/idtools/idtools.go @@ -119,6 +119,31 @@ type IdentityMapping struct { GIDMaps []IDMap `json:"GIDMaps"` } +// FromUserIdentityMapping converts a [user.IdentityMapping] to an [idtools.IdentityMapping]. +// +// Deprecated: use [user.IdentityMapping] directly, this is transitioning to user package. +func FromUserIdentityMapping(u user.IdentityMapping) IdentityMapping { + return IdentityMapping{ + UIDMaps: fromUserIDMap(u.UIDMaps), + GIDMaps: fromUserIDMap(u.GIDMaps), + } +} + +func fromUserIDMap(u []user.IDMap) []IDMap { + if u == nil { + return nil + } + m := make([]IDMap, len(u)) + for i := range u { + m[i] = IDMap{ + ContainerID: int(u[i].ID), + HostID: int(u[i].ParentID), + Size: int(u[i].Count), + } + } + return m +} + // RootPair returns a uid and gid pair for the root user. The error is ignored // because a root user always exists, and the defaults are correct when the uid // and gid maps are empty.