shellsift
A local auth-log anomaly hunter — parses sshd and auth logs for brute-force patterns, off-hours logins, and impossible travel. Nothing leaves the box.
What it does
shellsift reads your auth logs locally — no agent, no cloud, no data shipping anywhere — and surfaces patterns that typically get buried in log noise:
- Brute-force detection — N failed login attempts from a single source IP within a sliding time window (configurable, default: 5 attempts within 60 seconds)
- Off-hours logins — successful logins during a defined out-of-hours window (configurable per host)
- Impossible travel — a source IP appearing in two geographically distant locations within too short a time window to be real (uses offline GeoLite2 — no external API calls)
- Per-IP summary — total attempts, first/last seen, resolved ASN, and a risk-level indicator
Everything runs from a single binary against log files on disk. Useful in three contexts: post-incident review, regular health checks of internet-facing SSH hosts, and as a lightweight IDS component that doesn’t require a full SIEM.
Installation
Download the latest binary from the GitHub releases page and drop it anywhere in your PATH:
curl -Lo shellsift https://github.com/compilelog/shellsift/releases/latest/download/shellsift-linux-amd64
chmod +x shellsift
sudo mv shellsift /usr/local/bin/
Or build from source (requires Go 1.22+):
git clone https://github.com/compilelog/shellsift
cd shellsift
go build -o shellsift .
GeoLite2 database (required for impossible-travel detection only):
shellsift --download-geodb # downloads to ~/.config/shellsift/GeoLite2-City.mmdb
Usage
Analyse a single log file and print a report:
shellsift /var/log/auth.log
JSON output for piping:
shellsift /var/log/auth.log --format json | jq '.brute_force | .[] | select(.attempts > 20)'
Analyse multiple files (e.g. rotated logs):
shellsift /var/log/auth.log /var/log/auth.log.1 /var/log/auth.log.2.gz
Configure thresholds with a config file:
# ~/.config/shellsift/config.yaml
brute_force:
threshold: 10
window: 120s
off_hours:
start: "22:00"
end: "06:00"
timezone: "Europe/London"
Run shellsift with the config explicitly:
shellsift --config ~/.config/shellsift/config.yaml /var/log/auth.log
Licence: MIT — See the GitHub repository for full licence text.
Changelog
- Added impossible-travel detection using offline GeoLite2 lookups — flags source IPs that appear in geographically impossible sequence within a configurable time window
- Off-hours detection window is now configurable per-host via config file (was hardcoded 22:00–06:00)
- Report output now includes per-IP summary table with total attempts, first/last seen, and ASN
- JSON output mode added (`--format json`) — pipe into jq, SIEM, or anything else
- Brute-force threshold is now configurable per source IP range (`--threshold 5` globally or per CIDR block in config)
- Fixed edge case where IPv6-mapped IPv4 addresses (`::ffff:x.x.x.x`) were counted as separate source IPs from their IPv4 equivalents
- Initial release — sshd `auth.log` parsing, basic brute-force detection (N failed attempts from same IP within window)
- Plaintext and Markdown report output
- Single static binary, no dependencies beyond the log file