fix(sbom): report the pm package catalogue version the build installed with - #320
reyreavman wants to merge 12 commits into
Conversation
…with The provenance file written for the SBOM took PACKAGES_VERSION from the base image environment or a build secret only, ignoring packages[].env. A version set there never reached the guard, so a build on a base image without that variable failed as if nothing was set, and when both sources existed the SBOM reported a different version than the one pm installed with. Pass the directive env into the version file command so the guard, the provenance file and pm install all use one value. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
The packages stage silently resolved PACKAGES_VERSION and REGISTRY from /run/secrets entries that happened to carry those names, so an unrelated secret could redirect pm at another registry and nothing in werf.yaml revealed the link. The mechanism was also undocumented, leaving no way to learn about it. Both variables now come from the base image environment or from an explicit %secret:ID% reference in packages[].env. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
The scratch fixture relied on werf resolving PACKAGES_VERSION and REGISTRY from identically named secrets on its own, which the packages stage no longer does. Reference both secrets from packages[].env so the fixture keeps covering the base image that supplies neither variable. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
Dropping the implicit lookup leaves no signal for a config that declared PACKAGES_VERSION or REGISTRY as a secret and relied on werf wiring it into pm: the build keeps going and pm falls back to its own defaults. Warn on such a declaration and show the explicit reference that restores the value. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
The os-pm section claimed the builder base images always provide PACKAGES_VERSION and REGISTRY, leaving no answer for a base image that provides neither and no mention that PACKAGES_VERSION is mandatory. Describe both and show the explicit secret reference that supplies them. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
Verification
Review focus
|
… cannot be read The version assignment was chained to the provenance guard with `&&`, and bash exempts the left side of `&&` from `set -e`. An unreadable secret file therefore skipped the guard and let `pm install` run with an empty PACKAGES_VERSION while the stage still exited 0. Terminating the assignment with `;` lets `set -e` abort the stage on the failed read. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
…ever references The warning keyed on the secret ids PACKAGES_VERSION and REGISTRY. REGISTRY is a generic name with an unrelated meaning elsewhere in werf, so a secret declared for a shell stage would trigger it, and the list had no removal date. The guard on PACKAGES_VERSION already fires at the exact moment the value is missing and can carry the hint instead. This reverts commit 309fb8d. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
…e cannot find it The guard message named why werf needs the variable but not what to do about it. Spell out the explicit packages[].env reference in the message that stops the build. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
…annot be read A value read from a secret stayed in the command prefix of the package manager, where bash does not apply `set -e` to a failed command substitution: an unreadable secret left the variable empty, so `pm` installed from its own default registry and the stage still exited 0. Such a value now gets a statement of its own, and the install command reuses it. Literal values keep the inline prefix they had, so only an entry referencing a secret changes shape and rebuilds. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
The secrets section promised a referenced secret never reaches the resulting image, which does not hold for PACKAGES_VERSION: werf writes it into the image and the SBOM on purpose. Say what the guarantee covers, and name the migration for a build that relied on a secret named PACKAGES_VERSION or REGISTRY reaching pm on its own. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
7717c77 to
2a461a2
Compare
Re-running only the failed jobs skips kind_setup, whose cluster and test registry kind_cleanup has already torn down, so every e2e spec fails on a refused connection instead of reproducing the failure under investigation. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
Fral738
left a comment
There was a problem hiding this comment.
Requesting changes because secret-backed packages[].env assignments now outlive their directive. When the base image exports the variable, subsequent package managers inherit the previous directive's secret value even if their own env does not reference it. This changes package-source/proxy selection for otherwise unchanged later entries. Please isolate these assignments before merging.
The description's claim that packages[].env handling is otherwise unchanged does not hold for multiple directives sharing the packages-stage shell. The inline comment includes the reproduction and suggested correction.
Validation at 2eef160e: task build and full task test:unit passed. A temporary two-command regression probe failed for a secret reference (private-proxy / private-proxy instead of private-proxy / base-proxy) and passed for the equivalent literal value. Mutations of version precedence, the empty-version guard, and unreadable-secret handling were detected; the last required system bash 3.2 because Homebrew bash 5.2.37 already aborts on the old prefix form. All temporary changes were restored. The focused scratch/Vanilla Docker e2e did not reach the packages stage: pushing its builder image to the local registry failed with a connection error, so that e2e and its fixture mutation remain unverified locally.
| parts = append(parts, assignment) | ||
| continue | ||
| } | ||
|
|
There was a problem hiding this comment.
These standalone assignments overwrite the shell environment for subsequent packages directives. All commands run in one stage script (legacy executor, Buildah executor). If the base image exports GOPROXY=base-proxy, a first go-mod entry with GOPROXY: "%secret:proxy%" permanently replaces it; a second entry without env then installs through that private proxy as well. Previously the command prefix kept this override local to the first package manager.
A two-command formatWorkdirCommand probe under bash -e, using printenv GOPROXY as the manager command and a secret containing private-proxy, prints private-proxy\nprivate-proxy\n; the same probe with a literal env value correctly prints private-proxy\nbase-proxy\n.
Wrap the complete generated directive in a subshell when it needs standalone assignments, keeping the assignments and install inside it (and the version guard/provenance write for os-pm). This preserves fail-fast secret reads without modifying the next directive's environment. Add a regression with an exported base variable, a secret-backed override in the first directive, and no override in the second. For example:
(
GOPROXY="$(</run/secrets/proxy)"
cd "/app" && GOPROXY="$GOPROXY" go mod download
)
cd "/tools" && go mod downloadThere was a problem hiding this comment.
Confirmed and fixed in 03ae8cd. Reproduced your probe first: two go-mod directives under bash -e with GOPROXY=base-proxy exported printed private-proxy / private-proxy for the secret reference and private-proxy / base-proxy for the literal — the standalone assignment introduced in ec12f25c87 outlived its directive exactly as you describe.
The directive now runs in a subshell whenever it has such an assignment (formatWorkdirCommand and formatInstallCommand share joinDirective); a directive without one keeps its previous single-line form, so its digest does not move again. For os-pm the mkdir, guard, provenance write and pm install all sit inside the subshell.
Regression added as formatWorkdirCommand › two directives in one stage script: exported base variable, secret-backed override in the first directive, no env in the second, asserting private-proxy\nbase-proxy\n, plus the literal variant and an unreadable-secret case asserting the stage dies before the second directive runs. Mutation check: removing the parentheses from joinDirective fails the secret-backed entry (and the byte-for-byte golden), while the literal entry stays green — the same split your probe showed.
The PR description no longer claims the handling is otherwise unchanged; it now says so only for directives that reference no secret.
…tive Every packages directive runs in the same stage script. A value read from a secret got a statement of its own so that `set -e` catches an unreadable file, and that statement overwrote the shell variable for every later directive: with a base image exporting GOPROXY, a first entry referencing a secret sent the second entry, which set no env at all, through the same proxy. The whole directive now runs in a subshell whenever it has such a statement, so the read still fails the stage while the next directive sees the environment the base image exports. Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
Summary
An
os-pmbuild no longer resolvesPACKAGES_VERSIONandREGISTRYfrom build secrets whose id happens to match those names; both come from the base image environment or from an explicit%secret:ID%reference inpackages[].env. The same change fixes the SBOM reporting a package catalogue version the build did not install with:packages[].env.PACKAGES_VERSIONnever reached the guard that writes/var/lib/pm/container-factory-version, so ascratchbase failed as if the variable were unset, and when the base image also set it thecontainerfactoryversionpurl qualifier carried the base value whilepminstalled with theenvone.What
PACKAGES_VERSIONorREGISTRYand relied on werf wiring it intopmloses that value —pmfalls back to its own default registry and the stage fails on a missing version. The way out is onepackages[].enventry referencing the secret.packages[].env.PACKAGES_VERSIONnow satisfies the packages stage on a base image that sets no such variable; before, the stage failed as if the variable were unset.packages[].envsetPACKAGES_VERSION, the provenance file andpm installuse theenvvalue; the SBOM qualifier and the installed catalogue can no longer disagree.packages[].envvalue referencing a secret is now assigned by its own shell statement instead of riding in the command prefix, and the whole directive runs in a subshell: an unreadable secret file fails the stage, where beforepmran with the variable empty and the stage exited 0, and the assignment stays local to its directive — the next one in the same stage script still sees the variable the base image exports. Only an entry that references a secret changes shape and rebuilds; a literal value keeps its inline prefix.PACKAGES_VERSIONis missing now names the fix:PACKAGES_VERSION: werf records it in the SBOM; set it in packages[].env, e.g. PACKAGES_VERSION: "%secret:PACKAGES_VERSION%". No warning keyed on secret ids:REGISTRYis a generic name with an unrelated meaning in.Values.werf.image, andpmreports an unreachable registry itself./run/secretsunless the config references a secret, so the stage digest of everyos-pmimage changes once and the packages stage rebuilds.os-pmdocs (en + ru) now state thatPACKAGES_VERSIONis mandatory, show how to supply both variables when the base image provides neither, and name the migration for a build that relied on the implicit lookup.PACKAGES_VERSIONis written into the image and the SBOM by design.os-pmand for the file-based types alike.Why
formatVersionFileCommandbuilt its own copy of the value from the base image environment and the implicit secret lookup, whilepm installgot the directive env — one variable resolved twice from different inputs, which is why an explicitly set version could be both ignored and contradicted in the SBOM at once. The implicit lookup itself dates to 6a744aa, when ascratchbase had no other way to supply the variables; since%secret:ID%landed it is a second, undocumented mechanism for the same job, and it reserves two very common secret ids — a secret namedREGISTRYdeclared for anything else silently redirectspm.Keeping the lookup for
PACKAGES_VERSIONonly — narrower, and werf itself is what demands that variable — was the alternative. It was rejected because the argument for it is exactly the argument for documenting the explicit form instead: after the precedence fix, oneenvline covers the scratch case, and a single mechanism is what makes the behavior discoverable.Fixes: 6a744aaf6e6f ("fix(config): resolve pm env vars from build secrets in packages stage")