2010年02月23日(Tue)

FreeBSDでワンタイムパスワードを使ってsshログインする方法

FreeBSDには標準でワンタイムパスワード機構(OPIE)があり、ワンタイムパスワードでのsshログインが可能です。実際に試してみました。

前提

  • 現在は公開鍵認証だけ有効になっている(普通そうしますよね)
  • UNIXアカウント/パスワード認証は引き続き使用しない

まずこれを実現するために、PAMのsshdの設定を変更します。/etc/pam.d/sshdです。デフォルトでは

auth            sufficient      pam_opie.so             no_warn no_fake_prompts
auth            requisite       pam_opieaccess.so       no_warn allow_local
#auth           sufficient      pam_krb5.so             no_warn try_first_pass
#auth           sufficient      pam_ssh.so              no_warn try_first_pass
auth            required        pam_unix.so             no_warn try_first_pass

こうなってるところを
auth            requisite       pam_opie.so             no_warn no_fake_prompts

これだけに変更します。この変更は十分注意してください。油断するとパスワード無しでログイン出来る状態になったりします。ここにたどり着くまでにえらい苦労したんですがそれは別記

もしportsからsudoをインストールして使用している場合、こっちでもワンタイムパスワードが有効になってしまいます。それが鬱陶しい場合はsudoについてはワンタイムパスワード認証を無効にします。/usr/local/etc/pam.d/sudoの

# auth
auth            include         system

こうなっているところを

auth            required        pam_unix.so             no_warn try_first_pass

こう変更します。いっそのこと/etc/pam.d/systemの

auth            sufficient      pam_opie.so             no_warn no_fake_prompts
auth            requisite       pam_opieaccess.so       no_warn allow_local

この行をコメントアウトしてしまってもいいかもしれません。

sshdの設定を変更します。/etc/ssh/sshd_configです。

#ChallengeResponseAuthentication yes
 :
#UsePAM yes

デフォルトではこうですが、どっちかをnoにしていると思いますので元に戻します。変更したらsshdを再起動します。

公開鍵認証を使わずにログインを試して、リモートログイン出来ないことを確認します。繰り返しますが、設定ミスするとパスワード無しでログイン出来る状態になることがあります。

最後に、利用したいユーザーでワンタイムパスワードを有効にします。ローカルまたは安全な接続のコンソール上でopiepasswdを実行します

% opiepasswd -c foo
Adding foo:
Only use this method from the console; NEVER from remote. If you are using
telnet, xterm, or a dial-in, type ^C now or exit with no password.
Then run opiepasswd without the -c parameter.
Using MD5 to compute responses.
Enter new secret pass phrase: (パスフレーズ入力)
Again new secret pass phrase: (繰り返し)

ID foo OTP key is 499 ho1234
FOO BAR HOGE FUGA PIYO

これでユーザーfooのワンタイムパスワードが有効になりました。

最後から2行目にある499というのがシーケンスナンバーです。ワンタイムパスワードを1回使うと、この数字が1個減ります。その後のho1234はパスワードを生成するためのseed(種)です。ワンタイムパスワードを生成するにはシーケンスナンバーとseedとパスフレーズが必要です。

次に要求されるシーケンスナンバーは498になるので、作ってみます。

% opiekey 498 ho1234
Using the MD5 algorithm to compute response.
Reminder: Don't use opiekey from telnet or dial-in sessions.
Enter secret pass phrase:
NESS REND NICE WHEN EVER MANA

sshで実際に接続してみると、このようなプロンプトが出ますので

% ssh remotehost
otp-md5 498 ho1234 ext
Password:

さっき生成された"NESS REND NICE WHEN EVER MANA"を入力するとログインできるはずです。

ワンタイムパスワードはログイン先のホストで生成する必要はありません。シーケンス番号とseedとパスフレーズで一意なものが生成されるので、手元のホストでopiekeyコマンドを使って生成されたパスワードでもログインできます。他のOS用のパスワード生成機もあるらしいのですが実物は見つけられていません。

手元でパスワードが生成できない場合、opiekeyに-nオプションを付けると複数のパスワードを生成できます

% opiekey -n 5 999 ho1234
Using the MD5 algorithm to compute response.
Reminder: Don't use opiekey from telnet or dial-in sessions.
Enter secret pass phrase:
995: RACY FAT YAP TIE FORT NAP
996: GIN AWAY DIN HUGE FORT PAP
997: SKIM PAW BONE FALL DUMB BOCK
998: MITE CODY DUSK REEL GLUM ARK
999: HALE PET GEE WYNN DANK AWN

ので、これを控えておけばいいでしょう。未使用の物を間違っても落とさないように。

PAMの設定で悩みまくった記録

ワンタイムパスワードを使ってsshでログイン出来るようにするまでの/etc/pam.d/sshdをどう書いたらいいか悩みまくった試行錯誤の記録です。結論は、control-flagのsufficientは成功すればそこで打ち切りとマニュアルに書いてあるが実はそうではない

まずデフォルト。

auth            sufficient      pam_opie.so             no_warn no_fake_prompts
auth            requisite       pam_opieaccess.so       no_warn allow_local
auth            required        pam_unix.so             no_warn try_first_pass

これがどういう意味になるかは別記事に書きましたが、簡単に言うとワンタイムパスワードが有効なユーザーはワンタイムパスワード認証でログインでき、ワンタイムパスワードを設定していないユーザーはUNIX認証になります。

失敗例1。UNIX認証を無効にすればいいんだろ?とばかり最後の行をコメントアウトしたとしましょう。

auth            sufficient      pam_opie.so             no_warn no_fake_prompts
auth            requisite       pam_opieaccess.so       no_warn allow_local
#auth           required        pam_unix.so             no_warn try_first_pass

ワンタイムパスワード認証が有効なユーザーでは変化ありませんが、そうでないユーザーはなんとパスワード無しでログインできてしまうという大変危険な設定です。

失敗例2。そうかそうか、ワンタイムパスワード認証を使わないユーザーは上2行を突破するからそこで拒否すればいいんだろう、ということで、こうする。

auth            sufficient      pam_opie.so             no_warn no_fake_prompts
auth            requisite       pam_opieaccess.so       no_warn allow_local
auth            required        pam_deny.so

mod_deny.soはとにかく拒否、という認証モジュールです。これがどうなるかというと、ワンタイムパスワード認証を有効にしているユーザーもログインできなくなります。/var/log/auth.logを見ると

sshd[]: Accepted keyboard-interactive/pam for shin from xxx.xxx.xxx.xxx port 12345 ssh2
sshd[]: fatal: PAM: pam_setcred(): failed to set user credentials

なんて出てます。パスワードを間違えたときは

sshd[]: error: PAM: authentication error for foo from xxx.xxx.xxx.xxx

になるので、ワンタイムパスワード認証自体は正常に動いている様子。

マニュアルを見ても実際に試した結果でもcontrol-flagがsufficientのモジュールは成功すれば後ろの処理は走らないはずなので、後続に何が書いてあっても関係ないはず。なので、認証の問題ではなくてPAMのアカウント/セッション/パスワード管理がらみの問題かな、と判断し試行錯誤すること数日。

結局うまくいかないので、いよいよソースを追いかけてやっと原因がわかりました。

PAMの設定のauth(認証)サービスの部分は実は2回なめられます。実際の認証処理と、エラーメッセージにあるpam_setcred()の処理です。実際の認証処理ではcontrol-flagではマニュアル通りに処理されますが、pam_setcred()の処理でなめられるときは、sufficientだけ動作が異なり、成功しても処理が継続されるのです。PAMのソースに思いっきり書いてありました


/*
* For pam_setcred() and pam_chauthtok() with the
* PAM_PRELIM_CHECK flag, treat "sufficient" as
* "optional".
*/

なんでこんな実装になってるんでしょうね。いやなんか意味はあるんでしょう。しかしみんなよくこんなの悩まずに設定できるなぁ。だれも使ってないのか?

理由がわかったところでどう設定すべきか改めて考えて結局こうなりました。

auth            requisite       pam_opie.so             no_warn no_fake_prompts

とにかくシンプルに考えると、うちの要件ではワンタイムパスワード認証に成功するかどうかが全て、でいいのです。

なまじデフォルト設定をベースに考えると失敗する、という事例でした。


タグ

www.flickr.com
This is a Flickr badge showing public items from suzukis tagged with japan. Make your own badge here.

最近の話題 RSS feed

最近のコメント

この日記のはてなブックマーク数
メール("no-spam."を削除してください)