package handlers import ( "fmt" "net/http" "os" "path/filepath" "strings" "github.com/Fimeg/RedFlag/aggregator-server/internal/config" "github.com/gin-gonic/gin" ) // DownloadHandler handles agent binary downloads type DownloadHandler struct { agentDir string config *config.Config } func NewDownloadHandler(agentDir string, cfg *config.Config) *DownloadHandler { return &DownloadHandler{ agentDir: agentDir, config: cfg, } } // getServerURL determines the server URL with proper protocol detection func (h *DownloadHandler) getServerURL(c *gin.Context) string { // Priority 1: Use configured public URL if set if h.config.Server.PublicURL != "" { return h.config.Server.PublicURL } // Priority 2: Construct API server URL from configuration scheme := "http" host := h.config.Server.Host port := h.config.Server.Port // Use HTTPS if TLS is enabled in config if h.config.Server.TLS.Enabled { scheme = "https" } // For default host (0.0.0.0), use localhost for client connections if host == "0.0.0.0" { host = "localhost" } // Only include port if it's not the default for the protocol if (scheme == "http" && port != 80) || (scheme == "https" && port != 443) { return fmt.Sprintf("%s://%s:%d", scheme, host, port) } return fmt.Sprintf("%s://%s", scheme, host) } // DownloadAgent serves agent binaries for different platforms func (h *DownloadHandler) DownloadAgent(c *gin.Context) { platform := c.Param("platform") // Validate platform to prevent directory traversal (removed darwin - no macOS support) validPlatforms := map[string]bool{ "linux-amd64": true, "linux-arm64": true, "windows-amd64": true, "windows-arm64": true, } if !validPlatforms[platform] { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or unsupported platform"}) return } // Build filename based on platform filename := "redflag-agent" if strings.HasPrefix(platform, "windows") { filename += ".exe" } // Serve from platform-specific directory: binaries/{platform}/redflag-agent agentPath := filepath.Join(h.agentDir, "binaries", platform, filename) // Check if file exists if _, err := os.Stat(agentPath); os.IsNotExist(err) { c.JSON(http.StatusNotFound, gin.H{"error": "Agent binary not found"}) return } // Handle both GET and HEAD requests if c.Request.Method == "HEAD" { c.Status(http.StatusOK) return } c.File(agentPath) } // DownloadUpdatePackage serves signed agent update packages func (h *DownloadHandler) DownloadUpdatePackage(c *gin.Context) { packageID := c.Param("package_id") // Validate package ID format (UUID) if len(packageID) != 36 { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid package ID format"}) return } // TODO: Implement actual package serving from database/filesystem // For now, return a placeholder response c.JSON(http.StatusNotImplemented, gin.H{ "error": "Update package download not yet implemented", "package_id": packageID, "message": "This will serve the signed update package file", }) } // InstallScript serves the installation script func (h *DownloadHandler) InstallScript(c *gin.Context) { platform := c.Param("platform") // Validate platform (removed darwin - no macOS support) validPlatforms := map[string]bool{ "linux": true, "windows": true, } if !validPlatforms[platform] { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or unsupported platform"}) return } serverURL := h.getServerURL(c) scriptContent := h.generateInstallScript(platform, serverURL) c.Header("Content-Type", "text/plain") c.String(http.StatusOK, scriptContent) } func (h *DownloadHandler) generateInstallScript(platform, baseURL string) string { switch platform { case "linux": return `#!/bin/bash set -e # RedFlag Agent Installation Script # This script installs the RedFlag agent as a systemd service with proper security hardening REDFLAG_SERVER="` + baseURL + `" AGENT_USER="redflag-agent" AGENT_HOME="/var/lib/redflag-agent" AGENT_BINARY="/usr/local/bin/redflag-agent" SUDOERS_FILE="/etc/sudoers.d/redflag-agent" SERVICE_FILE="/etc/systemd/system/redflag-agent.service" CONFIG_DIR="/etc/aggregator" STATE_DIR="/var/lib/aggregator" echo "=== RedFlag Agent Installation ===" echo "" # Check if running as root if [ "$EUID" -ne 0 ]; then echo "ERROR: This script must be run as root (use sudo)" exit 1 fi # Detect architecture ARCH=$(uname -m) case "$ARCH" in x86_64) DOWNLOAD_ARCH="amd64" ;; aarch64|arm64) DOWNLOAD_ARCH="arm64" ;; *) echo "ERROR: Unsupported architecture: $ARCH" echo "Supported: x86_64 (amd64), aarch64 (arm64)" exit 1 ;; esac echo "Detected architecture: $ARCH (using linux-$DOWNLOAD_ARCH)" echo "" # Step 1: Create system user echo "Step 1: Creating system user..." if id "$AGENT_USER" &>/dev/null; then echo "✓ User $AGENT_USER already exists" else useradd -r -s /bin/false -d "$AGENT_HOME" -m "$AGENT_USER" echo "✓ User $AGENT_USER created" fi # Create home directory if it doesn't exist if [ ! -d "$AGENT_HOME" ]; then mkdir -p "$AGENT_HOME" chown "$AGENT_USER:$AGENT_USER" "$AGENT_HOME" echo "✓ Home directory created" fi # Stop existing service if running (to allow binary update) if systemctl is-active --quiet redflag-agent 2>/dev/null; then echo "" echo "Existing service detected - stopping to allow update..." systemctl stop redflag-agent sleep 2 echo "✓ Service stopped" fi # Step 2: Download agent binary echo "" echo "Step 2: Downloading agent binary..." echo "Downloading from ${REDFLAG_SERVER}/api/v1/downloads/linux-${DOWNLOAD_ARCH}..." # Download to temporary file first (to avoid root permission issues) TEMP_FILE="/tmp/redflag-agent-${DOWNLOAD_ARCH}" echo "Downloading to temporary file: $TEMP_FILE" # Try curl first (most reliable) if curl -sL "${REDFLAG_SERVER}/api/v1/downloads/linux-${DOWNLOAD_ARCH}" -o "$TEMP_FILE"; then echo "✓ Download successful, moving to final location" mv "$TEMP_FILE" "${AGENT_BINARY}" chmod 755 "${AGENT_BINARY}" chown root:root "${AGENT_BINARY}" echo "✓ Agent binary downloaded and installed" else echo "✗ Download with curl failed" # Fallback to wget if available if command -v wget >/dev/null 2>&1; then echo "Trying wget fallback..." if wget -q "${REDFLAG_SERVER}/api/v1/downloads/linux-${DOWNLOAD_ARCH}" -O "$TEMP_FILE"; then echo "✓ Download successful with wget, moving to final location" mv "$TEMP_FILE" "${AGENT_BINARY}" chmod 755 "${AGENT_BINARY}" chown root:root "${AGENT_BINARY}" echo "✓ Agent binary downloaded and installed (using wget fallback)" else echo "ERROR: Failed to download agent binary" echo "Both curl and wget failed" echo "Please ensure ${REDFLAG_SERVER} is accessible" # Clean up temp file if it exists rm -f "$TEMP_FILE" exit 1 fi else echo "ERROR: Failed to download agent binary" echo "curl failed and wget is not available" echo "Please ensure ${REDFLAG_SERVER} is accessible" # Clean up temp file if it exists rm -f "$TEMP_FILE" exit 1 fi fi # Clean up temp file if it still exists rm -f "$TEMP_FILE" # Set SELinux context for binary if SELinux is enabled if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" != "Disabled" ]; then echo "SELinux detected, setting file context for binary..." restorecon -v "${AGENT_BINARY}" 2>/dev/null || true echo "✓ SELinux context set for binary" fi # Step 3: Install sudoers configuration echo "" echo "Step 3: Installing sudoers configuration..." cat > "$SUDOERS_FILE" <<'SUDOERS_EOF' # RedFlag Agent minimal sudo permissions # This file grants the redflag-agent user limited sudo access for package management # Generated automatically during RedFlag agent installation # APT package management commands (Debian/Ubuntu) redflag-agent ALL=(root) NOPASSWD: /usr/bin/apt-get update redflag-agent ALL=(root) NOPASSWD: /usr/bin/apt-get install -y * redflag-agent ALL=(root) NOPASSWD: /usr/bin/apt-get upgrade -y * redflag-agent ALL=(root) NOPASSWD: /usr/bin/apt-get install --dry-run --yes * # DNF package management commands (RHEL/Fedora/Rocky/Alma) redflag-agent ALL=(root) NOPASSWD: /usr/bin/dnf makecache redflag-agent ALL=(root) NOPASSWD: /usr/bin/dnf install -y * redflag-agent ALL=(root) NOPASSWD: /usr/bin/dnf upgrade -y * redflag-agent ALL=(root) NOPASSWD: /usr/bin/dnf install --assumeno --downloadonly * # Docker operations redflag-agent ALL=(root) NOPASSWD: /usr/bin/docker pull * redflag-agent ALL=(root) NOPASSWD: /usr/bin/docker image inspect * redflag-agent ALL=(root) NOPASSWD: /usr/bin/docker manifest inspect * SUDOERS_EOF chmod 440 "$SUDOERS_FILE" # Validate sudoers file if visudo -c -f "$SUDOERS_FILE" &>/dev/null; then echo "✓ Sudoers configuration installed and validated" else echo "ERROR: Sudoers configuration is invalid" rm -f "$SUDOERS_FILE" exit 1 fi # Step 4: Create configuration and state directories echo "" echo "Step 4: Creating configuration and state directories..." mkdir -p "$CONFIG_DIR" chown "$AGENT_USER:$AGENT_USER" "$CONFIG_DIR" chmod 755 "$CONFIG_DIR" # Create state directory for acknowledgment tracking (v0.1.19+) mkdir -p "$STATE_DIR" chown "$AGENT_USER:$AGENT_USER" "$STATE_DIR" chmod 755 "$STATE_DIR" echo "✓ Configuration and state directories created" # Set SELinux context for directories if SELinux is enabled if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" != "Disabled" ]; then echo "Setting SELinux context for directories..." restorecon -Rv "$CONFIG_DIR" "$STATE_DIR" 2>/dev/null || true echo "✓ SELinux context set for directories" fi # Step 5: Install systemd service echo "" echo "Step 5: Installing systemd service..." cat > "$SERVICE_FILE" < " REGISTRATION_TOKEN else echo "" echo "IMPORTANT: Registration token required!" echo "" echo "Since you're running this via pipe, you need to:" echo "" echo "Option 1 - One-liner with token:" echo " curl -sfL ${REDFLAG_SERVER}/api/v1/install/linux | sudo bash -s -- YOUR_TOKEN" echo "" echo "Option 2 - Download and run interactively:" echo " curl -sfL ${REDFLAG_SERVER}/api/v1/install/linux -o install.sh" echo " chmod +x install.sh" echo " sudo ./install.sh" echo "" echo "Skipping registration for now." echo "Please register manually after installation." fi fi # Check if agent is already registered if [ -f "$CONFIG_DIR/config.json" ]; then echo "" echo "[INFO] Agent already registered - configuration file exists" echo "[INFO] Skipping registration to preserve agent history" echo "[INFO] If you need to re-register, delete: $CONFIG_DIR/config.json" echo "" elif [ -n "$REGISTRATION_TOKEN" ]; then echo "" echo "Registering agent..." # Create config file and register cat > "$CONFIG_DIR/config.json" <nul 2>&1 if %errorLevel% neq 0 ( echo ERROR: This script must be run as Administrator echo Right-click and select "Run as administrator" pause exit /b 1 ) REM Detect architecture if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( set DOWNLOAD_ARCH=amd64 ) else if "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( set DOWNLOAD_ARCH=arm64 ) else ( echo ERROR: Unsupported architecture: %PROCESSOR_ARCHITECTURE% echo Supported: AMD64, ARM64 pause exit /b 1 ) echo Detected architecture: %PROCESSOR_ARCHITECTURE% (using windows-%DOWNLOAD_ARCH%) echo. REM Create installation directory echo Creating installation directory... if not exist "%AGENT_DIR%" mkdir "%AGENT_DIR%" echo [OK] Installation directory created REM Create config directory if not exist "%CONFIG_DIR%" mkdir "%CONFIG_DIR%" echo [OK] Configuration directory created REM Grant full permissions to SYSTEM and Administrators on config directory echo Setting permissions on configuration directory... icacls "%CONFIG_DIR%" /grant "SYSTEM:(OI)(CI)F" icacls "%CONFIG_DIR%" /grant "Administrators:(OI)(CI)F" echo [OK] Permissions set echo. REM Stop existing service if running (to allow binary update) sc query RedFlagAgent >nul 2>&1 if %errorLevel% equ 0 ( echo Existing service detected - stopping to allow update... sc stop RedFlagAgent >nul 2>&1 timeout /t 3 /nobreak >nul echo [OK] Service stopped ) REM Download agent binary echo Downloading agent binary... echo From: %REDFLAG_SERVER%/api/v1/downloads/windows-%DOWNLOAD_ARCH% curl -sfL "%REDFLAG_SERVER%/api/v1/downloads/windows-%DOWNLOAD_ARCH%" -o "%AGENT_BINARY%" if %errorLevel% neq 0 ( echo ERROR: Failed to download agent binary echo Please ensure %REDFLAG_SERVER% is accessible pause exit /b 1 ) echo [OK] Agent binary downloaded echo. REM Agent registration echo === Agent Registration === echo. REM Check if token was provided as command-line argument if not "%1"=="" ( set TOKEN=%1 echo Using provided registration token ) else ( echo IMPORTANT: You need a registration token to enroll this agent. echo. echo To get a token: echo 1. Visit: %REDFLAG_SERVER%/settings/tokens echo 2. Create a new registration token echo 3. Copy the token echo. set /p TOKEN="Enter registration token (or press Enter to skip): " ) REM Check if agent is already registered if exist "%CONFIG_DIR%\config.json" ( echo. echo [INFO] Agent already registered - configuration file exists echo [INFO] Skipping registration to preserve agent history echo [INFO] If you need to re-register, delete: %CONFIG_DIR%\config.json echo. ) else if not "%TOKEN%"=="" ( echo. echo === Registering Agent === echo. REM Attempt registration "%AGENT_BINARY%" --server "%REDFLAG_SERVER%" --token "%TOKEN%" --register REM Check exit code if %errorLevel% equ 0 ( echo [OK] Agent registered successfully echo [OK] Configuration saved to: %CONFIG_DIR%\config.json echo. ) else ( echo. echo [ERROR] Registration failed echo. echo Please check: echo 1. Server is accessible: %REDFLAG_SERVER% echo 2. Registration token is valid and not expired echo 3. Token has available seats remaining echo. echo To try again: echo "%AGENT_BINARY%" --server "%REDFLAG_SERVER%" --token "%TOKEN%" --register echo. pause exit /b 1 ) ) else ( echo. echo [INFO] No registration token provided - skipping registration echo. echo To register later: echo "%AGENT_BINARY%" --server "%REDFLAG_SERVER%" --token YOUR_TOKEN --register ) REM Check if service already exists echo. echo === Configuring Windows Service === echo. sc query RedFlagAgent >nul 2>&1 if %errorLevel% equ 0 ( echo [INFO] RedFlag Agent service already installed echo [INFO] Service will be restarted with updated binary echo. ) else ( echo Installing RedFlag Agent service... "%AGENT_BINARY%" -install-service if %errorLevel% equ 0 ( echo [OK] Service installed successfully echo. REM Give Windows SCM time to register the service timeout /t 2 /nobreak >nul ) else ( echo [ERROR] Failed to install service echo. pause exit /b 1 ) ) REM Start the service if agent is registered if exist "%CONFIG_DIR%\config.json" ( echo Starting RedFlag Agent service... "%AGENT_BINARY%" -start-service if %errorLevel% equ 0 ( echo [OK] RedFlag Agent service started echo. echo Agent is now running as a Windows service in the background. echo You can verify it is working by checking the agent status in the web UI. ) else ( echo [WARNING] Failed to start service. You can start it manually: echo "%AGENT_BINARY%" -start-service echo Or use Windows Services: services.msc ) ) else ( echo [WARNING] Service not started (agent not registered) echo To register and start the service: echo 1. Register: "%AGENT_BINARY%" --server "%REDFLAG_SERVER%" --token YOUR_TOKEN --register echo 2. Start: "%AGENT_BINARY%" -start-service ) echo. echo === Installation Complete === echo. echo The RedFlag agent has been installed as a Windows service. echo Configuration file: %CONFIG_DIR%\config.json echo Agent binary: %AGENT_BINARY% echo. echo Managing the RedFlag Agent service: echo Check status: "%AGENT_BINARY%" -service-status echo Start manually: "%AGENT_BINARY%" -start-service echo Stop service: "%AGENT_BINARY%" -stop-service echo Remove service: "%AGENT_BINARY%" -remove-service echo. echo Alternative management with Windows Services: echo Open services.msc and look for "RedFlag Update Agent" echo. echo To run the agent directly (for debugging): echo "%AGENT_BINARY%" echo. echo To verify the agent is working: echo 1. Check the web UI for the agent status echo 2. Look for recent check-ins from this machine echo. pause ` default: return "# Unsupported platform" } }