

Simple Two-Factor SSH Authentication - moomerman
https://moocode.com/posts/5-simple-two-factor-ssh-authentication

======
corin_
As noted alsewhere, ForceCommand is a nicer option, and can be done with
something like this in sshd_config:

    
    
      Match group yubikey
      #       ForceCommand /usr/local/bin/yubikey.sh
             ForceCommand /usr/local/bin/mobileverification.sh
    

The commented out script is something I wrote to authenticate Yubikeys - see
<http://yubico.com/yubikey>

And the mobileverification.sh sends a randomly generated 4 digit pin code to
the phone number that user has saved in ~/.ssh/mobile_number and asks them to
enter it.

Edit: in case anyone is interested, mobileverification.sh at
[http://pastie.org/pastes/2579517/text?key=tv1xciwdubnwi165fz...](http://pastie.org/pastes/2579517/text?key=tv1xciwdubnwi165fzaksa)
and yubikey.sh at
[http://pastie.org/pastes/2579528/text?key=krpmwrivbvrjmx0xb3...](http://pastie.org/pastes/2579528/text?key=krpmwrivbvrjmx0xb3smq)

Edit2: It's worth pointing out that both those scripts were written fairly
quickly by me, for basic personal use. If you want to use them I would
recommend going through them and making sure I didn't screw anything up.

~~~
zobzu
These are much more sensible that the OP's solution.

Just to be clear to anyone reading, because it's not really explained:

 __* OP double-protects the SSH key. It means you need the key's passphrase
and another factor (Google authenticator) to decrypt the ssh key. Then the ssh
key is used to auth with the server.

=> the authentication with the server is still one factor auth, compromising
the key at any level still grants access.

=> obligatory analogy: OP did like that: put your car key in a box that also
has a key. Attacker just need a copy of the key in the box to open the door
(granted that he won't use any physical attack on the door :p)

=> 2 (or more) factor authentication should always be used on the component
that does the final authentication.

 __* People using ForceCommand apply the 2 factor at the last step of
authentication, that is, once the ssh key authenticated you correctly, you
still need to authenticate to something else before being given access.

=> obligatory analogy: now you have a key and a cellphone. you turn the key in
the car and the door doesn't fully unlock. you gotta enter a code given by the
cellphone before it actually opens. if the attacker get a copy of your key,
it's not enough. if the attacker gets a copy of your phone's passcode (even
thus it changes each time), its not enough.

~~~
moomerman
The method listed in the article does authenticate with the SSH key first and
then the second factor kicks in only if the key (and passphrase) are valid.

Doesn't ForceCommand do exactly the same thing except it allows you to do it
globally?

~~~
simcop2387
Not only does it allow you to do it globally, it doesn't allow a user to log
in and disable it on you either. If you have to have them turn it on in
~/.ssh/authorized_keys all it takes is someone to get in once to add in a key
that doesn't require that any more.

------
samarudge
If you're running a recent version of OpenSSH, you can add the 'ForceCommand'
param to sshd_config to add it for all users. The only downside to this is it
is for _all users_ , so if you run something that needs to use key based login
without the two factor method you'll need to validate that yourself within the
script.

~~~
tobiasu
> The only downside to this is it is for all users

Look up the Match command, you can limit ForceCommand and many others to a
specific "User, Group, Host and Address."

------
teeray
GRC's Perfect Paper Passwords works well for me
(<https://www.grc.com/ppp.htm>). It's similar to Last Pass's Grid
authentication but less obnoxious IMO. One member of the GRC newsgroups has
written a PAM which has served all of my 2FA needs
(<http://code.google.com/p/ppp-pam/>).

------
dendory
A while back I wondered how hard it would be to integrate two-factor
authentication on a web site using Google's Authenticator app, since it uses
open protocols and is available on all platforms. Turns out it's incredibly
easy. Even made a demo: <http://dendory.net/twofactors/>

------
sweis
This is cool. I'd also like to point out the PAM module:
[http://code.google.com/p/google-
authenticator/source/browse/...](http://code.google.com/p/google-
authenticator/source/browse/libpam/)

------
pedrocr
I was using the command="" stuff to restrict a user to only running rsync the
other day and was considering writing the script in ruby as was done here.
Does anyone have any opinion on how safe that is? The client shouldn't have
that many ways to interact with the ruby process but I was still wondering if
I should stick to something smaller like /bin/sh (not even bash) for safety.

------
js4all
What a great solution. I will use it, but...

I guess I found a serious security problem.

When logging ssh commands with '-vT', I can see the secret. The secret should
be hard coded in the two_factor script.

~~~
moomerman
I don't see the secret in the output when I run that command. It just says:

debug1: Remote: Forced command.

~~~
moomerman
It seems that certain versions of OpenSSH do print out the command and
parameters so I've updated the blog post to include a work-around

------
munin
if you exec the shell on your own (and make auth decisions on your own)
outside of PAM you are basically destroying meaningful logging of
successful/unsuccessful authentication, right?

~~~
ominous_prime
You can log the events yourself. If it's a shell script, it would be as easy
as:

    
    
        logger -p authpriv.notice "Some message about authentication"

~~~
moomerman
That is great thank you, I was looking for something along those lines

------
jaryd
Could a user bypass this by using: ssh user@host.com -Tv 'bash' ?

~~~
moomerman
Just tried it and it still prompts for the auth code

~~~
jaryd
Thanks for the follow up! Will continue to play with it.

------
pedrocr
In the authorized_keys script, if instead of exec()ing SHELL you use
SSH_ORIGINAL_COMMAND you won't break "ssh myhost <command>"

~~~
moomerman
In the extended example it does actually use the SSH_ORIGINAL_COMMAND, will
update the simple version too

~~~
moomerman
SSH_ORIGINAL_COMMAND isn't in the ENV at all if no command was passed through

~~~
pedrocr
Right, that was what I asked in my other post. I assumed sshd would fill in
the login shell there if nothing was passed as that is effectively what is
being called but it seems not. I see you're doing exec(SHELL) to fix that.

------
vaneck
Beware that this may break apps that use SSH as a transport protocol (like
rsync and mercurial, depending on your setup of course).

~~~
moomerman
The extended example 'should' work with those kinds of apps. The trivial
example does not.

~~~
pedrocr
More specifically it checks for the SSH_ORIGINAL_COMMAND environment variable
and executes it if it exists, thus making "ssh myhost <command>" work again.

------
leeoniya
\+ port knocking via knockd and running sshd on a diff port is good.

------
mise
I got a certificate warning when opening the page.

~~~
moomerman
Interesting, would you mind letting me know which browser/version you're
running? and the error message if possible?

~~~
ismarc
I got the same thing on the default browser for Android 2.3, it shows the
certificate was issued 9 17 2006 but expires 9 17 1936 (not sure if that's
cert or CA) issued by startcom.

