Skip to content

Move CLI image base to digest-pinned SUSE BCI micro 15.7 - #162

Merged
LouisLotter merged 1 commit into
mainfrom
cve-cli-base-bci-micro
Sep 8, 2026
Merged

Move CLI image base to digest-pinned SUSE BCI micro 15.7#162
LouisLotter merged 1 commit into
mainfrom
cve-cli-base-bci-micro

Conversation

@ai-collaboration-app

Copy link
Copy Markdown

Why

docker/Dockerfile.goreleaser built the published stackstate/stackstate-cli2 image FROM gcr.io/distroless/static-debian11. Debian 11 is end-of-life, and Trivy says so explicitly — Metadata.OS.EOSL is true, with a warning that "vulnerability detection may be insufficient because security updates are not provided". A green scan on that base was partly a dead advisory feed, not a clean image.

It was already hiding rows. Scanning the currently published 3.9.0 images with UNKNOWN severity included (the dev gate passes only CRITICAL,HIGH,MEDIUM,LOW, so UNKNOWN is filtered before the evaluator reads the report) surfaces five tzdata advisories on both architectures:

Severity Advisory Package Installed Fixed
UNKNOWN DLA-3972-1 tzdata 2024a-0+deb11u1 2024b-0+deb11u1
UNKNOWN DLA-4085-1 tzdata 2024a-0+deb11u1 2025a-0+deb11u1
UNKNOWN DLA-4105-1 tzdata 2024a-0+deb11u1 2025b-0+deb11u1
UNKNOWN DLA-4403-1 tzdata 2024a-0+deb11u1 2025b-0+deb11u2
UNKNOWN DLA-4569-1 tzdata 2024a-0+deb11u1 2026b-0+deb11u1

What changed

One FROM line, to registry.suse.com/bci/bci-micro:15.7, digest-pinned. SUSE BCI is the mandated base family and bci-micro can do the job, so no exception is claimed.

The digest is deliberately the multi-arch index digest sha256:44f5c047…, not a per-arch one. .goreleaser.yml builds this same Dockerfile twice, once with --platform=linux/amd64 and once with --platform=linux/arm64/v8; a per-arch pin would break one of them. Verified both resolve.

No package installation is added, so there is no zypper step — bci-micro ships what the CLI needs already.

Validation

Built on this branch's head from the real GoReleaser ldflags under Go 1.25.13 (go.mod's pin — the Go line is deliberately untouched; go1.25.13 has no open stdlib advisory and 1.26 would add exposure while clearing nothing).

Both published architectures covered — the scan resolves two per-arch digests.

Check distroless/static-debian11 (published 3.9.0) bci-micro:15.7 (this PR)
Detected OS debian 11.10 sles 15.7
Metadata.OS.EOSL true false
OS packages seen by scanner 3 21
Trivy vuln, VEX-aware, incl. UNKNOWN (amd64) 5 UNKNOWN 0
Trivy vuln, VEX-aware, incl. UNKNOWN (arm64) 5 UNKNOWN 0
Trivy secret scan, all severities 0 0 (both arches)
Grype 0 0 (both arches)
Go packages in binary 98 98 (identical — binary unchanged)

Tooling: Trivy 0.74.0, Grype 0.117.0. The 21-vs-3 OS package count matters: the clean result is a live SUSE feed reading a real rpmdb, not an empty scan.

Command surface, on the migrated amd64 image: sts version, sts version -o json (all three fields correct), and sts --help listing all 24 command groups.

TLS trust probe, and it is not vacuous. The CLI's own HTTPS path against a real host:

  • bci-micro: sts user-session roles --url https://example.com completes the handshake and returns a genuine 404 Not Found from example.com — the CA bundle at /etc/ssl/ca-bundle.pem is found by Go's x509 loader.
  • Same binary FROM scratch (control): tls: failed to verify certificate: x509: certificate signed by unknown authority.

So the probe detects the trust store rather than just a zero exit code.

Trade-offs, stated plainly

Attack surface grows. SUSE publishes no distroless-class base, so complying costs a shell and more executables in a customer-facing image:

executables (excl. sts) shell compressed size
distroless/static-debian11 10 none 16.6 MB
bci-micro:15.7 164 bash, sh 26.1 MB

That is the deliberate trade: +9.5 MB and a shell, in exchange for a maintained advisory feed. distroless/static-debian12 would be smaller and shell-free but is not BCI and would need a written exception.

bci-micro ships no zoneinfo; this is behaviourally inert here. Independently re-verified on this head: there is no time.Local, LoadLocation, or .In(...) conversion anywhere in non-test code. The one timestamp renderer, internal/util/string_util.go:120, is v.Format("Mon Jan 2 15:04:05 2006 MST"), which prints the zone already attached to the value the API returned and performs no tzdata lookup. The only LoadLocation("EST") is internal/util/string_util_test.go:39, which runs on the CI host, not in the image.

CI does not cover this change. ci.yml builds the image only in the publish job, gated on github.ref_type == 'tag'. No PR job builds docker/Dockerfile.goreleaser, so a green CI run here is evidence about the Go code, not about the base migration — the build and scan evidence above is what covers it. Worth a follow-up, out of scope for this PR.

go vet ./... and go test ./... pass on this head.

Context

Addresses the base-image finding tracked in https://github.com/StackVista/cve-reporter/issues/65, coordinated from https://github.com/StackVista/cve-reporter/issues/32. Neither is auto-closed by this PR: closure needs a later complete dev scan showing both CLI targets clean, and this base change reaches customers only once a release is cut above v3.9.0.

gcr.io/distroless/static-debian11 is end-of-life. Trivy reports
Metadata.OS.EOSL=true and warns that detection is insufficient because
Debian 11 no longer receives security updates, so a clean scan on that
base was partly a dead advisory feed rather than a clean image. Five
tzdata advisories sat below the dev gate only because the gate filters
UNKNOWN severity.

registry.suse.com/bci/bci-micro:15.7 is SLES 15-SP7 with a live SUSE
feed, publishes both linux/amd64 and linux/arm64/v8, and needs no
package installation, so no zypper step is introduced. The digest is
the multi-arch index digest so both GoReleaser per-arch builds resolve
it under --platform.
@LouisLotter
LouisLotter merged commit e14f11e into main Sep 8, 2026
5 checks passed
@LouisLotter
LouisLotter deleted the cve-cli-base-bci-micro branch September 8, 2026 18:37
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.

2 participants