Stop SSH Brute‑Force Attacks: 5 Ready‑to‑Use Scripts to Harden Your Server
The article reveals how thousands of SSH brute‑force attempts target servers daily, explains why password‑based root logins are a critical risk, and provides five practical Bash scripts—analysis, auto‑banning, security checks, user audits, and daily reporting—to quickly fortify SSH defenses.
Today I logged into a test server and the line below gave me chills. 74744 brute‑force attempts—averaging 10 per minute over 5 days . My server only had a single root password login. This is not a drill; it’s a daily "digital siege".
1. You think you’re not compromised? It’s just luck
Attackers don’t need complex passwords. They use bots to scan the whole Internet on port 22, trying combinations such as root/123456, admin/admin, ubuntu/ubuntu, etc. A single successful login can turn your server into a mining farm, botnet, or jump host. Worse, you may never know it’s been compromised. Many mining bots deliberately limit CPU usage to avoid triggering alerts.
2. Don’t wait for disaster! 5 scripts to immediately strengthen your defenses
All scripts are production‑tested, copy‑and‑paste ready, and should be bookmarked.
Script 1: One‑click analysis of top 10 brute‑force sources
#!/bin/bash
# analyze_ssh_brute.sh - Analyze /var/log/secure for brute‑force sources
echo "=== SSH 暴力破解来源 TOP 10 ==="
grep "Failed password" /var/log/secure* 2>/dev/null |
awk '{print $(NF-3)}' |
sort |
uniq -c |
sort -nr |
head -10 |
awk '{printf "%-5s %s
", $1, $2}'Example output is shown below. Run it weekly and manually add high‑frequency IPs to the firewall blacklist.
Script 2: Auto‑ban high‑frequency failed IPs (lightweight Fail2ban alternative)
If you don’t want to install Fail2ban, this script provides basic automatic banning.
#!/bin/bash
# auto_ban_ssh.sh - Auto‑ban IPs with >5 failures within 1 hour
LOG_FILE="/var/log/secure"
BAN_TIME=3600 # ban duration in seconds
awk '/Failed password/ {print $(NF-3)}' "$LOG_FILE" |
sort |
uniq -c |
awk '$1 > 5 {print $2}' |
while read ip; do
if ! iptables -C INPUT -s "$ip" -j DROP 2>/dev/null; then
echo "Banning $ip for $BAN_TIME seconds"
iptables -A INPUT -s "$ip" -j DROP
(sleep $BAN_TIME && iptables -D INPUT -s "$ip" -j DROP 2>/dev/null) &
fi
doneRun it every 5 minutes via crontab.
*/5 * * * * /opt/scripts/auto_ban_ssh.shScript 3: Check if password login risk still exists
#!/bin/bash
# check_ssh_security.sh - SSH security audit
echo "=== SSH 安全检查 ==="
# 1. Is password authentication disabled?
if grep -q "^PasswordAuthentication\s*no" /etc/ssh/sshd_config; then
echo "✅ PasswordAuthentication: disabled"
else
echo "❌ PasswordAuthentication: ENABLED! (high risk)"
fi
# 2. Is root login prohibited?
if grep -q "^PermitRootLogin\s*no" /etc/ssh/sshd_config; then
echo "✅ PermitRootLogin: disabled"
else
echo "❌ PermitRootLogin: ENABLED! (high risk)"
fi
# 3. Scan authorized_keys for suspicious entries
for user_dir in /home/*; do
key_file="$user_dir/.ssh/authorized_keys"
if [ -f "$key_file" ]; then
echo "🔍 Checking $key_file ..."
if grep -lqiE "(miner|bot|backdoor|test)" "$key_file" 2>/dev/null; then
echo "⚠️ $key_file may contain suspicious public keys!"
fi
fi
doneScript 4: Audit system users to find "ghost accounts"
Attackers often create hidden users to maintain access. This script helps you identify them.
#!/bin/bash
# audit_users.sh - Audit non‑system users
echo "=== Non‑system users (UID >= 1000) ==="
awk -F: '$3 >= 1000 && $1 != "nobody" {print "User: " $1 " | Shell: " $7}' /etc/passwd
echo -e "
=== Users without passwords ==="
passwd -S | awk '$2 == "NP" {print "No‑password user: " $1}'
echo -e "
=== Login‑capable users (shell not nologin/false) ==="
awk -F: '($3 >= 1000) && ($7 !~ /nologin|false/) {print "Loginable user: " $1}' /etc/passwdReal case: We once discovered a user named support with /bin/bash as its shell, whose origin was unknown—exactly an attacker‑left backdoor.
Script 5: Daily SSH security report (DingTalk version)
#!/bin/bash
# daily_ssh_report.sh - Generate daily SSH security report and send via DingTalk
# Configuration
DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"
HOSTNAME=$(hostname)
DATE=$(date '+%Y-%m-%d %H:%M:%S')
REPORT_FILE="/tmp/ssh_security_report_$(date +%Y%m%d).txt"
{
echo "Recent 24‑hour failed login count:"
FAIL_COUNT=$(lastb | wc -l)
echo "$FAIL_COUNT"
echo -e "
Top attacking IPs (TOP 5):"
TOP_IPS=$(grep "Failed password" /var/log/secure* 2>/dev/null | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -5 | sed 's/^/ - /')
echo "$TOP_IPS"
echo -e "
Current SSH configuration risks:"
RISK_OUTPUT=$(/opt/scripts/check_ssh_security.sh 2>&1)
echo "$RISK_OUTPUT"
} > "$REPORT_FILE"
# Build DingTalk markdown payload
MESSAGE=$(cat <<EOF
{
"msgtype": "markdown",
"markdown": {
"title": "【SSH安全日报】$HOSTNAME",
"text": "### 🔒 【SSH安全日报】$HOSTNAME
**时间**:$DATE
**🚨 最近24小时失败登录次数**:$FAIL_COUNT 次
**🔥 高频攻击 IP(TOP 5)**:
$TOP_IPS
**⚠️ SSH 配置风险检查**:
\`
$RISK_OUTPUT
\`"
},
"at": {"isAtAll": false}
}
EOF
)
curl -s -X POST "$DINGTALK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "$MESSAGE" > /dev/null
# Keep report files for 7 days
find /tmp -name "ssh_security_report_*.txt" -mtime +7 -deleteAdd to crontab to run each morning:
0 9 * * * /opt/scripts/daily_ssh_report.sh3 Principles to Guard the SSH Door
Never use password login → enforce SSH keys
Never allow direct root login → use regular user + sudo
Never trust any public IP → restrict sources + auto‑ban
Conclusion
The line " 74744 failed login attempts " is not just a log entry; it’s the attacker’s footprints at your doorstep. Don’t wait for the 74745 th success to regret. Spend 10 minutes today hardening your SSH – it’s far better than spending 10 hours tomorrow firefighting.
Xiao Liu Lab
An operations lab passionate about server tinkering 🔬 Sharing automation scripts, high-availability architecture, alert optimization, and incident reviews. Using technology to reduce overtime and experience to avoid major pitfalls. Follow me for easier, more reliable operations!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
