feat: add setup-env command to generate .env from vault container
This commit is contained in:
parent
dfe5162f66
commit
7ef1efa047
2 changed files with 129 additions and 0 deletions
|
|
@ -121,6 +121,25 @@ pub async fn run() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"setup-env" => {
|
||||||
|
let vault_container = args.get(2).map(|s| s.as_str()).unwrap_or("vault");
|
||||||
|
let container_name = vault_container.to_string();
|
||||||
|
|
||||||
|
println!("* Generating .env from vault container: {}", container_name);
|
||||||
|
|
||||||
|
match PackageManager::generate_env_from_vault(&container_name) {
|
||||||
|
Ok(env_vars) => {
|
||||||
|
println!("* Successfully generated .env from vault");
|
||||||
|
println!("\nGenerated configuration:");
|
||||||
|
println!("{}", env_vars);
|
||||||
|
println!("\nYou can now start botserver with: botserver start");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("x Failed to generate .env: {}", e);
|
||||||
|
return Err(anyhow::anyhow!("Setup env failed: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"remove" => {
|
"remove" => {
|
||||||
if args.len() < 3 {
|
if args.len() < 3 {
|
||||||
eprintln!("Usage: botserver remove <component> [--container] [--tenant <name>]");
|
eprintln!("Usage: botserver remove <component> [--container] [--tenant <name>]");
|
||||||
|
|
@ -289,6 +308,7 @@ fn print_usage() {
|
||||||
println!(" start Start all installed components");
|
println!(" start Start all installed components");
|
||||||
println!(" stop Stop all components");
|
println!(" stop Stop all components");
|
||||||
println!(" restart Restart all components");
|
println!(" restart Restart all components");
|
||||||
|
println!(" setup-env Generate .env from running vault container");
|
||||||
println!(" vault <subcommand> Manage Vault secrets");
|
println!(" vault <subcommand> Manage Vault secrets");
|
||||||
println!(" rotate-secret <comp> Rotate a component's credentials");
|
println!(" rotate-secret <comp> Rotate a component's credentials");
|
||||||
println!(" (tables, drive, cache, email, directory, encryption, jwt)");
|
println!(" (tables, drive, cache, email, directory, encryption, jwt)");
|
||||||
|
|
@ -299,6 +319,7 @@ fn print_usage() {
|
||||||
println!();
|
println!();
|
||||||
println!("Options:");
|
println!("Options:");
|
||||||
println!(" --container Use container mode (LXC)");
|
println!(" --container Use container mode (LXC)");
|
||||||
|
println!(" --container-only Create container only, don't complete installation");
|
||||||
println!(" --tenant <name> Specify tenant name");
|
println!(" --tenant <name> Specify tenant name");
|
||||||
println!();
|
println!();
|
||||||
println!("Security Protection (requires root):");
|
println!("Security Protection (requires root):");
|
||||||
|
|
|
||||||
|
|
@ -525,6 +525,114 @@ impl PackageManager {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generate_env_from_vault(container_name: &str) -> Result<String> {
|
||||||
|
let container_ip = Self::get_container_ip(container_name)?;
|
||||||
|
|
||||||
|
info!("Generating .env from vault at {}", container_ip);
|
||||||
|
|
||||||
|
let output = safe_lxc(&[
|
||||||
|
"exec",
|
||||||
|
container_name,
|
||||||
|
"--",
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"cat /opt/gbo/data/core/raft/raft_state 2>/dev/null || echo 'not_initialized'",
|
||||||
|
]);
|
||||||
|
|
||||||
|
let initialized = match output {
|
||||||
|
Some(o) => o.status.success(),
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !initialized {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Vault in container {} is not initialized. Please initialize it first with: \
|
||||||
|
lxc exec {} -- /opt/gbo/bin/vault operator init -key-shares=1 -key-threshold=1",
|
||||||
|
container_name, container_name
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let unseal_output = safe_lxc(&[
|
||||||
|
"exec",
|
||||||
|
container_name,
|
||||||
|
"--",
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"VAULT_ADDR=http://127.0.0.1:8200 vault status -format=json",
|
||||||
|
]);
|
||||||
|
|
||||||
|
let sealed = match unseal_output {
|
||||||
|
Some(o) if o.status.success() => {
|
||||||
|
let status: serde_json::Value = serde_json::from_str(
|
||||||
|
&String::from_utf8_lossy(&o.stdout)
|
||||||
|
).unwrap_or_default();
|
||||||
|
status["sealed"].as_bool().unwrap_or(true)
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if sealed {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Vault in container {} is sealed. Please unseal it first with: \
|
||||||
|
lxc exec {} -- /opt/gbo/bin/vault operator unseal <key>",
|
||||||
|
container_name, container_name
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let token_output = safe_lxc(&[
|
||||||
|
"exec",
|
||||||
|
container_name,
|
||||||
|
"--",
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
"cat /root/.vault-token 2>/dev/null || echo ''",
|
||||||
|
]);
|
||||||
|
|
||||||
|
let root_token = match token_output {
|
||||||
|
Some(o) => String::from_utf8_lossy(&o.stdout).trim().to_string(),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if root_token.is_empty() {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Could not find root token in vault container. Please ensure vault is properly initialized."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let env_content = format!(
|
||||||
|
"# Vault Configuration (auto-generated)\nVAULT_ADDR=http://{}:8200\nVAULT_TOKEN={}\n",
|
||||||
|
container_ip, root_token
|
||||||
|
);
|
||||||
|
|
||||||
|
let env_file = PathBuf::from(".env");
|
||||||
|
if env_file.exists() {
|
||||||
|
let existing = std::fs::read_to_string(&env_file)?;
|
||||||
|
|
||||||
|
if existing.contains("VAULT_ADDR=") {
|
||||||
|
info!(".env already contains VAULT_ADDR, updating...");
|
||||||
|
let updated: String = existing
|
||||||
|
.lines()
|
||||||
|
.filter(|line| !line.starts_with("VAULT_ADDR=") && !line.starts_with("VAULT_TOKEN="))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
std::fs::write(&env_file, format!("{}\n{}", updated.trim(), env_content))?;
|
||||||
|
} else {
|
||||||
|
let mut file = std::fs::OpenOptions::new().append(true).open(&env_file)?;
|
||||||
|
use std::io::Write;
|
||||||
|
file.write_all(env_content.as_bytes())?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::fs::write(&env_file, env_content.trim_start())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Generated .env with Vault config from container {}", container_name);
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"VAULT_ADDR=http://{}:8200\nVAULT_TOKEN={}",
|
||||||
|
container_ip, root_token
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_connection_info(
|
fn generate_connection_info(
|
||||||
&self,
|
&self,
|
||||||
component: &str,
|
component: &str,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue