Skip to content

Conversation

@spoonincode
Copy link
Contributor

@spoonincode spoonincode commented Aug 25, 2025

During discussion of #1834 and its usage of Debian's pinned package archive some security concerns were raised that could be resolved by verifying the hash of the package repository. Verifying the hash of the package repository may also give us leeway to mirror the package archive (for the date we require) without any reduction in security as well.

This PR adds a new "apt method" -- you can think of this as a URI scheme handler; pinned:// in this case -- which effectively intercepts all requests from apt and forwards them through to the existing http method. Except, when an InRelease file is encountered, it verifies the hash of that file against the configuration defined in the Dockerfile. If this does not match, the method fails. (I experimented with synthesizing a proper error response from the method, but the method self destructing was more reliable in stopping apt in its tracks no matter what)

Ultimately I'm happy with the simplicity of the approach: there is very little "noise" in the Dockerfile. And the pinned apt method is sub 150 lines where over half of it is pretty much boilerplate (i.e. it's reasonably reviewable as not being nefarious). And while arcane, apt methods are fully supported (including through trixie) so we're not really doing anything hacky here.

Now I know what you're thinking: Perl?! Well, the base docker image from Debian (i.e. docker run --rm -it debian:buster) has very little already installed. Notably python is not installed. I could do something like build a C++ application in a separate build stage and copy it in. That's going to be more gnarly from a Dockerfile standpoint, and it's not clear C++ would be a good fit here anyways -- honestly Perl is not terribly ill-suited for this task.

An example of a bad hash and failure of the docker build is at
https://github.com/AntelopeIO/spring/actions/runs/17197804792/job/48782669614#step:3:1283

EOF

# Install the 'pinned' apt method, and define required hashes for the InRelease files of the package repos
COPY tools/pinned.pl /usr/lib/apt/methods/pinned
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw I was reminded during this work that the caching we implement verifies the hash of the dockerfile to determine whether or not to rebuild the cached image. That means if you were to make a commit that changes pinned.pl, the commit would not invalidate the cache nor cause a rebuild of the base image. Not clear how to improve this at the moment just pointing it out.

Acquire::pinned::InReleaseHashes {
"buster" "d2126c57347cfe5ca81d912ddfecc02e9a741c6e100d8d8295b735f979bc1a9d";
"buster-updates" "2efadfba571a0c888a8e0175c6f782f7a0afe18dee0e3fbcf1939931639749b8";
"updates" "5a9bda70b67ba71088bc7576dd6ee078f75428ea6291ca7b22ac7d79a9ec73e8";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is annoyingly named because of the same issue pointed out here #1834 (comment) with how the package repos are laid out. In Debian 11+ this becomes something like trixie-security which is consistent.

# Install the 'pinned' apt method, and define required hashes for the InRelease files of the package repos
COPY tools/pinned.pl /usr/lib/apt/methods/pinned
RUN cat <<EOF > /etc/apt/apt.conf.d/99pinned.conf
Acquire::pinned::InReleaseHashes {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can verify these hashes via something like,

$ export SOURCE_DATE_EPOCH=1752331000
$ export DATETIMESTR=$(date -d @${SOURCE_DATE_EPOCH} +%Y%m%dT%H%M%SZ)

$ curl -L -o buster-InRelease https://snapshot.debian.org/archive/debian/${DATETIMESTR}/dists/buster/InRelease
$ curl -L -o buster-updates-InRelease https://snapshot.debian.org/archive/debian/${DATETIMESTR}/dists/buster-updates/InRelease
$ curl -L -o buster-security-InRelease https://snapshot.debian.org/archive/debian-security/${DATETIMESTR}/dists/buster/updates/InRelease

$ sha256sum *
d2126c57347cfe5ca81d912ddfecc02e9a741c6e100d8d8295b735f979bc1a9d  buster-InRelease
5a9bda70b67ba71088bc7576dd6ee078f75428ea6291ca7b22ac7d79a9ec73e8  buster-security-InRelease
2efadfba571a0c888a8e0175c6f782f7a0afe18dee0e3fbcf1939931639749b8  buster-updates-InRelease

Remember these are gpg signed files from Debian, so you can also gpg --verify them too.

@spoonincode spoonincode linked an issue Aug 25, 2025 that may be closed by this pull request
Base automatically changed from bustersnapshot_12 to release/1.2 August 25, 2025 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

reproducible builds failing when built from scratch

2 participants