最近二要素認証だったりでPAMを使うことが増えました。
ですが、二要素目が keyboard-interactive
になることが多く、複数の二要素認証を使い分けようとするとどうすれば良いか困ったのでメモとして残しておきます。
ただ、色々試していてこれで行けそうという感じなので、本当に理解が正しいかはわかりません(PAMの設定は難しすぎる)。
こういう意味だ、とかこうした方が良い、などありましたらご指摘下さい。
主に auth
の箇所について書いてます。
環境
以下の環境で試しました。
- OS
- CentOS 6.5
- PAM
- 1.1.1
- OpenSSH
- 5.3p1
SSHの設定
今回はDUOとYubikeyが利用したいとします。
その場合、sshd_configに分けて書ければ良いのですが、大体以下のようになってしまいます。
$ cat /etc/ssh/sshd_config
...
RequiredAuthentications2 publickey,keyboard-interactive
OpenSSHのバージョンによっては以下のようになります。
AuthenticationMethods publickey,keyboard-interactive
Yubikeyの場合もDUOの場合も keyboard-interactive
となってしまいsshdの設定では使い分けできません。
そこで、何とか分けられないかなと思ってPAMの設定をしてみました。
PAMの設定
PAMでは成功した場合や失敗した場合の処理が設定可能です。
詳細は参考ページを見て頂けると分かると思います。
PAMでは基本的に上から順にチェックされていきます。
例えば、以下のように書いた場合だと success=2
というのが成功した場合に2行スキップされることを意味します(pam_permitへ行く)。
default=ignore
は失敗した場合は無視して次の行へ移動することを意味しています。
auth [success=2 default=ignore] pam_unix.so nullok_secure
auth [success=1 default=ignore] pam_winbind.so krb5_auth try_first_pass
auth requisite pam_deny.so
auth required pam_permit.so
このようにスキップをうまく使えばif文のように書けるので特定のユーザだけ別のPAMで認証させることができそうです。
pam_succeed_if.so
というのがあるので、これを使ってみます。
以下のように書けるかと思います(コメントのあとは目印として記号を振っているだけです)。
auth required pam_sepermit.so
auth [success=ignore ignore=ignore default=1] pam_succeed_if.so user in test1:test2 # - (a)
auth [success=done default=die] pam_duo.so # - (b)
auth [success=done default=ignore] pam_yubico.so id=XXXXX key=hoge authfile=/etc/yubikey_mappings # - (c)
auth required pam_deny.so # - (d)
(a) まず pam_succeed_if.so
を使ってユーザの判定を行っています。
user in test1:test2
のところでtest1かtest2のユーザの場合のみ成功を返します。
success=ignore
なので、成功した場合は次の行(b)へ行き、 pam_duo.so
で認証が行われます。
失敗した場合はdefault=1なので1行スキップされ、 pam_yubico.so
の認証(c)が行われます。
ちなみに ignore=ignore
がないとエラーが出ますが、まだよく分かってません。
(b) ここでは success=done
なので成功した場合は終了、 default=die
なので失敗した場合も終了します。
(c) success=done
なのでここで成功すれば終了で、 default=ignore
なので失敗した場合は次の行(d)へ行きます。
(d) 最後に pam_deny.so
に行き失敗します。
まとめるとユーザがtest1かtest2の場合はDUOで認証され、それ以外のユーザの場合はYubikeyで認証されます(多分そうなっているはず。。)
擬似コード
口で説明してもよくわからないと思うので pam_succeed_if.so
以降に関して擬似コードを書きました。
大枠こんな感じだろう、と僕の中では思ってますが、正確ではない可能性があります。
正確な挙動を知ってる人がいたら教えてください。
if user in ["test1", "test2"]:
result = duo()
if result == success:
return success
else:
exit(1)
else:
result = yubico()
if result == success:
return success
else:
deny()
参考