Compare commits
48 Commits
main
...
23599ca1a5
| Author | SHA1 | Date | |
|---|---|---|---|
| 23599ca1a5 | |||
| 772a1d233d | |||
| a6bad70674 | |||
| 093f1e6c23 | |||
| c8f4335a71 | |||
| 78d3bbaa3f | |||
| 04a76310ba | |||
| a0fb5e58e9 | |||
| 472978bf06 | |||
| 38cb4f7318 | |||
| a978893c60 | |||
| 7ccbdcd4c0 | |||
| bfbddf363a | |||
| acad96bf93 | |||
| b62c83100c | |||
| 1d9e30bb54 | |||
| 8922dcbe05 | |||
| 573181216a | |||
| 9b7f3a1920 | |||
| 4aaa4b3f2b | |||
| aa6a2ac2df | |||
| 8fbfc73991 | |||
| ecb5f249ed | |||
| dfdca4fc5d | |||
| ccb1c47b60 | |||
| 0c4d970053 | |||
| 9ff7308de0 | |||
| 9cc203a87e | |||
| c73111eda1 | |||
| 0697e17783 | |||
| bce7bd1d28 | |||
| e668efa41c | |||
| a13c324d8e | |||
| 7c19a2b3a5 | |||
| 1dd950b358 | |||
| 6a6c8c036e | |||
| e21e4a9fb7 | |||
| 1674a62eae | |||
| 585785847b | |||
| e4eaf9afb5 | |||
| ab70a6e569 | |||
| b77e8bb9dd | |||
| 60cdad8e7b | |||
| 6632d96c82 | |||
| 1d77cad16c | |||
| 5cb34c913e | |||
| c96423539f | |||
| ade8011149 |
@@ -54,3 +54,4 @@ id_ed25519.pub
|
|||||||
# Config files that might contain sensitive data
|
# Config files that might contain sensitive data
|
||||||
config.local.*
|
config.local.*
|
||||||
*.credentials
|
*.credentials
|
||||||
|
downloads/
|
||||||
|
|||||||
@@ -46,15 +46,16 @@ server-toolkit/
|
|||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### Running
|
### Installation & Running
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Direct method
|
# Download and run in one command
|
||||||
bash /root/server-toolkit/launcher.sh
|
curl -sL https://git.mull.lol/cschantz/Linux-Server-Management-Toolkit/archive/main.tar.gz | tar xz && cd linux-server-management-toolkit && bash launcher.sh
|
||||||
|
```
|
||||||
|
|
||||||
# Or make executable and run
|
Or if already downloaded:
|
||||||
chmod +x /root/server-toolkit/launcher.sh
|
```bash
|
||||||
/root/server-toolkit/launcher.sh
|
bash /root/server-toolkit/launcher.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## ✨ Key Features
|
## ✨ Key Features
|
||||||
|
|||||||
+216
-38
@@ -222,10 +222,11 @@ show_bot_analysis_menu() {
|
|||||||
echo -e " ${CYAN}1)${NC} Full Bot Analysis - Complete scan (all logs)"
|
echo -e " ${CYAN}1)${NC} Full Bot Analysis - Complete scan (all logs)"
|
||||||
echo -e " ${CYAN}2)${NC} Quick Scan (1 hour) - Recent activity only"
|
echo -e " ${CYAN}2)${NC} Quick Scan (1 hour) - Recent activity only"
|
||||||
echo -e " ${CYAN}3)${NC} Live Monitor - Real-time threat tracking"
|
echo -e " ${CYAN}3)${NC} Live Monitor - Real-time threat tracking"
|
||||||
echo -e " ${CYAN}4)${NC} IP Lookup & Investigation - Deep-dive on specific IP"
|
echo -e " ${CYAN}4)${NC} IP Reputation Manager - Query/manage IP database (NEW!)"
|
||||||
echo -e " ${CYAN}5)${NC} DDoS Pattern Detector - Identify DDoS attacks"
|
echo -e " ${CYAN}5)${NC} IP Lookup & Investigation - Deep-dive on specific IP"
|
||||||
echo -e " ${CYAN}6)${NC} Traffic Pattern Analysis - Bandwidth & connection patterns"
|
echo -e " ${CYAN}6)${NC} DDoS Pattern Detector - Identify DDoS attacks"
|
||||||
echo -e " ${CYAN}7)${NC} User-Agent Analysis - Bot fingerprinting"
|
echo -e " ${CYAN}7)${NC} Traffic Pattern Analysis - Bandwidth & connection patterns"
|
||||||
|
echo -e " ${CYAN}8)${NC} User-Agent Analysis - Bot fingerprinting"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${RED}0)${NC} Back to Analysis Menu"
|
echo -e " ${RED}0)${NC} Back to Analysis Menu"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -432,26 +433,18 @@ show_wordpress_menu() {
|
|||||||
show_banner
|
show_banner
|
||||||
echo -e "${BLUE}${BOLD}🌐 Website Management${NC}"
|
echo -e "${BLUE}${BOLD}🌐 Website Management${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${BOLD}General Website Tools:${NC}"
|
echo -e "${BOLD}Error Analysis & Diagnostics:${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BLUE}1)${NC} 🔍 Website Error Analyzer - Find 500/config errors (filters bots)"
|
echo -e " ${BLUE}1)${NC} 🔍 Website Error Analyzer - Find 500/config errors (filters bots)"
|
||||||
|
echo -e " ${RED}2)${NC} 🔥 Fast 500 Error Tracker - ONLY 500s + root cause diagnosis"
|
||||||
|
echo -e " ${BLUE}3)${NC} 📋 Debug Log Analyzer - Parse WP debug logs"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${BOLD}WordPress Tools:${NC}"
|
echo -e "${BOLD}WordPress Management:${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BLUE}2)${NC} Health Check (All Sites) - Scan all WP installations"
|
echo -e " ${BLUE}4)${NC} 🏥 Health & Maintenance → Audits, optimization, cleanup"
|
||||||
echo -e " ${BLUE}3)${NC} WP-Cron Status - Check cron job status"
|
echo -e " ${BLUE}5)${NC} ⚙️ WP-Cron Management → Status, fixes, system cron setup"
|
||||||
echo -e " ${BLUE}4)${NC} WP-Cron Mass Fix - Fix/enable cron on all sites"
|
echo -e " ${BLUE}6)${NC} 🔄 Mass Updates → Core, plugins, themes updates"
|
||||||
echo -e " ${BLUE}5)${NC} WP-Cron Mass Create - Setup proper system crons"
|
echo -e " ${BLUE}7)${NC} 🔒 Security & Compliance → Malware scan, permissions, login audit"
|
||||||
echo -e " ${BLUE}6)${NC} Plugin Audit - Security scan of plugins"
|
|
||||||
echo -e " ${BLUE}7)${NC} Theme Audit - Security scan of themes"
|
|
||||||
echo -e " ${BLUE}8)${NC} Database Optimizer - Clean/optimize WP databases"
|
|
||||||
echo -e " ${BLUE}9)${NC} Cache Clear (All Sites) - Clear all WP caches"
|
|
||||||
echo -e " ${BLUE}10)${NC} Mass Update Core - Update WordPress core (all)"
|
|
||||||
echo -e " ${BLUE}11)${NC} Mass Update Plugins - Update plugins (all sites)"
|
|
||||||
echo -e " ${BLUE}12)${NC} Login Security Audit - Check for weak passwords"
|
|
||||||
echo -e " ${BLUE}13)${NC} Malware Scanner - Scan for infected files"
|
|
||||||
echo -e " ${BLUE}14)${NC} Permission Fixer - Fix file permissions"
|
|
||||||
echo -e " ${BLUE}15)${NC} Debug Log Analyzer - Parse WP debug logs"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${RED}0)${NC} Back to Main Menu"
|
echo -e " ${RED}0)${NC} Back to Main Menu"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -459,6 +452,68 @@ show_wordpress_menu() {
|
|||||||
echo -n "Select option: "
|
echo -n "Select option: "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# WordPress Health & Maintenance submenu
|
||||||
|
show_wp_health_menu() {
|
||||||
|
show_banner
|
||||||
|
echo -e "${BLUE}${BOLD}🏥 WordPress Health & Maintenance${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BLUE}1)${NC} Health Check (All Sites) - Scan all WP installations"
|
||||||
|
echo -e " ${BLUE}2)${NC} Database Optimizer - Clean/optimize WP databases"
|
||||||
|
echo -e " ${BLUE}3)${NC} Cache Clear (All Sites) - Clear all WP caches"
|
||||||
|
echo -e " ${BLUE}4)${NC} Plugin Audit - Security scan of plugins"
|
||||||
|
echo -e " ${BLUE}5)${NC} Theme Audit - Security scan of themes"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Back to Website Management"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -n "Select option: "
|
||||||
|
}
|
||||||
|
|
||||||
|
# WP-Cron Management submenu
|
||||||
|
show_wp_cron_menu() {
|
||||||
|
show_banner
|
||||||
|
echo -e "${BLUE}${BOLD}⚙️ WP-Cron Management${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BLUE}1)${NC} WP-Cron Status - Check cron job status"
|
||||||
|
echo -e " ${BLUE}2)${NC} WP-Cron Mass Fix - Fix/enable cron on all sites"
|
||||||
|
echo -e " ${BLUE}3)${NC} WP-Cron Mass Create - Setup proper system crons"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Back to Website Management"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -n "Select option: "
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mass Updates submenu
|
||||||
|
show_wp_updates_menu() {
|
||||||
|
show_banner
|
||||||
|
echo -e "${BLUE}${BOLD}🔄 WordPress Mass Updates${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BLUE}1)${NC} Mass Update Core - Update WordPress core (all)"
|
||||||
|
echo -e " ${BLUE}2)${NC} Mass Update Plugins - Update plugins (all sites)"
|
||||||
|
echo -e " ${BLUE}3)${NC} Mass Update Themes - Update themes (all sites)"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Back to Website Management"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -n "Select option: "
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security & Compliance submenu
|
||||||
|
show_wp_security_menu() {
|
||||||
|
show_banner
|
||||||
|
echo -e "${BLUE}${BOLD}🔒 WordPress Security & Compliance${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BLUE}1)${NC} Malware Scanner - Scan for infected files"
|
||||||
|
echo -e " ${BLUE}2)${NC} Permission Fixer - Fix file permissions"
|
||||||
|
echo -e " ${BLUE}3)${NC} Login Security Audit - Check for weak passwords"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Back to Website Management"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -n "Select option: "
|
||||||
|
}
|
||||||
|
|
||||||
# Performance & Diagnostics menu
|
# Performance & Diagnostics menu
|
||||||
show_performance_menu() {
|
show_performance_menu() {
|
||||||
show_banner
|
show_banner
|
||||||
@@ -495,6 +550,8 @@ show_backup_menu() {
|
|||||||
show_banner
|
show_banner
|
||||||
echo -e "${YELLOW}${BOLD}💾 Backup & Recovery${NC}"
|
echo -e "${YELLOW}${BOLD}💾 Backup & Recovery${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo -e "${BOLD}cPanel Backups:${NC}"
|
||||||
|
echo ""
|
||||||
echo -e " ${YELLOW}1)${NC} Auto Backup (All Sites) - Create full backups"
|
echo -e " ${YELLOW}1)${NC} Auto Backup (All Sites) - Create full backups"
|
||||||
echo -e " ${YELLOW}2)${NC} Selective Backup - Backup specific accounts"
|
echo -e " ${YELLOW}2)${NC} Selective Backup - Backup specific accounts"
|
||||||
echo -e " ${YELLOW}3)${NC} Restore Helper - Interactive restore tool"
|
echo -e " ${YELLOW}3)${NC} Restore Helper - Interactive restore tool"
|
||||||
@@ -504,12 +561,72 @@ show_backup_menu() {
|
|||||||
echo -e " ${YELLOW}7)${NC} Backup Verification - Test backup integrity"
|
echo -e " ${YELLOW}7)${NC} Backup Verification - Test backup integrity"
|
||||||
echo -e " ${YELLOW}8)${NC} Off-site Sync - Sync to remote storage"
|
echo -e " ${YELLOW}8)${NC} Off-site Sync - Sync to remote storage"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo -e "${BOLD}Acronis Cyber Protect:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}9)${NC} 🔷 Acronis Management → Install, configure, manage backups"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Data Management:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}10)${NC} 🗑️ Cleanup Toolkit Data - Remove IP reputation & temp files"
|
||||||
|
echo ""
|
||||||
echo -e " ${RED}0)${NC} Back to Main Menu"
|
echo -e " ${RED}0)${NC} Back to Main Menu"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
echo -n "Select option: "
|
echo -n "Select option: "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Acronis Management submenu
|
||||||
|
show_acronis_menu() {
|
||||||
|
show_banner
|
||||||
|
echo -e "${YELLOW}${BOLD}🔷 Acronis Cyber Protect${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Installation & Setup:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}1)${NC} Install Acronis Agent - Download and install Acronis"
|
||||||
|
echo -e " ${YELLOW}2)${NC} Register with Cloud - Connect to Acronis Cloud"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Backup Management:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${GREEN}3)${NC} 📊 Manage Backups - Complete backup management interface"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Quick Actions:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}4)${NC} Check Agent Status - Verify Acronis is running"
|
||||||
|
echo -e " ${YELLOW}5)${NC} Update Agent - Upgrade to latest version"
|
||||||
|
echo -e " ${YELLOW}6)${NC} View Logs - Check Acronis logs"
|
||||||
|
echo -e " ${YELLOW}7)${NC} Uninstall Acronis - Remove Acronis agent"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Troubleshooting:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}8)${NC} 🔧 Troubleshoot Backups - Diagnose backup failures"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Back to Backup & Recovery"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -n "Select option: "
|
||||||
|
}
|
||||||
|
|
||||||
|
# Acronis submenu handler
|
||||||
|
handle_acronis_menu() {
|
||||||
|
while true; do
|
||||||
|
show_acronis_menu
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) run_module "backup" "acronis-install.sh" ;;
|
||||||
|
2) run_module "backup" "acronis-register.sh" ;;
|
||||||
|
3) run_module "backup" "acronis-backup-manager.sh" ;;
|
||||||
|
4) run_module "backup" "acronis-agent-status.sh" ;;
|
||||||
|
5) run_module "backup" "acronis-update.sh" ;;
|
||||||
|
6) run_module "backup" "acronis-logs.sh" ;;
|
||||||
|
7) run_module "backup" "acronis-uninstall.sh" ;;
|
||||||
|
8) run_module "backup" "acronis-troubleshoot.sh" ;;
|
||||||
|
0) return ;;
|
||||||
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Monitoring menu
|
# Monitoring menu
|
||||||
show_monitoring_menu() {
|
show_monitoring_menu() {
|
||||||
show_banner
|
show_banner
|
||||||
@@ -929,15 +1046,16 @@ handle_bot_analysis_menu() {
|
|||||||
1) run_module "security" "bot-analyzer.sh" ;;
|
1) run_module "security" "bot-analyzer.sh" ;;
|
||||||
2) run_module "security" "bot-analyzer.sh" -H "${QUICK_SCAN_HOURS:-1}" ;;
|
2) run_module "security" "bot-analyzer.sh" -H "${QUICK_SCAN_HOURS:-1}" ;;
|
||||||
3) run_module "security" "live-monitor.sh" ;;
|
3) run_module "security" "live-monitor.sh" ;;
|
||||||
4)
|
4) run_module "security" "ip-reputation-manager.sh" ;;
|
||||||
|
5)
|
||||||
show_banner
|
show_banner
|
||||||
echo -e "${BOLD}IP Lookup & Investigation${NC}"
|
echo -e "${BOLD}IP Lookup & Investigation${NC}"
|
||||||
read -p "Enter IP address: " ip
|
read -p "Enter IP address: " ip
|
||||||
[ -n "$ip" ] && run_module "security" "ip-lookup.sh" "$ip"
|
[ -n "$ip" ] && run_module "security" "ip-lookup.sh" "$ip"
|
||||||
;;
|
;;
|
||||||
5) run_module "security" "ddos-detector.sh" ;;
|
6) run_module "security" "ddos-detector.sh" ;;
|
||||||
6) run_module "security" "traffic-pattern-analysis.sh" ;;
|
7) run_module "security" "traffic-pattern-analysis.sh" ;;
|
||||||
7) run_module "security" "user-agent-analysis.sh" ;;
|
8) run_module "security" "user-agent-analysis.sh" ;;
|
||||||
0) return ;;
|
0) return ;;
|
||||||
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
esac
|
esac
|
||||||
@@ -1148,20 +1266,78 @@ handle_wordpress_menu() {
|
|||||||
|
|
||||||
case $choice in
|
case $choice in
|
||||||
1) run_module "website" "website-error-analyzer.sh" ;;
|
1) run_module "website" "website-error-analyzer.sh" ;;
|
||||||
2) run_module "wordpress" "wp-health-check.sh" ;;
|
2) run_module "website" "500-error-tracker.sh" ;;
|
||||||
3) run_module "wordpress" "wp-cron-status.sh" ;;
|
3) run_module "wordpress" "wp-debug-log-analyzer.sh" ;;
|
||||||
4) run_module "wordpress" "wp-cron-mass-fix.sh" ;;
|
4) handle_wp_health_menu ;;
|
||||||
5) run_module "wordpress" "wp-cron-mass-create.sh" ;;
|
5) handle_wp_cron_menu ;;
|
||||||
6) run_module "wordpress" "wp-plugin-audit.sh" ;;
|
6) handle_wp_updates_menu ;;
|
||||||
7) run_module "wordpress" "wp-theme-audit.sh" ;;
|
7) handle_wp_security_menu ;;
|
||||||
8) run_module "wordpress" "wp-db-optimizer.sh" ;;
|
0) return ;;
|
||||||
9) run_module "wordpress" "wp-cache-clear.sh" ;;
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
10) run_module "wordpress" "wp-mass-update-core.sh" ;;
|
esac
|
||||||
11) run_module "wordpress" "wp-mass-update-plugins.sh" ;;
|
done
|
||||||
12) run_module "wordpress" "wp-login-security.sh" ;;
|
}
|
||||||
13) run_module "wordpress" "wp-malware-scanner.sh" ;;
|
|
||||||
14) run_module "wordpress" "wp-permission-fixer.sh" ;;
|
# WP Health & Maintenance submenu handler
|
||||||
15) run_module "wordpress" "wp-debug-log-analyzer.sh" ;;
|
handle_wp_health_menu() {
|
||||||
|
while true; do
|
||||||
|
show_wp_health_menu
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) run_module "wordpress" "wp-health-check.sh" ;;
|
||||||
|
2) run_module "wordpress" "wp-db-optimizer.sh" ;;
|
||||||
|
3) run_module "wordpress" "wp-cache-clear.sh" ;;
|
||||||
|
4) run_module "wordpress" "wp-plugin-audit.sh" ;;
|
||||||
|
5) run_module "wordpress" "wp-theme-audit.sh" ;;
|
||||||
|
0) return ;;
|
||||||
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# WP-Cron Management submenu handler
|
||||||
|
handle_wp_cron_menu() {
|
||||||
|
while true; do
|
||||||
|
show_wp_cron_menu
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) run_module "wordpress" "wp-cron-status.sh" ;;
|
||||||
|
2) run_module "wordpress" "wp-cron-mass-fix.sh" ;;
|
||||||
|
3) run_module "wordpress" "wp-cron-mass-create.sh" ;;
|
||||||
|
0) return ;;
|
||||||
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mass Updates submenu handler
|
||||||
|
handle_wp_updates_menu() {
|
||||||
|
while true; do
|
||||||
|
show_wp_updates_menu
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) run_module "wordpress" "wp-mass-update-core.sh" ;;
|
||||||
|
2) run_module "wordpress" "wp-mass-update-plugins.sh" ;;
|
||||||
|
3) run_module "wordpress" "wp-mass-update-themes.sh" ;;
|
||||||
|
0) return ;;
|
||||||
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security & Compliance submenu handler
|
||||||
|
handle_wp_security_menu() {
|
||||||
|
while true; do
|
||||||
|
show_wp_security_menu
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) run_module "wordpress" "wp-malware-scanner.sh" ;;
|
||||||
|
2) run_module "wordpress" "wp-permission-fixer.sh" ;;
|
||||||
|
3) run_module "wordpress" "wp-login-security.sh" ;;
|
||||||
0) return ;;
|
0) return ;;
|
||||||
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
esac
|
esac
|
||||||
@@ -1206,6 +1382,8 @@ handle_backup_menu() {
|
|||||||
6) run_module "backup" "log-archive.sh" ;;
|
6) run_module "backup" "log-archive.sh" ;;
|
||||||
7) run_module "backup" "backup-verification.sh" ;;
|
7) run_module "backup" "backup-verification.sh" ;;
|
||||||
8) run_module "backup" "offsite-sync.sh" ;;
|
8) run_module "backup" "offsite-sync.sh" ;;
|
||||||
|
9) handle_acronis_menu ;;
|
||||||
|
10) run_module "maintenance" "cleanup-toolkit-data.sh" ;;
|
||||||
0) return ;;
|
0) return ;;
|
||||||
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
*) echo -e "${RED}Invalid option${NC}"; sleep 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -0,0 +1,575 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# IP Reputation Management Library
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Centralized IP reputation tracking across all toolkit scripts
|
||||||
|
# Features:
|
||||||
|
# - Fast lookups using indexed file structure
|
||||||
|
# - Tracks: hits, country, last seen, reputation score, attack types
|
||||||
|
# - Optimized for high-volume traffic (attacks with thousands of IPs)
|
||||||
|
# - Automatic cleanup of old entries
|
||||||
|
# - GeoIP integration
|
||||||
|
# - Shared across all monitoring/analysis scripts
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Database location
|
||||||
|
IP_REP_DB_DIR="${IP_REP_DB_DIR:-/var/lib/server-toolkit/ip-reputation}"
|
||||||
|
IP_REP_DB="$IP_REP_DB_DIR/ip_database.db"
|
||||||
|
IP_REP_INDEX="$IP_REP_DB_DIR/ip_index.idx"
|
||||||
|
IP_REP_LOCK="$IP_REP_DB_DIR/.db.lock"
|
||||||
|
|
||||||
|
# Reputation score thresholds
|
||||||
|
REP_SCORE_CRITICAL=80 # Definitely malicious
|
||||||
|
REP_SCORE_HIGH=60 # Likely malicious
|
||||||
|
REP_SCORE_MEDIUM=40 # Suspicious
|
||||||
|
REP_SCORE_LOW=20 # Borderline
|
||||||
|
REP_SCORE_SAFE=0 # Safe/legitimate
|
||||||
|
|
||||||
|
# Attack type flags (bitmask for efficient storage)
|
||||||
|
ATTACK_FLAG_SQL_INJECTION=1
|
||||||
|
ATTACK_FLAG_XSS=2
|
||||||
|
ATTACK_FLAG_PATH_TRAVERSAL=4
|
||||||
|
ATTACK_FLAG_RCE=8
|
||||||
|
ATTACK_FLAG_BRUTEFORCE=16
|
||||||
|
ATTACK_FLAG_DDOS=32
|
||||||
|
ATTACK_FLAG_BOT=64
|
||||||
|
ATTACK_FLAG_SCANNER=128
|
||||||
|
ATTACK_FLAG_EXPLOIT=256
|
||||||
|
|
||||||
|
# Initialize the IP reputation database
|
||||||
|
init_ip_reputation_db() {
|
||||||
|
mkdir -p "$IP_REP_DB_DIR" 2>/dev/null
|
||||||
|
|
||||||
|
# Create empty database if it doesn't exist
|
||||||
|
if [ ! -f "$IP_REP_DB" ]; then
|
||||||
|
touch "$IP_REP_DB"
|
||||||
|
chmod 600 "$IP_REP_DB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$IP_REP_INDEX" ]; then
|
||||||
|
touch "$IP_REP_INDEX"
|
||||||
|
chmod 600 "$IP_REP_INDEX"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Database format (pipe-delimited for fast parsing):
|
||||||
|
# IP|HIT_COUNT|REPUTATION_SCORE|COUNTRY|ATTACK_FLAGS|FIRST_SEEN|LAST_SEEN|LAST_ACTIVITY|NOTES
|
||||||
|
# Example:
|
||||||
|
# 192.168.1.100|523|75|US|193|1730000000|1730800000|SQL injection on /admin|Auto-flagged
|
||||||
|
|
||||||
|
# Lock management for concurrent access
|
||||||
|
acquire_lock() {
|
||||||
|
local timeout=10
|
||||||
|
local elapsed=0
|
||||||
|
|
||||||
|
while [ -f "$IP_REP_LOCK" ] && [ $elapsed -lt $timeout ]; do
|
||||||
|
sleep 0.1
|
||||||
|
elapsed=$((elapsed + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $elapsed -ge $timeout ]; then
|
||||||
|
# Stale lock, remove it
|
||||||
|
rm -f "$IP_REP_LOCK" 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch "$IP_REP_LOCK"
|
||||||
|
}
|
||||||
|
|
||||||
|
release_lock() {
|
||||||
|
rm -f "$IP_REP_LOCK" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fast IP lookup using hash-based index for O(1) lookups
|
||||||
|
# Returns: IP data if found, empty if not found
|
||||||
|
lookup_ip() {
|
||||||
|
local ip="$1"
|
||||||
|
|
||||||
|
[ -z "$ip" ] && return 1
|
||||||
|
[ ! -f "$IP_REP_DB" ] && return 1
|
||||||
|
|
||||||
|
# Calculate hash bucket (first octet for IPv4 distributes IPs across 256 buckets)
|
||||||
|
local hash_bucket="${ip%%.*}"
|
||||||
|
local hash_file="${IP_REP_DB_DIR}/hash_${hash_bucket}.idx"
|
||||||
|
|
||||||
|
# Fast path: Check hash bucket first (much smaller file to grep)
|
||||||
|
if [ -f "$hash_file" ]; then
|
||||||
|
# Hash bucket contains line numbers for IPs in this bucket
|
||||||
|
local line_num=$(grep -m 1 "^${ip}|" "$hash_file" 2>/dev/null | cut -d'|' -f2)
|
||||||
|
if [ -n "$line_num" ]; then
|
||||||
|
# Direct line access - O(1) lookup!
|
||||||
|
sed -n "${line_num}p" "$IP_REP_DB" 2>/dev/null
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback: Linear search (for IPs not yet indexed)
|
||||||
|
# Use tac to read file backwards, then grep for first match
|
||||||
|
# This ensures we get the LATEST entry for IPs with duplicates
|
||||||
|
tac "$IP_REP_DB" 2>/dev/null | grep -m 1 "^${ip}|" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add or update IP in database
|
||||||
|
# Usage: update_ip_reputation IP [HIT_INCREMENT] [SCORE_DELTA] [ATTACK_FLAGS] [ACTIVITY_NOTE]
|
||||||
|
update_ip_reputation() {
|
||||||
|
local ip="$1"
|
||||||
|
local hit_increment="${2:-1}"
|
||||||
|
local score_delta="${3:-0}"
|
||||||
|
local new_attack_flags="${4:-0}"
|
||||||
|
local activity_note="${5:-}"
|
||||||
|
|
||||||
|
[ -z "$ip" ] && return 1
|
||||||
|
|
||||||
|
init_ip_reputation_db
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
|
local existing
|
||||||
|
existing=$(lookup_ip "$ip")
|
||||||
|
|
||||||
|
local current_time=$(date +%s)
|
||||||
|
|
||||||
|
if [ -n "$existing" ]; then
|
||||||
|
# Parse existing entry
|
||||||
|
IFS='|' read -r old_ip hit_count rep_score country attack_flags first_seen last_seen last_activity notes <<< "$existing"
|
||||||
|
|
||||||
|
# Update values
|
||||||
|
hit_count=$((hit_count + hit_increment))
|
||||||
|
rep_score=$((rep_score + score_delta))
|
||||||
|
|
||||||
|
# Cap reputation score at 0-100
|
||||||
|
[ $rep_score -lt 0 ] && rep_score=0
|
||||||
|
[ $rep_score -gt 100 ] && rep_score=100
|
||||||
|
|
||||||
|
# Merge attack flags (bitwise OR)
|
||||||
|
attack_flags=$((attack_flags | new_attack_flags))
|
||||||
|
|
||||||
|
last_seen="$current_time"
|
||||||
|
|
||||||
|
# Update activity note if provided
|
||||||
|
if [ -n "$activity_note" ]; then
|
||||||
|
last_activity="$activity_note"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OPTIMIZATION: Append-only writes (much faster than sed -i delete)
|
||||||
|
# Append updated entry to end of file
|
||||||
|
echo "$ip|$hit_count|$rep_score|$country|$attack_flags|$first_seen|$last_seen|$last_activity|$notes" >> "$IP_REP_DB"
|
||||||
|
|
||||||
|
# Mark for compaction (file will have duplicates until compact_database runs)
|
||||||
|
touch "${IP_REP_DB}.needs_compact" 2>/dev/null
|
||||||
|
else
|
||||||
|
# New entry
|
||||||
|
local country=$(get_ip_country "$ip")
|
||||||
|
echo "$ip|$hit_increment|$score_delta|$country|$new_attack_flags|$current_time|$current_time|$activity_note|" >> "$IP_REP_DB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
release_lock
|
||||||
|
|
||||||
|
# Auto-compact if file has lots of duplicates (from append-only writes)
|
||||||
|
# Check if compaction is needed (marked file exists)
|
||||||
|
if [ -f "${IP_REP_DB}.needs_compact" ]; then
|
||||||
|
local db_size=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
# Compact if database >50k lines (likely has significant duplicates)
|
||||||
|
# Use random check to avoid all processes compacting simultaneously
|
||||||
|
if [ "$db_size" -gt 50000 ] && [ $((RANDOM % 200)) -eq 0 ]; then
|
||||||
|
compact_database & # Background process (includes rebuild_index)
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rebuild index automatically when database grows significantly
|
||||||
|
# Check if hash index exists and is fresh
|
||||||
|
local db_size=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo "0")
|
||||||
|
local hash_count=$(ls -1 "${IP_REP_DB_DIR}"/hash_*.idx 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
# Rebuild if:
|
||||||
|
# 1. Database has >10k IPs but no hash index exists
|
||||||
|
# 2. Database has >100k IPs and 1% chance (frequent enough during attacks)
|
||||||
|
if [ "$hash_count" -eq 0 ] && [ "$db_size" -gt 10000 ]; then
|
||||||
|
rebuild_index & # Background process
|
||||||
|
elif [ "$db_size" -gt 100000 ] && [ $((RANDOM % 100)) -eq 0 ]; then
|
||||||
|
rebuild_index & # Background process
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get IP country using multiple methods
|
||||||
|
get_ip_country() {
|
||||||
|
local ip="$1"
|
||||||
|
local country="??"
|
||||||
|
|
||||||
|
# Method 1: Check if geoiplookup is available
|
||||||
|
if command -v geoiplookup >/dev/null 2>&1; then
|
||||||
|
country=$(geoiplookup "$ip" 2>/dev/null | grep -oP 'Country Edition: \K[A-Z]{2}' | head -1)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Method 2: Check if geoiplookup6 for IPv6
|
||||||
|
if [ -z "$country" ] || [ "$country" = "??" ]; then
|
||||||
|
if command -v geoiplookup6 >/dev/null 2>&1 && [[ "$ip" =~ : ]]; then
|
||||||
|
country=$(geoiplookup6 "$ip" 2>/dev/null | grep -oP 'Country Edition: \K[A-Z]{2}' | head -1)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Method 3: Check /usr/share/GeoIP databases directly
|
||||||
|
if [ -z "$country" ] || [ "$country" = "??" ]; then
|
||||||
|
if [ -f "/usr/share/GeoIP/GeoIP.dat" ] && command -v geoiplookup >/dev/null 2>&1; then
|
||||||
|
country=$(geoiplookup "$ip" 2>/dev/null | awk -F': ' '{print $2}' | cut -d',' -f1 | head -1)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Method 4: Fallback - use whois (slower, only if critically needed)
|
||||||
|
# Disabled by default for performance
|
||||||
|
# if [ -z "$country" ] || [ "$country" = "??" ]; then
|
||||||
|
# country=$(whois "$ip" 2>/dev/null | grep -iE "^country:" | head -1 | awk '{print $2}')
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# Default if all methods fail
|
||||||
|
[ -z "$country" ] && country="??"
|
||||||
|
|
||||||
|
echo "$country"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Increment IP hit count (fast path for common case)
|
||||||
|
increment_ip_hits() {
|
||||||
|
local ip="$1"
|
||||||
|
local increment="${2:-1}"
|
||||||
|
|
||||||
|
update_ip_reputation "$ip" "$increment" 0 0 ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Flag IP for specific attack type
|
||||||
|
flag_ip_attack() {
|
||||||
|
local ip="$1"
|
||||||
|
local attack_type="$2"
|
||||||
|
local score_increase="${3:-5}"
|
||||||
|
local note="${4:-$attack_type}"
|
||||||
|
|
||||||
|
local attack_flag=0
|
||||||
|
|
||||||
|
case "$attack_type" in
|
||||||
|
SQL_INJECTION|sql) attack_flag=$ATTACK_FLAG_SQL_INJECTION; score_increase=15 ;;
|
||||||
|
XSS|xss) attack_flag=$ATTACK_FLAG_XSS; score_increase=10 ;;
|
||||||
|
PATH_TRAVERSAL|path) attack_flag=$ATTACK_FLAG_PATH_TRAVERSAL; score_increase=12 ;;
|
||||||
|
RCE|rce|shell) attack_flag=$ATTACK_FLAG_RCE; score_increase=20 ;;
|
||||||
|
BRUTEFORCE|brute) attack_flag=$ATTACK_FLAG_BRUTEFORCE; score_increase=8 ;;
|
||||||
|
DDOS|ddos) attack_flag=$ATTACK_FLAG_DDOS; score_increase=10 ;;
|
||||||
|
BOT|bot) attack_flag=$ATTACK_FLAG_BOT; score_increase=3 ;;
|
||||||
|
SCANNER|scan) attack_flag=$ATTACK_FLAG_SCANNER; score_increase=5 ;;
|
||||||
|
EXPLOIT|exploit) attack_flag=$ATTACK_FLAG_EXPLOIT; score_increase=15 ;;
|
||||||
|
*) attack_flag=0; score_increase=5 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
update_ip_reputation "$ip" 1 "$score_increase" "$attack_flag" "$note"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mark IP as legitimate (reduces reputation score)
|
||||||
|
mark_ip_legitimate() {
|
||||||
|
local ip="$1"
|
||||||
|
local note="${2:-Marked as legitimate}"
|
||||||
|
|
||||||
|
update_ip_reputation "$ip" 0 -20 0 "$note"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get IP reputation category
|
||||||
|
get_ip_reputation_category() {
|
||||||
|
local score="$1"
|
||||||
|
|
||||||
|
if [ $score -ge $REP_SCORE_CRITICAL ]; then
|
||||||
|
echo "CRITICAL"
|
||||||
|
elif [ $score -ge $REP_SCORE_HIGH ]; then
|
||||||
|
echo "HIGH"
|
||||||
|
elif [ $score -ge $REP_SCORE_MEDIUM ]; then
|
||||||
|
echo "MEDIUM"
|
||||||
|
elif [ $score -ge $REP_SCORE_LOW ]; then
|
||||||
|
echo "LOW"
|
||||||
|
else
|
||||||
|
echo "SAFE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get attack types from flags
|
||||||
|
decode_attack_flags() {
|
||||||
|
local flags="$1"
|
||||||
|
local attacks=""
|
||||||
|
|
||||||
|
[ $((flags & ATTACK_FLAG_SQL_INJECTION)) -ne 0 ] && attacks="${attacks}SQL,"
|
||||||
|
[ $((flags & ATTACK_FLAG_XSS)) -ne 0 ] && attacks="${attacks}XSS,"
|
||||||
|
[ $((flags & ATTACK_FLAG_PATH_TRAVERSAL)) -ne 0 ] && attacks="${attacks}PATH,"
|
||||||
|
[ $((flags & ATTACK_FLAG_RCE)) -ne 0 ] && attacks="${attacks}RCE,"
|
||||||
|
[ $((flags & ATTACK_FLAG_BRUTEFORCE)) -ne 0 ] && attacks="${attacks}BRUTE,"
|
||||||
|
[ $((flags & ATTACK_FLAG_DDOS)) -ne 0 ] && attacks="${attacks}DDOS,"
|
||||||
|
[ $((flags & ATTACK_FLAG_BOT)) -ne 0 ] && attacks="${attacks}BOT,"
|
||||||
|
[ $((flags & ATTACK_FLAG_SCANNER)) -ne 0 ] && attacks="${attacks}SCAN,"
|
||||||
|
[ $((flags & ATTACK_FLAG_EXPLOIT)) -ne 0 ] && attacks="${attacks}EXPLOIT,"
|
||||||
|
|
||||||
|
# Remove trailing comma
|
||||||
|
attacks="${attacks%,}"
|
||||||
|
|
||||||
|
[ -z "$attacks" ] && attacks="NONE"
|
||||||
|
|
||||||
|
echo "$attacks"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Query and display IP information
|
||||||
|
query_ip_reputation() {
|
||||||
|
local ip="$1"
|
||||||
|
|
||||||
|
init_ip_reputation_db
|
||||||
|
|
||||||
|
local data
|
||||||
|
data=$(lookup_ip "$ip")
|
||||||
|
|
||||||
|
if [ -z "$data" ]; then
|
||||||
|
echo "IP $ip not found in reputation database"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS='|' read -r ip hit_count rep_score country attack_flags first_seen last_seen last_activity notes <<< "$data"
|
||||||
|
|
||||||
|
local category=$(get_ip_reputation_category "$rep_score")
|
||||||
|
local attacks=$(decode_attack_flags "$attack_flags")
|
||||||
|
local first_seen_date=$(date -d "@$first_seen" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$first_seen")
|
||||||
|
local last_seen_date=$(date -d "@$last_seen" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$last_seen")
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "IP Address: $ip"
|
||||||
|
echo "Country: $country"
|
||||||
|
echo "Reputation: $rep_score/100 [$category]"
|
||||||
|
echo "Total Hits: $hit_count"
|
||||||
|
echo "Attack Types: $attacks"
|
||||||
|
echo "First Seen: $first_seen_date"
|
||||||
|
echo "Last Seen: $last_seen_date"
|
||||||
|
echo "Last Activity: ${last_activity:-None recorded}"
|
||||||
|
echo "Notes: ${notes:-None}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get top IPs by reputation score
|
||||||
|
get_top_malicious_ips() {
|
||||||
|
local limit="${1:-20}"
|
||||||
|
|
||||||
|
init_ip_reputation_db
|
||||||
|
|
||||||
|
[ ! -f "$IP_REP_DB" ] && return 1
|
||||||
|
|
||||||
|
# OPTIMIZATION: For large files, use partial sort (much faster)
|
||||||
|
# Only sort enough to find top N instead of sorting entire file
|
||||||
|
local db_size=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
if [ "$db_size" -gt 100000 ]; then
|
||||||
|
# For very large databases, use awk to find high-scoring IPs first
|
||||||
|
# then sort only those (much faster than sorting 500k lines)
|
||||||
|
awk -F'|' '$3 >= 50' "$IP_REP_DB" | sort -t'|' -k3 -rn | head -n "$limit"
|
||||||
|
else
|
||||||
|
# For smaller databases, regular sort is fine
|
||||||
|
sort -t'|' -k3 -rn "$IP_REP_DB" | head -n "$limit"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get top IPs by hit count
|
||||||
|
get_top_active_ips() {
|
||||||
|
local limit="${1:-20}"
|
||||||
|
|
||||||
|
init_ip_reputation_db
|
||||||
|
|
||||||
|
[ ! -f "$IP_REP_DB" ] && return 1
|
||||||
|
|
||||||
|
# OPTIMIZATION: For large files, filter first then sort
|
||||||
|
local db_size=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
if [ "$db_size" -gt 100000 ]; then
|
||||||
|
# Filter to IPs with >100 hits, then sort (much faster)
|
||||||
|
awk -F'|' '$2 >= 100' "$IP_REP_DB" | sort -t'|' -k2 -rn | head -n "$limit"
|
||||||
|
else
|
||||||
|
# For smaller databases, regular sort is fine
|
||||||
|
sort -t'|' -k2 -rn "$IP_REP_DB" | head -n "$limit"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up old entries (not seen in X days)
|
||||||
|
cleanup_old_ips() {
|
||||||
|
local days_old="${1:-90}"
|
||||||
|
|
||||||
|
init_ip_reputation_db
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
|
local cutoff_time=$(($(date +%s) - (days_old * 86400)))
|
||||||
|
local temp_file="${IP_REP_DB}.tmp"
|
||||||
|
|
||||||
|
# Keep only IPs seen within the cutoff time
|
||||||
|
awk -F'|' -v cutoff="$cutoff_time" '$7 >= cutoff' "$IP_REP_DB" > "$temp_file"
|
||||||
|
|
||||||
|
mv "$temp_file" "$IP_REP_DB"
|
||||||
|
|
||||||
|
release_lock
|
||||||
|
|
||||||
|
echo "Cleaned up IPs not seen in $days_old days"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compact database to remove duplicate IP entries (from append-only writes)
|
||||||
|
compact_database() {
|
||||||
|
init_ip_reputation_db
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
|
echo "Compacting database (removing duplicate IP entries)..."
|
||||||
|
|
||||||
|
local temp_db="${IP_REP_DB}.compact_tmp"
|
||||||
|
local original_size=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
# Use awk to keep only the LAST occurrence of each IP (most recent data)
|
||||||
|
# Read file backwards, keep first occurrence of each IP, then reverse again
|
||||||
|
tac "$IP_REP_DB" | awk -F'|' '!seen[$1]++' | tac > "$temp_db"
|
||||||
|
|
||||||
|
# Replace original with compacted version
|
||||||
|
mv "$temp_db" "$IP_REP_DB"
|
||||||
|
|
||||||
|
local new_size=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo "0")
|
||||||
|
local removed=$((original_size - new_size))
|
||||||
|
|
||||||
|
# Remove compaction marker
|
||||||
|
rm -f "${IP_REP_DB}.needs_compact" 2>/dev/null
|
||||||
|
|
||||||
|
release_lock
|
||||||
|
|
||||||
|
echo "Compaction complete: Removed $removed duplicate entries ($original_size → $new_size IPs)"
|
||||||
|
|
||||||
|
# Rebuild index after compaction
|
||||||
|
rebuild_index
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rebuild index for faster lookups (for very large databases)
|
||||||
|
rebuild_index() {
|
||||||
|
init_ip_reputation_db
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
|
echo "Rebuilding hash-based index for fast lookups..."
|
||||||
|
|
||||||
|
# Remove old hash files
|
||||||
|
rm -f "${IP_REP_DB_DIR}"/hash_*.idx 2>/dev/null
|
||||||
|
|
||||||
|
# Build hash buckets (256 buckets based on first octet)
|
||||||
|
# This distributes 500k IPs into ~2k IPs per bucket = MUCH faster
|
||||||
|
local line_num=0
|
||||||
|
while IFS='|' read -r ip rest; do
|
||||||
|
((line_num++))
|
||||||
|
|
||||||
|
# Calculate hash bucket from first octet
|
||||||
|
local hash_bucket="${ip%%.*}"
|
||||||
|
local hash_file="${IP_REP_DB_DIR}/hash_${hash_bucket}.idx"
|
||||||
|
|
||||||
|
# Store IP and its line number in the hash bucket file
|
||||||
|
echo "${ip}|${line_num}" >> "$hash_file"
|
||||||
|
done < "$IP_REP_DB"
|
||||||
|
|
||||||
|
# Sort each hash bucket file for faster grep
|
||||||
|
for hash_file in "${IP_REP_DB_DIR}"/hash_*.idx; do
|
||||||
|
[ -f "$hash_file" ] && sort -t'|' -k1 -o "$hash_file" "$hash_file"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Also create main sorted index for compatibility
|
||||||
|
sort -t'|' -k1 "$IP_REP_DB" > "$IP_REP_INDEX"
|
||||||
|
|
||||||
|
release_lock
|
||||||
|
|
||||||
|
echo "Index rebuilt: $(ls -1 "${IP_REP_DB_DIR}"/hash_*.idx 2>/dev/null | wc -l) hash buckets created"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export reputation database to readable format
|
||||||
|
export_ip_reputation() {
|
||||||
|
local output_file="${1:-/tmp/ip_reputation_export_$(date +%Y%m%d_%H%M%S).txt}"
|
||||||
|
|
||||||
|
init_ip_reputation_db
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "SERVER TOOLKIT - IP REPUTATION DATABASE EXPORT"
|
||||||
|
echo "Generated: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "Total IPs: $(wc -l < "$IP_REP_DB" 2>/dev/null || echo 0)"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
printf "%-15s | %-7s | %-4s | %-8s | %-6s | %-30s | %-19s\n" \
|
||||||
|
"IP ADDRESS" "HITS" "CTRY" "REP" "LEVEL" "ATTACKS" "LAST SEEN"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
# Sort by reputation score, descending
|
||||||
|
sort -t'|' -k3 -rn "$IP_REP_DB" | while IFS='|' read -r ip hit_count rep_score country attack_flags first_seen last_seen last_activity notes; do
|
||||||
|
local category=$(get_ip_reputation_category "$rep_score")
|
||||||
|
local attacks=$(decode_attack_flags "$attack_flags")
|
||||||
|
local last_seen_date=$(date -d "@$last_seen" '+%Y-%m-%d %H:%M' 2>/dev/null || echo "$last_seen")
|
||||||
|
|
||||||
|
printf "%-15s | %-7s | %-4s | %-3s/100 | %-8s | %-30s | %-19s\n" \
|
||||||
|
"$ip" "$hit_count" "$country" "$rep_score" "$category" "${attacks:0:30}" "$last_seen_date"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
} > "$output_file"
|
||||||
|
|
||||||
|
echo "IP reputation database exported to: $output_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if IP should be blocked (based on reputation)
|
||||||
|
should_block_ip() {
|
||||||
|
local ip="$1"
|
||||||
|
local threshold="${2:-$REP_SCORE_HIGH}" # Default: block if reputation >= 60
|
||||||
|
|
||||||
|
local data
|
||||||
|
data=$(lookup_ip "$ip")
|
||||||
|
|
||||||
|
[ -z "$data" ] && return 1 # Unknown IP, don't block
|
||||||
|
|
||||||
|
IFS='|' read -r _ _ rep_score _ _ _ _ _ _ <<< "$data"
|
||||||
|
|
||||||
|
[ $rep_score -ge $threshold ] && return 0 # Should block
|
||||||
|
return 1 # Should not block
|
||||||
|
}
|
||||||
|
|
||||||
|
# Batch import IPs from various sources
|
||||||
|
import_ips_from_log() {
|
||||||
|
local log_file="$1"
|
||||||
|
local attack_type="${2:-SUSPICIOUS}"
|
||||||
|
local score_per_hit="${3:-5}"
|
||||||
|
|
||||||
|
[ ! -f "$log_file" ] && return 1
|
||||||
|
|
||||||
|
# Extract IPs and count occurrences
|
||||||
|
grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' "$log_file" | \
|
||||||
|
sort | uniq -c | while read count ip; do
|
||||||
|
update_ip_reputation "$ip" "$count" "$score_per_hit" 0 "Imported from $log_file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Imported IPs from $log_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Statistics summary
|
||||||
|
show_ip_statistics() {
|
||||||
|
init_ip_reputation_db
|
||||||
|
|
||||||
|
local total_ips=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo 0)
|
||||||
|
local critical=$(awk -F'|' -v thresh=$REP_SCORE_CRITICAL '$3 >= thresh' "$IP_REP_DB" 2>/dev/null | wc -l)
|
||||||
|
local high=$(awk -F'|' -v low=$REP_SCORE_HIGH -v hi=$REP_SCORE_CRITICAL '$3 >= low && $3 < hi' "$IP_REP_DB" 2>/dev/null | wc -l)
|
||||||
|
local medium=$(awk -F'|' -v low=$REP_SCORE_MEDIUM -v hi=$REP_SCORE_HIGH '$3 >= low && $3 < hi' "$IP_REP_DB" 2>/dev/null | wc -l)
|
||||||
|
local low=$(awk -F'|' -v low=$REP_SCORE_LOW -v hi=$REP_SCORE_MEDIUM '$3 >= low && $3 < hi' "$IP_REP_DB" 2>/dev/null | wc -l)
|
||||||
|
local safe=$(awk -F'|' -v thresh=$REP_SCORE_LOW '$3 < thresh' "$IP_REP_DB" 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "IP REPUTATION DATABASE STATISTICS"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Total Tracked IPs: $total_ips"
|
||||||
|
echo ""
|
||||||
|
echo "By Reputation Level:"
|
||||||
|
echo " CRITICAL (≥80): $critical"
|
||||||
|
echo " HIGH (60-79): $high"
|
||||||
|
echo " MEDIUM (40-59): $medium"
|
||||||
|
echo " LOW (20-39): $low"
|
||||||
|
echo " SAFE (<20): $safe"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize on library load
|
||||||
|
init_ip_reputation_db
|
||||||
Executable
+268
@@ -0,0 +1,268 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Agent Status Checker
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Check status of all Acronis Cyber Protect services
|
||||||
|
# Services monitored:
|
||||||
|
# - aakore (Acronis Agent Core)
|
||||||
|
# - acronis_mms (Management Service)
|
||||||
|
# - acronis_schedule (Scheduler)
|
||||||
|
# - active-protection.service (Ransomware Protection)
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Acronis Agent Status"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Checking Acronis Cyber Protect Services...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Array of services to check
|
||||||
|
declare -a SERVICES=(
|
||||||
|
"aakore:Acronis Agent Core"
|
||||||
|
"acronis_mms:Management Service"
|
||||||
|
"acronis_schedule:Backup Scheduler"
|
||||||
|
"active-protection:Ransomware Protection"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Track overall status
|
||||||
|
all_running=true
|
||||||
|
any_installed=false
|
||||||
|
|
||||||
|
# Function to check service status
|
||||||
|
check_service_status() {
|
||||||
|
local service_name="$1"
|
||||||
|
local service_desc="$2"
|
||||||
|
|
||||||
|
# Check if service exists
|
||||||
|
if systemctl list-unit-files | grep -q "^${service_name}.service"; then
|
||||||
|
any_installed=true
|
||||||
|
|
||||||
|
# Get service status
|
||||||
|
if systemctl is-active --quiet "$service_name"; then
|
||||||
|
echo -e " ${GREEN}●${NC} ${BOLD}${service_desc}${NC}"
|
||||||
|
echo -e " Status: ${GREEN}RUNNING${NC}"
|
||||||
|
|
||||||
|
# Get uptime
|
||||||
|
local uptime=$(systemctl show "$service_name" -p ActiveEnterTimestamp --value)
|
||||||
|
if [ -n "$uptime" ]; then
|
||||||
|
echo -e " Uptime: ${uptime}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get PID
|
||||||
|
local pid=$(systemctl show "$service_name" -p MainPID --value)
|
||||||
|
if [ "$pid" != "0" ]; then
|
||||||
|
echo -e " PID: ${pid}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
all_running=false
|
||||||
|
echo -e " ${RED}●${NC} ${BOLD}${service_desc}${NC}"
|
||||||
|
echo -e " Status: ${RED}STOPPED${NC}"
|
||||||
|
|
||||||
|
# Check if failed
|
||||||
|
if systemctl is-failed --quiet "$service_name"; then
|
||||||
|
echo -e " ${RED}[FAILED]${NC} - Service has errors"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
elif service "$service_name" status &>/dev/null; then
|
||||||
|
# Fallback to service command for older systems
|
||||||
|
any_installed=true
|
||||||
|
if service "$service_name" status | grep -q "running"; then
|
||||||
|
echo -e " ${GREEN}●${NC} ${BOLD}${service_desc}${NC}"
|
||||||
|
echo -e " Status: ${GREEN}RUNNING${NC}"
|
||||||
|
else
|
||||||
|
all_running=false
|
||||||
|
echo -e " ${RED}●${NC} ${BOLD}${service_desc}${NC}"
|
||||||
|
echo -e " Status: ${RED}STOPPED${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check each service
|
||||||
|
for service_entry in "${SERVICES[@]}"; do
|
||||||
|
IFS=':' read -r service_name service_desc <<< "$service_entry"
|
||||||
|
check_service_status "$service_name" "$service_desc"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if Acronis is even installed
|
||||||
|
if [ "$any_installed" = false ]; then
|
||||||
|
echo -e "${YELLOW}${BOLD}⚠ Acronis Agent Not Installed${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Acronis Cyber Protect is not installed on this system."
|
||||||
|
echo ""
|
||||||
|
echo "To install:"
|
||||||
|
echo " 1. Return to Backup & Recovery menu"
|
||||||
|
echo " 2. Select 'Acronis Management'"
|
||||||
|
echo " 3. Choose 'Install Acronis Agent'"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Overall status summary
|
||||||
|
if [ "$all_running" = true ]; then
|
||||||
|
echo -e "${GREEN}${BOLD}✓ All Services Running${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Acronis Cyber Protect is operational and ready for backups."
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}${BOLD}⚠ Some Services Not Running${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Some Acronis services are stopped. You may want to:"
|
||||||
|
echo " • Start services: Select 'Service Management' from Acronis menu"
|
||||||
|
echo " • Check logs: Select 'View Logs' for error details"
|
||||||
|
echo " • Restart services: Try restarting all services"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check agent registration status
|
||||||
|
echo -e "${BOLD}Agent Registration:${NC}"
|
||||||
|
if [ -f "/var/lib/Acronis/BackupAndRecovery/MMS/user.config" ]; then
|
||||||
|
# Check for registration info in user.config
|
||||||
|
if grep -q "<registration>" "/var/lib/Acronis/BackupAndRecovery/MMS/user.config" 2>/dev/null; then
|
||||||
|
reg_address=$(grep -oP '<address>\K[^<]+' /var/lib/Acronis/BackupAndRecovery/MMS/user.config 2>/dev/null)
|
||||||
|
reg_env=$(grep -oP '<environment>\K[^<]+' /var/lib/Acronis/BackupAndRecovery/MMS/user.config 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$reg_address" ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Agent is registered with Acronis Cloud"
|
||||||
|
echo -e " URL: ${reg_address}"
|
||||||
|
[ -n "$reg_env" ] && echo -e " Environment: ${reg_env}"
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} Registration incomplete"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} Agent not registered"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} Configuration file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check active ports
|
||||||
|
echo -e "${BOLD}Network Connectivity:${NC}"
|
||||||
|
|
||||||
|
# Check for actual Acronis listening ports (deduplicate IPv4/IPv6)
|
||||||
|
acronis_ports=$(netstat -tlnp 2>/dev/null | grep -E "(acronis|mms|aakore)" | awk '{
|
||||||
|
split($4, addr, ":");
|
||||||
|
port = addr[length(addr)];
|
||||||
|
if (!seen[port]++) {
|
||||||
|
print $4 " " $7;
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
|
||||||
|
if [ -n "$acronis_ports" ]; then
|
||||||
|
echo "Active Acronis services:"
|
||||||
|
echo "$acronis_ports" | while read -r addr process; do
|
||||||
|
port=$(echo "$addr" | grep -oP ':\K[0-9]+$')
|
||||||
|
if echo "$addr" | grep -q "127.0.0.1\|::1"; then
|
||||||
|
# Local-only port
|
||||||
|
if [ "$port" = "9850" ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Port $port (localhost) - MMS Service"
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✓${NC} Port $port (localhost) - $(basename "$process" | cut -d/ -f2)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✓${NC} Port $port - $(basename "$process" | cut -d/ -f2)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} No Acronis ports detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check outbound connectivity to cloud (port 443)
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Cloud Connectivity Test:${NC}"
|
||||||
|
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
reg_url=$(grep -oP '<address>\K[^<]+' /var/lib/Acronis/BackupAndRecovery/MMS/user.config 2>/dev/null)
|
||||||
|
if [ -n "$reg_url" ]; then
|
||||||
|
echo -n " Testing ${reg_url}... "
|
||||||
|
|
||||||
|
http_code=$(timeout 5 curl -s -o /dev/null -w "%{http_code}" "$reg_url" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 500 ]; then
|
||||||
|
echo -e "${GREEN}✓ Reachable${NC} (HTTP $http_code)"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Unreachable${NC}"
|
||||||
|
echo -e " ${YELLOW}⚠${NC} Cannot reach Acronis cloud (firewall/network issue)"
|
||||||
|
echo " • Check internet connectivity"
|
||||||
|
echo " • Verify firewall allows HTTPS (port 443)"
|
||||||
|
echo " • Test manually: curl -I $reg_url"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} Cloud URL not found in config"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} curl not installed (cannot test connectivity)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check cloud storage quota
|
||||||
|
echo -e "${BOLD}Cloud Backup Storage:${NC}"
|
||||||
|
if command -v acrocmd >/dev/null 2>&1; then
|
||||||
|
vault_info=$(acrocmd list vaults 2>/dev/null | tail -n +3 | head -1)
|
||||||
|
|
||||||
|
if [ -n "$vault_info" ]; then
|
||||||
|
# Extract storage info from vault output
|
||||||
|
vault_name=$(echo "$vault_info" | awk '{print $1}')
|
||||||
|
vault_free_val=$(echo "$vault_info" | awk '{print $4}')
|
||||||
|
vault_free_unit=$(echo "$vault_info" | awk '{print $5}')
|
||||||
|
vault_occupied=$(echo "$vault_info" | awk '{print $6, $7}')
|
||||||
|
|
||||||
|
echo -e " Vault: ${vault_name}"
|
||||||
|
echo -e " Available: ${vault_free_val} ${vault_free_unit}"
|
||||||
|
|
||||||
|
# Show occupied if available, otherwise note it's not synced
|
||||||
|
if [ "$vault_occupied" != "0 GB" ]; then
|
||||||
|
echo -e " Used: ${vault_occupied}"
|
||||||
|
else
|
||||||
|
echo -e " Used: ${DIM}(Check web console for accurate usage)${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} No vault information available"
|
||||||
|
echo -e " ${DIM}(Cloud storage visible after first backup)${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}⚠${NC} acrocmd not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check local disk space
|
||||||
|
echo -e "${BOLD}Local Storage Status:${NC}"
|
||||||
|
if [ -d "/var/lib/Acronis" ]; then
|
||||||
|
backup_dir_size=$(du -sh /var/lib/Acronis 2>/dev/null | awk '{print $1}')
|
||||||
|
echo -e " Agent data: ${backup_dir_size} (local cache/logs/config)"
|
||||||
|
|
||||||
|
# Check free space on partition
|
||||||
|
free_space=$(df -h /var/lib/Acronis | tail -1 | awk '{print $4}')
|
||||||
|
use_percent=$(df -h /var/lib/Acronis | tail -1 | awk '{print $5}' | tr -d '%')
|
||||||
|
|
||||||
|
echo -e " Free space: ${free_space} (on root partition)"
|
||||||
|
|
||||||
|
if [ -n "$use_percent" ] && [ "$use_percent" -gt 90 ] 2>/dev/null; then
|
||||||
|
echo -e " ${RED}⚠ Warning: Disk usage at ${use_percent}%${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Backup Manager
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Main interface for Acronis backup operations
|
||||||
|
# Features:
|
||||||
|
# - List backups and archives
|
||||||
|
# - Trigger manual backups
|
||||||
|
# - View backup schedules
|
||||||
|
# - Monitor backup/recovery status
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if Acronis is installed
|
||||||
|
if ! systemctl list-unit-files | grep -q "acronis_mms.service"; then
|
||||||
|
print_error "Acronis is not installed"
|
||||||
|
echo ""
|
||||||
|
echo "Install Acronis first from the Acronis menu."
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if acrocmd is available
|
||||||
|
if [ ! -f "/usr/sbin/acrocmd" ]; then
|
||||||
|
print_error "acrocmd command-line tool not found"
|
||||||
|
echo ""
|
||||||
|
echo "This may indicate an incomplete Acronis installation."
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
print_banner "Backup Management"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Agent Management${NC}"
|
||||||
|
echo -e " ${YELLOW}1)${NC} Check Agent Status"
|
||||||
|
echo -e " ${YELLOW}2)${NC} View Agent Logs"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Backup Operations${NC}"
|
||||||
|
echo -e " ${YELLOW}3)${NC} List Backups & Archives"
|
||||||
|
echo -e " ${YELLOW}4)${NC} Trigger Manual Backup"
|
||||||
|
echo -e " ${YELLOW}5)${NC} Check Backup Status"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Plan Management${NC}"
|
||||||
|
echo -e " ${YELLOW}6)${NC} View Backup Plans/Schedules"
|
||||||
|
echo -e " ${YELLOW}7)${NC} Manage Protection Plans"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Restore Operations${NC}"
|
||||||
|
echo -e " ${YELLOW}8)${NC} Restore from Backup (Future)"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}0)${NC} Return to Acronis Menu"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select option: "
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
1)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-agent-status.sh"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-logs.sh"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-list-backups.sh"
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-trigger-backup.sh"
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-backup-status.sh"
|
||||||
|
;;
|
||||||
|
6)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-schedule-viewer.sh"
|
||||||
|
;;
|
||||||
|
7)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-plan-manager.sh"
|
||||||
|
;;
|
||||||
|
8)
|
||||||
|
bash "$SCRIPT_DIR/modules/backup/acronis-restore.sh"
|
||||||
|
;;
|
||||||
|
0)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
print_error "Invalid option"
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
Executable
+118
@@ -0,0 +1,118 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Backup Status
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Check status of backup operations using acrocmd
|
||||||
|
# Features:
|
||||||
|
# - Show active/running backups
|
||||||
|
# - Display recent backup history
|
||||||
|
# - Show backup task status
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
clear
|
||||||
|
print_banner "Backup Status"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if acrocmd is available
|
||||||
|
if [ ! -f "/usr/sbin/acrocmd" ]; then
|
||||||
|
print_error "acrocmd command-line tool not found"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show active/running tasks
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BOLD}Active Backup Tasks${NC}"
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
task_output=$(/usr/sbin/acrocmd list tasks 2>&1)
|
||||||
|
|
||||||
|
if echo "$task_output" | grep -qi "no.*tasks\|error"; then
|
||||||
|
echo -e "${GREEN}✓${NC} No active backup tasks running"
|
||||||
|
else
|
||||||
|
echo "$task_output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show recent activities
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BOLD}Recent Backup Activities${NC}"
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
activity_output=$(/usr/sbin/acrocmd list activities 2>&1)
|
||||||
|
|
||||||
|
if echo "$activity_output" | grep -qi "no.*activities\|error"; then
|
||||||
|
echo -e "${YELLOW}No recent backup activities found${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "This may indicate:"
|
||||||
|
echo " • No backups have been run yet"
|
||||||
|
echo " • Agent needs registration"
|
||||||
|
echo " • No backup plans configured"
|
||||||
|
else
|
||||||
|
echo "$activity_output" | tail -20
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Parse logs for backup status
|
||||||
|
if [ -f "/var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log" ]; then
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BOLD}Log Summary${NC}"
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Count recent backup events
|
||||||
|
log_file="/var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log"
|
||||||
|
|
||||||
|
completed=$(grep -ic "backup.*completed\|backup.*success" "$log_file" 2>/dev/null || echo "0")
|
||||||
|
failed=$(grep -ic "backup.*failed\|backup.*error" "$log_file" 2>/dev/null || echo "0")
|
||||||
|
started=$(grep -ic "backup.*started\|backup.*begin" "$log_file" 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
echo "Backup Statistics (from current log):"
|
||||||
|
echo " • Started: $started"
|
||||||
|
echo " • Completed: $completed"
|
||||||
|
echo " • Failed: $failed"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show last 5 backup-related events
|
||||||
|
echo "Recent Events:"
|
||||||
|
echo ""
|
||||||
|
grep -i "backup" "$log_file" 2>/dev/null | tail -5 | while read -r line; do
|
||||||
|
# Highlight status
|
||||||
|
if echo "$line" | grep -qi "success\|completed"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} $line"
|
||||||
|
elif echo "$line" | grep -qi "fail\|error"; then
|
||||||
|
echo -e " ${RED}✗${NC} $line"
|
||||||
|
else
|
||||||
|
echo " → $line"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Options:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " • View full logs: Select 'View Agent Logs' from menu"
|
||||||
|
echo " • Trigger backup: Select 'Trigger Manual Backup'"
|
||||||
|
echo " • Troubleshoot: Use 'Troubleshoot Backups' for diagnostics"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
press_enter
|
||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Configure Backup Plans"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Acronis Backup Plan Configuration${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Backup plans are configured through the Acronis web console."
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Steps to configure backup plans:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "1. Log in to your Acronis web console"
|
||||||
|
echo " → https://us5-cloud.acronis.com (or your region)"
|
||||||
|
echo ""
|
||||||
|
echo "2. Navigate to: Devices → All devices"
|
||||||
|
echo ""
|
||||||
|
echo "3. Find this server in the device list"
|
||||||
|
echo ""
|
||||||
|
echo "4. Click on the device and select 'Protection'"
|
||||||
|
echo ""
|
||||||
|
echo "5. Click 'Add plan' and configure:"
|
||||||
|
echo " • Backup source (files, folders, system)"
|
||||||
|
echo " • Backup schedule (hourly, daily, weekly)"
|
||||||
|
echo " • Retention policy (how long to keep backups)"
|
||||||
|
echo " • Backup location (cloud or local)"
|
||||||
|
echo ""
|
||||||
|
echo "6. Apply the plan to this device"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Common Backup Plans:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " • Full Server Backup"
|
||||||
|
echo " → Entire system image for disaster recovery"
|
||||||
|
echo ""
|
||||||
|
echo " • cPanel Accounts"
|
||||||
|
echo " → /home/* directories for user data"
|
||||||
|
echo ""
|
||||||
|
echo " • Databases"
|
||||||
|
echo " → MySQL/MariaDB databases with consistent snapshots"
|
||||||
|
echo ""
|
||||||
|
echo " • Configuration Files"
|
||||||
|
echo " → /etc and other critical configs"
|
||||||
|
echo ""
|
||||||
|
echo " • Web Files"
|
||||||
|
echo " → /home/*/public_html websites"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+361
@@ -0,0 +1,361 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Agent Installer
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Download and install Acronis Cyber Protect agent
|
||||||
|
# Supports:
|
||||||
|
# - Interactive installation with prompts
|
||||||
|
# - Unattended installation (-a flag)
|
||||||
|
# - Skip registration (--skip-registration)
|
||||||
|
# - Install with token (--token=xxx)
|
||||||
|
# - Custom service URL (--rain=xxx)
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Acronis Agent Installation"
|
||||||
|
|
||||||
|
# Check if already installed
|
||||||
|
if systemctl list-unit-files | grep -q "acronis_mms.service"; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}${BOLD}⚠ Acronis Already Installed${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Acronis Cyber Protect agent is already installed on this system."
|
||||||
|
echo ""
|
||||||
|
echo -n "Do you want to reinstall/upgrade? (yes/no): "
|
||||||
|
read -r reinstall
|
||||||
|
if [ "$reinstall" != "yes" ]; then
|
||||||
|
echo "Installation cancelled"
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Acronis Cyber Protect Agent Installation${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "This will download and install the latest Acronis agent for Linux (x86_64)."
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Installation Options:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " 1) Interactive installation (default)"
|
||||||
|
echo " 2) Unattended installation (auto-accept)"
|
||||||
|
echo " 3) Install and register with token"
|
||||||
|
echo " 4) Install without registration"
|
||||||
|
echo " 5) Advanced/Custom installation (specify all flags)"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select installation mode [1]: "
|
||||||
|
read -r install_mode
|
||||||
|
install_mode="${install_mode:-1}"
|
||||||
|
|
||||||
|
# Build installation flags
|
||||||
|
INSTALL_FLAGS=""
|
||||||
|
SERVICE_URL="us5-cloud.acronis.com"
|
||||||
|
REGISTRATION_TOKEN=""
|
||||||
|
|
||||||
|
case "$install_mode" in
|
||||||
|
2)
|
||||||
|
INSTALL_FLAGS="-a"
|
||||||
|
echo ""
|
||||||
|
echo "Mode: Unattended installation"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
INSTALL_FLAGS="-a"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Register During Installation${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Paste your Acronis registration token below."
|
||||||
|
echo "To get a token:"
|
||||||
|
echo " 1. Log in to Acronis web console"
|
||||||
|
echo " 2. Go to: Settings → Registration tokens"
|
||||||
|
echo " 3. Create token or copy existing one"
|
||||||
|
echo ""
|
||||||
|
echo -n "Registration token: "
|
||||||
|
read -r REGISTRATION_TOKEN
|
||||||
|
|
||||||
|
# Allow pasting multi-line or trimming whitespace
|
||||||
|
REGISTRATION_TOKEN=$(echo "$REGISTRATION_TOKEN" | tr -d '[:space:]')
|
||||||
|
|
||||||
|
if [ -z "$REGISTRATION_TOKEN" ]; then
|
||||||
|
print_error "Token is required for this mode"
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
INSTALL_FLAGS="$INSTALL_FLAGS --token=$REGISTRATION_TOKEN"
|
||||||
|
echo ""
|
||||||
|
echo "Mode: Install with registration token"
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
INSTALL_FLAGS="-a --skip-registration"
|
||||||
|
echo ""
|
||||||
|
echo "Mode: Install without registration"
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
# Advanced/Custom mode
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Advanced Installation${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Build custom installation flags by selecting options."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Unattended mode
|
||||||
|
echo -n "Unattended install (auto-accept)? (y/n) [y]: "
|
||||||
|
read -r use_unattended
|
||||||
|
use_unattended="${use_unattended:-y}"
|
||||||
|
if [ "$use_unattended" = "y" ]; then
|
||||||
|
INSTALL_FLAGS="$INSTALL_FLAGS -a"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Registration options
|
||||||
|
echo ""
|
||||||
|
echo "Registration:"
|
||||||
|
echo " 1) Register with token during install"
|
||||||
|
echo " 2) Skip registration (register later)"
|
||||||
|
echo " 3) Interactive (installer will prompt)"
|
||||||
|
echo -n "Select [3]: "
|
||||||
|
read -r reg_choice
|
||||||
|
reg_choice="${reg_choice:-3}"
|
||||||
|
|
||||||
|
if [ "$reg_choice" = "1" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Paste your Acronis registration token:"
|
||||||
|
echo "(Spaces and line breaks will be automatically removed)"
|
||||||
|
echo ""
|
||||||
|
read -r REGISTRATION_TOKEN
|
||||||
|
REGISTRATION_TOKEN=$(echo "$REGISTRATION_TOKEN" | tr -d '[:space:]')
|
||||||
|
|
||||||
|
if [ -n "$REGISTRATION_TOKEN" ]; then
|
||||||
|
INSTALL_FLAGS="$INSTALL_FLAGS --token=$REGISTRATION_TOKEN"
|
||||||
|
else
|
||||||
|
print_error "Token cannot be empty"
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif [ "$reg_choice" = "2" ]; then
|
||||||
|
INSTALL_FLAGS="$INSTALL_FLAGS --skip-registration"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Additional flags
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Additional Options:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verbose logging
|
||||||
|
echo -n "Enable verbose logging? (y/n) [n]: "
|
||||||
|
read -r use_verbose
|
||||||
|
if [ "$use_verbose" = "y" ]; then
|
||||||
|
INSTALL_FLAGS="$INSTALL_FLAGS --verbose"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Custom flags
|
||||||
|
echo ""
|
||||||
|
echo "Enter any additional custom flags (or press Enter to skip):"
|
||||||
|
echo "Examples: --proxy=http://proxy:8080, --language=en, etc."
|
||||||
|
echo ""
|
||||||
|
echo -n "Custom flags: "
|
||||||
|
read -r custom_flags
|
||||||
|
if [ -n "$custom_flags" ]; then
|
||||||
|
INSTALL_FLAGS="$INSTALL_FLAGS $custom_flags"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Mode: Advanced/Custom installation"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
echo "Mode: Interactive installation"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Ask for service URL (for all modes except skip-registration)
|
||||||
|
if [[ "$INSTALL_FLAGS" != *"--skip-registration"* ]]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Acronis Cloud Region${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Common regions:"
|
||||||
|
echo " • us5-cloud.acronis.com (US - Default)"
|
||||||
|
echo " • eu2-cloud.acronis.com (Europe)"
|
||||||
|
echo " • ap1-cloud.acronis.com (Asia Pacific)"
|
||||||
|
echo " • ca1-cloud.acronis.com (Canada)"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter service URL [us5-cloud.acronis.com]: "
|
||||||
|
read -r input_url
|
||||||
|
if [ -n "$input_url" ]; then
|
||||||
|
SERVICE_URL="$input_url"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add --rain flag if token is being used
|
||||||
|
if [[ "$INSTALL_FLAGS" == *"--token"* ]]; then
|
||||||
|
INSTALL_FLAGS="$INSTALL_FLAGS --rain=$SERVICE_URL"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Installation Summary:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Download URL: https://${SERVICE_URL}/bc/api/ams/links/agents/redirect"
|
||||||
|
echo " Architecture: x86_64 (64-bit)"
|
||||||
|
echo " Install flags: ${INSTALL_FLAGS:-none}"
|
||||||
|
echo " Service URL: ${SERVICE_URL}"
|
||||||
|
[ -n "$REGISTRATION_TOKEN" ] && echo " Token: ********"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -n "Proceed with installation? (yes/no): "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [[ ! "$confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Installation cancelled"
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Starting Installation...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create download directory in toolkit folder
|
||||||
|
DOWNLOAD_DIR="$SCRIPT_DIR/downloads"
|
||||||
|
mkdir -p "$DOWNLOAD_DIR"
|
||||||
|
cd "$DOWNLOAD_DIR" || exit 1
|
||||||
|
|
||||||
|
# Use timestamped subdirectory for this installation
|
||||||
|
INSTALL_DIR="$DOWNLOAD_DIR/acronis-install-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
cd "$INSTALL_DIR" || exit 1
|
||||||
|
|
||||||
|
# Download installer
|
||||||
|
echo "→ Downloading Acronis agent installer..."
|
||||||
|
DOWNLOAD_URL="https://${SERVICE_URL}/bc/api/ams/links/agents/redirect?language=multi&system=linux&architecture=64&productType=enterprise"
|
||||||
|
|
||||||
|
if wget -q --show-progress "$DOWNLOAD_URL" -O "Cyber_Protection_Agent_for_Linux_x86_64.bin"; then
|
||||||
|
print_success "Download complete"
|
||||||
|
else
|
||||||
|
print_error "Download failed"
|
||||||
|
echo ""
|
||||||
|
echo "Possible causes:"
|
||||||
|
echo " • No internet connection"
|
||||||
|
echo " • Invalid service URL: ${SERVICE_URL}"
|
||||||
|
echo " • Firewall blocking connection"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
cd /
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Make executable
|
||||||
|
chmod +x "Cyber_Protection_Agent_for_Linux_x86_64.bin" 2>/dev/null
|
||||||
|
|
||||||
|
# Verify file exists and has size
|
||||||
|
if [ ! -f "Cyber_Protection_Agent_for_Linux_x86_64.bin" ]; then
|
||||||
|
print_error "Installer file not found"
|
||||||
|
cd /
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
file_size=$(stat -c%s "Cyber_Protection_Agent_for_Linux_x86_64.bin" 2>/dev/null || echo "0")
|
||||||
|
if [ "$file_size" -lt 1000000 ]; then
|
||||||
|
print_error "Installer file is too small (possibly corrupted)"
|
||||||
|
cd /
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run installer
|
||||||
|
echo "→ Running Acronis installer..."
|
||||||
|
echo ""
|
||||||
|
echo -e "${DIM}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
|
||||||
|
if [ -z "$INSTALL_FLAGS" ]; then
|
||||||
|
# Interactive mode - run directly
|
||||||
|
./Cyber_Protection_Agent_for_Linux_x86_64.bin
|
||||||
|
else
|
||||||
|
# Unattended mode - need to pass flags properly
|
||||||
|
./Cyber_Protection_Agent_for_Linux_x86_64.bin $INSTALL_FLAGS
|
||||||
|
fi
|
||||||
|
|
||||||
|
INSTALL_EXIT_CODE=$?
|
||||||
|
|
||||||
|
echo -e "${DIM}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check installation result
|
||||||
|
if [ $INSTALL_EXIT_CODE -eq 0 ]; then
|
||||||
|
print_success "Installation completed successfully!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Start services
|
||||||
|
echo "→ Starting Acronis services..."
|
||||||
|
systemctl start aakore
|
||||||
|
systemctl start acronis_mms
|
||||||
|
systemctl start acronis_schedule
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if services started
|
||||||
|
sleep 2
|
||||||
|
if systemctl is-active --quiet acronis_mms; then
|
||||||
|
print_success "Services started successfully"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show next steps
|
||||||
|
echo -e "${BOLD}Next Steps:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$install_mode" = "4" ]; then
|
||||||
|
echo " 1. Register the agent with Acronis Cloud"
|
||||||
|
echo " → Select 'Register with Cloud' from Acronis menu"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " 2. Configure backup plans in Acronis web console"
|
||||||
|
echo " → Visit: https://${SERVICE_URL}"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Check agent status"
|
||||||
|
echo " → Select 'Check Agent Status' from Acronis menu"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
print_error "Services failed to start"
|
||||||
|
echo ""
|
||||||
|
echo "Check logs for details:"
|
||||||
|
echo " tail -f /var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
print_error "Installation failed with exit code $INSTALL_EXIT_CODE"
|
||||||
|
echo ""
|
||||||
|
echo "Check the output above for error details."
|
||||||
|
echo ""
|
||||||
|
echo "Common issues:"
|
||||||
|
echo " • Incompatible system (requires 64-bit Linux)"
|
||||||
|
echo " • Insufficient disk space"
|
||||||
|
echo " • Conflicting backup software"
|
||||||
|
echo " • Invalid registration token"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
echo "→ Cleaning up installation files..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+76
@@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis List Backups
|
||||||
|
################################################################################
|
||||||
|
# Purpose: List all backups and archives using acrocmd
|
||||||
|
# Features:
|
||||||
|
# - Show backup archives
|
||||||
|
# - Show backup versions
|
||||||
|
# - Display backup details (size, date, location)
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
clear
|
||||||
|
print_banner "List Backups & Archives"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Retrieving backup information...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if acrocmd is available
|
||||||
|
if [ ! -f "/usr/sbin/acrocmd" ]; then
|
||||||
|
print_error "acrocmd command-line tool not found"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# List archives
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BOLD}Backup Archives${NC}"
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if /usr/sbin/acrocmd list archives 2>/dev/null | grep -q .; then
|
||||||
|
/usr/sbin/acrocmd list archives 2>/dev/null
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}No backup archives found${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Possible reasons:"
|
||||||
|
echo " • No backups have been created yet"
|
||||||
|
echo " • Agent not registered with Acronis Cloud"
|
||||||
|
echo " • No backup plans configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BOLD}Backup Details${NC}"
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if /usr/sbin/acrocmd list backups 2>/dev/null | grep -q .; then
|
||||||
|
/usr/sbin/acrocmd list backups 2>/dev/null
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}No backup details available${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Options:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " • Create backups via 'Trigger Manual Backup'"
|
||||||
|
echo " • Configure plans in Acronis web console"
|
||||||
|
echo " • Check backup status for active operations"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
press_enter
|
||||||
Executable
+296
@@ -0,0 +1,296 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Log Viewer
|
||||||
|
################################################################################
|
||||||
|
# Purpose: View and tail Acronis Cyber Protect logs
|
||||||
|
# Log location: /var/lib/Acronis/BackupAndRecovery/MMS/
|
||||||
|
# Primary log: mms.0.log
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log directory
|
||||||
|
LOG_DIR="/var/lib/Acronis/BackupAndRecovery/MMS"
|
||||||
|
PRIMARY_LOG="$LOG_DIR/mms.0.log"
|
||||||
|
|
||||||
|
print_banner "Acronis Logs Viewer"
|
||||||
|
|
||||||
|
# Check if Acronis is installed
|
||||||
|
if [ ! -d "$LOG_DIR" ]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Acronis log directory not found"
|
||||||
|
echo ""
|
||||||
|
echo "Acronis may not be installed or logs are in a different location."
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Acronis Log Management${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Log directory: ${LOG_DIR}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show log menu
|
||||||
|
show_log_menu() {
|
||||||
|
clear
|
||||||
|
print_banner "Acronis Logs Viewer"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Available Logs:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# List all log files with sizes
|
||||||
|
if [ -d "$LOG_DIR" ]; then
|
||||||
|
local log_count=0
|
||||||
|
while IFS= read -r log_file; do
|
||||||
|
((log_count++))
|
||||||
|
local size=$(du -h "$log_file" 2>/dev/null | awk '{print $1}')
|
||||||
|
local filename=$(basename "$log_file")
|
||||||
|
local mod_time=$(stat -c %y "$log_file" 2>/dev/null | cut -d'.' -f1)
|
||||||
|
echo -e " ${CYAN}${log_count})${NC} ${filename}"
|
||||||
|
echo -e " Size: ${size} | Modified: ${mod_time}"
|
||||||
|
done < <(find "$LOG_DIR" -name "*.log" -type f | sort)
|
||||||
|
|
||||||
|
if [ $log_count -eq 0 ]; then
|
||||||
|
echo -e " ${DIM}No log files found${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Actions:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${GREEN}v)${NC} View Primary Log (last 100 lines)"
|
||||||
|
echo -e " ${GREEN}t)${NC} Tail Primary Log (live follow)"
|
||||||
|
echo -e " ${GREEN}s)${NC} Search Logs"
|
||||||
|
echo -e " ${GREEN}e)${NC} Show Errors Only"
|
||||||
|
echo -e " ${GREEN}a)${NC} Archive Old Logs"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Return to Menu"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -n "Select option: "
|
||||||
|
}
|
||||||
|
|
||||||
|
# View primary log
|
||||||
|
view_primary_log() {
|
||||||
|
clear
|
||||||
|
print_banner "Acronis Primary Log (Last 100 Lines)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ -f "$PRIMARY_LOG" ]; then
|
||||||
|
tail -100 "$PRIMARY_LOG"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
else
|
||||||
|
print_error "Primary log file not found: $PRIMARY_LOG"
|
||||||
|
press_enter
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tail primary log
|
||||||
|
tail_primary_log() {
|
||||||
|
clear
|
||||||
|
print_banner "Acronis Live Log (Ctrl+C to Exit)"
|
||||||
|
echo ""
|
||||||
|
echo "Following: $PRIMARY_LOG"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ -f "$PRIMARY_LOG" ]; then
|
||||||
|
tail -f "$PRIMARY_LOG"
|
||||||
|
else
|
||||||
|
print_error "Primary log file not found: $PRIMARY_LOG"
|
||||||
|
press_enter
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search logs
|
||||||
|
search_logs() {
|
||||||
|
clear
|
||||||
|
print_banner "Search Acronis Logs"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter search term: "
|
||||||
|
read -r search_term
|
||||||
|
|
||||||
|
if [ -z "$search_term" ]; then
|
||||||
|
print_error "No search term provided"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Searching for: ${search_term}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Search all log files
|
||||||
|
local found=false
|
||||||
|
while IFS= read -r log_file; do
|
||||||
|
if grep -qi "$search_term" "$log_file" 2>/dev/null; then
|
||||||
|
found=true
|
||||||
|
echo -e "${BOLD}$(basename "$log_file"):${NC}"
|
||||||
|
grep -i --color=always "$search_term" "$log_file" | tail -20
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
done < <(find "$LOG_DIR" -name "*.log" -type f)
|
||||||
|
|
||||||
|
if [ "$found" = false ]; then
|
||||||
|
echo "No matches found for: $search_term"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show errors only
|
||||||
|
show_errors() {
|
||||||
|
clear
|
||||||
|
print_banner "Acronis Errors (Last 50)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ -f "$PRIMARY_LOG" ]; then
|
||||||
|
echo "Filtering for ERROR, WARN, FAIL, CRITICAL..."
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
grep -iE "error|warn|fail|critical" "$PRIMARY_LOG" | tail -50 | while IFS= read -r line; do
|
||||||
|
# Color code by severity
|
||||||
|
if echo "$line" | grep -qi "critical"; then
|
||||||
|
echo -e "${RED}${BOLD}${line}${NC}"
|
||||||
|
elif echo "$line" | grep -qi "error"; then
|
||||||
|
echo -e "${RED}${line}${NC}"
|
||||||
|
elif echo "$line" | grep -qi "warn"; then
|
||||||
|
echo -e "${YELLOW}${line}${NC}"
|
||||||
|
else
|
||||||
|
echo "$line"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
print_error "Primary log file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Archive old logs
|
||||||
|
archive_old_logs() {
|
||||||
|
clear
|
||||||
|
print_banner "Archive Old Logs"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Calculate total size
|
||||||
|
local total_size=$(du -sh "$LOG_DIR" 2>/dev/null | awk '{print $1}')
|
||||||
|
local log_count=$(find "$LOG_DIR" -name "*.log" -type f | wc -l)
|
||||||
|
|
||||||
|
echo "Current log status:"
|
||||||
|
echo " Directory: $LOG_DIR"
|
||||||
|
echo " Total size: $total_size"
|
||||||
|
echo " Log files: $log_count"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Find old logs (older than 30 days)
|
||||||
|
local old_logs=$(find "$LOG_DIR" -name "*.log" -type f -mtime +30 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
if [ $old_logs -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}✓ No old logs found (>30 days)${NC}"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found $old_logs log file(s) older than 30 days"
|
||||||
|
echo ""
|
||||||
|
echo "Archive location: /root/acronis-logs-archive-$(date +%Y%m%d).tar.gz"
|
||||||
|
echo ""
|
||||||
|
echo -n "Create archive and remove old logs? (yes/no): "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [ "$confirm" != "yes" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Archive cancelled"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "→ Creating archive..."
|
||||||
|
|
||||||
|
# Create archive
|
||||||
|
local archive_name="/root/acronis-logs-archive-$(date +%Y%m%d).tar.gz"
|
||||||
|
if find "$LOG_DIR" -name "*.log" -type f -mtime +30 -print0 2>/dev/null | tar -czf "$archive_name" --null -T -; then
|
||||||
|
print_success "Archive created: $archive_name"
|
||||||
|
|
||||||
|
# Remove old logs
|
||||||
|
echo ""
|
||||||
|
echo "→ Removing old logs..."
|
||||||
|
find "$LOG_DIR" -name "*.log" -type f -mtime +30 -delete 2>/dev/null
|
||||||
|
|
||||||
|
local remaining=$(find "$LOG_DIR" -name "*.log" -type f | wc -l)
|
||||||
|
echo ""
|
||||||
|
print_success "Old logs archived and removed"
|
||||||
|
echo ""
|
||||||
|
echo "Remaining log files: $remaining"
|
||||||
|
else
|
||||||
|
print_error "Failed to create archive"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
while true; do
|
||||||
|
show_log_menu
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
v) view_primary_log ;;
|
||||||
|
t) tail_primary_log ;;
|
||||||
|
s) search_logs ;;
|
||||||
|
e) show_errors ;;
|
||||||
|
a) archive_old_logs ;;
|
||||||
|
0) exit 0 ;;
|
||||||
|
*)
|
||||||
|
# Check if numeric selection for specific log file
|
||||||
|
if [[ "$choice" =~ ^[0-9]+$ ]]; then
|
||||||
|
log_files=($(find "$LOG_DIR" -name "*.log" -type f | sort))
|
||||||
|
if [ $choice -gt 0 ] && [ $choice -le ${#log_files[@]} ]; then
|
||||||
|
selected_log="${log_files[$((choice-1))]}"
|
||||||
|
clear
|
||||||
|
print_banner "Log: $(basename "$selected_log")"
|
||||||
|
echo ""
|
||||||
|
tail -100 "$selected_log"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
else
|
||||||
|
print_error "Invalid log selection"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "Invalid option"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Create Manual Backup"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Manual Backup Creation${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Manual backups are triggered through the Acronis web console or CLI."
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Web Console Method (Recommended):${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "1. Log in to Acronis web console"
|
||||||
|
echo "2. Go to: Devices → Select this server"
|
||||||
|
echo "3. Click 'Back up now' button"
|
||||||
|
echo "4. Monitor backup progress in real-time"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Command Line Method:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Use the Acronis CLI tool (acrocmd):"
|
||||||
|
echo ""
|
||||||
|
echo " # List available plans"
|
||||||
|
echo " acrocmd list plans"
|
||||||
|
echo ""
|
||||||
|
echo " # Run backup for specific plan"
|
||||||
|
echo " acrocmd backup run --plan <plan_id>"
|
||||||
|
echo ""
|
||||||
|
echo " # Create ad-hoc backup"
|
||||||
|
echo " acrocmd backup create --source /path/to/data --destination /backup/path"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Note:${NC} Detailed CLI backup functionality can be added here based on"
|
||||||
|
echo "your specific requirements. Would you like me to implement the full"
|
||||||
|
echo "CLI backup interface?"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+210
@@ -0,0 +1,210 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Plan Manager
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Manage Acronis protection plans
|
||||||
|
# Features:
|
||||||
|
# - List protection plans
|
||||||
|
# - View plan details
|
||||||
|
# - Enable/disable plans
|
||||||
|
# - Guidance for plan configuration
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
clear
|
||||||
|
print_banner "Protection Plan Management"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if acrocmd is available
|
||||||
|
if [ ! -f "/usr/sbin/acrocmd" ]; then
|
||||||
|
print_error "acrocmd command-line tool not found"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# List plans
|
||||||
|
echo -e "${BOLD}Current Protection Plans${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
plan_output=$(/usr/sbin/acrocmd list plans 2>&1)
|
||||||
|
|
||||||
|
if echo "$plan_output" | grep -qi "error\|no.*plans"; then
|
||||||
|
echo -e "${YELLOW}No protection plans configured${NC}"
|
||||||
|
HAS_PLANS=false
|
||||||
|
else
|
||||||
|
echo "$plan_output"
|
||||||
|
HAS_PLANS=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$HAS_PLANS" = true ]; then
|
||||||
|
echo -e "${BOLD}Plan Management Options${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " 1) View detailed plan information"
|
||||||
|
echo " 2) Enable/disable plan"
|
||||||
|
echo " 3) Delete plan"
|
||||||
|
echo " 4) Create new plan (via web console)"
|
||||||
|
echo " 0) Return"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select option [0]: "
|
||||||
|
read -r choice
|
||||||
|
choice="${choice:-0}"
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
1)
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter plan ID or name: "
|
||||||
|
read -r plan_id
|
||||||
|
|
||||||
|
if [ -n "$plan_id" ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Plan Details:${NC}"
|
||||||
|
echo ""
|
||||||
|
/usr/sbin/acrocmd show plan "$plan_id" 2>&1 || {
|
||||||
|
echo ""
|
||||||
|
print_error "Could not retrieve plan details"
|
||||||
|
echo "Check that the plan ID/name is correct"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
2)
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter plan ID to enable/disable: "
|
||||||
|
read -r plan_id
|
||||||
|
|
||||||
|
if [ -n "$plan_id" ]; then
|
||||||
|
echo ""
|
||||||
|
echo " 1) Enable plan"
|
||||||
|
echo " 2) Disable plan"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select [1]: "
|
||||||
|
read -r action
|
||||||
|
action="${action:-1}"
|
||||||
|
|
||||||
|
if [ "$action" = "1" ]; then
|
||||||
|
/usr/sbin/acrocmd plan enable "$plan_id" 2>&1 && {
|
||||||
|
print_success "Plan enabled"
|
||||||
|
} || {
|
||||||
|
print_error "Failed to enable plan"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/usr/sbin/acrocmd plan disable "$plan_id" 2>&1 && {
|
||||||
|
print_success "Plan disabled"
|
||||||
|
} || {
|
||||||
|
print_error "Failed to disable plan"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
3)
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}${BOLD}Delete Protection Plan${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Warning:${NC} This will delete the plan configuration."
|
||||||
|
echo "Existing backups will be retained."
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter plan ID to delete: "
|
||||||
|
read -r plan_id
|
||||||
|
|
||||||
|
if [ -n "$plan_id" ]; then
|
||||||
|
echo ""
|
||||||
|
echo -n "Confirm deletion (type 'yes'): "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [ "$confirm" = "yes" ]; then
|
||||||
|
/usr/sbin/acrocmd delete plan "$plan_id" 2>&1 && {
|
||||||
|
print_success "Plan deleted"
|
||||||
|
} || {
|
||||||
|
print_error "Failed to delete plan"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Cancelled"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
4)
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Create New Protection Plan${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Protection plans are best created via the web console"
|
||||||
|
echo "for full configuration options and validation."
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}Web Console Method:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "1. Log in to Acronis web console"
|
||||||
|
|
||||||
|
# Get cloud URL
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
cloud_url=$(grep -oP 'CloudUrl[>=\"].*?https://[^\"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^\"<]+' | head -1)
|
||||||
|
if [ -n "$cloud_url" ]; then
|
||||||
|
echo " ${cloud_url}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "2. Navigate to: Devices → Select this server"
|
||||||
|
echo "3. Click 'Add protection plan'"
|
||||||
|
echo "4. Configure plan settings:"
|
||||||
|
echo " • What to back up (entire system/volumes/files)"
|
||||||
|
echo " • Where to store (cloud/local)"
|
||||||
|
echo " • When to run (schedule)"
|
||||||
|
echo " • How long to keep (retention)"
|
||||||
|
echo "5. Save and activate"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Advanced CLI Method:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "For advanced users, plans can be created via acrocmd:"
|
||||||
|
echo " acrocmd create plan --help"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
echo -e "${BOLD}Getting Started with Protection Plans${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Protection plans define your backup strategy:"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}What:${NC} Files, folders, volumes, or entire system"
|
||||||
|
echo -e "${CYAN}Where:${NC} Cloud storage or local destination"
|
||||||
|
echo -e "${CYAN}When:${NC} Scheduled times (hourly/daily/weekly/monthly)"
|
||||||
|
echo -e "${CYAN}Keep:${NC} Retention policy (days/versions to keep)"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}To Create Your First Plan:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "1. Log in to Acronis web console"
|
||||||
|
|
||||||
|
# Get cloud URL
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
cloud_url=$(grep -oP 'CloudUrl[>=\"].*?https://[^\"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^\"<]+' | head -1)
|
||||||
|
if [ -n "$cloud_url" ]; then
|
||||||
|
echo " ${cloud_url}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "2. Navigate to: Devices → This server"
|
||||||
|
echo "3. Click 'Add protection plan'"
|
||||||
|
echo "4. Follow the configuration wizard"
|
||||||
|
echo "5. Activate the plan"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Tip:${NC} Start with a simple file backup plan to test,"
|
||||||
|
echo " then create full system backup plans as needed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+231
@@ -0,0 +1,231 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Agent Registration
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Register Acronis agent with Acronis Cloud
|
||||||
|
# Command: /usr/lib/Acronis/RegisterAgentTool/RegisterAgent
|
||||||
|
# Flags:
|
||||||
|
# -o register - Operation: register
|
||||||
|
# -t cloud - Type: cloud-based
|
||||||
|
# -a <url> - Service URL
|
||||||
|
# --token <token> - Registration token
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Acronis Agent Registration"
|
||||||
|
|
||||||
|
# Check if agent is installed
|
||||||
|
if [ ! -f "/usr/lib/Acronis/RegisterAgentTool/RegisterAgent" ]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Acronis agent is not installed"
|
||||||
|
echo ""
|
||||||
|
echo "Please install the agent first:"
|
||||||
|
echo " 1. Return to Acronis Management menu"
|
||||||
|
echo " 2. Select 'Install Acronis Agent'"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check current registration status
|
||||||
|
echo -e "${BOLD}Current Registration Status:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
if grep -q "CloudUrl" "/etc/Acronis/Global.config" 2>/dev/null; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Agent is currently registered"
|
||||||
|
|
||||||
|
# Extract current cloud URL
|
||||||
|
current_url=$(grep -oP 'CloudUrl[>="].*?https://[^"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^"<]+' | head -1)
|
||||||
|
if [ -n "$current_url" ]; then
|
||||||
|
echo -e " Current URL: ${current_url}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}⚠ Agent is already registered${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Do you want to:"
|
||||||
|
echo " 1) Keep current registration"
|
||||||
|
echo " 2) Re-register (will overwrite current registration)"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select [1]: "
|
||||||
|
read -r choice
|
||||||
|
choice="${choice:-1}"
|
||||||
|
|
||||||
|
if [ "$choice" = "1" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Keeping current registration"
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Proceeding with re-registration..."
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} Agent is not registered"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}○${NC} No configuration found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Agent Registration${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "You'll need:"
|
||||||
|
echo " • Acronis Cloud service URL (e.g., us5-cloud.acronis.com)"
|
||||||
|
echo " • Registration token from Acronis web console"
|
||||||
|
echo ""
|
||||||
|
echo "To get a registration token:"
|
||||||
|
echo " 1. Log in to Acronis web console"
|
||||||
|
echo " 2. Go to Settings → Registration tokens"
|
||||||
|
echo " 3. Create a new token or copy existing one"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Get service URL
|
||||||
|
echo -n "Enter Acronis Cloud service URL [us5-cloud.acronis.com]: "
|
||||||
|
read -r service_url
|
||||||
|
service_url="${service_url:-us5-cloud.acronis.com}"
|
||||||
|
|
||||||
|
# Validate URL format
|
||||||
|
if [[ ! "$service_url" =~ ^[a-z0-9.-]+\.acronis\.com$ ]]; then
|
||||||
|
print_error "Invalid service URL format"
|
||||||
|
echo ""
|
||||||
|
echo "Expected format: region-cloud.acronis.com"
|
||||||
|
echo "Examples:"
|
||||||
|
echo " • us5-cloud.acronis.com"
|
||||||
|
echo " • eu2-cloud.acronis.com"
|
||||||
|
echo " • ap1-cloud.acronis.com"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get registration token
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter registration token: "
|
||||||
|
read -r reg_token
|
||||||
|
|
||||||
|
if [ -z "$reg_token" ]; then
|
||||||
|
print_error "Registration token is required"
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Confirm registration
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Registration Summary:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Service URL: https://${service_url}"
|
||||||
|
echo " Token: ${reg_token:0:8}...${reg_token: -4}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -n "Proceed with registration? (yes/no): "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [ "$confirm" != "yes" ]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Registration cancelled"
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Registering Agent...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Run registration command
|
||||||
|
REGISTER_CMD="/usr/lib/Acronis/RegisterAgentTool/RegisterAgent"
|
||||||
|
REGISTER_ARGS="-o register -t cloud -a https://${service_url} --token ${reg_token}"
|
||||||
|
|
||||||
|
echo "→ Contacting Acronis Cloud..."
|
||||||
|
echo ""
|
||||||
|
echo -e "${DIM}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
|
||||||
|
# Execute registration
|
||||||
|
if $REGISTER_CMD $REGISTER_ARGS; then
|
||||||
|
REG_EXIT_CODE=$?
|
||||||
|
else
|
||||||
|
REG_EXIT_CODE=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${DIM}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check result
|
||||||
|
if [ $REG_EXIT_CODE -eq 0 ]; then
|
||||||
|
print_success "Registration successful!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Restart services to apply registration
|
||||||
|
echo "→ Restarting Acronis services..."
|
||||||
|
systemctl restart acronis_mms
|
||||||
|
systemctl restart aakore
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if systemctl is-active --quiet acronis_mms; then
|
||||||
|
print_success "Services restarted successfully"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BOLD}Agent Registered Successfully!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Log in to Acronis web console:"
|
||||||
|
echo " → https://${service_url}"
|
||||||
|
echo ""
|
||||||
|
echo " 2. Find this agent in the device list"
|
||||||
|
echo " → Navigate to: Devices → All devices"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Assign backup plans to this agent"
|
||||||
|
echo " → Select device → Protection → Add plan"
|
||||||
|
echo ""
|
||||||
|
echo " 4. Check agent status from this toolkit"
|
||||||
|
echo " → Select 'Check Agent Status' from Acronis menu"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
print_error "Services failed to restart"
|
||||||
|
echo ""
|
||||||
|
echo "Registration may have succeeded but services need attention."
|
||||||
|
echo "Check logs: tail -f /var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
print_error "Registration failed with exit code $REG_EXIT_CODE"
|
||||||
|
echo ""
|
||||||
|
echo "Common issues:"
|
||||||
|
echo " • Invalid registration token"
|
||||||
|
echo " • Incorrect service URL"
|
||||||
|
echo " • Network connectivity issues"
|
||||||
|
echo " • Firewall blocking connection to Acronis Cloud"
|
||||||
|
echo " • Token already used or expired"
|
||||||
|
echo ""
|
||||||
|
echo "Troubleshooting:"
|
||||||
|
echo " 1. Verify token in Acronis web console"
|
||||||
|
echo " 2. Check network connectivity:"
|
||||||
|
echo " curl -I https://${service_url}"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Check agent logs:"
|
||||||
|
echo " tail -f /var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
press_enter
|
||||||
Executable
+58
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Restore from Backup"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}${BOLD}⚠️ RESTORE OPERATION ⚠️${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Restoring from backups requires careful planning to avoid data loss."
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Restore Methods:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}1. Web Console Method (Recommended)${NC}"
|
||||||
|
echo " • Most user-friendly with visual interface"
|
||||||
|
echo " • Full preview of backup contents"
|
||||||
|
echo " • Granular file/folder selection"
|
||||||
|
echo ""
|
||||||
|
echo " Steps:"
|
||||||
|
echo " a) Log in to Acronis web console"
|
||||||
|
echo " b) Navigate to: Backup → Recovery"
|
||||||
|
echo " c) Select backup archive"
|
||||||
|
echo " d) Choose recovery point (date/time)"
|
||||||
|
echo " e) Select files/folders to restore"
|
||||||
|
echo " f) Choose restore destination"
|
||||||
|
echo " g) Start recovery process"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}2. Command Line Method${NC}"
|
||||||
|
echo " • For advanced users and automation"
|
||||||
|
echo " • Requires acrocmd CLI tool"
|
||||||
|
echo ""
|
||||||
|
echo " Basic syntax:"
|
||||||
|
echo " acrocmd recover --archive <archive_id> \\"
|
||||||
|
echo " --recoverypoint <point_id> \\"
|
||||||
|
echo " --destination /restore/path"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}3. Bootable Media Recovery${NC}"
|
||||||
|
echo " • For full system disaster recovery"
|
||||||
|
echo " • Boot from Acronis bootable USB/ISO"
|
||||||
|
echo " • Restore entire system image"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Important Notes:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " ⚠ Test restores in a non-production environment first"
|
||||||
|
echo " ⚠ Verify backup integrity before critical restores"
|
||||||
|
echo " ⚠ Consider restoring to alternate location first"
|
||||||
|
echo " ⚠ Backup current data before overwriting"
|
||||||
|
echo ""
|
||||||
|
echo "Would you like me to implement an interactive restore wizard"
|
||||||
|
echo "with CLI backup browsing and restore capabilities?"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+109
@@ -0,0 +1,109 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Schedule Viewer
|
||||||
|
################################################################################
|
||||||
|
# Purpose: View backup schedules and protection plans
|
||||||
|
# Features:
|
||||||
|
# - List all protection plans
|
||||||
|
# - Show backup schedules
|
||||||
|
# - Display plan details
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
clear
|
||||||
|
print_banner "Backup Plans & Schedules"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if acrocmd is available
|
||||||
|
if [ ! -f "/usr/sbin/acrocmd" ]; then
|
||||||
|
print_error "acrocmd command-line tool not found"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# List protection plans
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BOLD}Protection Plans${NC}"
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
plan_output=$(/usr/sbin/acrocmd list plans 2>&1)
|
||||||
|
|
||||||
|
if echo "$plan_output" | grep -qi "error\|no.*plans"; then
|
||||||
|
echo -e "${YELLOW}No protection plans found${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Protection plans define what, when, and how to back up."
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}To Create Protection Plans:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "1. Log in to Acronis web console"
|
||||||
|
|
||||||
|
# Try to get cloud URL
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
cloud_url=$(grep -oP 'CloudUrl[>=\"].*?https://[^\"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^\"<]+' | head -1)
|
||||||
|
if [ -n "$cloud_url" ]; then
|
||||||
|
echo " ${cloud_url}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "2. Navigate to: Devices → Select this server"
|
||||||
|
echo "3. Click 'Add protection plan'"
|
||||||
|
echo "4. Configure:"
|
||||||
|
echo " • Backup source (files/folders/volumes)"
|
||||||
|
echo " • Backup destination (cloud/local)"
|
||||||
|
echo " • Schedule (hourly/daily/weekly/monthly)"
|
||||||
|
echo " • Retention policy"
|
||||||
|
echo "5. Save and activate plan"
|
||||||
|
else
|
||||||
|
echo "$plan_output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check schedule status from service
|
||||||
|
echo -e "${BOLD}Schedule Service Status${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if systemctl is-active --quiet acronis_schedule 2>/dev/null; then
|
||||||
|
echo -e "${GREEN}✓${NC} Acronis scheduler is running"
|
||||||
|
|
||||||
|
# Show recent schedule events from log
|
||||||
|
if [ -f "/var/lib/Acronis/BackupAndRecovery/scheduler.log" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Recent scheduler activity:"
|
||||||
|
echo ""
|
||||||
|
tail -5 /var/lib/Acronis/BackupAndRecovery/scheduler.log 2>/dev/null | while read -r line; do
|
||||||
|
echo " $line"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠${NC} Acronis scheduler is not running"
|
||||||
|
echo ""
|
||||||
|
echo "Start it with: systemctl start acronis_schedule"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Next Steps:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " • Trigger manual backup: Select 'Trigger Manual Backup'"
|
||||||
|
echo " • Manage plans: Select 'Manage Protection Plans'"
|
||||||
|
echo " • Check status: Select 'Check Backup Status'"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
press_enter
|
||||||
Executable
+45
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "View Backup Status"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Acronis Backup Status${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Checking backup status..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if acrocmd exists
|
||||||
|
if ! command -v acrocmd &>/dev/null; then
|
||||||
|
echo -e "${YELLOW}acrocmd CLI tool not found${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Backup status is available through:"
|
||||||
|
echo " • Acronis web console (real-time status)"
|
||||||
|
echo " • Agent logs (see 'View Logs' option)"
|
||||||
|
echo ""
|
||||||
|
echo "To use CLI: acrocmd may need to be installed separately"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show recent backup activities from logs
|
||||||
|
if [ -f "/var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log" ]; then
|
||||||
|
echo -e "${BOLD}Recent Backup Activity:${NC}"
|
||||||
|
echo ""
|
||||||
|
grep -i "backup.*completed\|backup.*started\|backup.*failed" /var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log 2>/dev/null | tail -10
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "For detailed status, use:"
|
||||||
|
echo " • Web console: Full backup history and status"
|
||||||
|
echo " • acrocmd: Command-line status queries"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+202
@@ -0,0 +1,202 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Trigger Backup
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Trigger manual backups using acrocmd
|
||||||
|
# Features:
|
||||||
|
# - List available backup plans
|
||||||
|
# - Run backup for specific plan
|
||||||
|
# - Trigger ad-hoc backup
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
clear
|
||||||
|
print_banner "Trigger Manual Backup"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if acrocmd is available
|
||||||
|
if [ ! -f "/usr/sbin/acrocmd" ]; then
|
||||||
|
print_error "acrocmd command-line tool not found"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# List available plans
|
||||||
|
echo -e "${BOLD}Available Backup Plans${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Querying backup plans from Acronis..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
plan_output=$(/usr/sbin/acrocmd list plans 2>&1)
|
||||||
|
|
||||||
|
# Check if no plans exist (empty output or success message only)
|
||||||
|
if echo "$plan_output" | grep -qi "error\|failed\|no plans" || ! echo "$plan_output" | grep -q "[a-f0-9]\{8\}-[a-f0-9]\{4\}"; then
|
||||||
|
echo -e "${YELLOW}No backup plans found or error querying plans${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Possible reasons:"
|
||||||
|
echo " • No CLI-managed plans exist (acrocmd only shows local plans)"
|
||||||
|
echo " • Cloud-managed plans created in web console are not visible here"
|
||||||
|
echo " • Agent not registered with Acronis Cloud"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Important Note:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Plans created in the Acronis web console are managed at the cloud level"
|
||||||
|
echo "and are NOT accessible via the acrocmd CLI tool. The CLI can only see and"
|
||||||
|
echo "manage plans created locally via acrocmd commands."
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}To Trigger Cloud-Managed Backups:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "1. Log in to Acronis web console"
|
||||||
|
echo "2. Navigate to: Devices → Select this server"
|
||||||
|
echo "3. Click 'Back up now' to trigger your existing plan"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}To Create CLI-Managed Plans:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Use: acrocmd create plan --help"
|
||||||
|
echo "Note: CLI plans give you more control and optimization options"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$plan_output"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Select a Plan to Backup:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Enter the plan name or ID from the list above,"
|
||||||
|
echo "or press Enter to cancel and use web console instead."
|
||||||
|
echo ""
|
||||||
|
echo -n "Plan name/ID: "
|
||||||
|
read -r plan_id
|
||||||
|
|
||||||
|
if [ -z "$plan_id" ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Use Web Console Instead${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "To trigger backup via web console:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Log in to Acronis web console"
|
||||||
|
|
||||||
|
# Try to get cloud URL
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
cloud_url=$(grep -oP 'CloudUrl[>=\"].*?https://[^\"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^\"<]+' | head -1)
|
||||||
|
if [ -n "$cloud_url" ]; then
|
||||||
|
echo " ${cloud_url}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "2. Navigate to: Devices → This server"
|
||||||
|
echo "3. Click 'Back up now' button"
|
||||||
|
echo "4. Monitor progress in real-time"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# User selected a plan, proceed with backup type selection
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Backup Type Selection${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Select backup type:"
|
||||||
|
echo " 1) Auto (use plan's configured type)"
|
||||||
|
echo " 2) Full backup"
|
||||||
|
echo " 3) Incremental backup"
|
||||||
|
echo " 4) Differential backup"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select type [1]: "
|
||||||
|
read -r backup_type_choice
|
||||||
|
backup_type_choice="${backup_type_choice:-1}"
|
||||||
|
|
||||||
|
BACKUP_FLAGS=""
|
||||||
|
case "$backup_type_choice" in
|
||||||
|
2)
|
||||||
|
BACKUP_FLAGS="--backuptype=full"
|
||||||
|
echo " → Full backup selected"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
BACKUP_FLAGS="--backuptype=incremental"
|
||||||
|
echo " → Incremental backup selected (faster, stores only changes)"
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
BACKUP_FLAGS="--backuptype=differential"
|
||||||
|
echo " → Differential backup selected (changes since last full)"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo " → Using plan's default backup type"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Performance Options${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enable performance optimizations? (y/n) [n]: "
|
||||||
|
read -r opt_choice
|
||||||
|
|
||||||
|
if [ "$opt_choice" = "y" ] || [ "$opt_choice" = "Y" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Available optimizations:"
|
||||||
|
echo " 1) Lower compression (faster backup, larger size)"
|
||||||
|
echo " 2) High priority (use more system resources)"
|
||||||
|
echo " 3) Both"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select [3]: "
|
||||||
|
read -r perf_choice
|
||||||
|
perf_choice="${perf_choice:-3}"
|
||||||
|
|
||||||
|
case "$perf_choice" in
|
||||||
|
1)
|
||||||
|
BACKUP_FLAGS="$BACKUP_FLAGS --compression=normal"
|
||||||
|
echo " → Lower compression enabled"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
BACKUP_FLAGS="$BACKUP_FLAGS --priority=high"
|
||||||
|
echo " → High priority enabled"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
BACKUP_FLAGS="$BACKUP_FLAGS --compression=normal --priority=high"
|
||||||
|
echo " → Lower compression + high priority enabled"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Starting Backup...${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Plan: $plan_id"
|
||||||
|
[ -n "$BACKUP_FLAGS" ] && echo "Options: $BACKUP_FLAGS"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Try to run backup
|
||||||
|
if /usr/sbin/acrocmd backup run --plan "$plan_id" $BACKUP_FLAGS 2>&1; then
|
||||||
|
echo ""
|
||||||
|
print_success "Backup initiated successfully"
|
||||||
|
echo ""
|
||||||
|
echo "Monitor progress with 'Check Backup Status'"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
print_error "Failed to start backup"
|
||||||
|
echo ""
|
||||||
|
echo "Check that:"
|
||||||
|
echo " • Plan ID/name is correct"
|
||||||
|
echo " • Agent is online and registered"
|
||||||
|
echo " • No conflicting backups running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
Executable
+471
@@ -0,0 +1,471 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Backup Troubleshooter
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Diagnose and troubleshoot Acronis backup failures
|
||||||
|
# Features:
|
||||||
|
# - Multi-log location scanning
|
||||||
|
# - Common failure pattern detection
|
||||||
|
# - Service health checks
|
||||||
|
# - Disk space analysis
|
||||||
|
# - Network connectivity tests
|
||||||
|
# - Automated fix suggestions
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log locations to check
|
||||||
|
declare -A LOG_LOCATIONS=(
|
||||||
|
["MMS"]="/var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log"
|
||||||
|
["MMS_OLD"]="/var/lib/Acronis/BackupAndRecovery/MMS/mms.*.log"
|
||||||
|
["AGENT"]="/var/log/acronis/agent/*.log"
|
||||||
|
["CORE"]="/var/lib/Acronis/BackupAndRecovery/aakore.log"
|
||||||
|
["SCHEDULE"]="/var/lib/Acronis/BackupAndRecovery/scheduler.log"
|
||||||
|
["SYSTEM"]="/var/log/messages"
|
||||||
|
["SYSLOG"]="/var/log/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
print_banner "Acronis Backup Troubleshooter"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Diagnostic Mode${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "This tool will analyze:"
|
||||||
|
echo " • Service status and health"
|
||||||
|
echo " • Log files for errors and failures"
|
||||||
|
echo " • System resources (disk, memory)"
|
||||||
|
echo " • Network connectivity to Acronis Cloud"
|
||||||
|
echo " • Common backup failure patterns"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Track issues found
|
||||||
|
declare -a ISSUES_FOUND=()
|
||||||
|
declare -a WARNINGS_FOUND=()
|
||||||
|
declare -a RECOMMENDATIONS=()
|
||||||
|
|
||||||
|
# Function to add issue
|
||||||
|
add_issue() {
|
||||||
|
ISSUES_FOUND+=("$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to add warning
|
||||||
|
add_warning() {
|
||||||
|
WARNINGS_FOUND+=("$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to add recommendation
|
||||||
|
add_recommendation() {
|
||||||
|
RECOMMENDATIONS+=("$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Check service status
|
||||||
|
echo -e "${BOLD}[1/7] Checking Acronis Services...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
declare -a SERVICES=("aakore" "acronis_mms" "acronis_schedule" "active-protection")
|
||||||
|
all_services_running=true
|
||||||
|
|
||||||
|
for service in "${SERVICES[@]}"; do
|
||||||
|
if systemctl list-unit-files | grep -q "^${service}.service"; then
|
||||||
|
if systemctl is-active --quiet "$service"; then
|
||||||
|
echo -e " ${GREEN}✓${NC} $service is running"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} $service is NOT running"
|
||||||
|
add_issue "Service $service is stopped"
|
||||||
|
add_recommendation "Start service: systemctl start $service"
|
||||||
|
all_services_running=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$all_services_running" = false ]; then
|
||||||
|
add_recommendation "Start all services: Go to Acronis menu → Check Agent Status → Start All Services"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 2. Check disk space
|
||||||
|
echo -e "${BOLD}[2/7] Checking Disk Space...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check backup directory
|
||||||
|
if [ -d "/var/lib/Acronis" ]; then
|
||||||
|
backup_disk_usage=$(df -h /var/lib/Acronis | tail -1 | awk '{print $5}' | tr -d '%')
|
||||||
|
backup_disk_avail=$(df -h /var/lib/Acronis | tail -1 | awk '{print $4}')
|
||||||
|
|
||||||
|
echo " Acronis directory: /var/lib/Acronis"
|
||||||
|
echo " Disk usage: ${backup_disk_usage}%"
|
||||||
|
echo " Available: ${backup_disk_avail}"
|
||||||
|
|
||||||
|
if [ "$backup_disk_usage" -gt 95 ]; then
|
||||||
|
add_issue "Disk space critically low (${backup_disk_usage}% used)"
|
||||||
|
add_recommendation "Free up disk space or change backup destination"
|
||||||
|
elif [ "$backup_disk_usage" -gt 90 ]; then
|
||||||
|
add_warning "Disk space running low (${backup_disk_usage}% used)"
|
||||||
|
add_recommendation "Monitor disk space closely"
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✓${NC} Disk space OK"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check system disk
|
||||||
|
root_disk_usage=$(df -h / | tail -1 | awk '{print $5}' | tr -d '%')
|
||||||
|
if [ "$root_disk_usage" -gt 90 ]; then
|
||||||
|
add_warning "Root filesystem at ${root_disk_usage}% capacity"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 3. Check memory
|
||||||
|
echo -e "${BOLD}[3/7] Checking Memory...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
mem_total=$(free -h | grep "^Mem:" | awk '{print $2}')
|
||||||
|
mem_available=$(free -h | grep "^Mem:" | awk '{print $7}')
|
||||||
|
mem_used_percent=$(free | grep "^Mem:" | awk '{printf "%.0f", ($3/$2)*100}')
|
||||||
|
|
||||||
|
echo " Total memory: ${mem_total}"
|
||||||
|
echo " Available: ${mem_available}"
|
||||||
|
echo " Used: ${mem_used_percent}%"
|
||||||
|
|
||||||
|
if [ "$mem_used_percent" -gt 95 ]; then
|
||||||
|
add_warning "Memory usage critically high (${mem_used_percent}%)"
|
||||||
|
add_recommendation "Check for memory leaks or reduce backup concurrency"
|
||||||
|
elif [ "$mem_used_percent" -gt 90 ]; then
|
||||||
|
add_warning "Memory usage high (${mem_used_percent}%)"
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✓${NC} Memory OK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 4. Check network connectivity
|
||||||
|
echo -e "${BOLD}[4/7] Checking Network Connectivity...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if registered
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
cloud_url=$(grep -oP 'CloudUrl[>="].*?https://[^"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^"<]+' | head -1)
|
||||||
|
|
||||||
|
if [ -n "$cloud_url" ]; then
|
||||||
|
echo " Testing connection to: $cloud_url"
|
||||||
|
|
||||||
|
# Extract hostname
|
||||||
|
cloud_host=$(echo "$cloud_url" | sed 's|https://||' | sed 's|/.*||')
|
||||||
|
|
||||||
|
# Test connectivity
|
||||||
|
if curl -s --connect-timeout 5 -I "$cloud_url" >/dev/null 2>&1; then
|
||||||
|
echo -e " ${GREEN}✓${NC} Connection successful"
|
||||||
|
else
|
||||||
|
add_issue "Cannot connect to Acronis Cloud: $cloud_url"
|
||||||
|
add_recommendation "Check firewall rules and network connectivity"
|
||||||
|
add_recommendation "Test manually: curl -I $cloud_url"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test DNS resolution
|
||||||
|
if host "$cloud_host" >/dev/null 2>&1; then
|
||||||
|
echo -e " ${GREEN}✓${NC} DNS resolution OK"
|
||||||
|
else
|
||||||
|
add_issue "DNS resolution failed for $cloud_host"
|
||||||
|
add_recommendation "Check DNS configuration: /etc/resolv.conf"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
add_warning "Agent may not be registered with Acronis Cloud"
|
||||||
|
add_recommendation "Register agent: Acronis menu → Register with Cloud"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
add_warning "Acronis configuration file not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 5. Scan logs for errors
|
||||||
|
echo -e "${BOLD}[5/7] Scanning Logs for Errors...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Common error patterns
|
||||||
|
declare -A ERROR_PATTERNS=(
|
||||||
|
["INSUFFICIENT_SPACE"]="insufficient.*space|no.*space.*left|disk.*full"
|
||||||
|
["PERMISSION_DENIED"]="permission.*denied|access.*denied|cannot.*access"
|
||||||
|
["CONNECTION_FAILED"]="connection.*failed|connection.*refused|timeout|network.*error"
|
||||||
|
["AUTH_FAILED"]="authentication.*failed|invalid.*credentials|unauthorized"
|
||||||
|
["BACKUP_FAILED"]="backup.*failed|backup.*error|task.*failed"
|
||||||
|
["VSS_ERROR"]="vss.*error|snapshot.*failed|shadow.*copy.*error"
|
||||||
|
["DATABASE_ERROR"]="database.*error|sql.*error|db.*lock"
|
||||||
|
["FILE_LOCKED"]="file.*locked|file.*in.*use|sharing.*violation"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Scan primary log
|
||||||
|
primary_log="/var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log"
|
||||||
|
|
||||||
|
if [ -f "$primary_log" ]; then
|
||||||
|
echo " Scanning primary log: mms.0.log"
|
||||||
|
|
||||||
|
for pattern_name in "${!ERROR_PATTERNS[@]}"; do
|
||||||
|
pattern="${ERROR_PATTERNS[$pattern_name]}"
|
||||||
|
|
||||||
|
if grep -iE "$pattern" "$primary_log" 2>/dev/null | tail -1 | grep -q .; then
|
||||||
|
error_count=$(grep -icE "$pattern" "$primary_log" 2>/dev/null)
|
||||||
|
last_error=$(grep -iE "$pattern" "$primary_log" 2>/dev/null | tail -1)
|
||||||
|
|
||||||
|
echo -e " ${RED}⚠${NC} Found $pattern_name errors (count: $error_count)"
|
||||||
|
echo -e " Last: ${DIM}${last_error:0:80}...${NC}"
|
||||||
|
|
||||||
|
add_issue "$pattern_name detected in logs (count: $error_count)"
|
||||||
|
|
||||||
|
# Add specific recommendations
|
||||||
|
case "$pattern_name" in
|
||||||
|
"INSUFFICIENT_SPACE")
|
||||||
|
add_recommendation "Free up disk space or change backup destination"
|
||||||
|
;;
|
||||||
|
"PERMISSION_DENIED")
|
||||||
|
add_recommendation "Check file/directory permissions"
|
||||||
|
add_recommendation "Ensure Acronis agent has necessary access rights"
|
||||||
|
;;
|
||||||
|
"CONNECTION_FAILED")
|
||||||
|
add_recommendation "Check network connectivity and firewall rules"
|
||||||
|
add_recommendation "Verify Acronis Cloud URL is accessible"
|
||||||
|
;;
|
||||||
|
"AUTH_FAILED")
|
||||||
|
add_recommendation "Re-register agent with valid token"
|
||||||
|
add_recommendation "Check registration status in web console"
|
||||||
|
;;
|
||||||
|
"VSS_ERROR")
|
||||||
|
add_recommendation "Check VSS service: vssadmin list writers"
|
||||||
|
add_recommendation "Restart VSS: net stop vss && net start vss"
|
||||||
|
;;
|
||||||
|
"DATABASE_ERROR")
|
||||||
|
add_recommendation "Check database connections and locks"
|
||||||
|
add_recommendation "Consider application-aware backup settings"
|
||||||
|
;;
|
||||||
|
"FILE_LOCKED")
|
||||||
|
add_recommendation "Identify processes locking files: lsof"
|
||||||
|
add_recommendation "Schedule backups during low-activity periods"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for recent backup failures
|
||||||
|
recent_failures=$(grep -i "backup.*failed\|task.*failed" "$primary_log" 2>/dev/null | tail -5)
|
||||||
|
if [ -n "$recent_failures" ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}Recent backup failures:${NC}"
|
||||||
|
echo "$recent_failures" | while read -r line; do
|
||||||
|
echo -e " ${DIM}${line:0:100}${NC}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
add_warning "Primary log file not found: $primary_log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 6. Check for stuck backups
|
||||||
|
echo -e "${BOLD}[6/7] Checking for Stuck Processes...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check for long-running Acronis processes
|
||||||
|
old_processes=$(ps aux | grep -i acronis | grep -v grep | awk '{if ($10 ~ /[0-9][0-9]:[0-9][0-9]/) print $0}')
|
||||||
|
|
||||||
|
if [ -n "$old_processes" ]; then
|
||||||
|
echo -e " ${YELLOW}⚠${NC} Long-running Acronis processes detected:"
|
||||||
|
echo "$old_processes" | while read -r line; do
|
||||||
|
echo -e " ${DIM}$line${NC}"
|
||||||
|
done
|
||||||
|
add_warning "Long-running Acronis processes may indicate stuck backups"
|
||||||
|
add_recommendation "Review processes and consider restarting services if stuck"
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✓${NC} No stuck processes detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 7. Check configuration issues
|
||||||
|
echo -e "${BOLD}[7/7] Checking Configuration...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if backup plans are configured
|
||||||
|
if command -v acrocmd &>/dev/null; then
|
||||||
|
plan_count=$(acrocmd list plans 2>/dev/null | grep -c "^Plan ID" || echo "0")
|
||||||
|
echo " Configured backup plans: $plan_count"
|
||||||
|
|
||||||
|
if [ "$plan_count" -eq 0 ]; then
|
||||||
|
add_warning "No backup plans configured"
|
||||||
|
add_recommendation "Configure backup plans in Acronis web console"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ${DIM}acrocmd not available - cannot check backup plans${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check agent version
|
||||||
|
if [ -f "/usr/lib/Acronis/BackupAndRecovery/aakore" ]; then
|
||||||
|
agent_version=$(/usr/lib/Acronis/BackupAndRecovery/aakore --version 2>/dev/null | head -1 || echo "Unknown")
|
||||||
|
echo " Agent version: $agent_version"
|
||||||
|
else
|
||||||
|
add_warning "Cannot determine agent version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Summary Report
|
||||||
|
echo -e "${BOLD}DIAGNOSTIC SUMMARY${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ ${#ISSUES_FOUND[@]} -eq 0 ] && [ ${#WARNINGS_FOUND[@]} -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}${BOLD}✓ No issues detected${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Acronis appears to be healthy. If you're experiencing backup"
|
||||||
|
echo "failures, check the web console for detailed backup logs."
|
||||||
|
else
|
||||||
|
# Show issues
|
||||||
|
if [ ${#ISSUES_FOUND[@]} -gt 0 ]; then
|
||||||
|
echo -e "${RED}${BOLD}Critical Issues (${#ISSUES_FOUND[@]}):${NC}"
|
||||||
|
for issue in "${ISSUES_FOUND[@]}"; do
|
||||||
|
echo -e " ${RED}✗${NC} $issue"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show warnings
|
||||||
|
if [ ${#WARNINGS_FOUND[@]} -gt 0 ]; then
|
||||||
|
echo -e "${YELLOW}${BOLD}Warnings (${#WARNINGS_FOUND[@]}):${NC}"
|
||||||
|
for warning in "${WARNINGS_FOUND[@]}"; do
|
||||||
|
echo -e " ${YELLOW}⚠${NC} $warning"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show recommendations
|
||||||
|
if [ ${#RECOMMENDATIONS[@]} -gt 0 ]; then
|
||||||
|
echo -e "${CYAN}${BOLD}Recommendations:${NC}"
|
||||||
|
local rec_num=1
|
||||||
|
for rec in "${RECOMMENDATIONS[@]}"; do
|
||||||
|
echo -e " ${CYAN}${rec_num}.${NC} $rec"
|
||||||
|
((rec_num++))
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Quick actions
|
||||||
|
echo -e "${BOLD}Quick Actions:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${CYAN}1)${NC} View Full Logs (all errors)"
|
||||||
|
echo -e " ${CYAN}2)${NC} Restart All Services"
|
||||||
|
echo -e " ${CYAN}3)${NC} Generate Detailed Report"
|
||||||
|
echo -e " ${CYAN}4)${NC} Export Logs for Support"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Return to Menu"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select action (or press Enter to return): "
|
||||||
|
read -r action
|
||||||
|
|
||||||
|
case "$action" in
|
||||||
|
1)
|
||||||
|
# Show all errors
|
||||||
|
clear
|
||||||
|
print_banner "All Errors from Logs"
|
||||||
|
echo ""
|
||||||
|
if [ -f "$primary_log" ]; then
|
||||||
|
grep -iE "error|fail|critical|warn" "$primary_log" | tail -50
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
# Restart services
|
||||||
|
echo ""
|
||||||
|
echo "Restarting all Acronis services..."
|
||||||
|
systemctl restart aakore
|
||||||
|
systemctl restart acronis_mms
|
||||||
|
systemctl restart acronis_schedule
|
||||||
|
systemctl restart active-protection
|
||||||
|
echo ""
|
||||||
|
print_success "Services restarted"
|
||||||
|
echo ""
|
||||||
|
echo "Waiting 5 seconds for services to stabilize..."
|
||||||
|
sleep 5
|
||||||
|
echo ""
|
||||||
|
echo "Running diagnostic again..."
|
||||||
|
sleep 2
|
||||||
|
exec "$0"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
# Generate detailed report
|
||||||
|
report_file="/tmp/acronis-diagnostic-$(date +%Y%m%d-%H%M%S).txt"
|
||||||
|
echo ""
|
||||||
|
echo "Generating detailed report..."
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "Acronis Diagnostic Report"
|
||||||
|
echo "Generated: $(date)"
|
||||||
|
echo "Hostname: $(hostname)"
|
||||||
|
echo ""
|
||||||
|
echo "=== Service Status ==="
|
||||||
|
for service in "${SERVICES[@]}"; do
|
||||||
|
systemctl status "$service" 2>&1 | head -20
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "=== Recent Log Entries ==="
|
||||||
|
if [ -f "$primary_log" ]; then
|
||||||
|
tail -200 "$primary_log"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "=== System Resources ==="
|
||||||
|
df -h
|
||||||
|
echo ""
|
||||||
|
free -h
|
||||||
|
echo ""
|
||||||
|
echo "=== Network ==="
|
||||||
|
netstat -tuln | grep -E "7770|7800|8443|44445"
|
||||||
|
echo ""
|
||||||
|
echo "=== Processes ==="
|
||||||
|
ps aux | grep -i acronis | grep -v grep
|
||||||
|
} > "$report_file"
|
||||||
|
|
||||||
|
print_success "Report generated: $report_file"
|
||||||
|
echo ""
|
||||||
|
echo "You can send this report to Acronis support or review it locally."
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
# Export logs
|
||||||
|
archive_file="/tmp/acronis-logs-$(date +%Y%m%d-%H%M%S).tar.gz"
|
||||||
|
echo ""
|
||||||
|
echo "Exporting logs..."
|
||||||
|
|
||||||
|
if [ -d "/var/lib/Acronis/BackupAndRecovery/MMS" ]; then
|
||||||
|
tar -czf "$archive_file" /var/lib/Acronis/BackupAndRecovery/MMS/*.log 2>/dev/null
|
||||||
|
print_success "Logs exported: $archive_file"
|
||||||
|
echo ""
|
||||||
|
echo "Archive size: $(du -h "$archive_file" | awk '{print $1}')"
|
||||||
|
else
|
||||||
|
print_error "Log directory not found"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Executable
+249
@@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Agent Uninstaller
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Safely uninstall Acronis Cyber Protect agent
|
||||||
|
# Process:
|
||||||
|
# 1. Stop all Acronis services
|
||||||
|
# 2. Unregister from cloud (optional)
|
||||||
|
# 3. Remove Acronis packages
|
||||||
|
# 4. Clean up data directories (optional)
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Acronis Agent Uninstaller"
|
||||||
|
|
||||||
|
# Check if Acronis is installed
|
||||||
|
if ! systemctl list-unit-files | grep -q "acronis_mms.service"; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}⚠ Acronis Not Installed${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Acronis Cyber Protect does not appear to be installed on this system."
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}${BOLD}⚠️ WARNING ⚠️${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "This will completely remove Acronis Cyber Protect from this system."
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}What will be removed:${NC}"
|
||||||
|
echo " • All Acronis services (aakore, mms, schedule, active-protection)"
|
||||||
|
echo " • Acronis software packages"
|
||||||
|
echo " • Agent registration (if selected)"
|
||||||
|
echo " • Backup data and logs (if selected)"
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}This action cannot be easily undone!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Confirm uninstallation
|
||||||
|
echo -n "Type 'uninstall' to confirm removal: "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [ "$confirm" != "uninstall" ]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Uninstallation cancelled"
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Uninstallation Options:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Ask about data retention
|
||||||
|
echo -n "Remove backup data and logs? (yes/no) [no]: "
|
||||||
|
read -r remove_data
|
||||||
|
remove_data="${remove_data:-no}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -n "Unregister from Acronis Cloud? (yes/no) [yes]: "
|
||||||
|
read -r unregister
|
||||||
|
unregister="${unregister:-yes}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Uninstallation Summary:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Stop services: Yes"
|
||||||
|
echo " Remove software: Yes"
|
||||||
|
echo " Unregister agent: ${unregister}"
|
||||||
|
echo " Remove data/logs: ${remove_data}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -n "Proceed with uninstallation? (yes/no): "
|
||||||
|
read -r final_confirm
|
||||||
|
|
||||||
|
if [ "$final_confirm" != "yes" ]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Uninstallation cancelled"
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Starting Uninstallation...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Stop all services
|
||||||
|
echo "→ Stopping Acronis services..."
|
||||||
|
systemctl stop active-protection.service 2>/dev/null
|
||||||
|
systemctl stop acronis_schedule 2>/dev/null
|
||||||
|
systemctl stop acronis_mms 2>/dev/null
|
||||||
|
systemctl stop aakore 2>/dev/null
|
||||||
|
service acronis_mms stop 2>/dev/null
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if systemctl is-active --quiet acronis_mms; then
|
||||||
|
print_error "Warning: Some services may still be running"
|
||||||
|
else
|
||||||
|
print_success "Services stopped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Unregister from cloud if requested
|
||||||
|
if [ "$unregister" = "yes" ]; then
|
||||||
|
echo "→ Unregistering from Acronis Cloud..."
|
||||||
|
|
||||||
|
if [ -f "/usr/lib/Acronis/RegisterAgentTool/RegisterAgent" ]; then
|
||||||
|
if /usr/lib/Acronis/RegisterAgentTool/RegisterAgent -o unregister 2>/dev/null; then
|
||||||
|
print_success "Agent unregistered"
|
||||||
|
else
|
||||||
|
echo " ${YELLOW}Note: Unregistration may have failed (continuing anyway)${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ${YELLOW}Note: Registration tool not found (skipping)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disable services
|
||||||
|
echo "→ Disabling Acronis services..."
|
||||||
|
systemctl disable aakore 2>/dev/null
|
||||||
|
systemctl disable acronis_mms 2>/dev/null
|
||||||
|
systemctl disable acronis_schedule 2>/dev/null
|
||||||
|
systemctl disable active-protection.service 2>/dev/null
|
||||||
|
echo " ${GREEN}✓${NC} Services disabled"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Remove packages
|
||||||
|
echo "→ Removing Acronis packages..."
|
||||||
|
|
||||||
|
# Try different package managers
|
||||||
|
if command -v dpkg &>/dev/null; then
|
||||||
|
# Debian/Ubuntu
|
||||||
|
dpkg -l | grep -i acronis | awk '{print $2}' | while read -r pkg; do
|
||||||
|
echo " Removing: $pkg"
|
||||||
|
dpkg --purge "$pkg" 2>/dev/null
|
||||||
|
done
|
||||||
|
elif command -v rpm &>/dev/null; then
|
||||||
|
# RedHat/CentOS
|
||||||
|
rpm -qa | grep -i acronis | while read -r pkg; do
|
||||||
|
echo " Removing: $pkg"
|
||||||
|
rpm -e "$pkg" 2>/dev/null
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Packages removed"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Remove data directories if requested
|
||||||
|
if [ "$remove_data" = "yes" ]; then
|
||||||
|
echo "→ Removing Acronis data and logs..."
|
||||||
|
|
||||||
|
declare -a DATA_DIRS=(
|
||||||
|
"/var/lib/Acronis"
|
||||||
|
"/usr/lib/Acronis"
|
||||||
|
"/etc/Acronis"
|
||||||
|
"/opt/acronis"
|
||||||
|
)
|
||||||
|
|
||||||
|
for dir in "${DATA_DIRS[@]}"; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
local size=$(du -sh "$dir" 2>/dev/null | awk '{print $1}')
|
||||||
|
echo " Removing: $dir (${size})"
|
||||||
|
rm -rf "$dir" 2>/dev/null
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
print_success "Data directories removed"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up systemd
|
||||||
|
echo "→ Cleaning up system configuration..."
|
||||||
|
systemctl daemon-reload
|
||||||
|
echo " ${GREEN}✓${NC} systemd reloaded"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Final verification
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}${BOLD}✓ Uninstallation Complete${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if anything remains
|
||||||
|
local remaining=0
|
||||||
|
|
||||||
|
if systemctl list-unit-files | grep -q "acronis"; then
|
||||||
|
echo -e "${YELLOW}⚠ Some service files may still be present${NC}"
|
||||||
|
((remaining++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "/var/lib/Acronis" ] || [ -d "/usr/lib/Acronis" ]; then
|
||||||
|
echo -e "${YELLOW}⚠ Some directories were not removed${NC}"
|
||||||
|
((remaining++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $remaining -eq 0 ]; then
|
||||||
|
echo "Acronis Cyber Protect has been completely removed from this system."
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "Uninstallation mostly complete, but some files may remain."
|
||||||
|
echo "This is usually safe and won't affect system operation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show what was kept
|
||||||
|
if [ "$remove_data" = "no" ]; then
|
||||||
|
echo -e "${BOLD}Retained Data:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Backup data and logs were kept as requested:"
|
||||||
|
if [ -d "/var/lib/Acronis" ]; then
|
||||||
|
local data_size=$(du -sh /var/lib/Acronis 2>/dev/null | awk '{print $1}')
|
||||||
|
echo " Location: /var/lib/Acronis"
|
||||||
|
echo " Size: $data_size"
|
||||||
|
echo ""
|
||||||
|
echo "To remove this data later:"
|
||||||
|
echo " rm -rf /var/lib/Acronis"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "To reinstall Acronis in the future:"
|
||||||
|
echo " 1. Return to Backup & Recovery menu"
|
||||||
|
echo " 2. Select 'Acronis Management'"
|
||||||
|
echo " 3. Choose 'Install Acronis Agent'"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
press_enter
|
||||||
Executable
+314
@@ -0,0 +1,314 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Acronis Agent Update/Upgrade
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Update Acronis Cyber Protect agent to latest version
|
||||||
|
# Methods:
|
||||||
|
# - Automatic via cloud (web console)
|
||||||
|
# - Manual download and upgrade (preserves config/registration)
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Update Acronis Agent"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if Acronis is installed
|
||||||
|
if ! systemctl list-unit-files | grep -q "acronis_mms.service"; then
|
||||||
|
print_error "Acronis is not installed"
|
||||||
|
echo ""
|
||||||
|
echo "Install Acronis first:"
|
||||||
|
echo " 1. Return to Acronis menu"
|
||||||
|
echo " 2. Select 'Install Acronis Agent'"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BOLD}Current Installation${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check current version
|
||||||
|
echo "→ Checking current agent version..."
|
||||||
|
if [ -f "/usr/lib/Acronis/BackupAndRecovery/aakore" ]; then
|
||||||
|
current_version=$(/usr/lib/Acronis/BackupAndRecovery/aakore --version 2>/dev/null | head -1 || echo "Unknown")
|
||||||
|
echo " Current version: ${current_version}"
|
||||||
|
else
|
||||||
|
echo " ${YELLOW}Version unknown${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
echo ""
|
||||||
|
echo "→ Service status:"
|
||||||
|
if systemctl is-active --quiet acronis_mms; then
|
||||||
|
echo " ${GREEN}✓${NC} Services are running"
|
||||||
|
else
|
||||||
|
echo " ${YELLOW}⚠${NC} Some services are stopped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Update Methods${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}1. Automatic Update (via Acronis Cloud)${NC}"
|
||||||
|
echo " • Managed from web console"
|
||||||
|
echo " • Navigate to: Settings → Agent updates"
|
||||||
|
echo " • Can enable automatic updates"
|
||||||
|
echo " • Recommended for production environments"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}2. Manual Update (Download + Upgrade)${NC}"
|
||||||
|
echo " • Downloads latest installer"
|
||||||
|
echo " • Runs upgrade automatically"
|
||||||
|
echo " • Preserves configuration and registration"
|
||||||
|
echo " • Agent stays registered to same account"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select update method (1/2) or 0 to cancel [2]: "
|
||||||
|
read -r method
|
||||||
|
method="${method:-2}"
|
||||||
|
|
||||||
|
case "$method" in
|
||||||
|
1)
|
||||||
|
# Automatic update instructions
|
||||||
|
clear
|
||||||
|
print_banner "Automatic Agent Updates"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Configure Automatic Updates via Web Console${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Steps:"
|
||||||
|
echo " 1. Log in to Acronis web console"
|
||||||
|
|
||||||
|
# Try to get cloud URL
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
cloud_url=$(grep -oP 'CloudUrl[>="].*?https://[^"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^"<]+' | head -1)
|
||||||
|
if [ -n "$cloud_url" ]; then
|
||||||
|
echo " ${cloud_url}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo " 2. Navigate to: Settings → Agent updates"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Options available:"
|
||||||
|
echo " • Enable automatic updates"
|
||||||
|
echo " • Schedule update time"
|
||||||
|
echo " • Set update policy per device"
|
||||||
|
echo " • Configure notification preferences"
|
||||||
|
echo ""
|
||||||
|
echo " 4. Agents will update during maintenance window"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Benefits:${NC}"
|
||||||
|
echo " ✓ Centrally managed"
|
||||||
|
echo " ✓ Scheduled updates"
|
||||||
|
echo " ✓ Rollback capability"
|
||||||
|
echo " ✓ Update verification"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
;;
|
||||||
|
|
||||||
|
2)
|
||||||
|
# Manual update/upgrade
|
||||||
|
clear
|
||||||
|
print_banner "Manual Agent Upgrade"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Upgrade Process${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "This will:"
|
||||||
|
echo " 1. Download the latest Acronis agent installer"
|
||||||
|
echo " 2. Run installer over existing installation"
|
||||||
|
echo " 3. Automatically upgrade to latest version"
|
||||||
|
echo " 4. Preserve all configuration and registration"
|
||||||
|
echo " 5. Restart services with new version"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Note:${NC} The agent will stay registered to your Acronis account."
|
||||||
|
echo " No need to re-register after upgrade."
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Get cloud URL for download
|
||||||
|
SERVICE_URL="us5-cloud.acronis.com"
|
||||||
|
if [ -f "/etc/Acronis/Global.config" ]; then
|
||||||
|
config_url=$(grep -oP 'CloudUrl[>="].*?https://[^"<]+' /etc/Acronis/Global.config 2>/dev/null | grep -oP 'https://[^"<]+' | head -1)
|
||||||
|
if [ -n "$config_url" ]; then
|
||||||
|
SERVICE_URL=$(echo "$config_url" | sed 's|https://||' | sed 's|/.*||')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Download region: ${SERVICE_URL}"
|
||||||
|
echo ""
|
||||||
|
echo -n "Proceed with upgrade? (yes/no): "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [[ ! "$confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Upgrade cancelled"
|
||||||
|
press_enter
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Starting Upgrade...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Create download directory
|
||||||
|
DOWNLOAD_DIR="$SCRIPT_DIR/downloads"
|
||||||
|
mkdir -p "$DOWNLOAD_DIR"
|
||||||
|
cd "$DOWNLOAD_DIR" || exit 1
|
||||||
|
|
||||||
|
INSTALL_DIR="$DOWNLOAD_DIR/acronis-upgrade-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
cd "$INSTALL_DIR" || exit 1
|
||||||
|
|
||||||
|
# Download installer
|
||||||
|
echo "→ Downloading latest Acronis agent..."
|
||||||
|
DOWNLOAD_URL="https://${SERVICE_URL}/bc/api/ams/links/agents/redirect?language=multi&system=linux&architecture=64&productType=enterprise"
|
||||||
|
|
||||||
|
if wget -q --show-progress "$DOWNLOAD_URL" -O "Cyber_Protection_Agent_for_Linux_x86_64.bin"; then
|
||||||
|
print_success "Download complete"
|
||||||
|
else
|
||||||
|
print_error "Download failed"
|
||||||
|
echo ""
|
||||||
|
echo "Possible causes:"
|
||||||
|
echo " • No internet connection"
|
||||||
|
echo " • Invalid service URL"
|
||||||
|
echo " • Firewall blocking connection"
|
||||||
|
echo ""
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Make executable
|
||||||
|
chmod +x "Cyber_Protection_Agent_for_Linux_x86_64.bin" 2>/dev/null
|
||||||
|
|
||||||
|
# Verify file
|
||||||
|
file_size=$(stat -c%s "Cyber_Protection_Agent_for_Linux_x86_64.bin" 2>/dev/null || echo "0")
|
||||||
|
if [ "$file_size" -lt 1000000 ]; then
|
||||||
|
print_error "Downloaded file is too small (possibly corrupted)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
press_enter
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run upgrade (unattended mode)
|
||||||
|
echo "→ Running upgrade..."
|
||||||
|
echo ""
|
||||||
|
echo "The installer will automatically:"
|
||||||
|
echo " • Detect existing installation"
|
||||||
|
echo " • Upgrade to latest version"
|
||||||
|
echo " • Preserve configuration"
|
||||||
|
echo " • Keep registration"
|
||||||
|
echo ""
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
echo -e "${DIM}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
|
||||||
|
# Run installer in unattended mode
|
||||||
|
./Cyber_Protection_Agent_for_Linux_x86_64.bin -a
|
||||||
|
|
||||||
|
UPGRADE_EXIT_CODE=$?
|
||||||
|
|
||||||
|
echo -e "${DIM}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check result
|
||||||
|
if [ $UPGRADE_EXIT_CODE -eq 0 ]; then
|
||||||
|
print_success "Upgrade completed successfully!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check new version
|
||||||
|
echo "→ Verifying upgrade..."
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if [ -f "/usr/lib/Acronis/BackupAndRecovery/aakore" ]; then
|
||||||
|
new_version=$(/usr/lib/Acronis/BackupAndRecovery/aakore --version 2>/dev/null | head -1 || echo "Unknown")
|
||||||
|
echo " New version: ${new_version}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$new_version" != "$current_version" ]; then
|
||||||
|
print_success "Agent upgraded: $current_version → $new_version"
|
||||||
|
else
|
||||||
|
echo " ${YELLOW}Version appears unchanged (may already be latest)${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check services
|
||||||
|
echo "→ Checking services..."
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
if systemctl is-active --quiet acronis_mms; then
|
||||||
|
print_success "Services are running"
|
||||||
|
else
|
||||||
|
echo " ${YELLOW}⚠ Services may need restart${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -n "Restart Acronis services? (yes/no): "
|
||||||
|
read -r restart_confirm
|
||||||
|
|
||||||
|
if [[ "$restart_confirm" =~ ^[Yy]([Ee][Ss])?$ ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "→ Restarting services..."
|
||||||
|
systemctl restart aakore
|
||||||
|
systemctl restart acronis_mms
|
||||||
|
systemctl restart acronis_schedule
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if systemctl is-active --quiet acronis_mms; then
|
||||||
|
print_success "Services restarted"
|
||||||
|
else
|
||||||
|
print_error "Service restart failed"
|
||||||
|
echo "Check status: systemctl status acronis_mms"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}${BOLD}✓ Upgrade Complete${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "The agent has been upgraded and remains registered."
|
||||||
|
echo "Backups will continue according to existing schedules."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
else
|
||||||
|
print_error "Upgrade failed with exit code $UPGRADE_EXIT_CODE"
|
||||||
|
echo ""
|
||||||
|
echo "Common issues:"
|
||||||
|
echo " • Agent is already latest version"
|
||||||
|
echo " • Insufficient disk space"
|
||||||
|
echo " • Services in use"
|
||||||
|
echo ""
|
||||||
|
echo "Check logs for details:"
|
||||||
|
echo " tail -f /var/lib/Acronis/BackupAndRecovery/MMS/mms.0.log"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
echo "→ Cleaning up installation files..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
press_enter
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo ""
|
||||||
|
echo "Update cancelled"
|
||||||
|
press_enter
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Executable
+243
@@ -0,0 +1,243 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Server Toolkit Data Cleanup
|
||||||
|
################################################################################
|
||||||
|
# Purpose: Remove all toolkit-generated data (for wiping before system transfer)
|
||||||
|
# Use Case: When moving toolkit to another server or fresh start
|
||||||
|
#
|
||||||
|
# What gets cleaned:
|
||||||
|
# - IP reputation database
|
||||||
|
# - Temporary analysis files
|
||||||
|
# - Cached data
|
||||||
|
# - Generated reports
|
||||||
|
# - Session data
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_banner "Server Toolkit Data Cleanup"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}${BOLD}⚠️ WARNING ⚠️${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "This will remove ALL data collected by the Server Toolkit:"
|
||||||
|
echo ""
|
||||||
|
echo " • IP reputation database (/var/lib/server-toolkit/)"
|
||||||
|
echo " • Temporary analysis files (/tmp/)"
|
||||||
|
echo " • Generated reports"
|
||||||
|
echo " • Cached data"
|
||||||
|
echo " • Session files"
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}This action CANNOT be undone!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Use this when:"
|
||||||
|
echo " ✓ Moving toolkit to a different server"
|
||||||
|
echo " ✓ Starting fresh analysis"
|
||||||
|
echo " ✓ Removing server-specific data before sharing"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
read -p "Type 'yes' to confirm cleanup: " confirm
|
||||||
|
|
||||||
|
if [ "$confirm" != "yes" ]; then
|
||||||
|
echo ""
|
||||||
|
print_error "Cleanup cancelled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Starting cleanup..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Track what was cleaned
|
||||||
|
cleaned_count=0
|
||||||
|
cleaned_size=0
|
||||||
|
|
||||||
|
# Function to safely remove directory/file and track size
|
||||||
|
safe_remove() {
|
||||||
|
local path="$1"
|
||||||
|
local description="$2"
|
||||||
|
|
||||||
|
if [ -e "$path" ]; then
|
||||||
|
# Calculate size before removing
|
||||||
|
if [ -d "$path" ]; then
|
||||||
|
size=$(du -sb "$path" 2>/dev/null | awk '{print $1}' || echo "0")
|
||||||
|
else
|
||||||
|
size=$(stat -c%s "$path" 2>/dev/null || echo "0")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove
|
||||||
|
rm -rf "$path" 2>/dev/null
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
cleaned_size=$((cleaned_size + size))
|
||||||
|
((cleaned_count++))
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: $description"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Failed to remove: $description"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e " ${DIM}○${NC} Not found: $description (already clean)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo -e "${BOLD}IP Reputation Database:${NC}"
|
||||||
|
safe_remove "/var/lib/server-toolkit/ip-reputation" "IP reputation database (including hash index)"
|
||||||
|
safe_remove "/var/lib/server-toolkit" "Toolkit data directory"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BOLD}Temporary Analysis Files:${NC}"
|
||||||
|
# Bot analyzer temp files
|
||||||
|
for pattern in /tmp/bot_analysis_* /tmp/*_bot_*.txt; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
rm -f $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: Bot analysis temp files"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 500 error tracker temp files
|
||||||
|
for pattern in /tmp/500-tracker-* /tmp/*500*.txt; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
rm -rf $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: 500 error tracker temp files"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Live monitoring temp files
|
||||||
|
for pattern in /tmp/live-monitor-* /tmp/*monitor*.tmp; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
rm -rf $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: Live monitoring temp files"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Error analyzer temp files
|
||||||
|
for pattern in /tmp/error_analysis_* /tmp/*error*.tmp; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
rm -f $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: Error analyzer temp files"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Generic toolkit temp files
|
||||||
|
for pattern in /tmp/toolkit_* /tmp/server-toolkit*; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
rm -rf $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: Generic toolkit temp files"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BOLD}Generated Reports:${NC}"
|
||||||
|
# Look for common report locations
|
||||||
|
for pattern in /tmp/*_report_*.txt /tmp/*_analysis_*.txt /root/*toolkit*.txt /root/*_report*.txt; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
count=$(ls $pattern 2>/dev/null | wc -l)
|
||||||
|
rm -f $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: $count report file(s)"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BOLD}Cache and Session Data:${NC}"
|
||||||
|
# Cached analysis data
|
||||||
|
if [ -d "/var/cache/server-toolkit" ]; then
|
||||||
|
safe_remove "/var/cache/server-toolkit" "Toolkit cache directory"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Session/lock files
|
||||||
|
for pattern in /var/run/server-toolkit* /var/lock/server-toolkit*; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
rm -f $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: Session/lock files"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BOLD}Log Files (Optional):${NC}"
|
||||||
|
echo -n "Remove toolkit execution logs? (yes/no) [no]: "
|
||||||
|
read remove_logs
|
||||||
|
remove_logs="${remove_logs:-no}"
|
||||||
|
|
||||||
|
if [ "$remove_logs" = "yes" ]; then
|
||||||
|
for pattern in /var/log/server-toolkit*.log; do
|
||||||
|
if ls $pattern 2>/dev/null | grep -q .; then
|
||||||
|
count=$(ls $pattern 2>/dev/null | wc -l)
|
||||||
|
rm -f $pattern 2>/dev/null
|
||||||
|
echo -e " ${GREEN}✓${NC} Removed: $count log file(s)"
|
||||||
|
((cleaned_count++))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo -e " ${DIM}○${NC} Logs kept (skipped)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Convert size to human readable
|
||||||
|
if [ $cleaned_size -lt 1024 ]; then
|
||||||
|
size_human="${cleaned_size}B"
|
||||||
|
elif [ $cleaned_size -lt 1048576 ]; then
|
||||||
|
size_human="$((cleaned_size / 1024))KB"
|
||||||
|
elif [ $cleaned_size -lt 1073741824 ]; then
|
||||||
|
size_human="$((cleaned_size / 1048576))MB"
|
||||||
|
else
|
||||||
|
size_human="$((cleaned_size / 1073741824))GB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}${BOLD}✓ Cleanup Complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Summary:"
|
||||||
|
echo " Items removed: $cleaned_count"
|
||||||
|
echo " Space freed: $size_human"
|
||||||
|
echo ""
|
||||||
|
echo "The toolkit is now clean and ready for:"
|
||||||
|
echo " • Transfer to another server"
|
||||||
|
echo " • Fresh analysis start"
|
||||||
|
echo " • Sharing without server-specific data"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verify critical directories are gone
|
||||||
|
missing=0
|
||||||
|
[ -d "/var/lib/server-toolkit" ] && { echo -e "${YELLOW}Warning: /var/lib/server-toolkit still exists${NC}"; ((missing++)); }
|
||||||
|
[ -d "/tmp/live-monitor-current" ] && { echo -e "${YELLOW}Warning: /tmp/live-monitor-current still exists${NC}"; ((missing++)); }
|
||||||
|
|
||||||
|
if [ $missing -gt 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Some directories could not be removed (may be in use)${NC}"
|
||||||
|
echo "Try stopping any running toolkit scripts and run cleanup again."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
@@ -27,6 +27,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|||||||
source "$SCRIPT_DIR/lib/common-functions.sh"
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
source "$SCRIPT_DIR/lib/system-detect.sh"
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
source "$SCRIPT_DIR/lib/user-manager.sh"
|
source "$SCRIPT_DIR/lib/user-manager.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/ip-reputation.sh"
|
||||||
|
|
||||||
# Default configuration (auto-detected from system)
|
# Default configuration (auto-detected from system)
|
||||||
LOG_DIR="${SYS_LOG_DIR:-/var/log/apache2/domlogs}"
|
LOG_DIR="${SYS_LOG_DIR:-/var/log/apache2/domlogs}"
|
||||||
@@ -925,9 +926,29 @@ calculate_threat_scores() {
|
|||||||
|
|
||||||
# Only output IPs with score > 0
|
# Only output IPs with score > 0
|
||||||
[ $score -gt 0 ] && echo "$score|$ip|$req_count"
|
[ $score -gt 0 ] && echo "$score|$ip|$req_count"
|
||||||
|
|
||||||
|
# Track in centralized IP reputation database (background process)
|
||||||
|
if [ $score -gt 0 ]; then
|
||||||
|
(
|
||||||
|
# Update IP with hit count
|
||||||
|
increment_ip_hits "$ip" "$req_count" >/dev/null 2>&1
|
||||||
|
|
||||||
|
# Tag with specific attack types found
|
||||||
|
[ -n "${threat_ips_sqli[$ip]}" ] && flag_ip_attack "$ip" "SQL_INJECTION" 0 "Bot analyzer: SQL injection attempts" >/dev/null 2>&1
|
||||||
|
[ -n "${threat_ips_xss[$ip]}" ] && flag_ip_attack "$ip" "XSS" 0 "Bot analyzer: XSS attempts" >/dev/null 2>&1
|
||||||
|
[ -n "${threat_ips_path[$ip]}" ] && flag_ip_attack "$ip" "PATH_TRAVERSAL" 0 "Bot analyzer: Path traversal" >/dev/null 2>&1
|
||||||
|
[ -n "${threat_ips_rce[$ip]}" ] && flag_ip_attack "$ip" "RCE" 0 "Bot analyzer: RCE/shell upload attempts" >/dev/null 2>&1
|
||||||
|
[ -n "${threat_ips_login[$ip]}" ] && flag_ip_attack "$ip" "BRUTEFORCE" 0 "Bot analyzer: Login bruteforce" >/dev/null 2>&1
|
||||||
|
[ -n "${threat_ips_ddos[$ip]}" ] && flag_ip_attack "$ip" "DDOS" 0 "Bot analyzer: Rapid-fire requests" >/dev/null 2>&1
|
||||||
|
[ -n "${threat_ips_suspicious[$ip]}" ] && flag_ip_attack "$ip" "SCANNER" 0 "Bot analyzer: Suspicious user-agent" >/dev/null 2>&1
|
||||||
|
) &
|
||||||
|
fi
|
||||||
done | sort -t'|' -k1 -rn > "$TEMP_DIR/threat_scores.txt"
|
done | sort -t'|' -k1 -rn > "$TEMP_DIR/threat_scores.txt"
|
||||||
|
|
||||||
print_success "Threat scores calculated"
|
# Wait for background IP reputation updates to complete
|
||||||
|
wait
|
||||||
|
|
||||||
|
print_success "Threat scores calculated and IP reputation updated"
|
||||||
}
|
}
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|||||||
Executable
+494
@@ -0,0 +1,494 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# IP Reputation Manager
|
||||||
|
################################################################################
|
||||||
|
# Purpose: View, query, and manage the centralized IP reputation database
|
||||||
|
# Features:
|
||||||
|
# - Query individual IPs
|
||||||
|
# - View top malicious IPs
|
||||||
|
# - View top active IPs
|
||||||
|
# - Export database
|
||||||
|
# - Database statistics
|
||||||
|
# - Cleanup old entries
|
||||||
|
# - Manual IP flagging/whitelisting
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Get script directory
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/ip-reputation.sh"
|
||||||
|
|
||||||
|
# Require root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
print_error "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Menu display
|
||||||
|
show_menu() {
|
||||||
|
clear
|
||||||
|
print_banner "IP Reputation Manager"
|
||||||
|
|
||||||
|
# Show quick stats
|
||||||
|
local total_ips=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo 0)
|
||||||
|
local db_size=$(du -h "$IP_REP_DB" 2>/dev/null | awk '{print $1}' || echo "0B")
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}${BOLD}Database Status:${NC} $total_ips IPs tracked | Size: $db_size"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Query & View:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${GREEN}1)${NC} Query IP Reputation - Look up specific IP"
|
||||||
|
echo -e " ${GREEN}2)${NC} Top Malicious IPs - Highest reputation scores"
|
||||||
|
echo -e " ${GREEN}3)${NC} Top Active IPs - Most hits/requests"
|
||||||
|
echo -e " ${GREEN}4)${NC} Database Statistics - Overview of tracked IPs"
|
||||||
|
echo -e " ${GREEN}5)${NC} Live Monitoring - Real-time reputation updates"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Database Management:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BLUE}6)${NC} Export Database - Export to readable text file"
|
||||||
|
echo -e " ${BLUE}7)${NC} Cleanup Old Entries - Remove IPs not seen in X days"
|
||||||
|
echo -e " ${BLUE}8)${NC} Compact Database - Remove duplicate entries (faster writes)"
|
||||||
|
echo -e " ${BLUE}9)${NC} Rebuild Index - Optimize database for speed"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Manual Actions:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}10)${NC} Flag IP as Malicious - Manually mark IP as threat"
|
||||||
|
echo -e " ${YELLOW}11)${NC} Mark IP as Legitimate - Whitelist/reduce score"
|
||||||
|
echo -e " ${YELLOW}12)${NC} Import IPs from Log - Batch import from file"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${RED}0)${NC} Exit"
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}──────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -n "Select option: "
|
||||||
|
}
|
||||||
|
|
||||||
|
# Query individual IP
|
||||||
|
query_ip_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Query IP Reputation"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter IP address to query: "
|
||||||
|
read -r ip_address
|
||||||
|
|
||||||
|
if [ -z "$ip_address" ]; then
|
||||||
|
print_error "No IP address provided"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate IP format (basic check)
|
||||||
|
if ! [[ "$ip_address" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||||
|
print_error "Invalid IP address format"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
query_ip_reputation "$ip_address"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# View top malicious IPs
|
||||||
|
view_top_malicious() {
|
||||||
|
clear
|
||||||
|
print_banner "Top Malicious IPs"
|
||||||
|
echo ""
|
||||||
|
echo -n "How many top IPs to show? [20]: "
|
||||||
|
read -r limit
|
||||||
|
limit="${limit:-20}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}${BOLD}Top $limit Most Malicious IPs (by Reputation Score)${NC}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
printf "%-15s | %-7s | %-4s | %-8s | %-8s | %-30s\n" \
|
||||||
|
"IP ADDRESS" "HITS" "CTRY" "REP" "LEVEL" "ATTACK TYPES"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
get_top_malicious_ips "$limit" | while IFS='|' read -r ip hit_count rep_score country attack_flags first_seen last_seen last_activity notes; do
|
||||||
|
local category=$(get_ip_reputation_category "$rep_score")
|
||||||
|
local attacks=$(decode_attack_flags "$attack_flags")
|
||||||
|
|
||||||
|
# Color code by reputation
|
||||||
|
local color="$NC"
|
||||||
|
case "$category" in
|
||||||
|
CRITICAL) color="$RED$BOLD" ;;
|
||||||
|
HIGH) color="$RED" ;;
|
||||||
|
MEDIUM) color="$YELLOW" ;;
|
||||||
|
LOW) color="$CYAN" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf "${color}%-15s | %-7s | %-4s | %-3s/100 | %-8s | %-30s${NC}\n" \
|
||||||
|
"$ip" "$hit_count" "$country" "$rep_score" "$category" "${attacks:0:30}"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# View top active IPs
|
||||||
|
view_top_active() {
|
||||||
|
clear
|
||||||
|
print_banner "Top Active IPs"
|
||||||
|
echo ""
|
||||||
|
echo -n "How many top IPs to show? [20]: "
|
||||||
|
read -r limit
|
||||||
|
limit="${limit:-20}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}${BOLD}Top $limit Most Active IPs (by Hit Count)${NC}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
printf "%-15s | %-7s | %-4s | %-8s | %-8s | %-30s\n" \
|
||||||
|
"IP ADDRESS" "HITS" "CTRY" "REP" "LEVEL" "ATTACK TYPES"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
get_top_active_ips "$limit" | while IFS='|' read -r ip hit_count rep_score country attack_flags first_seen last_seen last_activity notes; do
|
||||||
|
local category=$(get_ip_reputation_category "$rep_score")
|
||||||
|
local attacks=$(decode_attack_flags "$attack_flags")
|
||||||
|
|
||||||
|
# Color code by hit count
|
||||||
|
local color="$NC"
|
||||||
|
if [ $hit_count -gt 10000 ]; then
|
||||||
|
color="$RED$BOLD"
|
||||||
|
elif [ $hit_count -gt 1000 ]; then
|
||||||
|
color="$YELLOW"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "${color}%-15s | %-7s | %-4s | %-3s/100 | %-8s | %-30s${NC}\n" \
|
||||||
|
"$ip" "$hit_count" "$country" "$rep_score" "$category" "${attacks:0:30}"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show statistics
|
||||||
|
view_statistics() {
|
||||||
|
clear
|
||||||
|
print_banner "Database Statistics"
|
||||||
|
echo ""
|
||||||
|
show_ip_statistics
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Additional stats
|
||||||
|
echo "Recent Activity (Last 24 hours):"
|
||||||
|
local cutoff=$(($(date +%s) - 86400))
|
||||||
|
local recent_count=$(awk -F'|' -v cut="$cutoff" '$7 >= cut' "$IP_REP_DB" 2>/dev/null | wc -l)
|
||||||
|
echo " Active IPs: $recent_count"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Top countries
|
||||||
|
echo "Top Countries by IP Count:"
|
||||||
|
awk -F'|' '{print $4}' "$IP_REP_DB" 2>/dev/null | grep -v '^$' | sort | uniq -c | sort -rn | head -5 | \
|
||||||
|
while read count country; do
|
||||||
|
printf " %-4s: %s IPs\n" "$country" "$count"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export database
|
||||||
|
export_database_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Export IP Reputation Database"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter output file path [/tmp/ip_reputation_export.txt]: "
|
||||||
|
read -r output_path
|
||||||
|
output_path="${output_path:-/tmp/ip_reputation_export.txt}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Exporting database to $output_path..."
|
||||||
|
export_ip_reputation "$output_path"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "Database exported successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "View with: cat $output_path"
|
||||||
|
echo "Or: less $output_path"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup old entries
|
||||||
|
cleanup_database_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Cleanup Old Entries"
|
||||||
|
echo ""
|
||||||
|
echo "Remove IPs that haven't been seen in how many days?"
|
||||||
|
echo ""
|
||||||
|
echo -n "Days [90]: "
|
||||||
|
read -r days
|
||||||
|
days="${days:-90}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "This will remove IPs not seen in the last $days days."
|
||||||
|
echo -n "Continue? (yes/no): "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [ "$confirm" != "yes" ]; then
|
||||||
|
echo "Cancelled"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
cleanup_old_ips "$days"
|
||||||
|
echo ""
|
||||||
|
print_success "Cleanup complete!"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compact database
|
||||||
|
compact_database_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Compact Database"
|
||||||
|
echo ""
|
||||||
|
local total_before=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo 0)
|
||||||
|
echo "Current database size: $total_before entries"
|
||||||
|
echo ""
|
||||||
|
echo "This will remove duplicate IP entries created by fast append-only writes."
|
||||||
|
echo "The database will be compacted and re-indexed."
|
||||||
|
echo ""
|
||||||
|
echo -n "Continue? (yes/no): "
|
||||||
|
read -r confirm
|
||||||
|
|
||||||
|
if [ "$confirm" != "yes" ]; then
|
||||||
|
echo "Cancelled"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
compact_database
|
||||||
|
echo ""
|
||||||
|
print_success "Database compacted successfully!"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rebuild index
|
||||||
|
rebuild_index_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Rebuild Database Index"
|
||||||
|
echo ""
|
||||||
|
echo "Rebuilding index for optimized lookups..."
|
||||||
|
rebuild_index
|
||||||
|
echo ""
|
||||||
|
print_success "Index rebuilt successfully!"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Flag IP as malicious
|
||||||
|
flag_ip_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Flag IP as Malicious"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter IP address: "
|
||||||
|
read -r ip_address
|
||||||
|
|
||||||
|
if [ -z "$ip_address" ]; then
|
||||||
|
print_error "No IP address provided"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Attack Type:"
|
||||||
|
echo " 1) SQL Injection"
|
||||||
|
echo " 2) XSS"
|
||||||
|
echo " 3) Path Traversal"
|
||||||
|
echo " 4) RCE/Shell Upload"
|
||||||
|
echo " 5) Brute Force"
|
||||||
|
echo " 6) DDoS"
|
||||||
|
echo " 7) Bot/Scanner"
|
||||||
|
echo " 8) Exploit"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select [1]: "
|
||||||
|
read -r attack_choice
|
||||||
|
attack_choice="${attack_choice:-1}"
|
||||||
|
|
||||||
|
case "$attack_choice" in
|
||||||
|
1) attack_type="SQL_INJECTION" ;;
|
||||||
|
2) attack_type="XSS" ;;
|
||||||
|
3) attack_type="PATH_TRAVERSAL" ;;
|
||||||
|
4) attack_type="RCE" ;;
|
||||||
|
5) attack_type="BRUTEFORCE" ;;
|
||||||
|
6) attack_type="DDOS" ;;
|
||||||
|
7) attack_type="SCANNER" ;;
|
||||||
|
8) attack_type="EXPLOIT" ;;
|
||||||
|
*) attack_type="SUSPICIOUS" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -n "Notes/Description: "
|
||||||
|
read -r notes
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Flagging $ip_address for $attack_type..."
|
||||||
|
flag_ip_attack "$ip_address" "$attack_type" 0 "$notes"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "IP flagged successfully!"
|
||||||
|
echo ""
|
||||||
|
query_ip_reputation "$ip_address"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mark IP as legitimate
|
||||||
|
whitelist_ip_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Mark IP as Legitimate"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter IP address: "
|
||||||
|
read -r ip_address
|
||||||
|
|
||||||
|
if [ -z "$ip_address" ]; then
|
||||||
|
print_error "No IP address provided"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -n "Reason/Notes: "
|
||||||
|
read -r notes
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Marking $ip_address as legitimate..."
|
||||||
|
mark_ip_legitimate "$ip_address" "$notes"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "IP marked as legitimate!"
|
||||||
|
echo ""
|
||||||
|
query_ip_reputation "$ip_address"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Import from log file
|
||||||
|
import_log_interactive() {
|
||||||
|
clear
|
||||||
|
print_banner "Import IPs from Log File"
|
||||||
|
echo ""
|
||||||
|
echo -n "Enter log file path: "
|
||||||
|
read -r log_path
|
||||||
|
|
||||||
|
if [ ! -f "$log_path" ]; then
|
||||||
|
print_error "File not found: $log_path"
|
||||||
|
press_enter
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Attack Type (will be applied to all IPs):"
|
||||||
|
echo " 1) Suspicious (default)"
|
||||||
|
echo " 2) SQL Injection"
|
||||||
|
echo " 3) XSS"
|
||||||
|
echo " 4) Bot/Scanner"
|
||||||
|
echo " 5) DDoS"
|
||||||
|
echo ""
|
||||||
|
echo -n "Select [1]: "
|
||||||
|
read -r attack_choice
|
||||||
|
attack_choice="${attack_choice:-1}"
|
||||||
|
|
||||||
|
case "$attack_choice" in
|
||||||
|
2) attack_type="SQL_INJECTION" ;;
|
||||||
|
3) attack_type="XSS" ;;
|
||||||
|
4) attack_type="SCANNER" ;;
|
||||||
|
5) attack_type="DDOS" ;;
|
||||||
|
*) attack_type="SUSPICIOUS" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Importing IPs from $log_path as $attack_type..."
|
||||||
|
import_ips_from_log "$log_path" "$attack_type" 5
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
print_success "Import complete!"
|
||||||
|
echo ""
|
||||||
|
press_enter
|
||||||
|
}
|
||||||
|
|
||||||
|
# Live monitoring (real-time updates)
|
||||||
|
live_monitoring() {
|
||||||
|
clear
|
||||||
|
print_banner "Live IP Reputation Monitoring"
|
||||||
|
echo ""
|
||||||
|
echo "Watching database for changes... (Press Ctrl+C to exit)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local last_count=0
|
||||||
|
local last_update=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
local current_count=$(wc -l < "$IP_REP_DB" 2>/dev/null || echo 0)
|
||||||
|
local current_time=$(stat -c %Y "$IP_REP_DB" 2>/dev/null || echo 0)
|
||||||
|
|
||||||
|
if [ $current_count -ne $last_count ] || [ $current_time -ne $last_update ]; then
|
||||||
|
clear
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "LIVE IP REPUTATION MONITORING - $(date '+%H:%M:%S')"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
show_ip_statistics
|
||||||
|
echo ""
|
||||||
|
echo "Recent Top 10 Updates:"
|
||||||
|
get_top_malicious_ips 10 | head -10 | while IFS='|' read -r ip hit_count rep_score country attack_flags _ _ _ _; do
|
||||||
|
local category=$(get_ip_reputation_category "$rep_score")
|
||||||
|
local attacks=$(decode_attack_flags "$attack_flags")
|
||||||
|
printf "%-15s | %5s hits | %3s/100 | %-8s | %s\n" "$ip" "$hit_count" "$rep_score" "$category" "${attacks:0:40}"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "Press Ctrl+C to exit | Refreshing every 2 seconds..."
|
||||||
|
|
||||||
|
last_count=$current_count
|
||||||
|
last_update=$current_time
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
main() {
|
||||||
|
while true; do
|
||||||
|
show_menu
|
||||||
|
read -r choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
1) query_ip_interactive ;;
|
||||||
|
2) view_top_malicious ;;
|
||||||
|
3) view_top_active ;;
|
||||||
|
4) view_statistics ;;
|
||||||
|
5) live_monitoring ;;
|
||||||
|
6) export_database_interactive ;;
|
||||||
|
7) cleanup_database_interactive ;;
|
||||||
|
8) compact_database_interactive ;;
|
||||||
|
9) rebuild_index_interactive ;;
|
||||||
|
10) flag_ip_interactive ;;
|
||||||
|
11) whitelist_ip_interactive ;;
|
||||||
|
12) import_log_interactive ;;
|
||||||
|
0)
|
||||||
|
clear
|
||||||
|
echo "Exiting..."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Invalid option"
|
||||||
|
sleep 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main
|
||||||
|
main
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
source "$SCRIPT_DIR/lib/common-functions.sh"
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
source "$SCRIPT_DIR/lib/system-detect.sh"
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/ip-reputation.sh"
|
||||||
|
|
||||||
# Require root
|
# Require root
|
||||||
if [ "$EUID" -ne 0 ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
@@ -341,6 +342,21 @@ process_threat_event() {
|
|||||||
local threat_level=$(classify_threat_level "${IP_COUNTER[$ip]}")
|
local threat_level=$(classify_threat_level "${IP_COUNTER[$ip]}")
|
||||||
IP_THREAT_LEVEL[$ip]="$threat_level"
|
IP_THREAT_LEVEL[$ip]="$threat_level"
|
||||||
|
|
||||||
|
# Track in centralized IP reputation database
|
||||||
|
# Map attack types to reputation flags
|
||||||
|
local rep_attack_type="SUSPICIOUS"
|
||||||
|
case "$attack_type" in
|
||||||
|
SSH_BRUTEFORCE) rep_attack_type="BRUTEFORCE" ;;
|
||||||
|
SQL_INJECTION) rep_attack_type="SQL_INJECTION" ;;
|
||||||
|
XSS_ATTACK) rep_attack_type="XSS" ;;
|
||||||
|
PATH_TRAVERSAL) rep_attack_type="PATH_TRAVERSAL" ;;
|
||||||
|
EXPLOIT) rep_attack_type="EXPLOIT" ;;
|
||||||
|
DDOS) rep_attack_type="DDOS" ;;
|
||||||
|
BOT) rep_attack_type="BOT" ;;
|
||||||
|
*) rep_attack_type="SCANNER" ;;
|
||||||
|
esac
|
||||||
|
flag_ip_attack "$ip" "$rep_attack_type" 0 "$attack_type: $details" >/dev/null 2>&1 &
|
||||||
|
|
||||||
# Log to feed
|
# Log to feed
|
||||||
log_event "$ip" "$attack_type" "$(get_threat_color "$threat_level")" "$details"
|
log_event "$ip" "$attack_type" "$(get_threat_color "$threat_level")" "$details"
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+839
@@ -0,0 +1,839 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Fast 500 Error Tracker
|
||||||
|
################################################################################
|
||||||
|
# Purpose: ONLY track 500 errors - find them fast and show WHY
|
||||||
|
# No menus, no options - just find 500s and diagnose the root cause
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/ip-reputation.sh"
|
||||||
|
|
||||||
|
# Ensure color variables are set
|
||||||
|
DIM='\033[2m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
|
||||||
|
print_banner "Fast 500 Error Tracker"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Scanning for 500 errors and diagnosing root causes..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Ask for time range
|
||||||
|
echo -e "${CYAN}How far back to scan?${NC}"
|
||||||
|
echo " 1) Last 24 hours (default)"
|
||||||
|
echo " 2) Last 7 days"
|
||||||
|
echo " 3) Last 30 days"
|
||||||
|
echo ""
|
||||||
|
read -p "Select option [1]: " time_choice
|
||||||
|
time_choice=${time_choice:-1}
|
||||||
|
|
||||||
|
case $time_choice in
|
||||||
|
1) HOURS_TO_SCAN=24 ;;
|
||||||
|
2) HOURS_TO_SCAN=168 ;;
|
||||||
|
3) HOURS_TO_SCAN=720 ;;
|
||||||
|
*) HOURS_TO_SCAN=24 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "→ Scanning last $HOURS_TO_SCAN hours of access logs..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
TEMP_DIR="/tmp/500-tracker-$$"
|
||||||
|
mkdir -p "$TEMP_DIR"
|
||||||
|
trap "rm -rf $TEMP_DIR" EXIT
|
||||||
|
|
||||||
|
ERRORS_500="$TEMP_DIR/errors_500.txt"
|
||||||
|
ERROR_DETAILS="$TEMP_DIR/error_details.txt"
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
DOMLOGS_DIR="/var/log/apache2/domlogs"
|
||||||
|
|
||||||
|
# Get cutoff time
|
||||||
|
cutoff_time=$(date -d "$HOURS_TO_SCAN hours ago" +%s 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
declare -A domain_count
|
||||||
|
declare -A domain_user
|
||||||
|
total_500s=0
|
||||||
|
filtered_bots=0
|
||||||
|
|
||||||
|
# Scan all domain access logs for 500 errors (including subdirectories)
|
||||||
|
while IFS= read -r log; do
|
||||||
|
[ -f "$log" ] || continue
|
||||||
|
[[ "$log" =~ (bytes_log|offset|error_log|ftpxferlog|-ssl_log)$ ]] && continue
|
||||||
|
|
||||||
|
# Extract domain from log filename
|
||||||
|
domain="${log##*/}"
|
||||||
|
domain="${domain%%-*}"
|
||||||
|
|
||||||
|
# Skip non-domain system logs (proxy, localhost, etc.)
|
||||||
|
[[ "$domain" =~ ^(proxy|localhost|default|cpanel|webmail|whm|cpcalendars|cpcontacts|webdisk)$ ]] && continue
|
||||||
|
|
||||||
|
# Find cPanel user for this domain
|
||||||
|
user=$(grep -l "DNS.*$domain" /var/cpanel/users/* 2>/dev/null | head -1 | xargs basename 2>/dev/null)
|
||||||
|
[ -z "$user" ] && user="unknown"
|
||||||
|
|
||||||
|
domain_user["$domain"]="$user"
|
||||||
|
|
||||||
|
# Scan for 500 errors in this log
|
||||||
|
while IFS= read -r line; do
|
||||||
|
# Check for 500 status code
|
||||||
|
if [[ "$line" =~ '"'[[:space:]](5[0-9]{2})[[:space:]] ]]; then
|
||||||
|
status="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
# Time filtering
|
||||||
|
if [ "$cutoff_time" != "0" ]; then
|
||||||
|
if [[ "$line" =~ \[([0-9]{2}/[A-Z][a-z]{2}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}) ]]; then
|
||||||
|
log_date="${BASH_REMATCH[1]}"
|
||||||
|
log_time=$(date -d "${log_date/:/ }" +%s 2>/dev/null || echo "0")
|
||||||
|
[ "$log_time" != "0" ] && [ "$log_time" -lt "$cutoff_time" ] && continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse log line to get IP first
|
||||||
|
read -r ip _ _ timestamp _ request _ _ <<< "$line"
|
||||||
|
|
||||||
|
# IP-based filtering - skip non-relevant IPs
|
||||||
|
# Skip localhost/internal IPs
|
||||||
|
if [[ "$ip" =~ ^(127\.|::1|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.) ]]; then
|
||||||
|
((filtered_bots++))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Skip common cloud scanner IPs and known bot networks
|
||||||
|
# AWS, GCP, Azure scanner ranges, security scanners
|
||||||
|
if [[ "$ip" =~ ^(3\.|13\.|18\.|34\.|35\.|52\.|54\.) ]] || \
|
||||||
|
[[ "$ip" =~ ^(104\.196\.|104\.154\.|130\.211\.|35\.184\.|35\.185\.|35\.186\.|35\.187\.|35\.188\.|35\.189\.|35\.19) ]] || \
|
||||||
|
[[ "$ip" =~ ^(20\.|40\.|51\.|52\.|13\.64\.|13\.65\.|13\.66\.|13\.67\.|13\.68\.) ]]; then
|
||||||
|
# Check if it's actually a bot by examining user agent
|
||||||
|
line_lower="${line,,}"
|
||||||
|
if [[ "$line_lower" =~ (bot|crawler|spider|scanner|monitor|cloud|amazon|google|microsoft|azure) ]]; then
|
||||||
|
((filtered_bots++))
|
||||||
|
# Track bot IP with BOT flag
|
||||||
|
flag_ip_attack "$ip" "BOT" 0 "500 error - filtered as bot/scanner" >/dev/null 2>&1 &
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Bot/Scanner filtering - skip noise
|
||||||
|
line_lower="${line,,}"
|
||||||
|
if [[ "$line_lower" =~ (bot|crawler|spider|scraper|scanner|check|monitor|uptime|pingdom|newrelic|datadog|nagios|zabbix|prtg|gomez|keynote|catchpoint|dotcom-monitor|site24x7|uptimerobot|statuscake|nodequery|hetrixtools|freshping|uptrendscom|siteuptime|montastic|updown\.io|apex|alertsite|webmon|wormly) ]]; then
|
||||||
|
((filtered_bots++))
|
||||||
|
# Track monitoring/uptime bot
|
||||||
|
flag_ip_attack "$ip" "BOT" 0 "Monitoring/uptime bot" >/dev/null 2>&1 &
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ "$line_lower" =~ (semrush|ahrefs|moz|majestic|serpstat|screaming|screamingfrog|sitebulb|linkchecker|validator|scanner|security|acunetix|nessus|openvas|burp|nikto|skipfish|w3af|sqlmap|metasploit|nmap|masscan|zmap|shodan|censys|binaryedge) ]]; then
|
||||||
|
((filtered_bots++))
|
||||||
|
# Track scanner with higher score
|
||||||
|
flag_ip_attack "$ip" "SCANNER" 0 "Security scanner detected" >/dev/null 2>&1 &
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ "$line_lower" =~ (curl|wget|python|perl|ruby|java|go-http|libwww|axios|node-fetch|http\.client|httpie|postman|insomnia|apachehttp|okhttp|httpclient) ]]; then
|
||||||
|
((filtered_bots++))
|
||||||
|
# Track programmatic access
|
||||||
|
flag_ip_attack "$ip" "BOT" 0 "HTTP library/tool" >/dev/null 2>&1 &
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract URL
|
||||||
|
if [[ "$request" =~ '"'[A-Z]+[[:space:]]([^[:space:]]+) ]]; then
|
||||||
|
url="${BASH_REMATCH[1]:0:80}"
|
||||||
|
else
|
||||||
|
url="/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract timestamp
|
||||||
|
if [[ "$line" =~ \[([^]]+)\] ]]; then
|
||||||
|
timestamp="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
timestamp="unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
((domain_count["$domain"]++))
|
||||||
|
((total_500s++))
|
||||||
|
|
||||||
|
# Track IP in reputation database (500 error = slight increase in score)
|
||||||
|
# Most 500s are due to server issues, not attacks, so low score increase
|
||||||
|
increment_ip_hits "$ip" 1 >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
# Save for analysis
|
||||||
|
echo "$domain|$user|$status|$url|$timestamp|$ip" >> "$ERRORS_500"
|
||||||
|
fi
|
||||||
|
done < <(tail -n 100000 "$log" 2>/dev/null)
|
||||||
|
done < <(find "$DOMLOGS_DIR" -type f ! -name "*bytes_log" ! -name "*offset*" ! -name "*error_log" ! -name "*ftpxferlog*" ! -name "*-ssl_log" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "$total_500s" -eq 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo -e "${GREEN} ✓ NO 500 ERRORS FOUND IN LAST $HOURS_TO_SCAN HOURS!${NC}"
|
||||||
|
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||||
|
echo ""
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 500 ERRORS DETECTED: $total_500s errors (filtered out $filtered_bots bot/scanner requests)"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${RED}${BOLD}TOP AFFECTED DOMAINS:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Sort and display top domains
|
||||||
|
for domain in "${!domain_count[@]}"; do
|
||||||
|
echo "${domain_count[$domain]} $domain ${domain_user[$domain]}"
|
||||||
|
done | sort -rn | head -10 | while read count domain user; do
|
||||||
|
printf " ${RED}●${NC} %-40s ${BOLD}%4d errors${NC} (user: %s)\n" "$domain" "$count" "$user"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " DIAGNOSING ROOT CAUSES"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Detailed diagnostic tracking - per URL/file
|
||||||
|
DETAILED_DIAGNOSIS="$TEMP_DIR/detailed_diagnosis.txt"
|
||||||
|
> "$DETAILED_DIAGNOSIS"
|
||||||
|
|
||||||
|
declare -A diagnosed_causes
|
||||||
|
declare -A cause_examples
|
||||||
|
|
||||||
|
total_to_diagnose=$(wc -l < "$ERRORS_500")
|
||||||
|
current_line=0
|
||||||
|
|
||||||
|
echo "Analyzing $total_to_diagnose errors for root causes..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
while IFS='|' read -r domain user status url timestamp ip; do
|
||||||
|
[ -z "$domain" ] && continue
|
||||||
|
|
||||||
|
((current_line++))
|
||||||
|
# Show progress every 500 errors
|
||||||
|
if [ $((current_line % 500)) -eq 0 ]; then
|
||||||
|
echo -ne "\rAnalyzed $current_line / $total_to_diagnose errors..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
diagnosis=""
|
||||||
|
cause="UNKNOWN"
|
||||||
|
specific_file=""
|
||||||
|
|
||||||
|
# Get document root
|
||||||
|
docroot="/home/$user/public_html"
|
||||||
|
|
||||||
|
# Try to determine the actual file being requested
|
||||||
|
if [[ "$url" =~ ^/([^?]+) ]]; then
|
||||||
|
url_path="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
# Handle common patterns
|
||||||
|
if [ -z "$url_path" ] || [ "$url_path" = "/" ]; then
|
||||||
|
possible_files=("$docroot/index.php" "$docroot/index.html")
|
||||||
|
elif [[ "$url_path" =~ \.php$ ]]; then
|
||||||
|
possible_files=("$docroot/$url_path")
|
||||||
|
else
|
||||||
|
possible_files=("$docroot/$url_path" "$docroot/${url_path}.php" "$docroot/$url_path/index.php")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the actual file
|
||||||
|
for pf in "${possible_files[@]}"; do
|
||||||
|
if [ -f "$pf" ]; then
|
||||||
|
specific_file="$pf"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find PHP error log - check multiple locations
|
||||||
|
error_log=""
|
||||||
|
if [ "$user" != "unknown" ]; then
|
||||||
|
for potential_log in \
|
||||||
|
"/home/$user/public_html/error_log" \
|
||||||
|
"/home/$user/logs/error_log" \
|
||||||
|
"/home/$user/error_log" \
|
||||||
|
"/var/log/apache2/domlogs/$domain-error_log" \
|
||||||
|
"/usr/local/apache/domlogs/$domain"; do
|
||||||
|
|
||||||
|
if [ -f "$potential_log" ]; then
|
||||||
|
error_log="$potential_log"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if error log exists and has recent errors
|
||||||
|
if [ -n "$error_log" ] && [ -f "$error_log" ]; then
|
||||||
|
# Look for errors matching this URL/timestamp
|
||||||
|
recent_error=$(tail -1000 "$error_log" | grep -F "$url" | tail -1)
|
||||||
|
|
||||||
|
# If no URL match, get most recent error
|
||||||
|
[ -z "$recent_error" ] && recent_error=$(tail -500 "$error_log" | grep -E "Fatal error|Parse error|syntax error|memory.*exhausted|database|MySQL|Permission denied|failed to open stream" | tail -1)
|
||||||
|
|
||||||
|
if [ -n "$recent_error" ]; then
|
||||||
|
# Extract file path from error if present
|
||||||
|
if [[ "$recent_error" =~ in[[:space:]](/[^:]+\.php) ]]; then
|
||||||
|
error_file="${BASH_REMATCH[1]}"
|
||||||
|
specific_file="$error_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine cause and diagnose
|
||||||
|
if [[ "$recent_error" =~ (memory.*exhausted|Allowed memory size) ]]; then
|
||||||
|
cause="PHP_MEMORY_EXHAUSTED"
|
||||||
|
|
||||||
|
# Check current memory limit
|
||||||
|
mem_limit=$(grep -h "memory_limit" "$docroot/.user.ini" "$docroot/../.php.ini" 2>/dev/null | tail -1 | cut -d'=' -f2 | tr -d ' ')
|
||||||
|
[ -z "$mem_limit" ] && mem_limit=$(php -r "echo ini_get('memory_limit');" 2>/dev/null)
|
||||||
|
|
||||||
|
diagnosis="$domain$url - Memory exhausted (current limit: ${mem_limit:-unknown})"
|
||||||
|
[ -n "$specific_file" ] && diagnosis="$diagnosis - File: $specific_file"
|
||||||
|
|
||||||
|
elif [[ "$recent_error" =~ (failed to open stream|Permission denied) ]]; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
|
||||||
|
# Extract the file with permission issue
|
||||||
|
if [[ "$recent_error" =~ failed\ to\ open\ stream.*\'([^\']+)\' ]]; then
|
||||||
|
perm_file="${BASH_REMATCH[1]}"
|
||||||
|
elif [[ "$recent_error" =~ Permission\ denied.*\'([^\']+)\' ]]; then
|
||||||
|
perm_file="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
perm_file="$specific_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check permissions on that specific file
|
||||||
|
if [ -n "$perm_file" ] && [ -e "$perm_file" ]; then
|
||||||
|
file_perms=$(stat -c "%a" "$perm_file" 2>/dev/null)
|
||||||
|
file_owner=$(stat -c "%U:%G" "$perm_file" 2>/dev/null)
|
||||||
|
diagnosis="$domain$url - Permission denied on: $perm_file (perms: $file_perms, owner: $file_owner)"
|
||||||
|
|
||||||
|
# Check if file is readable by Apache
|
||||||
|
if [ -f "$perm_file" ]; then
|
||||||
|
if ! sudo -u nobody test -r "$perm_file" 2>/dev/null; then
|
||||||
|
diagnosis="$diagnosis - File NOT readable by Apache (nobody user)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
diagnosis="$domain$url - Permission denied (file in error: $perm_file - does not exist)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "$recent_error" =~ (Fatal error|PHP Fatal error) ]]; then
|
||||||
|
if [[ "$recent_error" =~ (undefined function|Call to undefined function) ]]; then
|
||||||
|
cause="MISSING_PHP_FUNCTION"
|
||||||
|
|
||||||
|
# Extract function name
|
||||||
|
if [[ "$recent_error" =~ Call\ to\ undefined\ function\ ([a-zA-Z0-9_]+) ]]; then
|
||||||
|
missing_func="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
# Check which extension provides this function
|
||||||
|
ext_info=$(php -r "if(function_exists('$missing_func')) echo 'EXISTS'; else echo 'MISSING';" 2>&1)
|
||||||
|
|
||||||
|
diagnosis="$domain$url - Missing function: $missing_func"
|
||||||
|
[ -n "$specific_file" ] && diagnosis="$diagnosis - in file: $specific_file"
|
||||||
|
else
|
||||||
|
diagnosis="$domain$url - Missing PHP function - $recent_error"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cause="PHP_FATAL_ERROR"
|
||||||
|
diagnosis="$domain$url - PHP Fatal Error"
|
||||||
|
[ -n "$specific_file" ] && diagnosis="$diagnosis - File: $specific_file"
|
||||||
|
diagnosis="$diagnosis - ${recent_error:0:200}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "$recent_error" =~ (Parse error|syntax error) ]]; then
|
||||||
|
cause="PHP_SYNTAX_ERROR"
|
||||||
|
|
||||||
|
# Extract line number if present
|
||||||
|
if [[ "$recent_error" =~ line\ ([0-9]+) ]]; then
|
||||||
|
error_line="${BASH_REMATCH[1]}"
|
||||||
|
diagnosis="$domain$url - PHP Syntax Error at line $error_line"
|
||||||
|
else
|
||||||
|
diagnosis="$domain$url - PHP Syntax Error"
|
||||||
|
fi
|
||||||
|
[ -n "$specific_file" ] && diagnosis="$diagnosis - File: $specific_file"
|
||||||
|
|
||||||
|
elif [[ "$recent_error" =~ (database|MySQL|Can\'t connect|Access denied for user) ]]; then
|
||||||
|
cause="DATABASE_CONNECTION"
|
||||||
|
|
||||||
|
if [[ "$recent_error" =~ Access\ denied\ for\ user\ \'([^\']+)\' ]]; then
|
||||||
|
db_user="${BASH_REMATCH[1]}"
|
||||||
|
diagnosis="$domain$url - Database access denied for user: $db_user"
|
||||||
|
elif [[ "$recent_error" =~ Unknown\ database\ \'([^\']+)\' ]]; then
|
||||||
|
db_name="${BASH_REMATCH[1]}"
|
||||||
|
diagnosis="$domain$url - Unknown database: $db_name"
|
||||||
|
else
|
||||||
|
diagnosis="$domain$url - Database connection error"
|
||||||
|
fi
|
||||||
|
[ -n "$specific_file" ] && diagnosis="$diagnosis - from file: $specific_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save detailed diagnosis only if we identified a specific cause
|
||||||
|
if [ "$cause" != "UNKNOWN" ] && [ -n "$diagnosis" ]; then
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
else
|
||||||
|
# No matching error in error_log - run comprehensive checks
|
||||||
|
if [ "$user" != "unknown" ]; then
|
||||||
|
issue_found=""
|
||||||
|
|
||||||
|
# Check 1: .htaccess issues
|
||||||
|
if [ -f "$docroot/.htaccess" ]; then
|
||||||
|
htaccess_file="$docroot/.htaccess"
|
||||||
|
htaccess_issues=""
|
||||||
|
|
||||||
|
# Invalid PHP directives
|
||||||
|
if grep -qE "^[[:space:]]*(php_value|php_flag|php_admin_value|php_admin_flag)" "$htaccess_file" 2>/dev/null; then
|
||||||
|
if ! apache2ctl -M 2>/dev/null | grep -q "php.*module"; then
|
||||||
|
htaccess_issues="PHP directives incompatible with FPM"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Malformed RewriteRule
|
||||||
|
bad_rewrite=$(grep -nE "RewriteRule.*\[.*[^]]*$" "$htaccess_file" 2>/dev/null | head -1)
|
||||||
|
[ -n "$bad_rewrite" ] && htaccess_issues="${htaccess_issues:+$htaccess_issues; }Malformed RewriteRule: ${bad_rewrite:0:50}"
|
||||||
|
|
||||||
|
# Missing RewriteBase with RewriteRule
|
||||||
|
if grep -q "RewriteRule" "$htaccess_file" 2>/dev/null; then
|
||||||
|
if ! grep -q "RewriteBase" "$htaccess_file" 2>/dev/null; then
|
||||||
|
# Check if rules use relative paths
|
||||||
|
if grep -qE "RewriteRule.*\^[^/]" "$htaccess_file" 2>/dev/null; then
|
||||||
|
htaccess_issues="${htaccess_issues:+$htaccess_issues; }Missing RewriteBase with relative paths"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$htaccess_issues" ]; then
|
||||||
|
cause="HTACCESS_ERROR"
|
||||||
|
diagnosis="$domain$url - $htaccess_issues"
|
||||||
|
issue_found="yes"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check 2: PHP file exists and is readable
|
||||||
|
if [ -z "$issue_found" ] && [ -n "$specific_file" ]; then
|
||||||
|
if [ ! -f "$specific_file" ]; then
|
||||||
|
cause="FILE_NOT_FOUND"
|
||||||
|
diagnosis="$domain$url - File does not exist: $specific_file"
|
||||||
|
issue_found="yes"
|
||||||
|
elif [ ! -r "$specific_file" ]; then
|
||||||
|
file_perms=$(stat -c "%a" "$specific_file" 2>/dev/null)
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - File not readable: $specific_file (perms: $file_perms)"
|
||||||
|
issue_found="yes"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check 3: Document root permissions
|
||||||
|
if [ -z "$issue_found" ]; then
|
||||||
|
if [ ! -d "$docroot" ]; then
|
||||||
|
cause="DOCROOT_MISSING"
|
||||||
|
diagnosis="$domain$url - Document root does not exist: $docroot"
|
||||||
|
issue_found="yes"
|
||||||
|
else
|
||||||
|
docroot_perms=$(stat -c "%a" "$docroot" 2>/dev/null)
|
||||||
|
if [ "$docroot_perms" != "755" ] && [ "$docroot_perms" != "750" ] && [ "$docroot_perms" != "711" ]; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - Docroot bad perms: $docroot ($docroot_perms, should be 755)"
|
||||||
|
issue_found="yes"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check 4: PHP handler/version issues
|
||||||
|
if [ -z "$issue_found" ] && [ -n "$specific_file" ] && [ -f "$specific_file" ]; then
|
||||||
|
# Check if PHP is configured for this domain
|
||||||
|
php_handler=$(grep -i "phpversion\|php_admin_value" /var/cpanel/userdata/$user/$domain 2>/dev/null | head -1)
|
||||||
|
if [ -z "$php_handler" ]; then
|
||||||
|
# Check if .htaccess tries to set PHP version but fails
|
||||||
|
if grep -qE "AddHandler.*php|SetHandler.*php" "$docroot/.htaccess" 2>/dev/null; then
|
||||||
|
cause="PHP_HANDLER_ERROR"
|
||||||
|
diagnosis="$domain$url - PHP handler misconfigured (check .htaccess AddHandler/SetHandler)"
|
||||||
|
issue_found="yes"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check 5: WordPress-specific issues (if WP detected)
|
||||||
|
if [ -z "$issue_found" ] && [ -f "$docroot/wp-config.php" ]; then
|
||||||
|
# Check if wp-config.php is readable
|
||||||
|
if [ ! -r "$docroot/wp-config.php" ]; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - wp-config.php not readable"
|
||||||
|
issue_found="yes"
|
||||||
|
else
|
||||||
|
# Check for WP_DEBUG causing issues
|
||||||
|
if grep -q "define.*WP_DEBUG.*true" "$docroot/wp-config.php" 2>/dev/null; then
|
||||||
|
# Check if WP_DEBUG_DISPLAY is also true (can cause 500s with some errors)
|
||||||
|
if grep -q "define.*WP_DEBUG_DISPLAY.*true" "$docroot/wp-config.php" 2>/dev/null; then
|
||||||
|
cause="WP_DEBUG_ERROR"
|
||||||
|
diagnosis="$domain$url - WP_DEBUG_DISPLAY enabled (may cause 500 on warnings)"
|
||||||
|
issue_found="yes"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save diagnosis if we found an issue
|
||||||
|
if [ -n "$issue_found" ] && [ -n "$diagnosis" ]; then
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_PHP_ERROR_LOGGED"]++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_PHP_ERROR_LOGGED"]++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# No error in error_log at all - check .htaccess
|
||||||
|
if [ "$user" != "unknown" ] && [ -f "$docroot/.htaccess" ]; then
|
||||||
|
htaccess_file="$docroot/.htaccess"
|
||||||
|
htaccess_issues=""
|
||||||
|
|
||||||
|
# Check for invalid PHP directives
|
||||||
|
if grep -qE "^[[:space:]]*(php_value|php_flag|php_admin_value|php_admin_flag)" "$htaccess_file" 2>/dev/null; then
|
||||||
|
if ! apache2ctl -M 2>/dev/null | grep -q "php.*module"; then
|
||||||
|
htaccess_issues="PHP directives (php_value/php_flag) incompatible with current PHP handler (not mod_php)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for malformed RewriteRule
|
||||||
|
bad_rewrite=$(grep -nE "RewriteRule.*\[.*[^]]*$" "$htaccess_file" 2>/dev/null | head -1)
|
||||||
|
if [ -n "$bad_rewrite" ]; then
|
||||||
|
htaccess_issues="${htaccess_issues:+$htaccess_issues; }Malformed RewriteRule: $bad_rewrite"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$htaccess_issues" ]; then
|
||||||
|
cause="HTACCESS_ERROR"
|
||||||
|
diagnosis="$domain$url - .htaccess error: $htaccess_issues"
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_PHP_ERROR_LOGGED"]++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_PHP_ERROR_LOGGED"]++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# No error log found - check .htaccess and permissions thoroughly
|
||||||
|
if [ "$user" != "unknown" ]; then
|
||||||
|
htaccess_file="$docroot/.htaccess"
|
||||||
|
|
||||||
|
if [ -f "$htaccess_file" ]; then
|
||||||
|
# Check .htaccess file permissions first
|
||||||
|
htaccess_perms=$(stat -c "%a" "$htaccess_file" 2>/dev/null)
|
||||||
|
htaccess_owner=$(stat -c "%U:%G" "$htaccess_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Check if readable by Apache
|
||||||
|
htaccess_readable="yes"
|
||||||
|
if ! sudo -u nobody test -r "$htaccess_file" 2>/dev/null; then
|
||||||
|
htaccess_readable="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Actually validate .htaccess syntax
|
||||||
|
htaccess_test=$(apache2ctl -t 2>&1)
|
||||||
|
htaccess_issues=""
|
||||||
|
|
||||||
|
# Check for invalid directives
|
||||||
|
if grep -qE "^[[:space:]]*(php_value|php_flag|php_admin_value|php_admin_flag)" "$htaccess_file" 2>/dev/null; then
|
||||||
|
if ! apache2ctl -M 2>/dev/null | grep -q "php.*module"; then
|
||||||
|
htaccess_issues="PHP directives (php_value/php_flag) incompatible with current PHP handler (not mod_php)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for malformed RewriteRule
|
||||||
|
bad_rewrite=$(grep -nE "RewriteRule.*\[.*[^]]*$" "$htaccess_file" 2>/dev/null | head -1)
|
||||||
|
if [ -n "$bad_rewrite" ]; then
|
||||||
|
htaccess_issues="${htaccess_issues:+$htaccess_issues; }Malformed RewriteRule: $bad_rewrite"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for RewriteCond without following RewriteRule
|
||||||
|
orphan_cond=$(grep -n "RewriteCond" "$htaccess_file" | while read line; do
|
||||||
|
linenum=$(echo "$line" | cut -d: -f1)
|
||||||
|
nextline=$((linenum + 1))
|
||||||
|
next=$(sed -n "${nextline}p" "$htaccess_file")
|
||||||
|
if [[ ! "$next" =~ RewriteRule|RewriteCond ]]; then
|
||||||
|
echo "Line $linenum: RewriteCond without RewriteRule"
|
||||||
|
fi
|
||||||
|
done | head -1)
|
||||||
|
|
||||||
|
if [ -n "$orphan_cond" ]; then
|
||||||
|
htaccess_issues="${htaccess_issues:+$htaccess_issues; }$orphan_cond"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for syntax errors in .htaccess
|
||||||
|
if echo "$htaccess_test" | grep -qi "syntax error"; then
|
||||||
|
syntax_err=$(echo "$htaccess_test" | grep -i "syntax error" | head -1)
|
||||||
|
htaccess_issues="${htaccess_issues:+$htaccess_issues; }Apache syntax error: $syntax_err"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$htaccess_readable" = "no" ]; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - .htaccess not readable by Apache (perms: $htaccess_perms, owner: $htaccess_owner)"
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
elif [ -n "$htaccess_issues" ]; then
|
||||||
|
cause="HTACCESS_ERROR"
|
||||||
|
diagnosis="$domain$url - .htaccess error: $htaccess_issues"
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
else
|
||||||
|
# .htaccess appears valid - check document root and file permissions
|
||||||
|
docroot_perms=$(stat -c "%a" "$docroot" 2>/dev/null)
|
||||||
|
docroot_owner=$(stat -c "%U:%G" "$docroot" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "$docroot_perms" != "755" ] && [ "$docroot_perms" != "750" ]; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - Document root incorrect permissions (perms: $docroot_perms, owner: $docroot_owner, should be 755)"
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
elif [ -n "$specific_file" ] && [ -f "$specific_file" ]; then
|
||||||
|
# Check the specific PHP file permissions
|
||||||
|
file_perms=$(stat -c "%a" "$specific_file" 2>/dev/null)
|
||||||
|
file_owner=$(stat -c "%U:%G" "$specific_file" 2>/dev/null)
|
||||||
|
file_readable="yes"
|
||||||
|
|
||||||
|
if ! sudo -u nobody test -r "$specific_file" 2>/dev/null; then
|
||||||
|
file_readable="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$file_readable" = "no" ]; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - File not readable by Apache: $specific_file (perms: $file_perms, owner: $file_owner)"
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_PHP_ERROR_LOGGED"]++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_PHP_ERROR_LOGGED"]++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# No .htaccess - check document root and file permissions
|
||||||
|
docroot_perms=$(stat -c "%a" "$docroot" 2>/dev/null)
|
||||||
|
docroot_owner=$(stat -c "%U:%G" "$docroot" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "$docroot_perms" != "755" ] && [ "$docroot_perms" != "750" ]; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - Document root incorrect permissions (perms: $docroot_perms, owner: $docroot_owner, should be 755)"
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
elif [ -n "$specific_file" ] && [ -f "$specific_file" ]; then
|
||||||
|
# Check the specific PHP file
|
||||||
|
file_perms=$(stat -c "%a" "$specific_file" 2>/dev/null)
|
||||||
|
file_owner=$(stat -c "%U:%G" "$specific_file" 2>/dev/null)
|
||||||
|
|
||||||
|
if ! sudo -u nobody test -r "$specific_file" 2>/dev/null; then
|
||||||
|
cause="PERMISSION_ERROR"
|
||||||
|
diagnosis="$domain$url - File not readable: $specific_file (perms: $file_perms, owner: $file_owner)"
|
||||||
|
echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS"
|
||||||
|
((diagnosed_causes["$cause"]++))
|
||||||
|
[ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis"
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_ERROR_LOG_FILE"]++))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_ERROR_LOG_FILE"]++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
((diagnosed_causes["NO_ERROR_LOG_FILE"]++))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < "$ERRORS_500"
|
||||||
|
|
||||||
|
echo -e "\rAnalyzed $total_to_diagnose / $total_to_diagnose errors - Complete! "
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Display diagnosed causes
|
||||||
|
echo -e "${CYAN}${BOLD}ROOT CAUSES IDENTIFIED:${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for cause in "${!diagnosed_causes[@]}"; do
|
||||||
|
count="${diagnosed_causes[$cause]}"
|
||||||
|
echo "$count|$cause"
|
||||||
|
done | sort -rn | while IFS='|' read count cause; do
|
||||||
|
clean_cause=$(echo "$cause" | tr '_' ' ')
|
||||||
|
|
||||||
|
case "$cause" in
|
||||||
|
PHP_MEMORY_EXHAUSTED)
|
||||||
|
echo -e "${RED}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Increase memory_limit in /home/USER/public_html/.user.ini"
|
||||||
|
echo -e " ${YELLOW}Set:${NC} memory_limit = 512M"
|
||||||
|
;;
|
||||||
|
PHP_FATAL_ERROR)
|
||||||
|
echo -e "${RED}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Review recent code/plugin changes"
|
||||||
|
;;
|
||||||
|
PHP_SYNTAX_ERROR)
|
||||||
|
echo -e "${RED}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Check for PHP syntax errors in recent file edits"
|
||||||
|
;;
|
||||||
|
MISSING_PHP_FUNCTION)
|
||||||
|
echo -e "${RED}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Install missing PHP extension (check error for which one)"
|
||||||
|
;;
|
||||||
|
DATABASE_CONNECTION)
|
||||||
|
echo -e "${RED}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Check database credentials or MySQL service"
|
||||||
|
;;
|
||||||
|
HTACCESS_ERROR)
|
||||||
|
echo -e "${RED}.HTACCESS ERROR DETECTED${NC} - $count occurrences"
|
||||||
|
;;
|
||||||
|
PERMISSION_ERROR)
|
||||||
|
echo -e "${RED}PERMISSION ERROR DETECTED${NC} - $count occurrences"
|
||||||
|
;;
|
||||||
|
NO_ERROR_LOG_FILE)
|
||||||
|
echo -e "${YELLOW}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Note:${NC} Checked multiple error_log locations - none found"
|
||||||
|
echo -e " ${YELLOW}Check:${NC} May need to enable PHP error logging"
|
||||||
|
;;
|
||||||
|
NO_PHP_ERROR_LOGGED)
|
||||||
|
echo -e "${YELLOW}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Note:${NC} 500 error but no PHP error in log - likely .htaccess or Apache config"
|
||||||
|
;;
|
||||||
|
FILE_NOT_FOUND)
|
||||||
|
echo -e "${RED}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Requested file does not exist - check URL or restore missing files"
|
||||||
|
;;
|
||||||
|
PHP_HANDLER_ERROR)
|
||||||
|
echo -e "${RED}PHP HANDLER ERROR${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} PHP handler misconfigured - check cPanel PHP version or .htaccess AddHandler"
|
||||||
|
;;
|
||||||
|
WP_DEBUG_ERROR)
|
||||||
|
echo -e "${YELLOW}WORDPRESS DEBUG ERROR${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Disable WP_DEBUG_DISPLAY in wp-config.php or fix underlying warnings"
|
||||||
|
;;
|
||||||
|
DOCROOT_MISSING)
|
||||||
|
echo -e "${RED}$clean_cause${NC} - $count occurrences"
|
||||||
|
echo -e " ${YELLOW}Fix:${NC} Document root directory missing - restore from backup or check domain configuration"
|
||||||
|
;;
|
||||||
|
UNKNOWN)
|
||||||
|
# Skip - these are errors we couldn't diagnose
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${INFO_COLOR}$clean_cause${NC} - $count occurrences"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Show example if we have one
|
||||||
|
if [ -n "${cause_examples[$cause]}" ]; then
|
||||||
|
example="${cause_examples[$cause]}"
|
||||||
|
echo -e " ${DIM}Example: ${example:0:120}...${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " DETAILED 500 ERROR LIST"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show most frequent URLs getting 500s
|
||||||
|
echo "Most Frequent 500 Error URLs:"
|
||||||
|
echo ""
|
||||||
|
cut -d'|' -f1,4 "$ERRORS_500" | sort | uniq -c | sort -rn | head -15 | while read count domain_url; do
|
||||||
|
domain=$(echo "$domain_url" | cut -d'|' -f1)
|
||||||
|
url=$(echo "$domain_url" | cut -d'|' -f2)
|
||||||
|
user="${domain_user[$domain]}"
|
||||||
|
printf " ${RED}%4d×${NC} %s%s ${DIM}(user: %s)${NC}\n" "$count" "$domain" "$url" "$user"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " SPECIFIC DIAGNOSTICS (per URL/file)"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show detailed diagnostics grouped by cause and issue pattern
|
||||||
|
if [ -f "$DETAILED_DIAGNOSIS" ] && [ -s "$DETAILED_DIAGNOSIS" ]; then
|
||||||
|
for cause_type in PHP_MEMORY_EXHAUSTED PERMISSION_ERROR HTACCESS_ERROR PHP_FATAL_ERROR PHP_SYNTAX_ERROR MISSING_PHP_FUNCTION DATABASE_CONNECTION FILE_NOT_FOUND PHP_HANDLER_ERROR WP_DEBUG_ERROR DOCROOT_MISSING; do
|
||||||
|
|
||||||
|
cause_count=$(grep "^${cause_type}|" "$DETAILED_DIAGNOSIS" 2>/dev/null | wc -l)
|
||||||
|
cause_count=${cause_count//[^0-9]/} # Remove any non-numeric characters
|
||||||
|
cause_count=${cause_count:-0} # Default to 0 if empty
|
||||||
|
|
||||||
|
if [ "$cause_count" -gt 0 ]; then
|
||||||
|
cause_display=$(echo "$cause_type" | tr '_' ' ')
|
||||||
|
echo -e "${RED}${BOLD}$cause_display ($cause_count occurrences)${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Group by unique error pattern (not domain)
|
||||||
|
declare -A issue_domains
|
||||||
|
|
||||||
|
# First pass: collect all domains per issue pattern
|
||||||
|
declare -A pattern_domains_temp
|
||||||
|
|
||||||
|
while IFS='|' read -r ctype full_diag; do
|
||||||
|
# Extract just the error part (after domain/)
|
||||||
|
issue_pattern=$(echo "$full_diag" | sed 's/^[^ ]* - //')
|
||||||
|
domain_part=$(echo "$full_diag" | grep -oP '^[^/]+')
|
||||||
|
|
||||||
|
# Append to temporary storage
|
||||||
|
pattern_domains_temp[$issue_pattern]+="$domain_part"$'\n'
|
||||||
|
done < <(grep "^${cause_type}|" "$DETAILED_DIAGNOSIS" 2>/dev/null)
|
||||||
|
|
||||||
|
# Second pass: deduplicate and limit to 5 unique domains per pattern
|
||||||
|
for pattern in "${!pattern_domains_temp[@]}"; do
|
||||||
|
# Get unique domains, limit to 5
|
||||||
|
unique_list=$(echo "${pattern_domains_temp[$pattern]}" | sort -u | head -5 | tr '\n' ',')
|
||||||
|
# Remove trailing comma
|
||||||
|
unique_list=${unique_list%,}
|
||||||
|
|
||||||
|
# Count total unique domains
|
||||||
|
total_unique=$(echo "${pattern_domains_temp[$pattern]}" | sort -u | wc -l)
|
||||||
|
|
||||||
|
# Add "..." if there are more than 5
|
||||||
|
if [ "$total_unique" -gt 5 ]; then
|
||||||
|
unique_list="$unique_list,..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
issue_domains[$pattern]="$unique_list"
|
||||||
|
done
|
||||||
|
|
||||||
|
unset pattern_domains_temp
|
||||||
|
|
||||||
|
# Display grouped issues
|
||||||
|
shown=0
|
||||||
|
for pattern in "${!issue_domains[@]}"; do
|
||||||
|
[ $shown -ge 10 ] && break
|
||||||
|
((shown++))
|
||||||
|
|
||||||
|
domains="${issue_domains[$pattern]}"
|
||||||
|
domain_count=$(echo "$domains" | tr ',' '\n' | grep -v '^\.\.\.$' | wc -l)
|
||||||
|
|
||||||
|
echo -e " ${YELLOW}Issue:${NC} $pattern"
|
||||||
|
echo -e " ${DIM}Affected ($domain_count):${NC} ${domains//,/, }"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#issue_domains[@]}" -gt 10 ]; then
|
||||||
|
remaining=$((${#issue_domains[@]} - 10))
|
||||||
|
echo -e " ${DIM}... and $remaining more issue patterns${NC}"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset issue_domains
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "No detailed diagnostics available."
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Full error list saved to: $ERRORS_500"
|
||||||
|
echo "Detailed diagnostics saved to: $DETAILED_DIAGNOSIS"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
press_enter
|
||||||
@@ -12,6 +12,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|||||||
source "$SCRIPT_DIR/lib/common-functions.sh"
|
source "$SCRIPT_DIR/lib/common-functions.sh"
|
||||||
source "$SCRIPT_DIR/lib/system-detect.sh"
|
source "$SCRIPT_DIR/lib/system-detect.sh"
|
||||||
source "$SCRIPT_DIR/lib/user-manager.sh"
|
source "$SCRIPT_DIR/lib/user-manager.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/ip-reputation.sh"
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
APACHE_ERROR_LOG="/var/log/apache2/error_log"
|
APACHE_ERROR_LOG="/var/log/apache2/error_log"
|
||||||
@@ -190,127 +191,200 @@ echo ""
|
|||||||
|
|
||||||
is_noise() {
|
is_noise() {
|
||||||
local line="$1"
|
local line="$1"
|
||||||
|
local line_lower="${line,,}" # Convert to lowercase once
|
||||||
|
|
||||||
# Bot/Scanner patterns
|
# Bot/Scanner patterns
|
||||||
if echo "$line" | grep -qiE "bot|crawler|spider|scanner|nikto|nmap|masscan|sqlmap|nessus|acunetix|burp|shodan|censys|zgrab|nuclei|semrush|ahrefs|mj12"; then
|
[[ "$line_lower" =~ (bot|crawler|spider|scanner|nikto|nmap|masscan|sqlmap|nessus|acunetix|burp|shodan|censys|zgrab|nuclei|semrush|ahrefs|mj12) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Known scanner IPs
|
# Known scanner IPs
|
||||||
if echo "$line" | grep -qE "45\.148\.|185\.220\.|89\.248\.165\."; then
|
[[ "$line" =~ (45\.148\.|185\.220\.|89\.248\.165\.) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# WordPress plugin deprecation warnings (not user-facing)
|
# WordPress plugin deprecation warnings (not user-facing)
|
||||||
if echo "$line" | grep -qiE "PHP Deprecated|Deprecated.*plugin|call_user_func_array.*deprecated"; then
|
[[ "$line" =~ (PHP\ Deprecated|Deprecated.*plugin|call_user_func_array.*deprecated) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Non-critical PHP notices
|
# Non-critical PHP notices (unless critical indicators present)
|
||||||
if echo "$line" | grep -qiE "PHP Notice.*Undefined (variable|index|offset)" && \
|
if [[ "$line" =~ PHP\ Notice.*Undefined\ (variable|index|offset) ]] && ! [[ "$line_lower" =~ (fatal|error\ 500|white\ screen) ]]; then
|
||||||
! echo "$line" | grep -qiE "fatal|error 500|white screen"; then
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Missing favicon/common assets (not real errors)
|
# Missing favicon/common assets (not real errors)
|
||||||
if echo "$line" | grep -qiE "favicon\.ico|apple-touch-icon|robots\.txt|sitemap\.xml.*404"; then
|
[[ "$line_lower" =~ (favicon\.ico|apple-touch-icon|robots\.txt|sitemap\.xml.*404) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Security probes
|
# Security probes
|
||||||
if echo "$line" | grep -qiE "wp-admin|wp-login|phpMyAdmin|phpmyadmin|\.env|\.git|config\.php|xmlrpc|eval-stdin|shell\.php|c99\.php|r57\.php|adminer\.php" && \
|
if [[ "$line_lower" =~ (wp-admin|wp-login|phpmyadmin|\.env|\.git|config\.php|xmlrpc|eval-stdin|shell\.php|c99\.php|r57\.php|adminer\.php) ]] && \
|
||||||
echo "$line" | grep -qiE "404|403|not found"; then
|
[[ "$line" =~ (404|403|not\ found) ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# WordPress auto-updates and cron (normal operations)
|
# WordPress auto-updates and cron (normal operations)
|
||||||
if echo "$line" | grep -qiE "wp-cron\.php|doing_cron|auto.*update"; then
|
[[ "$line_lower" =~ (wp-cron\.php|doing_cron|auto.*update) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Common plugin update checks (not errors)
|
# Common plugin update checks (not errors)
|
||||||
if echo "$line" | grep -qiE "update-check|version-check|api\.wordpress\.org"; then
|
[[ "$line_lower" =~ (update-check|version-check|api\.wordpress\.org) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
is_critical_user_facing() {
|
is_critical_user_facing() {
|
||||||
local line="$1"
|
local line="$1"
|
||||||
|
local line_lower="${line,,}"
|
||||||
|
|
||||||
# 500 Internal Server Error (users see white page)
|
# 500 Internal Server Error (users see white page)
|
||||||
if echo "$line" | grep -qiE " 500 |Internal Server Error"; then
|
[[ "$line" =~ (\ 500\ |Internal\ Server\ Error) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# PHP Fatal Errors (breaks functionality)
|
# PHP Fatal Errors (breaks functionality)
|
||||||
if echo "$line" | grep -qiE "PHP Fatal error|Fatal error:.*in /"; then
|
[[ "$line" =~ (PHP\ Fatal\ error|Fatal\ error:.*in\ /) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# PHP Parse Errors (site completely broken)
|
# PHP Parse Errors (site completely broken)
|
||||||
if echo "$line" | grep -qiE "PHP Parse error|syntax error"; then
|
[[ "$line" =~ (PHP\ Parse\ error|syntax\ error) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Database connection failures (site down)
|
# Database connection failures (site down)
|
||||||
if echo "$line" | grep -qiE "Error establishing.*database|Can't connect.*MySQL|Access denied for user.*database|Too many connections|MySQL server has gone away"; then
|
[[ "$line" =~ (Error\ establishing.*database|Can\'t\ connect.*MySQL|Access\ denied\ for\ user.*database|Too\ many\ connections|MySQL\ server\ has\ gone\ away) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Memory exhaustion (white screen/incomplete pages)
|
# Memory exhaustion (white screen/incomplete pages)
|
||||||
if echo "$line" | grep -qiE "Allowed memory size.*exhausted|Out of memory|Fatal.*memory"; then
|
[[ "$line" =~ (Allowed\ memory\ size.*exhausted|Out\ of\ memory|Fatal.*memory) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Segmentation fault (complete crash)
|
# Segmentation fault (complete crash)
|
||||||
if echo "$line" | grep -qiE "Segmentation fault|signal 11"; then
|
[[ "$line" =~ (Segmentation\ fault|signal\ 11) ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Permission denied on critical files
|
# Permission denied on critical files
|
||||||
if echo "$line" | grep -qiE "Permission denied" && \
|
if [[ "$line" =~ Permission\ denied ]] && [[ "$line" =~ (index\.php|wp-config\.php|\.htaccess|config\.php) ]]; then
|
||||||
echo "$line" | grep -qE "index\.php|wp-config\.php|\.htaccess|config\.php"; then
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# File not found for MAIN page (404 on homepage/index)
|
# File not found for MAIN page (404 on homepage/index)
|
||||||
if echo "$line" | grep -qiE "File does not exist.*index\.(php|html)" || \
|
[[ "$line" =~ (File\ does\ not\ exist.*index\.(php|html)|\ 404\ .*\"\ \"GET\ /) ]] && return 0
|
||||||
echo "$line" | grep -qE " 404 .*\" \"GET / "; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_useful_info() {
|
extract_useful_info() {
|
||||||
local line="$1"
|
local line="$1"
|
||||||
|
local domain="unknown"
|
||||||
|
local file_path=""
|
||||||
|
local error_msg
|
||||||
|
|
||||||
# Extract domain
|
# Extract domain using bash regex (faster than grep|sed pipeline)
|
||||||
domain=$(echo "$line" | grep -oE '\[vhost [^:]+' | sed 's/\[vhost //' || \
|
if [[ "$line" =~ \[vhost\ ([^:]+) ]]; then
|
||||||
echo "$line" | grep -oE '[a-zA-Z0-9.-]+\.(com|net|org|io|co|uk|us|dev)' | head -1 || \
|
domain="${BASH_REMATCH[1]}"
|
||||||
echo "$line" | grep -oE '/home/[^/]+' | sed 's|/home/||' || echo "unknown")
|
elif [[ "$line" =~ ([a-zA-Z0-9.-]+\.(com|net|org|io|co|uk|us|dev)) ]]; then
|
||||||
|
domain="${BASH_REMATCH[1]}"
|
||||||
# Extract file path if PHP error
|
elif [[ "$line" =~ /home/([^/]+) ]]; then
|
||||||
file_path=$(echo "$line" | grep -oE "in /[^ ]+\.php" | sed 's/in //' || echo "")
|
domain="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
# Extract error message (clean up ModSec noise, timestamps, etc.)
|
|
||||||
error_msg=$(echo "$line" | \
|
|
||||||
sed 's/^\[.*\] //' | \
|
|
||||||
sed 's/\[client [^]]*\] //' | \
|
|
||||||
sed 's/\[unique_id "[^"]*"\]//g' | \
|
|
||||||
sed 's/\[pid [^]]*\]//g' | \
|
|
||||||
sed 's/\[tid [^]]*\]//g' | \
|
|
||||||
grep -v "^$" | \
|
|
||||||
cut -c1-150)
|
|
||||||
|
|
||||||
# Skip if error message is empty or just whitespace
|
|
||||||
if [ -z "$(echo "$error_msg" | tr -d '[:space:]')" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$domain|$file_path|$error_msg"
|
# Extract file path if PHP error
|
||||||
|
if [[ "$line" =~ in\ (/[^ ]+\.php) ]]; then
|
||||||
|
file_path="${BASH_REMATCH[1]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract error message (clean up ModSec noise, timestamps, etc.)
|
||||||
|
# Use single sed command instead of pipeline
|
||||||
|
error_msg=$(echo "$line" | sed -E 's/^\[.*\] //; s/\[client [^]]*\] //; s/\[unique_id "[^"]*"\]//g; s/\[pid [^]]*\]//g; s/\[tid [^]]*\]//g' | cut -c1-150)
|
||||||
|
|
||||||
|
# Skip if error message is empty or just whitespace
|
||||||
|
error_msg="${error_msg#"${error_msg%%[![:space:]]*}"}" # ltrim
|
||||||
|
error_msg="${error_msg%"${error_msg##*[![:space:]]}"}" # rtrim
|
||||||
|
[ -z "$error_msg" ] && return 1
|
||||||
|
|
||||||
|
# Correlate to root cause
|
||||||
|
local root_cause=$(correlate_root_cause "$line" "$error_msg" "$domain")
|
||||||
|
|
||||||
|
echo "$domain|$file_path|$error_msg|$root_cause"
|
||||||
|
}
|
||||||
|
|
||||||
|
correlate_root_cause() {
|
||||||
|
local line="$1"
|
||||||
|
local error_msg="$2"
|
||||||
|
local domain="$3"
|
||||||
|
local cause="UNKNOWN"
|
||||||
|
local line_lower="${line,,}"
|
||||||
|
local error_lower="${error_msg,,}"
|
||||||
|
|
||||||
|
# .htaccess issues
|
||||||
|
if [[ "$line_lower" =~ (\.htaccess|invalid\ command|rewriterule|rewritecond) ]]; then
|
||||||
|
cause="HTACCESS"
|
||||||
|
|
||||||
|
# ModSecurity blocks
|
||||||
|
elif [[ "$line_lower" =~ (modsecurity|mod_security|waf) ]]; then
|
||||||
|
cause="MODSECURITY"
|
||||||
|
|
||||||
|
# PHP memory limit
|
||||||
|
elif [[ "$error_lower" =~ (memory.*exhausted|allowed\ memory\ size) ]]; then
|
||||||
|
# Get current memory limit for this domain/user
|
||||||
|
if [ -n "$domain" ] && [ "$domain" != "unknown" ]; then
|
||||||
|
user=$(grep -l "DNS.*$domain" /var/cpanel/users/* 2>/dev/null | head -1 | xargs basename 2>/dev/null)
|
||||||
|
if [ -n "$user" ]; then
|
||||||
|
mem_limit=$(grep -h "memory_limit" /home/$user/public_html/.user.ini /home/$user/.php.ini 2>/dev/null | tail -1 | cut -d'=' -f2 | tr -d ' ')
|
||||||
|
[ -n "$mem_limit" ] && cause="PHP_MEMORY:$mem_limit" || cause="PHP_MEMORY:default"
|
||||||
|
else
|
||||||
|
cause="PHP_MEMORY"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cause="PHP_MEMORY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PHP max execution time
|
||||||
|
elif [[ "$error_lower" =~ (max_execution_time|maximum\ execution\ time) ]]; then
|
||||||
|
cause="PHP_TIMEOUT"
|
||||||
|
|
||||||
|
# PHP upload/post size limits
|
||||||
|
elif [[ "$error_lower" =~ (upload_max_filesize|post_max_size) ]]; then
|
||||||
|
cause="PHP_UPLOAD_LIMIT"
|
||||||
|
|
||||||
|
# File permissions
|
||||||
|
elif [[ "$error_lower" =~ (permission\ denied|failed\ to\ open\ stream.*permission) ]]; then
|
||||||
|
cause="FILE_PERMISSIONS"
|
||||||
|
|
||||||
|
# Missing PHP modules/extensions
|
||||||
|
elif [[ "$error_lower" =~ (undefined\ function|call\ to\ undefined\ function|class\ .*\ not\ found) ]]; then
|
||||||
|
# Try to identify which module
|
||||||
|
if [[ "$error_lower" =~ (imagecreate|imagefilter|gd_) ]]; then
|
||||||
|
cause="MISSING_PHP_GD"
|
||||||
|
elif [[ "$error_msg" =~ (curl_|CURL) ]]; then
|
||||||
|
cause="MISSING_PHP_CURL"
|
||||||
|
elif [[ "$error_msg" =~ (json_|JSON) ]]; then
|
||||||
|
cause="MISSING_PHP_JSON"
|
||||||
|
elif [[ "$error_lower" =~ (mysqli|mysql_connect) ]]; then
|
||||||
|
cause="MISSING_PHP_MYSQLI"
|
||||||
|
else
|
||||||
|
cause="MISSING_PHP_MODULE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Database issues
|
||||||
|
elif [[ "$error_lower" =~ (database|mysql|mysqli) ]]; then
|
||||||
|
if [[ "$error_lower" =~ (too\ many\ connections|max_connections) ]]; then
|
||||||
|
cause="DB_MAX_CONNECTIONS"
|
||||||
|
elif [[ "$error_lower" =~ (access\ denied|authentication) ]]; then
|
||||||
|
cause="DB_AUTH_FAILED"
|
||||||
|
elif [[ "$error_lower" =~ (gone\ away|lost\ connection) ]]; then
|
||||||
|
cause="DB_TIMEOUT"
|
||||||
|
else
|
||||||
|
cause="DB_ERROR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Apache configuration
|
||||||
|
elif [[ "$error_lower" =~ (invalid\ uri|request\ exceeded\ the\ limit|limitrequestline) ]]; then
|
||||||
|
cause="APACHE_CONFIG"
|
||||||
|
|
||||||
|
# PHP parse/syntax errors (code issues)
|
||||||
|
elif [[ "$error_lower" =~ (parse\ error|syntax\ error|unexpected) ]]; then
|
||||||
|
cause="PHP_SYNTAX_ERROR"
|
||||||
|
|
||||||
|
# File not found (may indicate bad symlinks or missing files)
|
||||||
|
elif [[ "$error_lower" =~ (no\ such\ file|failed\ to\ open\ stream) ]]; then
|
||||||
|
cause="MISSING_FILE"
|
||||||
|
|
||||||
|
# Generic PHP fatal error
|
||||||
|
elif [[ "$error_lower" =~ fatal\ error ]]; then
|
||||||
|
cause="PHP_FATAL_ERROR"
|
||||||
|
|
||||||
|
# 500 errors without specific cause
|
||||||
|
elif [[ "$error_msg" =~ (500|Internal\ Server) ]]; then
|
||||||
|
cause="SERVER_ERROR_500"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$cause"
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -356,41 +430,93 @@ while IFS='|' read -r log_path log_type; do
|
|||||||
uri=$(echo "$line" | grep -oE "uri: [^ ]+" | sed 's/uri: //' || echo "")
|
uri=$(echo "$line" | grep -oE "uri: [^ ]+" | sed 's/uri: //' || echo "")
|
||||||
domain=$(echo "$line" | grep -oE "hostname: [^ ]+" | sed 's/hostname: //' || echo "unknown")
|
domain=$(echo "$line" | grep -oE "hostname: [^ ]+" | sed 's/hostname: //' || echo "unknown")
|
||||||
|
|
||||||
echo "$domain||ModSecurity blocked: $attack_type $uri" >> "$CRITICAL_ERRORS"
|
# Extract rule ID if available
|
||||||
|
rule_id=$(echo "$line" | grep -oE "id \"[0-9]+\"" | grep -oE "[0-9]+" || echo "")
|
||||||
|
root_cause="MODSECURITY"
|
||||||
|
[ -n "$rule_id" ] && root_cause="MODSECURITY:rule_$rule_id"
|
||||||
|
|
||||||
|
echo "$domain||ModSecurity blocked: $attack_type $uri|$root_cause" >> "$CRITICAL_ERRORS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done < <(tail -n 5000 "$log_path" 2>/dev/null)
|
done < <(tail -n 5000 "$log_path" 2>/dev/null)
|
||||||
|
|
||||||
elif $is_access_log; then
|
elif $is_access_log; then
|
||||||
# Access log - look for 5xx status codes
|
# Access log - look for 5xx status codes
|
||||||
|
# Use time-based filtering if possible, otherwise last 50k lines
|
||||||
|
cutoff_time=$(date -d "$HOURS_TO_ANALYZE hours ago" +%s 2>/dev/null || echo "0")
|
||||||
|
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
((total_lines++))
|
((total_lines++))
|
||||||
|
|
||||||
# Skip if bot/scanner
|
# Skip if bot/scanner
|
||||||
if is_noise "$line"; then
|
if is_noise "$line"; then
|
||||||
((filtered_out++))
|
((filtered_out++))
|
||||||
|
# Track bot/scanner IPs
|
||||||
|
read -r ip _ _ _ _ _ _ _ <<< "$line"
|
||||||
|
if [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||||
|
flag_ip_attack "$ip" "BOT" 0 "Bot/scanner filtered in error analysis" >/dev/null 2>&1 &
|
||||||
|
fi
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract status code and URL
|
# Time filtering (Apache format: [DD/Mon/YYYY:HH:MM:SS +ZONE])
|
||||||
if echo "$line" | grep -qE '" 5[0-9]{2} '; then
|
if [ "$cutoff_time" != "0" ]; then
|
||||||
status=$(echo "$line" | grep -oE '" 5[0-9]{2} ' | tr -d '" ')
|
if [[ "$line" =~ \[([0-9]{2}/[A-Z][a-z]{2}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}) ]]; then
|
||||||
url=$(echo "$line" | awk '{print $7}' | cut -c1-80)
|
log_date="${BASH_REMATCH[1]}"
|
||||||
ip=$(echo "$line" | awk '{print $1}')
|
log_time=$(date -d "${log_date/:/ }" +%s 2>/dev/null || echo "0")
|
||||||
domain=$(basename "$log_path" | sed 's/-.*//')
|
[ "$log_time" != "0" ] && [ "$log_time" -lt "$cutoff_time" ] && continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract status code and URL using bash regex and read
|
||||||
|
if [[ "$line" =~ '"'[[:space:]](5[0-9]{2})[[:space:]] ]]; then
|
||||||
|
status="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
# Parse Apache log format: IP - - [timestamp] "METHOD URL PROTOCOL" STATUS SIZE
|
||||||
|
read -r ip _ _ timestamp _ request status_check _ <<< "$line"
|
||||||
|
|
||||||
|
# Extract URL from request (format: "GET /path HTTP/1.1")
|
||||||
|
if [[ "$request" =~ '"'[A-Z]+[[:space:]]([^[:space:]]+) ]]; then
|
||||||
|
url="${BASH_REMATCH[1]:0:80}"
|
||||||
|
else
|
||||||
|
url="/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract timestamp
|
||||||
|
if [[ "$line" =~ \[([^]]+)\] ]]; then
|
||||||
|
timestamp="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
timestamp=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get domain from log filename
|
||||||
|
domain="${log_path##*/}" # basename
|
||||||
|
domain="${domain%%-*}" # remove everything after first dash
|
||||||
|
|
||||||
# Apply domain filter if set
|
# Apply domain filter if set
|
||||||
if [ -n "$FILTER_DOMAIN" ] && [ "$domain" != "$FILTER_DOMAIN" ]; then
|
if [ -n "$FILTER_DOMAIN" ] && [ "$domain" != "$FILTER_DOMAIN" ]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Determine root cause from status code
|
||||||
|
case $status in
|
||||||
|
500) root_cause="SERVER_ERROR_500" ;;
|
||||||
|
502) root_cause="APACHE_CONFIG:bad_gateway" ;;
|
||||||
|
503) root_cause="APACHE_CONFIG:service_unavailable" ;;
|
||||||
|
504) root_cause="APACHE_CONFIG:gateway_timeout" ;;
|
||||||
|
*) root_cause="SERVER_ERROR_5XX" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
((critical_found++))
|
((critical_found++))
|
||||||
echo "$domain||HTTP $status - $url (from $ip)" >> "$CRITICAL_ERRORS"
|
echo "$domain||[$timestamp] HTTP $status - $url (from $ip)|$root_cause" >> "$CRITICAL_ERRORS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done < <(tail -n 5000 "$log_path" 2>/dev/null)
|
done < <(tail -n 50000 "$log_path" 2>/dev/null)
|
||||||
else
|
else
|
||||||
# Error log - look for critical errors
|
# Error log - look for critical errors
|
||||||
|
# Use time-based filtering if possible, otherwise last 50k lines
|
||||||
|
cutoff_time=$(date -d "$HOURS_TO_ANALYZE hours ago" +%s 2>/dev/null || echo "0")
|
||||||
|
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
((total_lines++))
|
((total_lines++))
|
||||||
|
|
||||||
@@ -400,12 +526,21 @@ while IFS='|' read -r log_path log_type; do
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Time filtering (Apache/PHP error log format: [Day Mon DD HH:MM:SS YYYY])
|
||||||
|
if [ "$cutoff_time" != "0" ]; then
|
||||||
|
if [[ "$line" =~ \[([A-Z][a-z]{2}\ [A-Z][a-z]{2}\ [0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}\ [0-9]{4})\] ]]; then
|
||||||
|
log_date="${BASH_REMATCH[1]}"
|
||||||
|
log_time=$(date -d "$log_date" +%s 2>/dev/null || echo "0")
|
||||||
|
[ "$log_time" != "0" ] && [ "$log_time" -lt "$cutoff_time" ] && continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Apply user/domain filter if set
|
# Apply user/domain filter if set
|
||||||
if [ -n "$FILTER_USER" ]; then
|
if [ -n "$FILTER_USER" ]; then
|
||||||
echo "$line" | grep -q "/home/$FILTER_USER" || continue
|
[[ "$line" =~ /home/$FILTER_USER ]] || continue
|
||||||
fi
|
fi
|
||||||
if [ -n "$FILTER_DOMAIN" ]; then
|
if [ -n "$FILTER_DOMAIN" ]; then
|
||||||
echo "$line" | grep -q "$FILTER_DOMAIN" || continue
|
[[ "$line" =~ $FILTER_DOMAIN ]] || continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if it's critical and user-facing
|
# Check if it's critical and user-facing
|
||||||
@@ -414,7 +549,7 @@ while IFS='|' read -r log_path log_type; do
|
|||||||
extract_useful_info "$line" >> "$CRITICAL_ERRORS"
|
extract_useful_info "$line" >> "$CRITICAL_ERRORS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done < <(tail -n 10000 "$log_path" 2>/dev/null)
|
done < <(tail -n 50000 "$log_path" 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done < "$LOG_FILES_LIST"
|
done < "$LOG_FILES_LIST"
|
||||||
@@ -462,8 +597,8 @@ echo ""
|
|||||||
|
|
||||||
# Group identical errors and count them
|
# Group identical errors and count them
|
||||||
awk -F'|' '{
|
awk -F'|' '{
|
||||||
key = $1 "|" $3 # domain|error_msg (skip file_path for grouping)
|
key = $1 "|" $3 "|" $4 # domain|error_msg|root_cause
|
||||||
file[$1"|"$3] = $2 # Store file path
|
file[$1"|"$3"|"$4] = $2 # Store file path
|
||||||
count[key]++
|
count[key]++
|
||||||
}
|
}
|
||||||
END {
|
END {
|
||||||
@@ -471,14 +606,15 @@ END {
|
|||||||
split(key, parts, "|")
|
split(key, parts, "|")
|
||||||
domain = parts[1]
|
domain = parts[1]
|
||||||
error = parts[2]
|
error = parts[2]
|
||||||
|
root_cause = parts[3]
|
||||||
file_path = file[key]
|
file_path = file[key]
|
||||||
|
|
||||||
# Skip empty errors
|
# Skip empty errors
|
||||||
if (length(error) == 0) next
|
if (length(error) > 0) {
|
||||||
|
print count[key] "|" domain "|" file_path "|" error "|" root_cause
|
||||||
print count[key] "|" domain "|" file_path "|" error
|
}
|
||||||
}
|
}
|
||||||
}' "$CRITICAL_ERRORS" | sort -t'|' -k1 -rn | head -20 | while IFS='|' read -r count domain file_path error_msg; do
|
}' "$CRITICAL_ERRORS" | sort -t'|' -k1 -rn | head -20 | while IFS='|' read -r count domain file_path error_msg root_cause; do
|
||||||
|
|
||||||
# Color code by frequency
|
# Color code by frequency
|
||||||
if [ "$count" -ge 10 ]; then
|
if [ "$count" -ge 10 ]; then
|
||||||
@@ -496,14 +632,150 @@ END {
|
|||||||
[ -n "$domain" ] && [ "$domain" != "unknown" ] && echo " Domain: $domain"
|
[ -n "$domain" ] && [ "$domain" != "unknown" ] && echo " Domain: $domain"
|
||||||
[ -n "$file_path" ] && echo " File: $file_path"
|
[ -n "$file_path" ] && echo " File: $file_path"
|
||||||
echo " Error: $error_msg"
|
echo " Error: $error_msg"
|
||||||
|
|
||||||
|
# Display root cause with color coding and actionable fix
|
||||||
|
if [ -n "$root_cause" ] && [ "$root_cause" != "UNKNOWN" ]; then
|
||||||
|
echo -ne " ${CYAN}Root Cause: ${BOLD}"
|
||||||
|
|
||||||
|
case "$root_cause" in
|
||||||
|
HTACCESS)
|
||||||
|
echo -e "${YELLOW}⚙️ .htaccess Configuration${NC}"
|
||||||
|
echo " → Check .htaccess syntax in domain root"
|
||||||
|
echo " → Look for invalid RewriteRule or directives"
|
||||||
|
;;
|
||||||
|
MODSECURITY*)
|
||||||
|
rule=$(echo "$root_cause" | cut -d':' -f2)
|
||||||
|
echo -e "${YELLOW}🛡️ ModSecurity WAF Block${NC}"
|
||||||
|
[ -n "$rule" ] && echo " → Rule ID: $rule"
|
||||||
|
echo " → Check if this is a false positive"
|
||||||
|
echo " → Review: /usr/local/apache/logs/modsec_audit.log"
|
||||||
|
;;
|
||||||
|
PHP_MEMORY*)
|
||||||
|
limit=$(echo "$root_cause" | cut -d':' -f2)
|
||||||
|
echo -e "${RED}💾 PHP Memory Exhausted${NC}"
|
||||||
|
[ -n "$limit" ] && echo " → Current limit: $limit"
|
||||||
|
echo " → Increase memory_limit in .user.ini or php.ini"
|
||||||
|
echo " → Recommended: 256M minimum, 512M for WooCommerce"
|
||||||
|
;;
|
||||||
|
PHP_TIMEOUT)
|
||||||
|
echo -e "${YELLOW}⏱️ PHP Execution Timeout${NC}"
|
||||||
|
echo " → Increase max_execution_time in php.ini"
|
||||||
|
echo " → Recommended: 300 for imports/backups"
|
||||||
|
;;
|
||||||
|
PHP_UPLOAD_LIMIT)
|
||||||
|
echo -e "${YELLOW}📤 PHP Upload Size Limit${NC}"
|
||||||
|
echo " → Increase upload_max_filesize and post_max_size"
|
||||||
|
echo " → Match both values (e.g., 64M)"
|
||||||
|
;;
|
||||||
|
FILE_PERMISSIONS)
|
||||||
|
echo -e "${RED}🔒 File Permission Denied${NC}"
|
||||||
|
echo " → Check file ownership and permissions"
|
||||||
|
echo " → Files should be 644, directories 755"
|
||||||
|
echo " → Owner should match cPanel user"
|
||||||
|
;;
|
||||||
|
MISSING_PHP_GD)
|
||||||
|
echo -e "${RED}📦 Missing PHP GD Extension${NC}"
|
||||||
|
echo " → Install: yum install ea-phpXX-php-gd"
|
||||||
|
echo " → Required for image processing"
|
||||||
|
;;
|
||||||
|
MISSING_PHP_CURL)
|
||||||
|
echo -e "${RED}📦 Missing PHP cURL Extension${NC}"
|
||||||
|
echo " → Install: yum install ea-phpXX-php-curl"
|
||||||
|
;;
|
||||||
|
MISSING_PHP_*)
|
||||||
|
module=$(echo "$root_cause" | sed 's/MISSING_PHP_//' | tr '[:upper:]' '[:lower:]')
|
||||||
|
echo -e "${RED}📦 Missing PHP Extension: $module${NC}"
|
||||||
|
echo " → Install: yum install ea-phpXX-php-$module"
|
||||||
|
;;
|
||||||
|
DB_MAX_CONNECTIONS)
|
||||||
|
echo -e "${RED}🗄️ Database Max Connections Reached${NC}"
|
||||||
|
echo " → Check: mysql -e 'SHOW VARIABLES LIKE \"max_connections\"'"
|
||||||
|
echo " → Increase max_connections in my.cnf"
|
||||||
|
echo " → Or optimize slow queries reducing connection time"
|
||||||
|
;;
|
||||||
|
DB_AUTH_FAILED)
|
||||||
|
echo -e "${RED}🗄️ Database Authentication Failed${NC}"
|
||||||
|
echo " → Verify credentials in wp-config.php / config files"
|
||||||
|
echo " → Check database user permissions"
|
||||||
|
;;
|
||||||
|
DB_TIMEOUT)
|
||||||
|
echo -e "${YELLOW}🗄️ Database Connection Timeout${NC}"
|
||||||
|
echo " → MySQL server may be overloaded"
|
||||||
|
echo " → Check slow query log"
|
||||||
|
;;
|
||||||
|
DB_ERROR)
|
||||||
|
echo -e "${RED}🗄️ Database Error${NC}"
|
||||||
|
echo " → Check MySQL error log for details"
|
||||||
|
;;
|
||||||
|
APACHE_CONFIG*)
|
||||||
|
issue=$(echo "$root_cause" | cut -d':' -f2)
|
||||||
|
echo -e "${YELLOW}⚙️ Apache Configuration${NC}"
|
||||||
|
[ -n "$issue" ] && echo " → Issue: $issue"
|
||||||
|
echo " → Check Apache config and vhost settings"
|
||||||
|
;;
|
||||||
|
PHP_SYNTAX_ERROR)
|
||||||
|
echo -e "${RED}⚠️ PHP Syntax Error in Code${NC}"
|
||||||
|
echo " → Review recent code changes"
|
||||||
|
echo " → Check for missing semicolons, brackets, quotes"
|
||||||
|
;;
|
||||||
|
MISSING_FILE)
|
||||||
|
echo -e "${YELLOW}📄 File Not Found${NC}"
|
||||||
|
echo " → File may have been deleted or moved"
|
||||||
|
echo " → Check for broken symlinks"
|
||||||
|
;;
|
||||||
|
PHP_FATAL_ERROR)
|
||||||
|
echo -e "${RED}⚠️ PHP Fatal Error${NC}"
|
||||||
|
echo " → Review PHP error logs for details"
|
||||||
|
echo " → May be compatibility issue or code bug"
|
||||||
|
;;
|
||||||
|
SERVER_ERROR_500)
|
||||||
|
echo -e "${RED}🔥 Generic 500 Internal Server Error${NC}"
|
||||||
|
echo " → Check PHP/Apache error logs for specifics"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${NC}$root_cause"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Root Cause Summary
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 📊 ROOT CAUSE BREAKDOWN"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Count errors by root cause
|
||||||
|
cut -d'|' -f4 "$CRITICAL_ERRORS" | grep -v "^$" | sort | uniq -c | sort -rn | while read -r count cause; do
|
||||||
|
# Clean up cause display
|
||||||
|
clean_cause=$(echo "$cause" | sed 's/:.*//; s/_/ /g')
|
||||||
|
|
||||||
|
# Color code by severity
|
||||||
|
case "$cause" in
|
||||||
|
PHP_MEMORY*|DB_MAX_CONNECTIONS|PHP_FATAL_ERROR|SERVER_ERROR_500)
|
||||||
|
color="${RED}"; icon="🔥" ;;
|
||||||
|
HTACCESS|MODSECURITY*|PHP_TIMEOUT|DB_*)
|
||||||
|
color="${YELLOW}"; icon="⚠️ " ;;
|
||||||
|
MISSING_PHP*|FILE_PERMISSIONS)
|
||||||
|
color="${YELLOW}"; icon="📦" ;;
|
||||||
|
*)
|
||||||
|
color="${INFO_COLOR}"; icon="•" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf " ${color}${icon} %-35s %4d errors${NC}\n" "$clean_cause" "$count"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Intelligent Recommendations
|
# Intelligent Recommendations
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
echo " 🔧 RECOMMENDED ACTIONS"
|
echo " 🔧 RECOMMENDED ACTIONS"
|
||||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|||||||
Reference in New Issue
Block a user