Skip to content
SSH Security

How to Detect SSH Brute-Force Attacks on Your Linux Server

7 min read

Right now, someone is trying to guess the password to your server. If your machine has a public IP and SSH exposed on port 22, brute-force attempts are not a question of "if" but "how many per hour." Most server operators discover this only after something goes wrong.

This guide explains what SSH brute-force attacks are, how to check if your server is under attack right now, why the standard defenses have real gaps, and how to close those gaps with automated detection and response.

What is an SSH brute-force attack?

An SSH brute-force attack is straightforward: an attacker connects to your server's SSH port and tries username/password combinations until one works. The tools are free, the IP lists are public, and botnets run these scans at industrial scale 24/7.

A typical attack sends hundreds of login attempts per minute. Each failure is logged by your system, but unless something is watching those logs in real time, the attacker gets unlimited tries.

Are you being attacked right now?

Open a terminal on your server and run this:

grep "Failed password" /var/log/auth.log | tail -20

If you see output, those are real login failures. Look at the IP addresses. If the same IP appears dozens of times, that is an active brute-force attack. On most public-facing servers, this list is never empty.

To see a count of the worst offenders:

grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10

The numbers are usually worse than you expect.

Why fail2ban alone is not enough

fail2ban is a solid tool and a good first step. It watches log files, matches lines against regex patterns, and bans IPs after a threshold. But there are specific limitations that matter in production:

  • Regex-only detection - fail2ban matches log lines with regular expressions. It does not understand context, correlation across sources, or attack patterns that span multiple services.
  • No confidence scoring - every match is treated equally. There is no way to weigh the severity of an attack or factor in threat intelligence data before blocking.
  • No audit trail - fail2ban bans IPs, but there is no structured record of why, what evidence was used, and what the outcome was. When something breaks, tracing back to the root cause is manual.
  • No notifications - out of the box, fail2ban does not push alerts to Telegram, Slack, or webhooks. You learn about attacks when you remember to check.
  • No threat intelligence sharing - fail2ban blocks an IP on your server but does nothing to warn the rest of the internet. The attacker moves to the next target.

fail2ban is not broken. It just solves a narrower problem than most people assume.

How Inner Warden detects SSH brute-force

Inner Warden's sensor daemon reads /var/log/auth.log (or journald on systemd hosts) in real time. The ssh_bruteforce detector tracks failed login attempts per IP in a sliding time window:

Threshold8 failed logins from the same IP
Window300 seconds (5 minutes)
AI confidenceScored per incident (0.0 to 1.0)
ResponseFirewall deny rule (ufw, iptables, nftables, or pf)
CleanupAutomatic after TTL (default 24 hours)

When the threshold is crossed, the sensor creates a structured incident. The agent picks it up, enriches it with AbuseIPDB data and GeoIP, runs it through the AI provider for confidence scoring, and executes the appropriate firewall skill. Every step is recorded in the JSONL audit trail.

Real example from production

On our production server, the IP 203.0.113.19 started sending SSH login attempts. Inner Warden detected it, scored it, and blocked it in under 4 seconds. Here is what the detection-to-response chain looked like:

detect8 failed SSH logins from 203.0.113.19 in 47 seconds
enrichAbuseIPDB confidence: 100% | GeoIP: DigitalOcean, NL
decideAI confidence 0.95 | recommend block-ip-ufw
blockFirewall deny rule added | TTL 24 hours | Telegram alert sent

Total elapsed time from first failed login to firewall block: 3.8 seconds. The operator received a Telegram notification with full context and took no manual action.

Set it up in 60 seconds

Install Inner Warden. SSH brute-force detection is enabled by default. The sensor starts watching auth logs immediately.

Install
curl -fsSL https://innerwarden.com/install | sudo bash

Then enable automated IP blocking:

Enable blocking
innerwarden enable block-ip

Everything starts in dry-run mode. The system detects and logs, but does not block until you explicitly disable dry-run. Review the decisions first, then go live when you trust the output.

Verify it works
innerwarden status

What to do next

SSH brute-force detection is one layer. Once it is running, consider adding:

  • An SSH honeypot - capture attacker commands and credentials on a fake shell to learn what they are after.
  • Threat intelligence sharing - report blocked IPs to AbuseIPDB and push blocks to Cloudflare WAF so other servers benefit too.
  • AI-powered triage - add an AI provider (OpenAI, Anthropic, Groq, Ollama, and others) for confidence-scored decisions instead of threshold-only blocking.