Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/admin-cli/cli_domains.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ admin:
- dev-env
- ssh
- jump
- secrets
8 changes: 5 additions & 3 deletions crates/admin-cli/src/cfg/cli_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ use crate::{
ipxe_template, jump, machine, machine_interfaces, machine_validation, managed_host,
managed_switch, mlx, network_devices, network_security_group, network_segment, nvl_domain,
nvl_logical_partition, nvl_partition, nvlink_nmxc_endpoints, operating_system, os_image, ping,
power_shelf, rack, redfish, resource_pool, rms, route_server, scout_stream, set, site_explorer,
sku, spx_partition, ssh, switch, tenant, tenant_keyset, tpm_ca, trim_table, version, vpc,
vpc_peering, vpc_prefix,
power_shelf, rack, redfish, resource_pool, rms, route_server, scout_stream, secrets, set,
site_explorer, sku, spx_partition, ssh, switch, tenant, tenant_keyset, tpm_ca, trim_table,
version, vpc, vpc_peering, vpc_prefix,
};

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -202,6 +202,8 @@ pub enum CliCommand {
ExtensionService(extension_service::Cmd),
#[clap(about = "Firmware related actions", subcommand)]
Firmware(firmware::Cmd),
#[clap(about = "Secrets management", subcommand)]
Secrets(secrets::Cmd),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#[clap(
about = "Regenerate the docs/manuals/nico-admin-cli markdown reference",
hide = true
Expand Down
2 changes: 2 additions & 0 deletions crates/admin-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ mod rms;
mod route_server;
mod rpc;
mod scout_stream;
mod secrets;
mod set;
mod site_explorer;
mod sku;
Expand Down Expand Up @@ -274,6 +275,7 @@ async fn main() -> color_eyre::Result<()> {
CliCommand::ResourcePool(cmd) => cmd.dispatch(ctx).await?,
CliCommand::RouteServer(cmd) => cmd.dispatch(ctx).await?,
CliCommand::ScoutStream(cmd) => cmd.dispatch(ctx).await?,
CliCommand::Secrets(cmd) => cmd.dispatch(ctx).await?,
CliCommand::Set(cmd) => cmd.dispatch(ctx).await?,
CliCommand::Ssh(cmd) => cmd.dispatch(ctx).await?,
CliCommand::SiteExplorer(cmd) => cmd.dispatch(ctx).await?,
Expand Down
31 changes: 31 additions & 0 deletions crates/admin-cli/src/secrets/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

mod re_wrap;

use clap::Parser;

use crate::cfg::dispatch::Dispatch;

#[derive(Parser, Debug, Clone, Dispatch)]
#[clap(rename_all = "kebab_case")]
pub enum Cmd {
#[clap(about = "Re-wrap secret DEKs to use the \
currently active KEK per routing \
config")]
ReWrap(re_wrap::Args),
}
38 changes: 38 additions & 0 deletions crates/admin-cli/src/secrets/re_wrap/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

use clap::Parser;

#[derive(Parser, Debug, Clone)]
#[command(after_long_help = "\
EXAMPLES:

Re-wrap every credential whose KEK no longer matches the routing config
(run this after rotating a key in [secrets.routing]):
$ nico-admin-cli secrets re-wrap

Use a smaller batch size to lighten load on an external KMS:
$ nico-admin-cli secrets re-wrap --batch-size 25

")]
pub struct Args {
#[clap(
long,
help = "Rows scanned per batch during the walk. The server applies its own default and limits."
)]
pub batch_size: Option<u32>,
}
43 changes: 43 additions & 0 deletions crates/admin-cli/src/secrets/re_wrap/cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

use crate::errors::CarbideCliResult;

use crate::rpc::ApiClient;

pub async fn re_wrap(api_client: &ApiClient, batch_size: Option<u32>) -> CarbideCliResult<()> {
let request = ::rpc::forge::ReWrapSecretsRequest { batch_size };

let resp = api_client.0.re_wrap_secrets(request).await?;

println!(
"Re-wrap complete: {} re-wrapped, {} already current",
resp.re_wrapped, resp.already_current
);
if resp.stale_remaining == 0 {
println!(
"No rows remain on KEKs outside the routing config; unrouted KEKs can be retired."
);
} else {
println!(
"{} rows are still wrapped by KEKs outside the routing config -- \
concurrent writers likely landed rows mid-walk; run re-wrap again.",
resp.stale_remaining
);
}
Ok(())
}
31 changes: 31 additions & 0 deletions crates/admin-cli/src/secrets/re_wrap/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

pub mod args;
mod cmd;

use crate::errors::CarbideCliResult;
pub use args::Args;

use crate::cfg::run::Run;
use crate::cfg::runtime::RuntimeContext;

impl Run for Args {
async fn run(self, ctx: &mut RuntimeContext) -> CarbideCliResult<()> {
cmd::re_wrap(&ctx.api_client, self.batch_size).await
}
}
3 changes: 3 additions & 0 deletions crates/api-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ carbide-ib-fabric = { path = "../ib-fabric" }
carbide-ib-partition-controller = { path = "../ib-partition-controller" }
carbide-ipmi = { path = "../ipmi" }
carbide-ipxe-renderer = { path = "../ipxe-renderer" }
carbide-kms-provider = { path = "../kms-provider" }
carbide-libmlx = { path = "../libmlx" }
carbide-machine-controller = { path = "../machine-controller" }
carbide-measured-boot = { path = "../measured-boot", features = ["sqlx"] }
Expand Down Expand Up @@ -180,7 +181,9 @@ tracing-subscriber = { workspace = true, features = [
tss-esapi = { workspace = true, optional = true }
url = { workspace = true, features = ["serde"] }
uuid = { workspace = true, features = ["v4", "serde"] }
vaultrs = { workspace = true }
x509-parser = { workspace = true, features = ["verify"] }
zeroize = { workspace = true }

[features]
default = ["linux-build"]
Expand Down
8 changes: 8 additions & 0 deletions crates/api-core/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub struct Api {
pub(crate) metric_emitter: ApiMetricsEmitter,
pub(crate) component_manager: Option<component_manager::component_manager::ComponentManager>,
pub(crate) bms_client: OnceLock<Arc<BmsDsxExchangeHandle>>,
pub(crate) secrets_context: Option<crate::secrets::SecretsContext>,
}

pub(crate) type ScoutStreamType =
Expand Down Expand Up @@ -1444,6 +1445,13 @@ impl Forge for Api {
crate::handlers::credential::delete_credential(self, request).await
}

async fn re_wrap_secrets(
&self,
request: Request<rpc::ReWrapSecretsRequest>,
) -> Result<Response<rpc::ReWrapSecretsResponse>, Status> {
crate::handlers::secrets::re_wrap_secrets(self, request).await
}

/// get_route_servers returns a list of all configured route server
/// entries for all source types.
async fn get_route_servers(
Expand Down
1 change: 1 addition & 0 deletions crates/api-core/src/auth/internal_rbac_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ impl InternalRBACRules {
"UpdateOperatingSystemCachableIpxeTemplateArtifacts",
vec![ForgeAdminCLI],
);
x.perm("ReWrapSecrets", vec![ForgeAdminCLI]);
x.perm("GetIpxeTemplate", vec![ForgeAdminCLI, SiteAgent]);
x.perm("ListIpxeTemplates", vec![ForgeAdminCLI, SiteAgent]);
x.perm("FindRackStateHistories", vec![ForgeAdminCLI, Machineatron]);
Expand Down
Loading