这就是个新的轮子,技术上没有大的创新,只是更好用一些。SSH双因素认证的开源方案有挺多的,但是实践应用中发现有三个问题,让推广的情况不是很好。
以google otp为例:
1、需要安装APP
2、需要修改客户端ssh的登陆方式
3、无法集中管理
第三点先不谈,因为跟用户没啥关系。前面两点对于运维人员还好说,但是对于其他的人来说就不是那么友好,特别是第二个步骤引起的问题就比较多,比如不同软件不同的配置方式等。
所以优化的思路就是尽量透明化,越方便越好。
JXOTP计划有两个版本,一个是单机版本,一个是企业版本,针对的是不同的需求,目前开源的是单机版本,企业版本还在内测中,后续会放出。
单机版本比较适合服务器少的情况,区分程度可以简单划为管理的服务器是否多于10台,10台以内,特别是只有几台服务器的情况下,用单机版本是个不错的选择。
写这个JXOTP的背景是,云上的服务器被没完没了的怼,每时每刻都被进行SSH暴力破解,虽说密码本身不弱,但是哪天要是被撞库了也没地方说理去,所以保险起见还是上个双因素认证系统好。
目前在centos6/7上测试通过,其他系统自测。
安装如下:
1、# git clone https://github.com/jx-sec/jxotp.git
2、# cd jxotp
3、# sh install_otp.sh
结果如下:
拿出你发财的小手,打开微信小程序,搜索 “运维密码” ,打开后 点击 “添加场景” 扫描二维码即可完成OTP的配置。
最后是在服务器上启用OTP功能:
# vi /etc/pam.d/sshd
在最上一行添加:
auth optional pam_python.so auth.py
保存文件即可生效,无需重启sshd服务。
安装配置过程到此结束,下面校验效果:
# tail -F /var/log/messages
新开个窗口登陆服务器,随便输入个密码,如123456:
日志为”sshd: otp auth log: login user is root,login fail,code is 123456,must 054040″。
code is 123456,是取当前输入密码的后六位,即123456。
must is 054040, 054040是当前OTP生成的code,需要对比运维密码中的code与服务器的code是否一致,正常服务器时间没问题的话,是一致的。
当确定服务器和运维密码的code一致后,安装就此结束。
假设密码为abcdfgww,code为951753,那么当登陆的时候,输入的密码为abcdfgww951753。
# -*- coding: utf-8 -*-import syslogimport pyotp OTP_SECRET = "YOU OTP SECRET KEY" # 可以手动修改SECRET KEY,必须为16位base32格式字符串WHITE_IP = ["YOU BYPASS IP"]#设置白名单IP,白名单IP将无需进行动态口令认证,适合有堡垒机的场景,或者固定IP的情况GLOBAL_USER_CHECK = False#开启所有系统用户双因素认证,默认为否,即只针对特定用户开启双因素认证CHECK_USER = ['root']#当GLOBAL_USER_CHECK = False时生效,配置需要进行双因素认证的用户,可添加多个用户,默认只对root用户开启双因素认证def otp_auth(code): totp = pyotp.TOTP(OTP_SECRET) if totp.now() == code: return True else: return False#进行OTP校验def otp_log(msg): syslog.openlog(facility=syslog.LOG_AUTH) syslog.syslog("otp auth log: "+msg) syslog.closelog()#记录日志def otp_code(): totp = pyotp.TOTP(OTP_SECRET) return totp.now()#获取当前时间的codedef pam_sm_authenticate(pamh, flags, argv): for white in WHITE_IP: if pamh.rhost == white: otp_log("white ip login,ip is "+pamh.rhost) return pamh.PAM_SUCCESS#判断是否有白名单IP,有的话直接返回验证成功,无需进行双因素认证 if GLOBAL_USER_CHECK:#判断是否开启双因素认证 resp = pamh.conversation(pamh.Message(pamh.PAM_PROMPT_ECHO_OFF,'Password:'))#获取输入的密码 code = resp.resp[-6:]#取密码后六位 if otp_auth(code): pamh.authtok = resp.resp[:-6] otp_log("login user is "+pamh.user+",login success,code is "+resp.resp[-6:]) else: pamh.authtok = "" otp_log("login user is "+pamh.user+",login fail,code is "+ resp.resp[-6:]+",must "+otp_code()) return pamh.PAM_SUCCESS#判断密码后六位与服务器code是否一致,如果是将密码后六位删除,重写密码参数,如果不是将整个密码参数设置为空 else: for user in CHECK_USER: if pamh.user == user: resp = pamh.conversation(pamh.Message(pamh.PAM_PROMPT_ECHO_OFF,'Password:')) code = resp.resp[-6:] if otp_auth(code): pamh.authtok = resp.resp[:-6] otp_log("login user is "+pamh.user+",login success,code is "+resp.resp[-6:]) else: pamh.authtok = "" otp_log("login user is "+pamh.user+",login fail,code is "+ resp.resp[-6:]+",must "+otp_code()) else: otp_log("user login otp check bypass,user is "+ pamh.user) return pamh.PAM_SUCCESS#判断用户是否为设置开启校验的用户,不是直接返回成功,是的话进行检测,流程同上def pam_sm_setcred(pamh, flags, argv): return pamh.PAM_SUCCESS
新轮子更好用的地方主要体现在,不需要像传统的方法去改sshd的配置文件开启ChallengeResponseAuthentication选项,也即对于使用的用户来说,部署完后是透明的,无需修改windows下登陆客户端的配置,降低使用的成本,其次支持用户和IP设置,提高了灵活性,但是相对企业版来说,单机版存在不好维护的问题,所以适合少量服务器使用。
*本文作者chenjc,转载自FreeBuf.COM。
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.