#!/bin/sh # Ensure the script is run as root if [ "$(id -u)" -ne 0 ]; then echo "Error: This script must be run as root." >&2 exit 1 fi echo "--- 1. Installing Fail2ban, Rsyslog, and Iptables ---" apk update # iptables is required for Fail2ban's default banning actions apk add fail2ban rsyslog iptables echo "--- 2. Ripping out BusyBox Syslog and configuring Rsyslog ---" # Stop and remove the default Alpine logger rc-service syslog stop 2>/dev/null rc-update del syslog boot 2>/dev/null # Enable Rsyslog rc-update add rsyslog boot # Force Rsyslog to use the traditional date format (must be at the absolute top of the config) sed -i '/\$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat/d' /etc/rsyslog.conf echo '$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat' > /tmp/rsyslog-new.conf cat /etc/rsyslog.conf >> /tmp/rsyslog-new.conf mv /tmp/rsyslog-new.conf /etc/rsyslog.conf # Start Rsyslog rc-service rsyslog restart echo "--- 3. Creating Custom SSHD Filter ---" mkdir -p /etc/fail2ban/filter.d # This regex ignores Alpine/rsyslog prefix bloat and hunts directly for OpenSSH 9.8+ session failures cat << 'EOF' > /etc/fail2ban/filter.d/sshd-nuclear.conf [Definition] failregex = ^.*(?:sshd|sshd-session)(?:\[\d+\])?: (?:Failed password for|Invalid user) .*? from .*$ ignoreregex = EOF echo "--- 4. Configuring jail.local ---" cat << 'EOF' > /etc/fail2ban/jail.local [DEFAULT] bantime = 1h findtime = 10m maxretry = 5 [sshd] enabled = true port = ssh filter = sshd-nuclear logpath = /var/log/messages # Force polling to prevent silent failures on Alpine backend = polling EOF echo "--- 5. Enabling and Starting Fail2ban ---" rc-update add fail2ban default rc-service fail2ban restart echo "" echo "Fail2ban install complete. Check Fail2ban is now actively monitoring /var/log/messages."