使用Google Authenticator实现SSH两步认证
by yuanyi
at 2011-09-26 23:30:49
original http://item.feedsky.com/~feedsky/heikezhi/~8608072/569336371/6713895/1/item.html
最近,为了能够通过PCI(安全支付行业标准)的自我测评,我得到了几台服务器,其中的一个要求时需要通过两步认证来访问这些服务器,我询问了是否可以使用SSH Key加密码(Passphrase),但是没有得到一个明确的答复,于是我决定自己动手来实现SSH的两步认证,正好我也对这个很感兴趣。
经过一番调研之后,我发现我可以使用Google Authenticator的PAM认证模块,但是它只支持SSH密码认证,而我使用的是SSH key认证。
意想不到
我想找一种最简单的实现方式,所以我决定先从SSH开始,看看有没什么特性可供利用,很快,我就发现SSH的authorized_keys文件有一个选项,可以为通过public key方式登录的用户设置一个钩子命令:
command="/usr/bin/my_script" ssh-dsa AAA...zzz me@example.com
command选项可以让你为每个key设置一条不同的命令,下面就让我们开始集成Google Authenticator吧!
更新:根据Hacker news上的评论,你也可以在sshd_config中配置ForceCommand选项来设置一个全局钩子。
简单实现
我选择了ruby来编写我的第二步认证脚本,但理论上,你可以使用任何你喜欢的语言,下面的代码只是为了验证概念,要运行下面的代码,你需要安装rotp,一个ruby的HOTP/TOTP协议实现:
gem install rotp
现在,将下面的代码保存到 /usr/bin/two_factor_ssh:
#!/usr/bin/env ruby require 'rubygems' require 'rotp' # we'll pass in a secret to this script from the authorized_keys file abort unless secret=ARGV[0] # prompt the user for their validation code STDERR.write "Enter the validation code: " until validation_code=STDIN.gets.strip sleep 1 end # check the validation code is correct abort "Invalid" unless validation_code==ROTP::TOTP.new(secret).now.to_s # user has validated so we'll give them their shell Kernel.exec ENV['SSH_ORIGINAL_COMMAND'] || ENV['SHELL']
这里的诀窍就是kernel.exec,如果通过了验证,它会继续调用用户原来正在执行的命令,或是给用户一个全新的shell,从而让整个认证过程更加顺畅。
生成密钥
下面,我们需要为Google Authenticator和服务器生成一个共享密钥,我写了一个简单的脚本:
#!/usr/bin/env ruby require 'rubygems' require 'rotp' secret=ROTP::Base32.random_base32 data="otpauth://totp/#{`hostname -s`.strip}?secret=#{secret}" url="https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=#{data}" puts "Your secret key is: #{secret}" puts url
运行这个脚本得到:
Your secret key is: 4rr7kc47sc5a2fgt
https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/myserver?secret=4rr7kc47sc5a2fgt
现在,通过Google Authenticator扫描生成的二维码,然后更新authorized_keys:
command="/usr/bin/two_factor_ssh 4rr7kc47sc5a2fgt" ssh-dsa AAA...zzz me@example.com
大功告成!
更新: 感谢@js4all提醒,OpenSSH的某个特定版本可能会打印出正在执行的命令(以及参数),这样我们的密钥就暴露了,解决办法就是在命令行只传递一个标识符,然后在脚本内再通过这个标识符去查找真正的密钥。
试试看
[richard@mbp ~]$ ssh moocode@myserver
Enter the validation code: wrong
Invalid
Connection to myserver closed.
[richard@mbp ~]$
[richard@mbp ~]$ ssh moocode@myserver
Enter the validation code: 410353
moocode@myserver:~$
不错,和我预期的完全一样。
再进一步
这里有一个更完善的例子可以支持根据IP地址自动保存登录信息一段时间,这样,如果你使用同一个IP登录,就不用每次都输入验证码了。
这个例子里面包含了一些简单的日志记录,但是我更愿意使用监控(audit)系统来做这件事(这也是PCI的一项要求),这样就可以记录某个key在什么时间登录过服务器,以及它的认证结果。
或许我们还应该增加一种备份机制(一个主key或是5个一次性的code,就像Google做的那样),以保证我们自己不会在某些意外情况下被挡在外面,如果你有任何好点子,欢迎留言,另外,下周我会再写一篇跟进文章,如果你感兴趣,欢迎通过Twitter follow我。
-----------
本文来自"Simple Two-Factor SSH Authentication",作者:Richard Taylor ,翻译:@yuanyiz
想和我们一道传播黑客精神?快来加入吧!
无觅猜您也喜欢: | |||
16条技巧让你更高效使用SSH |
Gate One: 基于HTML5的Web SSH客户端 |
Google SPDY初探:HTTP 1.1之外的世界 |
在 Google Chrome 中运行 Termkit |
无觅 |