Files
govoplan/docs/security/RUNTIME_IMAGE_REMEDIATION_2026-09-08.md
zemion 58d320d9b3
Dependency Audit / dependency-audit (push) Successful in 1m51s
Deployment Installer / deployment-installer (push) Successful in 8s
Security Audit / security-audit (push) Successful in 12m32s
Harden source release preparation and record verified security follow-up
2026-09-08 08:04:12 +02:00

140 lines
9.0 KiB
Markdown

# Runtime image remediation follow-up — 8 September 2026
Canonical tracking: [Meta #52](https://git.add-ideas.de/GovOPlaN/govoplan/issues/52)
and [website #9](https://git.add-ideas.de/add-ideas/addideas-govoplan-website/issues/9).
This addendum supplements the [original audit](RUNTIME_IMAGE_AUDIT_2026-09-08.md);
it does not replace that historical baseline or lift either publication or
deployment gate. The immutable 0.1.45 release and `catalog-v0.1.45` are unchanged.
## Source change and candidate decisions
New installer specifications now use
`haproxy:3.2.23-alpine@sha256:6343ce34a132a5dceaa24767d739df2bd519f8f7c1079ae39e4821334e8eb42e`.
This is a patch update from 3.2.21 within the supported
[3.2 LTS branch](https://www.haproxy.org/), keeping Alpine 3.24.1.
The [publisher's exact build source](https://github.com/docker-library/haproxy/blob/7a5c202cde713867a737033dca56e7a211a8b8df/3.2/alpine/Dockerfile)
and scanned image configuration retain the non-root `haproxy` user,
`/usr/local/etc/haproxy/haproxy.cfg`, entrypoint and graceful-stop signal.
The [upstream changelog](https://www.haproxy.org/download/3.2/src/CHANGELOG)
includes HTTP parsing, TLS and memory-safety fixes in 3.2.22/3.2.23.
Existing specifications retain their explicit image, including an older pin;
this source change does not update a running installation.
| Candidate | OS | C / H / M / L / Unknown, per architecture | Disposition |
| --- | --- | --- | --- |
| HAProxy `3.2.23-alpine` | Alpine 3.24.1 | 0 / 0 / 0 / 0 / 0 | Installer source default updated; binary/runtime checks pending |
| nginx-unprivileged `1.30.4-alpine` | Alpine 3.24.1 | 0 / 0 / 0 / 0 / 0 | Candidate only; compatibility and website OS upgrade review pending |
| Caddy `2.11.4-alpine` | Alpine 3.23.5 | 1 / 38 / 41 / 12 / 23 | Not selected; all 39 C/H records have recorded fixes |
| Node `24-alpine` (24.20.0) | Alpine 3.24.1 | 0 / 6 / 11 / 12 / 0 | Not selected; major change and six fixable HIGH records |
Each row was scanned separately for **linux/amd64 and linux/arm64**, with the
same counts on both. Counts are package-vulnerability records, not distinct
CVEs or proven exploits. The [machine-readable evidence](runtime-image-candidates-2026-09-08.json)
contains exact index, platform-manifest, config and report digests, inventory
counts, scanner bounds and decisions. It is audit data, not an accepted release
manifest or an installer input.
nginx's candidate reference is
`docker.io/nginxinc/nginx-unprivileged:1.30.4-alpine@sha256:442753882674b49ae2c1de83ed67896131c0777f56df5005e356e62bc3f7e7ce`.
It inventories 70 Alpine packages including nginx/NJS, uses UID 101 and exposes
8080. The [upstream stable release](https://nginx.org/en/download.html) and
[security advisories](https://nginx.org/en/security_advisories.html) include the
1.30.4 fixes. The publisher retains its
[unprivileged port and temporary-path contract](https://github.com/nginx/docker-nginx-unprivileged).
For the website, this changes nginx 1.27.5 to 1.30.4, NJS 0.8.10 to 1.0.1 and
Alpine 3.21.3 to 3.24.1. These changes are explicit review items; the website
Dockerfile has not been changed. The GovOPlaN Web image still requires an
explicit verified `NGINX_IMAGE` build argument.
Caddy's candidate reference is
`docker.io/library/caddy:2.11.4-alpine@sha256:5f5c8640aae01df9654968d946d8f1a56c497f1dd5c5cda4cf95ab7c14d58648`.
Although this is the current
[official image line](https://raw.githubusercontent.com/docker-library/official-images/master/library/caddy),
its inventory still includes Go 1.26.3, `x/crypto` 0.52.0, `x/net` 0.55.0,
`x/text` 0.37.0 and gRPC 1.81.0. Recorded fixes include Go 1.26.6,
`x/crypto` 0.55.0, `x/net` 0.56.0, `x/text` 0.39.0 and gRPC 1.83.1; Alpine
findings also remain in c-ares, curl/libcurl and OpenSSL. The CRITICAL
`CVE-2026-56854` concerns `x/crypto/ssh` source-address enforcement. A module
record alone does not establish that this binary exposes that SSH path; exact
binary symbol/reachability analysis is still required for a disposition.
The [official Node image catalogue](https://raw.githubusercontent.com/docker-library/official-images/master/library/node)
still maps Node 22 Alpine to 22.23.2 and the previously scanned digest. Node 24's
candidate is
`docker.io/library/node:24-alpine@sha256:e67514e5d0f6c46656005e1b693b2ec9d52e80b641307de684d4a015ba7a4eaf`.
Its HIGH records remain in two OpenSSL packages and npm dependencies
`brace-expansion`, `ip-address` and `tar`; fixing the earlier critical tar
record alone is insufficient. The website builder stays on Node 22 pending
a reviewed build-tool remedy and a final builder scan.
## Method, verification and retained evidence
The existing Trivy 0.74.0 executable was rehashed against the previously verified
archive member: `d89bcc6510a267f11b773398cbf1be5520ce39f9e8b6633178c4487f05b7d791`.
The same schema-2 vulnerability database was used, updated
`2026-09-07T19:06:01.154199452Z`. No tool installation or database refresh occurred.
Index bytes matched both the registry digest header and Docker Hub publisher
metadata; both platform-manifest byte hashes matched the index. All eight
registry-only scans completed successfully with validated JSON, `--list-all-pkgs`,
`--scanners vuln`, an eight-minute/2GB image bound, an empty Docker configuration
and no inherited credentials. Exit zero means execution succeeded. Private
temporary paths and in-memory artifact cache isolated this follow-up from the
earlier scanner's artifact cache; its vulnerability database was read only.
Raw reports, logs, manifests, publisher metadata and the scanner script are in
`/home/zemion/.cache/govoplan-runtime-remediation.qfDSWHJh/`:
- `summary.json` SHA-256: `0d44390408ab35270e4430516f77bf11aa7877334eff2ef19e11e9a863fe5c56`.
- `frozen-images.json` SHA-256: `d0cb156f4a88998531ec55ab950067a3f1350ded648f07650c463af101dad467`.
- `scan_successors.py` SHA-256: `f64c69e06efc2ad7b5a657a25aa73f9ff586e3685737d525456bcecbe3ab5f07`.
Local retention is not permanent artifact hosting; preserve this evidence with
the eventual reviewed release. The JSON evidence records compressed registry
layer sizes; these are not expanded filesystem limits or final GovOPlaN sizes.
Installer regression checks cover the new generated image pin, legacy
specification fallback, preserved explicit images, generated topology and
configuration: `python -I -m unittest discover -s tests -p
test_deployment_installer.py` ran 45 tests successfully with one skip because
Core was not importable in that isolated test environment. The skipped Core
startup-configuration integration was subsequently rerun in the shared development
environment with Core available: all 45 installer tests passed with no skips,
including generated-environment startup validation. This is configuration
validation, not execution of the candidate image.
Both repositories passed `git diff --check`; the audit JSON and all eight
report hashes were checked against the retained evidence.
**Docker, Podman and HAProxy executables are unavailable on
this host**, so no image or HAProxy configuration was executed and no daemon was
installed. Publisher metadata and installer tests support the scoped source
patch; they do not establish binary or deployed compatibility.
## Gates that remain open
- Validate `haproxy -c` on generated local, existing-proxy and managed-ingress
configurations using the exact pinned image and target architectures. Run
bounded isolated checks without live mounts, secrets, privilege or external
network access. Then verify DNS discovery, readiness, forwarded headers,
replica routing and graceful termination in the intended runtime.
- Test the nginx candidate with both the website configuration and GovOPlaN
WebUI entrypoint/proxy configuration, including UID 101, writable temporary
paths, health paths, cache headers and static catalog bytes. Approve the
website nginx/NJS/Alpine version changes before changing its Dockerfile.
- Resolve Caddy, Node build-tool and all unchanged baseline dependencies with
updated publisher images or narrow reviewed applicability evidence. No
severity-wide exceptions or custom third-party rebuilds were introduced.
- Close the original source-built/static inventory gaps. HAProxy's 24-package
OS inventory still omits the source-built HAProxy executable. Node's npm
inventory still omits the Node executable/stdlib. Garage, CPython, Redis,
PostgreSQL and QEMU gaps are unchanged. Alpine 3.24 EOL metadata is still
missing from this scanner; zero findings is not complete coverage.
- Scan **final built** API/Web/website layers and the selected managed
dependencies on both architectures, then perform migration, worker,
readiness and ingress smoke checks. Record failure and unknown states.
Secrets, misconfiguration and image signature policy need separate checks.
- Obtain the website deployment host/operator and rebuild/restart authority,
preserving the exact immutable catalog/keyring/module-directory bytes and
verifying fresh public responses after an authorized rollout.
No images were built, executed, published or deployed; no running service,
release tag, signed manifest, CI image input or live infrastructure was changed.