Skip to content

fix(api-core): don't hold a db connection across the host UEFI credential read#4020

Merged
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-4001
Jul 24, 2026
Merged

fix(api-core): don't hold a db connection across the host UEFI credential read#4020
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-4001

Conversation

@chet

@chet chet commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Setting or clearing a host's UEFI password resolves the credential in two steps: read the target/device version out of Postgres, then fetch the secret at that version from the credential reader. With the Vault-backed reader that second step is a remote network call -- and we were making it with the database connection still in hand. #3872 restored the txn_held_across_await lint on api-core (it had been silenced since the #2054 crate split) and this was one of the cases it caught. The surrounding code even claimed it committed first -- a // Don't hold the transaction across an await point sits right above the commit -- but the vault read happened before it.

So, the version is the only thing the reader needs from the DB. This splits "resolve the credential key" (the DB half) from "read the secret" (the reader half): host_uefi_{set,clear}_credentials become host_uefi_{set,clear}_credential_key, and every caller resolves the key while the txn is open, commits, then calls read_uefi_credentials on nobody's connection. The three #[allow(txn_held_across_await)] + TODOs go away with them.

It's behavior-preserving -- same reads, same order -- the commit just finally lands before the remote read like the comment always said. Both handler transactions are read-only (the actual writes live in separate later transactions), so moving the read past the commit is safe. The force-delete helper stays best effort: it returns None and skips the clear on any failure -- now including a failed commit, which it warns about rather than silently ignoring.

Related issues

This supports #4001

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Behavior-preserving restructure -- same reads, same order, with the remote reader call moved past the commit. The existing credential_rotation integration tests exercise the set/clear paths; the change is lint-green (clippy + carbide-lints) and passed a clean cross-model (Codex) and local CodeRabbit review.

Additional Notes

Follow-up to #3872, which restored the lint and parked this case behind #[allow(txn_held_across_await)] + a TODO(#2991). Companion to the #2665 SessionLock follow-up from the same PR.

Closes #4001

…tial read

Setting or clearing a host's UEFI password resolves the credential in two steps: read the target/device version out of Postgres, then fetch the secret at that version from the credential reader. With the Vault-backed reader that second step is a remote network call -- and we were making it with the database connection still in hand. NVIDIA#3872 restored the `txn_held_across_await` lint on `api-core` (it had been silenced since the NVIDIA#2054 crate split) and this was one of the cases it caught. The surrounding code even *claimed* it committed first -- a `// Don't hold the transaction across an await point` sits right above the commit -- but the vault read happened before it.

So, the version is the only thing the reader needs from the DB. This splits "resolve the credential *key*" (the DB half) from "read the secret" (the reader half): `host_uefi_{set,clear}_credentials` become `host_uefi_{set,clear}_credential_key`, and every caller resolves the key while the txn is open, commits, then calls `read_uefi_credentials` on nobody's connection. The three `#[allow(txn_held_across_await)]` + TODOs go away with them.

It's behavior-preserving -- same reads, same order -- the commit just finally lands before the remote read like the comment always said. Both handler transactions are read-only (the actual writes live in separate later transactions), so moving the read past the commit is safe. The force-delete helper stays best effort: it returns `None` and skips the clear on any failure -- now including a failed commit, which it warns about rather than silently ignoring.

This supports NVIDIA#4001

Follow-up to NVIDIA#3872.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@chet
chet requested a review from a team as a code owner July 23, 2026 19:10
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 08f43fcb-c96c-4f30-9090-64831a09d91e

📥 Commits

Reviewing files that changed from the base of the PR and between cd37f83 and 8c94d56.

📒 Files selected for processing (2)
  • crates/api-core/src/handlers/machine.rs
  • crates/api-core/src/handlers/uefi.rs

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability when setting and clearing host UEFI passwords.
    • Ensured credential resolution completes any database transaction before retrieving secrets from the credential store, reducing transaction-related failures.
    • Improved error handling for transaction startup/commit during UEFI credential operations; commit failures are now surfaced and treated as a failed credential retrieval.

Walkthrough

UEFI credential handling now resolves database-backed credential keys inside transactions, commits before remote secret reads, and then performs UEFI password operations with the retrieved credentials.

Changes

UEFI credential flow

Layer / File(s) Summary
Credential key resolution
crates/api-core/src/handlers/uefi.rs
read_uefi_credentials is exposed within the crate, while separate helpers resolve set and clear credential keys from database state.
Post-commit UEFI operations
crates/api-core/src/handlers/uefi.rs, crates/api-core/src/handlers/machine.rs
Set, clear, and machine credential flows commit transactions before reading credentials through the credential reader; machine-flow commit failures now emit a warning and return None.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant UefiPasswordHandler
  participant DatabaseTransaction
  participant CredentialReader
  participant RedfishPool
  UefiPasswordHandler->>DatabaseTransaction: resolve credential key
  UefiPasswordHandler->>DatabaseTransaction: commit transaction
  UefiPasswordHandler->>CredentialReader: read UEFI credentials
  CredentialReader-->>UefiPasswordHandler: return credentials
  UefiPasswordHandler->>RedfishPool: uefi_setup with credentials
Loading

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately states the primary change: avoiding holding a DB connection across the host UEFI credential read.
Description check ✅ Passed The description clearly matches the implemented transaction-boundary fix and credential key split.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@chet

chet commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full_review, thanks!

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@chet Understood — I’ll perform a complete review of the PR, including the transaction-boundary changes and affected call paths.

🐇🔎

✅ Action performed

Full review finished.

let clear_key = key.ok()?;
crate::handlers::uefi::read_uefi_credentials(api.redfish_pool.credential_reader(), &clear_key)
.await
.ok()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

all errors are just ignored. seems like a bad idea (yes, its an original code issue)

let clear_key = host_uefi_clear_credential_key(&mut txn, bmc_mac).await?;

// Don't hold the transaction across an await point
// Commit before the remote reader (Vault) request so the connection is not

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

vault?

@chet
chet merged commit a9e97bc into NVIDIA:main Jul 24, 2026
61 checks passed
@chet
chet deleted the gh-issue-4001 branch July 24, 2026 16:23
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.

Host UEFI credential resolution holds a DB connection across the remote Vault read

2 participants