privilege escalation in Moby
Description
In Docker before versions 9.03.15, 20.10.3 there is a vulnerability involving the --userns-remap option in which access to remapped root allows privilege escalation to real root. When using "--userns-remap", if the root user in the remapped namespace has access to the host filesystem they can modify files under "/var/lib/docker/" that cause writing files with extended privileges. Versions 20.10.3 and 19.03.15 contain patches that prevent privilege escalation from remapped user.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Docker before versions 19.03.15 and 20.10.3, the --userns-remap option allows a remapped root user to escalate to real root by modifying files under /var/lib/docker/.
Vulnerability
Overview
CVE-2021-21284 is a privilege escalation vulnerability in Docker's user namespace remapping feature (--userns-remap). When this feature is enabled, the root user inside a container is remapped to a less privileged user on the host. However, due to improper file permission handling, the remapped root user can modify files under /var/lib/docker/ with extended privileges, potentially leading to full root compromise on the host [2][3].
Exploitation
An attacker who gains root access within a container that uses --userns-remap can exploit this flaw if they also have access to the host filesystem. By writing specially crafted files in the remapped directory, they can trigger actions that execute with real root privileges. The vulnerability affects the overlay2 storage driver, where directory and file permissions were not correctly restricted to the remapped user [3].
Impact
Successful exploitation allows an attacker to escalate from remapped root (which is typically unprivileged on the host) to full real root on the host system. This provides complete control over the host, including the ability to start new containers, access all host resources, and persist across reboots.
Mitigation
Docker released patches in versions 19.03.15 and 20.10.3 that correct the file permission handling [4]. The fix ensures that directories under /var/lib/docker/ are owned by the remapped user and have appropriate permissions (e.g., 0701 instead of 0700) to prevent arbitrary writes by the remapped root [3]. Users are advised to upgrade to these patched versions or apply the workaround of not using --userns-remap with untrusted container images.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/moby/mobyGo | < 19.3.15 | 19.3.15 |
github.com/moby/mobyGo | >= 20.10.0-beta1, < 20.10.3 | 20.10.3 |
Affected products
120- osv-coords119 versionspkg:apk/chainguard/docker-machine-driver-harvesterpkg:apk/chainguard/py3.10-dockerpkg:apk/chainguard/py3.11-dockerpkg:apk/chainguard/py3.12-dockerpkg:apk/chainguard/py3.13-dockerpkg:apk/chainguard/py3-dockerpkg:apk/chainguard/py3-supported-dockerpkg:apk/chainguard/rancher-machinepkg:apk/wolfi/docker-machine-driver-harvesterpkg:apk/wolfi/py3.10-dockerpkg:apk/wolfi/py3.11-dockerpkg:apk/wolfi/py3.12-dockerpkg:apk/wolfi/py3.13-dockerpkg:apk/wolfi/py3-dockerpkg:apk/wolfi/py3-supported-dockerpkg:apk/wolfi/rancher-machinepkg:golang/github.com/moby/mobypkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%2015.2pkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/docker&distro=openSUSE%20Leap%2015.2pkg:rpm/opensuse/docker&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/docker&distro=openSUSE%20Tumbleweedpkg:rpm/opensuse/docker-runc&distro=openSUSE%20Leap%2015.2pkg:rpm/opensuse/docker-stable&distro=openSUSE%20Leap%2015.6pkg:rpm/opensuse/docker-stable&distro=openSUSE%20Tumbleweedpkg:rpm/opensuse/fish&distro=openSUSE%20Leap%2015.2pkg:rpm/opensuse/golang-github-docker-libnetwork&distro=openSUSE%20Leap%2015.2pkg:rpm/opensuse/runc&distro=openSUSE%20Leap%2015.2pkg:rpm/opensuse/runc&distro=openSUSE%20Leap%2015.3pkg:rpm/suse/containerd&distro=SUSE%20Enterprise%20Storage%206pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-ESPOSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-ESPOSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Micro%205.0pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2012pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP2pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP3pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-BCLpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%2015-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/containerd&distro=SUSE%20Manager%20Proxy%204.0pkg:rpm/suse/containerd&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.0pkg:rpm/suse/containerd&distro=SUSE%20Manager%20Server%204.0pkg:rpm/suse/docker&distro=SUSE%20Enterprise%20Storage%206pkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-ESPOSpkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-ESPOSpkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-LTSSpkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Micro%205.0pkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2012pkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP2pkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP3pkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-BCLpkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Server%2015-LTSSpkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015pkg:rpm/suse/docker&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/docker&distro=SUSE%20Manager%20Proxy%204.0pkg:rpm/suse/docker&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.0pkg:rpm/suse/docker&distro=SUSE%20Manager%20Server%204.0pkg:rpm/suse/docker-runc&distro=SUSE%20Enterprise%20Storage%206pkg:rpm/suse/docker-runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-ESPOSpkg:rpm/suse/docker-runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/docker-runc&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2012pkg:rpm/suse/docker-runc&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP2pkg:rpm/suse/docker-runc&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-BCLpkg:rpm/suse/docker-runc&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/docker-runc&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/docker-runc&distro=SUSE%20Manager%20Proxy%204.0pkg:rpm/suse/docker-runc&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.0pkg:rpm/suse/docker-runc&distro=SUSE%20Manager%20Server%204.0pkg:rpm/suse/docker-stable&distro=SUSE%20Enterprise%20Storage%207.1pkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP3-LTSSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP4-ESPOSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP4-LTSSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP5-ESPOSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP5-LTSSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP6pkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP7pkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP5-LTSSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP3-LTSSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP4-LTSSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP5-LTSSpkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP3pkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP4pkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP5pkg:rpm/suse/docker-stable&distro=SUSE%20Linux%20Enterprise%20Server%20LTSS%20Extended%20Security%2012%20SP5pkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Enterprise%20Storage%206pkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-ESPOSpkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2012pkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP2pkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-BCLpkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Manager%20Proxy%204.0pkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.0pkg:rpm/suse/golang-github-docker-libnetwork&distro=SUSE%20Manager%20Server%204.0pkg:rpm/suse/runc&distro=SUSE%20Enterprise%20Storage%206pkg:rpm/suse/runc&distro=SUSE%20Enterprise%20Storage%207pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-ESPOSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-ESPOSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Micro%205.0pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2012pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP2pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP3pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-BCLpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%2015-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/runc&distro=SUSE%20Manager%20Proxy%204.0pkg:rpm/suse/runc&distro=SUSE%20Manager%20Retail%20Branch%20Server%204.0pkg:rpm/suse/runc&distro=SUSE%20Manager%20Server%204.0
< 1.0.5-r0+ 118 more
- (no CPE)range: < 1.0.5-r0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0.15.0.137-r0
- (no CPE)range: < 1.0.5-r0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0.15.0.137-r0
- (no CPE)range: < 19.3.15
- (no CPE)range: < 1.3.9-lp152.2.3.1
- (no CPE)range: < 1.4.4-5.32.1
- (no CPE)range: < 19.03.15_ce-lp152.2.3.1
- (no CPE)range: < 20.10.6_ce-6.49.3
- (no CPE)range: < 20.10.6_ce-2.1
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-lp152.2.3.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-15.1
- (no CPE)range: < 2.7.1-lp152.5.3.1
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-lp152.2.3.1
- (no CPE)range: < 1.0.0~rc93-lp152.2.3.1
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.4.4-5.32.1
- (no CPE)range: < 1.4.4-5.32.1
- (no CPE)range: < 1.4.4-5.32.1
- (no CPE)range: < 1.3.9-16.35.1
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.4.4-5.32.1
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.4.4-5.32.1
- (no CPE)range: < 1.4.4-5.32.1
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 1.3.9-5.29.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 20.10.6_ce-6.49.3
- (no CPE)range: < 20.10.6_ce-6.49.3
- (no CPE)range: < 20.10.6_ce-6.49.3
- (no CPE)range: < 19.03.15_ce-98.60.2
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 20.10.6_ce-6.49.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 20.10.6_ce-6.49.3
- (no CPE)range: < 20.10.6_ce-6.49.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 19.03.15_ce-6.43.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-1.52.1
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 1.0.0rc10+gitr3981_dc9208a3303f-6.45.3
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-1.20.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-150000.1.25.1
- (no CPE)range: < 24.0.9_ce-1.20.1
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-37.1
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 0.7.0.1+gitr2908_55e924b8a842-4.28.3
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-16.8.1
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- (no CPE)range: < 1.0.0~rc93-1.14.2
- moby/mobyv5Range: < 19.03.15
Patches
164bd4485b3a6Merge pull request #41964 from thaJeztah/CVE-2021-21284_master
14 files changed · +69 −71
daemon/container_operations_unix.go+1 −1 modified@@ -466,5 +466,5 @@ func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error { if err != nil { return err } - return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair()) + return idtools.MkdirAllAndChown(p, 0701, idtools.CurrentIdentity()) }
daemon/create.go+2 −4 modified@@ -194,12 +194,10 @@ func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr } ctr.RWLayer = rwLayer - rootIDs := daemon.idMapping.RootPair() - - if err := idtools.MkdirAndChown(ctr.Root, 0700, rootIDs); err != nil { + if err := idtools.MkdirAndChown(ctr.Root, 0701, idtools.CurrentIdentity()); err != nil { return nil, err } - if err := idtools.MkdirAndChown(ctr.CheckpointDir(), 0700, rootIDs); err != nil { + if err := idtools.MkdirAndChown(ctr.CheckpointDir(), 0700, idtools.CurrentIdentity()); err != nil { return nil, err }
daemon/daemon.go+4 −6 modified@@ -795,7 +795,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } // set up the tmpDir to use a canonical path - tmp, err := prepareTempDir(config.Root, rootIDs) + tmp, err := prepareTempDir(config.Root) if err != nil { return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err) } @@ -861,7 +861,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } daemonRepo := filepath.Join(config.Root, "containers") - if err := idtools.MkdirAllAndChown(daemonRepo, 0700, rootIDs); err != nil { + if err := idtools.MkdirAllAndChown(daemonRepo, 0701, idtools.CurrentIdentity()); err != nil { return nil, err } @@ -1374,7 +1374,7 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) { // prepareTempDir prepares and returns the default directory to use // for temporary files. // If it doesn't exist, it is created. If it exists, its content is removed. -func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (string, error) { +func prepareTempDir(rootDir string) (string, error) { var tmpDir string if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" { tmpDir = filepath.Join(rootDir, "tmp") @@ -1392,9 +1392,7 @@ func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (string, erro } } } - // We don't remove the content of tmpdir if it's not the default, - // it may hold things that do not belong to us. - return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIdentity) + return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, idtools.CurrentIdentity()) } func (daemon *Daemon) setGenericResources(conf *config.Config) error {
daemon/daemon_unix.go+10 −4 modified@@ -1196,7 +1196,7 @@ func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) return &idtools.IdentityMapping{}, nil } -func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error { +func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools.Identity) 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 @@ -1221,10 +1221,16 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity 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", rootIdentity.UID, rootIdentity.GID)) + id := idtools.CurrentIdentity() + // First make sure the current root dir has the correct perms. + if err := idtools.MkdirAllAndChown(config.Root, 0701, id); err != nil { + return errors.Wrapf(err, "could not create or set daemon root permissions: %s", config.Root) + } + + config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", remappedRoot.UID, remappedRoot.GID)) logrus.Debugf("Creating user namespaced daemon root: %s", config.Root) // Create the root directory if it doesn't exist - if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIdentity); err != nil { + if err := idtools.MkdirAllAndChown(config.Root, 0701, id); 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 @@ -1237,7 +1243,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools if dirPath == "/" { break } - if !idtools.CanAccess(dirPath, rootIdentity) { + if !idtools.CanAccess(dirPath, remappedRoot) { 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) } }
daemon/graphdriver/aufs/aufs.go+3 −6 modified@@ -129,18 +129,15 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap locker: locker.New(), } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } + currentID := idtools.CurrentIdentity() // Create the root aufs driver dir - if err := idtools.MkdirAllAndChown(root, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(root, 0701, currentID); err != nil { return nil, err } // Populate the dir structure for _, p := range paths { - if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(root, p), 0701, currentID); err != nil { return nil, err } }
daemon/graphdriver/btrfs/btrfs.go+3 −7 modified@@ -70,11 +70,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, graphdriver.ErrPrerequisites } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } - if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil { return nil, err } @@ -525,7 +521,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if err != nil { return err } - if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(subvolumes, 0701, idtools.CurrentIdentity()); err != nil { return err } if parent == "" { @@ -560,7 +556,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil { return err } - if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.CurrentIdentity()); err != nil { return err } if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil {
daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go+5 −9 modified@@ -88,12 +88,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, graphdriver.ErrNotSupported } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } - // Create the driver home dir - if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0701, idtools.CurrentIdentity()); err != nil { return nil, err } @@ -178,10 +173,11 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr } root := idtools.Identity{UID: rootUID, GID: rootGID} - if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { + currentID := idtools.CurrentIdentity() + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, currentID); err != nil { return err } - if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { + if err := idtools.MkdirAndChown(dir, 0701, currentID); err != nil { return err } @@ -215,7 +211,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr return nil } - if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0700, root); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0701, currentID); err != nil { return err }
daemon/graphdriver/overlay2/overlay.go+4 −8 modified@@ -165,12 +165,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap logger.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } - // Create the driver home dir - if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0701, idtools.CurrentIdentity()); err != nil { return nil, err } @@ -339,11 +334,12 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr return err } root := idtools.Identity{UID: rootUID, GID: rootGID} + current := idtools.CurrentIdentity() - if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, current); err != nil { return err } - if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { + if err := idtools.MkdirAndChown(dir, 0701, current); err != nil { return err }
daemon/graphdriver/overlay/overlay.go+7 −9 modified@@ -156,12 +156,8 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap logrus.WithField("storage-driver", "overlay").Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs)) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err - } // Create the driver home dir - if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil { return nil, err } @@ -265,10 +261,11 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr } root := idtools.Identity{UID: rootUID, GID: rootGID} - if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { + currentID := idtools.CurrentIdentity() + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0701, currentID); err != nil { return err } - if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { + if err := idtools.MkdirAndChown(dir, 0701, currentID); err != nil { return err } @@ -281,6 +278,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr // Toplevel images are just a "root" dir if parent == "" { + // This must be 0755 otherwise unprivileged users will in the container will not be able to read / in the container return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, root) } @@ -301,7 +299,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { return err } - return ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666) + return ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0600) } // Otherwise, copy the upper and the lower-id from the parent @@ -311,7 +309,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr return err } - if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil { + if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0600); err != nil { return err }
daemon/graphdriver/vfs/driver.go+2 −3 modified@@ -38,8 +38,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } - rootIDs := d.idMapping.RootPair() - if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil { + if err := idtools.MkdirAllAndChown(home, 0701, idtools.CurrentIdentity()); err != nil { return nil, err } @@ -141,7 +140,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { func (d *Driver) create(id, parent string, size uint64) error { dir := d.dir(id) rootIDs := d.idMapping.RootPair() - if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil { + if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0701, idtools.CurrentIdentity()); err != nil { return err } if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil {
daemon/graphdriver/zfs/zfs.go+1 −5 modified@@ -104,11 +104,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, fmt.Errorf("Failed to get root uid/guid: %v", err) - } - if err := idtools.MkdirAllAndChown(base, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(base, 0701, idtools.CurrentIdentity()); err != nil { return nil, fmt.Errorf("Failed to create '%s': %v", base, err) }
pkg/idtools/idtools.go+8 −3 modified@@ -35,13 +35,13 @@ const ( // MkdirAllAndChown creates a directory (include any along the path) and then modifies // ownership to the requested uid/gid. If the directory already exists, this -// function will still change ownership to the requested uid/gid pair. +// function will still change ownership and permissions. func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error { return mkdirAs(path, mode, owner, true, true) } // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. -// If the directory already exists, this function still changes ownership. +// If the directory already exists, this function still changes ownership and permissions. // Note that unlike os.Mkdir(), this function does not return IsExist error // in case path already exists. func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { @@ -50,7 +50,7 @@ func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { // MkdirAllAndChownNew creates a directory (include any along the path) and then modifies // ownership ONLY of newly created directories to the requested uid/gid. If the -// directories along the path exist, no change of ownership will be performed +// directories along the path exist, no change of ownership or permissions will be performed func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error { return mkdirAs(path, mode, owner, true, false) } @@ -234,3 +234,8 @@ func parseSubidFile(path, username string) (ranges, error) { return rangeList, s.Err() } + +// CurrentIdentity returns the identity of the current process +func CurrentIdentity() Identity { + return Identity{UID: os.Getuid(), GID: os.Getegid()} +}
pkg/idtools/idtools_unix.go+10 −4 modified@@ -40,7 +40,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting } // short-circuit--we were called with an existing directory and chown was requested - return lazyChown(path, owner.UID, owner.GID, stat) + return setPermissions(path, mode, owner.UID, owner.GID, stat) } if os.IsNotExist(err) { @@ -71,7 +71,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting // even if it existed, we will chown the requested path + any subpaths that // didn't exist when we called MkdirAll for _, pathComponent := range paths { - if err := lazyChown(pathComponent, owner.UID, owner.GID, nil); err != nil { + if err := setPermissions(pathComponent, mode, owner.UID, owner.GID, nil); err != nil { return err } } @@ -213,17 +213,23 @@ func callGetent(database, key string) (io.Reader, error) { return bytes.NewReader(out), nil } -// lazyChown performs a chown only if the uid/gid don't match what's requested +// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested // Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the // dir is on an NFS share, so don't call chown unless we absolutely must. -func lazyChown(p string, uid, gid int, stat *system.StatT) error { +// Likewise for setting permissions. +func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT) error { if stat == nil { var err error stat, err = system.Stat(p) if err != nil { return err } } + if os.FileMode(stat.Mode()).Perm() != mode.Perm() { + if err := os.Chmod(p, mode.Perm()); err != nil { + return err + } + } if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) { return nil }
volume/local/local.go+9 −2 modified@@ -50,7 +50,7 @@ type activeMount struct { func New(scope string, rootIdentity idtools.Identity) (*Root, error) { rootDirectory := filepath.Join(scope, volumesPathName) - if err := idtools.MkdirAllAndChown(rootDirectory, 0700, rootIdentity); err != nil { + if err := idtools.MkdirAllAndChown(rootDirectory, 0701, idtools.CurrentIdentity()); err != nil { return nil, err } @@ -153,8 +153,15 @@ func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error } path := r.DataPath(name) + volRoot := filepath.Dir(path) + // Root dir does not need to be accessed by the remapped root + if err := idtools.MkdirAllAndChown(volRoot, 0701, idtools.CurrentIdentity()); err != nil { + return nil, errors.Wrapf(errdefs.System(err), "error while creating volume root path '%s'", volRoot) + } + + // Remapped root does need access to the data path if err := idtools.MkdirAllAndChown(path, 0755, r.rootIdentity); err != nil { - return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path) + return nil, errors.Wrapf(errdefs.System(err), "error while creating volume data path '%s'", path) } var err error
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
11- github.com/advisories/GHSA-7452-xqpj-6rpcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-21284ghsaADVISORY
- security.gentoo.org/glsa/202107-23ghsavendor-advisoryx_refsource_GENTOOWEB
- www.debian.org/security/2021/dsa-4865ghsavendor-advisoryx_refsource_DEBIANWEB
- docs.docker.com/engine/release-notes/ghsax_refsource_MISCWEB
- github.com/moby/moby/commit/64bd4485b3a66a597c02c95f5776395e540b2c7cghsax_refsource_MISCWEB
- github.com/moby/moby/releases/tag/v19.03.15ghsax_refsource_MISCWEB
- github.com/moby/moby/releases/tag/v20.10.3ghsax_refsource_MISCWEB
- github.com/moby/moby/security/advisories/GHSA-7452-xqpj-6rpcghsax_refsource_CONFIRMWEB
- security.netapp.com/advisory/ntap-20210226-0005ghsaWEB
- security.netapp.com/advisory/ntap-20210226-0005/mitrex_refsource_CONFIRM
News mentions
0No linked articles in our index yet.