このサーバーのBlogやメールサーバーへのログインをSSLクライアント証明書による認証に切り替えた話を書いた。
このとき今後の課題についてこんなことを書いた。
あと類似するところでSSHのログイン用の秘密鍵の管理も課題がある。
ローカルに配置するとその端末でしかログインできない、
かといってオンラインストレージにおくと流出の懸念なしとは言えない。
というわけで、SSHのログイン方法の見直しを行っていた。
オンラインストレージからの流出は本当に恐れるべきことなのかというのはある。
とはいえ、鍵データだけに依存する状況もよくない。
一応、鍵データを紛失したときのワークアラウンドとしては、VPSのコントロールパネルから操作するという方法がある。
これはsudoが効かなくなったときのワークアラウンドでもある。(コンソールではrootへのログインもできるので)
ただ、SSHのログインに使える「物」が、鍵データ以外にあってもよいと思う。
いろいろ調べて、いろいろ考えた結果、
鍵データ または Google認証システムのワンタイムパスワード+Linuxのパスワードというところに落ち着いた。
まず、SSHの鍵認証は単独でも十分に安全性が高いといってよいだろう。利便性も高い。
ただし、鍵データの管理に十分注意が必要なので、本来は鍵データはむやみやたらに持ち出さないだろうと。
鍵データを持っていない場合、あるいは紛失した場合の代替手段としては「物」と「知識」による2要素認証だろうと。
知識はLinux自体のパスワードでよいと思うが、物を何にするかは少し迷った。
住信SBIネット銀行のログインロック機能のように、端末操作を行った後、しばらく認証が通る方法も考えた。
ただ、SSHをどんなときに使うかと考えてみると、他のサービスが正しく動作していないときに使うことも多い。
そうなるとSSH単独で他のサービスに依存せずに動く方式がよいだろうと。
そこでGoogle認証システムである。
Googleとは付いているけど、Googleが開発したってだけで、Googleのサービスに依存して動くものではない。
Google認証システムはワンタイムパスワードを生成するアプリで、このアプリの入った端末はオフラインでもよい。
オープンソースのワンタイムパスワードのシステムとして普及したものである。
そこら辺にいくらでも導入方法は書いてあるが、ここにも書いておく。
SSHでGoogle認証システムを使う場合は、PAM認証のプラグインとして導入する。
yumでインストール出来なかったので、ソースコードでダウンロードしてきてインストールする。
google/google-authenticator-libpam (GitHub)
書いてある通りにビルドすると /usr/local/lib/security/ にインストールされるので、/etc/pam.d/sshdの先頭に次の一行を追加する。
auth requisite /usr/local/lib/security/pam_google_authenticator.so
これにより、Google認証システムによるワンタイムパスワード認証が通れば、従来のパスワード認証に進むということになる。
requisite を require にすると、失敗してもそれを隠して次に進む。
どちらがよいかは場合によるだろうが、今回はワンタイムパスワードでだめでしたとすぐわかる方がよいだろうと。
これでsshdのPAM認証がワンタイムパスワード+Linuxのパスワード認証 になったので、これを使う設定をsshd_configにする。
PasswordAuthentication no
ChallengeResponseAuthentication yes
UsePAM yes
パスワード認証とチャレンジレスポンス認証の両方を切ると鍵認証強制になるが、
チャレンジレスポンス認証を有効化した上で、PAM認証とすると、先ほど設定したPAMの認証が使えると。
あとは各ユーザーでGoogle認証システムの設定を行う。
$ google-authenticator -t -d -e 0 -r 2 -R 30 -w 2 -Q UTF8 -l "Libserver SSH"
Google認証システムの初期化にはQRコードを使うと便利なので、qrencodeをインストールしておく。
その上でこのコマンドを実行すると、コンソールに巨大なQRコードが表示される。
これをAndroidまたはiOSにインストールしたGoogle認証システムで読み取ると、
端末にワンタイムパスワードが出るので、これを確認に入力すれば、認証情報が~/.google_authenticatorに格納される。
いろいろオプションを付けたが、これを付けないといろんなことを聞かれるので。
“-t -d”は時間ベースのワンタイムパスワードで同一コードの再利用禁止、
“-e 0″は非常用コードなし、これはGoogle認証システムが使えないときのワークアラウンドは鍵認証と考えているから。
“-r 2 -R 30 “は30秒間に2回まで再試行可能、”-w 2″は最近2回(30秒ごと更新なので1分以内)のコードが有効、
“-Q UTF8″はUTF-8環境でのQRコード表示、-l オプションはGoogle認証システムでの表示名。
なお、QRコードが表示できない場合は、secret keyの文字をアプリに入れるか、相当するQRコードを作ればよい。
GoogleのQRコード生成用URLで生成してもよいが「キーがGoogleに漏れるから注意」と書かれている。
これで、あとは実際にSSHで使うだけ。
Using username "hidemaro".
Keyboard-interactive authentication prompts from server:
| Verification code: (ワンタイムパスワードを入力)
| Password: (Linuxのパスワードを入力)
End of keyboard-interactive prompts from server
まぁこんな感じですね。
この認証方式ってSFTPでも使えるのかなと、FileZillaの設定を見てみたら「インタラクティブ」てのがあるんですね。
これを選ぶと、対話的に2つのパスワードを聞いてくれる。
Pageantに鍵データを読み込んでいれば何もきかれずログイン成立となる。
以前はFileZillaで鍵認証するにはPageantで鍵データを読み込んだ状態で、ダミーのパスワードを設定してたものだが。
これが完了したところで、新しい鍵データを作って、改めて公開鍵を登録し直して、秘密鍵はローカル保存にした。
秘密鍵が使えないときは2要素認証で、Google認証システムが使えないときは鍵認証で、これで安心ですね。
一度ログインできれば、新しい鍵を登録することもできるし、
PAMつながりでもう1つ。
パスワード見直しの一環で、Linuxのパスワードも見直したが、けっこう長いパスワードになった。
ログインするときはよいが、sudoするたびにこの長いパスワードを入れるのはいやだ。
パスワードなしでsudoできるようにしてもよいが、ちょっとなぁと。
そこでsudo用のパスワードを別設定して、数字数桁のPINコードで認証するようにした。
pam_userdb.soというモジュールを使って、別に用意したパスワードデータベースで認証出来るようだ。
ただ、これの使用例を調べると、平文でパスワードをデータベースに格納しているので、それはおかしいだろうと。
$ echo "username" > db.in
$ perl -e "print crypt('xxxxxx',sprintf('\$6\$%08x',rand 0xFFFFFFFF)).\"\\n\"" >> db.in
# db_load -T -t hash -f db.in /etc/sudo_pw.db
ユーザー名とパスワードを交互に書いたテキストファイルを作って、/etc/sudo_pw.db のデータベースに格納するのだが、
pam_userdb.soではcryptされたパスワードを受け入れることも出来る。
じゃあ、どうやってcryptするのかって、perlのcrypt関数を使うのがよいだろうと。
引数の2つ目に暗号化方式とSaltを指定するのだが、”$6$xxxxxxxx”とすると、
SHA512方式(最近のLinuxでは標準)で、xxxxxxxxというSaltを付けて、1つ目の引数を暗号化するという意味になる。
Saltは乱数を16進数表記にすることで8文字作るようにした。こんなもんでいいでしょう。
その上で、/etc/pam.d/sudo にsudo時の認証方式を記載する。こちらはまっさらにして次の通り。
auth required pam_userdb.so crypt=crypt db=/etc/sudo_pw
account required pam_userdb.so crypt=crypt db=/etc/sudo_pw
session optional pam_keyinit.so revoke
password requisite pam_cracklib.so try_first_pass retry=3 type=
/etc/sudo_pwというデータベース(dbという拡張子がないことに注意)に、パスワードはcrypt形式で暗号化したので認証と。
keyinit.soはオリジナルの設定、pam_cracklib.soはinclude先のsystem-authにあったから書いたけど、意味があるかは知らない。
あと、sudo実行時に表示されるプロンプトに”password”って書いてあると勘違いしそうなので、
/etc/sudoers (visudoコマンドで編集する)でプロンプトを変更した。
Defaults passprompt="PIN for %u:"
これでPINを入力するように読めるからいいんじゃないかなと。