Monitor and analyze failed SSH login attempts from authentication logs.

Overview

This script parses authentication logs to identify failed SSH login attempts, showing statistics by IP address and username. It helps detect brute force attacks and unauthorized access attempts.

Use Case: Security monitoring, detecting brute force attacks, identifying compromised accounts.

Platform: Linux (all distributions), macOS Requirements: Root or sudo access to read auth logs Execution Time: Seconds (depends on log size)

The Script

Lang: bash
  1#!/bin/bash
  2#
  3# failed-login-monitor.sh
  4# Monitor and analyze failed SSH login attempts
  5#
  6# Author: glyph.sh
  7# Reference: https://glyph.sh/kb/linux-security/
  8#
  9
 10# Color codes
 11RED='\033[0;31m'
 12YELLOW='\033[1;33m'
 13GREEN='\033[0;32m'
 14CYAN='\033[0;36m'
 15NC='\033[0m' # No Color
 16
 17# Default values
 18HOURS=24
 19LOG_FILE=""
 20TOP_COUNT=10
 21SHOW_BY_IP=true
 22SHOW_BY_USER=true
 23CRON_MODE=false
 24
 25# Usage function
 26usage() {
 27    cat << EOF
 28Usage: $(basename "$0") [OPTIONS]
 29
 30Monitor and analyze failed SSH login attempts.
 31
 32OPTIONS:
 33    -h, --hours HOURS       Time range in hours (default: 24)
 34    -d, --days DAYS         Time range in days
 35    -f, --file FILE         Specify auth log file (auto-detect if not set)
 36    -t, --top COUNT         Show top N entries (default: 10)
 37    -i, --ip-only           Show only IP statistics
 38    -u, --user-only         Show only username statistics
 39    -c, --cron              Cron mode (minimal output, only if failures found)
 40    --help                  Show this help message
 41
 42EXAMPLES:
 43    $(basename "$0")                    # Last 24 hours
 44    $(basename "$0") -h 1               # Last hour
 45    $(basename "$0") -d 7               # Last 7 days
 46    $(basename "$0") -t 20              # Show top 20
 47    $(basename "$0") -i                 # IP statistics only
 48    $(basename "$0") -c                 # Cron mode
 49
 50EOF
 51    exit 1
 52}
 53
 54# Parse command line arguments
 55while [[ $# -gt 0 ]]; do
 56    case $1 in
 57        -h|--hours)
 58            HOURS="$2"
 59            shift 2
 60            ;;
 61        -d|--days)
 62            HOURS=$((${2} * 24))
 63            shift 2
 64            ;;
 65        -f|--file)
 66            LOG_FILE="$2"
 67            shift 2
 68            ;;
 69        -t|--top)
 70            TOP_COUNT="$2"
 71            shift 2
 72            ;;
 73        -i|--ip-only)
 74            SHOW_BY_USER=false
 75            shift
 76            ;;
 77        -u|--user-only)
 78            SHOW_BY_IP=false
 79            shift
 80            ;;
 81        -c|--cron)
 82            CRON_MODE=true
 83            shift
 84            ;;
 85        --help)
 86            usage
 87            ;;
 88        *)
 89            echo "Unknown option: $1"
 90            usage
 91            ;;
 92    esac
 93done
 94
 95# Auto-detect log file if not specified
 96if [ -z "$LOG_FILE" ]; then
 97    if [ -f "/var/log/auth.log" ]; then
 98        LOG_FILE="/var/log/auth.log"
 99    elif [ -f "/var/log/secure" ]; then
100        LOG_FILE="/var/log/secure"
101    else
102        echo -e "${RED}Error: Cannot find auth log file${NC}"
103        echo "Please specify with -f option"
104        exit 1
105    fi
106fi
107
108# Check if log file exists and is readable
109if [ ! -r "$LOG_FILE" ]; then
110    echo -e "${RED}Error: Cannot read log file: $LOG_FILE${NC}"
111    echo "Try running with sudo"
112    exit 1
113fi
114
115# Calculate time range
116TIME_RANGE=$(date -d "$HOURS hours ago" '+%Y-%m-%d %H:%M:%S' 2>/dev/null)
117if [ -z "$TIME_RANGE" ]; then
118    # macOS date command syntax
119    TIME_RANGE=$(date -v-${HOURS}H '+%Y-%m-%d %H:%M:%S' 2>/dev/null)
120fi
121
122# Create temporary file
123TEMP_FILE=$(mktemp)
124trap "rm -f $TEMP_FILE" EXIT
125
126# Extract failed login attempts
127grep -i "failed password\|authentication failure\|invalid user" "$LOG_FILE" > "$TEMP_FILE"
128
129# Count total failures
130TOTAL_FAILURES=$(wc -l < "$TEMP_FILE")
131
132# Exit early in cron mode if no failures
133if [ "$CRON_MODE" = true ] && [ "$TOTAL_FAILURES" -eq 0 ]; then
134    exit 0
135fi
136
137# Header (skip in cron mode)
138if [ "$CRON_MODE" = false ]; then
139    echo -e "${CYAN}========================================${NC}"
140    echo -e "${CYAN} Failed Login Monitor${NC}"
141    echo -e "${CYAN}========================================${NC}"
142    echo ""
143    echo -e "${YELLOW}Log File:${NC} $LOG_FILE"
144    echo -e "${YELLOW}Time Range:${NC} Last $HOURS hours"
145    echo -e "${YELLOW}Total Failed Attempts:${NC} $TOTAL_FAILURES"
146    echo ""
147fi
148
149# Show failures by IP address
150if [ "$SHOW_BY_IP" = true ]; then
151    if [ "$CRON_MODE" = false ]; then
152        echo -e "${CYAN}Top $TOP_COUNT IPs by Failed Attempts:${NC}"
153        echo "----------------------------------------"
154    else
155        echo "Failed Login Alert - Last $HOURS hours"
156        echo "Total Failures: $TOTAL_FAILURES"
157        echo ""
158        echo "Top IPs:"
159    fi
160
161    # Extract IPs and count
162    grep -oP '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' "$TEMP_FILE" | \
163        sort | uniq -c | sort -rn | head -n "$TOP_COUNT" | \
164        awk '{printf "  %-6s attempts from %s\n", $1, $2}'
165
166    echo ""
167fi
168
169# Show failures by username
170if [ "$SHOW_BY_USER" = true ]; then
171    if [ "$CRON_MODE" = false ]; then
172        echo -e "${CYAN}Top $TOP_COUNT Usernames by Failed Attempts:${NC}"
173        echo "----------------------------------------"
174    else
175        echo "Top Usernames:"
176    fi
177
178    # Extract usernames and count
179    grep -oP '(?<=user |for )[a-zA-Z0-9_-]+' "$TEMP_FILE" | \
180        sort | uniq -c | sort -rn | head -n "$TOP_COUNT" | \
181        awk '{printf "  %-6s attempts for user %s\n", $1, $2}'
182
183    echo ""
184fi
185
186# Show recent attempts (not in cron mode)
187if [ "$CRON_MODE" = false ] && [ "$TOTAL_FAILURES" -gt 0 ]; then
188    echo -e "${CYAN}Most Recent Failures (last 5):${NC}"
189    echo "----------------------------------------"
190    tail -n 5 "$TEMP_FILE" | while read line; do
191        echo "  $line"
192    done
193    echo ""
194fi
195
196# Summary and recommendations
197if [ "$CRON_MODE" = false ]; then
198    echo -e "${CYAN}========================================${NC}"
199    echo -e "${CYAN} Recommendations${NC}"
200    echo -e "${CYAN}========================================${NC}"
201    echo ""
202
203    if [ "$TOTAL_FAILURES" -gt 100 ]; then
204        echo -e "${RED}WARNING: High number of failed attempts detected!${NC}"
205        echo ""
206        echo "Recommended actions:"
207        echo "1. Review the IPs and consider blocking with fail2ban"
208        echo "2. Disable password authentication (use SSH keys only)"
209        echo "3. Change SSH port from default (22)"
210        echo "4. Enable two-factor authentication"
211        echo "5. Review and strengthen security policies"
212    elif [ "$TOTAL_FAILURES" -gt 10 ]; then
213        echo -e "${YELLOW}Moderate failed attempts detected${NC}"
214        echo ""
215        echo "Recommended actions:"
216        echo "1. Monitor these IPs for continued attempts"
217        echo "2. Consider implementing fail2ban"
218        echo "3. Review SSH security configuration"
219    else
220        echo -e "${GREEN}Low number of failed attempts - normal activity${NC}"
221        echo ""
222        echo "Continue monitoring regularly"
223    fi
224    echo ""
225fi
226
227# Exit with appropriate code
228if [ "$TOTAL_FAILURES" -gt 100 ]; then
229    exit 2
230elif [ "$TOTAL_FAILURES" -gt 10 ]; then
231    exit 1
232else
233    exit 0
234fi

Usage

Basic Monitoring

Lang: bash
1# Last 24 hours (default)
2sudo ./failed-login-monitor.sh
3
4# Last hour
5sudo ./failed-login-monitor.sh -h 1
6
7# Last 7 days
8sudo ./failed-login-monitor.sh -d 7

Focused Analysis

Lang: bash
1# Show only IP statistics
2sudo ./failed-login-monitor.sh -i
3
4# Show only username statistics
5sudo ./failed-login-monitor.sh -u
6
7# Show top 20 entries
8sudo ./failed-login-monitor.sh -t 20

Cron Mode

Lang: bash
1# Minimal output, only alerts if failures found
2sudo ./failed-login-monitor.sh -c

What It Does

  1. Detects Log Location: Automatically finds auth.log or secure log
  2. Filters Time Range: Analyzes specified time period
  3. Extracts Failures: Identifies failed SSH login attempts
  4. Aggregates by IP: Counts attempts from each IP address
  5. Aggregates by User: Counts attempts for each username
  6. Provides Recommendations: Suggests security actions based on volume

Cron Setup

Hourly Monitoring

Add to root’s crontab:

Lang: bash
1# Check for failed logins every hour
20 * * * * /usr/local/bin/failed-login-monitor.sh -c -h 1 | mail -s "Failed Login Alert" admin@example.com

Daily Summary

Lang: bash
1# Daily summary at 6 AM
20 6 * * * /usr/local/bin/failed-login-monitor.sh -d 1 | mail -s "Daily Failed Login Report" admin@example.com

Alert on High Volume

Lang: bash
1# Alert only if >50 failures in last hour
20 * * * * /usr/local/bin/failed-login-monitor.sh -c -h 1 || mail -s "HIGH Failed Login Activity" admin@example.com < /tmp/login-alert.txt

Security Recommendations

If High Failures Detected

  1. Install fail2ban:

    Lang: bash
    1sudo apt install fail2ban  # Debian/Ubuntu
    2sudo yum install fail2ban  # CentOS/RHEL
  2. Disable Password Authentication:

    Lang: bash
    1# In /etc/ssh/sshd_config
    2PasswordAuthentication no
    3ChallengeResponseAuthentication no
  3. Change SSH Port:

    Lang: bash
    1# In /etc/ssh/sshd_config
    2Port 2222  # Use any port > 1024
  4. Use SSH Keys Only:

    Lang: bash
    1# Generate key pair
    2ssh-keygen -t ed25519
    3# Copy to server
    4ssh-copy-id user@server

Log File Locations

DistributionLog File Location
Debian/Ubuntu/var/log/auth.log
CentOS/RHEL/var/log/secure
Arch Linux/var/log/auth.log
macOS/var/log/system.log

Exit Codes

CodeMeaning
0Success, low failures (<10)
1Moderate failures (10-100)
2High failures (>100)

Integration with Monitoring

Send to Syslog

Lang: bash
1./failed-login-monitor.sh -c | logger -t failed-login-monitor

Export to JSON

Lang: bash
1# Modify script to output JSON for parsing
2./failed-login-monitor.sh --json > /var/log/failed-logins.json

Integration with ELK Stack

Forward alerts to Elasticsearch for centralized monitoring and analysis.

See Also

Download

Lang: bash
1curl -O https://glyph.sh/scripts/failed-login-monitor.sh
2chmod +x failed-login-monitor.sh
3sudo ./failed-login-monitor.sh