use std::path::Path; use crate::error::AgentError; /// Ensure the SSH key pair exists at the given path, generating it if missing. /// Returns the public key contents. pub fn ensure_ssh_key(key_path: &str) -> Result { let private_path = Path::new(key_path); let public_path = private_path.with_extension("pub"); if private_path.exists() && public_path.exists() { return std::fs::read_to_string(&public_path) .map_err(|e| AgentError::Config(format!("Failed to read SSH public key: {e}"))); } // Create parent directory if let Some(parent) = private_path.parent() { std::fs::create_dir_all(parent)?; } // Generate ed25519 key pair using ssh-keygen let output = std::process::Command::new("ssh-keygen") .args([ "-t", "ed25519", "-f", key_path, "-N", "", // no passphrase "-C", "compliance-scanner-agent", ]) .output() .map_err(|e| AgentError::Config(format!("Failed to run ssh-keygen: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(AgentError::Config(format!("ssh-keygen failed: {stderr}"))); } // Set correct permissions #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; std::fs::set_permissions(private_path, std::fs::Permissions::from_mode(0o600))?; } let public_key = std::fs::read_to_string(&public_path) .map_err(|e| AgentError::Config(format!("Failed to read generated SSH public key: {e}")))?; tracing::info!("Generated new SSH key pair at {key_path}"); Ok(public_key) }