Linux下使用pam_python实现SSH的双因子认证登录.md

关键字

Linux PAM Python SSH 2 Two Multi Factor Authentication Login 双因子 多因子 密保 TOKEN 一次性口令 PASSPOD OTP yubikey 认证 安全 登录

引言

Linux系统管理员(System Administrator,SA)经常碰到的问题就是放在公网的服务器经常被人猜测密码,每天都可以从系统日志里看到探测密码的信息,再加上最近很多厂商泄露了包含用户密码的数据库,撞库的行为也逐步开始转移到SSH上。

最初SA的防御手段一般是限制IP地址、修改SSH端口、部署失败一定次数就锁定或者封IP的程序或者脚本,更有极客想出了敲门3次端口才开放的办法,可谓无所不用其极。但是这些办法很多都不是很方便,改了端口,连接时需要指定端口;限制了IP地址,发现在家上网就登录不了了,封锁脚本可能把自己也锁定了。

在大公司里一般是采用的“RSA SecurID”方案,或者类似的技术。我们称其为双因子认证或者多因子认证(Two Factor Authentication;MFA,Multi Factor Authentication),在输入密码的同时需要输入一个一次性口令(OTP,One Time Password)。这种方案也有软件实现和硬件实现,软件例如google authenticator、Symantec Validation and ID Protection (VIP) ;硬件例如 RSA SecurID、飞天诚信的密保产品。


使用RSA SecurID的方案看起来虽然很好,但是他需要独立部署RSA Server,需要占用一台服务器,并且Server端软件是收费的,RSA SecurID密保也是收费的。

有没有免费的办法?

有啊,今天就来介绍一个。

实现方法

最简单的实现的方式,用户登录时需要输入用户名+PIN+密码方式才能登录。

这里的PIN是一个字符串,例如”ipcpu.com”,固定死的,不会变。

[root@IPCPU-0security]#ssh root@192.168.110.11
EnterYourPIN:
Password:
Lastlogin:MonMar2100:44:262016from192.168.110.11
[root@IPCPU-11~]#

安装pam_python模块

pam_python (注意不是python_pam)是一款开源的软件,将需要使用C语言编写的PAM模块转换成了可以使用python语言来写,顿时感觉方便多了。

安装方法比较简单
##@@安装编译依赖
yum install pam pam-devel-y
##@@解压进入src目录
make lib
##@@拷贝.so文件到/lib64/security/
cp build/lib.linux-x86_64-2.6/pam_python.so/lib64/security/
编写Python程序实现认证流程
我们进入到 /lib64/security/ 编写一个auth.py文件,内容如下
#!/usr/bin/env python
# -*- coding=utf-8 -*-
"""
#这个函数是本次的重点内容哦,判断用户输入的PIN是否为ipcpu.com
"""
defpam_sm_authenticate(pamh,flags,argv):
forattemptinrange(0,3):
msg=pamh.Message(pamh.PAM_PROMPT_ECHO_OFF,"Enter Your PIN: ")
resp=pamh.conversation(msg)
ifresp.resp=="ipcpu.com":
returnpamh.PAM_SUCCESS
else:
continue
returnpamh.PAM_AUTH_ERR
"""
#以下都是默认函数
"""
defpam_sm_setcred(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_acct_mgmt(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_open_session(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_close_session(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_chauthtok(pamh,flags,argv):
returnpamh.PAM_SUCCESS
配置SSHD,开启PAM模块
修改/etc/pam.d/sshd,新增一行,如下
#%PAM-1.0
auth requisite pam_python.so auth.py
auth required pam_sepermit.so
auth include password-auth
修改/etc/ssh/sshd_config,打开ChallengeResponse
ChallengeResponseAuthenticationyes

重启SSHD服务,接下来就可以测试了。

如果出现错误,日志会写到/var/log/secure里面。

进阶-独立的PIN

使用固定的PIN优点太low了,接下来我们介绍进阶的办法,每个人用自己的PIN。

首先PIN需要有个地方存放起来,我们就直接使用/etc/passwd的comment字段来存储。

可以通过命令 usermod来修改。如下,

[root@IPCPU2factor-with-PIN]#usermod-c',,15801581158,'ipcpu
[root@IPCPU2factor-with-PIN]#cat/etc/passwd|grep ipcpu
ipcpu:x:501:501:,,15801581158,:/home/ipcpu:/bin/bash
[root@IPCPU2factor-with-PIN]#
python的代码也需要修改下,如下
importrandom,string,hashlib,requests
importpwd,syslog
defauth_log(msg):
syslog.syslog("IPCPU-PAM-AUTH: "+msg)
defget_user_number(user):
"""Extract user's phone number for pw entry"""
try:
comments=pwd.getpwnam(user).pw_gecos
exceptKeyError:# Bad user name
auth_log("No local user (%s) found."%user)
return-1
try:
returncomments.split(',')[2]# Return Office Phone
exceptIndexError:# Bad comment section format
auth_log("Invalid comment block for user %s. Phone number must be listed as Office Phone"%(user))
return-1
defpam_sm_authenticate(pamh,flags,argv):
try:
user=pamh.get_user()
user_number=get_user_number(user)
exceptpamh.exception,e:
returne.pam_result
ifuserisNoneoruser_number==-1:
msg=pamh.Message(pamh.PAM_ERROR_MSG,"Unable to send one time PIN.\nPlease contact your System Administrator")
pamh.conversation(msg)
returnpamh.PAM_AUTH_ERR
forattemptinrange(0,3):# 3 attempts to enter the one time PIN
msg=pamh.Message(pamh.PAM_PROMPT_ECHO_OFF,"Enter Your PIN: ")
resp=pamh.conversation(msg)
ifresp.resp==user_number:
auth_log("user: "+user+" login successful with PIN.")
returnpamh.PAM_SUCCESS
else:
auth_log("user: "+user+" login failed with PIN.")
continue
returnpamh.PAM_AUTH_ERR
defpam_sm_setcred(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_acct_mgmt(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_open_session(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_close_session(pamh,flags,argv):
returnpamh.PAM_SUCCESS
defpam_sm_chauthtok(pamh,flags,argv):
returnpamh.PAM_SUCCESS

继续进阶-短信

上一步,我们使用了每个用户独立的PIN来进行双因子认证,如果我们把PIN换成自己的手机号,然后在登陆的时候先生成随机字符串,然后短信发送到用户的手机上,对比字符串是否一致,这样我们就实现了基于短信形式的双因子认证。