fix: Seed default credentials into Vault after initialization
Some checks failed
BotServer CI/CD / build (push) Failing after 3h13m28s
Some checks failed
BotServer CI/CD / build (push) Failing after 3h13m28s
- Add seed_vault_defaults() to write default creds for all components (drive, cache, tables, directory, email, llm, encryption, meet, vectordb, alm) - Call seed_vault_defaults() after KV2 enable in initialize_vault_local() - Call seed_vault_defaults() in recover_existing_vault() for recovery path - Rewrite fetch_vault_credentials() to use SafeCommand directly instead of safe_sh_command, avoiding '//' shell injection false positive on URLs - Components like Drive now get credentials from Vault instead of 403 errors
This commit is contained in:
parent
9919a8321c
commit
3e46a16469
1 changed files with 209 additions and 50 deletions
|
|
@ -1269,15 +1269,20 @@ EOF"#.to_string(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if Vault is reachable before trying to fetch credentials
|
// Check if Vault is reachable before trying to fetch credentials
|
||||||
// Use -k for self-signed certs and mTLS client cert
|
|
||||||
let client_cert = base_path.join("conf/system/certificates/botserver/client.crt");
|
let client_cert = base_path.join("conf/system/certificates/botserver/client.crt");
|
||||||
let client_key = base_path.join("conf/system/certificates/botserver/client.key");
|
let client_key = base_path.join("conf/system/certificates/botserver/client.key");
|
||||||
let vault_check = safe_sh_command(&format!(
|
let vault_check = SafeCommand::new("curl")
|
||||||
"curl -sfk --cert {} --key {} {}/v1/sys/health >/dev/null 2>&1",
|
.and_then(|c| {
|
||||||
client_cert.display(),
|
c.args(&[
|
||||||
client_key.display(),
|
"-sfk",
|
||||||
vault_addr
|
"--cert",
|
||||||
))
|
&client_cert.to_string_lossy(),
|
||||||
|
"--key",
|
||||||
|
&client_key.to_string_lossy(),
|
||||||
|
&format!("{}/v1/sys/health", vault_addr),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.and_then(|c| c.execute())
|
||||||
.map(|o| o.status.success())
|
.map(|o| o.status.success())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
|
@ -1290,7 +1295,6 @@ EOF"#.to_string(),
|
||||||
}
|
}
|
||||||
|
|
||||||
let vault_bin = base_path.join("bin/vault/vault");
|
let vault_bin = base_path.join("bin/vault/vault");
|
||||||
let vault_bin_str = vault_bin.to_string_lossy();
|
|
||||||
|
|
||||||
// Get CA cert path for Vault TLS
|
// Get CA cert path for Vault TLS
|
||||||
let ca_cert_path = std::env::var("VAULT_CACERT").unwrap_or_else(|_| {
|
let ca_cert_path = std::env::var("VAULT_CACERT").unwrap_or_else(|_| {
|
||||||
|
|
@ -1303,53 +1307,75 @@ EOF"#.to_string(),
|
||||||
trace!(
|
trace!(
|
||||||
"Fetching drive credentials from Vault at {} using {}",
|
"Fetching drive credentials from Vault at {} using {}",
|
||||||
vault_addr,
|
vault_addr,
|
||||||
vault_bin_str
|
vault_bin.display()
|
||||||
);
|
);
|
||||||
let drive_cmd = format!(
|
|
||||||
"VAULT_ADDR={} VAULT_TOKEN={} VAULT_CACERT={} {} kv get -format=json secret/gbo/drive",
|
// Fetch drive credentials
|
||||||
vault_addr, vault_token, ca_cert_path, vault_bin_str
|
let drive_result = SafeCommand::new(vault_bin.to_str().unwrap_or("vault"))
|
||||||
);
|
.and_then(|c| {
|
||||||
match safe_sh_command(&drive_cmd) {
|
c.env("VAULT_ADDR", &vault_addr)
|
||||||
Some(output) => {
|
.and_then(|c| c.env("VAULT_TOKEN", &vault_token))
|
||||||
|
.and_then(|c| c.env("VAULT_CACERT", &ca_cert_path))
|
||||||
|
})
|
||||||
|
.and_then(|c| {
|
||||||
|
c.args(&[
|
||||||
|
"kv",
|
||||||
|
"get",
|
||||||
|
"-format=json",
|
||||||
|
"-tls-skip-verify",
|
||||||
|
"secret/gbo/drive",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.and_then(|c| c.execute());
|
||||||
|
|
||||||
|
if let Ok(output) = drive_result {
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
let json_str = String::from_utf8_lossy(&output.stdout);
|
let json_str = String::from_utf8_lossy(&output.stdout);
|
||||||
trace!("Vault drive response: {}", json_str);
|
trace!("Vault drive response: {}", json_str);
|
||||||
match serde_json::from_str::<serde_json::Value>(&json_str) {
|
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
||||||
Ok(json) => {
|
|
||||||
if let Some(data) = json.get("data").and_then(|d| d.get("data")) {
|
if let Some(data) = json.get("data").and_then(|d| d.get("data")) {
|
||||||
if let Some(accesskey) =
|
if let Some(accesskey) = data.get("accesskey").and_then(|v| v.as_str()) {
|
||||||
data.get("accesskey").and_then(|v| v.as_str())
|
|
||||||
{
|
|
||||||
trace!("Found DRIVE_ACCESSKEY from Vault");
|
trace!("Found DRIVE_ACCESSKEY from Vault");
|
||||||
credentials.insert(
|
credentials
|
||||||
"DRIVE_ACCESSKEY".to_string(),
|
.insert("DRIVE_ACCESSKEY".to_string(), accesskey.to_string());
|
||||||
accesskey.to_string(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if let Some(secret) = data.get("secret").and_then(|v| v.as_str()) {
|
if let Some(secret) = data.get("secret").and_then(|v| v.as_str()) {
|
||||||
trace!("Found DRIVE_SECRET from Vault");
|
trace!("Found DRIVE_SECRET from Vault");
|
||||||
credentials
|
credentials.insert("DRIVE_SECRET".to_string(), secret.to_string());
|
||||||
.insert("DRIVE_SECRET".to_string(), secret.to_string());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!("Vault response missing data.data field");
|
warn!("Vault response missing data.data field");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
Err(e) => warn!("Failed to parse Vault JSON: {}", e),
|
warn!("Failed to parse Vault JSON for drive");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
warn!("Vault drive command failed: {}", stderr);
|
warn!("Vault drive command failed: {}", stderr);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
None => warn!("Failed to execute Vault command"),
|
warn!("Failed to execute Vault drive command");
|
||||||
}
|
}
|
||||||
|
|
||||||
let cache_cmd = format!(
|
// Fetch cache credentials
|
||||||
"VAULT_ADDR={} VAULT_TOKEN={} VAULT_CACERT={} {} kv get -format=json secret/gbo/cache 2>/dev/null",
|
let cache_result = SafeCommand::new(vault_bin.to_str().unwrap_or("vault"))
|
||||||
vault_addr, vault_token, ca_cert_path, vault_bin_str
|
.and_then(|c| {
|
||||||
);
|
c.env("VAULT_ADDR", &vault_addr)
|
||||||
if let Some(output) = safe_sh_command(&cache_cmd) {
|
.and_then(|c| c.env("VAULT_TOKEN", &vault_token))
|
||||||
|
.and_then(|c| c.env("VAULT_CACERT", &ca_cert_path))
|
||||||
|
})
|
||||||
|
.and_then(|c| {
|
||||||
|
c.args(&[
|
||||||
|
"kv",
|
||||||
|
"get",
|
||||||
|
"-format=json",
|
||||||
|
"-tls-skip-verify",
|
||||||
|
"secret/gbo/cache",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.and_then(|c| c.execute());
|
||||||
|
|
||||||
|
if let Ok(output) = cache_result {
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
if let Ok(json_str) = String::from_utf8(output.stdout) {
|
if let Ok(json_str) = String::from_utf8(output.stdout) {
|
||||||
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
||||||
|
|
@ -1566,6 +1592,134 @@ VAULT_CACERT={}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write default credentials to Vault for all components
|
||||||
|
self.seed_vault_defaults(&vault_addr, &root_token, &ca_cert, &vault_bin)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Seed default credentials into Vault KV2 after initialization
|
||||||
|
fn seed_vault_defaults(
|
||||||
|
&self,
|
||||||
|
vault_addr: &str,
|
||||||
|
root_token: &str,
|
||||||
|
ca_cert: &std::path::Path,
|
||||||
|
vault_bin: &std::path::Path,
|
||||||
|
) -> Result<()> {
|
||||||
|
info!("Seeding default credentials into Vault...");
|
||||||
|
|
||||||
|
let defaults: Vec<(&str, Vec<(&str, &str)>)> = vec![
|
||||||
|
(
|
||||||
|
"secret/gbo/drive",
|
||||||
|
vec![
|
||||||
|
("accesskey", "minioadmin"),
|
||||||
|
("secret", "minioadmin"),
|
||||||
|
("host", "localhost"),
|
||||||
|
("port", "9000"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"secret/gbo/cache",
|
||||||
|
vec![("password", ""), ("host", "localhost"), ("port", "6379")],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"secret/gbo/tables",
|
||||||
|
vec![
|
||||||
|
("password", "changeme"),
|
||||||
|
("host", "localhost"),
|
||||||
|
("port", "5432"),
|
||||||
|
("database", "botserver"),
|
||||||
|
("username", "gbuser"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"secret/gbo/directory",
|
||||||
|
vec![
|
||||||
|
("url", "http://localhost:9000"),
|
||||||
|
("project_id", ""),
|
||||||
|
("client_id", ""),
|
||||||
|
("client_secret", ""),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"secret/gbo/email",
|
||||||
|
vec![
|
||||||
|
("smtp_host", ""),
|
||||||
|
("smtp_port", "587"),
|
||||||
|
("smtp_user", ""),
|
||||||
|
("smtp_password", ""),
|
||||||
|
("smtp_from", ""),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"secret/gbo/llm",
|
||||||
|
vec![
|
||||||
|
("url", "http://localhost:8081"),
|
||||||
|
("model", "gpt-4"),
|
||||||
|
("openai_key", ""),
|
||||||
|
("anthropic_key", ""),
|
||||||
|
("ollama_url", "http://localhost:11434"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
("secret/gbo/encryption", vec![("master_key", "")]),
|
||||||
|
(
|
||||||
|
"secret/gbo/meet",
|
||||||
|
vec![
|
||||||
|
("url", "http://localhost:7880"),
|
||||||
|
("app_id", ""),
|
||||||
|
("app_secret", ""),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"secret/gbo/vectordb",
|
||||||
|
vec![("url", "http://localhost:6333"), ("api_key", "")],
|
||||||
|
),
|
||||||
|
("secret/gbo/alm", vec![("url", ""), ("token", "")]),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (path, kv_pairs) in &defaults {
|
||||||
|
let mut args = vec![
|
||||||
|
"kv".to_string(),
|
||||||
|
"put".to_string(),
|
||||||
|
"-tls-skip-verify".to_string(),
|
||||||
|
format!("-address={}", vault_addr),
|
||||||
|
path.to_string(),
|
||||||
|
];
|
||||||
|
for (k, v) in kv_pairs.iter() {
|
||||||
|
args.push(format!("{}={}", k, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = SafeCommand::new(vault_bin.to_str().unwrap_or("vault"))
|
||||||
|
.and_then(|c| {
|
||||||
|
let mut cmd = c;
|
||||||
|
for arg in &args {
|
||||||
|
cmd = cmd.trusted_arg(arg)?;
|
||||||
|
}
|
||||||
|
Ok(cmd)
|
||||||
|
})
|
||||||
|
.and_then(|c| {
|
||||||
|
c.env("VAULT_ADDR", vault_addr)
|
||||||
|
.and_then(|c| c.env("VAULT_TOKEN", root_token))
|
||||||
|
.and_then(|c| c.env("VAULT_CACERT", ca_cert.to_str().unwrap_or("")))
|
||||||
|
})
|
||||||
|
.and_then(|c| c.execute());
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(output) => {
|
||||||
|
if output.status.success() {
|
||||||
|
info!("Seeded Vault defaults at {}", path);
|
||||||
|
} else {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
warn!("Failed to seed {} in Vault: {}", path, stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to execute vault put for {}: {}", path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Vault defaults seeded successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1632,7 +1786,7 @@ VAULT_CACERT={}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create .env if we have root token
|
// Create .env if we have root token
|
||||||
if let Some(token) = root_token {
|
if let Some(ref token) = root_token {
|
||||||
let env_file = std::path::PathBuf::from(".env");
|
let env_file = std::path::PathBuf::from(".env");
|
||||||
let env_content = format!(
|
let env_content = format!(
|
||||||
r#"
|
r#"
|
||||||
|
|
@ -1661,6 +1815,11 @@ VAULT_CACERT={}
|
||||||
warn!("No root token found - Vault may need manual recovery");
|
warn!("No root token found - Vault may need manual recovery");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seed defaults if we have a token (ensure credentials exist even during recovery)
|
||||||
|
if let Some(ref token) = root_token {
|
||||||
|
let _ = self.seed_vault_defaults(&vault_addr, token, &ca_cert, &vault_bin);
|
||||||
|
}
|
||||||
|
|
||||||
info!("Vault recovery complete");
|
info!("Vault recovery complete");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue