诡异的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的行为是否合适,仅仅因为连接没有中断就判定端口转发仍然可以进行,显然我陷入了惯性思维的陷阱,为此还浪费了大量的时间。应当引以为戒。


分享