use anyhow::{anyhow, Result};
use tracing::{info, warn};
use crate::vault::autounseal;
use crate::vault::common::{check_vault_status, wait_for_vault_unseal};
use crate::vault::init::{init_vault, unseal_root_vault, InitResult};
use crate::vault::transit;
#[derive(Debug, Clone)]
pub struct RootSetupConfig {
pub root_addr: String,
pub secret_shares: u8,
pub secret_threshold: u8,
pub key_name: String,
pub mode: String,
pub output_file: Option<String>,
}
#[derive(Debug, Clone)]
pub struct RootSetupResult {
pub root_init: InitResult,
pub unwrapped_token: String,
}
pub async fn setup_root_vault(config: RootSetupConfig) -> Result<RootSetupResult> {
let RootSetupConfig {
root_addr,
secret_shares,
secret_threshold,
key_name,
mode,
output_file,
} = config;
info!(
"Checking if root vault at {} is already initialized...",
root_addr
);
let status = check_vault_status(&root_addr).await?;
let root_init = if !status.initialized {
info!("Root vault not initialized; proceeding with init...");
let init_result = init_vault(
&root_addr,
secret_shares,
secret_threshold,
None, None, )
.await?;
info!(
"Root vault init done. Root token = {}",
init_result.root_token
);
let unseal_res = unseal_root_vault(&root_addr, init_result.keys.clone()).await?;
if unseal_res.sealed {
warn!(
"Vault still sealed after unseal attempts; progress={}",
unseal_res.progress
);
}
init_result
} else {
info!("Root vault is already initialized. Provide the root token.");
return Err(anyhow!(
"Root vault is already initialized; please provide root token."
));
};
wait_for_vault_unseal(&root_addr, std::time::Duration::from_secs(60)).await?;
info!("Root vault is unsealed and ready.");
info!("Setting up transit auto-unseal on root vault...");
autounseal::setup_transit_autounseal(&root_addr, &root_init.root_token, &key_name).await?;
info!("Transit engine + unseal policy set up successfully.");
let wrap_ttl = "300s";
let wrapped_token = transit::generate_wrapped_transit_token(
&root_addr,
&root_init.root_token,
"autounseal", wrap_ttl,
)
.await?;
let unwrapped_token = autounseal::unwrap_token(&root_addr, &wrapped_token).await?;
info!(
"Successfully unwrapped the transit token for sub vault = {}",
unwrapped_token
);
if let Some(path) = output_file {
use std::fs;
let mut out = String::new();
out.push_str("# Root Vault Setup:\n");
out.push_str(&format!("Root token: {}\n", root_init.root_token));
if !root_init.keys.is_empty() {
out.push_str("Unseal keys:\n");
for k in &root_init.keys {
out.push_str(&format!(" {}\n", k));
}
}
out.push_str("\n# Sub Vault unwrapped token:\n");
out.push_str(&format!(" {}\n", unwrapped_token));
fs::write(&path, out)?;
info!("Wrote root credentials + unwrapped sub token to {}", path);
}
if mode == "local" {
info!("Local mode: You must now update your sub-vault container env with the unwrapped token.");
info!(
"Example: export VAULT_TOKEN={} && docker-compose up -d sub-vault",
unwrapped_token
);
} else {
info!("Remote mode: copy/paste the token into your helm chart or environment for the sub vault");
}
Ok(RootSetupResult {
root_init,
unwrapped_token,
})
}