Skip to content

feat: rotate BF4 service password#3858

Open
hakhondzadeh wants to merge 3 commits into
NVIDIA:mainfrom
hakhondzadeh:BF4-service-acct
Open

feat: rotate BF4 service password#3858
hakhondzadeh wants to merge 3 commits into
NVIDIA:mainfrom
hakhondzadeh:BF4-service-acct

Conversation

@hakhondzadeh

@hakhondzadeh hakhondzadeh commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Description: NICo already ingests BF3 DPUs by logging in as root, rotating root to the site-wide BMC root password, and storing root credentials per BMC in Vault. BF4 needs the same root path for Redfish, plus a separate step to rotate the service account used for SSH.

What this PR does
On first ingest of a BF4 DPU BMC (no per-BMC Vault entry yet):

Detect BF4 via anonymous Redfish ServiceRoot.Product (B4240V / BlueField-4) using bmc_explorer::is_bf4_product.
Ingest with root — same as BF3: factory root:0penBmc → rotate to site-wide BMC root password → store root creds at machines/bmc/{mac}/root.
Rotate service account — while authenticated as root, call libredfish change_password_by_id("service", …) to move off factory Nvidia_12345!.
Store site-wide service password — a dedicated password (distinct from sitewide root) is created once at machines/bmc/site/bf4_service and reused for all BF4 DPUs at the site.
Subsequent Redfish exploration continues to use root credentials from the per-BMC Vault path. The rotated service password is stored sitewide for SSH use; it is not used for Redfish exploration today.

Type of Change

  • [ x ] 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.)

Testing

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

@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Summary by CodeRabbit

  • New Features
    • Improved BMC endpoint exploration to detect BlueField-4 DPU devices.
    • Added secure rotation of the BF4 “service” account password using root credentials.
    • Introduced site-wide storage with automatic generation for the BF4 service password when missing.
  • Bug Fixes
    • Enhanced recovery flow when per-device BMC credentials are missing, including BF4-specific fallback behavior.
    • Redacts sensitive passwords from surfaced errors.
  • Tests
    • Expanded coverage for BF4 product matching, BF4 service password creation/retention, and rotation behavior.

Walkthrough

Adds site-wide BF4 DPU service credential storage and retrieval, Redfish service-account password rotation, BF4 product detection, and endpoint exploration integration during missing per-BMC credential recovery.

Changes

BF4 service credential rotation

Layer / File(s) Summary
BF4 credential storage
crates/secrets/src/credentials.rs, crates/site-explorer/src/credentials.rs
Adds the SiteWideBf4Service credential path and supports retrieving, creating, persisting, and testing the BF4 service password.
Redfish password API
crates/bmc-explorer/src/lib.rs, crates/redfish/src/libredfish/*, crates/site-explorer/src/redfish.rs
Exports BF4 product detection, adds Redfish service-password rotation, exposes product lookup, and tests simulator password changes.
Endpoint recovery integration
crates/site-explorer/src/bmc_endpoint_explorer.rs
Detects BF4 products during missing-credential recovery, rotates the service password, and then generates the exploration report.

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

Sequence Diagram(s)

sequenceDiagram
  participant BmcEndpointExplorer
  participant RedfishClient
  participant CredentialClient
  participant RedfishClientPool
  BmcEndpointExplorer->>RedfishClient: Query Redfish product
  RedfishClient-->>BmcEndpointExplorer: Return product
  BmcEndpointExplorer->>CredentialClient: Get or create BF4 service password
  CredentialClient-->>BmcEndpointExplorer: Return password
  BmcEndpointExplorer->>RedfishClient: Rotate service password
  RedfishClient->>RedfishClientPool: Change service account password
  RedfishClientPool-->>RedfishClient: Return rotation result
  RedfishClient-->>BmcEndpointExplorer: Return rotation result
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly captures the primary change: rotating the BF4 service password.
Description check ✅ Passed The description is directly aligned with the BF4 service-password rotation changes in the PR.
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.

@hakhondzadeh
hakhondzadeh marked this pull request as ready for review July 22, 2026 18:07
@hakhondzadeh
hakhondzadeh requested a review from a team as a code owner July 22, 2026 18:07

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/site-explorer/src/bmc_endpoint_explorer.rs (1)

826-900: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Retry BF4 service rotation after partial ingest failures.

The root credential is persisted before Lines 889-892 run. If service rotation fails, the next exploration takes the existing per-BMC credential path at Line 749 and never retries it, leaving the factory service password active indefinitely. Existing BF4 devices also bypass this migration. Move the BF4 detection/rotation into a recoverable path after root credentials are obtained, or persist explicit per-device completion state; add a regression test for retry after a failed rotation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/site-explorer/src/bmc_endpoint_explorer.rs` around lines 826 - 900,
The BF4 service-password rotation in the exploration flow must be retried when a
prior attempt failed, including for existing devices that already have persisted
root credentials. Update the path around set_sitewide_bmc_root_password,
try_sitewide_bmc_root_credentials, and rotate_bf4_dpu_service_password so BF4
detection and rotation run after either credential source and remain recoverable
on subsequent explorations; add a regression test covering a failed rotation
followed by a retry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/site-explorer/src/credentials.rs`:
- Around line 156-168: Update the create-if-missing branch in the credentials
lookup flow around get_credentials and set_credentials to use an atomic
create/CAS operation, preventing concurrent writers from overwriting one
another. When creation reports an already-exists conflict, re-read the
credentials and return the stored password instead of the newly generated value;
preserve the existing return path for an actual successful create. Add a
concurrent regression test covering two callers racing to create the same
site-wide password.

---

Outside diff comments:
In `@crates/site-explorer/src/bmc_endpoint_explorer.rs`:
- Around line 826-900: The BF4 service-password rotation in the exploration flow
must be retried when a prior attempt failed, including for existing devices that
already have persisted root credentials. Update the path around
set_sitewide_bmc_root_password, try_sitewide_bmc_root_credentials, and
rotate_bf4_dpu_service_password so BF4 detection and rotation run after either
credential source and remain recoverable on subsequent explorations; add a
regression test covering a failed rotation followed by a retry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 53f972ff-1c7b-43cd-8439-a587f1aaebdd

📥 Commits

Reviewing files that changed from the base of the PR and between 7d5c98a and 31cd4df.

📒 Files selected for processing (7)
  • crates/bmc-explorer/src/lib.rs
  • crates/redfish/src/libredfish/mod.rs
  • crates/redfish/src/libredfish/test_support.rs
  • crates/secrets/src/credentials.rs
  • crates/site-explorer/src/bmc_endpoint_explorer.rs
  • crates/site-explorer/src/credentials.rs
  • crates/site-explorer/src/redfish.rs

Comment on lines +156 to +168
match self.get_credentials(&key).await {
Ok(Credentials::UsernamePassword { password, .. }) => Ok(password),
Err(EndpointExplorationError::MissingCredentials { .. }) if create_if_missing => {
let password = Credentials::generate_password();
self.set_credentials(
&key,
&Credentials::UsernamePassword {
username: "service".to_string(),
password: password.clone(),
},
)
.await?;
Ok(password)

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make site-wide password creation atomic.

Concurrent BF4 ingests can both observe a missing key, generate different passwords, and overwrite each other through set_credentials. Each BMC may then receive a different password while Vault retains only the last write. Use an atomic create/CAS operation; on an already-exists conflict, re-read and return the stored password. Add a concurrent regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/site-explorer/src/credentials.rs` around lines 156 - 168, Update the
create-if-missing branch in the credentials lookup flow around get_credentials
and set_credentials to use an atomic create/CAS operation, preventing concurrent
writers from overwriting one another. When creation reports an already-exists
conflict, re-read the credentials and return the stored password instead of the
newly generated value; preserve the existing return path for an actual
successful create. Add a concurrent regression test covering two callers racing
to create the same site-wide password.

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/site-explorer/src/bmc_endpoint_explorer.rs (2)

891-902: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Retry BF4 service rotation after partial first-ingest failures.

Root credentials are stored before service rotation. If rotation fails, the next run takes the existing-credentials path and generates a report without retrying it, leaving the device and Vault with mismatched service passwords. Track rotation completion or retry BF4 rotation in that path before reporting success.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/site-explorer/src/bmc_endpoint_explorer.rs` around lines 891 - 902,
Update the BF4 handling around generate_exploration_report to retry
rotate_bf4_dpu_service_password when existing credentials indicate rotation was
not completed, including runs following a partial first-ingest failure. Track or
detect rotation completion before generating the report, while preserving the
existing behavior for successfully rotated devices.

280-285: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make BF4 service-password creation atomic.

Concurrent first ingests can both observe a missing credential, generate different passwords, and program different devices before Vault retains only the last write. Use an atomic create-if-absent operation or shared lock, and return the committed value before updating any device.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/site-explorer/src/bmc_endpoint_explorer.rs` around lines 280 - 285,
The BF4 service-password flow around get_sitewide_bf4_dpu_service_password and
set_bf4_dpu_service_password is vulnerable to concurrent first-ingest races.
Make password creation atomic using an atomic create-if-absent operation or
shared lock, return the committed password value, and only then program devices
so every concurrent caller uses the same retained credential.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/site-explorer/src/bmc_endpoint_explorer.rs`:
- Around line 891-902: Update the BF4 handling around
generate_exploration_report to retry rotate_bf4_dpu_service_password when
existing credentials indicate rotation was not completed, including runs
following a partial first-ingest failure. Track or detect rotation completion
before generating the report, while preserving the existing behavior for
successfully rotated devices.
- Around line 280-285: The BF4 service-password flow around
get_sitewide_bf4_dpu_service_password and set_bf4_dpu_service_password is
vulnerable to concurrent first-ingest races. Make password creation atomic using
an atomic create-if-absent operation or shared lock, return the committed
password value, and only then program devices so every concurrent caller uses
the same retained credential.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: de80766c-857f-463f-bdf2-3ea466f09717

📥 Commits

Reviewing files that changed from the base of the PR and between 31cd4df and 352899b.

📒 Files selected for processing (1)
  • crates/site-explorer/src/bmc_endpoint_explorer.rs

@wminckler

Copy link
Copy Markdown
Contributor

cc @spydaNVIDIA who is working other bmc creds changes

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/site-explorer/src/bmc_endpoint_explorer.rs (2)

862-897: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep BF4 service rotation retryable after root-credential persistence.

set_sitewide_bmc_root_password persists the per-BMC root credentials before this rotation runs. If rotate_bf4_dpu_service_password fails, the next ingest takes the existing-credentials branch around Lines 746-803 and never re-enters BF4 detection or service rotation. The device can remain on its factory service password indefinitely. Run BF4 service convergence from an idempotent path that also handles already-persisted root credentials, or make the credential updates transactional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/site-explorer/src/bmc_endpoint_explorer.rs` around lines 862 - 897,
Ensure BF4 service-password rotation remains retryable after
set_sitewide_bmc_root_password persists credentials. Update the
existing-credentials ingest path, including the flow around
try_sitewide_bmc_root_credentials and rotate_bf4_dpu_service_password, so
already-persisted root credentials still trigger BF4 detection and service
rotation on later ingests; preserve idempotent behavior and propagate rotation
failures for future retries.

275-284: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make BF4 service-password creation atomic.

create_if_missing = true reaches a get-then-set implementation in crates/site-explorer/src/credentials.rs:148-172. Concurrent first ingests can therefore generate different passwords, overwrite the site-wide Vault entry, and leave one device configured with a password that is no longer stored. Use an atomic create-if-absent operation or a shared lock, then rotate devices with the winning stored value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/site-explorer/src/bmc_endpoint_explorer.rs` around lines 275 - 284,
Update rotate_bf4_dpu_service_password and the underlying
get_sitewide_bf4_dpu_service_password create-if-missing flow to use an atomic
create-if-absent operation or shared lock. Ensure concurrent first ingests
converge on one winning site-wide Vault password, and configure each device with
the value actually stored rather than a superseded generated value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/site-explorer/src/bmc_endpoint_explorer.rs`:
- Around line 862-897: Ensure BF4 service-password rotation remains retryable
after set_sitewide_bmc_root_password persists credentials. Update the
existing-credentials ingest path, including the flow around
try_sitewide_bmc_root_credentials and rotate_bf4_dpu_service_password, so
already-persisted root credentials still trigger BF4 detection and service
rotation on later ingests; preserve idempotent behavior and propagate rotation
failures for future retries.
- Around line 275-284: Update rotate_bf4_dpu_service_password and the underlying
get_sitewide_bf4_dpu_service_password create-if-missing flow to use an atomic
create-if-absent operation or shared lock. Ensure concurrent first ingests
converge on one winning site-wide Vault password, and configure each device with
the value actually stored rather than a superseded generated value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b46bb346-b2a9-4e40-aada-8777f5eb0547

📥 Commits

Reviewing files that changed from the base of the PR and between 352899b and e1c73db.

📒 Files selected for processing (4)
  • crates/redfish/src/libredfish/test_support.rs
  • crates/secrets/src/credentials.rs
  • crates/site-explorer/src/bmc_endpoint_explorer.rs
  • crates/site-explorer/src/credentials.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/redfish/src/libredfish/test_support.rs
  • crates/secrets/src/credentials.rs
  • crates/site-explorer/src/credentials.rs

/// Site-wide BF4 DPU BMC `service` account password
/// (`machines/bmc/site/bf4_service`). Written on first BF4 DPU ingestion;
/// distinct from the site-wide BMC root password.
SiteWideBf4Service,

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.

Is it worth calling this SitewideBmcServiceCredentials ? I imagine that the BFs going forward will have a service account as well?

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.

The information that I have is that this is only for BF4. BF3s will continue to have one root account for both ssh and redfish. Let me confirm with someone

BmcCredentialType::BmcForgeAdmin { bmc_mac_address } => Cow::from(format!(
"machines/bmc/{bmc_mac_address}/forge-admin-account"
)),
BmcCredentialType::SiteWideBf4Service => Cow::from("machines/bmc/site/bf4_service"),

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.

similarly, maybe the path should be machines/bmc/site/service

.map_err(|err| redact_password(err, new_password.as_str()))
.map_err(|err| redact_password(err, root_password.as_str()))
.map_err(RedfishClientCreationError::RedfishError)?;

@spydaNVIDIA spydaNVIDIA Jul 22, 2026

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.

Today, when we update the factory credentials of a DPU BMC, we add an entry per BMC mac into vault: machines/secret/bmc/DPU_BMC_MAC/root.

Similarly, I think we should add an entry per service account we configure (one for every BF4): machines/secrets/bmc/DPU_BMC_MAC/service

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.

This should help us down the line: it enables operators to configure custom passwords (in some edge cases we have had to do that in the past) and will make our lives easier when we want to support rotation of this credential.

async fn rotate_bf4_dpu_service_password(
&self,
bmc_ip_address: SocketAddr,
root_credentials: &Credentials,

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.

nit: rename root_credentials to factory_service_credentials

})
}

async fn rotate_bf4_dpu_service_password(

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.

nit: function rename to rotate_dpu_service_password_from_factory_defaults may reflect the semantics of this function better

Err(EndpointExplorationError::MissingCredentials { .. }) => {
// No per-BMC vault entry exists. Now try to:
// 1) Login with expected/factory credentials
// 2) Rotate the BMC root password to the sitewide root password

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.

should we update this comment to reflect that we are also rotating the BMC service password on BF4s (and indicating that this account is required for SSH access to BF4 BMCs)

};

if is_bf4_dpu {
self.rotate_bf4_dpu_service_password(bmc_ip_address, &bmc_credentials)

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.

Why are we passing in the bmc_credentials as the factory defaults?

}

/// Rotate a BF4 DPU BMC's `service` account password using root credentials.
async fn set_bf4_dpu_service_password(

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.

Worth adding an API/CLI endpoint to test configuring the service account's password?

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.

3 participants