I reviewed docker.io-app 29.1.3-0ubuntu5 as checked into stonking. This is
a shallow, tool-assisted MIR security review rather than an in-depth audit.
Findings below were derived from the auto-generated audit artifacts in this
directory (packaging.txt, binaries.txt, *.txt tool outputs) and this request.
docker.io-app is the Ubuntu-only repackaging of the Moby Docker engine
(engine/, the `dockerd` daemon), the Docker CLI (cli/), tini
(`docker-init`, the PID-1 subreaper used as the container init) and the
`docker-trust` CLI plugin (cli-plugins). It is built as a mostly-static Go
binary with a small amount of C (tini, libnetwork's `docker-proxy`,
`contrib/syscall-test`). It provides an OCI-compliant container runtime,
exposing a Unix socket API (/var/run/docker.sock) and an optional TCP/REST
API. The daemon runs as root by design (it manages namespaces, cgroups,
seccomp, AppArmor and storage drivers). Only `bin:docker.io` is being
promoted to main; `docker-doc` stays in universe.
- CVE History
- Per the MIR template, docker.io-app has had 19 security issues; counting
the predecessor src:docker.io the full history is 65 issues
(https://ubuntu.com/security/cves?package=docker.io-app and
https://ubuntu.com/security/cves?package=docker.io). Historical fixes
were delivered via new-upstream-version uploads (e.g. 26.1.3-0ubuntu2,
24.0.7-0ubuntu3) as documented in the Docker SRU exception
(https://documentation.ubuntu.com/project/SRU/reference/exception-Docker-Updates/).
Upstream (moby/moby) is responsive and the package has effectively been
treated as supported-in-main for some time already.
- Build-Depends
- From the build log "Merged Build-Depends":
bash-completion, ca-certificates, cmake, debconf (>= 0.5),
debhelper-compat (= 12), dh-apparmor, dh-exec, dh-golang, git,
golang-1.24-go, libapparmor-dev, libbtrfs-dev | btrfs-progs (<< 4.16.1~),
libdevmapper-dev (>= 2:1.02.68~), libltdl-dev, libnftables-dev,
libseccomp-dev, libsystemd-dev (>= 209~), pkg-config, po-debconf, procps,
tzdata, build-essential, fakeroot.
- Sensitive libraries linked: libapparmor (AppArmor integration),
libseccomp (seccomp profiles), libsystemd (journald logger),
libnftables (nftables firewall driver), libdevmapper / libbtrfs (storage
drivers), libltdl (dynamic plugin loading).
- golang-1.24 (1.24.13-2) is used for the (mostly static) Go build;
Built-Using records glibc and golang-1.24.
- Runtime Recommends of note: pigz (parallel gzip for image-layer
decompression - has its own MIR, LP #2150649), ubuntu-fan
(Canonical-specific fan networking - kernel team will re-promote it to
main once this MIR completes).
- pre/post inst/rm scripts
- `preinst`: aborts configuration if the legacy aufs storage driver
directory `/var/lib/docker/aufs` exists, asking the user to remove it.
- `postinst`: creates the `docker` system group (used to set the group on
`docker.sock`); handles ZFS dataset creation/migration for `/var/lib/docker`
(gated on `zfs` being available and the `zfs` module being loaded,
guarded against missing commands and only migrating for old versions);
asks a debconf high-priority question (`docker.io/restart`) before
restarting the daemon on upgrades (with explicit handling to avoid
double-prompting); reloads the AppArmor profile; enables `docker.service`
and `docker.socket` via `dh_installsystemd`; starts the daemon on first
install via `dh_installinit --no-start` shim.
- `prerm`: stops `docker.service`/`docker.socket`; on `remove`, copies
`nuke-graph-directory.sh` into `/var/lib/docker/` so it survives package
removal and can purge the graph directory later.
- `postrm`: on `purge`, runs `nuke-graph-directory.sh` against
`/var/lib/docker`; purges systemd units, debconf db and AppArmor bits.
- The scripts are consistent: `prerm`/`postrm` clean up what
`preinst`/`postinst` set up. One lintian informational note,
`postinst-uses-db-input` (postinst:100), is intentional and documented
in-line (the maintainer explains why a `.config` script would cause
double prompting).
- init scripts
- None (SysV initscripts section empty). The daemon is managed exclusively
via systemd.
- systemd units
- `docker.service` and `docker.socket` are shipped under
`/usr/lib/systemd/system/`. `docker.socket` creates
`/var/run/docker.sock` (group `docker`, mode 0660) and activates
`docker.service`. The daemon runs as root by design (namespaces, cgroups,
seccomp, storage drivers). No `DBus` activation, no socket activation of
external interfaces.
- dbus services
- None.
- setuid binaries
- None. `packaging.txt` setuid/setgid/fs-capabilities sections are empty.
(Consistent with the MIR template's "no suid/sgid binaries".)
- binaries in PATH
- `/usr/bin/docker` (CLI, ~31 MB), `/usr/bin/dockerd` (daemon, ~84 MB),
`/usr/bin/docker-init` (tini, ~806 KB, statically linked by design),
`/usr/bin/docker-proxy` (libnetwork userland proxy, ~2.3 MB). Plus the
`docker-trust` CLI plugin under `/usr/libexec/docker/cli-plugins/`.
- sudo fragments
- None shipped. (`grep` only finds `sudo` mentioned in documentation
strings instructing users how to add themselves to the `docker` group.)
- polkit files
- None.
- udev rules
- None.
- unit tests / autopkgtests
- Build-time: a superficial Go test suite runs and will fail the build on
error. A full integration suite is not run at build time as it requires
non-trivial network setup and isolation-machine capabilities.
- Autopkgtest: present and passing locally on amd64, but flaky across
supported architectures due to Docker Hub pull rate-limit constraints
(a Launchpad/infrastructure limitation). The Server team relies on local
autopkgtest runs when flakiness spikes and intends to move away from
depending on Docker Hub in the long run.
- cron jobs
- None.
- Build logs
- No compiler errors. Compiler/lintian warnings are limited to:
* `dh_golang` LTO-stripping warnings for cgo (expected; cgo does not
support LTO and the flags are stripped automatically).
* `dh_installdocs` cannot auto-detect the main package for `docker-doc`
(cosmetic).
* `rehash: warning: skipping ca-certificates.crt` (tooling).
* `groff-message` warnings/errors when rendering the docker manpages
(tbl preprocessor / `troff` segfault) - a manpage-rendering tooling
issue, not a build defect; lintian also reports `E: Lintian run
failed (runtime error)` as a consequence.
* `dpkg-gencontrol: ... ${misc:Static-Built-Using} unused` (cosmetic).
- No incautious use of malloc/sprintf or other compiler red flags.
- Processes spawned
- The daemon and CLI extensively shell out to system utilities
(iptables/nft, fuse-overlayfs, ip, user/group lookups, containerd,
container runtimes, etc.). Semgrep flags ~20 `dangerous-exec-command`
sites and gosec flags 3 G204 (CWE-78) sites in `cli/`:
`cli/cli/connhelper/commandconn/commandconn.go:49`,
`cli/cli/command/image/build/internal/git/gitutils.go:206`,
`cli/e2e/testutils/plugins.go:88`. All inspected call sites pass
argument lists (not shell strings) derived from configuration or
trusted inputs; none were found to splice untrusted user data into a
shell. tini uses `execvp(argv[0], argv)` (expected). The C
`contrib/syscall-test/*` helpers use `execvp` (test-only, not shipped
in the binary package).
- Memory management
- Go is garbage-collected; the small amount of C is in tini and
`contrib/syscall-test`. tini uses a single `calloc` for child args
(`tini/src/tini.c:374`) with a checked return code; no manual malloc/free
patterns to audit. cppcheck reports only uninitialised-variable errors
in vendored Darwin cgo code (`process_collector_mem_cgo_darwin.c`),
which is not built on Linux.
- File IO
- Paths are derived from configuration (`/var/lib/docker`, context dirs,
TLS material, manifest stores, etc.). gosec G304 (CWE-22, 20 occurrences
in `cli/`) flags `os.Open`/`os.ReadFile` with variables; these are the
expected behaviour for a tool whose purpose is to read user-supplied
files (Dockerfiles, contexts, TLS certs, manifests). Directory
permissions are flagged by gosec G301 (4 sites) and file-write perms by
G306 (3 sites) - all in `cli/cli/{manifest,context}` stores and
`cli/cli/command/image/build.go`; these write to the user's docker config
directory. Not a cross-user privilege issue.
- Logging
- tini uses `PRINT_FATAL`/`PRINT_DEBUG` macros that format `strerror(errno)`
into fixed buffers via `snprintf`-style calls; no tainted format strings.
The engine uses structured logging (logrus) and a journald driver
(libsystemd). Semgrep flags some `fprintf` to `http.ResponseWriter` in
networkdb/diagnostic test helpers (`no-fprintf-to-responsewriter`) -
these are internal diagnostic HTTP endpoints, not user-facing web
content.
- Environment variable usage
- tini reads `TINI_SUBREAPER`, `TINI_KILL_PROCESS_GROUP`, `TINI_VERBOSITY`
(string comparisons, no execution). The engine reads numerous `DOCKER_*`
configuration variables; the Python `ssd.py` helper iterates over
`$PATH`. No environment variable was found to be passed unsanitised to a
shell or used as a privileged function argument.
- Use of privileged functions
- The daemon intentionally runs as root and uses the full set of privileged
primitives (namespace/cgroup/seccomp/AppArmor setup, mount, network
configuration). This is by design for a container runtime. tini uses
`setpgid`/`tcsetpgrp`/`sigprocmask` and registers as a child subreaper.
`contrib/syscall-test/*` calls `setuid`/`setgid`/`socket` (test-only,
not shipped). No setuid/sgid binaries or file capabilities are installed
on the package.
- Use of cryptography / random number sources etc
- Vendored crypto: `golang.org/x/crypto`, `go-jose`, cfssl,
`cloudflare/circl`.
The TLS server in `engine/daemon/hosts.go:75` and the Splunk logger in
`engine/daemon/logger/splunk/splunk.go:172` are missing an explicit
`tls.MinVersion` (semgrep `missing-ssl-minversion`); Go 1.22 defaults to
TLS 1.2 as the minimum, so this is not currently exploitable but should
be tightened to `tls.VersionTLS13` upstream.
- `math/rand` is used in 6 non-cryptographic contexts
(names-generator, networkdb cluster ID, resolver, graphtest, random
test-image generator) - none of these are security-sensitive.
- Docker Content Trust (Notary) provides signed-tag repositories via the
`docker-trust` plugin. No certificate-validation issues were flagged.
- Use of temp files
- No predictable `/tmp` filenames found in shipped code. The only `tmp`
hit is `contrib/syscall-test/acct.c:10` (`acct("/tmp/t")`), which is
test-only and not shipped.
- Use of networking
- The daemon listens on `/var/run/docker.sock` (Unix socket, group
`docker`, mode 0660) by default; the optional TCP/REST API is not
enabled by default and must be explicitly configured by the admin
(https://docs.docker.com/engine/daemon/remote-access/). All container
input is treated as untrusted. No external endpoints are exposed by
default. The userland `docker-proxy` handles port forwarding from the
host into containers (and is the binary with the hardening gap noted
below). No web-application debug-output concerns.
- Use of WebKit
- None.
- Use of PolicyKit
- None.
- Any significant cppcheck results
- 8 findings, all in vendored Darwin cgo code
(`process_collector_mem_cgo_darwin.c`: uninitialised `info`/`b_info`)
plus one encoding-error on `engine/vendor/.../goid_go1.3.c`. None of
this code is compiled on Linux; no significant Linux-side findings.
- Any significant Coverity results
- Not run (no `coverity.txt` was produced for this audit, as we are having
infrastructure issues on its server).
- Any significant shellcheck results
- 638 warnings, the overwhelming majority in vendored codegen scripts
(`debian/extra/vendor/golang.org/x/sys/{plan9,unix}/mk*.sh`,
`engine/vendor/.../mk*.sh`) - these generate Go source from kernel
headers and are not shipped. The only shipped hits are minor style
issues in `debian/get-orig-source.sh` (unquoted variables, `if mycmd; $?`
pattern) and `debian/docker.io.postinst` (unquoted variables in the ZFS
migration block - worth a follow-up but not a security issue as inputs
are controlled).
- Any significant bandit results
- 37 LOW findings, all in `tini/test/*.py` (subprocess usage, `assert`,
one `random` import). Test-only; no shipped Python code is affected.
- Any significant govulncheck results
- govulncheck did not run (no top-level `go.mod`); see `osv-scanner`
below for the vendored-dependency vulnerability scan.
- Any significant Semgrep results
- 97 findings. After filtering test fixtures and CI:
* 5 private-key detections - all test fixtures
(`engine/client/testdata/key.pem`,
`engine/integration/testdata/https/*`,
`engine/integration-cli/fixtures/https/*-rogue-key.pem`). Not shipped.
* 6 `math/rand` usages - non-crypto, see cryptography section.
* 2 `missing-ssl-minversion` - see cryptography section.
* 1 `decompression-bomb` (`engine/daemon/pkg/plugin/backend_linux.go:813`)
- plugin pull path; worth a follow-up to add an `io.CopyN` size
limit, but plugin installation is an admin-controlled operation.
* ~20 `dangerous-exec-command` - see "Processes spawned".
* 7 `missing-unlock-before-return` / `missing-runlock-on-rwmutex`
(`engine/daemon/events/events.go:59`,
`engine/daemon/internal/stream/bytespipe/bytespipe.go:183`,
`engine/daemon/libnetwork/drivers/overlay/ov_network.go:192,196,628`,
`engine/internal/testutil/helpers.go:136`,
`engine/daemon/logger/loggerutils/logfile.go:396`). These look like
false positives given Go's `defer` semantics but are worth an
upstream follow-up.
* 3 `invalid-usage-of-modified-variable`
(`image_builder.go:161`, `osallocator_windows.go:73`,
`volume/service/store.go:774`) - potential nil-deref on error paths,
worth upstream follow-up.
* GitHub Actions `${{...}}` injection findings
(`.github/workflows/.vm.yml`,
`.windows.yml`, `.test.yml`) are CI-only and not shipped.
* `docker-compose` privileged/no-new-privileges/read-only findings are
in compose test fixtures, not shipped defaults.
Vendored dependency vulnerabilities (osv-scanner)
- 28 known vulnerabilities across 15 packages, 27 of which are fixable:
* CVE-2026-1229 (CVSS 9.8) in `engine/vendor/github.com/...` (no fixed
version listed).
* google.golang.org/grpc 1.76.0 -> 1.79.3 (GO-2026-4762, CVSS 9.1).
* github.com/containerd/containerd/v2 2.2.0 -> 2.2.5 (6 advisories,
up to CVSS 8.7).
* github.com/moby/buildkit 0.26.2 -> 0.28.1 (8.4, 8.2).
* golang.org/x/crypto 0.45.0 -> 0.52.0 (5 advisories).
* golang.org/x/net 0.47.0 -> 0.55.0 (2 advisories).
* go.opentelemetry.io/otel* 1.38.0 -> 1.41/1.43.
* github.com/cloudflare/circl 1.6.1 -> 1.6.3 (2.9).
* github.com/in-toto/in-toto-golang 0.9.0 -> 0.11.0 (4.1).
* github.com/aws/aws-sdk-go-v2 (eventstream/cloudwatchlogs) (5.9).
- Most of these are expected to be cleared by packaging upstream 29.5.x
(already requested by the MIR reviewer in comment #7 of the bug).
Hardening (binaries.txt + lintian)
- dockerd: PIE yes, RELRO yes, Fortify yes; no stack protector, no
stack-clash protection, no CFI. This is largely an artefact of the Go
runtime, which manages its own stack and control flow.
- docker (CLI): PIE yes, RELRO yes; no stack protector / stack-clash / CFI
(same Go-runtime caveat).
- docker-init (tini): statically linked by design (lintian
`statically-linked-binary`); has stack-clash protection and CFI.
- docker-trust: statically linked by design.
- docker-proxy (C, libnetwork userland proxy, runs as root): **no PIE, no
RELRO, no immediate binding, no stack protector, no stack-clash, no CFI**.
Lintian flags `hardening-no-pie` and `hardening-no-relro`. This is the
one genuine hardening gap and is worth a follow-up with the Server team
(the binary is built via the upstream cgo/libnetwork Makefile, which
does not pick up the Debian build flags).
General comments
- The package is well-structured for a container runtime: no setuid/sgid,
no sudo/polkit/udev/cron, no external network endpoints by default,
AppArmor profile shipped, sane maintainer scripts with clean cleanup
paths, debconf-driven daemon restarts on upgrade.
- The principal security exposure is inherent to any container runtime
(root daemon managing namespaces/cgroups/seccomp), mitigated by the
upstream hardening features (seccomp profiles, AppArmor, namespaces,
cgroups, user-namespace remapping option).
- The principal audit-specific concerns are: (1) the set of vendored-Go
CVEs flagged by osv-scanner, which the planned 29.5.x upload should
clear; (2) the `docker-proxy` hardening gap; and (3) a handful of
low-severity upstream follow-ups (missing TLS MinVersion, a couple of
semgrep concurrency false-positives, plugin-pull decompression limit).
None of these rise to the level of blocking promotion to main.
Security team ACK for promoting docker.io-app to main
- ACK, conditional on packaging upstream 29.5.x (or newer) to
clear the vendored-Go CVEs flagged by osv-scanner
(in particular the CVSS 9.1/9.8 issues in
grpc/CVE-2026-1229/containerd/buildkit/x-crypto/x-net),
with a re-run of osv-scanner against the promoted version.
- Recommended (non-blocking) follow-ups for the Server team to raise
upstream:
* Add explicit `tls.MinVersion: tls.VersionTLS13` in
`engine/daemon/hosts.go` and `engine/daemon/logger/splunk/splunk.go`.
* Add an `io.CopyN` size cap on plugin pull in
`engine/daemon/pkg/plugin/backend_linux.go:813`.
* Build `docker-proxy` with PIE/RELRO/stack-protector enabled
(`hardening-no-pie`, `hardening-no-relro` lintian flags).
* Tidy the minor shellcheck findings in `debian/get-orig-source.sh`
and the `postinst` ZFS migration block.
** CVE added: https://cve.org/CVERecord?id=CVE-2026-1229
** Changed in: docker.io-app (Ubuntu)
Assignee: Ubuntu Security Team (ubuntu-security) => (unassigned)
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2140335
Title:
[MIR] Promote docker.io-app
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/docker.io-app/+bug/2140335/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs