Telegram VPS 登录告警
这套配置在 Ubuntu VPS 的会话成功建立后调用 Telegram Bot API。通知包含主机名、登录用户、认证方式、来源 IP、IP 归属地和北京时间。它用来发现异常登录,不会阻止攻击者,也不能代替密钥登录和云平台 MFA
准备信息
-
获取Telegram Bot Token
在 Telegram 全局搜索框中输入:@BotFather

进入对话框输入
/start后输入/newbot开始新建机器人系统提示
Alright, a new bot. How are we going to call it? Please choose a name for your bot.开始设置显示名称,我这里设置的是“vps登录告警”
命名规则:支持中文、空格、特殊符号
回车确认后系统提示
Good. Now let's choose a username for your bot. It must end in bot. Like this, for example: TetrisBot or tetris_bot.接下来设置唯一寻址名,这是机器人的“全局唯一标识符”
命名规则:纯英文字母、数字或下划线的组合,且必须以 bot 结尾(例:vps_login_warning_bot)。全局唯一,不能与他人重复
创建完毕后会返回给你一串API Token,这个要自己保存好不要泄露给任何人最后为了防止机器人被随意拉入别人群聊 在
@BotFather对话框中发送/mybot
点击我们刚刚创建的机器人依次点击:Bot Settings -> Allow Groups?->Turn groups off
-
获取自己的 Telegram
chat_id在 Telegram 全局搜索框中输入:@userinfobot
在对话框中输入指令
请务必核对精确匹配的用户名 /start
返回的第一行就是自己的chat_id
BOT_TOKEN 等同于机器人的控制凭据。不要把它写入聊天记录、Shell 历史、截图或公开仓库
保存凭据
以下命令假定当前 Shell 具有 root 权限;否则在每条命令前加 sudo
先创建仅 root 可读的配置文件:
install -m 600 /dev/null /etc/ssh-login-telegram.env然后通过标准输入写入凭据:
tee /etc/ssh-login-telegram.env粘贴以下两行,将示例值替换为实际值。按 Enter 后按 Ctrl+D 保存:
BOT_TOKEN=replace-with-your-bot-tokenCHAT_ID=123456一旦 Bot Token 出现在公开位置,应立即在 @BotFather 中撤销并生成新 Token,然后更新服务器上的文件
创建通知脚本
创建脚本文件:
tee /usr/local/sbin/ssh-login-telegram粘贴以下内容,按 Enter 后按 Ctrl + D。脚本只处理会话打开事件;IP 归属地查询失败时仍会发送告警,只是地点显示为“未知”
#!/bin/sh
[ "$PAM_TYPE" = "open_session" ] || exit 0[ -r /etc/ssh-login-telegram.env ] || exit 0
. /etc/ssh-login-telegram.env
auth_method='未知'auth_line=$( /usr/bin/awk -v user="$PAM_USER" -v ip="$PAM_RHOST" ' index($0, "Accepted ") && index($0, " for " user " from " ip " ") { latest=$0 } END { print latest } ' /var/log/auth.log 2>/dev/null)
case "$auth_line" in *"Accepted publickey "*) auth_method='SSH 密钥登录' ;; *"Accepted password "*) auth_method='密码登录' ;; *"Accepted keyboard-interactive"*) auth_method='交互式认证' ;;esac
source_ip=${PAM_RHOST:-local}location='未知'
case "$source_ip" in *[!0-9a-fA-F:.]*|'') ;; *) resolved_location=$( /usr/bin/python3 -c 'import json,sys,urllib.request; ip=sys.argv[1]; d=json.load(urllib.request.urlopen("https://ipwho.is/"+ip+"?lang=zh-CN", timeout=3)); print(" ".join(filter(None, (d.get("country"), d.get("region"), d.get("city")))))' "$source_ip" 2>/dev/null ) [ -n "$resolved_location" ] && location=$resolved_location ;;esac
now=$(TZ=Asia/Shanghai /bin/date '+%Y-%m-%d %H:%M:%S %Z')message=$(printf 'VPS 登录告警\n主机: %s\n用户: %s\n登录方式: %s\n来源: %s(%s)\n时间: %s' \ "$(hostname)" "$PAM_USER" "$auth_method" "$source_ip" "$location" "$now")
/usr/bin/curl --silent --show-error --fail --max-time 10 --request POST \ --data-urlencode "chat_id=$CHAT_ID" \ --data-urlencode "text=$message" \ "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \ >/dev/null 2>&1限制脚本权限:
chown root:root /usr/local/sbin/ssh-login-telegramchmod 700 /usr/local/sbin/ssh-login-telegram先做独立测试
在未接入 PAM 前,先手动调用脚本:
PAM_TYPE=open_session PAM_USER=test PAM_RHOST=8.8.8.8 /usr/local/sbin/ssh-login-telegramTelegram 应收到中文通知,时间为北京时间。手动测试不会产生 SSH 认证日志,因此“登录方式”显示“未知”是正常的
未收到通知时,依次检查凭据、服务器出网、curl 和 python3 是否存在:
command -v curl python3curl -I --max-time 10 https://api.telegram.org接入 PAM
编辑 PAM 配置并在文件末尾添加一行:
sudoedit /etc/pam.d/sshdsession optional pam_exec.so quiet type=open_session seteuid /usr/local/sbin/ssh-login-telegramoptional 使 Telegram、DNS 或归属地查询故障不影响 SSH 登录;type=open_session 让 PAM 只在会话建立时执行脚本。此规则在下一次新建 SSH 会话时生效,无需重启 SSH
验证真实登录
保持当前连接不关闭,在本地新建 SSH 连接。成功登录后应很快收到告警,认证方式通常显示“SSH 密钥登录”或“密码登录”
脚本从 Ubuntu 的 /var/log/auth.log 匹配最新的 Accepted 记录。若系统只使用 systemd journal 而没有该文件,通知仍会发送,但认证方式会保持“未知”。这种情况下可执行 journalctl -u ssh --since '10 minutes ago' 确认日志来源,再按实际发行版调整脚本
安全边界
- IP 归属地查询会将登录来源 IP 发送给
ipwho.is。不接受这一点时,删除脚本中的归属地查询块即可 - 任何已经获得 root 权限的人都可以停用或篡改本地告警。应同时使用密钥登录、限制管理 IP、云平台 MFA 和独立的外部监控
- 多台 VPS 可以共用一个只用于运维告警的机器人,也可以各自使用不同机器人;不要复用与个人敏感聊天混用的机器人
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!







