Signed-off-by: Derek McGowan <derek@mcg.dev>
| ... | ... |
@@ -2,6 +2,7 @@ package daemon // import "github.com/docker/docker/daemon" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"github.com/docker/docker/pkg/archive" |
| 5 |
+ "github.com/docker/docker/pkg/idtools" |
|
| 5 | 6 |
) |
| 6 | 7 |
|
| 7 | 8 |
// defaultTarCopyOptions is the setting that is used when unpacking an archive |
| ... | ... |
@@ -9,6 +10,6 @@ import ( |
| 9 | 9 |
func (daemon *Daemon) defaultTarCopyOptions(noOverwriteDirNonDir bool) *archive.TarOptions {
|
| 10 | 10 |
return &archive.TarOptions{
|
| 11 | 11 |
NoOverwriteDirNonDir: noOverwriteDirNonDir, |
| 12 |
- IDMap: daemon.idMapping, |
|
| 12 |
+ IDMap: idtools.FromUserIdentityMapping(daemon.idMapping), |
|
| 13 | 13 |
} |
| 14 | 14 |
} |
| ... | ... |
@@ -23,6 +23,7 @@ import ( |
| 23 | 23 |
"github.com/docker/docker/pkg/process" |
| 24 | 24 |
"github.com/docker/docker/pkg/stringid" |
| 25 | 25 |
"github.com/moby/sys/mount" |
| 26 |
+ "github.com/moby/sys/user" |
|
| 26 | 27 |
"github.com/opencontainers/selinux/go-selinux/label" |
| 27 | 28 |
"github.com/pkg/errors" |
| 28 | 29 |
"go.opentelemetry.io/otel" |
| ... | ... |
@@ -251,14 +252,14 @@ func (daemon *Daemon) setupIPCDirs(ctr *container.Container) error {
|
| 251 | 251 |
fallthrough |
| 252 | 252 |
|
| 253 | 253 |
case ipcMode.IsShareable(): |
| 254 |
- rootIDs := daemon.idMapping.RootPair() |
|
| 254 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 255 | 255 |
if !ctr.HasMountFor("/dev/shm") {
|
| 256 | 256 |
shmPath, err := ctr.ShmResourcePath() |
| 257 | 257 |
if err != nil {
|
| 258 | 258 |
return err |
| 259 | 259 |
} |
| 260 | 260 |
|
| 261 |
- if err := idtools.MkdirAllAndChown(shmPath, 0o700, rootIDs); err != nil {
|
|
| 261 |
+ if err := user.MkdirAllAndChown(shmPath, 0o700, uid, gid); err != nil {
|
|
| 262 | 262 |
return err |
| 263 | 263 |
} |
| 264 | 264 |
|
| ... | ... |
@@ -266,7 +267,7 @@ func (daemon *Daemon) setupIPCDirs(ctr *container.Container) error {
|
| 266 | 266 |
if err := unix.Mount("shm", shmPath, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), label.FormatMountLabel(shmproperty, ctr.GetMountLabel())); err != nil {
|
| 267 | 267 |
return fmt.Errorf("mounting shm tmpfs: %s", err)
|
| 268 | 268 |
} |
| 269 |
- if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil {
|
|
| 269 |
+ if err := os.Chown(shmPath, uid, gid); err != nil {
|
|
| 270 | 270 |
return err |
| 271 | 271 |
} |
| 272 | 272 |
ctr.ShmPath = shmPath |
| ... | ... |
@@ -298,7 +299,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) |
| 298 | 298 |
} |
| 299 | 299 |
|
| 300 | 300 |
// retrieve possible remapped range start for root UID, GID |
| 301 |
- rootIDs := daemon.idMapping.RootPair() |
|
| 301 |
+ ruid, rgid := daemon.idMapping.RootPair() |
|
| 302 | 302 |
|
| 303 | 303 |
for _, s := range ctr.SecretReferences {
|
| 304 | 304 |
// TODO (ehazlett): use type switch when more are supported |
| ... | ... |
@@ -313,7 +314,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) |
| 313 | 313 |
if err != nil {
|
| 314 | 314 |
return errors.Wrap(err, "error getting secret file path") |
| 315 | 315 |
} |
| 316 |
- if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0o700, rootIDs); err != nil {
|
|
| 316 |
+ if err := user.MkdirAllAndChown(filepath.Dir(fPath), 0o700, ruid, rgid); err != nil {
|
|
| 317 | 317 |
return errors.Wrap(err, "error creating secret mount path") |
| 318 | 318 |
} |
| 319 | 319 |
|
| ... | ... |
@@ -338,7 +339,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) |
| 338 | 338 |
return err |
| 339 | 339 |
} |
| 340 | 340 |
|
| 341 |
- if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
|
|
| 341 |
+ if err := os.Chown(fPath, ruid+uid, rgid+gid); err != nil {
|
|
| 342 | 342 |
return errors.Wrap(err, "error setting ownership for secret") |
| 343 | 343 |
} |
| 344 | 344 |
if err := os.Chmod(fPath, s.File.Mode); err != nil {
|
| ... | ... |
@@ -364,7 +365,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) |
| 364 | 364 |
if err != nil {
|
| 365 | 365 |
return errors.Wrap(err, "error getting config file path for container") |
| 366 | 366 |
} |
| 367 |
- if err := idtools.MkdirAllAndChown(filepath.Dir(fPath), 0o700, rootIDs); err != nil {
|
|
| 367 |
+ if err := user.MkdirAllAndChown(filepath.Dir(fPath), 0o700, ruid, rgid); err != nil {
|
|
| 368 | 368 |
return errors.Wrap(err, "error creating config mount path") |
| 369 | 369 |
} |
| 370 | 370 |
|
| ... | ... |
@@ -389,7 +390,7 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) |
| 389 | 389 |
return err |
| 390 | 390 |
} |
| 391 | 391 |
|
| 392 |
- if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil {
|
|
| 392 |
+ if err := os.Chown(fPath, ruid+uid, rgid+gid); err != nil {
|
|
| 393 | 393 |
return errors.Wrap(err, "error setting ownership for config") |
| 394 | 394 |
} |
| 395 | 395 |
if err := os.Chmod(fPath, configRef.File.Mode); err != nil {
|
| ... | ... |
@@ -404,18 +405,18 @@ func (daemon *Daemon) setupSecretDir(ctr *container.Container) (setupErr error) |
| 404 | 404 |
// In practice this is using a tmpfs mount and is used for both "configs" and "secrets" |
| 405 | 405 |
func (daemon *Daemon) createSecretsDir(ctr *container.Container) error {
|
| 406 | 406 |
// retrieve possible remapped range start for root UID, GID |
| 407 |
- rootIDs := daemon.idMapping.RootPair() |
|
| 407 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 408 | 408 |
dir, err := ctr.SecretMountPath() |
| 409 | 409 |
if err != nil {
|
| 410 | 410 |
return errors.Wrap(err, "error getting container secrets dir") |
| 411 | 411 |
} |
| 412 | 412 |
|
| 413 | 413 |
// create tmpfs |
| 414 |
- if err := idtools.MkdirAllAndChown(dir, 0o700, rootIDs); err != nil {
|
|
| 414 |
+ if err := user.MkdirAllAndChown(dir, 0o700, uid, gid); err != nil {
|
|
| 415 | 415 |
return errors.Wrap(err, "error creating secret local mount path") |
| 416 | 416 |
} |
| 417 | 417 |
|
| 418 |
- tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
|
|
| 418 |
+ tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", uid, gid)
|
|
| 419 | 419 |
if err := mount.Mount("tmpfs", dir, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
|
| 420 | 420 |
return errors.Wrap(err, "unable to setup secret mount") |
| 421 | 421 |
} |
| ... | ... |
@@ -430,8 +431,8 @@ func (daemon *Daemon) remountSecretDir(ctr *container.Container) error {
|
| 430 | 430 |
if err := label.Relabel(dir, ctr.MountLabel, false); err != nil {
|
| 431 | 431 |
log.G(context.TODO()).WithError(err).WithField("dir", dir).Warn("Error while attempting to set selinux label")
|
| 432 | 432 |
} |
| 433 |
- rootIDs := daemon.idMapping.RootPair() |
|
| 434 |
- tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootIDs.UID, rootIDs.GID)
|
|
| 433 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 434 |
+ tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", uid, gid)
|
|
| 435 | 435 |
|
| 436 | 436 |
// remount secrets ro |
| 437 | 437 |
if err := mount.Mount("tmpfs", dir, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil {
|
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
"github.com/docker/docker/container" |
| 15 | 15 |
"github.com/docker/docker/errdefs" |
| 16 | 16 |
"github.com/docker/docker/oci" |
| 17 |
+ "github.com/docker/docker/pkg/idtools" |
|
| 17 | 18 |
volumemounts "github.com/docker/docker/volume/mounts" |
| 18 | 19 |
volumeopts "github.com/docker/docker/volume/service/opts" |
| 19 | 20 |
"github.com/opencontainers/selinux/go-selinux/label" |
| ... | ... |
@@ -27,8 +28,8 @@ func (daemon *Daemon) createContainerOSSpecificSettings(ctx context.Context, con |
| 27 | 27 |
} |
| 28 | 28 |
defer daemon.Unmount(container) |
| 29 | 29 |
|
| 30 |
- rootIDs := daemon.idMapping.RootPair() |
|
| 31 |
- if err := container.SetupWorkingDirectory(rootIDs); err != nil {
|
|
| 30 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 31 |
+ if err := container.SetupWorkingDirectory(idtools.Identity{UID: uid, GID: gid}); err != nil {
|
|
| 32 | 32 |
return err |
| 33 | 33 |
} |
| 34 | 34 |
|
| ... | ... |
@@ -107,8 +108,8 @@ func (daemon *Daemon) populateVolume(ctx context.Context, c *container.Container |
| 107 | 107 |
} |
| 108 | 108 |
return err |
| 109 | 109 |
} |
| 110 |
- |
|
| 111 |
- volumePath, cleanup, err := mnt.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), nil) |
|
| 110 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 111 |
+ volumePath, cleanup, err := mnt.Setup(ctx, c.MountLabel, idtools.Identity{UID: uid, GID: gid}, nil)
|
|
| 112 | 112 |
if err != nil {
|
| 113 | 113 |
if errdefs.IsNotFound(err) {
|
| 114 | 114 |
return nil |
| ... | ... |
@@ -75,6 +75,7 @@ import ( |
| 75 | 75 |
"github.com/moby/buildkit/util/grpcerrors" |
| 76 | 76 |
"github.com/moby/buildkit/util/tracing" |
| 77 | 77 |
"github.com/moby/locker" |
| 78 |
+ "github.com/moby/sys/user" |
|
| 78 | 79 |
"github.com/moby/sys/userns" |
| 79 | 80 |
"github.com/pkg/errors" |
| 80 | 81 |
"go.etcd.io/bbolt" |
| ... | ... |
@@ -113,7 +114,7 @@ type Daemon struct {
|
| 113 | 113 |
sysInfoOnce sync.Once |
| 114 | 114 |
sysInfo *sysinfo.SysInfo |
| 115 | 115 |
shutdown bool |
| 116 |
- idMapping idtools.IdentityMapping |
|
| 116 |
+ idMapping user.IdentityMapping |
|
| 117 | 117 |
PluginStore *plugin.Store // TODO: remove |
| 118 | 118 |
pluginManager *plugin.Manager |
| 119 | 119 |
linkIndex *linkIndex |
| ... | ... |
@@ -791,7 +792,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S |
| 791 | 791 |
if err != nil {
|
| 792 | 792 |
return nil, err |
| 793 | 793 |
} |
| 794 |
- rootIDs := idMapping.RootPair() |
|
| 794 |
+ uid, gid := idMapping.RootPair() |
|
| 795 | 795 |
|
| 796 | 796 |
// set up the tmpDir to use a canonical path |
| 797 | 797 |
tmp, err := prepareTempDir(config.Root) |
| ... | ... |
@@ -878,10 +879,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S |
| 878 | 878 |
} |
| 879 | 879 |
|
| 880 | 880 |
daemonRepo := filepath.Join(cfgStore.Root, "containers") |
| 881 |
- if err := idtools.MkdirAllAndChown(daemonRepo, 0o710, idtools.Identity{
|
|
| 882 |
- UID: idtools.CurrentIdentity().UID, |
|
| 883 |
- GID: rootIDs.GID, |
|
| 884 |
- }); err != nil {
|
|
| 881 |
+ if err := user.MkdirAllAndChown(daemonRepo, 0o710, os.Getuid(), gid); err != nil {
|
|
| 885 | 882 |
return nil, err |
| 886 | 883 |
} |
| 887 | 884 |
|
| ... | ... |
@@ -999,7 +997,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S |
| 999 | 999 |
} |
| 1000 | 1000 |
log.G(ctx).Debugf("Using default logging driver %s", d.defaultLogConfig.Type)
|
| 1001 | 1001 |
|
| 1002 |
- d.volumes, err = volumesservice.NewVolumeService(cfgStore.Root, d.PluginStore, rootIDs, d) |
|
| 1002 |
+ d.volumes, err = volumesservice.NewVolumeService(cfgStore.Root, d.PluginStore, idtools.Identity{UID: uid, GID: gid}, d)
|
|
| 1003 | 1003 |
if err != nil {
|
| 1004 | 1004 |
return nil, err |
| 1005 | 1005 |
} |
| ... | ... |
@@ -1074,15 +1072,15 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S |
| 1074 | 1074 |
RegistryHosts: d.RegistryHosts, |
| 1075 | 1075 |
Registry: d.registryService, |
| 1076 | 1076 |
EventsService: d.EventsService, |
| 1077 |
- IDMapping: idMapping, |
|
| 1078 |
- RefCountMounter: snapshotter.NewMounter(config.Root, driverName, idMapping), |
|
| 1077 |
+ IDMapping: idtools.FromUserIdentityMapping(idMapping), |
|
| 1078 |
+ RefCountMounter: snapshotter.NewMounter(config.Root, driverName, idtools.FromUserIdentityMapping(idMapping)), |
|
| 1079 | 1079 |
}) |
| 1080 | 1080 |
} else {
|
| 1081 | 1081 |
layerStore, err := layer.NewStoreFromOptions(layer.StoreOptions{
|
| 1082 | 1082 |
Root: cfgStore.Root, |
| 1083 | 1083 |
GraphDriver: driverName, |
| 1084 | 1084 |
GraphDriverOptions: cfgStore.GraphOptions, |
| 1085 |
- IDMapping: idMapping, |
|
| 1085 |
+ IDMapping: idtools.FromUserIdentityMapping(idMapping), |
|
| 1086 | 1086 |
}) |
| 1087 | 1087 |
if err != nil {
|
| 1088 | 1088 |
return nil, err |
| ... | ... |
@@ -1423,7 +1421,7 @@ func prepareTempDir(rootDir string) (string, error) {
|
| 1423 | 1423 |
} |
| 1424 | 1424 |
} |
| 1425 | 1425 |
} |
| 1426 |
- return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0o700, idtools.CurrentIdentity()) |
|
| 1426 |
+ return tmpDir, user.MkdirAllAndChown(tmpDir, 0o700, os.Getuid(), os.Getegid()) |
|
| 1427 | 1427 |
} |
| 1428 | 1428 |
|
| 1429 | 1429 |
func (daemon *Daemon) setGenericResources(conf *config.Config) error {
|
| ... | ... |
@@ -1545,7 +1543,8 @@ func CreateDaemonRoot(config *config.Config) error {
|
| 1545 | 1545 |
if err != nil {
|
| 1546 | 1546 |
return err |
| 1547 | 1547 |
} |
| 1548 |
- return setupDaemonRoot(config, realRoot, idMapping.RootPair()) |
|
| 1548 |
+ uid, gid := idMapping.RootPair() |
|
| 1549 |
+ return setupDaemonRoot(config, realRoot, uid, gid) |
|
| 1549 | 1550 |
} |
| 1550 | 1551 |
|
| 1551 | 1552 |
// RemapContainerdNamespaces returns the right containerd namespaces to use: |
| ... | ... |
@@ -1561,16 +1560,16 @@ func RemapContainerdNamespaces(config *config.Config) (ns string, pluginNs strin |
| 1561 | 1561 |
if idMapping.Empty() {
|
| 1562 | 1562 |
return config.ContainerdNamespace, config.ContainerdPluginNamespace, nil |
| 1563 | 1563 |
} |
| 1564 |
- root := idMapping.RootPair() |
|
| 1564 |
+ uid, gid := idMapping.RootPair() |
|
| 1565 | 1565 |
|
| 1566 | 1566 |
ns = config.ContainerdNamespace |
| 1567 | 1567 |
if _, ok := config.ValuesSet["containerd-namespace"]; !ok {
|
| 1568 |
- ns = fmt.Sprintf("%s-%d.%d", config.ContainerdNamespace, root.UID, root.GID)
|
|
| 1568 |
+ ns = fmt.Sprintf("%s-%d.%d", config.ContainerdNamespace, uid, gid)
|
|
| 1569 | 1569 |
} |
| 1570 | 1570 |
|
| 1571 | 1571 |
pluginNs = config.ContainerdPluginNamespace |
| 1572 | 1572 |
if _, ok := config.ValuesSet["containerd-plugin-namespace"]; !ok {
|
| 1573 |
- pluginNs = fmt.Sprintf("%s-%d.%d", config.ContainerdPluginNamespace, root.UID, root.GID)
|
|
| 1573 |
+ pluginNs = fmt.Sprintf("%s-%d.%d", config.ContainerdPluginNamespace, uid, gid)
|
|
| 1574 | 1574 |
} |
| 1575 | 1575 |
|
| 1576 | 1576 |
return ns, pluginNs, nil |
| ... | ... |
@@ -1601,7 +1600,7 @@ func (daemon *Daemon) GetAttachmentStore() *network.AttachmentStore {
|
| 1601 | 1601 |
|
| 1602 | 1602 |
// IdentityMapping returns uid/gid mapping or a SID (in the case of Windows) for the builder |
| 1603 | 1603 |
func (daemon *Daemon) IdentityMapping() idtools.IdentityMapping {
|
| 1604 |
- return daemon.idMapping |
|
| 1604 |
+ return idtools.FromUserIdentityMapping(daemon.idMapping) |
|
| 1605 | 1605 |
} |
| 1606 | 1606 |
|
| 1607 | 1607 |
// ImageService returns the Daemon's ImageService |
| ... | ... |
@@ -45,6 +45,7 @@ import ( |
| 45 | 45 |
"github.com/docker/docker/runconfig" |
| 46 | 46 |
volumemounts "github.com/docker/docker/volume/mounts" |
| 47 | 47 |
"github.com/moby/sys/mount" |
| 48 |
+ "github.com/moby/sys/user" |
|
| 48 | 49 |
"github.com/opencontainers/runtime-spec/specs-go" |
| 49 | 50 |
"github.com/opencontainers/selinux/go-selinux" |
| 50 | 51 |
"github.com/opencontainers/selinux/go-selinux/label" |
| ... | ... |
@@ -1250,9 +1251,10 @@ func removeDefaultBridgeInterface() {
|
| 1250 | 1250 |
} |
| 1251 | 1251 |
} |
| 1252 | 1252 |
|
| 1253 |
-func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error {
|
|
| 1253 |
+func setupInitLayer(idMapping user.IdentityMapping) func(string) error {
|
|
| 1254 | 1254 |
return func(initPath string) error {
|
| 1255 |
- return initlayer.Setup(initPath, idMapping.RootPair()) |
|
| 1255 |
+ uid, gid := idMapping.RootPair() |
|
| 1256 |
+ return initlayer.Setup(initPath, idtools.Identity{UID: uid, GID: gid})
|
|
| 1256 | 1257 |
} |
| 1257 | 1258 |
} |
| 1258 | 1259 |
|
| ... | ... |
@@ -1349,9 +1351,9 @@ func parseRemappedRoot(usergrp string) (string, string, error) {
|
| 1349 | 1349 |
return username, groupname, nil |
| 1350 | 1350 |
} |
| 1351 | 1351 |
|
| 1352 |
-func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) {
|
|
| 1352 |
+func setupRemappedRoot(config *config.Config) (user.IdentityMapping, error) {
|
|
| 1353 | 1353 |
if runtime.GOOS != "linux" && config.RemappedRoot != "" {
|
| 1354 |
- return idtools.IdentityMapping{}, fmt.Errorf("User namespaces are only supported on Linux")
|
|
| 1354 |
+ return user.IdentityMapping{}, fmt.Errorf("User namespaces are only supported on Linux")
|
|
| 1355 | 1355 |
} |
| 1356 | 1356 |
|
| 1357 | 1357 |
// if the daemon was started with remapped root option, parse |
| ... | ... |
@@ -1359,13 +1361,13 @@ func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) {
|
| 1359 | 1359 |
if config.RemappedRoot != "" {
|
| 1360 | 1360 |
username, groupname, err := parseRemappedRoot(config.RemappedRoot) |
| 1361 | 1361 |
if err != nil {
|
| 1362 |
- return idtools.IdentityMapping{}, err
|
|
| 1362 |
+ return user.IdentityMapping{}, err
|
|
| 1363 | 1363 |
} |
| 1364 | 1364 |
if username == "root" {
|
| 1365 | 1365 |
// Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op |
| 1366 | 1366 |
// effectively |
| 1367 | 1367 |
log.G(context.TODO()).Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF")
|
| 1368 |
- return idtools.IdentityMapping{}, nil
|
|
| 1368 |
+ return user.IdentityMapping{}, nil
|
|
| 1369 | 1369 |
} |
| 1370 | 1370 |
log.G(context.TODO()).Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s", username)
|
| 1371 | 1371 |
// 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) {
|
| 1373 | 1373 |
|
| 1374 | 1374 |
mappings, err := usergroup.LoadIdentityMapping(username) |
| 1375 | 1375 |
if err != nil {
|
| 1376 |
- return idtools.IdentityMapping{}, errors.Wrap(err, "Can't create ID mappings")
|
|
| 1376 |
+ return user.IdentityMapping{}, errors.Wrap(err, "Can't create ID mappings")
|
|
| 1377 | 1377 |
} |
| 1378 | 1378 |
return mappings, nil |
| 1379 | 1379 |
} |
| 1380 |
- return idtools.IdentityMapping{}, nil
|
|
| 1380 |
+ return user.IdentityMapping{}, nil
|
|
| 1381 | 1381 |
} |
| 1382 | 1382 |
|
| 1383 |
-func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools.Identity) error {
|
|
| 1383 |
+func setupDaemonRoot(config *config.Config, rootDir string, uid, gid int) error {
|
|
| 1384 | 1384 |
config.Root = rootDir |
| 1385 | 1385 |
// the docker root metadata directory needs to have execute permissions for all users (g+x,o+x) |
| 1386 | 1386 |
// 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 |
| 1400 | 1400 |
} |
| 1401 | 1401 |
} |
| 1402 | 1402 |
|
| 1403 |
- id := idtools.Identity{UID: idtools.CurrentIdentity().UID, GID: remappedRoot.GID}
|
|
| 1403 |
+ curuid := os.Getuid() |
|
| 1404 | 1404 |
// First make sure the current root dir has the correct perms. |
| 1405 |
- if err := idtools.MkdirAllAndChown(config.Root, 0o710, id); err != nil {
|
|
| 1405 |
+ if err := user.MkdirAllAndChown(config.Root, 0o710, curuid, gid); err != nil {
|
|
| 1406 | 1406 |
return errors.Wrapf(err, "could not create or set daemon root permissions: %s", config.Root) |
| 1407 | 1407 |
} |
| 1408 | 1408 |
|
| ... | ... |
@@ -1411,10 +1413,10 @@ func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools |
| 1411 | 1411 |
// a new subdirectory with ownership set to the remapped uid/gid (so as to allow |
| 1412 | 1412 |
// `chdir()` to work for containers namespaced to that uid/gid) |
| 1413 | 1413 |
if config.RemappedRoot != "" {
|
| 1414 |
- config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", remappedRoot.UID, remappedRoot.GID))
|
|
| 1414 |
+ config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", uid, gid))
|
|
| 1415 | 1415 |
log.G(context.TODO()).Debugf("Creating user namespaced daemon root: %s", config.Root)
|
| 1416 | 1416 |
// Create the root directory if it doesn't exist |
| 1417 |
- if err := idtools.MkdirAllAndChown(config.Root, 0o710, id); err != nil {
|
|
| 1417 |
+ if err := user.MkdirAllAndChown(config.Root, 0o710, curuid, gid); err != nil {
|
|
| 1418 | 1418 |
return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err)
|
| 1419 | 1419 |
} |
| 1420 | 1420 |
// 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 |
| 1427 | 1427 |
if dirPath == "/" {
|
| 1428 | 1428 |
break |
| 1429 | 1429 |
} |
| 1430 |
- if !canAccess(dirPath, remappedRoot) {
|
|
| 1430 |
+ if !canAccess(dirPath, uid, gid) {
|
|
| 1431 | 1431 |
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)
|
| 1432 | 1432 |
} |
| 1433 | 1433 |
} |
| ... | ... |
@@ -1445,7 +1447,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools |
| 1445 | 1445 |
// Note: this is a very rudimentary check, and may not produce accurate results, |
| 1446 | 1446 |
// so should not be used for anything other than the current use, see: |
| 1447 | 1447 |
// https://github.com/moby/moby/issues/43724 |
| 1448 |
-func canAccess(path string, pair idtools.Identity) bool {
|
|
| 1448 |
+func canAccess(path string, uid, gid int) bool {
|
|
| 1449 | 1449 |
statInfo, err := os.Stat(path) |
| 1450 | 1450 |
if err != nil {
|
| 1451 | 1451 |
return false |
| ... | ... |
@@ -1456,11 +1458,11 @@ func canAccess(path string, pair idtools.Identity) bool {
|
| 1456 | 1456 |
return true |
| 1457 | 1457 |
} |
| 1458 | 1458 |
ssi := statInfo.Sys().(*syscall.Stat_t) |
| 1459 |
- if ssi.Uid == uint32(pair.UID) && (perms&0o100 == 0o100) {
|
|
| 1459 |
+ if ssi.Uid == uint32(uid) && (perms&0o100 == 0o100) {
|
|
| 1460 | 1460 |
// owner access. |
| 1461 | 1461 |
return true |
| 1462 | 1462 |
} |
| 1463 |
- if ssi.Gid == uint32(pair.GID) && (perms&0o010 == 0o010) {
|
|
| 1463 |
+ if ssi.Gid == uint32(gid) && (perms&0o010 == 0o010) {
|
|
| 1464 | 1464 |
// group access. |
| 1465 | 1465 |
return true |
| 1466 | 1466 |
} |
| ... | ... |
@@ -24,10 +24,10 @@ import ( |
| 24 | 24 |
"github.com/docker/docker/libnetwork/netlabel" |
| 25 | 25 |
"github.com/docker/docker/libnetwork/options" |
| 26 | 26 |
"github.com/docker/docker/libnetwork/scope" |
| 27 |
- "github.com/docker/docker/pkg/idtools" |
|
| 28 | 27 |
"github.com/docker/docker/pkg/parsers/operatingsystem" |
| 29 | 28 |
"github.com/docker/docker/pkg/sysinfo" |
| 30 | 29 |
"github.com/docker/docker/pkg/system" |
| 30 |
+ "github.com/moby/sys/user" |
|
| 31 | 31 |
"github.com/pkg/errors" |
| 32 | 32 |
"golang.org/x/sys/windows" |
| 33 | 33 |
"golang.org/x/sys/windows/svc/mgr" |
| ... | ... |
@@ -56,7 +56,7 @@ func (daemon *Daemon) parseSecurityOpt(daemonCfg *config.Config, securityOptions |
| 56 | 56 |
return nil |
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 |
-func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error {
|
|
| 59 |
+func setupInitLayer(idMapping user.IdentityMapping) func(string) error {
|
|
| 60 | 60 |
return nil |
| 61 | 61 |
} |
| 62 | 62 |
|
| ... | ... |
@@ -462,11 +462,11 @@ func recursiveUnmount(_ string) error {
|
| 462 | 462 |
return nil |
| 463 | 463 |
} |
| 464 | 464 |
|
| 465 |
-func setupRemappedRoot(config *config.Config) (idtools.IdentityMapping, error) {
|
|
| 466 |
- return idtools.IdentityMapping{}, nil
|
|
| 465 |
+func setupRemappedRoot(config *config.Config) (user.IdentityMapping, error) {
|
|
| 466 |
+ return user.IdentityMapping{}, nil
|
|
| 467 | 467 |
} |
| 468 | 468 |
|
| 469 |
-func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error {
|
|
| 469 |
+func setupDaemonRoot(config *config.Config, rootDir string, uid, gid int) error {
|
|
| 470 | 470 |
config.Root = rootDir |
| 471 | 471 |
// Create the root directory if it doesn't exists |
| 472 | 472 |
if err := system.MkdirAllWithACL(config.Root, 0, system.SddlAdministratorsLocalSystem); err != nil {
|
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/errdefs" |
| 12 | 12 |
"github.com/docker/docker/pkg/archive" |
| 13 | 13 |
"github.com/docker/docker/pkg/chrootarchive" |
| 14 |
+ "github.com/docker/docker/pkg/idtools" |
|
| 14 | 15 |
) |
| 15 | 16 |
|
| 16 | 17 |
// ContainerExport writes the contents of the container to the given |
| ... | ... |
@@ -65,7 +66,7 @@ func (daemon *Daemon) containerExport(ctx context.Context, ctr *container.Contai |
| 65 | 65 |
|
| 66 | 66 |
archv, err := chrootarchive.Tar(basefs, &archive.TarOptions{
|
| 67 | 67 |
Compression: archive.Uncompressed, |
| 68 |
- IDMap: daemon.idMapping, |
|
| 68 |
+ IDMap: idtools.FromUserIdentityMapping(daemon.idMapping), |
|
| 69 | 69 |
}, basefs) |
| 70 | 70 |
if err != nil {
|
| 71 | 71 |
return err |
| ... | ... |
@@ -191,7 +191,7 @@ func (daemon *Daemon) fillSecurityOptions(v *system.Info, sysInfo *sysinfo.SysIn |
| 191 | 191 |
if selinux.GetEnabled() {
|
| 192 | 192 |
securityOptions = append(securityOptions, "name=selinux") |
| 193 | 193 |
} |
| 194 |
- if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 {
|
|
| 194 |
+ if uid, gid := daemon.idMapping.RootPair(); uid != 0 || gid != 0 {
|
|
| 195 | 195 |
securityOptions = append(securityOptions, "name=userns") |
| 196 | 196 |
} |
| 197 | 197 |
if Rootless(cfg) {
|
| ... | ... |
@@ -360,13 +360,13 @@ func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts {
|
| 360 | 360 |
} |
| 361 | 361 |
} |
| 362 | 362 |
|
| 363 |
-func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping {
|
|
| 363 |
+func specMapping(s []user.IDMap) []specs.LinuxIDMapping {
|
|
| 364 | 364 |
var ids []specs.LinuxIDMapping |
| 365 | 365 |
for _, item := range s {
|
| 366 | 366 |
ids = append(ids, specs.LinuxIDMapping{
|
| 367 |
- HostID: uint32(item.HostID), |
|
| 368 |
- ContainerID: uint32(item.ContainerID), |
|
| 369 |
- Size: uint32(item.Size), |
|
| 367 |
+ HostID: uint32(item.ParentID), |
|
| 368 |
+ ContainerID: uint32(item.ID), |
|
| 369 |
+ Size: uint32(item.Count), |
|
| 370 | 370 |
}) |
| 371 | 371 |
} |
| 372 | 372 |
return ids |
| ... | ... |
@@ -712,7 +712,8 @@ func withCommonOptions(daemon *Daemon, daemonCfg *dconfig.Config, c *container.C |
| 712 | 712 |
Path: c.BaseFS, |
| 713 | 713 |
Readonly: c.HostConfig.ReadonlyRootfs, |
| 714 | 714 |
} |
| 715 |
- if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil {
|
|
| 715 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 716 |
+ if err := c.SetupWorkingDirectory(idtools.Identity{UID: uid, GID: gid}); err != nil {
|
|
| 716 | 717 |
return err |
| 717 | 718 |
} |
| 718 | 719 |
cwd := c.Config.WorkingDir |
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
mounttypes "github.com/docker/docker/api/types/mount" |
| 15 | 15 |
"github.com/docker/docker/container" |
| 16 | 16 |
"github.com/docker/docker/internal/cleanups" |
| 17 |
+ "github.com/docker/docker/pkg/idtools" |
|
| 17 | 18 |
volumemounts "github.com/docker/docker/volume/mounts" |
| 18 | 19 |
"github.com/pkg/errors" |
| 19 | 20 |
) |
| ... | ... |
@@ -61,7 +62,8 @@ func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ( |
| 61 | 61 |
return nil |
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 |
- path, clean, err := m.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), checkfunc) |
|
| 64 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 65 |
+ path, clean, err := m.Setup(ctx, c.MountLabel, idtools.Identity{UID: uid, GID: gid}, checkfunc)
|
|
| 65 | 66 |
if err != nil {
|
| 66 | 67 |
return nil, nil, err |
| 67 | 68 |
} |
| ... | ... |
@@ -106,13 +108,13 @@ func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ( |
| 106 | 106 |
// if we are going to mount any of the network files from container |
| 107 | 107 |
// metadata, the ownership must be set properly for potential container |
| 108 | 108 |
// remapped root (user namespaces) |
| 109 |
- rootIDs := daemon.idMapping.RootPair() |
|
| 109 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 110 | 110 |
for _, mnt := range netMounts {
|
| 111 | 111 |
// we should only modify ownership of network files within our own container |
| 112 | 112 |
// metadata repository. If the user specifies a mount path external, it is |
| 113 | 113 |
// up to the user to make sure the file has proper ownership for userns |
| 114 | 114 |
if strings.Index(mnt.Source, daemon.repository) == 0 {
|
| 115 |
- if err := os.Chown(mnt.Source, rootIDs.UID, rootIDs.GID); err != nil {
|
|
| 115 |
+ if err := os.Chown(mnt.Source, uid, gid); err != nil {
|
|
| 116 | 116 |
return nil, nil, err |
| 117 | 117 |
} |
| 118 | 118 |
} |
| ... | ... |
@@ -1,5 +1,7 @@ |
| 1 | 1 |
package daemon // import "github.com/docker/docker/daemon" |
| 2 | 2 |
|
| 3 |
+import "github.com/docker/docker/pkg/idtools" |
|
| 4 |
+ |
|
| 3 | 5 |
// ContainerCreateWorkdir creates the working directory. This solves the |
| 4 | 6 |
// issue arising from https://github.com/docker/docker/issues/27545, |
| 5 | 7 |
// 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 {
|
| 16 | 16 |
return err |
| 17 | 17 |
} |
| 18 | 18 |
defer daemon.Unmount(container) |
| 19 |
- return container.SetupWorkingDirectory(daemon.idMapping.RootPair()) |
|
| 19 |
+ uid, gid := daemon.idMapping.RootPair() |
|
| 20 |
+ return container.SetupWorkingDirectory(idtools.Identity{UID: uid, GID: gid})
|
|
| 20 | 21 |
} |
| ... | ... |
@@ -7,7 +7,7 @@ import ( |
| 7 | 7 |
"syscall" |
| 8 | 8 |
"testing" |
| 9 | 9 |
|
| 10 |
- "github.com/docker/docker/pkg/idtools" |
|
| 10 |
+ mobyuser "github.com/moby/sys/user" |
|
| 11 | 11 |
"gotest.tools/v3/assert" |
| 12 | 12 |
is "gotest.tools/v3/assert/cmp" |
| 13 | 13 |
"gotest.tools/v3/skip" |
| ... | ... |
@@ -29,14 +29,13 @@ func TestNewIDMappings(t *testing.T) {
|
| 29 | 29 |
idMapping, err := LoadIdentityMapping(tempUser.Username) |
| 30 | 30 |
assert.Check(t, err) |
| 31 | 31 |
|
| 32 |
- rootUID, rootGID, err := idtools.GetRootUIDGID(idMapping.UIDMaps, idMapping.GIDMaps) |
|
| 33 |
- assert.Check(t, err) |
|
| 32 |
+ rootUID, rootGID := idMapping.RootPair() |
|
| 34 | 33 |
|
| 35 | 34 |
dirName, err := os.MkdirTemp("", "mkdirall")
|
| 36 | 35 |
assert.Check(t, err, "Couldn't create temp directory") |
| 37 | 36 |
defer os.RemoveAll(dirName) |
| 38 | 37 |
|
| 39 |
- err = idtools.MkdirAllAndChown(dirName, 0o700, idtools.Identity{UID: rootUID, GID: rootGID})
|
|
| 38 |
+ err = mobyuser.MkdirAllAndChown(dirName, 0o700, rootUID, rootGID) |
|
| 40 | 39 |
assert.Check(t, err, "Couldn't change ownership of file path. Got error") |
| 41 | 40 |
cmd := exec.Command("ls", "-la", dirName)
|
| 42 | 41 |
cmd.SysProcAttr = &syscall.SysProcAttr{
|
| ... | ... |
@@ -10,7 +10,6 @@ import ( |
| 10 | 10 |
"strconv" |
| 11 | 11 |
"syscall" |
| 12 | 12 |
|
| 13 |
- "github.com/docker/docker/pkg/idtools" |
|
| 14 | 13 |
"github.com/moby/sys/user" |
| 15 | 14 |
) |
| 16 | 15 |
|
| ... | ... |
@@ -140,28 +139,28 @@ func getExitCode(err error) (int, error) {
|
| 140 | 140 |
// LoadIdentityMapping takes a requested username and |
| 141 | 141 |
// using the data from /etc/sub{uid,gid} ranges, creates the
|
| 142 | 142 |
// proper uid and gid remapping ranges for that user/group pair |
| 143 |
-func LoadIdentityMapping(name string) (idtools.IdentityMapping, error) {
|
|
| 143 |
+func LoadIdentityMapping(name string) (user.IdentityMapping, error) {
|
|
| 144 | 144 |
usr, err := LookupUser(name) |
| 145 | 145 |
if err != nil {
|
| 146 |
- return idtools.IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err)
|
|
| 146 |
+ return user.IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err)
|
|
| 147 | 147 |
} |
| 148 | 148 |
|
| 149 | 149 |
subuidRanges, err := lookupSubRangesFile("/etc/subuid", usr)
|
| 150 | 150 |
if err != nil {
|
| 151 |
- return idtools.IdentityMapping{}, err
|
|
| 151 |
+ return user.IdentityMapping{}, err
|
|
| 152 | 152 |
} |
| 153 | 153 |
subgidRanges, err := lookupSubRangesFile("/etc/subgid", usr)
|
| 154 | 154 |
if err != nil {
|
| 155 |
- return idtools.IdentityMapping{}, err
|
|
| 155 |
+ return user.IdentityMapping{}, err
|
|
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 |
- return idtools.IdentityMapping{
|
|
| 158 |
+ return user.IdentityMapping{
|
|
| 159 | 159 |
UIDMaps: subuidRanges, |
| 160 | 160 |
GIDMaps: subgidRanges, |
| 161 | 161 |
}, nil |
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 |
-func lookupSubRangesFile(path string, usr user.User) ([]idtools.IDMap, error) {
|
|
| 164 |
+func lookupSubRangesFile(path string, usr user.User) ([]user.IDMap, error) {
|
|
| 165 | 165 |
uidstr := strconv.Itoa(usr.Uid) |
| 166 | 166 |
rangeList, err := user.ParseSubIDFileFilter(path, func(sid user.SubID) bool {
|
| 167 | 167 |
return sid.Name == usr.Name || sid.Name == uidstr |
| ... | ... |
@@ -173,16 +172,16 @@ func lookupSubRangesFile(path string, usr user.User) ([]idtools.IDMap, error) {
|
| 173 | 173 |
return nil, fmt.Errorf("no subuid ranges found for user %q", usr.Name)
|
| 174 | 174 |
} |
| 175 | 175 |
|
| 176 |
- idMap := []idtools.IDMap{}
|
|
| 176 |
+ idMap := []user.IDMap{}
|
|
| 177 | 177 |
|
| 178 |
- containerID := 0 |
|
| 178 |
+ containerID := int64(0) |
|
| 179 | 179 |
for _, idrange := range rangeList {
|
| 180 |
- idMap = append(idMap, idtools.IDMap{
|
|
| 181 |
- ContainerID: containerID, |
|
| 182 |
- HostID: int(idrange.SubID), |
|
| 183 |
- Size: int(idrange.Count), |
|
| 180 |
+ idMap = append(idMap, user.IDMap{
|
|
| 181 |
+ ID: containerID, |
|
| 182 |
+ ParentID: idrange.SubID, |
|
| 183 |
+ Count: idrange.Count, |
|
| 184 | 184 |
}) |
| 185 |
- containerID = containerID + int(idrange.Count) |
|
| 185 |
+ containerID = containerID + idrange.Count |
|
| 186 | 186 |
} |
| 187 | 187 |
return idMap, nil |
| 188 | 188 |
} |
| ... | ... |
@@ -119,6 +119,31 @@ type IdentityMapping struct {
|
| 119 | 119 |
GIDMaps []IDMap `json:"GIDMaps"` |
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 |
+// FromUserIdentityMapping converts a [user.IdentityMapping] to an [idtools.IdentityMapping]. |
|
| 123 |
+// |
|
| 124 |
+// Deprecated: use [user.IdentityMapping] directly, this is transitioning to user package. |
|
| 125 |
+func FromUserIdentityMapping(u user.IdentityMapping) IdentityMapping {
|
|
| 126 |
+ return IdentityMapping{
|
|
| 127 |
+ UIDMaps: fromUserIDMap(u.UIDMaps), |
|
| 128 |
+ GIDMaps: fromUserIDMap(u.GIDMaps), |
|
| 129 |
+ } |
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+func fromUserIDMap(u []user.IDMap) []IDMap {
|
|
| 133 |
+ if u == nil {
|
|
| 134 |
+ return nil |
|
| 135 |
+ } |
|
| 136 |
+ m := make([]IDMap, len(u)) |
|
| 137 |
+ for i := range u {
|
|
| 138 |
+ m[i] = IDMap{
|
|
| 139 |
+ ContainerID: int(u[i].ID), |
|
| 140 |
+ HostID: int(u[i].ParentID), |
|
| 141 |
+ Size: int(u[i].Count), |
|
| 142 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+ return m |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 122 | 147 |
// RootPair returns a uid and gid pair for the root user. The error is ignored |
| 123 | 148 |
// because a root user always exists, and the defaults are correct when the uid |
| 124 | 149 |
// and gid maps are empty. |