FR
live

Docker Engine 29.8.0 adds --umask and blocks a 32-bit sandbox-escape path

Docker Engine 29.8.0, released September 3, 2026, introduces a --umask flag to set a container’s file-creation mask and adds AppArmor/SELinux rules that block the 32-bit socketcall(2) path to AF_VSOCK. Upgrade if you share volumes between host and containers or harden your containers.

A row of identical server rack drive trays in near-darkness, one tray’s amber-yellow status LED glowing while all the others stay dark.

September 3, 2026. Docker Engine 29.8.0 shipped, and two changes deserve the attention of teams running containers in production. The first is functional: the --umask flag, requested for years, finally lets you set a container’s file-creation mask. The second is defensive: AppArmor and SELinux rules now block the 32-bit socketcall(2) path used to create AF_VSOCK sockets — a potential escape route toward the host’s virtual machines. Why it matters: both touch the security surface of containers, not cosmetic comfort.

—umask: the permission mask becomes explicit

Historically, a container inherited the umask of the dockerd daemon — almost always 0022 — with no way for an operator to override it per container. Images that wrote sensitive files into shared volumes then produced files that were world-readable (0644), or carried unpredictable group permissions.

Docker Engine 29.8.0 introduces HostConfig.Umask and a --umask <octal> flag on docker create and docker run. The value applies to the main process, execs, and healthchecks — the three places where an inconsistent mask caused permission leaks.

The usage is straightforward:

bash
# Files created 0600 (rw-------) instead of 0644 (rw-r--r--)
docker run --rm --umask 077 alpine sh -c 'umask && touch /data/secret && ls -l /data/secret'

The first umask prints 0077, and /data/secret comes out -rw-------. For a secret, a private key, or a token written into a mounted volume, that is the difference between a file readable by the owner alone and one readable by the whole host.

The most common scenario is the volume shared between containers. Two services that share a volume, under different UIDs, produce files the other can neither read nor modify — or worse, that everyone can read when one runs with a permissive mask. Setting --umask explicitly makes the behavior deterministic and reproducible across machines, which is exactly what operators have wanted since they started running orchestrated deployments.

Consider a CI job that builds artifacts into a bind-mounted cache. Under the inherited 0022 mask, those artifacts are 0644 — readable by any other container on the same host. A neighboring service, or a compromised one, can read your build output without ever touching your process. --umask 027 or --umask 077 closes that hole at creation time.

Why —umask took so long

The --umask request dates back more than a decade and ranks among the most-followed issues in moby/moby. The blocker was not technical but semantic: which umask should a container use by default, and who — the image, the daemon, or the operator — has the final word? A mask that is too permissive creates world-readable files; one that is too strict breaks images that assume 0022.

The 29.8.0 answer is pragmatic: --umask is optional, and without it, the inherited behavior does not change one bit. That is the right way to ship a security option — opt-in, explicit, no breaking change. Teams that need it adopt it immediately; everyone else sees no difference.

Worth noting for orchestrators: Kubernetes still has no first-class equivalent, so pods that write shared files keep inheriting the runtime’s mask unless the image sets it explicitly. Docker’s flag closes a gap the wider container ecosystem has left open for years.

The socketcall(2) rule: closing a 32-bit escape route

The second change is subtler and more defensive. On 32-bit x86, socket-related system calls all flow through a single multiplexer, socketcall(2), instead of the dedicated calls (socket, connect…) used on 64-bit. Docker now blocks this path with AppArmor and SELinux rules to stop a container from using socketcall(2) to create AF_VSOCK sockets.

AF_VSOCK is the address family that connects a guest to its hypervisor (the vsock channel of virtual machines). Letting a container — especially a privileged one, or one running untrusted code — create that socket is handing it a channel to the host’s virtualization layer. The rule does not remove the functionality: it closes the 32-bit detour, leaving the 64-bit paths, which existing policies already cover.

This is textbook defense in depth: it does not patch a specific vulnerability, it removes an entire vector whose exploitation would require a chain of preconditions. Concretely, the risk targeted is a container reaching the hypervisor layer: AF_VSOCK is the standard channel by which a guest — VM or container — talks to the host (vsock powers virtio-vsock and VM agents). A container that manages to open one bypasses normal network isolation (namespaces, firewalls) to speak directly to the underlying infrastructure.

The default AppArmor template becomes configurable

The release’s third security change is less dramatic but just as useful: the daemon can now configure the default container AppArmor profile template. Until now, hardening this profile meant replacing it by hand or maintaining per-container profiles — a fragile operation that was often abandoned in practice.

With this setting, a team can define a baseline policy once, version it alongside the rest of the infrastructure, and have it applied uniformly to every unprivileged container. Combined with the socketcall(2) rule, it is one more step toward hardening by default, without post-install fiddling.

The rest of the release

Version 29.8.0 is more than these three items. On logging, the awslogs driver can attach service names, environments, and custom CloudWatch entity attributes to logs — letting teams pushing to CloudWatch correlate container output with the service name and environment directly in the log attributes, useful when tracing a multi-service incident. On networking, a set of Swarm fixes reduces gossip traffic and stabilizes name resolution after a node failure. On rootless, RootlessKit moves to v3.1.0 and introduces the pesto port driver (IPv4 only, with the pasta network driver) — a welcome simplification for teams publishing ports in rootless mode, a recurring pain point. The technical base also steps up: BuildKit 0.33.0, containerd 2.3.4, runc 1.5.1, and Go 1.26.8.

Verifying after upgrade

Two checks are enough to validate the upgrade. The first confirms the effective mask:

bash
docker run --rm --umask 0027 alpine umask

The output should read 0027. The second checks the AppArmor profile applied to a test container, via the AppArmorProfile field returned by docker inspect. If both answer correctly, the version is operational and the socketcall(2) path is covered by the new rules. Both checks take under a minute and are enough to rule out a silent regression.

Verdict

If you share volumes between the host and containers, or if your images write secrets, keys, or logs into mounts, upgrade and adopt --umask immediately — it fixes an entire class of permission bugs at zero cost to apply. If you harden containers with AppArmor or SELinux, the socketcall(2) rule is one more defensive layer that costs nothing in compatibility for nearly all workloads. If you are on an earlier 29-series release, the upgrade is low risk: no major breaking change, just a refreshed technical base.

The one caveat is the privileged container: AppArmor and SELinux rules do not cover a container launched with --privileged, which by construction retains access to the host kernel. The defense in depth in 29.8.0 applies to the mass of unprivileged containers — and that is already most of the fleet. For the rest, residual risk is handled by reducing the number of privileged containers, not by this patch. For the remainder of the fleet, the upgrade is a quiet win: better file permissions and a smaller escape surface, with no behavioral change to plan around.

References

The cyber brief, every Tuesday

The flaws that matter and the patches to apply, in a ten-minute read.

No spam. One-click unsubscribe.
read next

On the same topic

Kubernetes 1.37 Promotes Rootless Mode for Node Components to Beta

Kubernetes v1.37 enables KubeletInUserNamespace by default: the kubelet, runtimes, and CNI plugins can now run as a non-root user inside a user namespace. Turn it on to confine container-breakout flaws away from host root.

BadHost bypasses FastAPI authentication with a single character in the Host header

CVE-2026-48710, nicknamed BadHost, lets an unauthenticated attacker turn a blocked request into an allowed one by adding a character to the Host header, in every Starlette release before 1.0.1. Upgrade Starlette and audit every middleware that reads request.url.path instead of the raw ASGI path.

← Back to the feed

Type at least two characters.

navigate open esc dismiss