ワンタイムパスワードを使って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
とにかくシンプルに考えると、うちの要件ではワンタイムパスワード認証に成功するかどうかが全て、でいいのです。
なまじデフォルト設定をベースに考えると失敗する、という事例でした。