On Debian and Ubuntu, sshd writes to /var/log/auth.log. On RHEL, Rocky, Alma and CentOS Stream, it writes to /var/log/secure. If neither file exists, your system logs only to the systemd journal. Then the command you want is journalctl -u ssh on Debian-based systems, or journalctl -u sshd on the Red Hat family, Fedora and Arch.
SSH is the front door of nearly every Linux server, which is why its logs show up in almost every Linux investigation. Prime Core has 11,632 sigma rules that run on Linux authentication logs like these, mapped to 643 MITRE ATT&CK techniques. Among them: brute force (T1110), logins with stolen credentials (T1078) and lateral movement over SSH (T1021.004). Below is how to find your sshd logs, export them, sanitize them, and investigate them for free in LogTotal.
TL;DR
| Distro family | Log file | Journal command |
|---|---|---|
| Debian, Ubuntu | /var/log/auth.log | journalctl -u ssh |
| RHEL, Rocky, Alma, CentOS Stream | /var/log/secure | journalctl -u sshd |
| Fedora, Arch, minimal installs without rsyslog | none by default | journalctl -u sshd |
Before you start: can you actually read them?
Authentication logs are locked down on purpose, so a regular account usually gets nothing back.
- /var/log/auth.log on Debian and Ubuntu is readable by root and the adm group.
- /var/log/secure on the Red Hat family is root-only.
- The system journal is open to root and to members of systemd-journal, adm and wheel. Everyone else sees only their own user journal. If you run journalctl -u ssh as a normal user, you get an empty result and a hint that you’re not seeing messages from other users. That’s permissions, not a quiet server.
The simple fix is sudo. If you’re setting up an account just for log collection, add it to adm or systemd-journal instead of handing out root. Our [Linux log permissions guide] covers the details.
One more thing before you copy anything off a server: auth logs contain usernames, source IPs and hostnames. LogTotal sanitizes uploads, but check your data-handling policy first.
Step 1: File, journal, or both?
The table above shows the defaults, and defaults are exactly what cloud images and hardening scripts like to change. Spend ten seconds checking:
ls -l /var/log/auth.log /var/log/secure 2>/dev/null
systemctl is-active rsyslog
If a file shows up and rsyslog is active, you have both: rsyslog copies messages out of the journal into a plain text file. Either source works, and the file is easier to copy around.
If there’s no file, rsyslog simply isn’t installed. That’s the norm now: since Debian 12, rsyslog is no longer part of a default install, so a fresh Debian box has no auth.log at all. Fedora and Arch have worked this way for years. Go to Step 2b.
While you’re here, check whether the journal survives a reboot:
ls -d /var/log/journal
If that directory doesn’t exist, journald keeps logs in memory under /run/log/journal, and a reboot wipes them. On a machine you’re investigating, that’s worth knowing before anyone decides to “just restart it.”
One more point for incident work: if the box might be compromised, its local logs might be too. Wiping or editing auth.log is a classic cleanup step. If the server forwards logs to a SIEM, export from there as well and compare. Our [Splunk export guide] shows how.
Step 2a: Export from auth.log or secure
The current file only covers the last few days. Older entries sit in rotated files next to it. On Debian and Ubuntu they look like auth.log.1, auth.log.2.gz, and so on. On the Red Hat family they carry a date, like secure-20261012. If the incident started last week, you need those too.
This pulls sshd lines from the current and rotated files, oldest first, into one file:
# Debian / Ubuntu
sudo zgrep -h ‘sshd’ $(ls -tr /var/log/auth.log*) > web-03_sshd.log
# RHEL / Rocky / Alma
sudo zgrep -h ‘sshd’ $(ls -tr /var/log/secure*) > web-03_sshd.log
zgrep reads plain and gzipped files alike, so you don’t have to unpack anything. ls -tr sorts by modification time, so the oldest file goes first and the output stays roughly chronological. The sshd pattern also matches sshd-session, which matters on newer systems (see Step 2b).
Why filter at all? Besides cutting noise, auth.log also records sudo, and sudo lines include full command lines. People do occasionally type passwords into those. Filtering to sshd keeps that out of the file. The exception is when you’re investigating what an attacker did after logging in. Then you want the sudo lines too: take the whole file and let LogTotal’s sanitize step do its job.
Quick check before you upload:
wc -l web-03_sshd.log
head -3 web-03_sshd.log; tail -3 web-03_sshd.log
Look at the timestamps. Depending on the rsyslog config, they’re either classic syslog (Oct 19 10:15:01, no year, no time zone) or full ISO 8601. If they’re classic, write down the server’s time zone (timedatectl). You’ll need it the moment you try to line these events up with anything else.
Step 2b: Export from the systemd journal
On journal-only systems, this is the whole job:
sudo journalctl -u ssh -u sshd \
–since “2026-10-19 00:00” –until “2026-10-20 00:00” \
–no-pager -o short-iso > web-03_sshd.log
A few choices in there are deliberate.
Both unit names. The service is called ssh on Debian and Ubuntu and sshd almost everywhere else. journalctl combines repeated -u flags with OR, so the same command works on both families.
Filter by unit, not by tag. A lot of older guides use journalctl -t sshd. Since OpenSSH 9.8, the server is split into two binaries, and per-connection messages, including logins, are tagged sshd-session instead of sshd. Filter by tag, and you lose exactly the lines you came for. This broke plenty of fail2ban and sshguard setups when 9.8 landed in 2024. RHEL 10 and other current releases ship OpenSSH 9.8 or newer. Filtering by unit catches every process that belongs to the service, whatever it’s called.
-o short-iso. It gives you a syslog-style line with a full ISO 8601 timestamp, year and time zone included, so you don’t have to guess. [PROOF: confirm LogTotal parses short-iso; if not, use the default -o short]. If you’d rather keep every journal field (PID, boot ID, hostname, and so on), use -o json, which writes one JSON object per line. The file gets bigger, but nothing is lost.
The time filters take relative values too: –since “2 hours ago” or –since yesterday. With a persistent journal, -b -1 limits output to the previous boot, which helps if the server was restarted after the incident.
Don’t copy the binary .journal files from /var/log/journal and upload those. LogTotal reads text and JSON, and journal files are neither.
What to look for once you have the file
Most sshd lines fall into a handful of patterns:
| Line starts with | What it means |
|---|---|
| Failed password for <user> from <ip> | Wrong password for an account that exists |
| Invalid user <user> from <ip> | The username doesn’t exist; typical of scanners cycling through admin, test, oracle |
| Accepted password for / Accepted publickey for | A successful login; the publickey line also carries the key’s SHA256 fingerprint |
| Disconnecting authenticating user … Too many authentication failures | The client hit MaxAuthTries |
| Connection closed by <ip> … [preauth] | Dropped before authentication, usually scanners and banner grabbers |
| pam_unix(sshd:session): session opened for user | The session actually started |
The sequence worth stopping for: a run of Failed or Invalid user lines from one IP, then an Accepted from the same IP. That’s a brute force that worked. Close behind: an Accepted for a service account, from an address nobody recognizes, at an hour nobody works.
On OpenSSH 9.8 and later you may also see messages about penalties: sshd now temporarily refuses addresses that keep failing authentication. Treat them as a symptom. They tell you someone was hammering the port, not that anyone got in.
Upload, sanitize and review in LogTotal
Reading a few hundred lines by eye is fine. Reading 40,000 isn’t. LogTotal is SOC Prime’s free service for running detection logic against a log file without setting anything up. It sanitizes what you upload and doesn’t keep your files.
- Upload. Open logtotal.com and drop in web-03_sshd.log. [PROOF: upload screen, size limit]
- Sanitize. [PROOF: what gets masked in an sshd log: usernames, source IPs, hostnames]. Look over the result before you go on. You know which of those names are sensitive in your environment.
- Review. [PROOF: what the results screen shows and how findings map to rules and ATT&CK techniques]
What this looks like on real data. We generated sshd logs on a throwaway test VM: [PROOF: what was simulated, e.g. an SSH password-spraying run against a few common usernames, ending in one successful login]. The file came out to [PROOF: N lines, X KB]. LogTotal flagged [PROOF: actual findings, matched rules, ATT&CK techniques].
Want to try it before touching your own servers? [PROOF: link to the sample file] is the same file we used above.
When it goes wrong
| Symptom | Likely cause | Fix |
|---|---|---|
| No such file or directory for auth.log | rsyslog isn’t installed (default since Debian 12) | Use journalctl, Step 2b |
| Permission denied on the log file | auth.log is adm-only, secure is root-only | sudo, or add the account to adm |
| journalctl -u ssh returns nothing, with a hint about other users | You’re not in systemd-journal, adm or wheel | sudo, or add the account to systemd-journal |
| journalctl -u ssh returns nothing as root | Wrong unit name for this distro | Use -u ssh -u sshd |
| Logouts are there, but no Accepted lines | Filtering by tag (-t sshd) on OpenSSH 9.8+ | Filter by unit instead |
| Nothing older than the last reboot | Volatile journal; no /var/log/journal | The old logs are gone; check your SIEM or log forwarder |
| The file only covers a few days | Rotated files weren’t included | Use the zgrep command with auth.log* or secure* |
| Timestamps don’t line up with other sources | Classic syslog timestamps carry no time zone | Note the server’s time zone, or export with -o short-iso |
FAQ
Where are SSH logs on Ubuntu?
On a standard Ubuntu install, in /var/log/auth.log, plus the journal (journalctl -u ssh). Minimal cloud images sometimes leave out rsyslog. If the file isn’t there, use the journal.
How do I see only failed SSH logins?
sudo journalctl -u ssh -u sshd | grep -E ‘Failed|Invalid user’ on journal systems, or sudo zgrep -hE ‘Failed|Invalid user’ /var/log/auth.log* when you have the file. For anything beyond a quick look, upload the full sshd log instead. The failures only mean something next to the successes.
What about last and lastb?
They read the binary login records in /var/log/wtmp and /var/log/btmp. They’re handy for a quick look at who logged in, but they carry far less detail than sshd’s own log, and they’re just as easy for an intruder to tamper with.
How do I collect sshd logs from several servers at once?
For a one-off, a loop does it: for h in web-01 web-02 web-03; do ssh “$h” “sudo journalctl -u ssh -u sshd –since yesterday -o short-iso –no-pager” > “${h}_sshd.log” done That needs sudo to work without a password prompt on the remote side. Otherwise, add -t to ssh and type it each time. If you’re doing this more than once a quarter, ship the logs to a SIEM instead.
Is it safe to upload sshd logs to an online tool?
LogTotal sanitizes uploads before analysis and doesn’t retain files; our [sanitization guide] shows what gets masked. Your organization’s data-handling rules still come first.
Next step
Got the file? Upload it to LogTotal and see what those 11,632 sigma rules make of it. If you want the same detection logic running on all your Linux servers, not one file at a time.
References
- Debian 12 release notes: changes to system logging
- OpenSSH 9.8 release notes: the sshd / sshd-session split and changed log tags
- journalctl(1): unit filters, output formats, journal access groups
- journald.conf(5): persistent vs. volatile storage