Skip to content

Verified bundle download with a pre-GA fallback on master - #158

Merged
jsokol merged 1 commit into
masterfrom
FIX-master-prega-bundle-fallback
Aug 21, 2026
Merged

Verified bundle download with a pre-GA fallback on master#158
jsokol merged 1 commit into
masterfrom
FIX-master-prega-bundle-fallback

Conversation

@jsokol

@jsokol jsokol commented Aug 21, 2026

Copy link
Copy Markdown
Member

Description

container-validation fails on every release-bump PR, and has since at least update-20260709-001. This is the fix, ported from the testing branch where it already works.

Why it fails

The image build fetched the app bundle with a bare pipe:

curl -sL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-<version>.tgz | tar xz -C /var/www

public/bundles/ is the GA path — written only by propagate_release_bundle at the testing → master cut. But bump_downstream_versions opens the docker update-<version> PR at the testing cut, from base master. So the version it pins has no prod bundle yet, and validation fails until GA.

Confirmed across three releases: update-20260709-001, update-20260811-001, update-20260820-001 — all red, while unrelated PRs from the same weeks passed because they build against an already-published GA bundle.

Second, subtler problem: curl -sL has no --fail, so the S3 error document is streamed into tar and the failure surfaces as a tar exit code rather than an HTTP status. The real cause never appears in the log. That is precisely how this got misdiagnosed as expected-at-RC behaviour rather than a bug.

The fix

common/download_and_verify_bundle.sh (identical in both build contexts, taken verbatim from testing) downloads the prod bundle, resolves its published sha256md5 fallback — from the prod updates feed, and verifies it before extracting.

Fail-closed by default. A missing prod bundle, a missing feed hash, or a hash mismatch aborts the build. A swapped S3 object therefore cannot be baked into a published image, and the bundle and its hash come from independent sources (S3 object vs. served feed).

PREGA_BUNDLE_FALLBACK=true — set only by container-validation, never for a released image — lets a pre-GA build fall back to bundles-test without verification, warning loudly. That path exists because a release genuinely has no published hash before GA.

On PR #146

#146 attempted this in July and was correctly closed. Its diff adds the ARG, the COPY and the generator changes but never adds the script, so it would have failed at COPY common/download_and_verify_bundle.sh. The complete implementation later landed on testing only — which is why the bug persisted on master, the branch the bump PRs are actually cut from.

Ported surgically, not copied

testing's versions of these files also carry changes unrelated to this bug, none of which are included here:

Unrelated change on testing Included?
PHP default 8.48.5
New source_mode generator parameter (context|download)
Added php85 validation job, php83/php84 job renames
Trigger branches: [master][master, testing]

Verified after patching: php_version is still 8.4, there are zero source_mode references, and the container-validation.yml diff is exactly the four build_args lines.

Generators, not Dockerfiles

The Dockerfiles are generated (# Dockerfile generated by script) and make update_version re-runs generate_dockerfile.sh on every version bump. So I patched the generators and re-ran them to produce the Dockerfiles. Editing the Dockerfiles directly would have been silently overwritten by the next bump.

One more defect found while in there

The full-stack image's SQL fetch had the same missing --fail:

curl -sL "https://github.com/simplerisk/database/raw/master/simplerisk-$DB_LANG-<version>.sql" > /simplerisk.sql

Without --fail, a 404 body is written to /simplerisk.sql and the image ships an HTML error page as its database schema. Now -fsSL.

Validation

Ran the script in the real alpine/curl:8.12.1 downloader image across all three paths:

Scenario Expected Result
PREGA_BUNDLE_FALLBACK=false, pre-GA version fail closed ✅ exit 1, refusing to build a release from unverified bytes
PREGA_BUNDLE_FALLBACK=true, pre-GA version fall back to bundles-test ✅ exit 0, warned, Extracted bundle to /var/www.
PREGA_BUNDLE_FALLBACK=false, GA version present verify hash, extract ✅ (20260519-001, the current GA)

So the security property holds in both directions: a released image can only be built from the hash-verified prod bundle, and unverified bytes require the explicit CI-only flag.

Also confirmed the surrounding facts rather than assuming them: GA is currently 20260519-001 (agreed across code-development master, the prod feed, public/bundles/, and this repo's pinned version), so 20260811-001 and 20260820-001 are RC-only and their absence from public/bundles/ is correct.

container-validation on this PR is the live test — it builds all four images with the fallback enabled against a master still pinned to the current GA release, so it should exercise the verified prod path, not the fallback.

Follow-up (not in this PR)

After this merges, bump_downstream_versions should be re-dispatched so update-20260820-001 is regenerated from the fixed master. The existing branch was cut before this change and will keep failing until it is.

The image build fetched the app bundle with a bare
`curl -sL .../public/bundles/simplerisk-<version>.tgz | tar xz`. Two problems.

1. public/bundles/ is the GA path, written only by propagate_release_bundle at
   the testing -> master cut. But bump_downstream_versions opens the docker
   update-<version> PR at the TESTING cut, from base master -- so the version it
   pins has no prod bundle yet and container-validation fails on every release.
   Observed on update-20260709-001, update-20260811-001 and update-20260820-001.

2. Without --fail, curl streams the S3 error document into tar, so the failure
   surfaces as a tar exit code rather than an HTTP status. The real cause is
   invisible in the log, which is how this got misread as expected behaviour.

The fix already exists on the testing branch and is ported here. PR #146
attempted this in July and was correctly closed: it added the ARG, the COPY and
the generator changes but never added the script itself, so it would have failed
at COPY.

common/download_and_verify_bundle.sh (identical in both build contexts, taken
verbatim from testing) downloads the prod bundle, resolves its published sha256
(md5 fallback) from the prod updates feed, and verifies before extracting.
Fail-closed by default: a missing bundle, a missing feed hash or a mismatch
aborts the build, so a swapped S3 object cannot be baked into a published image.
PREGA_BUNDLE_FALLBACK=true -- set ONLY by container-validation -- allows a pre-GA
build to fall back to bundles-test without verification, warning loudly, because
a release has no published hash before GA.

Ported surgically rather than copied. The testing branch's versions of these
files also carry a PHP default bump (8.4 -> 8.5), a new source_mode generator
parameter, an added php85 validation job and a trigger change -- all unrelated,
none included here. Verified after patching: php_version is still 8.4, there are
no source_mode references, and the container-validation diff is exactly four
build_args lines.

Patched the GENERATORS and re-ran them rather than editing the Dockerfiles: the
Dockerfiles are generated, and make update_version regenerates them on every
version bump, so a hand-edit would be silently overwritten.

Also fixed the SQL fetch in the full-stack image, which had the same missing
--fail: `curl -sL ... > /simplerisk.sql` writes a 404 body to disk, so the image
would ship an HTML error page as its database schema.

Verified by running the script in the real alpine/curl:8.12.1 downloader image
across all three paths: fail-closed on a pre-GA version without the flag (exit 1),
pre-GA fallback to bundles-test with the flag (exit 0, warned, extracted), and
the verified-prod path against a GA version that exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jsokol
jsokol merged commit 63d4f0a into master Aug 21, 2026
5 checks passed
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.

1 participant