Este manual está diseñado específicamente para administradores de sistemas Linux que desean utilizar Rust para automatización, herramientas de sistema y administración de infraestructura.
# Instalar Rust via rustup (recomendado)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile default
# Configurar entorno (añadir a ~/.bashrc o ~/.zshrc)
echo 'source $HOME/.cargo/env' >> ~/.bashrc
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Verificar instalación
rustc --version
cargo --version
# Instalar componentes adicionales
rustup component add rustfmt clippy rust-analysis rust-src
# Instalar herramientas útiles
cargo install exa # ls moderno
cargo install bat # cat con sintaxis coloreada
cargo install fd-find # find alternativo
cargo install ripgrep # grep alternativo
cargo install dust # du alternativo
cargo install bottom # top alternativo
# Configurar variables de entorno (en ~/.bashrc o ~/.profile)
export RUST_BACKTRACE=full # Para debugging completo
export CARGO_HOME="$HOME/.cargo" # Localización de cargo
export RUSTUP_HOME="$HOME/.rustup" # Localización de rustup
# Para builds optimizados
export RUSTFLAGS="-C target-cpu=native -C opt-level=3"
use std::fs;
use std::io::{self, Write};
use std::path::Path;
fn main() -> io::Result<()> {
// Crear directorio
fs::create_dir_all("/tmp/rust_sysadmin")?;
// Escribir archivo
let mut file = fs::File::create("/tmp/rust_sysadmin/test.txt")?;
file.write_all(b"Hola, administrador de sistemas!")?;
// Leer archivo
let content = fs::read_to_string("/tmp/rust_sysadmin/test.txt")?;
println!("Contenido: {}", content);
// Verificar existencia
if Path::new("/tmp/rust_sysadmin/test.txt").exists() {
println!("El archivo existe");
}
// Copiar archivo
fs::copy("/tmp/rust_sysadmin/test.txt", "/tmp/rust_sysadmin/test_copy.txt")?;
// Eliminar archivo
fs::remove_file("/tmp/rust_sysadmin/test_copy.txt")?;
// Listar directorio
for entry in fs::read_dir("/tmp/rust_sysadmin")? {
let entry = entry?;
println!("{:?}", entry.path());
}
Ok(())
}
use std::fs::File;
use std::io::{BufRead, BufReader};
fn analyze_logs(file_path: &str) -> std::io::Result<()> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);
let mut error_count = 0;
let mut warning_count = 0;
let mut info_count = 0;
for line in reader.lines() {
let line = line?;
if line.contains("ERROR") {
error_count += 1;
println!("Error encontrado: {}", line);
} else if line.contains("WARNING") {
warning_count += 1;
} else if line.contains("INFO") {
info_count += 1;
}
}
println!("Resumen del análisis:");
println!("- Errores: {}", error_count);
println!("- Advertencias: {}", warning_count);
println!("- Información: {}", info_count);
Ok(())
}
fn main() {
if let Err(e) = analyze_logs("/var/log/syslog") {
eprintln!("Error al analizar logs: {}", e);
}
}
use std::process::{Command, Stdio};
use std::io::{self, Write};
fn run_system_command() -> io::Result<()> {
// Comando simple
let output = Command::new("ls")
.arg("-lh")
.arg("/var/log")
.output()?;
println!("Salida:\n{}", String::from_utf8_lossy(&output.stdout));
// Comando con pipes (similar a shell)
let mut child = Command::new("grep")
.arg("error")
.arg("/var/log/syslog")
.stdout(Stdio::piped())
.spawn()?;
let grep_output = child.wait_with_output()?;
io::stdout().write_all(&grep_output.stdout)?;
// Comando con entorno personalizado
Command::new("bash")
.arg("-c")
.arg("echo $MY_VAR")
.env("MY_VAR", "valor_personalizado")
.status()?;
Ok(())
}
fn main() {
if let Err(e) = run_system_command() {
eprintln!("Error al ejecutar comando: {}", e);
}
}
use std::process::Command;
use std::thread;
use std::time::Duration;
fn monitor_system(interval_secs: u64) {
loop {
// Limpiar pantalla (similar a 'clear')
print!("\x1B[2J\x1B[1;1H");
// Obtener uso de CPU
let cpu = Command::new("sh")
.arg("-c")
.arg("top -bn1 | grep 'Cpu(s)' | sed 's/.*, *\\([0-9.]*\\)%* id.*/\\1/' | awk '{print 100 - $1}'")
.output()
.expect("Error al obtener uso de CPU");
// Obtener memoria libre
let memory = Command::new("free")
.arg("-m")
.output()
.expect("Error al obtener memoria libre");
// Obtener espacio en disco
let disk = Command::new("df")
.arg("-h")
.arg("/")
.output()
.expect("Error al obtener espacio en disco");
println!("=== Monitor del Sistema ===");
println!("Uso de CPU: {}%", String::from_utf8_lossy(&cpu.stdout).trim());
println!("Memoria:\n{}", String::from_utf8_lossy(&memory.stdout));
println!("Disco:\n{}", String::from_utf8_lossy(&disk.stdout));
thread::sleep(Duration::from_secs(interval_secs));
}
}
fn main() {
monitor_system(2);
}
use std::net::{TcpStream, SocketAddr};
use std::time::Duration;
use std::thread;
fn port_scan(target: &str, start_port: u16, end_port: u16, timeout_ms: u64) {
println!("Escaneando {} (puertos {}-{})", target, start_port, end_port);
for port in start_port..=end_port {
let target = target.to_string();
thread::spawn(move || {
let socket_addr = format!("{}:{}", target, port);
if let Ok(addr) = socket_addr.parse::() {
if TcpStream::connect_timeout(&addr, Duration::from_millis(timeout_ms)).is_ok() {
println!("Puerto {} abierto", port);
}
}
});
}
// Esperar a que terminen los hilos
thread::sleep(Duration::from_secs(2));
}
fn main() {
port_scan("127.0.0.1", 80, 100, 200);
}
use reqwest::blocking::Client;
use serde_json::Value;
use std::collections::HashMap;
fn check_web_service(url: &str) -> Result<(), reqwest::Error> {
let client = Client::new();
// GET request
let response = client.get(url).send()?;
println!("Status: {}", response.status());
println!("Headers:\n{:#?}", response.headers());
// POST request con JSON
let mut map = HashMap::new();
map.insert("service", "healthcheck");
map.insert("status", "ok");
let response = client.post("https://httpbin.org/post")
.json(&map)
.send()?;
let json: Value = response.json()?;
println!("Respuesta JSON:\n{}", serde_json::to_string_pretty(&json)?);
Ok(())
}
fn main() {
if let Err(e) = check_web_service("https://httpbin.org/get") {
eprintln!("Error al verificar servicio web: {}", e);
}
}
use std::fs::File;
use std::io::{Read};
use sha2::{Sha256, Digest};
use std::path::Path;
fn calculate_sha256(file_path: &str) -> std::io::Result {
let mut file = File::open(file_path)?;
let mut hasher = Sha256::new();
let mut buffer = [0; 1024];
loop {
let bytes_read = file.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
hasher.update(&buffer[..bytes_read]);
}
Ok(format!("{:x}", hasher.finalize()))
}
fn monitor_file_changes(file_path: &str) -> std::io::Result<()> {
let original_hash = calculate_sha256(file_path)?;
println!("Hash original de {}: {}", file_path, original_hash);
// En producción, aquí iría un loop con sleep para monitoreo continuo
let current_hash = calculate_sha256(file_path)?;
if original_hash != current_hash {
println!("ALERTA: El archivo {} ha sido modificado!", file_path);
println!("Nuevo hash: {}", current_hash);
} else {
println!("El archivo no ha sido modificado");
}
Ok(())
}
fn main() {
if let Err(e) = monitor_file_changes("/etc/passwd") {
eprintln!("Error: {}", e);
}
}
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::fs;
fn check_file_permissions(file_path: &str) -> std::io::Result<()> {
let metadata = fs::metadata(file_path)?;
let permissions = metadata.permissions();
let mode = permissions.mode();
println!("Permisos de {}: {:o}", file_path, mode & 0o777);
// Verificar permisos inseguros
if mode & 0o777 == 0o777 {
println!("ADVERTENCIA: Permisos demasiado abiertos (777) en {}", file_path);
}
// Verificar propiedad
if metadata.uid() == 0 {
println!("ADVERTENCIA: Archivo propiedad de root: {}", file_path);
}
Ok(())
}
fn main() {
let critical_files = [
"/etc/passwd",
"/etc/shadow",
"/etc/sudoers",
"/etc/ssh/sshd_config"
];
for file in &critical_files {
if Path::new(file).exists() {
if let Err(e) = check_file_permissions(file) {
eprintln!("Error al verificar {}: {}", file, e);
}
}
}
}
use clap::{Arg, App, SubCommand};
fn main() {
let matches = App::new("SysAdmin Tool")
.version("1.0")
.author("Admin ")
.about("Herramienta de administración de sistemas")
.subcommand(SubCommand::with_name("scan")
.about("Escanea puertos en un host")
.arg(Arg::with_name("host")
.help("Host a escanear")
.required(true)
.index(1))
.arg(Arg::with_name("start_port")
.short("s")
.long("start")
.help("Puerto inicial")
.default_value("1"))
.arg(Arg::with_name("end_port")
.short("e")
.long("end")
.help("Puerto final")
.default_value("1024")))
.subcommand(SubCommand::with_name("monitor")
.about("Monitor del sistema")
.arg(Arg::with_name("interval")
.short("i")
.long("interval")
.help("Intervalo en segundos")
.default_value("5")))
.get_matches();
match matches.subcommand() {
("scan", Some(scan_matches)) => {
let host = scan_matches.value_of("host").unwrap();
let start_port: u16 = scan_matches.value_of("start_port").unwrap().parse().unwrap();
let end_port: u16 = scan_matches.value_of("end_port").unwrap().parse().unwrap();
println!("Escaneando {} (puertos {}-{})", host, start_port, end_port);
// Aquí iría la lógica de escaneo
},
("monitor", Some(monitor_matches)) => {
let interval: u64 = monitor_matches.value_of("interval").unwrap().parse().unwrap();
println!("Iniciando monitor con intervalo de {} segundos", interval);
// Aquí iría la lógica de monitoreo
},
_ => {
println!("Use --help para ver las opciones");
}
}
}
use std::fs;
use std::path::Path;
use std::process::Command;
use chrono::Local;
use flate2::write::GzEncoder;
use flate2::Compression;
use std::fs::File;
use std::io::Write;
fn create_backup(source: &str, dest_dir: &str) -> std::io::Result<()> {
// Crear directorio de destino si no existe
fs::create_dir_all(dest_dir)?;
// Nombre del archivo de backup con timestamp
let timestamp = Local::now().format("%Y%m%d_%H%M%S");
let backup_name = format!("{}/backup_{}.tar.gz", dest_dir, timestamp);
// Crear archivo comprimido
let tar_gz = File::create(&backup_name)?;
let enc = GzEncoder::new(tar_gz, Compression::default());
let mut tar = tar::Builder::new(enc);
// Añadir directorio al tar
tar.append_dir_all("backup", source)?;
println!("Backup creado exitosamente: {}", backup_name);
// Verificar integridad del backup
let output = Command::new("gzip")
.arg("-t")
.arg(&backup_name)
.output()?;
if output.status.success() {
println!("Verificación de integridad exitosa");
} else {
eprintln!("Error: El archivo de backup está corrupto");
fs::remove_file(backup_name)?;
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Backup corrupto"
));
}
Ok(())
}
fn main() {
let args: Vec = std::env::args().collect();
if args.len() != 3 {
eprintln!("Uso: {} ", args[0]);
std::process::exit(1);
}
if let Err(e) = create_backup(&args[1], &args[2]) {
eprintln!("Error al crear backup: {}", e);
std::process::exit(1);
}
}
use std::fs;
use std::io::Write;
use std::thread;
use std::time::Duration;
fn daemon_work() {
loop {
// Simular trabajo del demonio
let log_entry = format!("[{}] Demonio activo\n", chrono::Local::now());
// Escribir en log (en producción usaría syslog)
if let Ok(mut file) = fs::OpenOptions::new()
.append(true)
.create(true)
.open("/var/log/rust_daemon.log")
{
let _ = file.write_all(log_entry.as_bytes());
}
thread::sleep(Duration::from_secs(60));
}
}
fn main() {
// Configurar comportamiento ante señales
ctrlc::set_handler(move || {
println!("Recibida señal de terminación");
// Limpieza antes de salir
std::process::exit(0);
}).expect("Error al configurar manejador de señales");
println!("Iniciando demonio...");
daemon_work();
}
Archivo de servicio systemd (/etc/systemd/system/rust-daemon.service):
[Unit]
Description=Rust System Daemon
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/rust-daemon
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
Comandos para administrar el servicio:
# Recargar systemd
sudo systemctl daemon-reload
# Habilitar e iniciar el servicio
sudo systemctl enable rust-daemon
sudo systemctl start rust-daemon
# Verificar estado
sudo systemctl status rust-daemon
# Ver logs
journalctl -u rust-daemon -f
Crate | Propósito |
---|---|
clap |
Parseo de argumentos CLI |
sysinfo |
Información del sistema |
libc |
LLamadas al sistema |
nix |
API Unix avanzada |
users |
Gestión de usuarios |
shadow-rs |
Información de build |
indicatif |
Barras de progreso |
serde_json |
Manejo de JSON |
reqwest |
Cliente HTTP |
tar, flate2 |
Compresión |
log
+ syslog
)sudo
solo cuando sea necesario--release
para producciónjemalloc
para cargas de trabajo específicasmusl
para builds estáticosConsejo profesional: Empieza por reescribir tus scripts Bash existentes en Rust para familiarizarte con el lenguaje. Prioriza herramientas que necesiten mayor seguridad o rendimiento.
Rust es una excelente opción para administradores de sistemas Linux que necesitan:
Con su creciente adopción en infraestructura (Firecracker, Docker, Kubernetes), aprender Rust proporciona una valiosa habilidad para cualquier administrador de sistemas moderno.