诡异的OpenSSH行为
最近几天在FreeBSD上尝试搭建SSH服务器,主要是想利用SSH的端口转发功能。然而却遇到了一个诡异的问题,耗费了我许多时间。
我希望限制每个用户的登录数,即,同一用户在同一时间只能在一个主机上连接SSH服务器进行端口转发。网上查了老半天,基本上没有什么有用的信息。
据说Linux中可以使用/etc/security/limits.conf
来限制用户的登录数量。也有资料说这个办法只能限制shell的登录数量,对不需要shell的端口转发连接无效。
limits.conf
通过pam_limits
模块产生作用,FreeBSD无此模块,因此我无法尝试。受此启发,我找到了pam_exec
模块,允许我在开启/关闭会话的时候执行任意的脚本。这样我只需要自己编写一个脚本来检测是否存在重复登录即可。
#!/bin/sh
# file: pam_session.sh
# prevent multiple ssh connections from a single user
USER_SESSION="/var/run/${PAM_USER}.ssh_session"
LOG="/var/log/ssh_session.log"
echo `date` "$PAM_USER" "$PAM_RHOST" "$PAM_SM_FUNC" >> $LOG
if [ "$PAM_SM_FUNC" = "pam_sm_open_session" ]; then
if [ -f $USER_SESSION ]; then
echo BLOCKED by `cat $USER_SESSION` >> $LOG
exit 1
fi
echo "$PAM_RHOST" > $USER_SESSION
elif [ "$PAM_SM_FUNC" = "pam_sm_close_session" ]; then
rm -f $USER_SESSION
fi
保存为/etc/ssh/pam_session.sh
,然后在/etc/pam.d/sshd
中添加一行
session required pam_exec.so /etc/ssh/pam_session.sh
即告成功。诡异的事现在发生了:我在一个主机上登录SSH,然后在另一个主机上也登录SSH,但后者只进行端口转发,不登录shell。结果后者也登录成功了!
我在Windows上用Bitvise的SSH客户端,连接并没有中断。
我在Android上用ConnectBot连接,在不登录shell的情况下,连接也没有中断。
我的脚本工作正常,PAM的Session应该是认证失败的(系统日志中有Permission Denied
的记录)。也就是说,OpenSSH忽略了PAM会话认证失败而继续保持着连接!
沿着这样的思路,我尝试了其他各种方法,试图阻断重复的SSH连接,均告失败。绝望的我去查看了OpenSSH的源代码,想搞清楚作者为什么指定了这样的行为。
看到第1052行:
sshpam_err = pam_open_session(sshpam_handle, 0);
if (sshpam_err == PAM_SUCCESS)
sshpam_session_open = 1;
else {
sshpam_session_open = 0;
/*1052*/ disable_forwarding();
error("PAM: pam_open_session(): %s",
pam_strerror(sshpam_handle, sshpam_err));
}
瞬间释然,其实OpenSSH在PAM会话认证失败的情况下是会禁用端口转发的。至于连接为什么没有中断,我不知道。
且不提OpenSSH的行为是否合适,仅仅因为连接没有中断就判定端口转发仍然可以进行,显然我陷入了惯性思维的陷阱,为此还浪费了大量的时间。应当引以为戒。