Edit the SystemVersion.plist file to change your Mac OS X version from 10.7 to 10.6:
sudo vi /System/Library/CoreServices/SystemVersion.plist
Replace each occurrence of 10.7 with 10.6
Save the file
Install Universal Subversion 1.6.17 Binaries for Snow Leopard (Mac OS X 10.6) from http://www.collab.net/downloads/community/
Revert the file we edited previously (10.6 to 10.7 this time)
The magic
I wanted to find the simplest method of implementing this so I started looking at what we can do with SSH itself. There is an option in the authorized_keys file that allows you to run a command when a user authorizes with a particular key eg.
command="/usr/bin/my_script" ssh-dsa AAA...zzz me@example.com |
The command="..." part invokes a different command upon key authentication and runs the /usr/bin/my_script instead. Now we’ve got a starting point to work on the Google Authenticator logic.
Simple implementation
I’ve chosen ruby to implement this simple example but in theory you could use anything you want. This is a naive implementation but it will prove the concept. You’re going to need therotp library as well for this to work gem install rotp.
We put the following in /usr/bin/two_factor_ssh
#!/usr/bin/env rubyrequire 'rubygems'require 'rotp'# we'll pass in a secret to this script from the authorized_keys fileabort unless secret = ARGV[0]# prompt the user for their validation codeSTDERR.write "Enter the validation code: "until validation_code = STDIN.gets.strip sleep 1end# check the validation code is correctabort "Invalid" unless validation_code == ROTP::TOTP.new(secret).now.to_s# user has validated so we'll give them their shellKernel.exec ENV['SSH_ORIGINAL_COMMAND'] || ENV['SHELL'] |
The secret is in Kernel.exec which, upon successful validation, replaces thetwo_factor_ssh script process with the original command the user was attempting or their default shell so it is a completely seamless experience from that point on.
Generating the secret
We need to generate a secret token that is shared between the Google Authenticator app and the server.
Here’s a little script that will spit out a new token and a link to a QR code that can be scanned into the Google Authenticator application.
#!/usr/bin/env rubyrequire 'rubygems'require 'rotp'secret = ROTP::Base32.random_base32data = "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 |
Running this produces:
Your secret key is: 4rr7kc47sc5a2fgt |
We can scan the QR code directly into Google Authenticator and then update ourauthorized_keys file as follows:
command="/usr/bin/two_factor_ssh 4rr7kc47sc5a2fgt" ssh-dsa AAA...zzz me@example.com |
That should do it!
Testing it out
[richard@mbp ~]$ ssh moocode@myserverEnter the validation code: wrongInvalidConnection to myserver closed.[richard@mbp ~]$[richard@mbp ~]$ ssh moocode@myserverEnter the validation code: 410353moocode@myserver:~$ |
Great, that seems to work as expected.
Wrapping up
I’ve got a slightly more involved example that adds in support for ‘remember me’ by IP address for a fixed period of time so you don’t have to reach for the phone on every single login from the same IP.
The extended example also does some primitive logging but I’d like to add in a better auditing system (another PCI compliance requirement) as this would allow us to know which key is used to log into the server and whether they validated.
We should also probably have a fallback mechanism (a master key or 5 one-time codes like Google does) so we don’t inadvertently lock ourselves out of the server.
Article: moocode.com


Rewritten installer that makes installation even easier





To get help, provide any feedback, ask questions, or get involved and help contribute to the openSUSE distribution, please 




