原计划是想基于 PAM 做一个需要输入接收到的验证码才能进入 SSH 的两步验证的。
其实这个功能已有现有的软件包—— Google-Authenticator 可供使用,但是这个不是我现在想要的那种。
登录验证码 2FA
编辑/etc/profile.d/目录下新增一个文件,文件名暂定login_verify_code.sh:
#!/bin/bash
# 登录验证码比对
# 发送验证码
code=$(openssl rand -base64 6 | cksum | cut -c1-6);
curl --silent --location 'https://gotify.cyzwb.com/message' --header 'X-Gotify-Key: xxxx' --header 'Content-Type: application/json' --data '{ "title": "验证码", "priority": 5, "message": "你的验证码:'"${code}"'" }' >> null
curl --silent --location --request PUT 'https://ntfy.cyzwb.com' --header 'Content-Type: application/json' --header 'Authorization: Bearer tk_xxxxx' --data '{ "title": "验证码", "topic": "2mBN3DwlwZP2Grhf", "message": "你的验证码:'"${code}"'" }' >> null
# 比较验证码
# 安全增强版:限制重试次数,防止暴力破解
MAX_RETRIES=5 # 最大重试次数
RETRY_COUNT=0
TARGET_NUMBER=$code
echo "请输入验证码(最多${MAX_RETRIES}次机会)"
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
read -s -p ">>> " input
# 验证输入是否为纯数字
if ! [[ "$input" =~ ^[0-9]+$ ]]; then
echo "错误:请输入有效的数字!"
((RETRY_COUNT++))
continue
fi
# 验证目标数字
if [ "$input" -eq "$TARGET_NUMBER" ]; then
echo "验证通过。"
return 0
else
((RETRY_COUNT++))
remaining=$((MAX_RETRIES - RETRY_COUNT))
echo "输入错误,剩余${remaining}次机会。"
# 失败后增加延迟,减缓暴力破解速度
sleep 1
fi
done
# 达到最大重试次数后的处理
echo "错误:重试次数过多,程序强制退出。"
exit 1
脚本启动时随机生成6位数字,随后再将生成的6位数发送到 Gotify 以及 NTFY 两个消息通知服务。之所以一并发送到两处,那是因为我就部署了两套不同的这类服务。
题外话
本来以为很好的以为可以在登录 SSH 后挂起终端直至输入正确的验证码,谁料在我后面研究用 C 语言写个 PAM 模块的时候,在调试中无意间的一次Ctrl-c就给退出了。
也曾想过用软件将 BASH 脚本转成 C 来替换这个脚本的。
在用 C 写好 PAM 模块之后准备试试 Terminal 会不会也需要验证码才能进入系统的,在准备启动 VNC 进入终端时发现, VNC 因为一些错误无法启动。
错误一
SConnection: AuthFailureException: Authentication failure VNCSConnST: closing 147.182.182.196::42636: Authentication failure EncodeManager: Framebuffer updates: 0 EncodeManager: Total: 0 rects, 0 pixels EncodeManager: 0 B (1:-nan ratio) Connections: closed: 147.182.182.196::42636 ComparingUpdateTracker: 0 pixels in / 0 pixels out ComparingUpdateTracker: (1:-nan ratio) Connections: accepted: 147.182.182.196::42644 SConnection: Client needs protocol version 3.3 SConnection: AuthFailureException: Authentication failure 请输入验证码(最多5次机会) 错误:请输入有效的数字! 错误:请输入有效的数字! 错误:请输入有效的数字! 错误:请输入有效的数字! 错误:请输入有效的数字! 错误:重试次数过多,程序强制退出。 VNCSConnST: closing 147.182.182.196::42644: Authentication failure EncodeManager: Framebuffer updates: 0 EncodeManager: Total: 0 rects, 0 pixels EncodeManager: 0 B (1:-nan ratio) Connections: closed: 147.182.182.196::42644 ComparingUpdateTracker: 0 pixels in / 0 pixels out ComparingUpdateTracker: (1:-nan ratio) ComparingUpdateTracker: 0 pixels in / 0 pixels out ComparingUpdateTracker: (1:-nan ratio) X connection to :0 broken (explicit kill or server shutdown). Killing Xtigervnc process ID 30371... success! ============================================================================================ Session startup via '/etc/X11/Xtigervnc-session' cleanly exited too early (< 3 seconds)! Maybe try something simple first, e.g., tigervncserver -xstartup /usr/bin/xterm The X session cleanly exited! The Xtigervnc server cleanly exited! root@UnitedStates:~#
错误二
New Xtigervnc server 'UnitedStates.YunLtd.cyzwb.com:0 (root)' on port 5900 for display :0.
Use xtigervncviewer -SecurityTypes VncAuth,TLSVnc -passwd /tmp/tigervnc.8ohvY0/passwd UnitedStates.YunLtd.147180.com:0 to connect to the VNC server.
=================== tail /root/.vnc/UnitedStates.YunLtd.147180.com:0.log ===================
================================ 服务器运行时间 ================================
up 1 day, 10 hours, 39 minutes
启动时间: 2026-05-11 01:26:56
================================= 内存占用情况 =================================
total used free shared buff/cache available
Mem: 768Mi 110Mi 467Mi 636Ki 190Mi 657Mi
Swap: 768Mi 0B 768Mi
================================= 其他硬件信息 =================================
CPU 核心数: 1
================================= 当前登录用户 =================================
root pts/0 2026-05-12 11:33 (240e:xxx.xxx.xx)
root pts/1 2026-05-12 08:57 (240e:xxx.xxx.xx)
root pts/2 2026-05-12 09:04 (240e:xxx.xxx.xx)
root pts/3 2026-05-12 11:58 (113.xxx.xxx.xxx)
root pts/4 2026-05-12 12:00 (113.xxx.xxx.xxx)
root pts/5 2026-05-12 12:03 (113.xxx.xxx.xxx)
===================================== tmux 会话 =====================================
没有运行中的 tmux 会话
Tue May 12 12:06:14 2026
Connections: accepted: 164.90.151.212::58974
SConnection: Client needs protocol version 3.3
请输入验证码(最多5次机会)
错误:请输入有效的数字!
错误:请输入有效的数字!
错误:请输入有效的数字!
错误:请输入有效的数字!
错误:请输入有效的数字!
错误:重试次数过多,程序强制退出。
SConnection: AuthFailureException: Authentication failure
VNCSConnST: closing 164.90.151.212::58974: Authentication failure
EncodeManager: Framebuffer updates: 0
EncodeManager: Total: 0 rects, 0 pixels
EncodeManager: 0 B (1:-nan ratio)
Connections: closed: 164.90.151.212::58974
ComparingUpdateTracker: 0 pixels in / 0 pixels out
ComparingUpdateTracker: (1:-nan ratio)
ComparingUpdateTracker: 0 pixels in / 0 pixels out
ComparingUpdateTracker: (1:-nan ratio)
X connection to :0 broken (explicit kill or server shutdown).
Killing Xtigervnc process ID 30433... success!
============================================================================================
Session startup via '/etc/X11/Xtigervnc-session' cleanly exited too early (< 3 seconds)!
Maybe try something simple first, e.g.,
tigervncserver -xstartup /usr/bin/xterm
The X session cleanly exited!
The Xtigervnc server cleanly exited!
root@UnitedStates:~#
Socket error Event: 32 Error: 10053.
Connection closing...Socket close.
Connection closed by foreign host.
Disconnected from remote host(xxx.xxx.xxx.xxx) at 01:08:41.
Type `help' to learn how to use Xshell prompt.
结论
反正都已经编写好 PAM 模块,至于这个刚准备没24小时的脚本就这么舍弃掉了。不过也不是没有用的,可以用来做需要动态码才能启动的运行脚本。当然需要将脚本加密/混淆后这个才有意义,这不想着加密/混淆或者是转成 C 语言。
ChiuYut
2026年05月12日