Security Alerts
Get security alerts for failed SSH logins, root and sudo access, and application auth events, routed by priority to Slack, email, or SMS.
Security events need fast response. Route login failures, SSH access, and suspicious activity through Alphorn so the right people are alerted immediately.
Failed SSH login attempts
Count failed SSH logins from the systemd journal (the SSH unit is ssh on Debian/Ubuntu and sshd on RHEL/Fedora, so the script asks for both). Run it every 10 minutes from root's crontab (reading the journal needs root or the systemd-journal group):
#!/bin/bash
WEBHOOK="https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT"
ALPHORN_API_KEY="${ALPHORN_API_KEY:-alp_...}" # or export it in the environment
HOSTNAME=$(hostname)
THRESHOLD=5
# Failed attempts in the last 10 minutes
LINES=$(journalctl -u ssh -u sshd --since "10 min ago" -o cat --no-pager | grep "Failed password")
FAILURES=$(printf '%s' "$LINES" | grep -c "Failed password")
if [ "$FAILURES" -ge "$THRESHOLD" ]; then
# "Failed password for <user> from <ip> port <port> ssh2": the IP is 4th from the end
SOURCES=$(printf '%s\n' "$LINES" | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -5)
# jq escapes the multi-line source list into valid JSON
jq -n --arg h "$HOSTNAME" --arg f "$FAILURES" --arg s "$SOURCES" \
'{title: "SSH brute force detected: \($h)", message: "\($f) failed login attempts in the last 10 minutes.\n\nTop sources:\n\($s)", priority: 5, tags: ["security", "ssh", "brute-force", $h]}' |
curl -fsS -X POST "$WEBHOOK" \
-H "Authorization: Bearer $ALPHORN_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @-
fiThis counts password failures. On servers with PasswordAuthentication no, attackers show up as "Invalid user" or "Connection closed by authenticating user" instead - add those patterns to the grep (e.g. grep -E "Failed password|Invalid user") and adjust the IP extraction.
Successful root/sudo access
Alert whenever someone starts a root login shell (direct root login, su -, sudo -i). Files in /etc/profile.d are sourced by login shells (on RHEL/Fedora also by interactive shells like sudo -s), so a plain sudo cmd doesn't trigger it. Store the key in a root-only file first:
echo 'ALPHORN_API_KEY=alp_...' | sudo tee /etc/alphorn.env >/dev/null
sudo chmod 600 /etc/alphorn.env/etc/profile.d is sourced by sh, so the script sticks to POSIX syntax:
if [ "$(id -u)" -eq 0 ] && [ -r /etc/alphorn.env ]; then
. /etc/alphorn.env
curl -fsS -X POST "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT" \
-H "Authorization: Bearer $ALPHORN_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Root login: $(hostname)\",
\"message\": \"User: $(logname 2>/dev/null), IP: ${SSH_CLIENT%% *}, TTY: $(tty)\",
\"priority\": 4,
\"tags\": [\"security\", \"root-login\", \"$(hostname)\"]
}" >/dev/null 2>&1 &
fiApplication-level auth alerts
Notify on suspicious login patterns from your app:
async function onLoginFailed(email, ip, attempts) {
if (attempts >= 5) {
await fetch("https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ALPHORN_API_KEY}`,
},
body: JSON.stringify({
title: `Account lockout: ${email}`,
message: `${attempts} failed login attempts from IP ${ip}`,
priority: 4,
tags: ["security", "login-failed", "lockout"],
}),
});
}
}
async function onLoginFromNewDevice(user, device, ip, location) {
await fetch("https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.ALPHORN_API_KEY}`,
},
body: JSON.stringify({
title: `New device login: ${user.email}`,
message: `Device: ${device}\nIP: ${ip}\nLocation: ${location}`,
priority: 3,
tags: ["security", "new-device"],
}),
});
}Routing examples
| Channel | Filter | Purpose |
|---|---|---|
| PagerDuty | Tags has any of brute-force | Immediate response to attacks |
| Slack (#security) | Tags has any of security | All security events |
| Telegram | Tags has any of root-login | Personal alert for root access |
Tags has any of security | Audit trail |