367 lines
13 KiB
Bash
367 lines
13 KiB
Bash
#!/bin/bash
|
|
# RedFlag Agent Installer - Linux
|
|
# Generated for agent: {{.AgentID}}
|
|
# Platform: {{.Platform}}
|
|
# Architecture: {{.Architecture}}
|
|
# Version: {{.Version}}
|
|
|
|
set -e
|
|
|
|
# Check if running as root (required for user creation and sudoers)
|
|
if [ "$EUID" -ne 0 ]; then
|
|
echo "ERROR: This script must be run as root for secure installation (use sudo)"
|
|
exit 1
|
|
fi
|
|
|
|
AGENT_USER="redflag-agent"
|
|
BASE_DIR="/var/lib/redflag"
|
|
CONFIG_DIR="/etc/redflag"
|
|
AGENT_CONFIG_DIR="/etc/redflag/agent"
|
|
LOG_DIR="/var/log/redflag"
|
|
AGENT_LOG_DIR="/var/log/redflag/agent"
|
|
SUDOERS_FILE="/etc/sudoers.d/redflag-agent"
|
|
|
|
# Function to detect package manager
|
|
detect_package_manager() {
|
|
if command -v apt-get &> /dev/null; then
|
|
echo "apt"
|
|
elif command -v dnf &> /dev/null; then
|
|
echo "dnf"
|
|
elif command -v yum &> /dev/null; then
|
|
echo "yum"
|
|
elif command -v pacman &> /dev/null; then
|
|
echo "pacman"
|
|
elif command -v zypper &> /dev/null; then
|
|
echo "zypper"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
|
|
AGENT_ID="{{.AgentID}}"
|
|
BINARY_URL="{{.BinaryURL}}"
|
|
CONFIG_URL="{{.ConfigURL}}"
|
|
INSTALL_DIR="/usr/local/bin"
|
|
CONFIG_DIR="/etc/redflag"
|
|
OLD_CONFIG_DIR="/etc/aggregator"
|
|
SERVICE_NAME="redflag-agent"
|
|
VERSION="{{.Version}}"
|
|
LOG_DIR="/var/log/redflag"
|
|
BACKUP_DIR="${CONFIG_DIR}/backups/backup.$(date +%s)"
|
|
AGENT_USER="redflag-agent"
|
|
AGENT_HOME="{{.AgentHome}}"
|
|
SUDOERS_FILE="/etc/sudoers.d/redflag-agent"
|
|
|
|
echo "=== RedFlag Agent v${VERSION} Installation ==="
|
|
echo "Agent ID: ${AGENT_ID}"
|
|
echo "Platform: {{.Platform}}"
|
|
echo "Installing to: ${INSTALL_DIR}/${SERVICE_NAME}"
|
|
echo
|
|
|
|
# Step 1: Detect existing installation
|
|
echo "Detecting existing RedFlag installations..."
|
|
MIGRATION_NEEDED=false
|
|
|
|
if [ -f "${CONFIG_DIR}/config.json" ]; then
|
|
echo "✓ Existing installation detected at ${CONFIG_DIR}"
|
|
MIGRATION_NEEDED=true
|
|
elif [ -f "${OLD_CONFIG_DIR}/config.json" ]; then
|
|
echo "⚠ Old installation detected at ${OLD_CONFIG_DIR} - MIGRATION REQUIRED"
|
|
MIGRATION_NEEDED=true
|
|
else
|
|
echo "✓ Fresh installation"
|
|
fi
|
|
|
|
# Step 2: Create backup if migration needed
|
|
if [ "${MIGRATION_NEEDED}" = true ]; then
|
|
echo
|
|
echo "=== Migration Required ==="
|
|
echo "Agent will migrate on first start. Backing up configuration..."
|
|
sudo mkdir -p "${BACKUP_DIR}"
|
|
|
|
if [ -f "${OLD_CONFIG_DIR}/config.json" ]; then
|
|
echo "Backing up old configuration..."
|
|
sudo cp -r "${OLD_CONFIG_DIR}"/* "${BACKUP_DIR}/" 2>/dev/null || true
|
|
fi
|
|
|
|
if [ -f "${CONFIG_DIR}/config.json" ]; then
|
|
echo "Backing up current configuration..."
|
|
sudo cp "${CONFIG_DIR}/config.json" "${BACKUP_DIR}/config.json.backup" 2>/dev/null || true
|
|
fi
|
|
|
|
echo "Migration will run automatically when agent starts."
|
|
echo "View migration logs with: sudo journalctl -u ${SERVICE_NAME} -f"
|
|
echo
|
|
fi
|
|
|
|
# Step 3: Create system user and home directory
|
|
echo "Creating system user for agent..."
|
|
if id "$AGENT_USER" &>/dev/null; then
|
|
echo "✓ User $AGENT_USER already exists"
|
|
else
|
|
sudo useradd -r -s /bin/false -d "$AGENT_HOME" "$AGENT_USER"
|
|
echo "✓ User $AGENT_USER created"
|
|
fi
|
|
|
|
# Create home directory structure
|
|
if [ ! -d "$AGENT_HOME" ]; then
|
|
# Create nested directory structure
|
|
sudo mkdir -p "$BASE_DIR"
|
|
sudo mkdir -p "$AGENT_HOME"
|
|
sudo mkdir -p "$AGENT_HOME/cache"
|
|
sudo mkdir -p "$AGENT_HOME/state"
|
|
sudo mkdir -p "$AGENT_CONFIG_DIR"
|
|
sudo mkdir -p "$AGENT_LOG_DIR"
|
|
|
|
# Set ownership and permissions
|
|
sudo chown -R "$AGENT_USER:$AGENT_USER" "$BASE_DIR"
|
|
sudo chmod 750 "$BASE_DIR"
|
|
sudo chmod 750 "$AGENT_HOME"
|
|
sudo chmod 750 "$AGENT_HOME/cache"
|
|
sudo chmod 750 "$AGENT_HOME/state"
|
|
sudo chmod 755 "$AGENT_CONFIG_DIR"
|
|
sudo chmod 755 "$AGENT_LOG_DIR"
|
|
|
|
echo "✓ Agent directory structure created:"
|
|
echo " - Agent home: $AGENT_HOME"
|
|
echo " - Config: $AGENT_CONFIG_DIR"
|
|
echo " - Logs: $AGENT_LOG_DIR"
|
|
fi
|
|
|
|
# Step 4: Install sudoers configuration with OS-specific commands
|
|
PM=$(detect_package_manager)
|
|
echo "Detected package manager: $PM"
|
|
echo "Installing sudoers configuration..."
|
|
|
|
case "$PM" in
|
|
apt)
|
|
cat <<'EOF' | sudo tee "$SUDOERS_FILE" > /dev/null
|
|
# RedFlag Agent minimal sudo permissions - APT
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/apt-get update
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/apt-get install -y *
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/apt-get upgrade -y
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/apt-get install --dry-run --yes *
|
|
EOF
|
|
;;
|
|
dnf|yum)
|
|
cat <<'EOF' | sudo tee "$SUDOERS_FILE" > /dev/null
|
|
# RedFlag Agent minimal sudo permissions - DNF/YUM
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/dnf makecache
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/dnf install -y *
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/dnf upgrade -y
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/yum makecache
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/yum install -y *
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/yum update -y
|
|
EOF
|
|
;;
|
|
pacman)
|
|
cat <<'EOF' | sudo tee "$SUDOERS_FILE" > /dev/null
|
|
# RedFlag Agent minimal sudo permissions - Pacman
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/pacman -Sy
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/pacman -S --noconfirm *
|
|
EOF
|
|
;;
|
|
*)
|
|
cat <<'EOF' | sudo tee "$SUDOERS_FILE" > /dev/null
|
|
# RedFlag Agent minimal sudo permissions - Generic (APT and DNF)
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/apt-get update
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/apt-get install -y *
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/dnf makecache
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/dnf install -y *
|
|
EOF
|
|
;;
|
|
esac
|
|
|
|
# Add Docker commands
|
|
cat <<'DOCKER_EOF' | sudo tee -a "$SUDOERS_FILE" > /dev/null
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/docker pull *
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/docker image inspect *
|
|
{{.AgentUser}} ALL=(root) NOPASSWD: /usr/bin/docker manifest inspect *
|
|
DOCKER_EOF
|
|
|
|
sudo chmod 440 "$SUDOERS_FILE"
|
|
if visudo -c -f "$SUDOERS_FILE" &>/dev/null; then
|
|
echo "✓ Sudoers configuration installed and validated"
|
|
else
|
|
echo "⚠ Sudoers configuration validation failed - using generic version"
|
|
fi
|
|
|
|
# Step 5: Stop existing service
|
|
if systemctl is-active --quiet ${SERVICE_NAME} 2>/dev/null; then
|
|
echo "Stopping existing RedFlag agent service..."
|
|
sudo systemctl stop ${SERVICE_NAME}
|
|
fi
|
|
|
|
# Step 4: Create directories
|
|
echo "Creating directories..."
|
|
sudo mkdir -p "${AGENT_CONFIG_DIR}"
|
|
sudo mkdir -p "${CONFIG_DIR}/backups" # Legacy backup location
|
|
sudo mkdir -p "$AGENT_HOME"
|
|
sudo mkdir -p "$AGENT_LOG_DIR"
|
|
|
|
# Step 5: 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 6: Handle configuration
|
|
# IMPORTANT: The agent handles its own migration on first start.
|
|
# We either preserve existing config OR create a minimal template.
|
|
if [ -f "${AGENT_CONFIG_DIR}/config.json" ]; then
|
|
echo "[CONFIG] Upgrade detected - preserving existing configuration"
|
|
echo "[CONFIG] Agent will handle migration automatically on first start"
|
|
echo "[CONFIG] Backup created at: ${BACKUP_DIR}"
|
|
else
|
|
echo "[CONFIG] Fresh install - generating minimal configuration with registration token"
|
|
# Create minimal config template - agent will populate missing fields on first start
|
|
sudo tee "${AGENT_CONFIG_DIR}/config.json" > /dev/null <<EOF
|
|
{
|
|
"version": 5,
|
|
"agent_version": "${VERSION}",
|
|
"agent_id": "",
|
|
"token": "",
|
|
"refresh_token": "",
|
|
"registration_token": "{{.RegistrationToken}}",
|
|
"machine_id": "",
|
|
"check_in_interval": 300,
|
|
"server_url": "{{.ServerURL}}",
|
|
"network": {
|
|
"timeout": 30000000000,
|
|
"retry_count": 3,
|
|
"retry_delay": 5000000000,
|
|
"max_idle_conn": 10
|
|
},
|
|
"proxy": {
|
|
"enabled": false
|
|
},
|
|
"tls": {
|
|
"enabled": false,
|
|
"insecure_skip_verify": false
|
|
},
|
|
"logging": {
|
|
"level": "info",
|
|
"max_size": 100,
|
|
"max_backups": 3,
|
|
"max_age": 28
|
|
},
|
|
"subsystems": {
|
|
"system": {"enabled": true, "timeout": 10000000000, "circuit_breaker": {"enabled": true, "failure_threshold": 3, "failure_window": 600000000000, "open_duration": 1800000000000, "half_open_attempts": 2}},
|
|
"filesystem": {"enabled": true, "timeout": 10000000000, "circuit_breaker": {"enabled": true, "failure_threshold": 3, "failure_window": 600000000000, "open_duration": 1800000000000, "half_open_attempts": 2}},
|
|
"network": {"enabled": true, "timeout": 30000000000, "circuit_breaker": {"enabled": true, "failure_threshold": 3, "failure_window": 600000000000, "open_duration": 1800000000000, "half_open_attempts": 2}},
|
|
"processes": {"enabled": true, "timeout": 30000000000, "circuit_breaker": {"enabled": true, "failure_threshold": 3, "failure_window": 600000000000, "open_duration": 1800000000000, "half_open_attempts": 2}},
|
|
"updates": {"enabled": true, "timeout": 30000000000, "circuit_breaker": {"enabled": false, "failure_threshold": 0, "failure_window": 0, "open_duration": 0, "half_open_attempts": 0}},
|
|
"storage": {"enabled": true, "timeout": 10000000000, "circuit_breaker": {"enabled": true, "failure_threshold": 3, "failure_window": 600000000000, "open_duration": 1800000000000, "half_open_attempts": 2}}
|
|
},
|
|
"security": {
|
|
"ed25519_verification": true,
|
|
"nonce_validation": true,
|
|
"machine_id_binding": true
|
|
}
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
# Step 7: Set permissions on config file
|
|
sudo chmod 600 "${AGENT_CONFIG_DIR}/config.json"
|
|
|
|
# Step 8: Create systemd service with security hardening
|
|
echo "Creating systemd service with security configuration..."
|
|
cat <<EOF | sudo tee /etc/systemd/system/${SERVICE_NAME}.service
|
|
[Unit]
|
|
Description=RedFlag Security Agent
|
|
After=network.target
|
|
StartLimitBurst=5
|
|
StartLimitIntervalSec=60
|
|
|
|
[Service]
|
|
Type=simple
|
|
User={{.AgentUser}}
|
|
Group={{.AgentUser}}
|
|
WorkingDirectory={{.AgentHome}}
|
|
ExecStart=${INSTALL_DIR}/${SERVICE_NAME}
|
|
Restart=always
|
|
RestartSec=30
|
|
RestartPreventExitStatus=255
|
|
|
|
# Security hardening
|
|
# Note: NoNewPrivileges disabled to allow sudo for package management
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths={{.AgentHome}} {{.AgentHome}}/cache {{.AgentHome}}/state {{.AgentHome}}/migration_backups {{.AgentConfigDir}} {{.AgentLogDir}}
|
|
PrivateTmp=true
|
|
ProtectKernelTunables=true
|
|
ProtectKernelModules=true
|
|
ProtectControlGroups=true
|
|
RestrictRealtime=true
|
|
RestrictSUIDSGID=true
|
|
RemoveIPC=true
|
|
|
|
# Logging
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=${SERVICE_NAME}
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# Set proper permissions on directories
|
|
echo "Setting directory permissions..."
|
|
sudo chown -R {{.AgentUser}}:{{.AgentUser}} "{{.AgentConfigDir}}"
|
|
sudo chown {{.AgentUser}}:{{.AgentUser}} "{{.AgentConfigDir}}/config.json"
|
|
sudo chmod 600 "{{.AgentConfigDir}}/config.json"
|
|
sudo chown -R {{.AgentUser}}:{{.AgentUser}} "{{.AgentHome}}"
|
|
sudo chmod 750 "{{.AgentHome}}"
|
|
sudo chown -R {{.AgentUser}}:{{.AgentUser}} "{{.AgentLogDir}}"
|
|
sudo chmod 750 "{{.AgentLogDir}}"
|
|
|
|
# Register agent with server (if token provided)
|
|
if [ -n "{{.RegistrationToken}}" ]; then
|
|
echo "[INFO] [installer] [register] Registering agent with server..."
|
|
if sudo -u "{{.AgentUser}}" "${INSTALL_DIR}/${SERVICE_NAME}" --server "{{.ServerURL}}" --token "{{.RegistrationToken}}" --register; then
|
|
echo "[SUCCESS] [installer] [register] Agent registered successfully"
|
|
echo "[INFO] [installer] [register] Agent ID assigned, configuration updated"
|
|
else
|
|
echo "[ERROR] [installer] [register] Registration failed - check token validity and server connectivity"
|
|
echo "[WARN] [installer] [register] Agent installed but not registered. Service will not start."
|
|
echo ""
|
|
echo "[INFO] [installer] [register] To retry registration manually:"
|
|
echo "[INFO] [installer] [register] sudo -u {{.AgentUser}} ${INSTALL_DIR}/${SERVICE_NAME} --server {{.ServerURL}} --token YOUR_TOKEN --register"
|
|
echo "[INFO] [installer] [register] Then start service:"
|
|
echo "[INFO] [installer] [register] sudo systemctl start ${SERVICE_NAME}"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "[INFO] [installer] [register] No registration token provided - skipping registration"
|
|
echo "[INFO] [installer] [register] Service will start but agent will exit until registered"
|
|
echo "[INFO] [installer] [register] To register manually:"
|
|
echo "[INFO] [installer] [register] sudo -u {{.AgentUser}} ${INSTALL_DIR}/${SERVICE_NAME} --server {{.ServerURL}} --token YOUR_TOKEN --register"
|
|
fi
|
|
|
|
# Step 9: Enable and start service
|
|
echo "Enabling and starting service..."
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable ${SERVICE_NAME}
|
|
sudo systemctl start ${SERVICE_NAME}
|
|
|
|
echo
|
|
if systemctl is-active --quiet ${SERVICE_NAME}; then
|
|
echo "✓ Installation complete!"
|
|
echo ""
|
|
echo "=== Security Information ==="
|
|
echo "Agent is running with security hardening:"
|
|
echo " ✓ Dedicated system user: {{.AgentUser}}"
|
|
echo " ✓ Limited sudo access for package management only"
|
|
echo " ✓ Systemd service with security restrictions"
|
|
echo " ✓ Protected configuration directory"
|
|
echo ""
|
|
echo "Check status: sudo systemctl status ${SERVICE_NAME}"
|
|
echo "View logs: sudo journalctl -u ${SERVICE_NAME} -f"
|
|
else
|
|
echo "⚠ Installation complete but service not started"
|
|
echo " This may be normal for fresh installs awaiting registration"
|
|
echo ""
|
|
echo "To start after registration:"
|
|
echo " sudo systemctl start ${SERVICE_NAME}"
|
|
fi
|