linux · sshd pam python · 2013-09-29 · yuex

承接上一篇 blog,某本意是想用 pam_limits.so 来做用户登录限制,从而可以让多个用户共用一台机器做 ssh 代理。这样一来,服务器端就必须要对用户进行基于用户名和登录 ip 的。

想当然的做法是用 /etc/security/limits.conf 中的 maxlogins 解决。但是这个是有问题的,maxlogins 是要用户登录到 shell 才会统计的。pam_limits.so 中读取的是 utmp,这个是存在 /var/run/utmp 中的,用 w 可以查看。而 ssh -N 是不会被 utmp 记录为 login 的。所以,如果用上篇 blog 中的方法实践的话,用户可以用 ssh -N 绕过所有的接入控制。一般而言,这也不是什么大问题,因为用户 ssh -N 接入的话,只能做端口转发这一件事情。而若 sshd 禁用端口转发的话,这个也就只能用来攻击了,而这种又太容易防范了(做贼还要用真名吗),所以也不会够成实质性的威胁。

但对于希望把端口转发做为一项服务的某来话,这就是个大问题了。一项服务,如果不能进行用户权限控制的话,那只能算是自己给自己开了一个安全漏洞。解决的方法,最好就是 ssh 可以提供相应的功能。但目前来看,似乎不太可能。而且即使有这个可能,自己动手 commit 的话,直到 merge 也要太长的周期。所以某转而来调研 pam 是不是也能实现这个功能。

答案是肯定的。而且更加方便的方法是用 pam_script.so 来做这件事。在进行账户验证的时候,执行脚本,来完成对 ssh 接入用户数和用户 ip 的检查,从而完成对单个账号登录和单个 ip 登录数目的限制。可惜的是,pam 的发行包中没有 pam_script 这个模块。网上可以找到一些别人写的,但大多语焉不详,有的是 doc 一笔带过,有的是编译无法一次通过。此外,某发现这些 pam_script.so 的实现,都只能针对 auth,password 和 session 进行插入,而独没有 account。根据 man pam 中的解释

account - provide account verification types of service: has the user's password expired?; is this user permitted access to the requested service?

似乎 account 才是最符合某的接入控制需求的。所以,某决定还是自己动手写一个简单的 pam_script.so 吧。对于如何写一个 pam module,可以参考 这个 这个 。因为我只做 account 用,所以只要实现 pam_sm_acct_mgmt 就可以了。只要在 C 中指定一个脚本路径,然后用一些参数调用这个脚本,并读取返回值就可以了。而外部脚本,可以使用 shell script,也可以使用 python,对登录数目的统计可以用 psnetstat,只要检查完把结果返回给 pam_script.so 就可以了。

最后一步,不要忘记把 pam_script.so 加入到 /etc/pam.d/sshd 中

因为目前某的 vps 还在做小范围的测试,代码要晚点才能放到 github 上。