2 Commits

Author SHA1 Message Date
Muhammad Ibrahim
f0b028cb77 alpine support on the agent installation script 2025-11-08 22:00:34 +00:00
Muhammad Ibrahim
8c2d4aa42b alpine support (apk) support agents 2025-11-08 21:15:08 +00:00
5 changed files with 202 additions and 72 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,7 +1,32 @@
#!/bin/bash
#!/bin/sh
# PatchMon Agent Installation Script
# This script requires bash for full functionality
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | sh
# Check if bash is available, if not try to install it (for Alpine Linux)
if ! command -v bash >/dev/null 2>&1; then
if command -v apk >/dev/null 2>&1; then
echo "Installing bash for script compatibility..."
apk add --no-cache bash >/dev/null 2>&1 || true
fi
fi
# If bash is available and we're not already running in bash, switch to bash
# When piped, we can't re-execute easily, so we'll continue with sh
# but ensure bash is available for bash-specific features
if command -v bash >/dev/null 2>&1 && [ -z "${BASH_VERSION:-}" ]; then
# Check if we're being piped (stdin is not a terminal)
if [ -t 0 ]; then
# Direct execution, re-execute with bash
exec bash "$0" "$@"
exit $?
fi
# When piped, we continue with sh but bash is now available
# The script will use bash-specific features which should work if bash is installed
fi
# PatchMon Agent Installation Script
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | bash
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | sh
set -e
@@ -36,7 +61,7 @@ warning() {
}
# Check if running as root
if [[ $EUID -ne 0 ]]; then
if [ "$(id -u)" -ne 0 ]; then
error "This script must be run as root (use sudo)"
fi
@@ -45,8 +70,8 @@ verify_datetime() {
info "🕐 Verifying system datetime and timezone..."
# Get current system time
local system_time=$(date)
local timezone=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Unknown")
system_time=$(date)
timezone=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Unknown")
# Display current datetime info
echo ""
@@ -56,14 +81,17 @@ verify_datetime() {
echo ""
# Check if we can read from stdin (interactive terminal)
if [[ -t 0 ]]; then
if [ -t 0 ]; then
# Interactive terminal - ask user
read -p "Does this date/time look correct to you? (y/N): " -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
printf "Does this date/time look correct to you? (y/N): "
read -r response
case "$response" in
[Yy]*)
success "✅ Date/time verification passed"
echo ""
return 0
else
;;
*)
echo ""
echo -e "${RED}❌ Date/time verification failed${NC}"
echo ""
@@ -74,7 +102,8 @@ verify_datetime() {
echo ""
echo -e "${BLUE} After fixing the date/time, re-run this installation script.${NC}"
error "Installation cancelled - please fix date/time and re-run"
fi
;;
esac
else
# Non-interactive (piped from curl) - show warning and continue
echo -e "${YELLOW}⚠️ Non-interactive installation detected${NC}"
@@ -121,9 +150,9 @@ cleanup_old_files
# Generate or retrieve machine ID
get_machine_id() {
# Try multiple sources for machine ID
if [[ -f /etc/machine-id ]]; then
if [ -f /etc/machine-id ]; then
cat /etc/machine-id
elif [[ -f /var/lib/dbus/machine-id ]]; then
elif [ -f /var/lib/dbus/machine-id ]; then
cat /var/lib/dbus/machine-id
else
# Fallback: generate from hardware info (less ideal but works)
@@ -132,12 +161,12 @@ get_machine_id() {
}
# Parse arguments from environment (passed via HTTP headers)
if [[ -z "$PATCHMON_URL" ]] || [[ -z "$API_ID" ]] || [[ -z "$API_KEY" ]]; then
if [ -z "$PATCHMON_URL" ] || [ -z "$API_ID" ] || [ -z "$API_KEY" ]; then
error "Missing required parameters. This script should be called via the PatchMon web interface."
fi
# Auto-detect architecture if not explicitly set
if [[ -z "$ARCHITECTURE" ]]; then
if [ -z "$ARCHITECTURE" ]; then
arch_raw=$(uname -m 2>/dev/null || echo "unknown")
# Map architecture to supported values
@@ -162,13 +191,16 @@ if [[ -z "$ARCHITECTURE" ]]; then
fi
# Validate architecture
if [[ "$ARCHITECTURE" != "amd64" && "$ARCHITECTURE" != "386" && "$ARCHITECTURE" != "arm64" && "$ARCHITECTURE" != "arm" ]]; then
if [ "$ARCHITECTURE" != "amd64" ] && [ "$ARCHITECTURE" != "386" ] && [ "$ARCHITECTURE" != "arm64" ] && [ "$ARCHITECTURE" != "arm" ]; then
error "Invalid architecture '$ARCHITECTURE'. Must be one of: amd64, 386, arm64, arm"
fi
# Check if --force flag is set (for bypassing broken packages)
FORCE_INSTALL="${FORCE_INSTALL:-false}"
if [[ "$*" == *"--force"* ]] || [[ "$FORCE_INSTALL" == "true" ]]; then
case "$*" in
*"--force"*) FORCE_INSTALL="true" ;;
esac
if [ "$FORCE_INSTALL" = "true" ]; then
FORCE_INSTALL="true"
warning "⚠️ Force mode enabled - will bypass broken packages"
fi
@@ -224,7 +256,7 @@ install_apt_packages() {
# Build apt-get command based on force mode
local apt_cmd="apt-get install ${missing_packages[*]} -y"
if [[ "$FORCE_INSTALL" == "true" ]]; then
if [ "$FORCE_INSTALL" = "true" ]; then
info "Using force mode - bypassing broken packages..."
apt_cmd="$apt_cmd -o APT::Get::Fix-Broken=false -o DPkg::Options::=\"--force-confold\" -o DPkg::Options::=\"--force-confdef\""
fi
@@ -240,7 +272,7 @@ install_apt_packages() {
local all_ok=true
for pkg in "${packages[@]}"; do
if ! command_exists "$pkg"; then
if [[ "$FORCE_INSTALL" == "true" ]]; then
if [ "$FORCE_INSTALL" = "true" ]; then
error "Critical dependency '$pkg' is not available even with --force. Please install manually."
else
error "Critical dependency '$pkg' is not available. Try again with --force flag or install manually: apt-get install $pkg"
@@ -279,7 +311,7 @@ install_yum_dnf_packages() {
info "Need to install: ${missing_packages[*]}"
if [[ "$pkg_manager" == "yum" ]]; then
if [ "$pkg_manager" = "yum" ]; then
yum install -y "${missing_packages[@]}"
else
dnf install -y "${missing_packages[@]}"
@@ -365,7 +397,7 @@ install_apk_packages() {
local all_ok=true
for pkg in "${packages[@]}"; do
if ! command_exists "$pkg"; then
if [[ "$FORCE_INSTALL" == "true" ]]; then
if [ "$FORCE_INSTALL" = "true" ]; then
error "Critical dependency '$pkg' is not available even with --force. Please install manually."
else
error "Critical dependency '$pkg' is not available. Try again with --force flag or install manually: apk add $pkg"
@@ -391,7 +423,7 @@ if command -v apt-get >/dev/null 2>&1; then
# Check for broken packages
if dpkg -l | grep -q "^iH\|^iF" 2>/dev/null; then
if [[ "$FORCE_INSTALL" == "true" ]]; then
if [ "$FORCE_INSTALL" = "true" ]; then
warning "Detected broken packages on system - force mode will work around them"
else
warning "⚠️ Broken packages detected on system"
@@ -446,7 +478,7 @@ echo ""
info "📁 Setting up configuration directory..."
# Check if configuration directory already exists
if [[ -d "/etc/patchmon" ]]; then
if [ -d "/etc/patchmon" ]; then
warning "⚠️ Configuration directory already exists at /etc/patchmon"
warning "⚠️ Preserving existing configuration files"
@@ -463,8 +495,8 @@ fi
# Check if agent is already configured and working (before we overwrite anything)
info "🔍 Checking if agent is already configured..."
if [[ -f /etc/patchmon/config.yml ]] && [[ -f /etc/patchmon/credentials.yml ]]; then
if [[ -f /usr/local/bin/patchmon-agent ]]; then
if [ -f /etc/patchmon/config.yml ] && [ -f /etc/patchmon/credentials.yml ]; then
if [ -f /usr/local/bin/patchmon-agent ]; then
info "📋 Found existing agent configuration"
info "🧪 Testing existing configuration with ping..."
@@ -495,7 +527,7 @@ fi
info "🔐 Creating configuration files..."
# Check if config file already exists
if [[ -f "/etc/patchmon/config.yml" ]]; then
if [ -f "/etc/patchmon/config.yml" ]; then
warning "⚠️ Config file already exists at /etc/patchmon/config.yml"
warning "⚠️ Moving existing file out of the way for fresh installation"
@@ -508,7 +540,7 @@ if [[ -f "/etc/patchmon/config.yml" ]]; then
fi
# Check if credentials file already exists
if [[ -f "/etc/patchmon/credentials.yml" ]]; then
if [ -f "/etc/patchmon/credentials.yml" ]; then
warning "⚠️ Credentials file already exists at /etc/patchmon/credentials.yml"
warning "⚠️ Moving existing file out of the way for fresh installation"
@@ -521,7 +553,7 @@ if [[ -f "/etc/patchmon/credentials.yml" ]]; then
fi
# Clean up old credentials file if it exists (from previous installations)
if [[ -f "/etc/patchmon/credentials" ]]; then
if [ -f "/etc/patchmon/credentials" ]; then
warning "⚠️ Found old credentials file, removing it..."
rm -f /etc/patchmon/credentials
info "📋 Removed old credentials file"
@@ -557,7 +589,7 @@ info "📥 Downloading PatchMon agent binary..."
BINARY_NAME="patchmon-agent-linux-${ARCHITECTURE}"
# Check if agent binary already exists
if [[ -f "/usr/local/bin/patchmon-agent" ]]; then
if [ -f "/usr/local/bin/patchmon-agent" ]; then
warning "⚠️ Agent binary already exists at /usr/local/bin/patchmon-agent"
warning "⚠️ Moving existing file out of the way for fresh installation"
@@ -570,7 +602,7 @@ if [[ -f "/usr/local/bin/patchmon-agent" ]]; then
fi
# Clean up old shell script if it exists (from previous installations)
if [[ -f "/usr/local/bin/patchmon-agent.sh" ]]; then
if [ -f "/usr/local/bin/patchmon-agent.sh" ]; then
warning "⚠️ Found old shell script agent, removing it..."
rm -f /usr/local/bin/patchmon-agent.sh
info "📋 Removed old shell script agent"
@@ -596,7 +628,7 @@ info "📁 Setting up log directory..."
mkdir -p /etc/patchmon/logs
# Handle existing log files
if [[ -f "/etc/patchmon/logs/patchmon-agent.log" ]]; then
if [ -f "/etc/patchmon/logs/patchmon-agent.log" ]; then
warning "⚠️ Existing log file found at /etc/patchmon/logs/patchmon-agent.log"
warning "⚠️ Rotating log file for fresh start"
@@ -613,8 +645,11 @@ else
error "❌ Failed to validate API credentials or reach server"
fi
# Step 5: Setup systemd service for WebSocket connection
# Step 5: Setup service for WebSocket connection
# Note: The service will automatically send an initial report on startup (see serve.go)
# Detect init system and create appropriate service
if command -v systemctl >/dev/null 2>&1; then
# Systemd is available
info "🔧 Setting up systemd service..."
# Stop and disable existing service if it exists
@@ -672,6 +707,86 @@ else
warning "⚠️ Service may have failed to start. Check status with: systemctl status patchmon-agent"
fi
SERVICE_TYPE="systemd"
elif [ -d /etc/init.d ] && command -v rc-service >/dev/null 2>&1; then
# OpenRC is available (Alpine Linux)
info "🔧 Setting up OpenRC service..."
# Stop and disable existing service if it exists
if rc-service patchmon-agent status >/dev/null 2>&1; then
warning "⚠️ Stopping existing PatchMon agent service..."
rc-service patchmon-agent stop
fi
if rc-update show default 2>/dev/null | grep -q "patchmon-agent"; then
warning "⚠️ Disabling existing PatchMon agent service..."
rc-update del patchmon-agent default
fi
# Create OpenRC service file
cat > /etc/init.d/patchmon-agent << 'EOF'
#!/sbin/openrc-run
name="patchmon-agent"
description="PatchMon Agent Service"
command="/usr/local/bin/patchmon-agent"
command_args="serve"
command_user="root"
pidfile="/var/run/patchmon-agent.pid"
command_background="yes"
working_dir="/etc/patchmon"
depend() {
need net
after net
}
EOF
chmod +x /etc/init.d/patchmon-agent
# Clean up old crontab entries if they exist (from previous installations)
if crontab -l 2>/dev/null | grep -q "patchmon-agent"; then
warning "⚠️ Found old crontab entries, removing them..."
crontab -l 2>/dev/null | grep -v "patchmon-agent" | crontab -
info "📋 Removed old crontab entries"
fi
# Enable and start the service
rc-update add patchmon-agent default
rc-service patchmon-agent start
# Check if service started successfully
if rc-service patchmon-agent status >/dev/null 2>&1; then
success "✅ PatchMon Agent service started successfully"
info "🔗 WebSocket connection established"
else
warning "⚠️ Service may have failed to start. Check status with: rc-service patchmon-agent status"
fi
SERVICE_TYPE="openrc"
else
# No init system detected, use crontab as fallback
warning "⚠️ No init system detected (systemd or OpenRC). Using crontab for service management."
# Clean up old crontab entries if they exist
if crontab -l 2>/dev/null | grep -q "patchmon-agent"; then
warning "⚠️ Found old crontab entries, removing them..."
crontab -l 2>/dev/null | grep -v "patchmon-agent" | crontab -
info "📋 Removed old crontab entries"
fi
# Add crontab entry to run the agent
(crontab -l 2>/dev/null; echo "@reboot /usr/local/bin/patchmon-agent serve >/dev/null 2>&1") | crontab -
info "📋 Added crontab entry for PatchMon agent"
# Start the agent manually
/usr/local/bin/patchmon-agent serve >/dev/null 2>&1 &
success "✅ PatchMon Agent started in background"
info "🔗 WebSocket connection established"
SERVICE_TYPE="crontab"
fi
# Installation complete
success "🎉 PatchMon Agent installation completed successfully!"
echo ""
@@ -680,14 +795,20 @@ echo " • Configuration directory: /etc/patchmon"
echo " • Agent binary installed: /usr/local/bin/patchmon-agent"
echo " • Architecture: $ARCHITECTURE"
echo " • Dependencies installed: jq, curl, bc"
if [ "$SERVICE_TYPE" = "systemd" ]; then
echo " • Systemd service configured and running"
elif [ "$SERVICE_TYPE" = "openrc" ]; then
echo " • OpenRC service configured and running"
else
echo " • Service configured via crontab"
fi
echo " • API credentials configured and tested"
echo " • WebSocket connection established"
echo " • Logs directory: /etc/patchmon/logs"
# Check for moved files and show them
MOVED_FILES=$(ls /etc/patchmon/credentials.yml.backup.* /etc/patchmon/config.yml.backup.* /usr/local/bin/patchmon-agent.backup.* /etc/patchmon/logs/patchmon-agent.log.old.* /usr/local/bin/patchmon-agent.sh.backup.* /etc/patchmon/credentials.backup.* 2>/dev/null || true)
if [[ -n "$MOVED_FILES" ]]; then
if [ -n "$MOVED_FILES" ]; then
echo ""
echo -e "${YELLOW}📋 Files Moved for Fresh Installation:${NC}"
echo "$MOVED_FILES" | while read -r moved_file; do
@@ -702,8 +823,17 @@ echo -e "${BLUE}🔧 Management Commands:${NC}"
echo " • Test connection: /usr/local/bin/patchmon-agent ping"
echo " • Manual report: /usr/local/bin/patchmon-agent report"
echo " • Check status: /usr/local/bin/patchmon-agent diagnostics"
if [ "$SERVICE_TYPE" = "systemd" ]; then
echo " • Service status: systemctl status patchmon-agent"
echo " • Service logs: journalctl -u patchmon-agent -f"
echo " • Restart service: systemctl restart patchmon-agent"
elif [ "$SERVICE_TYPE" = "openrc" ]; then
echo " • Service status: rc-service patchmon-agent status"
echo " • Service logs: tail -f /etc/patchmon/logs/patchmon-agent.log"
echo " • Restart service: rc-service patchmon-agent restart"
else
echo " • Service logs: tail -f /etc/patchmon/logs/patchmon-agent.log"
echo " • Restart service: pkill -f 'patchmon-agent serve' && /usr/local/bin/patchmon-agent serve &"
fi
echo ""
success "✅ Your system is now being monitored by PatchMon!"