SEC2-039: The image build trusts a signing key fetched over HTTPS with no fingerprint check #981

Closed
opened 2026-10-17 10:39:10 +00:00 by tiagoagueda · 0 comments
Owner

Found on 2026-10-16 in a read-only security review of postulo at 56fc9d531, checked by a second, independent pass.

Severity: Informational · Category: Dependencies · Reference: CWE-494 (Download of Code Without Integrity Check); OWASP A08:2021 (Software and Data Integrity Failures)

Location:

  • docker/Dockerfile:262-268 (curl -fsSL -o .../apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc, then apt-get install postgresql-client-17)
  • .forgejo/workflows/ci.yml:473-474 (the same in the PostgreSQL job)

Issue

#790 pinned the uv binary by digest and the uv installer by sha256 because "the script is the trust root for the binary it then fetches". The PGDG apt key is the same kind of trust root, fetched the same way, and left unpinned: whatever www.postgresql.org answers becomes the key signed-by= trusts for every package apt installs from that repository into the runtime stage. TLS is the only check.

Impact

Only a compromise of postgresql.org's web host (or a TLS interception on the build host) could substitute the key and then the client packages; the released image is built on the project's own runner. Recorded because the rest of the build's trust roots were pinned deliberately.

Fix

Commit the ASCII-armoured key under docker/ and COPY it, and have the CI job use the same file. This is cheaper and sturdier than a fingerprint check: the runtime stage is a slim image with no gpg, so gpg --show-keys would mean installing gnupg for one check, and a sha256 of the downloaded .asc would break whenever PGDG re-exports the key with a refreshed expiry. If the fingerprint route is chosen instead, compare the full 40-hex fingerprint PGDG publishes for key id ACCC4CF8 on postgresql.org's Debian download page. Done when: tests/test_image_build.py asserts the Dockerfile's key step copies a committed key (or carries the fingerprint check) and fetches nothing.

#790 (closed) - pinned uv and its installer; the apt key is not named in it.

Found on 2026-10-16 in a read-only security review of `postulo` at `56fc9d531`, checked by a second, independent pass. **Severity:** Informational · **Category:** Dependencies · **Reference:** CWE-494 (Download of Code Without Integrity Check); OWASP A08:2021 (Software and Data Integrity Failures) **Location:** - `docker/Dockerfile:262-268` (`curl -fsSL -o .../apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc`, then `apt-get install postgresql-client-17`) - `.forgejo/workflows/ci.yml:473-474` (the same in the PostgreSQL job) ## Issue #790 pinned the uv binary by digest and the uv installer by sha256 because "the script is the trust root for the binary it then fetches". The PGDG apt key is the same kind of trust root, fetched the same way, and left unpinned: whatever `www.postgresql.org` answers becomes the key `signed-by=` trusts for every package apt installs from that repository into the runtime stage. TLS is the only check. ## Impact Only a compromise of postgresql.org's web host (or a TLS interception on the build host) could substitute the key and then the client packages; the released image is built on the project's own runner. Recorded because the rest of the build's trust roots were pinned deliberately. ## Fix Commit the ASCII-armoured key under `docker/` and `COPY` it, and have the CI job use the same file. This is cheaper and sturdier than a fingerprint check: the runtime stage is a slim image with no `gpg`, so `gpg --show-keys` would mean installing `gnupg` for one check, and a sha256 of the downloaded `.asc` would break whenever PGDG re-exports the key with a refreshed expiry. If the fingerprint route is chosen instead, compare the full 40-hex fingerprint PGDG publishes for key id `ACCC4CF8` on postgresql.org's Debian download page. Done when: `tests/test_image_build.py` asserts the Dockerfile's key step copies a committed key (or carries the fingerprint check) and fetches nothing. ## Related #790 (closed) - pinned uv and its installer; the apt key is not named in it.
tiagoagueda added this to the 1.0.0 milestone 2026-10-17 10:39:11 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#981
No description provided.