Skip to content

feat(server): add ELF signing using delivery-kit-sdk#389

Open
dmmordvi wants to merge 26 commits into
mainfrom
feat/server/elf-signing
Open

feat(server): add ELF signing using delivery-kit-sdk#389
dmmordvi wants to merge 26 commits into
mainfrom
feat/server/elf-signing

Conversation

@dmmordvi

Copy link
Copy Markdown
Contributor

No description provided.

dmmordvi added 6 commits June 26, 2026 19:42
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
The e2e job b

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…ation

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
@dmmordvi dmmordvi added the test docs Check documentation on test label Jun 29, 2026
dmmordvi added 15 commits June 30, 2026 13:58
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…ocal testing)

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
@dmmordvi
dmmordvi requested a review from alexey-igrychev July 2, 2026 18:48
@dmmordvi
dmmordvi marked this pull request as ready for review July 2, 2026 18:49
dmmordvi added 2 commits July 2, 2026 23:59
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>

@dmmordvi dmmordvi left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated code review of the ELF signing feature. 7 findings (2 MAJOR, 5 MINOR), all posted as inline comments below. No blockers. Verified correct and not flagged: the ELF-embed → PGP → TUF ordering all bind the same final bytes; temp-file perms (0600) and cleanup on every error/success path; peek/replay for non-ELF and <4-byte inputs; the interface signature change threaded through the sole caller; unsupported-arch objcopy failing as a returned error. Note: this PR adds two new external dependencies (deckhouse/delivery-kit-sdk, sigstore/sigstore) plus a large transitive bump, and touches signing-key/credential handling.

(generated by pi-pi)

Comment thread server/pkg/elf_signing/signing.go Outdated
}

for envKey, val := range envs {
origVal, _ := os.LookupEnv(envKey)

@dmmordvi dmmordvi Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR: origVal, _ := os.LookupEnv(envKey) discards the presence bool. The returned restore closure always calls os.Setenv(key, origVal), so any VAULT_* variable that was originally unset is left set to "" after signing instead of being unset. This leaks empty/stale values into the process environment across signings and to any other in-process consumer of VAULT_ADDR / VAULT_ROLE_ID / etc. Record whether each var was present and os.Unsetenv on restore when it wasn't.

(generated by pi-pi)

}
}()

if _, deferErr = io.Copy(tmp, br); deferErr != nil {

@dmmordvi dmmordvi Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR: for an ELF artifact this copies the entire artifact into os.TempDir() with no size cap, while publisher.mu is held for the whole StageReleaseTarget call. A large or malicious ELF can exhaust temp storage and block all publisher operations. Consider a size limit and/or a configurable spool directory, and document the disk requirement.

(generated by pi-pi)

// configures Vault transit access only through process-global env vars
// (VAULT_ADDR, role/secret id, etc.), so concurrent signings from different
// plugin mounts would otherwise race and leak settings into each other.
var signMu sync.Mutex

@dmmordvi dmmordvi Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: signMu serializes Sign, but the env vars it mutates are process-global; unrelated goroutines reading VAULT_ADDR (or the vault client used elsewhere) during a signing window can observe the mutated values. Serialization prevents signing-vs-signing races but not signing-vs-other-reader races. Prefer an explicit client-configuration API over environment variables if the SDK allows; otherwise document the constraint.

(generated by pi-pi)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We postpone it for better days, when we adopt delivery-kit-sdk to passing vault settings like opts instead of envs


passFunc := cryptoutils.SkipPassword
if opts.KeyPassword != "" {
passFunc = cryptoutils.StaticPasswordFunc([]byte(opts.KeyPassword))

@dmmordvi dmmordvi Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: a non-empty password supplied together with a hashivault:// key reference is silently ignored (as documented) but not rejected at config time. Reject it during validation so operators do not believe their password protects the remote key.

(generated by pi-pi)

Comment thread server/pkg/elf_signing/signing.go Outdated
origEnvs[envKey] = origVal

if err := os.Setenv(envKey, val); err != nil {
panic(fmt.Errorf("failed to set env var %q: %w", envKey, err))

@dmmordvi dmmordvi Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: setVaultEnvVars panics if os.Setenv fails (same in the restore closure). Though rare, a panic on the plugin publish path crashes the process rather than failing a single publication; return an error instead.

(generated by pi-pi)

return NewErrIncorrectTargetPath(releaseFilePath)
}

source, err := publisher.trySignELF(ctx, storage, releaseFilePath, data)

@dmmordvi dmmordvi Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: trySignELF/StageReleaseTarget have no publisher-level tests. Missing coverage: ELF detection, non-ELF pass-through, temp cleanup on error, unsupported-arch rejection, malformed ELF, and copy failure. EM_AARCH64 is accepted but only amd64 is exercised end-to-end — add focused unit tests and ideally an arm64 signing path test.

(generated by pi-pi)

return fmt.Errorf("%q is required", fieldNameELFSigningKey)
}

if strings.Contains(settings.KeyRef, "://") {

@dmmordvi dmmordvi Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: strings.Contains(settings.KeyRef, "://") selects the reference branch (fine, since the base64 alphabet excludes :), but signing.go uses HasPrefix(hashivault.ReferenceScheme) — keep detection consistent. Validation also accepts trailing garbage after a certificate/intermediate PEM block and does not verify certificate/key correspondence; the sign operation would catch a mismatch later, but config-time validation could be stricter.

(generated by pi-pi)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this branch, we need to select all the links, including hashivault. The code below distinguishes the hashivault link from other links that are not supported.
But i changed cert/key correspondence

dmmordvi added 3 commits July 17, 2026 18:44
setVaultEnvVars restored every captured var with os.Setenv, so vars that
were originally absent were left set to "" instead of being unset,
polluting the process environment for other in-process consumers.

Record per-key presence via os.LookupEnv and, on restore, os.Unsetenv
vars that were originally absent. Make setup transactional: a mid-setup
failure rolls back already-applied vars and returns a wrapped error
instead of panicking, and the restore path returns joined errors that
Sign surfaces alongside the signing result.

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…config

A `password` supplied alongside a `hashivault://` key reference was
silently ignored, misleading operators into thinking it protected the
remote key. Reject it at config-validation time instead.

Addresses PR #389 review finding m4.

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
…emp-disk use and password prohibition

Signed-off-by: Dmitry Mordvinov <dmitry.mordvinov@flant.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test docs Check documentation on test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant