Auditing who reached a host—and whether SSH authentication succeeded or failed—usually means combining two kinds of evidence: session records (who opened a login session and when) and auth records (what sshd and PAM wrote about each attempt). On Linux the first often comes from last and wtmp; the second from login logs such as /var/log/auth.log or /var/log/secure, or from journalctl for the SSH unit.
Tested the commands below on Ubuntu 25.04, kernel 6.14.0-37-generic, OpenSSH log lines from
journalctland/var/log/auth.log(2026-06-13). Adjust paths if you run RHEL, AlmaLinux, or Fedora (/var/log/secure).
Linux view login history with last and who
last reads /var/log/wtmp and shows recent logins, terminals, remote hosts, and session start times. It answers “who logged in successfully recently?” without reading sshd line by line.
last -n 20Example (hostnames and times are from this environment):
golinuxcloud pts/0 2026-06-13 10:35 still logged in 10.0.2.2lastb lists bad login attempts from /var/log/btmp (often root-only). who and w show who is on the system right now.
These tools are quick for a high-level view of successful sessions; they do not replace auth logs or the journal when you need every failed password attempt.
Linux login log files: auth.log, secure, and journalctl
Where login history is stored depends on the distro and logging stack:
- Debian / Ubuntu:
/var/log/auth.log(rotated files may beauth.log.1,auth.log.*.gz). - RHEL / AlmaLinux / Rocky / Fedora:
/var/log/secure. - systemd:
journalctlcan show the same events even when you prefer not to open files directly.
To follow SSH logins live:
sudo journalctl -u ssh -fOn some installs the unit is sshd instead of ssh.
Recent lines look like this (timestamps and keys shortened):
Jun 13 10:35:55 server1 sshd-session[21026]: Accepted password for golinuxcloud from 10.0.2.2 port 61787 ssh2
Jun 13 10:35:55 server1 sshd-session[21026]: pam_unix(sshd:session): session opened for user golinuxcloud(uid=1000) by golinuxcloud(uid=0)RFC3339-style lines in auth.log are common on newer Debian/Ubuntu:
2026-06-07T14:27:41.146672+05:30 server1 sshd-session[14020]: Accepted publickey for golinuxcloud from 10.0.2.2 port 54547 ssh2: RSA SHA256:…For filtering concepts across services, see view logs with journalctl.
sshd_config.
Linux check login history with grep
Typical auth log lines for SSH failures and successes look like:
sshd[3217]: Failed password for root from 192.168.0.102 port 53720 ssh2
sshd[4881]: Accepted password for root from 192.168.0.106 port 49920 ssh2Examples below use auth.log; substitute secure on the RHEL family:
sudo grep 'Failed password' /var/log/auth.log | tail -n 5
sudo grep -E 'Accepted (password|publickey|keyboard-interactive)' /var/log/auth.log | tail -n 5Or via the journal:
sudo journalctl -u ssh --since "today" | grep -E 'Failed password|Accepted 'Tighten scope to one user:
sudo grep 'sshd' /var/log/auth.log | grep 'golinuxcloud' | tail -n 20Script: summarize SSH attempts from a log file
Below is an updated version of the original idea: it defaults to /var/log/secure for RHEL-style layouts, but you can pass /var/log/auth.log as the first argument on Debian/Ubuntu. It expects plain log text (not binary wtmp), similar to what grep would read from those files.
Changes worth noting:
- Uses
grep -Einstead of deprecatedegrep. - Extracts the username by finding the word
for(works for both classic syslog and RFC3339 timestamps, unlike a fixed$(NF-5)field index). - Calls
host "$ip"without hard-coding a public resolver.
#!/usr/bin/env bash
# intruder_detect.sh — summarize SSH success/failure lines from auth log text
set -euo pipefail
AUTHLOG=/var/log/secure
if [[ -n ${1-} ]]; then
AUTHLOG=$1
echo "Using log file: $AUTHLOG"
fi
if [[ ! -r $AUTHLOG ]]; then
echo "Cannot read $AUTHLOG (try sudo or pass a copied log)." >&2
exit 1
fi
FAILED_LOG=$(mktemp)
SUCCESS_LOG=$(mktemp)
trap 'rm -f "$FAILED_LOG" "$SUCCESS_LOG"' EXIT
grep -hE 'Failed password' "$AUTHLOG" >"$FAILED_LOG" || true
grep -hE 'Accepted (password|publickey|keyboard-interactive)' "$AUTHLOG" >"$SUCCESS_LOG" || true
extract_user() {
awk '
/Failed password/ { for (i = 1; i < NF; i++) if ($i == "for") { print $(i + 1); next } }
/Accepted (password|publickey|keyboard-interactive)/ { for (i = 1; i < NF; i++) if ($i == "for") { print $(i + 1); next } }
'
}
failed_users=$(extract_user <"$FAILED_LOG" | sort -u)
success_users=$(extract_user <"$SUCCESS_LOG" | sort -u)
failed_ip_list=$(grep -hoE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' "$FAILED_LOG" | sort -u)
success_ip_list=$(grep -hoE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' "$SUCCESS_LOG" | sort -u)
printf '%-10s|%-12s|%-10s|%-15s|%-20s|%s\n' "Status" "User" "Attempts" "IP address" "Host" "Time range"
for ip in $failed_ip_list; do
for user in $failed_users; do
attempts=$(grep -F "$ip" "$FAILED_LOG" | grep -F " $user " | wc -l)
attempts=${attempts//[[:space:]]/}
if [[ "$attempts" -ne 0 ]]; then
first_time=$(grep -F "$ip" "$FAILED_LOG" | grep -F " $user " | head -1 | cut -c1-24)
time=$first_time
if [[ "$attempts" -gt 1 ]]; then
last_time=$(grep -F "$ip" "$FAILED_LOG" | grep -F " $user " | tail -1 | cut -c1-24)
time="$first_time -> $last_time"
fi
HOST=$(host "$ip" 2>/dev/null | tail -1 | awk '{ print $NF }')
printf '%-10s|%-12s|%-10s|%-15s|%-20s|%s\n' "Failed" "$user" "$attempts" "$ip" "${HOST:-?}" "$time"
fi
done
done
for ip in $success_ip_list; do
for user in $success_users; do
attempts=$(grep -F "$ip" "$SUCCESS_LOG" | grep -F " $user " | wc -l)
attempts=${attempts//[[:space:]]/}
if [[ "$attempts" -ne 0 ]]; then
first_time=$(grep -F "$ip" "$SUCCESS_LOG" | grep -F " $user " | head -1 | cut -c1-24)
time=$first_time
if [[ "$attempts" -gt 1 ]]; then
last_time=$(grep -F "$ip" "$SUCCESS_LOG" | grep -F " $user " | tail -1 | cut -c1-24)
time="$first_time -> $last_time"
fi
HOST=$(host "$ip" 2>/dev/null | tail -1 | awk '{ print $NF }')
printf '%-10s|%-12s|%-10s|%-15s|%-20s|%s\n' "Success" "$user" "$attempts" "$ip" "${HOST:-?}" "$time"
fi
done
doneMake it executable (chmod u+x intruder_detect.sh), then run ./intruder_detect.sh on RHEL-style hosts or ./intruder_detect.sh /var/log/auth.log on Debian/Ubuntu (often needs sudo for read permission). The table layout matches the original article’s intent: user, attempt counts, IPv4, forward/reverse DNS hint, and a coarse time range.
The script only matches IPv4 addresses in the summary table. IPv6 logins happen; extending the script would mean replacing the IPv4-only grep -hoE pattern with something that also captures IPv6 literals.
For ad-hoc awk on log columns, see awk examples.
Summary
Linux login history spans both session records (last, who, wtmp) and authentication traces (auth.log or /var/log/secure, plus journalctl for SSH). For a quick view of recent successful logins, start with last. To inspect denied passwords and accepted keys or passwords, grep the sshd lines or use journalctl -u ssh (or -u sshd). The script section is optional: it aggregates repeated IPv4 + user pairs from a text auth log; use grep and journalctl first if you only need a fast snapshot.

