大多数入侵之所以发现得晚,是因为没人查看谁登录过。攻击者用偷来的密码登录,和您本人登录没有任何区别;除非您清楚哪些 IP 和密钥是自己的,否则日志看起来一切正常。本文介绍如何审计 Linux 服务器的 SSH 登录、将每次登录对应到具体密钥、检查宝塔面板登录记录,并在每次有人登录时发送 Telegram 消息,让陌生登录在几秒内就暴露出来。
who # interactive sessions
ss -tnp | grep sshd # every SSH connection, including tunnels without a terminal
ssh -N)不会打开终端,因此不会出现在 who 或 w 中。务必同时检查 ss -tnp 或 ps -ef | grep "sshd:"。如果有来自陌生 IP 的连接已经持续了好几天,这是严重的危险信号。last -a | head -30 # successful logins (from /var/log/wtmp)
lastb -a | head -30 # failed logins (from /var/log/btmp)
# Successful SSH logins with the method used (password or key)
grep "Accepted" /var/log/auth.log | tail -50 # Debian / Ubuntu
grep "Accepted" /var/log/secure | tail -50 # Rocky / AlmaLinux / CentOS
journalctl -u ssh -u sshd --since "7 days ago" | grep Accepted
每一行都会显示登录方式、用户、来源 IP,密钥登录还会显示密钥指纹:
Accepted password for root from 198.51.100.7 port 51234 ssh2
Accepted publickey for root from 203.0.113.20 port 60122 ssh2: ED25519 SHA256:Xq3vR8...
汇总统计,即可一目了然地看到所有来源 IP 和登录方式:
grep -h "Accepted" /var/log/auth.log* 2>/dev/null | awk '{for(i=1;i<=NF;i++){if($i=="Accepted")m=$(i+1); if($i=="from")ip=$(i+1)}; print m, ip}' | sort | uniq -c | sort -rn
如果您只使用密钥登录,那么任何 Accepted password 记录都值得怀疑。
列出所有可登录密钥的指纹,再与日志中的指纹进行比对:
ssh-keygen -lf /root/.ssh/authorized_keys
for h in /home/*; do [ -f "$h/.ssh/authorized_keys" ] && ssh-keygen -lf "$h/.ssh/authorized_keys"; done
每个密钥都应带有您认识的注释(例如 alice@laptop)。删除无法解释来源的密钥,并检查是否有人通过 /etc/ssh/sshd_config 中的 AuthorizedKeysFile 添加了额外的密钥文件。
在宝塔面板中,首页会显示最近的面板登录,日志 → 面板日志 中列出了每次登录及其 IP。也可以通过命令行读取:
sqlite3 /www/server/panel/data/default.db \
"select addtime, log from logs where type in ('Login','用户登录') order by id desc limit 30"
发现登录记录后,在同一日志中检查该会话做了什么(上传、下载文件,计划任务,新建网站)。然后在 设置 中开启 Google 身份验证器(两步验证),设置安全入口,并尽可能按 IP 限制面板访问,具体请参阅 宝塔面板安全加固指南。
通过 @BotFather 创建一个 Telegram 机器人,给它发一条消息,然后按照 AI 服务器日报指南 获取您的 chat ID。接着创建告警脚本:
cat > /usr/local/bin/ssh-login-alert.sh <<'EOF'
#!/bin/bash
# Called by PAM when an SSH session opens: send user, source IP and host to Telegram
[ "$PAM_TYPE" = "open_session" ] || exit 0
TG_TOKEN="123456789:replace-with-your-bot-token"
TG_CHAT="123456789"
TEXT="SSH login on $(hostname): user=${PAM_USER} from=${PAM_RHOST} at $(date '+%F %T %Z')"
curl -s -m 5 "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
--data-urlencode "chat_id=${TG_CHAT}" --data-urlencode "text=${TEXT}" >/dev/null 2>&1 &
exit 0
EOF
chmod 700 /usr/local/bin/ssh-login-alert.sh
将其接入 PAM,使每个 SSH 会话(包括密钥登录和隧道)都会触发它:
echo "session optional pam_exec.so /usr/local/bin/ssh-login-alert.sh" >> /etc/pam.d/sshd
grep -i "^UsePAM" /etc/ssh/sshd_config # must be "UsePAM yes" (the default on most systems)
保持当前 SSH 窗口不要关闭,从第二个窗口登录,确认能收到 Telegram 消息。optional 关键字和 5 秒超时可以确保 Telegram 故障时绝不会影响您登录。
journalctl -u ssh -n 20 读取该用户最新的 Accepted 记录。脚本要尽量简短:每次登录都会运行它。非交互式会话(scp、sftp、隧道)通常不会写入 wtmp。SSH 日志和上面的 PAM 告警可以捕获这些登录。
检查 /var/log/auth.log.1 和压缩的 .gz 文件(zgrep Accepted /var/log/auth.log*)。如需更长时间的历史,请将日志发送到另一台服务器,或保留 Telegram 告警消息。
按照以上步骤操作后仍未解决?请 提交工单,IMIDC 7×24 小时技术团队将为您处理。请附上服务器 IP 和可疑的日志记录,以便我们更快定位问题。