Failed Login Monitor
Monitor and analyze failed SSH login attempts
Table of Contents
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
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
234fiUsage
Basic Monitoring
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 7Focused Analysis
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 20Cron Mode
1# Minimal output, only alerts if failures found
2sudo ./failed-login-monitor.sh -cWhat It Does
- Detects Log Location: Automatically finds auth.log or secure log
- Filters Time Range: Analyzes specified time period
- Extracts Failures: Identifies failed SSH login attempts
- Aggregates by IP: Counts attempts from each IP address
- Aggregates by User: Counts attempts for each username
- Provides Recommendations: Suggests security actions based on volume
Cron Setup
Hourly Monitoring
Add to root’s crontab:
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.comDaily Summary
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.comAlert on High Volume
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.txtSecurity Recommendations
If High Failures Detected
Install fail2ban:
1sudo apt install fail2ban # Debian/Ubuntu 2sudo yum install fail2ban # CentOS/RHELDisable Password Authentication:
1# In /etc/ssh/sshd_config 2PasswordAuthentication no 3ChallengeResponseAuthentication noChange SSH Port:
1# In /etc/ssh/sshd_config 2Port 2222 # Use any port > 1024Use SSH Keys Only:
1# Generate key pair 2ssh-keygen -t ed25519 3# Copy to server 4ssh-copy-id user@server
Log File Locations
| Distribution | Log 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
| Code | Meaning |
|---|---|
| 0 | Success, low failures (<10) |
| 1 | Moderate failures (10-100) |
| 2 | High failures (>100) |
Integration with Monitoring
Send to Syslog
1./failed-login-monitor.sh -c | logger -t failed-login-monitorExport to JSON
1# Modify script to output JSON for parsing
2./failed-login-monitor.sh --json > /var/log/failed-logins.jsonIntegration with ELK Stack
Forward alerts to Elasticsearch for centralized monitoring and analysis.
See Also
Download
1curl -O https://glyph.sh/scripts/failed-login-monitor.sh
2chmod +x failed-login-monitor.sh
3sudo ./failed-login-monitor.sh