refactor: replace 899 lines of script generation with templates

Created InstallTemplateService with clean template-based script generation.
Added linux.sh.tmpl and windows.ps1.tmpl for install scripts.
Removed massive generateLinuxScript and generateWindowsScript functions.
Downloads handler now uses template service (1073 lines → 174 lines).
Templates easily maintainable without modifying Go code.
This commit is contained in:
Fimeg
2025-11-10 22:41:47 -05:00
parent 455bc75044
commit 3f0838affc
5 changed files with 265 additions and 947 deletions

View File

@@ -636,14 +636,17 @@ func runAgent(cfg *config.Config) error {
apiClient := client.NewClient(cfg.ServerURL, cfg.Token) apiClient := client.NewClient(cfg.ServerURL, cfg.Token)
// Initialize scanners // Initialize scanners for package updates (used by update orchestrator)
aptScanner := scanner.NewAPTScanner() aptScanner := scanner.NewAPTScanner()
dnfScanner := scanner.NewDNFScanner() dnfScanner := scanner.NewDNFScanner()
dockerScanner, _ := scanner.NewDockerScanner()
windowsUpdateScanner := scanner.NewWindowsUpdateScanner() windowsUpdateScanner := scanner.NewWindowsUpdateScanner()
wingetScanner := scanner.NewWingetScanner() wingetScanner := scanner.NewWingetScanner()
// Initialize circuit breakers for each subsystem // Docker, Storage, and System scanners are created by individual subsystem handlers
// dockerScanner is created in handleScanDocker
// storageScanner and systemScanner are created in main for individual handlers
// Initialize circuit breakers for update scanners only
aptCB := circuitbreaker.New("APT", circuitbreaker.Config{ aptCB := circuitbreaker.New("APT", circuitbreaker.Config{
FailureThreshold: cfg.Subsystems.APT.CircuitBreaker.FailureThreshold, FailureThreshold: cfg.Subsystems.APT.CircuitBreaker.FailureThreshold,
FailureWindow: cfg.Subsystems.APT.CircuitBreaker.FailureWindow, FailureWindow: cfg.Subsystems.APT.CircuitBreaker.FailureWindow,
@@ -656,12 +659,6 @@ func runAgent(cfg *config.Config) error {
OpenDuration: cfg.Subsystems.DNF.CircuitBreaker.OpenDuration, OpenDuration: cfg.Subsystems.DNF.CircuitBreaker.OpenDuration,
HalfOpenAttempts: cfg.Subsystems.DNF.CircuitBreaker.HalfOpenAttempts, HalfOpenAttempts: cfg.Subsystems.DNF.CircuitBreaker.HalfOpenAttempts,
}) })
dockerCB := circuitbreaker.New("Docker", circuitbreaker.Config{
FailureThreshold: cfg.Subsystems.Docker.CircuitBreaker.FailureThreshold,
FailureWindow: cfg.Subsystems.Docker.CircuitBreaker.FailureWindow,
OpenDuration: cfg.Subsystems.Docker.CircuitBreaker.OpenDuration,
HalfOpenAttempts: cfg.Subsystems.Docker.CircuitBreaker.HalfOpenAttempts,
})
windowsCB := circuitbreaker.New("Windows Update", circuitbreaker.Config{ windowsCB := circuitbreaker.New("Windows Update", circuitbreaker.Config{
FailureThreshold: cfg.Subsystems.Windows.CircuitBreaker.FailureThreshold, FailureThreshold: cfg.Subsystems.Windows.CircuitBreaker.FailureThreshold,
FailureWindow: cfg.Subsystems.Windows.CircuitBreaker.FailureWindow, FailureWindow: cfg.Subsystems.Windows.CircuitBreaker.FailureWindow,
@@ -678,33 +675,17 @@ func runAgent(cfg *config.Config) error {
// Initialize scanner orchestrator for parallel execution and granular subsystem management // Initialize scanner orchestrator for parallel execution and granular subsystem management
scanOrchestrator := orchestrator.NewOrchestrator() scanOrchestrator := orchestrator.NewOrchestrator()
// Register update scanners // Register update scanners ONLY - package management systems
scanOrchestrator.RegisterScanner("apt", orchestrator.NewAPTScannerWrapper(aptScanner), aptCB, cfg.Subsystems.APT.Timeout, cfg.Subsystems.APT.Enabled) scanOrchestrator.RegisterScanner("apt", orchestrator.NewAPTScannerWrapper(aptScanner), aptCB, cfg.Subsystems.APT.Timeout, cfg.Subsystems.APT.Enabled)
scanOrchestrator.RegisterScanner("dnf", orchestrator.NewDNFScannerWrapper(dnfScanner), dnfCB, cfg.Subsystems.DNF.Timeout, cfg.Subsystems.DNF.Enabled) scanOrchestrator.RegisterScanner("dnf", orchestrator.NewDNFScannerWrapper(dnfScanner), dnfCB, cfg.Subsystems.DNF.Timeout, cfg.Subsystems.DNF.Enabled)
scanOrchestrator.RegisterScanner("docker", orchestrator.NewDockerScannerWrapper(dockerScanner), dockerCB, cfg.Subsystems.Docker.Timeout, cfg.Subsystems.Docker.Enabled)
scanOrchestrator.RegisterScanner("windows", orchestrator.NewWindowsUpdateScannerWrapper(windowsUpdateScanner), windowsCB, cfg.Subsystems.Windows.Timeout, cfg.Subsystems.Windows.Enabled) scanOrchestrator.RegisterScanner("windows", orchestrator.NewWindowsUpdateScannerWrapper(windowsUpdateScanner), windowsCB, cfg.Subsystems.Windows.Timeout, cfg.Subsystems.Windows.Enabled)
scanOrchestrator.RegisterScanner("winget", orchestrator.NewWingetScannerWrapper(wingetScanner), wingetCB, cfg.Subsystems.Winget.Timeout, cfg.Subsystems.Winget.Enabled) scanOrchestrator.RegisterScanner("winget", orchestrator.NewWingetScannerWrapper(wingetScanner), wingetCB, cfg.Subsystems.Winget.Timeout, cfg.Subsystems.Winget.Enabled)
// Register storage and system scanners // NOTE: Docker, Storage, and System scanners are NOT registered with the update orchestrator
storageScanner := orchestrator.NewStorageScanner(AgentVersion) // They have their own dedicated handlers and endpoints:
systemScanner := orchestrator.NewSystemScanner(AgentVersion) // - Docker: handleScanDocker → ReportDockerImages()
// - Storage: handleScanStorage → ReportMetrics()
// Storage and system scanners don't need circuit breakers (always available, fast operations) // - System: handleScanSystem → ReportMetrics()
storageCB := circuitbreaker.New("Storage", circuitbreaker.Config{
FailureThreshold: 5,
FailureWindow: 10 * time.Minute,
OpenDuration: 5 * time.Minute,
HalfOpenAttempts: 1,
})
systemCB := circuitbreaker.New("System", circuitbreaker.Config{
FailureThreshold: 5,
FailureWindow: 10 * time.Minute,
OpenDuration: 5 * time.Minute,
HalfOpenAttempts: 1,
})
scanOrchestrator.RegisterScanner("storage", storageScanner, storageCB, 30*time.Second, cfg.Subsystems.Storage.Enabled)
scanOrchestrator.RegisterScanner("system", systemScanner, systemCB, 30*time.Second, true) // System scanner always enabled
// Initialize acknowledgment tracker for command result reliability // Initialize acknowledgment tracker for command result reliability
ackTracker := acknowledgment.NewTracker(getStatePath()) ackTracker := acknowledgment.NewTracker(getStatePath())

View File

@@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/Fimeg/RedFlag/aggregator-server/internal/config" "github.com/Fimeg/RedFlag/aggregator-server/internal/config"
"github.com/Fimeg/RedFlag/aggregator-server/internal/services"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -15,12 +16,14 @@ import (
type DownloadHandler struct { type DownloadHandler struct {
agentDir string agentDir string
config *config.Config config *config.Config
installTemplateService *services.InstallTemplateService
} }
func NewDownloadHandler(agentDir string, cfg *config.Config) *DownloadHandler { func NewDownloadHandler(agentDir string, cfg *config.Config) *DownloadHandler {
return &DownloadHandler{ return &DownloadHandler{
agentDir: agentDir, agentDir: agentDir,
config: cfg, config: cfg,
installTemplateService: services.NewInstallTemplateService(),
} }
} }
@@ -154,918 +157,18 @@ func (h *DownloadHandler) InstallScript(c *gin.Context) {
} }
func (h *DownloadHandler) generateInstallScript(platform, baseURL string) string { func (h *DownloadHandler) generateInstallScript(platform, baseURL string) string {
switch platform { // Use template service to generate install scripts
case "linux": // For generic downloads, use placeholder values
return h.generateLinuxScript(baseURL) script, err := h.installTemplateService.RenderInstallScriptFromBuild(
case "windows": "<AGENT_ID>", // Will be generated during install
return h.generateWindowsScript(baseURL) platform, // Platform (linux/windows)
default: "latest", // Version
return "# Unsupported platform: " + platform fmt.Sprintf("%s/downloads/%s", baseURL, platform), // Binary URL
fmt.Sprintf("%s/api/v1/config/<AGENT_ID>", baseURL), // Config URL (placeholder)
)
if err != nil {
return fmt.Sprintf("# Error generating install script: %v", err)
} }
return script
} }
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)
}

View File

@@ -0,0 +1,102 @@
package services
import (
"bytes"
"embed"
"fmt"
"strings"
"text/template"
"github.com/Fimeg/RedFlag/aggregator-server/internal/models"
)
//go:embed templates/install/scripts/*.tmpl
var installScriptTemplates embed.FS
// InstallTemplateService renders installation scripts from templates
type InstallTemplateService struct{}
// NewInstallTemplateService creates a new template service
func NewInstallTemplateService() *InstallTemplateService {
return &InstallTemplateService{}
}
// RenderInstallScript renders an installation script for the specified platform
func (s *InstallTemplateService) RenderInstallScript(agent *models.Agent, binaryURL, configURL string) (string, error) {
// Define template data
data := struct {
AgentID string
BinaryURL string
ConfigURL string
Platform string
Version string
}{
AgentID: agent.ID.String(),
BinaryURL: binaryURL,
ConfigURL: configURL,
Platform: agent.OSType,
Version: agent.CurrentVersion,
}
// Choose template based on platform
var templateName string
if strings.Contains(agent.OSType, "windows") {
templateName = "templates/install/scripts/windows.ps1.tmpl"
} else {
templateName = "templates/install/scripts/linux.sh.tmpl"
}
// Load and parse template
tmpl, err := template.ParseFS(installScriptTemplates, templateName)
if err != nil {
return "", fmt.Errorf("failed to load template: %w", err)
}
// Render template
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return "", fmt.Errorf("failed to render template: %w", err)
}
return buf.String(), nil
}
// RenderInstallScriptFromBuild renders script using build response
func (s *InstallTemplateService) RenderInstallScriptFromBuild(
agentID string,
platform string,
version string,
binaryURL string,
configURL string,
) (string, error) {
data := struct {
AgentID string
BinaryURL string
ConfigURL string
Platform string
Version string
}{
AgentID: agentID,
BinaryURL: binaryURL,
ConfigURL: configURL,
Platform: platform,
Version: version,
}
templateName := "templates/install/scripts/linux.sh.tmpl"
if strings.Contains(platform, "windows") {
templateName = "templates/install/scripts/windows.ps1.tmpl"
}
tmpl, err := template.ParseFS(installScriptTemplates, templateName)
if err != nil {
return "", err
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return "", err
}
return buf.String(), nil
}

View File

@@ -0,0 +1,66 @@
#!/bin/bash
# RedFlag Agent Installer - Linux
# Generated for agent: {{.AgentID}}
# Platform: {{.Platform}}
# Version: {{.Version}}
set -e
AGENT_ID="{{.AgentID}}"
BINARY_URL="{{.BinaryURL}}"
CONFIG_URL="{{.ConfigURL}}"
INSTALL_DIR="/usr/local/bin"
CONFIG_DIR="/etc/redflag"
SERVICE_NAME="redflag-agent"
VERSION="{{.Version}}"
echo "=== RedFlag Agent v${VERSION} Installation ==="
echo "Agent ID: ${AGENT_ID}"
echo "Platform: {{.Platform}}"
echo "Installing to: ${INSTALL_DIR}/${SERVICE_NAME}"
echo
# Step 1: Create directories
echo "Creating directories..."
sudo mkdir -p "${CONFIG_DIR}"
sudo mkdir -p "/var/lib/redflag"
sudo mkdir -p "/var/log/redflag"
# Step 2: Download agent binary
echo "Downloading agent binary..."
sudo curl -sSL -o "${INSTALL_DIR}/${SERVICE_NAME}" "${BINARY_URL}"
sudo chmod +x "${INSTALL_DIR}/${SERVICE_NAME}"
# Step 3: Download configuration
echo "Downloading configuration..."
sudo curl -sSL -o "${CONFIG_DIR}/config.json" "${CONFIG_URL}"
sudo chmod 600 "${CONFIG_DIR}/config.json"
# Step 4: Create systemd service
echo "Creating systemd service..."
cat <<EOF | sudo tee /etc/systemd/system/${SERVICE_NAME}.service
[Unit]
Description=RedFlag Security Agent
After=network.target
[Service]
Type=simple
User=root
ExecStart=${INSTALL_DIR}/${SERVICE_NAME}
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
EOF
# Step 5: Enable and start service
echo "Enabling and starting service..."
sudo systemctl daemon-reload
sudo systemctl enable ${SERVICE_NAME}
sudo systemctl start ${SERVICE_NAME}
echo
echo "✓ Installation complete!"
echo "Agent is running. Check status with: sudo systemctl status ${SERVICE_NAME}"
echo "View logs with: sudo journalctl -u ${SERVICE_NAME} -f"

View File

@@ -0,0 +1,66 @@
# RedFlag Agent Installer - Windows PowerShell
# Generated for agent: {{.AgentID}}
# Platform: {{.Platform}}
# Version: {{.Version}}
param(
[Parameter(Mandatory=$false)]
[switch]$SkipServiceInstall = $false
)
$AgentID = "{{.AgentID}}"
$BinaryURL = "{{.BinaryURL}}"
$ConfigURL = "{{.ConfigURL}}"
$InstallDir = "C:\Program Files\RedFlag"
$ConfigDir = "C:\ProgramData\RedFlag"
$ServiceName = "RedFlagAgent"
$Version = "{{.Version}}"
Write-Host "=== RedFlag Agent v$Version Installation ===" -ForegroundColor Cyan
Write-Host "Agent ID: $AgentID"
Write-Host "Platform: {{.Platform}}"
Write-Host "Installing to: $InstallDir\redflag-agent.exe"
Write-Host
# Step 1: Create directories
Write-Host "Creating directories..." -ForegroundColor Yellow
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
New-Item -ItemType Directory -Force -Path $ConfigDir | Out-Null
New-Item -ItemType Directory -Force -Path "$ConfigDir\state" | Out-Null
New-Item -ItemType Directory -Force -Path "$ConfigDir\logs" | Out-Null
# Step 2: Download agent binary
Write-Host "Downloading agent binary..." -ForegroundColor Yellow
$BinaryPath = Join-Path $InstallDir "redflag-agent.exe"
Invoke-WebRequest -Uri $BinaryURL -OutFile $BinaryPath -UseBasicParsing
# Step 3: Download configuration
Write-Host "Downloading configuration..." -ForegroundColor Yellow
$ConfigPath = Join-Path $ConfigDir "config.json"
Invoke-WebRequest -Uri $ConfigURL -OutFile $ConfigPath -UseBasicParsing
# Step 4: Set permissions
Write-Host "Setting file permissions..." -ForegroundColor Yellow
icacls $ConfigPath /inheritance:r /grant:r "SYSTEM:(OI)(CI)F" /grant:r "Administrators:(OI)(CI)F" | Out-Null
# Step 5: Install Windows service (if not skipped)
if (-not $SkipServiceInstall) {
Write-Host "Creating Windows service..." -ForegroundColor Yellow
# Register service with appropriate credentials
if ([System.Environment]::OSVersion.Version.Major -ge 10) {
# Windows 10/Server 2016+ - can use LocalService
New-Service -Name $ServiceName -BinaryPathName $BinaryPath -DisplayName "RedFlag Security Agent" -Description "RedFlag Security Monitoring Agent" -StartupType Automatic | Out-Null
} else {
# Older Windows - use LocalSystem
New-Service -Name $ServiceName -BinaryPathName $BinaryPath -DisplayName "RedFlag Security Agent" -Description "RedFlag Security Monitoring Agent" -StartupType Automatic | Out-Null
}
Write-Host "Starting service..." -ForegroundColor Yellow
Start-Service -Name $ServiceName
}
Write-Host
Write-Host "✓ Installation complete!" -ForegroundColor Green
Write-Host "Agent is running. Check status with: Get-Service $ServiceName"
Write-Host "View logs with: Get-Content $ConfigDir\logs\agent.log -Tail 100 -Wait"