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") version := c.Query("version") // Optional version parameter for signed binaries // Validate platform to prevent directory traversal 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" } var agentPath string // Try to serve signed package first if version is specified // TODO: Implement database lookup for signed packages // if version != "" { // signedPackage, err := h.packageQueries.GetSignedPackage(version, platform) // if err == nil && fileExists(signedPackage.BinaryPath) { // agentPath = signedPackage.BinaryPath // } // } // Fallback to unsigned generic binary if agentPath == "" { 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", "platform": platform, "version": version, }) 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 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 h.generateLinuxScript(baseURL) case "windows": return h.generateWindowsScript(baseURL) default: return "# Unsupported platform: " + platform } } func (h *DownloadHandler) generateLinuxScript(baseURL string) string { return fmt.Sprintf(`#!/bin/bash set -e # RedFlag Agent Smart Installer # Uses the sophisticated build orchestrator and migration system REDFLAG_SERVER="%s" 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/redflag" STATE_DIR="/var/lib/redflag" OLD_CONFIG_DIR="/etc/aggregator" OLD_STATE_DIR="/var/lib/aggregator" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color echo -e "${BLUE}=== RedFlag Agent Smart Installer ===${NC}" echo "" # Check if running as root if [ "$EUID" -ne 0 ]; then echo -e "${RED}ERROR: This script must be run as root (use sudo)${NC}" exit 1 fi # Get registration token from first argument REGISTRATION_TOKEN="$1" if [ -z "$REGISTRATION_TOKEN" ]; then echo -e "${RED}ERROR: Registration token is required${NC}" echo -e "${YELLOW}Usage: curl -sL ${REDFLAG_SERVER}/api/v1/install/linux | sudo bash -s -- YOUR_REGISTRATION_TOKEN${NC}" exit 1 fi echo -e "${BLUE}Registration token: ${GREEN}${REGISTRATION_TOKEN:0:8}...${NC}" echo "" # Detect architecture ARCH=$(uname -m) case "$ARCH" in x86_64) DOWNLOAD_ARCH="amd64" ;; aarch64|arm64) DOWNLOAD_ARCH="arm64" ;; *) echo -e "${RED}ERROR: Unsupported architecture: $ARCH${NC}" echo -e "${YELLOW}Supported: x86_64 (amd64), aarch64 (arm64)${NC}" exit 1 ;; esac echo -e "${BLUE}Detected architecture: $ARCH (using linux-$DOWNLOAD_ARCH)${NC}" echo "" # Function to detect existing installation using our sophisticated system detect_existing_agent() { echo -e "${YELLOW}Detecting existing RedFlag agent installation...${NC}" # DEBUGGING: Start comprehensive debugging trace echo "=== DEBUGGING: detect_existing_agent() ===" echo "DEBUG: Starting detection process..." # Check for config files in both new and old locations echo "DEBUG: Checking for config files in all locations..." # Check new location first echo "DEBUG: Checking new config file: /etc/redflag/config.json" if [ -f "/etc/redflag/config.json" ]; then echo "DEBUG: New config file exists!" CONFIG_FILE="/etc/redflag/config.json" CONFIG_LOCATION="new" else echo "DEBUG: New config file does not exist, checking legacy location..." # Check old location if [ -f "/etc/aggregator/config.json" ]; then echo "DEBUG: Found legacy config file: /etc/aggregator/config.json" CONFIG_FILE="/etc/aggregator/config.json" CONFIG_LOCATION="old" else echo "DEBUG: No config file found in either location" CONFIG_FILE="" CONFIG_LOCATION="none" fi fi # If we found a config file, try to extract agent_id (using single reliable method) if [ -n "$CONFIG_FILE" ]; then echo "DEBUG: Processing config file: $CONFIG_FILE (location: $CONFIG_LOCATION)" # Check file permissions echo "DEBUG: File permissions:" ls -la "$CONFIG_FILE" # Check file ownership echo "DEBUG: File ownership:" stat -c "%U:%G" "$CONFIG_FILE" # Try reading file content echo "DEBUG: Attempting to read file content..." echo "DEBUG: Method 1 - Direct cat:" if sudo cat "$CONFIG_FILE" 2>/dev/null; then echo "DEBUG: Direct cat successful" else echo "DEBUG: Direct cat failed" fi # Extract agent_id using single reliable method echo "DEBUG: Extracting agent_id with grep:" agent_id=$(grep -o '"agent_id": *"[^"]*"' "$CONFIG_FILE" 2>/dev/null | cut -d'"' -f4) echo "DEBUG: Extracted agent_id: '$agent_id'" # Check if agent_id looks valid (UUID format) if [ -n "$agent_id" ]; then if echo "$agent_id" | grep -qE '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'; then echo "DEBUG: Agent ID appears to be valid UUID format" else echo "DEBUG: Agent ID does not appear to be valid UUID format" fi else echo "DEBUG: Agent ID is empty or null" fi # Note if migration is needed if [ "$CONFIG_LOCATION" = "old" ]; then echo "DEBUG: *** MIGRATION REQUIRED - Config found in legacy location ***" fi else echo "DEBUG: No config files found, checking directories..." # Check if directories exist for debugging for dir_path in "/etc/redflag" "/etc/aggregator" "/var/lib/redflag" "/var/lib/aggregator"; do if [ -d "$dir_path" ]; then echo "DEBUG: Found directory: $dir_path" echo "DEBUG: Directory contents:" ls -la "$dir_path/" 2>/dev/null || echo "DEBUG: Cannot list contents (permissions?)" else echo "DEBUG: Directory does not exist: $dir_path" fi done fi # Check if systemd service exists echo "DEBUG: Checking systemd service..." if systemctl list-unit-files | grep -q "redflag-agent.service"; then echo "DEBUG: Systemd service file exists" # Check service status echo "DEBUG: Service status:" systemctl status redflag-agent --no-pager -l || echo "DEBUG: Could not get service status" # Check if service is enabled if systemctl is-enabled --quiet redflag-agent 2>/dev/null; then echo "DEBUG: Service is enabled" else echo "DEBUG: Service is not enabled" fi # Check if service is active if systemctl is-active --quiet redflag-agent 2>/dev/null; then echo "DEBUG: Service is active" else echo "DEBUG: Service is not active" fi else echo "DEBUG: Systemd service file does not exist" fi # Check if binary exists echo "DEBUG: Checking for agent binary..." for binary_path in "/usr/local/bin/redflag-agent" "/usr/bin/redflag-agent" "/opt/redflag-agent/bin/redflag-agent"; do if [ -f "$binary_path" ]; then echo "DEBUG: Found agent binary at: $binary_path" echo "DEBUG: Binary permissions:" ls -la "$binary_path" break fi done # Test server connectivity echo "DEBUG: Testing server connectivity..." echo "DEBUG: Server URL: ${REDFLAG_SERVER}" # Test basic connectivity echo "DEBUG: Testing basic HTTP connectivity..." if curl -s --connect-timeout 5 "${REDFLAG_SERVER}/api/v1/health" >/dev/null 2>&1; then echo "DEBUG: Server connectivity test successful" else echo "DEBUG: Server connectivity test failed" echo "DEBUG: curl exit code: $?" fi # Call detection API with debugging echo "DEBUG: Calling detection API..." echo "DEBUG: URL: ${REDFLAG_SERVER}/api/v1/build/detect" echo "DEBUG: Payload: {\"agent_id\": \"${agent_id}\"}" DETECTION_RESPONSE=$(curl -s -X POST "${REDFLAG_SERVER}/api/v1/build/detect" \ -H "Content-Type: application/json" \ -d '{"agent_id": "'"$agent_id"'"}' 2>/dev/null) echo "DEBUG: curl exit code: $?" echo "DEBUG: Detection response: '$DETECTION_RESPONSE'" if [ $? -eq 0 ] && [ -n "$DETECTION_RESPONSE" ]; then echo "DEBUG: Successfully received detection response" # Parse JSON response with debugging echo "DEBUG: Parsing detection response..." HAS_AGENT=$(echo "$DETECTION_RESPONSE" | grep -o '"has_existing_agent":[^,]*' | cut -d':' -f2 | tr -d ' ') echo "DEBUG: Extracted has_existing_agent: '$HAS_AGENT'" AGENT_ID=$(echo "$DETECTION_RESPONSE" | grep -o '"agent_id":"[^"]*"' | cut -d'"' -f4) echo "DEBUG: Extracted agent_id from response: '$AGENT_ID'" REQUIRES_MIGRATION=$(echo "$DETECTION_RESPONSE" | grep -o '"requires_migration":[^,]*' | cut -d':' -f2 | tr -d ' ') echo "DEBUG: Extracted requires_migration: '$REQUIRES_MIGRATION'" CURRENT_VERSION=$(echo "$DETECTION_RESPONSE" | grep -o '"current_version":"[^"]*"' | cut -d'"' -f4) echo "DEBUG: Extracted current_version: '$CURRENT_VERSION'" # Check conditions for successful detection if [ "$HAS_AGENT" = "true" ] && [ -n "$AGENT_ID" ]; then echo "DEBUG: Detection SUCCESS - existing agent found" echo -e "${GREEN}✓ Existing agent detected: ${AGENT_ID}${NC}" echo -e "${BLUE} Current version: ${CURRENT_VERSION}${NC}" if [ "$REQUIRES_MIGRATION" = "true" ]; then echo -e "${YELLOW}⚠ Migration will be performed during upgrade${NC}" fi echo "=== END DEBUGGING: detect_existing_agent() ===" return 0 # Upgrade path else echo "DEBUG: Detection indicates no existing agent" echo "DEBUG: has_existing_agent: '$HAS_AGENT'" echo "DEBUG: agent_id from response: '$AGENT_ID'" fi else echo "DEBUG: Detection API call failed or returned empty response" echo "DEBUG: curl exit code: $?" echo "DEBUG: response length: ${#DETECTION_RESPONSE}" fi echo "DEBUG: Returning new installation path" echo -e "${GREEN}✓ No existing agent detected - performing new installation${NC}" echo "=== END DEBUGGING: detect_existing_agent() ===" return 1 # New installation path } # Function to perform migration from old paths perform_migration() { echo "" echo -e "${BLUE}=== Migration Required ===${NC}" # Create backup directories with timestamp BACKUP_TIMESTAMP=$(date +%%Y%%m%%d_%%H%%M%%S) OLD_CONFIG_BACKUP="${OLD_CONFIG_DIR}.backup.${BACKUP_TIMESTAMP}" OLD_STATE_BACKUP="${OLD_STATE_DIR}.backup.${BACKUP_TIMESTAMP}" # Backup old directories if they exist if [ -d "$OLD_CONFIG_DIR" ]; then echo -e "${YELLOW}Backing up old configuration: ${OLD_CONFIG_DIR} -> ${OLD_CONFIG_BACKUP}${NC}" mv "$OLD_CONFIG_DIR" "$OLD_CONFIG_BACKUP" fi if [ -d "$OLD_STATE_DIR" ]; then echo -e "${YELLOW}Backing up old state: ${OLD_STATE_DIR} -> ${OLD_STATE_BACKUP}${NC}" mv "$OLD_STATE_DIR" "$OLD_STATE_BACKUP" fi # Migrate configuration data if backup exists if [ -d "$OLD_CONFIG_BACKUP" ]; then echo -e "${YELLOW}Migrating configuration data to new location...${NC}" mkdir -p "$CONFIG_DIR" # Copy config files, preserving permissions when possible cp -r "$OLD_CONFIG_BACKUP"/* "$CONFIG_DIR/" 2>/dev/null || true # Set proper ownership for new location chown -R "$AGENT_USER:$AGENT_USER" "$CONFIG_DIR" 2>/dev/null || true chmod 755 "$CONFIG_DIR" 2>/dev/null || true # Ensure config file has correct permissions if [ -f "$CONFIG_DIR/config.json" ]; then chmod 600 "$CONFIG_DIR/config.json" 2>/dev/null || true chown "$AGENT_USER:$AGENT_USER" "$CONFIG_DIR/config.json" 2>/dev/null || true fi fi # Migrate state data if backup exists if [ -d "$OLD_STATE_BACKUP" ]; then echo -e "${YELLOW}Migrating state data to new location...${NC}" mkdir -p "$STATE_DIR" cp -r "$OLD_STATE_BACKUP"/* "$STATE_DIR/" 2>/dev/null || true chown -R "$AGENT_USER:$AGENT_USER" "$STATE_DIR" 2>/dev/null || true fi # Migrate secrets to Docker secrets if available migrate_secrets_to_docker echo -e "${GREEN}✓ Migration completed${NC}" } # Function to migrate secrets from filesystem to Docker secrets migrate_secrets_to_docker() { echo -e "${YELLOW}Checking for secrets migration...${NC}" # Look for potential secret files in old locations local secrets_found=false # Check for common secret file patterns for secret_pattern in "agent.key" "private_key" "secrets.json" ".env" "credentials.json"; do if [ -f "$OLD_CONFIG_BACKUP/$secret_pattern" ] || [ -f "$OLD_STATE_BACKUP/$secret_pattern" ]; then echo -e "${YELLOW}Found potential secret file: $secret_pattern${NC}" secrets_found=true fi done # Check for agent private keys or certificates for key_path in "$OLD_CONFIG_BACKUP" "$OLD_STATE_BACKUP"; do if [ -d "$key_path" ]; then # Look for key files find "$key_path" -type f \( -name "*.key" -o -name "*.pem" -o -name "*.crt" -o -name "id_*" \) 2>/dev/null | while read -r key_file; do echo -e "${YELLOW}Found key file: $(basename "$key_file")${NC}" secrets_found=true done fi done if [ "$secrets_found" = true ]; then echo -e "${BLUE}Secrets migration available${NC}" echo -e "${YELLOW}Note: Secrets will be migrated to Docker secrets when the agent starts${NC}" echo -e "${YELLOW}The agent will automatically detect and migrate filesystem secrets to Docker storage${NC}" # Create a migration marker for the agent to find mkdir -p "$CONFIG_DIR" echo '{"secrets_migration_required": true, "migration_timestamp": "'$(date -Iseconds)'"}' > "$CONFIG_DIR/secrets_migration.json" chown "$AGENT_USER:$AGENT_USER" "$CONFIG_DIR/secrets_migration.json" 2>/dev/null || true chmod 600 "$CONFIG_DIR/secrets_migration.json" 2>/dev/null || true else echo -e "${GREEN}No secrets requiring migration found${NC}" fi } # Function to perform new installation using build orchestrator perform_new_installation() { echo "" echo -e "${BLUE}=== New Agent Installation ===${NC}" # Call build/new endpoint to get proper configuration and upgrade logic echo -e "${YELLOW}Requesting agent build configuration...${NC}" BUILD_RESPONSE=$(curl -s -X POST "${REDFLAG_SERVER}/api/v1/build/new" \ -H "Content-Type: application/json" \ -d '{ "server_url": "'"${REDFLAG_SERVER}"'", "environment": "production", "agent_type": "linux-server", "organization": "default", "registration_token": "'"${REGISTRATION_TOKEN}"'" }' 2>/dev/null) if [ $? -ne 0 ] || [ -z "$BUILD_RESPONSE" ]; then echo -e "${RED}✗ Failed to request agent build configuration${NC}" exit 1 fi # Extract agent ID from build response AGENT_ID=$(echo "$BUILD_RESPONSE" | grep -o '"agent_id":"[^"]*"' | cut -d'"' -f4) if [ -z "$AGENT_ID" ]; then echo -e "${RED}✗ Invalid response from server${NC}" exit 1 fi echo -e "${GREEN}✓ Agent configuration created: ${AGENT_ID}${NC}" # Download native agent binary echo -e "${YELLOW}Downloading native signed agent binary...${NC}" if curl -sL "${REDFLAG_SERVER}/api/v1/downloads/linux-${DOWNLOAD_ARCH}" -o "$AGENT_BINARY"; then chmod 755 "$AGENT_BINARY" chown root:root "$AGENT_BINARY" echo -e "${GREEN}✓ Native signed agent binary installed${NC}" else echo -e "${RED}✗ Failed to download agent binary${NC}" exit 1 fi deploy_agent "$AGENT_ID" "$BUILD_RESPONSE" "new" } # Function to perform upgrade using build orchestrator perform_upgrade() { echo "" echo -e "${BLUE}=== Agent Upgrade ===${NC}" # Extract agent ID from detection AGENT_ID=$(echo "$DETECTION_RESPONSE" | grep -o '"agent_id":"[^"]*"' | cut -d'"' -f4) if [ -z "$AGENT_ID" ]; then echo -e "${RED}✗ Could not extract agent ID for upgrade${NC}" exit 1 fi echo -e "${YELLOW}Requesting upgrade configuration for agent: ${AGENT_ID}${NC}" # Call build/upgrade endpoint to get upgrade configuration BUILD_RESPONSE=$(curl -s -X POST "${REDFLAG_SERVER}/api/v1/build/upgrade/${AGENT_ID}" \ -H "Content-Type: application/json" \ -d '{ "server_url": "'"${REDFLAG_SERVER}"'", "environment": "production", "agent_type": "linux-server", "preserve_existing": true }' 2>/dev/null) if [ $? -ne 0 ] || [ -z "$BUILD_RESPONSE" ]; then echo -e "${RED}✗ Failed to request agent upgrade configuration${NC}" exit 1 fi echo -e "${GREEN}✓ Upgrade configuration prepared for agent: ${AGENT_ID}${NC}" # STOP SERVICE BEFORE DOWNLOADING BINARY echo -e "${YELLOW}Stopping agent service to allow binary replacement...${NC}" if systemctl is-active --quiet redflag-agent 2>/dev/null; then systemctl stop redflag-agent # Wait for service to fully stop local retry_count=0 while [ $retry_count -lt 10 ]; do if ! systemctl is-active --quiet redflag-agent 2>/dev/null; then echo -e "${GREEN}✓ Service stopped successfully${NC}" break fi echo -e "${YELLOW}Waiting for service to stop... (attempt $((retry_count + 1))/10)${NC}" sleep 1 retry_count=$((retry_count + 1)) done if systemctl is-active --quiet redflag-agent 2>/dev/null; then echo -e "${RED}✗ Failed to stop service, forcing...${NC}" systemctl kill redflag-agent sleep 2 fi else echo -e "${BLUE}✓ Service is already stopped${NC}" fi # Download updated native agent binary to temporary location first echo -e "${YELLOW}Downloading updated native signed agent binary...${NC}" TEMP_BINARY="${AGENT_BINARY}.new" # Remove any existing temp binary rm -f "$TEMP_BINARY" if curl -sL "${REDFLAG_SERVER}/api/v1/downloads/linux-${DOWNLOAD_ARCH}" -o "$TEMP_BINARY"; then # Verify the download if [ -f "$TEMP_BINARY" ] && [ -s "$TEMP_BINARY" ]; then chmod 755 "$TEMP_BINARY" chown root:root "$TEMP_BINARY" # Atomic move to replace binary mv "$TEMP_BINARY" "$AGENT_BINARY" # Verify the replacement if [ -f "$AGENT_BINARY" ] && [ -s "$AGENT_BINARY" ]; then echo -e "${GREEN}✓ Native signed agent binary updated successfully${NC}" else echo -e "${RED}✗ Binary replacement verification failed${NC}" exit 1 fi else echo -e "${RED}✗ Downloaded binary is empty or missing${NC}" rm -f "$TEMP_BINARY" exit 1 fi else echo -e "${RED}✗ Failed to download agent binary${NC}" rm -f "$TEMP_BINARY" exit 1 fi deploy_agent "$AGENT_ID" "$BUILD_RESPONSE" "upgrade" } # Function to deploy native agent with systemd deploy_agent() { local AGENT_ID="$1" local BUILD_RESPONSE="$2" local INSTALL_TYPE="$3" echo "" echo -e "${BLUE}=== Agent Deployment ===${NC}" # Create agent user if it doesn't exist if ! id "$AGENT_USER" &>/dev/null; then echo -e "${YELLOW}Creating agent user: $AGENT_USER${NC}" useradd -r -s /bin/false -d "$AGENT_HOME" -m "$AGENT_USER" fi # Note: Service is already stopped for upgrades, but check for new installations if [ "$INSTALL_TYPE" = "new" ] && systemctl is-active --quiet redflag-agent 2>/dev/null; then echo -e "${YELLOW}Stopping existing agent service...${NC}" systemctl stop redflag-agent sleep 2 fi # Save build response for potential recovery and debugging echo "$BUILD_RESPONSE" > "${CONFIG_DIR}/build_response.json" chown "$AGENT_USER:$AGENT_USER" "${CONFIG_DIR}/build_response.json" chmod 600 "${CONFIG_DIR}/build_response.json" # Create directories mkdir -p "$CONFIG_DIR" "$STATE_DIR" # Install sudoers configuration if not exists if [ ! -f "$SUDOERS_FILE" ]; then echo -e "${YELLOW}Installing sudoers configuration...${NC}" cat > "$SUDOERS_FILE" << 'SUDOERS_EOF' # RedFlag Agent minimal sudo permissions # 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 * # Directory operations for RedFlag redflag-agent ALL=(root) NOPASSWD: /bin/mkdir -p /etc/redflag redflag-agent ALL=(root) NOPASSWD: /bin/mkdir -p /var/lib/redflag redflag-agent ALL=(root) NOPASSWD: /bin/chown redflag-agent:redflag-agent /etc/redflag redflag-agent ALL=(root) NOPASSWD: /bin/chown redflag-agent:redflag-agent /var/lib/redflag redflag-agent ALL=(root) NOPASSWD: /bin/chmod 755 /etc/redflag redflag-agent ALL=(root) NOPASSWD: /bin/chmod 755 /var/lib/redflag # Migration operations (for existing installations) redflag-agent ALL=(root) NOPASSWD: /bin/mv /etc/aggregator /etc/redflag.backup.* redflag-agent ALL=(root) NOPASSWD: /bin/mv /var/lib/aggregator/* /var/lib/redflag/ redflag-agent ALL=(root) NOPASSWD: /bin/rmdir /var/lib/aggregator 2>/dev/null || true redflag-agent ALL=(root) NOPASSWD: /bin/rmdir /etc/aggregator 2>/dev/null || true SUDOERS_EOF chmod 440 "$SUDOERS_FILE" # Validate sudoers file if visudo -c -f "$SUDOERS_FILE" &>/dev/null; then echo -e "${GREEN}✓ Sudoers configuration installed${NC}" else echo -e "${RED}✗ Invalid sudoers configuration${NC}" rm -f "$SUDOERS_FILE" exit 1 fi fi # Install/update systemd service echo -e "${YELLOW}Installing systemd service...${NC}" cat > "$SERVICE_FILE" << EOF [Unit] Description=RedFlag Update Agent After=network.target Documentation=https://github.com/Fimeg/RedFlag [Service] Type=simple User=$AGENT_USER Group=$AGENT_USER WorkingDirectory=$AGENT_HOME ExecStart=$AGENT_BINARY Restart=always RestartSec=30 # Security hardening ProtectSystem=strict ProtectHome=true ReadWritePaths=$AGENT_HOME /var/log $CONFIG_DIR $STATE_DIR PrivateTmp=true # Logging StandardOutput=journal StandardError=journal SyslogIdentifier=redflag-agent [Install] WantedBy=multi-user.target EOF chmod 644 "$SERVICE_FILE" systemctl daemon-reload # Set directory permissions chown "$AGENT_USER:$AGENT_USER" "$CONFIG_DIR" "$STATE_DIR" chmod 755 "$CONFIG_DIR" "$STATE_DIR" # Start and enable service systemctl enable redflag-agent systemctl restart redflag-agent # Wait for service to start sleep 3 # Verify deployment if systemctl is-active --quiet redflag-agent; then echo -e "${GREEN}✓ Native agent deployed successfully${NC}" # Check logs briefly echo -e "${BLUE}Agent status:${NC}" systemctl status redflag-agent --no-pager -l | head -n 10 echo "" echo -e "${GREEN}=== Installation Complete ===${NC}" echo -e "${BLUE}Agent Details:${NC}" echo -e " • Agent ID: ${AGENT_ID}" echo -e " • Binary: ${AGENT_BINARY}" echo -e " • Service: redflag-agent" echo -e " • Config: ${CONFIG_DIR}/config.json" echo -e " • Build Response: ${CONFIG_DIR}/build_response.json" if [ "$INSTALL_TYPE" = "upgrade" ]; then echo -e " • ${GREEN}Seat preserved: No additional license consumed${NC}" else echo -e " • ${GREEN}Seat allocated: One license consumed${NC}" fi echo "" echo -e "${BLUE}Management Commands:${NC}" echo -e " • Status: systemctl status redflag-agent" echo -e " • Logs: journalctl -u redflag-agent -f" echo -e " • Restart: systemctl restart redflag-agent" echo -e " • Update: Re-run this installer script" echo "" else echo -e "${RED}✗ Failed to start agent service${NC}" echo -e "${YELLOW}Troubleshooting:${NC}" echo -e " • Check logs: journalctl -u redflag-agent -n 50" echo -e " • Check binary: ls -la ${AGENT_BINARY}" echo -e " • Check service: systemctl status redflag-agent" exit 1 fi } # Main execution echo -e "${BLUE}Starting smart installation process...${NC}" # Detect existing installation using our sophisticated system if detect_existing_agent; then # Check if migration is needed by looking for old directories if [ -d "$OLD_CONFIG_DIR" ] || [ -d "$OLD_STATE_DIR" ]; then perform_migration fi # Upgrade path perform_upgrade else # Check if migration is needed for new install if [ -d "$OLD_CONFIG_DIR" ] || [ -d "$OLD_STATE_DIR" ]; then perform_migration fi # New installation path perform_new_installation fi echo "" echo -e "${GREEN}=== Smart Installer Complete ===${NC}" echo -e "${BLUE}Thank you for using RedFlag!${NC}" `, baseURL) } func (h *DownloadHandler) generateWindowsScript(baseURL string) string { return fmt.Sprintf(`@echo off REM RedFlag Agent Installation Script for Windows REM This script downloads the agent and sets up Windows service REM REM Usage: REM install.bat - Interactive mode (prompts for token) REM install.bat YOUR_TOKEN_HERE - Automatic mode (uses provided token) set REDFLAG_SERVER=%s set AGENT_DIR=%%ProgramFiles%%\RedFlag set AGENT_BINARY=%%AGENT_DIR%%\redflag-agent.exe set CONFIG_DIR=%%ProgramData%%\RedFlag echo === RedFlag Agent Installation === echo. REM Check for admin privileges net session >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 "%%CONFIGDIR%%" /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 "%%REDFLAGSERVER%%" --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 `, baseURL) }