#!/bin/bash set -euo pipefail # =============================================================================== # Linux System Audit Deployment Script # This script helps deploy and run the audit across multiple devices # Version: 2.0 - Enhanced with better error handling and security features # =============================================================================== # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" INVENTORY_FILE="$SCRIPT_DIR/inventory.ini" PLAYBOOK_FILE="$SCRIPT_DIR/linux_audit_playbook.yml" AUDIT_SCRIPT="$SCRIPT_DIR/linux_system_audit.sh" RESULTS_DIR="$SCRIPT_DIR/audit_results" # Enhanced error handling error_handler() { local exit_code=$1 local line_no=$2 local last_command=$3 echo -e "${RED}Error occurred in: $last_command${NC}" echo -e "${RED}Exit code: $exit_code${NC}" echo -e "${RED}Line number: $line_no${NC}" exit $exit_code } # Set up error trap trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' print_status() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } check_dependencies() { print_status "Checking dependencies..." local missing_deps=() local optional_deps=() # Required dependencies if ! command -v ansible >/dev/null 2>&1; then missing_deps+=("ansible") fi if ! command -v ansible-playbook >/dev/null 2>&1; then missing_deps+=("ansible-playbook") fi # Optional but recommended dependencies if ! command -v jq >/dev/null 2>&1; then optional_deps+=("jq") fi if ! command -v tar >/dev/null 2>&1; then optional_deps+=("tar") fi if [ ${#missing_deps[@]} -ne 0 ]; then print_error "Missing required dependencies: ${missing_deps[*]}" echo "Please install Ansible:" echo " Ubuntu/Debian: sudo apt update && sudo apt install ansible" echo " Fedora: sudo dnf install ansible" echo " Or via pip: pip3 install ansible" exit 1 fi if [ ${#optional_deps[@]} -ne 0 ]; then print_warning "Missing optional dependencies: ${optional_deps[*]}" echo "These are recommended but not required:" echo " jq: For enhanced JSON processing" echo " tar: For result compression" fi print_success "All required dependencies found" # Check Ansible version local ansible_version=$(ansible --version | head -1 | awk '{print $2}') print_status "Ansible version: $ansible_version" } check_files() { print_status "Checking required files..." local missing_files=() if [ ! -f "$INVENTORY_FILE" ]; then missing_files+=("$INVENTORY_FILE") fi if [ ! -f "$PLAYBOOK_FILE" ]; then missing_files+=("$PLAYBOOK_FILE") fi if [ ! -f "$AUDIT_SCRIPT" ]; then missing_files+=("$AUDIT_SCRIPT") fi if [ ${#missing_files[@]} -ne 0 ]; then print_error "Missing files: ${missing_files[*]}" exit 1 fi print_success "All required files found" } test_connectivity() { print_status "Testing connectivity to hosts..." # Test connectivity with detailed output local connectivity_test=$(ansible all -i "$INVENTORY_FILE" -m ping --one-line 2>&1) local success_count=$(echo "$connectivity_test" | grep -c "SUCCESS" || echo "0") local total_hosts=$(ansible all -i "$INVENTORY_FILE" --list-hosts 2>/dev/null | grep -v "hosts" | wc -l) print_status "Connectivity test results: $success_count/$total_hosts hosts reachable" if [ "$success_count" -eq "$total_hosts" ]; then print_success "Successfully connected to all hosts" elif [ "$success_count" -gt 0 ]; then print_warning "Connected to $success_count/$total_hosts hosts" echo "Some hosts may not be reachable. Check your inventory file and SSH keys." echo "Failed connections:" echo "$connectivity_test" | grep -v "SUCCESS" || true read -p "Continue with available hosts? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi else print_error "No hosts are reachable. Please check:" echo " 1. SSH key authentication is set up" echo " 2. Inventory file is correct" echo " 3. Hosts are online and accessible" echo " 4. Firewall rules allow SSH connections" exit 1 fi } run_audit() { print_status "Starting audit across all hosts..." # Create results directory mkdir -p "$RESULTS_DIR" # Run the playbook if ansible-playbook -i "$INVENTORY_FILE" "$PLAYBOOK_FILE" -v; then print_success "Audit completed successfully" else print_error "Audit failed. Check the output above for details." exit 1 fi } generate_summary_report() { print_status "Generating summary report..." local summary_file="$RESULTS_DIR/MASTER_SUMMARY_$(date +%Y%m%d_%H%M%S).txt" cat > "$summary_file" << EOF =============================================================================== COMPREHENSIVE LINUX SYSTEM AUDIT SUMMARY REPORT Generated: $(date) Script Version: 1.0 =============================================================================== OVERVIEW: --------- EOF # Count hosts local total_hosts=$(find "$RESULTS_DIR" -maxdepth 1 -type d | grep -v "^$RESULTS_DIR$" | wc -l) echo "Total hosts audited: $total_hosts" >> "$summary_file" echo "" >> "$summary_file" # Process each host for host_dir in "$RESULTS_DIR"/*; do if [ -d "$host_dir" ] && [ "$(basename "$host_dir")" != "audit_results" ]; then local hostname=$(basename "$host_dir") echo "=== $hostname ===" >> "$summary_file" if [ -f "$host_dir/SUMMARY.txt" ]; then cat "$host_dir/SUMMARY.txt" >> "$summary_file" else echo "No summary file found" >> "$summary_file" fi echo "" >> "$summary_file" fi done print_success "Summary report generated: $summary_file" } create_dashboard() { print_status "Creating dynamic HTML dashboard..." local dashboard_file="$RESULTS_DIR/dashboard.html" local all_results_file="$RESULTS_DIR/all_results.json" # Aggregate all JSON results into a single file echo "[" > "$all_results_file" first=true for host_dir in "$RESULTS_DIR"/*; do if [ -d "$host_dir" ]; then local json_file="$host_dir/results.json" if [ -f "$json_file" ]; then if [ "$first" = false ]; then echo "," >> "$all_results_file" fi cat "$json_file" >> "$all_results_file" first=false fi fi done echo "]" >> "$all_results_file" cat > "$dashboard_file" << 'EOF' Linux System Audit Dashboard

Linux System Audit Dashboard

EOF print_success "HTML dashboard created: $dashboard_file" } cleanup_old_results() { if [ -d "$RESULTS_DIR" ]; then print_status "Cleaning up old results..." rm -rf "$RESULTS_DIR"/* print_success "Old results cleaned" fi } show_help() { cat << EOF Linux System Audit Deployment Script Usage: $0 [OPTIONS] OPTIONS: -h, --help Show this help message -c, --check Check dependencies and connectivity only -n, --no-cleanup Don't cleanup old results -q, --quick Skip connectivity test --inventory FILE Use custom inventory file --results-dir DIR Use custom results directory --setup-sudo Set up passwordless sudo on all hosts in inventory EXAMPLES: $0 Run full audit $0 -c Check setup only $0 --setup-sudo Set up passwordless sudo $0 --inventory /path/to/custom/inventory.ini Before running: 1. Edit inventory.ini with your server details 2. Ensure SSH key authentication is set up 3. Verify sudo access on target hosts EOF } setup_passwordless_sudo() { print_status "Setting up passwordless sudo for all hosts in inventory..." if [ ! -f "$INVENTORY_FILE" ]; then print_error "Inventory file not found at $INVENTORY_FILE" exit 1 fi # Extract user and host from inventory, ignore comments and empty lines mapfile -t hosts < <(grep -vE '^\s*(#|$|\[)' "$INVENTORY_FILE" | grep 'ansible_host' | awk '{print $1, $2}' | sed 's/ansible_host=//') if [ ${#hosts[@]} -eq 0 ]; then print_error "No hosts found in inventory file." exit 1 fi for host_info in "${hosts[@]}"; do read -r alias host <<< "$host_info" user_host=$(awk -v alias="$alias" '$1 == alias {print $3}' "$INVENTORY_FILE" | sed 's/ansible_user=//') if [[ -z "$user_host" ]]; then # Fallback for different inventory formats user_host=$(grep "$host" "$INVENTORY_FILE" | grep "ansible_user" | sed 's/.*ansible_user=\([^ ]*\).*/\1@'${host}'/') user_host=${user_host%@*} fi if [[ -z "$user_host" ]]; then print_warning "Could not determine user for host $alias. Skipping." continue fi user=$(echo "$user_host" | cut -d'@' -f1) print_status "Configuring $alias ($user@$host)..." if [ "$user" = "root" ]; then print_success "Skipping $alias - already root user" continue fi # Create sudoers file for the user if ssh "$user@$host" "echo '$user ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/audit-$user && sudo chmod 440 /etc/sudoers.d/audit-$user"; then print_success "Successfully configured passwordless sudo for $alias" else print_error "Failed to configure passwordless sudo for $alias" fi done print_success "Passwordless sudo setup complete!" } main() { local cleanup=true local check_only=false local quick=false local setup_sudo=false # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_help exit 0 ;; -c|--check) check_only=true shift ;; -n|--no-cleanup) cleanup=false shift ;; -q|--quick) quick=true shift ;; --inventory) INVENTORY_FILE="$2" shift 2 ;; --results-dir) RESULTS_DIR="$2" shift 2 ;; --setup-sudo) setup_sudo=true shift ;; *) print_error "Unknown option: $1" show_help exit 1 ;; esac done if [ "$setup_sudo" = true ]; then setup_passwordless_sudo exit 0 fi print_status "Starting Linux System Audit Deployment" check_dependencies check_files if [ "$check_only" = true ]; then test_connectivity print_success "All checks passed!" exit 0 fi if [ "$cleanup" = true ]; then cleanup_old_results fi if [ "$quick" = false ]; then test_connectivity fi run_audit generate_summary_report create_dashboard print_success "Audit deployment completed!" echo "" echo "Results available in: $RESULTS_DIR" echo "Open dashboard.html in your browser for a visual overview" } # Run main function with all arguments main "$@"