609 lines
18 KiB
Bash
609 lines
18 KiB
Bash
#!/bin/bash
|
|
# Security Audit Script for SAW
|
|
# Version: 1.0
|
|
# Date: 2026-04-02
|
|
#
|
|
# This script performs a comprehensive security audit of the Fedora Kinoite SAW system.
|
|
# It checks all security controls and provides a detailed report of the system's
|
|
# security posture.
|
|
#
|
|
# Usage: sudo ./security-audit.sh
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Regular security audits are essential for:
|
|
# 1. Verifying security controls are still in place
|
|
# 2. Detecting unauthorized changes
|
|
# 3. Identifying potential security issues
|
|
# 4. Providing compliance documentation
|
|
# 5. Tracking security posture over time
|
|
#
|
|
# This script should be run:
|
|
# - Weekly for critical systems
|
|
# - Monthly for standard systems
|
|
# - After any system changes
|
|
# - Before and after security updates
|
|
#
|
|
# Reference: NSA Linux Security Hardening Guide, CIS Fedora Benchmarks
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
LOG_FILE="/var/log/security-audit.log"
|
|
AUDIT_DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
|
AUDIT_REPORT="/var/log/security-audit-report-$(date +%Y%m%d).txt"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
# Print functions
|
|
log_info() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${GREEN}[$timestamp]${NC} ${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_warning() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${YELLOW}[$timestamp]${NC} ${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_error() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${RED}[$timestamp]${NC} ${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Check if running as root
|
|
check_root() {
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Check sudo access
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Sudo access should be disabled in SAW because:
|
|
# 1. Prevents privilege escalation attacks
|
|
# 2. Reduces attack surface
|
|
# 3. Enforces least privilege principle
|
|
# 4. Makes lateral movement harder
|
|
# 5. All admin operations should go through controlled workflows
|
|
#
|
|
# This check verifies:
|
|
# - User is not in wheel group
|
|
# - Root SSH login is disabled
|
|
# - No sudo commands available
|
|
check_sudo_access() {
|
|
echo -e "${CYAN}1. Sudo Access Check${NC}"
|
|
echo "=========================================="
|
|
|
|
local user=$(whoami)
|
|
|
|
# Check if user is in wheel group
|
|
if id -nG "$user" | grep -q wheel; then
|
|
log_error "FAIL: User '$user' is in wheel group"
|
|
echo " Status: SUDO ENABLED (Security Risk)"
|
|
return 1
|
|
else
|
|
log_info "PASS: User '$user' is not in wheel group"
|
|
echo " Status: SUDO DISABLED"
|
|
fi
|
|
|
|
# Check root SSH login
|
|
if [ -f /etc/ssh/sshd_config ]; then
|
|
if grep -q "^PermitRootLogin yes" /etc/ssh/sshd_config || \
|
|
grep -q "^#PermitRootLogin yes" /etc/ssh/sshd_config; then
|
|
log_warning "WARNING: Root SSH login may be enabled"
|
|
echo " Check: /etc/ssh/sshd_config"
|
|
else
|
|
log_info "PASS: Root SSH login is disabled"
|
|
echo " Status: ROOT SSH DISABLED"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check SELinux
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# SELinux (Security-Enhanced Linux) provides:
|
|
# 1. Mandatory Access Control (MAC)
|
|
# 2. Protection against privilege escalation
|
|
# 3. Application containment
|
|
# 4. Fine-grained access control
|
|
# 5. Defense in depth
|
|
#
|
|
# SELinux should be in enforcing mode to provide protection.
|
|
check_selinux() {
|
|
echo -e "${CYAN}2. SELinux Check${NC}"
|
|
echo "=========================================="
|
|
|
|
local selinux_status=$(sestatus 2>/dev/null || echo "SELinux not available")
|
|
|
|
if echo "$selinux_status" | grep -q "Current mode: enforcing"; then
|
|
log_info "PASS: SELinux is in enforcing mode"
|
|
echo " Status: ENFORCING"
|
|
echo " Mode: Mandatory Access Control active"
|
|
else
|
|
log_warning "WARNING: SELinux is not in enforcing mode"
|
|
echo " Status: $(echo "$selinux_status" | grep "Current mode" | awk '{print $3}')"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check audit daemon
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# auditd (audit daemon) provides:
|
|
# 1. Comprehensive system auditing
|
|
# 2. Logging of security-relevant events
|
|
# 3. Detection of unauthorized access
|
|
# 4. Forensic capabilities
|
|
# 5. Compliance with security standards
|
|
#
|
|
# auditd should be running to ensure security events are logged.
|
|
check_auditd() {
|
|
echo -e "${CYAN}3. Audit Daemon Check${NC}"
|
|
echo "=========================================="
|
|
|
|
if systemctl is-active auditd >/dev/null 2>&1; then
|
|
log_info "PASS: Auditd is running"
|
|
echo " Status: RUNNING"
|
|
echo " Logging: All security events"
|
|
else
|
|
log_warning "WARNING: Auditd is not running"
|
|
echo " Status: NOT RUNNING"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check firewall
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# The firewall provides:
|
|
# 1. Network traffic filtering
|
|
# 2. Egress filtering (outbound)
|
|
# 3. Ingress filtering (inbound)
|
|
# 4. Network segmentation
|
|
# 5. Protection against network attacks
|
|
#
|
|
# The firewall should be:
|
|
# - Active
|
|
# - Configured with default deny
|
|
# - Only allowing necessary traffic
|
|
check_firewall() {
|
|
echo -e "${CYAN}4. Firewall Check${NC}"
|
|
echo "=========================================="
|
|
|
|
if command -v firewall-cmd &> /dev/null; then
|
|
if firewall-cmd --state 2>/dev/null | grep -q "running"; then
|
|
log_info "PASS: Firewall is running"
|
|
echo " Status: RUNNING"
|
|
|
|
# Show active zones
|
|
echo " Active Zones:"
|
|
firewall-cmd --list-zones 2>/dev/null | sed 's/^/ /'
|
|
|
|
# Show services
|
|
echo " Active Services:"
|
|
firewall-cmd --list-services 2>/dev/null | sed 's/^/ /'
|
|
else
|
|
log_warning "WARNING: Firewall is not running"
|
|
echo " Status: NOT RUNNING"
|
|
fi
|
|
else
|
|
log_warning "WARNING: Firewall command not found"
|
|
echo " Status: COMMAND NOT FOUND"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check VPN
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# WireGuard VPN provides:
|
|
# 1. Encrypted network traffic
|
|
# 2. Protection against network eavesdropping
|
|
# 3. Traffic routing through secure tunnel
|
|
# 4. Kill-switch protection
|
|
# 5. Centralized network control
|
|
#
|
|
# VPN should be:
|
|
# - Active and connected
|
|
# - Routing all traffic
|
|
# - Using strong encryption
|
|
check_vpn() {
|
|
echo -e "${CYAN}5. VPN Check${NC}"
|
|
echo "=========================================="
|
|
|
|
if command -v wg &> /dev/null; then
|
|
if wg show >/dev/null 2>&1; then
|
|
log_info "PASS: WireGuard is active"
|
|
echo " Status: ACTIVE"
|
|
echo " Interface: $(wg show | grep 'interface:' | awk '{print $2}')"
|
|
|
|
# Show peers
|
|
echo " Peers:"
|
|
wg show 2>/dev/null | grep 'peer:' | sed 's/^/ /'
|
|
|
|
# Show allowed IPs
|
|
echo " Allowed IPs:"
|
|
wg show 2>/dev/null | grep 'allowed-ips:' | sed 's/^/ /'
|
|
else
|
|
log_warning "WARNING: WireGuard is not active"
|
|
echo " Status: NOT ACTIVE"
|
|
fi
|
|
else
|
|
log_warning "WARNING: WireGuard not installed"
|
|
echo " Status: NOT INSTALLED"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check DNS configuration
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# DNS lockdown provides:
|
|
# 1. Prevention of DNS leaks
|
|
# 2. All DNS through VPN tunnel
|
|
# 3. Protection against DNS tracking
|
|
# 4. Centralized DNS logging
|
|
# 5. Block malicious domains
|
|
#
|
|
# DNS should be:
|
|
# - Configured to use VPN gateway only
|
|
# - Not allowing external DNS
|
|
# - Using secure DNS resolution
|
|
check_dns() {
|
|
echo -e "${CYAN}6. DNS Configuration Check${NC}"
|
|
echo "=========================================="
|
|
|
|
if [ -f /etc/resolv.conf ]; then
|
|
local dns_servers=$(grep "^nameserver" /etc/resolv.conf | awk '{print $2}')
|
|
|
|
if echo "$dns_servers" | grep -q "127.0.0.1"; then
|
|
log_info "PASS: DNS configured to use local resolver"
|
|
echo " Status: LOCAL DNS"
|
|
echo " Servers: $dns_servers"
|
|
elif echo "$dns_servers" | grep -q "10.0.0.1"; then
|
|
log_info "PASS: DNS configured to use VPN gateway"
|
|
echo " Status: VPN DNS"
|
|
echo " Servers: $dns_servers"
|
|
else
|
|
log_warning "WARNING: DNS may not be properly configured"
|
|
echo " Status: EXTERNAL DNS"
|
|
echo " Servers: $dns_servers"
|
|
fi
|
|
else
|
|
log_warning "WARNING: /etc/resolv.conf not found"
|
|
echo " Status: FILE NOT FOUND"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check package verification
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Package verification ensures:
|
|
# 1. Only signed packages installed
|
|
# 2. Package integrity maintained
|
|
# 3. Authentic package source
|
|
# 4. Protection against tampering
|
|
# 5. Supply chain attack prevention
|
|
#
|
|
# Package verification should be:
|
|
# - Enabled in DNF configuration
|
|
# - CA key imported
|
|
# - CRL checking enabled
|
|
check_package_verification() {
|
|
echo -e "${CYAN}7. Package Verification Check${NC}"
|
|
echo "=========================================="
|
|
|
|
# Check CA key
|
|
if [ -f /etc/pki/rpm-gpg/RPM-GPG-KEY-custom-ca ]; then
|
|
log_info "PASS: CA key installed"
|
|
echo " Status: CA KEY PRESENT"
|
|
else
|
|
log_warning "WARNING: CA key not found"
|
|
echo " Status: CA KEY MISSING"
|
|
fi
|
|
|
|
# Check DNF configuration
|
|
if [ -f /etc/dnf/dnf.conf ]; then
|
|
if grep -q "^gpgcheck=1" /etc/dnf/dnf.conf; then
|
|
log_info "PASS: Package signature verification enabled"
|
|
echo " Status: GPG CHECK ENABLED"
|
|
else
|
|
log_warning "WARNING: Package signature verification not enabled"
|
|
echo " Status: GPG CHECK DISABLED"
|
|
fi
|
|
|
|
if grep -q "^repo_gpgcheck=1" /etc/dnf/dnf.conf; then
|
|
log_info "PASS: Repository signature verification enabled"
|
|
echo " Status: REPO GPG CHECK ENABLED"
|
|
else
|
|
log_warning "WARNING: Repository signature verification not enabled"
|
|
echo " Status: REPO GPG CHECK DISABLED"
|
|
fi
|
|
else
|
|
log_warning "WARNING: DNF configuration not found"
|
|
echo " Status: FILE NOT FOUND"
|
|
fi
|
|
|
|
# Check audit script
|
|
if [ -f /usr/local/bin/verify-package.sh ]; then
|
|
log_info "PASS: Package verification script exists"
|
|
echo " Status: SCRIPT PRESENT"
|
|
else
|
|
log_warning "WARNING: Package verification script not found"
|
|
echo " Status: SCRIPT MISSING"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check kernel hardening
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Kernel hardening provides:
|
|
# 1. Protection against kernel exploits
|
|
# 2. Memory layout randomization
|
|
# 3. Protection against buffer overflows
|
|
# 4. Control Flow Integrity
|
|
# 5. Defense in depth
|
|
#
|
|
# Kernel hardening should include:
|
|
# - ASLR enabled
|
|
# - NX bit enabled
|
|
# - Stack protection
|
|
# - SELinux enabled
|
|
check_kernel_hardening() {
|
|
echo -e "${CYAN}8. Kernel Hardening Check${NC}"
|
|
echo "=========================================="
|
|
|
|
# Check ASLR
|
|
if [ -f /proc/sys/kernel/randomize_va_space ]; then
|
|
local aslr=$(cat /proc/sys/kernel/randomize_va_space)
|
|
if [ "$aslr" = "2" ]; then
|
|
log_info "PASS: ASLR enabled"
|
|
echo " Status: FULL ASLR (Randomize everything)"
|
|
elif [ "$aslr" = "1" ]; then
|
|
log_info "PASS: ASLR enabled"
|
|
echo " Status: BASIC ASLR (Standard)"
|
|
else
|
|
log_warning "WARNING: ASLR not enabled"
|
|
echo " Status: ASLR DISABLED"
|
|
fi
|
|
fi
|
|
|
|
# Check SELinux
|
|
if command -v getenforce &> /dev/null; then
|
|
if [ "$(getenforce)" = "Enforcing" ]; then
|
|
log_info "PASS: SELinux enforcing"
|
|
echo " Status: ENFORCING"
|
|
else
|
|
log_warning "WARNING: SELinux not enforcing"
|
|
echo " Status: $(getenforce)"
|
|
fi
|
|
fi
|
|
|
|
# Check for ExecShield
|
|
if [ -f /proc/sys/kernel/exec-shield ]; then
|
|
local execshield=$(cat /proc/sys/kernel/exec-shield)
|
|
if [ "$execshield" = "1" ]; then
|
|
log_info "PASS: ExecShield enabled"
|
|
echo " Status: EXEC SHIELD ENABLED"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check for suspicious processes
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Suspicious processes check provides:
|
|
# 1. Detection of unauthorized processes
|
|
# 2. Identification of potential malware
|
|
# 3. Monitoring of system activity
|
|
# 4. Early detection of compromise
|
|
#
|
|
# This check looks for:
|
|
# - Unknown processes
|
|
# - Processes running as root
|
|
# - Suspicious network processes
|
|
check_suspicious_processes() {
|
|
echo -e "${CYAN}9. Suspicious Processes Check${NC}"
|
|
echo "=========================================="
|
|
|
|
# Check for processes running as root
|
|
local root_processes=$(ps aux | grep "root" | wc -l)
|
|
if [ "$root_processes" -gt 50 ]; then
|
|
log_warning "WARNING: High number of root processes: $root_processes"
|
|
echo " Status: $root_processes root processes running"
|
|
else
|
|
log_info "PASS: Reasonable number of root processes"
|
|
echo " Status: $root_processes root processes running"
|
|
fi
|
|
|
|
# Check for suspicious processes
|
|
local suspicious=$(ps aux | grep -E "(nc|netcat|nmap|hydra|metasploit)" | grep -v grep)
|
|
if [ -n "$suspicious" ]; then
|
|
log_error "FAIL: Suspicious processes detected!"
|
|
echo " Status: SUSPICIOUS PROCESSES FOUND"
|
|
echo "$suspicious" | sed 's/^/ /'
|
|
else
|
|
log_info "PASS: No suspicious processes detected"
|
|
echo " Status: NO SUSPICIOUS PROCESSES"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check audit logs
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# Audit log review provides:
|
|
# 1. Detection of security events
|
|
# 2. Identification of unauthorized access
|
|
# 3. Forensic information
|
|
# 4. Compliance documentation
|
|
# 5. Security incident response
|
|
#
|
|
# This check looks for:
|
|
# - Failed sudo attempts
|
|
# - Privilege escalation attempts
|
|
# - File access to sensitive files
|
|
# - System configuration changes
|
|
check_audit_logs() {
|
|
echo -e "${CYAN}10. Audit Log Check${NC}"
|
|
echo "=========================================="
|
|
|
|
# Check for failed sudo attempts
|
|
if [ -f /var/log/secure ]; then
|
|
local sudo_failures=$(grep "authentication failure" /var/log/secure 2>/dev/null | wc -l)
|
|
if [ "$sudo_failures" -gt 0 ]; then
|
|
log_warning "WARNING: Failed sudo attempts detected: $sudo_failures"
|
|
echo " Status: $sudo_failures failed sudo attempts"
|
|
echo " Last 5 failures:"
|
|
grep "authentication failure" /var/log/secure 2>/dev/null | tail -5 | sed 's/^/ /'
|
|
else
|
|
log_info "PASS: No failed sudo attempts"
|
|
echo " Status: No failed sudo attempts"
|
|
fi
|
|
fi
|
|
|
|
# Check auditd logs
|
|
if command -v ausearch &> /dev/null; then
|
|
local avc_denials=$(ausearch -m avc -ts recent 2>/dev/null | wc -l)
|
|
if [ "$avc_denials" -gt 0 ]; then
|
|
log_warning "WARNING: SELinux denials detected: $avc_denials"
|
|
echo " Status: $avc_denials SELinux denials"
|
|
else
|
|
log_info "PASS: No SELinux denials"
|
|
echo " Status: No SELinux denials"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Check file permissions
|
|
#
|
|
# SECURITY RATIONALE:
|
|
# File permissions check provides:
|
|
# 1. Detection of overly permissive files
|
|
# 2. Verification of sensitive file protection
|
|
# 3. Identification of potential security issues
|
|
# 4. Compliance with security standards
|
|
#
|
|
# This check looks for:
|
|
# - World-writable files in critical directories
|
|
# - Files with incorrect ownership
|
|
# - SUID/SGID files
|
|
check_file_permissions() {
|
|
echo -e "${CYAN}11. File Permissions Check${NC}"
|
|
echo "=========================================="
|
|
|
|
# Check /etc permissions
|
|
local etc_world_writable=$(find /etc -type f -perm -0002 2>/dev/null | wc -l)
|
|
if [ "$etc_world_writable" -gt 0 ]; then
|
|
log_warning "WARNING: World-writable files in /etc: $etc_world_writable"
|
|
echo " Status: $etc_world_writable world-writable files"
|
|
else
|
|
log_info "PASS: No world-writable files in /etc"
|
|
echo " Status: No world-writable files"
|
|
fi
|
|
|
|
# Check for SUID/SGID files
|
|
local suid_files=$(find /usr -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | wc -l)
|
|
if [ "$suid_files" -gt 100 ]; then
|
|
log_warning "WARNING: High number of SUID/SGID files: $suid_files"
|
|
echo " Status: $suid_files SUID/SGID files"
|
|
else
|
|
log_info "PASS: Reasonable number of SUID/SGID files"
|
|
echo " Status: $suid_files SUID/SGID files"
|
|
fi
|
|
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
# Generate summary
|
|
generate_summary() {
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Security Audit Summary"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Date: $AUDIT_DATE"
|
|
echo "Hostname: $(hostname)"
|
|
echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)"
|
|
echo ""
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Fedora Kinoite SAW Security Audit"
|
|
echo " Version: 1.0"
|
|
echo " Date: 2026-04-02"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
check_root
|
|
|
|
# Run all checks
|
|
check_sudo_access
|
|
check_selinux
|
|
check_auditd
|
|
check_firewall
|
|
check_vpn
|
|
check_dns
|
|
check_package_verification
|
|
check_kernel_hardening
|
|
check_suspicious_processes
|
|
check_audit_logs
|
|
check_file_permissions
|
|
|
|
# Generate summary
|
|
generate_summary
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Audit Complete!"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Recommendations:"
|
|
echo "- Review any WARNING or ERROR items"
|
|
echo "- Run this script weekly for critical systems"
|
|
echo "- Run this script monthly for standard systems"
|
|
echo "- Keep audit logs secure"
|
|
echo "- Test rollback capability regularly"
|
|
echo ""
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |