最新消息:

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

Linux ipcpu 13867浏览 0评论

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”,固定死的,不会变。

  1. [root@IPCPU-0 security]# ssh root@192.168.110.11
  2. Enter Your PIN:
  3. Password:
  4. Last login: Mon Mar 21 00:44:26 2016 from 192.168.110.11
  5. [root@IPCPU-11 ~]#

安装pam_python模块

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

官网地址:http://pam-python.sourceforge.net/

github备份:https://github.com/ipcpu/pam-python-ipcpu (修正了CentOS的报错,放了一些案例和中文说明进去)

安装方法比较简单

  1. ##@@安装编译依赖
  2. yum install pam pam-devel -y
  3. ##@@解压进入src目录
  4. make lib
  5. ##@@拷贝.so文件到/lib64/security/
  6. cp build/lib.linux-x86_64-2.6/pam_python.so /lib64/security/

编写Python程序实现认证流程

我们进入到 /lib64/security/ 编写一个auth.py文件,内容如下

  1. #!/usr/bin/env python
  2. # -*- coding=utf-8 -*-
  3. """
  4. #这个函数是本次的重点内容哦,判断用户输入的PIN是否为ipcpu.com
  5. """
  6. def pam_sm_authenticate(pamh, flags, argv):
  7. for attempt in range(0,3):
  8. msg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Enter Your PIN: ")
  9. resp = pamh.conversation(msg)
  10. if resp.resp == "ipcpu.com":
  11. return pamh.PAM_SUCCESS
  12. else:
  13. continue
  14. return pamh.PAM_AUTH_ERR
  15. """
  16. #以下都是默认函数
  17. """
  18. def pam_sm_setcred(pamh, flags, argv):
  19. return pamh.PAM_SUCCESS
  20. def pam_sm_acct_mgmt(pamh, flags, argv):
  21. return pamh.PAM_SUCCESS
  22. def pam_sm_open_session(pamh, flags, argv):
  23. return pamh.PAM_SUCCESS
  24. def pam_sm_close_session(pamh, flags, argv):
  25. return pamh.PAM_SUCCESS
  26. def pam_sm_chauthtok(pamh, flags, argv):
  27. return pamh.PAM_SUCCESS

配置SSHD,开启PAM模块

修改/etc/pam.d/sshd,新增一行,如下

  1. #%PAM-1.0
  2. auth requisite pam_python.so auth.py
  3. auth required pam_sepermit.so
  4. auth include password-auth

修改/etc/ssh/sshd_config,打开ChallengeResponse

  1. ChallengeResponseAuthentication yes

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

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

进阶-独立的PIN

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

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

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

  1. [root@IPCPU 2factor-with-PIN]# usermod -c ',,15801581158,' ipcpu
  2. [root@IPCPU 2factor-with-PIN]# cat /etc/passwd |grep ipcpu
  3. ipcpu:x:501:501:,,15801581158,:/home/ipcpu:/bin/bash
  4. [root@IPCPU 2factor-with-PIN]#

python的代码也需要修改下,如下

  1. import random, string, hashlib, requests
  2. import pwd, syslog
  3. def auth_log(msg):
  4. syslog.syslog("IPCPU-PAM-AUTH: " + msg)
  5. def get_user_number(user):
  6. """Extract user's phone number for pw entry"""
  7. try:
  8. comments = pwd.getpwnam(user).pw_gecos
  9. except KeyError: # Bad user name
  10. auth_log("No local user (%s) found." % user)
  11. return -1
  12. try:
  13. return comments.split(',')[2] # Return Office Phone
  14. except IndexError: # Bad comment section format
  15. auth_log("Invalid comment block for user %s. Phone number must be listed as Office Phone" % (user))
  16. return -1
  17. def pam_sm_authenticate(pamh, flags, argv):
  18. try:
  19. user = pamh.get_user()
  20. user_number = get_user_number(user)
  21. except pamh.exception, e:
  22. return e.pam_result
  23. if user is None or user_number == -1:
  24. msg = pamh.Message(pamh.PAM_ERROR_MSG, "Unable to send one time PIN.\nPlease contact your System Administrator")
  25. pamh.conversation(msg)
  26. return pamh.PAM_AUTH_ERR
  27. for attempt in range(0,3): # 3 attempts to enter the one time PIN
  28. msg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Enter Your PIN: ")
  29. resp = pamh.conversation(msg)
  30. if resp.resp == user_number:
  31. auth_log("user: " + user + " login successful with PIN.")
  32. return pamh.PAM_SUCCESS
  33. else:
  34. auth_log("user: " + user + " login failed with PIN.")
  35. continue
  36. return pamh.PAM_AUTH_ERR
  37. def pam_sm_setcred(pamh, flags, argv):
  38. return pamh.PAM_SUCCESS
  39. def pam_sm_acct_mgmt(pamh, flags, argv):
  40. return pamh.PAM_SUCCESS
  41. def pam_sm_open_session(pamh, flags, argv):
  42. return pamh.PAM_SUCCESS
  43. def pam_sm_close_session(pamh, flags, argv):
  44. return pamh.PAM_SUCCESS
  45. def pam_sm_chauthtok(pamh, flags, argv):
  46. return pamh.PAM_SUCCESS

继续进阶-短信

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

这部分代码就留给读者自行练习了。需要注意的是为了防止别人猜测密码时收到大量短信,这里最好连手机号也对比认证下。

参考资料

谷歌google authenticator算法分析
https://garbagecollected.org/2014/09/14/how-google-authenticator-works/
RSA SecurID相关资料
http://www.slideshare.net/Sandra4211/rsa-security-authentication-ace-serversecurid
鸟哥关于PAM的讲述
http://vbird.dic.ksu.edu.tw/linux_basic/0410accountmanager_5.php

转载请注明:IPCPU-网络之路 » Linux下使用pam_python实现SSH的双因子认证登录

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (1)

  1. 怎么先输入密码,在输入短信验证码?当我把pam_python.so放到最下面,如果出入正确的密码就跳过短信验证码了!
    赤水6年前 (2018-07-13)Reply