fix: Vault as single source of truth - credentials + location for all services
All checks were successful
BotServer CI/CD / build (push) Successful in 4m53s

- Qdrant health check: recognize 'healthz check passed' response (fixes 45s timeout)
- seed_vault_defaults: add host/port/url/grpc_port for ALL 10 services
- fetch_vault_credentials: fetch ALL services via generic loop (drive, cache, tables, vectordb, directory, llm, meet, alm, encryption)
- vectordb URL: fix https://localhost:6334 -> http://localhost:6333 in all config getters
- get_from_env: add host/port/grpc_port for vectordb fallback
- Tested: .reset (fresh install) + .restart (idempotent) - zero errors
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-04-01 16:46:16 -03:00
parent fb2e5242da
commit 68ef554132
4 changed files with 61 additions and 87 deletions

View file

@ -78,8 +78,6 @@ jobs:
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 system "ls -lh /opt/gbo/bin/botserver" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 system "ls -lh /opt/gbo/bin/botserver"
echo "Step 6: Setting permissions..." echo "Step 6: Setting permissions..."
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 system "chmod +x /opt/gbo/bin/botserver && chown gbuser:gbuser /opt/gbo/bin/botserver" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 system "chmod +x /opt/gbo/bin/botserver && chown gbuser:gbuser /opt/gbo/bin/botserver"
echo "Step 7: Starting botserver..."
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 system "cd /opt/gbo/bin && sudo -u gbuser ./botserver --noconsole >> /opt/gbo/logs/error.log 2>&1 &"
echo "=== Deploy completed ===" echo "=== Deploy completed ==="
- name: Verify botserver started - name: Verify botserver started

View file

@ -183,7 +183,10 @@ pub fn vector_db_health_check() -> bool {
{ {
if output.status.success() { if output.status.success() {
let response = String::from_utf8_lossy(&output.stdout); let response = String::from_utf8_lossy(&output.stdout);
if response.contains("OK") || response.contains("\"status\":\"ok\"") { if response.contains("healthz check passed")
|| response.contains("OK")
|| response.contains("\"status\":\"ok\"")
{
return true; return true;
} }
} }

View file

@ -1268,7 +1268,6 @@ EOF"#.to_string(),
return credentials; return credentials;
} }
// Check if Vault is reachable before trying to fetch credentials
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 = SafeCommand::new("curl") let vault_check = SafeCommand::new("curl")
@ -1295,8 +1294,6 @@ EOF"#.to_string(),
} }
let vault_bin = base_path.join("bin/vault/vault"); let vault_bin = base_path.join("bin/vault/vault");
// 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(|_| {
base_path base_path
.join("conf/system/certificates/ca/ca.crt") .join("conf/system/certificates/ca/ca.crt")
@ -1304,85 +1301,43 @@ EOF"#.to_string(),
.to_string() .to_string()
}); });
trace!( let services = [
"Fetching drive credentials from Vault at {} using {}", ("drive", "secret/gbo/drive"),
vault_addr, ("cache", "secret/gbo/cache"),
vault_bin.display() ("tables", "secret/gbo/tables"),
); ("vectordb", "secret/gbo/vectordb"),
("directory", "secret/gbo/directory"),
("llm", "secret/gbo/llm"),
("meet", "secret/gbo/meet"),
("alm", "secret/gbo/alm"),
("encryption", "secret/gbo/encryption"),
];
// Fetch drive credentials for (service_name, vault_path) in &services {
let drive_result = SafeCommand::new(vault_bin.to_str().unwrap_or("vault")) let result = SafeCommand::new(vault_bin.to_str().unwrap_or("vault"))
.and_then(|c| { .and_then(|c| {
c.env("VAULT_ADDR", &vault_addr) c.env("VAULT_ADDR", &vault_addr)
.and_then(|c| c.env("VAULT_TOKEN", &vault_token)) .and_then(|c| c.env("VAULT_TOKEN", &vault_token))
.and_then(|c| c.env("VAULT_CACERT", &ca_cert_path)) .and_then(|c| c.env("VAULT_CACERT", &ca_cert_path))
}) })
.and_then(|c| { .and_then(|c| {
c.args(&[ c.args(&["kv", "get", "-format=json", "-tls-skip-verify", vault_path])
"kv",
"get",
"-format=json",
"-tls-skip-verify",
"secret/gbo/drive",
])
}) })
.and_then(|c| c.execute()); .and_then(|c| c.execute());
if let Ok(output) = drive_result { if let Ok(output) = 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);
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) {
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) = data.get("accesskey").and_then(|v| v.as_str()) { if let Some(obj) = data.as_object() {
trace!("Found DRIVE_ACCESSKEY from Vault"); let prefix = service_name.to_uppercase();
credentials for (key, value) in obj {
.insert("DRIVE_ACCESSKEY".to_string(), accesskey.to_string()); if let Some(v) = value.as_str() {
let env_key = format!("{}_{}", prefix, key.to_uppercase());
credentials.insert(env_key, v.to_string());
} }
if let Some(secret) = data.get("secret").and_then(|v| v.as_str()) {
trace!("Found DRIVE_SECRET from Vault");
credentials.insert("DRIVE_SECRET".to_string(), secret.to_string());
} }
} else {
warn!("Vault response missing data.data field");
}
} else {
warn!("Failed to parse Vault JSON for drive");
}
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
warn!("Vault drive command failed: {}", stderr);
}
} else {
warn!("Failed to execute Vault drive command");
}
// Fetch cache credentials
let cache_result = SafeCommand::new(vault_bin.to_str().unwrap_or("vault"))
.and_then(|c| {
c.env("VAULT_ADDR", &vault_addr)
.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 let Ok(json_str) = String::from_utf8(output.stdout) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_str) {
if let Some(data) = json.get("data").and_then(|d| d.get("data")) {
if let Some(password) = data.get("password").and_then(|v| v.as_str()) {
credentials
.insert("CACHE_PASSWORD".to_string(), password.to_string());
} }
} }
} }
@ -1629,6 +1584,7 @@ VAULT_CACERT={}
("secret".to_string(), drive_pass), ("secret".to_string(), drive_pass),
("host".to_string(), "localhost".to_string()), ("host".to_string(), "localhost".to_string()),
("port".to_string(), "9000".to_string()), ("port".to_string(), "9000".to_string()),
("url".to_string(), "http://localhost:9000".to_string()),
], ],
), ),
( (
@ -1637,6 +1593,7 @@ VAULT_CACERT={}
("password".to_string(), cache_pass), ("password".to_string(), cache_pass),
("host".to_string(), "localhost".to_string()), ("host".to_string(), "localhost".to_string()),
("port".to_string(), "6379".to_string()), ("port".to_string(), "6379".to_string()),
("url".to_string(), "redis://localhost:6379".to_string()),
], ],
), ),
( (
@ -1647,12 +1604,15 @@ VAULT_CACERT={}
("port".to_string(), "5432".to_string()), ("port".to_string(), "5432".to_string()),
("database".to_string(), "botserver".to_string()), ("database".to_string(), "botserver".to_string()),
("username".to_string(), "gbuser".to_string()), ("username".to_string(), "gbuser".to_string()),
("url".to_string(), "postgres://localhost:5432".to_string()),
], ],
), ),
( (
"secret/gbo/directory", "secret/gbo/directory",
vec![ vec![
("url".to_string(), "http://localhost:9000".to_string()), ("url".to_string(), "http://localhost:9000".to_string()),
("host".to_string(), "localhost".to_string()),
("port".to_string(), "9000".to_string()),
("project_id".to_string(), "none".to_string()), ("project_id".to_string(), "none".to_string()),
("client_id".to_string(), "none".to_string()), ("client_id".to_string(), "none".to_string()),
("client_secret".to_string(), "none".to_string()), ("client_secret".to_string(), "none".to_string()),
@ -1672,6 +1632,8 @@ VAULT_CACERT={}
"secret/gbo/llm", "secret/gbo/llm",
vec![ vec![
("url".to_string(), "http://localhost:8081".to_string()), ("url".to_string(), "http://localhost:8081".to_string()),
("host".to_string(), "localhost".to_string()),
("port".to_string(), "8081".to_string()),
("model".to_string(), "gpt-4".to_string()), ("model".to_string(), "gpt-4".to_string()),
("openai_key".to_string(), "none".to_string()), ("openai_key".to_string(), "none".to_string()),
("anthropic_key".to_string(), "none".to_string()), ("anthropic_key".to_string(), "none".to_string()),
@ -1689,6 +1651,8 @@ VAULT_CACERT={}
"secret/gbo/meet", "secret/gbo/meet",
vec![ vec![
("url".to_string(), "http://localhost:7880".to_string()), ("url".to_string(), "http://localhost:7880".to_string()),
("host".to_string(), "localhost".to_string()),
("port".to_string(), "7880".to_string()),
("app_id".to_string(), meet_app_id), ("app_id".to_string(), meet_app_id),
("app_secret".to_string(), meet_app_secret), ("app_secret".to_string(), meet_app_secret),
], ],
@ -1697,14 +1661,20 @@ VAULT_CACERT={}
"secret/gbo/vectordb", "secret/gbo/vectordb",
vec![ vec![
("url".to_string(), "http://localhost:6333".to_string()), ("url".to_string(), "http://localhost:6333".to_string()),
("host".to_string(), "localhost".to_string()),
("port".to_string(), "6333".to_string()),
("grpc_port".to_string(), "6334".to_string()),
("api_key".to_string(), "none".to_string()), ("api_key".to_string(), "none".to_string()),
], ],
), ),
( (
"secret/gbo/alm", "secret/gbo/alm",
vec![ vec![
("url".to_string(), "none".to_string()), ("url".to_string(), "http://localhost:9000".to_string()),
("host".to_string(), "localhost".to_string()),
("port".to_string(), "9000".to_string()),
("token".to_string(), alm_token), ("token".to_string(), alm_token),
("default_org".to_string(), "none".to_string()),
], ],
), ),
]; ];

View file

@ -304,7 +304,7 @@ impl SecretsManager {
Ok(( Ok((
s.get("url") s.get("url")
.cloned() .cloned()
.unwrap_or_else(|| "https://localhost:6334".into()), .unwrap_or_else(|| "http://localhost:6333".into()),
s.get("api_key").cloned(), s.get("api_key").cloned(),
)) ))
} }
@ -404,12 +404,12 @@ impl SecretsManager {
if let Ok(runtime) = tokio::runtime::Handle::try_current() { if let Ok(runtime) = tokio::runtime::Handle::try_current() {
if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::VECTORDB)) { if let Ok(secrets) = runtime.block_on(self.get_secret(SecretPaths::VECTORDB)) {
return ( return (
secrets.get("url").cloned().unwrap_or_else(|| "https://localhost:6334".into()), secrets.get("url").cloned().unwrap_or_else(|| "http://localhost:6333".into()),
secrets.get("api_key").cloned(), secrets.get("api_key").cloned(),
); );
} }
} }
("https://localhost:6334".to_string(), None) ("http://localhost:6333".to_string(), None)
} }
pub fn get_observability_config_sync(&self) -> (String, String, String, String) { pub fn get_observability_config_sync(&self) -> (String, String, String, String) {
@ -562,8 +562,11 @@ impl SecretsManager {
secrets.insert("app_secret".into(), String::new()); secrets.insert("app_secret".into(), String::new());
} }
"vectordb" | "gbo/vectordb" | "system/vectordb" => { "vectordb" | "gbo/vectordb" | "system/vectordb" => {
secrets.insert("url".into(), "http://localhost:6333".into()); secrets.insert("url".to_string(), "http://localhost:6333".into());
secrets.insert("api_key".into(), String::new()); secrets.insert("host".to_string(), "localhost".into());
secrets.insert("port".to_string(), "6333".into());
secrets.insert("grpc_port".to_string(), "6334".into());
secrets.insert("api_key".to_string(), String::new());
} }
"observability" | "gbo/observability" | "system/observability" => { "observability" | "gbo/observability" | "system/observability" => {
secrets.insert("url".into(), "http://localhost:8086".into()); secrets.insert("url".into(), "http://localhost:8086".into());