October 4, 2021·6 min read·engineeringdockersecuritypermissionscredentials

Docker Engine 20.10.9 repairs filesystem and credential boundaries

Engine 20.10.9 fixes host filesystem permission exposure and private registry credential routing. The advisories require different actions for the client and daemon, including a container restart for existing directory permissions.

Updating a daemon does not automatically repair every filesystem permission it previously created. That distinction matters in Docker Engine 20.10.9, released October 4. The release addresses permission failures around container storage and file copying, alongside a client bug that can send registry credentials to the wrong service. The fixes concern different components and leave different obligations after the binaries have changed.

The shared engineering problem is preserving the intended authority of an operation. A private registry login should keep its destination when a credential helper fails. Container storage should not expose executable files to an unrelated host user. Copying into a container should not widen permissions on an existing host file. Each operation needs its target and access conditions to remain correct through failure handling and filesystem translation.

I value containers as a way to package an explicit application environment. These fixes are a reminder that the software managing that environment also handles host resources and credentials. The artifact description does not remove the need for precise behavior at those boundaries.

A failed helper must not change the login destination

The Docker CLI advisory for CVE-2021-41092 describes a login to a private registry with a misconfigured credential helper. If a credsStore or credHelpers entry names a helper that cannot execute, supplied credentials can be sent to registry-1.docker.io instead of the intended registry. The affected component is the CLI, with versions before 20.10.9 listed as vulnerable.

The advisory's workaround is to ensure configured helpers are installed, executable and available on PATH. That addresses the failure condition, while updating the CLI supplies the repair. Changing only the daemon cannot establish that a separate client executable contains the corrected handling.

The fixed authentication code makes the important invariant visible. GetDefaultAuthConfig receives the intended server address. For a non-default registry it converts the address to a hostname. If looking up credentials returns an error, the function still returns an authentication configuration whose ServerAddress is populated.

That is a small but meaningful distinction between returning no credentials and returning no destination. The lookup may fail, yet the caller still knows where newly supplied credentials belong. The successful path also sets the address explicitly. Both paths retain the routing information needed for the next authentication operation.

The login path continues after lookup failure

The login implementation explains why retaining that field matters. It selects the server from the command's supplied address or the elected default authentication server, then asks for default credentials. Stored username and password can be tried first. A lookup error or missing credentials instead leads to configuring authentication from the user's input.

The resulting configuration is passed to RegistryLogin. When the server connection fails, the code can try client-side authentication. Once login succeeds, it stores credentials using the store associated with the selected server. These steps make the authentication configuration an object carried across several decisions, rather than a temporary result used only during credential lookup.

An error return therefore does not necessarily terminate the whole workflow. Here it can cause the workflow to request credentials and continue. Returning the known destination alongside the error preserves information the continuation needs. This is a useful pattern to examine in other authentication code, where a failure in optional credential retrieval may precede an interactive or explicit replacement.

The code also distinguishes an identity token returned by authentication from an ordinary password. It clears the password when retaining that token. That handling is another example of why the authentication object has several independent fields whose meaning must survive the flow. A correct username alone is not a complete description of a login request.

Default registry selection also has a defined source. The authentication code asks the daemon's information endpoint for its index server address. If the daemon does not respond, or returns an empty address, the CLI uses the system default. The code comments explain this in terms of clients and daemons on different platforms. This is intentional default selection for a request that needs a default. It is a different operation from retaining an explicit private registry destination after a helper error.

File copying can alter host permissions

The CVE-2021-41089 advisory concerns docker cp into a specially crafted container. Copying can change Unix permissions of existing host files, widening access. The advisory explicitly says this does not directly read, modify or execute those files without another cooperating process. That limit is part of the disclosed impact and should remain in the description.

Engine 20.10.9 fixes the issue. The advisory states that running containers do not need to restart for this repair. Its workaround for systems that cannot update is to run only trusted containers. The release notes describe creating parent directories inside a chroot as the implementation change that prevents the unexpected host permission modification.

The archive extraction path in the fixed daemon shows how much filesystem interpretation surrounds a copy. The daemon mounts the container and its volumes, normalizes the supplied path and resolves symbolic links within the container filesystem. It checks that the destination is a directory before extracting the archive.

It also distinguishes a destination inside a volume from one in the container root filesystem. The code checks read-only constraints before selecting archive copy options and performing extraction. This is more than a byte transfer. The operation interprets paths and metadata while acting through the daemon's authority.

The extraction function's documented contract requires a directory destination. It can also reject replacing a directory with a non-directory, or the reverse, when the corresponding option is active. These checks show that an archive operation has several independent filesystem invariants to maintain, alongside the permission boundary addressed by this release.

That explains why permission changes belong in the security analysis of a copy operation. Even if the command's apparent purpose is adding files to a container, the implementation must keep every filesystem side effect within the intended scope. Resolving the final file payload correctly is insufficient if preparing directories changes an unrelated host object's permissions.

Existing storage permissions require a restart

The CVE-2021-41091 advisory describes insufficiently restricted subdirectories in Docker's data directory, typically /var/lib/docker. Unprivileged Linux users can traverse contents and execute programs. Executables with extended permission bits such as setuid make that exposure consequential. Matching host user IDs and file ownership inside a container can also permit access to files.

The advisory requires updating to 20.10.9 and stopping and restarting running containers so their permissions are fixed. This differs from the copy-operation advisory. A corrected daemon operation and the repair of existing filesystem state are separate effects, so the absence of a restart requirement for one issue cannot be generalized to the other.

For users unable to update, the stated mitigations restrict host access to trusted users and host-volume access to trusted containers. These are narrower descriptions than saying an arbitrary remote request can exploit the problem. The disclosed exposure depends on access to the relevant host filesystem paths and the permissions of the files present there.

I find the distinction between immutable image content and mutable host state useful here. The image describes files to run, while the engine creates and manages storage around them. A permission defect in that storage can persist beyond replacing the executable that created it, which is why the advisory includes an explicit lifecycle action.

The release bundle contains additional dependency repairs

The release notes also update the Go runtime and the packaged containerd and runc versions. These are separate components included in the security release. The named Docker vulnerabilities still need their own scope understood, particularly because one affects the client and the others concern daemon-managed filesystem behavior.

The practical outcome is specific to each repair. The CLI must preserve a private registry address when credential retrieval fails. Archive preparation must stay within the intended filesystem scope. Existing container storage needs the permissions repair applied through stop and restart, as directed by its advisory. Together these changes restore constraints on operations that continue to use familiar Docker commands.