Skip to content

feat: lock and verify external NGINX artifacts - #11

Merged
joey-huckabee merged 3 commits into
mainfrom
feature/artifact-locks-and-acquisition
Sep 16, 2026
Merged

joey-huckabee merged 3 commits into
mainfrom
feature/artifact-locks-and-acquisition

Conversation

@joey-huckabee

Copy link
Copy Markdown
Contributor

Closes the first three Package 2 roadmap items: approve the package channel,
add reviewed architecture locks, and add verified acquisition.

What this adds

  • Channel decision. Official NGINX stable, with nginx-2:1.30.4-1.el9.ngx
    selected as the first-release implementation and qualification candidate.
    Reviewed against authoritative NGINX sources on 2026-09-12, including the
    security-advisory index. The selection does not freeze release inputs.
  • Architecture locks. artifacts/locks/amd64.json and arm64.json pin the
    selected RPM, its complete 79-package installation closure, 59 source RPMs,
    sizes, SHA-256 values, the actual signer fingerprint, and the builder and
    runtime manifest-list digests. Schema in
    artifacts/artifact-lock.schema.json, reviewed inputs in
    artifacts/lock-inputs.json.
  • Lock-update path. fetch-lock-inputs.py (HTTPS-only, approved hosts,
    reviewed SHA-256), resolve-lock.sh (independent inventory, hash,
    fingerprint, signature, NEVRA, architecture, closure, and source-RPM checks),
    and render-lock.py (maps the observed signing key ID to exactly one
    approved fingerprint, fails closed on inconsistent output).
  • Acquisition. artifacts.py acquire publishes atomically and refuses to
    replace an existing output directory. verify-rpm-bundle.sh performs
    RPM-level verification before the bundle is exposed to any build.
  • Alternate sources. --source-map accepts an external JSON map with
    --token-env and --ca-bundle. URLs with credentials, query strings, or
    fragments are rejected; redirects must stay on the original HTTPS host;
    source URLs and tokens are never printed on error. Maps, credentials, and
    private CA material stay outside this repository and the build context.

Notes for review

  • The signer fingerprint for the selected RPMs
    (8540 A6F1 ... B49F 6B46) differs from the older fingerprint in NGINX's
    published install instructions. The locks accept the actual signer
    specifically rather than trusting every certificate in the downloaded
    bundle. See docs/PACKAGE-SOURCE.md.
  • Most of the line count is the two lock JSON files.
  • The Containerfile still resolves RPMs in the builder stage. Migrating it
    to consume the verified bundle is the next increment and is deliberately
    kept out of this PR.

Verification

python -m unittest tests.test_artifacts -v (11 tests) now runs in the lint
job. Lock validation commands documented in the README and acquisition guide
were executed against both architecture locks.

🤖 Generated with Claude Code

Approve the official NGINX stable channel and select
`nginx-2:1.30.4-1.el9.ngx` as the first-release implementation and
qualification candidate.

Add reviewed AMD64 and ARM64 artifact locks covering the selected RPM, its
complete 79-package dependency closure, 59 source RPMs, sizes, SHA-256
values, the actual signer fingerprint, and the digest-pinned base images.
Add the explicit lock-update path and atomic official and alternate-source
acquisition with inventory, digest, signature, signer, NEVRA, architecture,
and lock-manifest verification.

Source maps, credentials, and private CA material stay outside the
repository and the image build. The Containerfile still resolves RPMs in the
builder stage; migrating it to the verified bundle is the next increment.
…s-and-acquisition

# Conflicts:
#	CHANGELOG.md
Regenerate both architecture locks against the refreshed UBI 9.8 Minimal and
Micro base digests so the locked closure carries
`openssl-1:3.5.8-1.el9_8` from RHSA-2026:67165, which addresses
CVE-2026-14456.

Reviewed diff, identical on both architectures:

  openssl             1:3.5.5-6.el9_8   -> 1:3.5.8-1.el9_8
  openssl-libs        1:3.5.5-6.el9_8   -> 1:3.5.8-1.el9_8
  systemd             0:252-67.el9_8.4  -> 0:252-67.el9_8.6
  systemd-libs        0:252-67.el9_8.4  -> 0:252-67.el9_8.6
  systemd-pam         0:252-67.el9_8.4  -> 0:252-67.el9_8.6
  systemd-rpm-macros  0:252-67.el9_8.4  -> 0:252-67.el9_8.6

The systemd rebuild arrived with the refreshed base and was not the reason
for the refresh. No package was added or removed, the 79-package closure and
59 source packages keep the same shape, both architectures resolve identical
versions, the NGINX package is unchanged at 1.30.4-1.el9.ngx, and no signing
identity changed.

Resolution ran in a clean networked UBI 9 container per the lock-refresh
procedure. Resolver output was not committed.
@joey-huckabee

Copy link
Copy Markdown
Contributor Author

Rebuilt on current main (which now carries the #12 base/module fix) and refreshed both architecture locks.

Why the locks changed. They pinned openssl-libs 1:3.5.5-6.el9_8, the package CVE-2026-14456 affects. Landing them unchanged would have handed the hermetic build in the next increment a known-vulnerable closure — the stricter path reintroducing what the legacy one just fixed.

Reviewed lock diff (step 6 of docs/ARTIFACT-LIFECYCLE.md), identical on both architectures:

openssl             1:3.5.5-6.el9_8   -> 1:3.5.8-1.el9_8
openssl-libs        1:3.5.5-6.el9_8   -> 1:3.5.8-1.el9_8
systemd             0:252-67.el9_8.4  -> 0:252-67.el9_8.6
systemd-libs        0:252-67.el9_8.4  -> 0:252-67.el9_8.6
systemd-pam         0:252-67.el9_8.4  -> 0:252-67.el9_8.6
systemd-rpm-macros  0:252-67.el9_8.4  -> 0:252-67.el9_8.6
Invariant Result
Packages added / removed 0 / 0 (79 -> 79)
Source packages 59 -> 59, 2 swapped
Signing identities unchanged, both approved fingerprints
Repositories unchanged (nginx-stable, ubi-9-baseos-rpms)
NGINX package unchanged, nginx-2:1.30.4-1.el9.ngx
AMD64 vs ARM64 identical package names, versions, and source set

openssl is the intended RHSA-2026:67165 fix. systemd is incidental — it came with the refreshed base rather than being targeted, so it deserves a reviewer's attention even though it is a same-stream rebuild.

Resolution ran in a clean networked UBI 9 container; resolver output was not committed. artifacts/components.json is regenerated from these same locks in the component-accountability increment, so the inputs, both locks, and the inventory still land as one reviewed set before any release.

@joey-huckabee
joey-huckabee merged commit 98e3a55 into main Sep 16, 2026
7 checks passed
@joey-huckabee
joey-huckabee deleted the feature/artifact-locks-and-acquisition branch September 16, 2026 03:02
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