Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
.venv
build
dist
.artifact-bundle
.artifact-inputs
.artifact-resolver
docs
security-results
tests
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
- name: Run repository checks
run: pre-commit run --all-files --show-diff-on-failure

- name: Validate artifact locks
run: python -m unittest tests.test_artifacts -v

- name: Audit GitHub Actions security
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
with:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ security-results/
# Local container build output
build/
dist/
.artifact-inputs/
.artifact-resolver/
.artifact-bundle/
.artifact-transfer/
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ but container releases use the upstream-derived format documented in
cached package layer that predates the errata. The previously pinned
`nginx-core` build had been superseded and was no longer resolvable, so the
legacy pipeline was succeeding only from that cache.
- Approved the official NGINX stable channel, selected
`nginx-2:1.30.4-1.el9.ngx` for first-release implementation qualification,
and retired the completed source-selection item from the forward roadmap.
- Added reviewed AMD64 and ARM64 artifact locks for the selected NGINX RPM,
its complete UBI dependency closure, source RPMs, signing identities, and
base-image digests, with fail-closed validation and lock-update tooling.
The locks record the refreshed UBI 9.8 bases and therefore
`openssl-1:3.5.8-1.el9_8` from RHSA-2026:67165 and the
`systemd-0:252-67.el9_8.6` rebuild.
- Added atomic official and protected alternate-source acquisition for locked
artifact bundles, including exact inventory, digest, RPM signature, signer,
NEVRA, architecture, and lock-manifest verification without storing source
credentials or private trust material in the repository or image build.
- Expanded logging guidance with a field-by-field explanation of `$request`,
a sensitive ClickHouse example, and safer variable choices.
- Defined a source-independent pipeline contract that downloads and verifies
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ root filesystem, explicit `tmpfs` mounts, dropped capabilities, and
- [NGINX RPM provenance](docs/RPM-PROVENANCE.md) records the exact Red Hat UBI
package source, build path, trust checks, and local verification commands.
- [NGINX package-source decision](docs/PACKAGE-SOURCE.md) compares the current
Red Hat RPM with the proposed official NGINX stable RPM and defines migration
Red Hat RPM with the selected official NGINX stable RPM and defines migration
acceptance criteria.
- [External artifact acquisition](docs/ARTIFACT-ACQUISITION.md) defines the
pre-build download and verification process and hermetic image assembly
Expand Down Expand Up @@ -152,6 +152,26 @@ python -m pip install --require-hashes --only-binary=:all: \
pre-commit run --all-files --show-diff-on-failure
```

Validate the reviewed artifact locks and their negative cases with:

```console
python -m unittest tests.test_artifacts -v
```

Acquire and verify the exact AMD64 RPM bundle from the official sources:

```console
python scripts/artifacts.py acquire \
--lock artifacts/locks/amd64.json \
--output .artifact-bundle/amd64
bash scripts/verify-rpm-bundle.sh \
artifacts/locks/amd64.json .artifact-bundle/amd64
```

The acquisition guide documents ARM64, optional source RPMs, and protected
alternate-source configuration. Python 3, RPM, and GnuPG are required for
preparation.

Build and exercise the current AMD64 development image with rootless Podman on
native Linux or WSL2:

Expand Down
110 changes: 110 additions & 0 deletions artifacts/artifact-lock.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/datopsis/nginx-ubi/artifacts/artifact-lock.schema.json",
"title": "nginx-ubi artifact lock",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"bundle_version",
"architecture",
"rpm_architecture",
"generated_at",
"nginx_version",
"nginx_rpm_version",
"base_images",
"signing_keys",
"packages",
"source_packages"
],
"properties": {
"schema_version": { "const": 1 },
"bundle_version": { "const": 1 },
"architecture": { "enum": ["amd64", "arm64"] },
"rpm_architecture": { "enum": ["x86_64", "aarch64"] },
"generated_at": { "type": "string", "format": "date-time" },
"nginx_version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
"nginx_rpm_version": { "type": "string", "minLength": 1 },
"base_images": {
"type": "object",
"additionalProperties": false,
"required": ["builder", "runtime"],
"properties": {
"builder": { "$ref": "#/$defs/baseImage" },
"runtime": { "$ref": "#/$defs/baseImage" }
}
},
"signing_keys": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/key" }
},
"packages": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/package" }
},
"source_packages": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/sourcePackage" }
}
},
"$defs": {
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
"baseImage": {
"type": "object",
"additionalProperties": false,
"required": ["reference", "digest", "platform"],
"properties": {
"reference": { "type": "string", "minLength": 1 },
"digest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
"platform": { "type": "string", "pattern": "^linux/(amd64|arm64)$" }
}
},
"key": {
"type": "object",
"additionalProperties": false,
"required": ["id", "filename", "url", "sha256", "fingerprint"],
"properties": {
"id": { "type": "string", "pattern": "^[a-z0-9-]+$" },
"filename": { "type": "string", "pattern": "^[A-Za-z0-9._-]+$" },
"url": { "type": "string", "pattern": "^https://" },
"sha256": { "$ref": "#/$defs/sha256" },
"fingerprint": { "type": "string", "pattern": "^[A-F0-9]{40}$" }
}
},
"package": {
"type": "object",
"additionalProperties": false,
"required": ["name", "epoch", "version", "release", "architecture", "nevra", "filename", "url", "repository", "size", "sha256", "signing_key_fingerprint", "source_rpm"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"epoch": { "type": "integer", "minimum": 0 },
"version": { "type": "string", "minLength": 1 },
"release": { "type": "string", "minLength": 1 },
"architecture": { "type": "string", "minLength": 1 },
"nevra": { "type": "string", "minLength": 1 },
"filename": { "type": "string", "pattern": "^[A-Za-z0-9+_.-]+\\.rpm$" },
"url": { "type": "string", "pattern": "^https://" },
"repository": { "type": "string", "minLength": 1 },
"size": { "type": "integer", "minimum": 1 },
"sha256": { "$ref": "#/$defs/sha256" },
"signing_key_fingerprint": { "type": "string", "pattern": "^[A-F0-9]{40}$" },
"source_rpm": { "type": "string", "pattern": "^[A-Za-z0-9+_.-]+\\.src\\.rpm$" }
}
},
"sourcePackage": {
"type": "object",
"additionalProperties": false,
"required": ["filename", "url", "repository", "size", "sha256"],
"properties": {
"filename": { "type": "string", "pattern": "^[A-Za-z0-9+_.-]+\\.src\\.rpm$" },
"url": { "type": "string", "pattern": "^https://" },
"repository": { "type": "string", "minLength": 1 },
"size": { "type": "integer", "minimum": 1 },
"sha256": { "$ref": "#/$defs/sha256" }
}
}
}
}
47 changes: 47 additions & 0 deletions artifacts/lock-inputs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"schema_version": 1,
"bundle_version": 1,
"nginx_version": "1.30.4",
"nginx_rpm_version": "1.30.4-1.el9.ngx",
"ubi_release": "9.8",
"base_images": {
"builder": "registry.access.redhat.com/ubi9/ubi-minimal:9.8@sha256:e5161a7d7d99cf22e4f34b72e111211a399d956d9b0e8714da18e9c4c8151041",
"runtime": "registry.access.redhat.com/ubi9/ubi-micro:9.8@sha256:7a0454cbd9bd847e8f6a63b6f0254a6efbeb6e0ed71a5d824a4f6cccbe626650"
},
"architectures": {
"amd64": {
"rpm_architecture": "x86_64",
"nginx_rpm": {
"sha256": "d9e44a6f68e230b0f2c2fd5a265b2e82ce47a19a04cb68bba7cddce690706568",
"url": "https://nginx.org/packages/rhel/9/x86_64/RPMS/nginx-1.30.4-1.el9.ngx.x86_64.rpm"
}
},
"arm64": {
"rpm_architecture": "aarch64",
"nginx_rpm": {
"sha256": "7b26ea1724ab5276413fa702f95003e5517aed1b3e88c17012f79285df051ec3",
"url": "https://nginx.org/packages/rhel/9/aarch64/RPMS/nginx-1.30.4-1.el9.ngx.aarch64.rpm"
}
}
},
"signing_keys": [
{
"fingerprint": "8540A6F18833A80E9C1653A42FD21310B49F6B46",
"id": "nginx-signing",
"sha256": "55385da31d198fa6a5012d40ae98ecb272a6c4e8fffffba94719ffd3e87de37a",
"url": "https://nginx.org/keys/nginx_signing.key"
},
{
"fingerprint": "567E347AD0044ADE55BA8A5F199E2F91FD431D51",
"id": "redhat-release",
"sha256": "d4b2543626bee31d9438d4a31731aae712c072893c7aed8854f16c42fafc742b",
"url": "https://security.access.redhat.com/data/fd431d51.txt"
},
{
"fingerprint": "7E4624258C406535D56D6F135054E4A45A6340B3",
"id": "redhat-release-2",
"sha256": "d37ed8bd8bddb29983a38c6fde4e59d329e6a7c2ecbc702e929f0d05ae5eba59",
"url": "https://security.access.redhat.com/data/5a6340b3.txt"
}
]
}
Loading
Loading