認証面倒くさい。
LDAPで連携くんだり、PAMで認証したり、OAuthしたり、ユーザ認証をする方法は山ほどある。 でも、それぞれ一長一短があってそれらを解決するいい方法がほしいと思っていた。
WEBのユーザのログイン認証をSSHでやる
ひょんなことから、sshpass
というコマンドを知ったので、これをつかって、WEBなどのユーザ認証をローカルのLinuxユーザーとパスワードで出来るんじゃないかと思ってやってみた。
仕組み
フォームから受け取ったユーザ名パスワードをつかってSSHで認証してOKならセッションを発行する
ssh のコマンドはパスワードを受け付けないし、expectなどは面倒なので sshpass
コマンドを使うと解決する
サンプルコード
<?php function alternative_pam_auth_by_shell_ssh( $user , $pass ){ $allow_group='students'; //このグループに所属するユーザーだけに限定する if ( ! check_user_group($user, $allow_group ) ) { return false; } $sshpass = exec('/usr/bin/which sshpass'); $user_esc = escapeshellarg($user); $pass_esc = escapeshellarg($pass); $cmd = "{$sshpass} -p ${pass_esc} ssh -o 'StrictHostKeyChecking no' {$user_esc}@192.168.0.22 whoami 2>&1"; $str = exec($cmd,$out,$ret); if ( $ret > 0 ){ return false; } return strpos( $str, $user ) !== false; }
root に総当りされる・・・
このままだとroot に総当りされるので、WEBからのssh経由でログインできるユーザのグループを限定する
<?php function check_user_group( $user, $group ) { $user = escapeshellarg($user); $groups = exec('/usr/bin/which groups'); $cmd = "{$groups} '${user}'"; $ret = exec($cmd, $code ); $ok = strpos($ret ,$group) !== false ; return $ok; }
割りと解決する
ユーザのログインを別個に作るのは、面倒だし。登録画面も面倒だ、SSHはSSHで管理したいと思うし。
かといって、次のアイデアには各種の懸念もある。
- LDAPにユーザ管理を任せるの設定が煩雑だ
- LDAPだととldif を覚えるのがメンテ上のハードルになり教えるのが面倒だ。
- PAMだとwww-data に 読み取り権限を与える必要がある。
- あらゆるプログラムが/etc/shadow を読み取るわけにも行かないし。
- setuid したコマンドを別途用意するのも面倒だ。
- OAuthにたよるとSSHログインと管理が別になる
- SSHをOAuth対応させるとなるとAuthenticator必須になったりコレも面倒だ。
- MySQLをPAM認証してMySQLに投げるとshell経由じゃないと怒られる
どの方法も一長一短があってむずかしい。
SSH に丸投げでどうだろう。
そこで、ユーザー認証してくれるプログラムで常時起動してるものに認証を投げたら楽じゃんと思って試してみた。
sshにお願いするのはパスワードのチェックだけで権限などは別にいらないしセッション管理もWEBは独立してるので楽だ。 こういう専用のライブラリがアレば良いのだけれど。。。
しかたないんので こういう ssh のヒューリスティックなハックに頼ることになった。
どうせどの方法もメリットデメリットがあるので、一番手軽な方法があっても良いんじゃないかと思ったり。