fix: add Dockerfile.multistage as a CVE-hardened build option#239
Open
treymujak wants to merge 1 commit into
Open
fix: add Dockerfile.multistage as a CVE-hardened build option#239treymujak wants to merge 1 commit into
treymujak wants to merge 1 commit into
Conversation
Adds an alternative multi-stage Dockerfile that keeps the default Dockerfile
untouched. Users who want a smaller, more locked-down image can opt in via:
docker build -f Dockerfile.multistage -t nca-toolkit:secured .
Compared to the default Dockerfile:
- Multi-stage split: compilers and -dev headers stay in the builder stage and
never reach the final image.
- Playwright + Chromium not installed (disables /v1/image/screenshot_webpage
on this image; all other endpoints unchanged). Closes the nss/cups CVE
cluster pulled in by Chromium.
- ffmpeg built without --enable-gnutls; SRT uses the openssl backend. Closes
the gnutls CVE cluster.
- libtheora dropped from the ffmpeg build.
- wheel, setuptools, jaraco.context pinned to patched versions at pip layer.
- apt-get -y upgrade in the runtime stage to pick up the latest trixie patches.
docs/secure-docker.md documents the tradeoff and when to prefer the default
Dockerfile instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
Dockerfile.multistageas an opt-in, CVE-hardened alternative to thedefault
Dockerfile. The existingDockerfileis untouched. Users who wantthe hardened image build with:
Vulnerability reduction
Scanner counts against the same image config, before and after:
Image size: 4.82 GB → 3.08 GB uncompressed.
Critical CVEs closed
All five are upstream-unfixed (no patched package available). The multistage
build closes them by removing the affected package from the runtime image,
not by upgrading.
ninja-buildas a python3 transitive. Multi-stage keeps ninja in the builder; runtime image has only python 3.10 (the base image).--enable-gnutls. SRT switched to the openssl backend. gnutls28 not installed in runtime.Why these were realistically exploitable
These aren't theoretical. Each one has a plausible reach path in this app:
bugs. ffmpeg was linked against gnutls and any endpoint that feeds a URL
through ffmpeg (e.g. transcribe/caption with a remote source) could trigger
a TLS handshake against an attacker-controlled host. RCE-class.
runs on python 3.10, so 3.13 wasn't invoked deliberately — but any command
injection or shell-out chain that resolves
python3could land on it. Deadweight that widened the blast radius.
Playwright rendering a malicious page — exactly what
/v1/image/screenshot_webpagedoes. URL → server-side browser → RCE.No direct archive-extract endpoint, but the package's presence in the
runtime image meant any future code path (or transitive tool invocation)
that handed it user input would inherit the vuln.
All five share a common shape: the package didn't need to be in the runtime
image. Multi-stage builds + dropping Playwright remove them rather than
waiting for upstream fixes.
What else changes
--enable-libtheora(CVE-2026-5673). Re-add the flagif Theora output is needed.
wheel,setuptools,jaraco.contextpinned at install time to clearCVE-2026-24049, CVE-2026-23949 and related.
apt-get -y upgraderuns in the runtime stage to pick up the latestDebian trixie patches at build time.
Tradeoff
POST /v1/image/screenshot_webpagewill fail on this image — Playwright +Chromium aren't installed. All other endpoints behave identically. This is
the price for closing CVE-2026-40393 and the nss/cups Critical+High cluster
that Chromium drags in.
docs/secure-docker.mddocuments this and points users at the defaultDockerfile if they need the screenshot endpoint.
Why additive, not a replacement
The default Dockerfile keeps Playwright and the screenshot endpoint working
out of the box, which matters for existing users. This PR doesn't take that
away — it just gives operators who don't need screenshots a hardened option.
Test plan
docker build -f Dockerfile.multistage -t nca-toolkit:secured .docker runand hitPOST /v1/toolkit/test— auth + queue path works/v1/media/transcode) to confirmcompiled ffmpeg works without gnutls/libtheora
POST /v1/image/screenshot_webpagefails on this image(documented tradeoff)
above