From f7b57ec497bf172d9a68e17af401e4bc156fdd26 Mon Sep 17 00:00:00 2001 From: CodeMaster4711 Date: Thu, 8 Jan 2026 21:15:21 +0100 Subject: [PATCH] fix: update permission error --- backend/src/routes/updates.rs | 124 +++++++++++++++++++++++++++++----- scripts/install.sh | 22 ++++-- 2 files changed, 124 insertions(+), 22 deletions(-) diff --git a/backend/src/routes/updates.rs b/backend/src/routes/updates.rs index 6ef0f866..87a58f48 100644 --- a/backend/src/routes/updates.rs +++ b/backend/src/routes/updates.rs @@ -6,7 +6,7 @@ use axum::{ Json, Router, }; use serde::{Deserialize, Serialize}; -use std::process::Command; +use tokio::process::Command; use crate::AppState; @@ -254,39 +254,129 @@ pub async fn install_update( tokio::spawn(async move { tracing::info!("Spawning update process for version {}", version_clone); - // Check if we're running as root or need sudo - // On Unix systems, check effective user ID via USER env var - let use_sudo = std::env::var("USER").map(|u| u != "root").unwrap_or(true); + // Log current user for debugging + let current_user = std::env::var("USER").unwrap_or_else(|_| "unknown".to_string()); + tracing::info!("Running as user: {}", current_user); - let mut command = if use_sudo { + // Check if we're running as root + let is_root = std::fs::metadata("/etc/shadow") + .and_then(|_| std::fs::File::open("/etc/shadow")) + .is_ok(); + + tracing::info!("Running as root: {}", is_root); + + // Build command - use sudo with direct script path + // This matches the sudoers configuration: /bin/bash /opt/csf-core/scripts/update.sh* + let mut command = if is_root { + // Running as root, execute script directly + Command::new(&script_path_clone) + } else { + // Need sudo - call script via sudo bash let mut cmd = Command::new("sudo"); - // Use full path to bash for better compatibility with sudoers cmd.arg("/bin/bash"); + cmd.arg(&script_path_clone); cmd - } else { - Command::new("/bin/bash") }; - match command - .arg(&script_path_clone) + // Set up environment variables that the script might need + command.env( + "PATH", + "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + ); + command.env( + "HOME", + std::env::var("HOME").unwrap_or_else(|_| "/root".to_string()), + ); + command.env("LANG", "C.UTF-8"); + command.env("LC_ALL", "C.UTF-8"); + + // Set working directory to script location + if let Some(script_dir) = script_path_clone.parent() { + command.current_dir(script_dir); + tracing::info!("Working directory: {:?}", script_dir); + } + + command .arg(&version_clone) .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()) - .spawn() - { + .stderr(std::process::Stdio::piped()); + + tracing::info!("Executing command: {:?}", command); + + match command.spawn() { Ok(mut child) => { tracing::info!( "Update process started successfully (PID: {:?})", child.id() ); - // Wait for process to complete in background - match child.wait() { + // Capture and log output in real-time + let stdout = child.stdout.take(); + let stderr = child.stderr.take(); + + // Spawn tasks to read stdout and stderr + let stdout_handle = tokio::spawn(async move { + if let Some(stdout) = stdout { + use tokio::io::{AsyncBufReadExt, BufReader}; + let reader = BufReader::new(stdout); + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + tracing::info!("[UPDATE STDOUT] {}", line); + } + } + }); + + let stderr_handle = tokio::spawn(async move { + if let Some(stderr) = stderr { + use tokio::io::{AsyncBufReadExt, BufReader}; + let reader = BufReader::new(stderr); + let mut lines = reader.lines(); + while let Ok(Some(line)) = lines.next_line().await { + tracing::warn!("[UPDATE STDERR] {}", line); + } + } + }); + + // Wait for process to complete + match child.wait().await { Ok(status) => { + // Wait for output handlers to finish + let _ = tokio::join!(stdout_handle, stderr_handle); + if status.success() { - tracing::info!("Update process completed successfully"); + tracing::info!( + "Update process completed successfully with exit code: {:?}", + status.code() + ); } else { - tracing::error!("Update process failed with status: {}", status); + tracing::error!( + "Update process failed with status: {} (exit code: {:?})", + status, + status.code() + ); + + // Write detailed error to status file + let ts = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + + let error_status = format!( + r#"{{ + "status": "error", + "message": "Update script failed with exit code {}", + "progress": 0, + "version": "{}", + "timestamp": "{}" +}}"#, + status.code().unwrap_or(-1), + version_clone, + ts + ); + + let _ = + tokio::fs::write("/tmp/csf-core-update-status.json", error_status) + .await; } } Err(e) => { diff --git a/scripts/install.sh b/scripts/install.sh index c744fff3..980d384b 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -279,14 +279,26 @@ create_service_user() { # Configure sudo access for update script (passwordless, specific command only) print_step "Konfiguriere sudo-Zugriff für Updates..." - cat > /etc/sudoers.d/csf-core << EOF + cat > /etc/sudoers.d/csf-core << 'EOF' # Allow csf-core user to run update script without password -$SERVICE_USER ALL=(ALL) NOPASSWD: /opt/csf-core/scripts/update.sh -$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/bash /opt/csf-core/scripts/update.sh* -$SERVICE_USER ALL=(ALL) NOPASSWD: /usr/bin/bash /opt/csf-core/scripts/update.sh* +csf-core ALL=(ALL) NOPASSWD: /opt/csf-core/scripts/update.sh +csf-core ALL=(ALL) NOPASSWD: /bin/bash /opt/csf-core/scripts/update.sh* +csf-core ALL=(ALL) NOPASSWD: /usr/bin/bash /opt/csf-core/scripts/update.sh* + +# Allow csf-core to preserve environment and run non-interactively +Defaults:csf-core !requiretty +Defaults:csf-core env_keep += "PATH HOME LANG LC_ALL" EOF chmod 0440 /etc/sudoers.d/csf-core - print_success "sudo-Zugriff für Updates konfiguriert" + + # Validate sudoers file + if visudo -c -f /etc/sudoers.d/csf-core > /dev/null 2>&1; then + print_success "sudo-Zugriff für Updates konfiguriert" + else + print_error "Sudoers-Datei ist ungültig" + rm -f /etc/sudoers.d/csf-core + print_warning "sudo-Zugriff konnte nicht konfiguriert werden, Updates erfordern manuelle sudo-Rechte" + fi # Add user to docker group if Docker is installed if command -v docker &> /dev/null; then