Part 8 of 10

    Security Hardening & Compliance Scripts

    Firewall configuration, SSH hardening, intrusion detection, CIS compliance checks, and vulnerability scanning. Because security isn't a one-time setup.

    nftables
    CIS Benchmarks
    AIDE

    Security isn't a one-time setup—it's ongoing maintenance. Configurations drift, new vulnerabilities emerge, and what was secure last year might not be today.

    This guide uses Claude Code to generate security hardening scripts, compliance checks, and audit tools. We'll cover firewall configuration, SSH hardening, intrusion detection, security scanning, and automated compliance verification against CIS benchmarks.

    1

    Prerequisites

    • Claude Code installed (see Part 1)
    • Root access to your server
    • Basic understanding of Linux security concepts
    2

    Comprehensive Firewall Configuration

    Let's start with a robust firewall setup that goes beyond basic UFW rules:

    "Create a comprehensive firewall configuration script using nftables with default deny incoming, rate limiting for SSH, port knocking, geographic blocking, Docker-aware rules, logging, whitelist for trusted IPs, and protection against common attacks."

    scripts/firewall-setup.sh
    #!/bin/bash
    set -euo pipefail
    
    # =============================================================================
    # Advanced Firewall Configuration with nftables
    # =============================================================================
    
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    CONFIG_FILE="${CONFIG_FILE:-$SCRIPT_DIR/firewall.conf}"
    RULES_FILE="/etc/nftables.conf"
    
    # Load configuration
    source "$CONFIG_FILE"
    
    # Defaults
    SSH_PORT="${SSH_PORT:-22}"
    SSH_RATE_LIMIT="${SSH_RATE_LIMIT:-4/minute}"
    WHITELIST_IPS="${WHITELIST_IPS:-}"
    BLOCKED_COUNTRIES="${BLOCKED_COUNTRIES:-}"
    ENABLE_PORT_KNOCKING="${ENABLE_PORT_KNOCKING:-false}"
    KNOCK_SEQUENCE="${KNOCK_SEQUENCE:-7000,8000,9000}"
    LOG_DROPPED="${LOG_DROPPED:-true}"
    MAINTENANCE_MODE="${MAINTENANCE_MODE:-false}"
    
    log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
    
    generate_nftables_config() {
        cat << 'EOF'
    #!/usr/sbin/nft -f
    
    flush ruleset
    
    table inet filter {
        set ssh_meter {
            type ipv4_addr
            flags dynamic,timeout
            timeout 1m
        }
        
        set port_scanners {
            type ipv4_addr
            flags dynamic,timeout
            timeout 1h
        }
    
        chain input {
            type filter hook input priority 0; policy drop;
            
            ct state established,related accept
            ct state invalid drop
            iif "lo" accept
            
            # Block known port scanners
            ip saddr @port_scanners drop
            
            # Rate limit ICMP
            ip protocol icmp icmp type echo-request limit rate 4/second accept
            
            # Protection against SYN floods
            tcp flags syn limit rate 100/second burst 150 packets accept
            tcp flags syn drop
            
            # SSH with rate limiting
            tcp dport $SSH_PORT ct state new accept
            
            # HTTP/HTTPS
            tcp dport { 80, 443 } accept
            
            # Allow Docker networks
            ip saddr 172.16.0.0/12 accept
            ip saddr 10.0.0.0/8 accept
        }
        
        chain forward {
            type filter hook forward priority 0; policy accept;
            oifname "docker*" accept
            iifname "docker*" accept
        }
        
        chain output {
            type filter hook output priority 0; policy accept;
        }
    }
    EOF
    }
    
    apply_firewall() {
        log "Generating nftables configuration..."
        generate_nftables_config > "$RULES_FILE"
        
        log "Applying firewall rules..."
        nft -f "$RULES_FILE"
        
        systemctl enable nftables
        log "Firewall configured successfully"
    }
    
    case "${1:-apply}" in
        apply) apply_firewall ;;
        status) nft list ruleset | head -100 ;;
        *) echo "Usage: $0 {apply|status}" ;;
    esac
    firewall.conf
    # =============================================================================
    # Firewall Configuration
    # =============================================================================
    
    SSH_PORT=22
    SSH_RATE_LIMIT="4/minute"
    
    # Whitelist (comma-separated, always allowed)
    WHITELIST_IPS="YOUR_HOME_IP, YOUR_OFFICE_IP"
    
    # Additional allowed TCP ports
    ALLOWED_TCP_PORTS="3000, 8080, 9090"
    
    # Port Knocking
    ENABLE_PORT_KNOCKING=false
    KNOCK_SEQUENCE="7000,8000,9000"
    
    # Geographic Blocking (ISO country codes)
    BLOCKED_COUNTRIES=""
    
    # Logging
    LOG_DROPPED=true
    
    # Maintenance Mode
    MAINTENANCE_MODE=false
    3

    SSH Hardening

    Beyond basic SSH configuration, let's implement comprehensive hardening:

    "Create an SSH hardening script that generates new ED25519 host keys, configures secure ciphers and MACs, sets up 2FA with TOTP, configures fail2ban with aggressive settings, and enables detailed logging for forensics."

    scripts/harden-ssh.sh
    #!/bin/bash
    set -euo pipefail
    
    # =============================================================================
    # SSH Hardening Script
    # =============================================================================
    
    BACKUP_DIR="/root/ssh-backup-$(date +%Y%m%d)"
    REAL_SSH_PORT="${REAL_SSH_PORT:-2222}"
    ENABLE_2FA="${ENABLE_2FA:-false}"
    
    log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
    log_success() { echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] \033[0;32m✓\033[0m $*"; }
    
    backup_config() {
        log "Backing up current SSH configuration..."
        mkdir -p "$BACKUP_DIR"
        cp -r /etc/ssh "$BACKUP_DIR/"
        log_success "Backup created at $BACKUP_DIR"
    }
    
    regenerate_host_keys() {
        log "Regenerating host keys (ED25519 only)..."
        rm -f /etc/ssh/ssh_host_*
        ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N "" -C "$(hostname)"
        chmod 600 /etc/ssh/ssh_host_ed25519_key
        chmod 644 /etc/ssh/ssh_host_ed25519_key.pub
        log_success "New ED25519 host key generated"
    }
    
    configure_sshd() {
        log "Configuring SSH daemon..."
        
        cat > /etc/ssh/sshd_config << EOF
    # Hardened SSH Configuration
    Port $REAL_SSH_PORT
    AddressFamily inet
    
    # Host Keys (ED25519 only)
    HostKey /etc/ssh/ssh_host_ed25519_key
    
    # Security
    Protocol 2
    StrictModes yes
    PermitRootLogin prohibit-password
    MaxAuthTries 3
    MaxSessions 3
    PubkeyAuthentication yes
    PasswordAuthentication no
    PermitEmptyPasswords no
    UsePAM yes
    
    # Cryptography (modern, secure algorithms only)
    KexAlgorithms curve25519-sha256@libssh.org,curve25519-sha256
    Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
    MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
    
    # Disable dangerous features
    X11Forwarding no
    AllowTcpForwarding no
    AllowAgentForwarding no
    PermitTunnel no
    
    # Session
    ClientAliveInterval 300
    ClientAliveCountMax 2
    LoginGraceTime 30
    
    # Logging
    SyslogFacility AUTH
    LogLevel VERBOSE
    
    # Banner
    Banner /etc/ssh/banner
    EOF
    
        # Create warning banner
        cat > /etc/ssh/banner << 'BANNER'
    ***************************************************************************
                            AUTHORIZED ACCESS ONLY
    ***************************************************************************
    This system is for authorized users only. All activities are monitored.
    ***************************************************************************
    BANNER
    
        if sshd -t; then
            log_success "SSH configuration valid"
        else
            log "SSH configuration invalid! Restoring backup..."
            cp "$BACKUP_DIR/ssh/sshd_config" /etc/ssh/sshd_config
            exit 1
        fi
    }
    
    setup_fail2ban() {
        log "Configuring fail2ban..."
        apt-get install -y fail2ban
        
        cat > /etc/fail2ban/jail.local << EOF
    [DEFAULT]
    bantime = 1h
    findtime = 10m
    maxretry = 3
    banaction = nftables-multiport
    
    [sshd]
    enabled = true
    port = $REAL_SSH_PORT
    filter = sshd
    logpath = /var/log/auth.log
    maxretry = 3
    bantime = 24h
    
    [sshd-aggressive]
    enabled = true
    port = $REAL_SSH_PORT
    filter = sshd[mode=aggressive]
    logpath = /var/log/auth.log
    maxretry = 1
    bantime = 1w
    findtime = 1d
    
    [recidive]
    enabled = true
    filter = recidive
    logpath = /var/log/fail2ban.log
    bantime = 1w
    findtime = 1d
    maxretry = 3
    EOF
    
        systemctl enable fail2ban
        systemctl restart fail2ban
        log_success "Fail2ban configured"
    }
    
    main() {
        log "=========================================="
        log "SSH Hardening Script"
        log "=========================================="
        
        [[ $EUID -ne 0 ]] && { echo "Run as root"; exit 1; }
        
        backup_config
        regenerate_host_keys
        configure_sshd
        setup_fail2ban
        
        systemctl restart sshd
        
        log "=========================================="
        log "SSH hardening complete!"
        log "SSH is now on port $REAL_SSH_PORT"
        log "=========================================="
    }
    
    main "$@"
    4

    CIS Benchmark Compliance Checker

    Automate compliance verification against CIS benchmarks:

    "Create a CIS benchmark compliance checker for Ubuntu that checks filesystem configuration, user accounts, network settings, SSH configuration, and file permissions. Generate reports with pass/fail status, severity levels, remediation commands, and overall compliance score."

    scripts/cis-audit.sh
    #!/bin/bash
    set -uo pipefail
    
    # =============================================================================
    # CIS Benchmark Compliance Checker
    # Based on CIS Ubuntu Linux Benchmark
    # =============================================================================
    
    OUTPUT_DIR="${OUTPUT_DIR:-/var/log/compliance}"
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    REPORT_JSON="$OUTPUT_DIR/cis-audit-$TIMESTAMP.json"
    REPORT_HTML="$OUTPUT_DIR/cis-audit-$TIMESTAMP.html"
    
    mkdir -p "$OUTPUT_DIR"
    
    declare -a RESULTS=()
    TOTAL_CHECKS=0
    PASSED_CHECKS=0
    FAILED_CHECKS=0
    
    add_result() {
        local id="$1" title="$2" status="$3" severity="$4" message="$5"
        ((TOTAL_CHECKS++))
        [[ "$status" == "PASS" ]] && ((PASSED_CHECKS++))
        [[ "$status" == "FAIL" ]] && ((FAILED_CHECKS++))
        RESULTS+=("{"id":"$id","title":"$title","status":"$status","severity":"$severity","message":"$message"}")
    }
    
    check_filesystem() {
        echo "Checking filesystem configuration..."
        
        # Check unused filesystems
        local unused_fs=("cramfs" "freevxfs" "jffs2" "hfs" "hfsplus")
        for fs in "${unused_fs[@]}"; do
            if ! lsmod | grep -q "^$fs "; then
                add_result "1.1.1.$fs" "Ensure $fs is disabled" "PASS" "Low" "Not loaded"
            else
                add_result "1.1.1.$fs" "Ensure $fs is disabled" "FAIL" "Low" "Loaded"
            fi
        done
        
        # Check /tmp mount options
        if mount | grep -q " /tmp "; then
            local tmp_opts=$(mount | grep " /tmp " | awk '{print $6}')
            if [[ "$tmp_opts" == *"noexec"* ]] && [[ "$tmp_opts" == *"nosuid"* ]]; then
                add_result "1.1.2" "Ensure /tmp has noexec,nosuid" "PASS" "Medium" "Properly configured"
            else
                add_result "1.1.2" "Ensure /tmp has noexec,nosuid" "FAIL" "Medium" "Missing options"
            fi
        fi
    }
    
    check_ssh() {
        echo "Checking SSH configuration..."
        local sshd_config="/etc/ssh/sshd_config"
        
        # Check PermitRootLogin
        if grep -qi "^PermitRootLogin no" "$sshd_config" || grep -qi "^PermitRootLogin prohibit-password" "$sshd_config"; then
            add_result "5.2.9" "Ensure SSH root login is disabled" "PASS" "Critical" "Properly restricted"
        else
            add_result "5.2.9" "Ensure SSH root login is disabled" "FAIL" "Critical" "Not restricted"
        fi
        
        # Check PermitEmptyPasswords
        if grep -qi "^PermitEmptyPasswords no" "$sshd_config" || ! grep -qi "^PermitEmptyPasswords yes" "$sshd_config"; then
            add_result "5.2.10" "Ensure SSH empty passwords disabled" "PASS" "Critical" "Disabled"
        else
            add_result "5.2.10" "Ensure SSH empty passwords disabled" "FAIL" "Critical" "Enabled"
        fi
    }
    
    check_file_permissions() {
        echo "Checking file permissions..."
        
        # /etc/passwd
        local passwd_perms=$(stat -c "%a" /etc/passwd)
        if [[ "$passwd_perms" == "644" ]]; then
            add_result "6.1.2" "Ensure /etc/passwd permissions" "PASS" "High" "Correct (644)"
        else
            add_result "6.1.2" "Ensure /etc/passwd permissions" "FAIL" "High" "Incorrect ($passwd_perms)"
        fi
        
        # /etc/shadow
        local shadow_perms=$(stat -c "%a" /etc/shadow 2>/dev/null)
        if [[ "$shadow_perms" == "640" ]] || [[ "$shadow_perms" == "600" ]]; then
            add_result "6.1.4" "Ensure /etc/shadow permissions" "PASS" "Critical" "Correct ($shadow_perms)"
        else
            add_result "6.1.4" "Ensure /etc/shadow permissions" "FAIL" "Critical" "Incorrect ($shadow_perms)"
        fi
    }
    
    generate_report() {
        local score=$((PASSED_CHECKS * 100 / TOTAL_CHECKS))
        
        echo ""
        echo "=========================================="
        echo "Audit Complete"
        echo "=========================================="
        echo "Total Checks: $TOTAL_CHECKS"
        echo "Passed: $PASSED_CHECKS"
        echo "Failed: $FAILED_CHECKS"
        echo "Score: ${score}%"
        
        # Generate JSON
        local results_json=$(printf '%s\n' "${RESULTS[@]}" | paste -sd,)
        cat > "$REPORT_JSON" << EOF
    {
        "timestamp": "$(date -Iseconds)",
        "hostname": "$(hostname)",
        "summary": {"total": $TOTAL_CHECKS, "passed": $PASSED_CHECKS, "failed": $FAILED_CHECKS, "score": $score},
        "results": [$results_json]
    }
    EOF
        echo "Report saved to: $REPORT_JSON"
    }
    
    main() {
        echo "=========================================="
        echo "CIS Benchmark Compliance Checker"
        echo "=========================================="
        
        check_filesystem
        check_ssh
        check_file_permissions
        generate_report
    }
    
    main "$@"
    5

    Intrusion Detection with AIDE

    Set up file integrity monitoring with AIDE (Advanced Intrusion Detection Environment):

    "Create an AIDE configuration that monitors critical system files, runs daily checks, sends Discord/email alerts on changes, and provides detailed diff reports."

    scripts/setup-aide.sh
    #!/bin/bash
    set -euo pipefail
    
    # =============================================================================
    # AIDE Setup Script
    # =============================================================================
    
    log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
    
    log "Installing AIDE..."
    apt-get update && apt-get install -y aide aide-common
    
    log "Configuring AIDE..."
    cat > /etc/aide/aide.conf << 'EOF'
    # Database locations
    database_in=file:/var/lib/aide/aide.db
    database_out=file:/var/lib/aide/aide.db.new
    gzip_dbout=yes
    
    # Rule definitions
    FULL = p+i+n+u+g+s+m+c+sha256+sha512
    BINLIB = p+i+n+u+g+s+b+m+c+sha256+sha512+xattrs
    CONFIG = p+i+n+u+g+s+sha256
    
    # Monitored paths
    /bin BINLIB
    /sbin BINLIB
    /usr/bin BINLIB
    /usr/sbin BINLIB
    /lib BINLIB
    /etc CONFIG
    /boot FULL
    
    # SSH
    /etc/ssh CONFIG
    /root/.ssh CONFIG
    
    # Cron
    /etc/cron.d CONFIG
    /etc/cron.daily CONFIG
    /var/spool/cron CONFIG
    
    # Exclusions
    !/var/log/.*
    !/var/cache/.*
    !/tmp/.*
    !/var/tmp/.*
    !/run/.*
    !/proc/.*
    !/sys/.*
    EOF
    
    log "Initializing AIDE database..."
    aideinit
    mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
    
    # Create check script
    cat > /usr/local/bin/aide-check << 'CHECK_EOF'
    #!/bin/bash
    LOG_FILE="/var/log/aide/aide-$(date +%Y%m%d).log"
    DISCORD_WEBHOOK="${DISCORD_WEBHOOK:-}"
    
    mkdir -p /var/log/aide
    echo "AIDE check started: $(date)" >> "$LOG_FILE"
    
    aide --check >> "$LOG_FILE" 2>&1
    AIDE_EXIT=$?
    
    if [[ $AIDE_EXIT -eq 0 ]]; then
        echo "No changes detected" >> "$LOG_FILE"
        exit 0
    fi
    
    # Changes detected - send alert
    if [[ -n "$DISCORD_WEBHOOK" ]]; then
        curl -s -X POST "$DISCORD_WEBHOOK" \
            -H "Content-Type: application/json" \
            -d "{
                "embeds": [{
                    "title": "⚠️ AIDE: File Changes Detected",
                    "description": "File integrity changes on $(hostname)",
                    "color": 16744256
                }]
            }" || true
    fi
    exit $AIDE_EXIT
    CHECK_EOF
    
    chmod +x /usr/local/bin/aide-check
    
    # Create update script
    cat > /usr/local/bin/aide-update << 'UPDATE_EOF'
    #!/bin/bash
    echo "Updating AIDE database..."
    aide --update
    mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
    echo "AIDE database updated"
    UPDATE_EOF
    
    chmod +x /usr/local/bin/aide-update
    
    # Cron job
    echo "0 3 * * * root /usr/local/bin/aide-check" > /etc/cron.d/aide
    
    log "AIDE setup complete"
    log "Commands: aide-check (run check), aide-update (update after changes)"
    6

    Security Scanning Script

    Regular vulnerability scanning to catch security issues:

    scripts/security-scan.sh
    #!/bin/bash
    set -uo pipefail
    
    # =============================================================================
    # Security Scanning Script
    # =============================================================================
    
    OUTPUT_DIR="${OUTPUT_DIR:-/var/log/security-scans}"
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    REPORT_FILE="$OUTPUT_DIR/security-scan-$TIMESTAMP.txt"
    DISCORD_WEBHOOK="${DISCORD_WEBHOOK:-}"
    
    mkdir -p "$OUTPUT_DIR"
    
    log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$REPORT_FILE"; }
    section() { echo -e "\n========== $* ==========" | tee -a "$REPORT_FILE"; }
    
    check_updates() {
        section "SECURITY UPDATES"
        apt-get update -qq
        local security_updates=$(apt-get -s upgrade 2>/dev/null | grep -i security | wc -l)
        log "Security updates pending: $security_updates"
        [[ $security_updates -gt 0 ]] && return 1
        return 0
    }
    
    check_open_ports() {
        section "OPEN PORTS"
        local baseline_file="/etc/security/port-baseline.txt"
        local current_ports=$(ss -tlnp | grep LISTEN | awk '{print $4}' | sort -u)
        
        log "Currently listening:"
        echo "$current_ports" | tee -a "$REPORT_FILE"
        
        if [[ -f "$baseline_file" ]]; then
            local new_ports=$(comm -23 <(echo "$current_ports" | sort) <(sort "$baseline_file"))
            if [[ -n "$new_ports" ]]; then
                log "WARNING: New ports detected!"
                return 1
            fi
        else
            echo "$current_ports" > "$baseline_file"
        fi
        return 0
    }
    
    check_auth_failures() {
        section "AUTH FAILURES (24h)"
        local failures=$(journalctl --since "24 hours ago" -u sshd 2>/dev/null | grep -i "failed\|invalid" | wc -l)
        log "Failed authentication attempts: $failures"
        [[ $failures -gt 100 ]] && { log "WARNING: High failure count!"; return 1; }
        return 0
    }
    
    check_ssl_certs() {
        section "SSL CERTIFICATES"
        local domains="${SSL_DOMAINS:-}"
        [[ -z "$domains" ]] && { log "No domains configured"; return 0; }
        
        for domain in $domains; do
            local expiry=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null | \
                openssl x509 -noout -dates 2>/dev/null | grep notAfter | cut -d= -f2)
            if [[ -n "$expiry" ]]; then
                local days_left=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 ))
                [[ $days_left -lt 14 ]] && log "WARNING: $domain expires in $days_left days!"
                [[ $days_left -ge 14 ]] && log "OK: $domain expires in $days_left days"
            fi
        done
    }
    
    check_rootkits() {
        section "ROOTKIT SCAN"
        command -v rkhunter &>/dev/null && rkhunter --check --skip-keypress --report-warnings-only 2>/dev/null | tee -a "$REPORT_FILE"
        command -v chkrootkit &>/dev/null && chkrootkit 2>/dev/null | grep -v "not found" | tee -a "$REPORT_FILE"
    }
    
    run_lynis() {
        section "LYNIS AUDIT"
        command -v lynis &>/dev/null || apt-get install -y lynis
        lynis audit system --quick --quiet 2>/dev/null | tail -50 >> "$REPORT_FILE"
        local score=$(grep "Hardening index" /var/log/lynis.log 2>/dev/null | tail -1 | grep -oE '[0-9]+')
        [[ -n "$score" ]] && log "Lynis hardening score: $score/100"
    }
    
    generate_summary() {
        section "SCAN SUMMARY"
        log "Scan completed at $(date)"
        log "Full report: $REPORT_FILE"
        
        if [[ -n "$DISCORD_WEBHOOK" ]]; then
            local color=3066993 status="✅ Clean"
            grep -q "WARNING\|ERROR" "$REPORT_FILE" && { color=16744256; status="⚠️ Issues Found"; }
            
            curl -s -X POST "$DISCORD_WEBHOOK" \
                -H "Content-Type: application/json" \
                -d "{"embeds": [{"title": "Security Scan: $status","description": "Scan on $(hostname)","color": $color}]}" || true
        fi
    }
    
    main() {
        log "=========================================="
        log "Security Scan - $(hostname)"
        log "=========================================="
        
        check_updates || true
        check_open_ports || true
        check_auth_failures || true
        check_ssl_certs || true
        check_rootkits || true
        run_lynis || true
        generate_summary
    }
    
    main "$@"
    7

    Scheduling Security Tasks

    Use systemd timers to run security checks automatically:

    /etc/systemd/system/security-scan.timer
    [Unit]
    Description=Weekly Security Scan
    
    [Timer]
    OnCalendar=Sun *-*-* 02:00:00
    Persistent=true
    
    [Install]
    WantedBy=timers.target
    /etc/systemd/system/security-scan.service
    [Unit]
    Description=Security Scan
    After=network.target
    
    [Service]
    Type=oneshot
    ExecStart=/opt/scripts/security-scan.sh
    Environment=DISCORD_WEBHOOK=https://discord.com/api/webhooks/xxx
    Environment=SSL_DOMAINS=example.com api.example.com
    Enable timers
    systemctl daemon-reload
    systemctl enable --now security-scan.timer
    systemctl enable --now aide-check.timer
    8

    Tips & Quick Reference

    Security Best Practices

    • Layer your defenses — No single tool catches everything
    • Automate verification — Manual checks get forgotten
    • Alert on changes — Unexpected changes are often first signs of compromise
    • Keep baselines updated — After legitimate changes, update baselines
    • Test your alerts — Make sure notifications actually reach you
    • Document exceptions — Know why each security exception exists

    Quick Reference: Security Prompts

    NeedPrompt Pattern
    Firewall rules"Create nftables rules for [service] with [rate limiting/geo blocking]"
    SSH hardening"Generate SSH config with [2FA/certificates/specific ciphers]"
    Compliance"Create compliance checker for [CIS/PCI-DSS/HIPAA] on [OS]"
    IDS setup"Configure AIDE to monitor [paths] with [exclusions]"
    Scanning"Create security scan script checking [updates/ports/certs/rootkits]"