Files
HomeAudit/deploy_audit.sh
2025-08-24 11:13:39 -04:00

484 lines
15 KiB
Bash
Executable File

#!/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'
<!DOCTYPE html>
<html>
<head>
<title>Linux System Audit Dashboard</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; margin: 0; background-color: #f0f2f5; }
.header { background-color: #fff; padding: 20px; border-bottom: 1px solid #ddd; text-align: center; }
.header h1 { margin: 0; }
.container { display: flex; flex-wrap: wrap; padding: 20px; justify-content: center; }
.card { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin: 10px; padding: 20px; width: 300px; }
.card h2 { margin-top: 0; color: #333; border-bottom: 1px solid #eee; padding-bottom: 10px; }
.card p { color: #666; }
.card .label { font-weight: bold; color: #333; }
.status-ok { color: #28a745; }
.status-warning { color: #ffc107; }
.status-error { color: #dc3545; }
</style>
</head>
<body>
<div class="header">
<h1>Linux System Audit Dashboard</h1>
<p id="generation-date"></p>
</div>
<div id="hosts-container" class="container">
<!-- Host information will be inserted here -->
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('generation-date').textContent = `Generated: ${new Date().toLocaleString()}`;
fetch('all_results.json')
.then(response => response.json())
.then(data => {
const container = document.getElementById('hosts-container');
data.forEach(host => {
const card = document.createElement('div');
card.className = 'card';
let ufwStatusClass = 'status-ok';
if (host.security.ufw_status !== 'active') {
ufwStatusClass = 'status-warning';
}
let rootLoginClass = 'status-ok';
if (host.security.ssh_root_login.toLowerCase() !== 'no') {
rootLoginClass = 'status-error';
}
card.innerHTML = `
<h2>${host.system.hostname}</h2>
<p><span class="label">OS:</span> ${host.system.os}</p>
<p><span class="label">Kernel:</span> ${host.system.kernel}</p>
<p><span class="label">IP Address:</span> ${host.system.ip_addresses}</p>
<p><span class="label">Uptime:</span> ${host.system.uptime}</p>
<p><span class="label">Running Containers:</span> ${host.containers.running_containers}</p>
<p><span class="label">UFW Status:</span> <span class="${ufwStatusClass}">${host.security.ufw_status}</span></p>
<p><span class="label">SSH Root Login:</span> <span class="${rootLoginClass}">${host.security.ssh_root_login}</span></p>
<p><span class="label">Open Ports:</span> ${host.security.open_ports.join(', ')}</p>
`;
container.appendChild(card);
});
})
.catch(error => {
console.error('Error loading audit data:', error);
const container = document.getElementById('hosts-container');
container.innerHTML = '<p style="color: red;">Could not load audit data. Make sure all_results.json is available.</p>';
});
});
</script>
</body>
</html>
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 "$@"