Add OpenWRT SSH provisioning and readiness workflow
This commit is contained in:
@@ -43,6 +43,15 @@ pub struct OpenWrtReadiness {
|
||||
free_memory: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct OpenWrtPrepareResult {
|
||||
success: bool,
|
||||
hostname_updated: bool,
|
||||
password_updated: bool,
|
||||
message: String,
|
||||
logs: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn detect_openwrt_router() -> OpenWrtDetectionResult {
|
||||
let candidates = vec![detect_default_gateway(), Some("192.168.1.1".to_string())];
|
||||
@@ -169,6 +178,127 @@ pub fn test_openwrt_ssh(host: String, username: String, password: String) -> Ope
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn prepare_openwrt_router(
|
||||
host: String,
|
||||
username: String,
|
||||
password: String,
|
||||
target_hostname: String,
|
||||
new_password: String
|
||||
) -> OpenWrtPrepareResult {
|
||||
if target_hostname.trim().is_empty() {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: "Hostname inválido.".to_string(),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
if new_password.trim().is_empty() {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: "Password root inválida.".to_string(),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
if password.trim().is_empty() {
|
||||
return prepare_openwrt_router_with_system_ssh(
|
||||
host,
|
||||
username,
|
||||
target_hostname,
|
||||
new_password
|
||||
);
|
||||
}
|
||||
|
||||
let address = format!("{}:22", host);
|
||||
|
||||
let socket = match address.parse::<SocketAddr>() {
|
||||
Ok(socket) => socket,
|
||||
Err(err) => {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: format!("Endereço inválido: {}", err),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let tcp = match TcpStream::connect_timeout(&socket, Duration::from_secs(5)) {
|
||||
Ok(stream) => stream,
|
||||
Err(err) => {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: format!("Falha ao ligar por TCP/SSH: {}", err),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let mut session = match Session::new() {
|
||||
Ok(session) => session,
|
||||
Err(err) => {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: format!("Falha ao criar sessão SSH: {}", err),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
session.set_tcp_stream(tcp);
|
||||
|
||||
if let Err(err) = session.handshake() {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: format!("Falha no handshake SSH: {}", err),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
if let Err(err) = session.userauth_password(&username, &password) {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: format!("Falha na autenticação SSH: {}", err),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
let command = build_prepare_command(&target_hostname, &new_password);
|
||||
|
||||
let logs = run_ssh_command(&session, &command).unwrap_or_default();
|
||||
|
||||
let hostname_updated = logs.contains("HOSTNAME_UPDATED");
|
||||
let password_updated = logs.contains("PASSWORD_UPDATED");
|
||||
let success = hostname_updated && password_updated;
|
||||
|
||||
OpenWrtPrepareResult {
|
||||
success,
|
||||
hostname_updated,
|
||||
password_updated,
|
||||
message: if success {
|
||||
"Router preparado com sucesso.".to_string()
|
||||
} else {
|
||||
"Preparação do router incompleta. Verifique os logs.".to_string()
|
||||
},
|
||||
logs,
|
||||
}
|
||||
}
|
||||
|
||||
fn tcp_port_open(host: &str, port: u16) -> bool {
|
||||
let address = format!("{}:{}", host, port);
|
||||
|
||||
@@ -436,3 +566,82 @@ fn extract_prefixed_value(content: &str, prefix: &str) -> Option<String> {
|
||||
.map(|line| line.trim_start_matches(prefix).trim().to_string())
|
||||
.filter(|value| !value.is_empty())
|
||||
}
|
||||
|
||||
fn prepare_openwrt_router_with_system_ssh(
|
||||
host: String,
|
||||
username: String,
|
||||
target_hostname: String,
|
||||
new_password: String
|
||||
) -> OpenWrtPrepareResult {
|
||||
let target = format!("{}@{}", username, host);
|
||||
let command = build_prepare_command(&target_hostname, &new_password);
|
||||
|
||||
let output = Command::new("ssh")
|
||||
.args([
|
||||
"-o",
|
||||
"StrictHostKeyChecking=no",
|
||||
"-o",
|
||||
"UserKnownHostsFile=NUL",
|
||||
"-o",
|
||||
"BatchMode=yes",
|
||||
"-o",
|
||||
"ConnectTimeout=5",
|
||||
&target,
|
||||
&command,
|
||||
])
|
||||
.output();
|
||||
|
||||
let output = match output {
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
return OpenWrtPrepareResult {
|
||||
success: false,
|
||||
hostname_updated: false,
|
||||
password_updated: false,
|
||||
message: format!("Falha ao executar ssh local: {}", err),
|
||||
logs: "".to_string(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
let logs = format!("{}\n{}", stdout, stderr);
|
||||
|
||||
let hostname_updated = logs.contains("HOSTNAME_UPDATED");
|
||||
let password_updated = logs.contains("PASSWORD_UPDATED");
|
||||
let success = output.status.success() && hostname_updated && password_updated;
|
||||
|
||||
OpenWrtPrepareResult {
|
||||
success,
|
||||
hostname_updated,
|
||||
password_updated,
|
||||
message: if success {
|
||||
"Router preparado com sucesso.".to_string()
|
||||
} else {
|
||||
"Preparação do router incompleta. Verifique os logs.".to_string()
|
||||
},
|
||||
logs,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_prepare_command(target_hostname: &str, new_password: &str) -> String {
|
||||
let safe_hostname = shell_escape_single_quotes(target_hostname);
|
||||
let safe_password = shell_escape_single_quotes(new_password);
|
||||
|
||||
format!(
|
||||
"set -e; \
|
||||
uci set system.@system[0].hostname='{hostname}'; \
|
||||
uci commit system; \
|
||||
/etc/init.d/system reload >/dev/null 2>&1 || true; \
|
||||
echo HOSTNAME_UPDATED; \
|
||||
printf '%s\\n%s\\n' '{password}' '{password}' | passwd root; \
|
||||
echo PASSWORD_UPDATED",
|
||||
hostname = safe_hostname,
|
||||
password = safe_password
|
||||
)
|
||||
}
|
||||
|
||||
fn shell_escape_single_quotes(value: &str) -> String {
|
||||
value.replace('\'', "'\\''")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user