setodaNote

忘れる用のメモ書き for Cybersecurity

ssh を Google Authenticator PAM module で二要素認証化する(CentOS 8)

以下の記事を教えてもらい、比較的簡単に ssh にワンタイムパスワード認証を追加できるようだったので CentOS 8 でも試してみました。 Raspberry Pi の場合と異なり、SELinux による制御を考慮する必要があったので、それを踏まえて設定しました。


設定方針

ID/パスワードに加えて、ワンタイムパスワードを用いた認証となるよう ssh に Google Authenticator PAM module を設定します。 *1

Google Authenticator PAM module の設定

  • Google Authenticator PAM module のインストール

    • sudo yum install epel-release
    • sudo yum install google-authenticator
  • QRコードを表示するための準備 *2

    • sudo yum install qrencode
  • ワンタイムパスワードの生成

    • google-authenticator -t -d -W -R 30 -r 3 -e 10 -f --secret=/home/${USER}/.ssh/.google_authenticator

設定ファイルの保存場所をデフォルトから変更しているのは、SELinux による制御で認証処理に失敗するのを避けるためです。

うまくいくと QR コードが表示されます。

f:id:soji256:20200517130738p:plain:w300
表示される QR コードのイメージ

これを以下のようなアプリで読み込み、登録します。*3 *4

確認としてワンタイムパスワードの試し打ちを求められます。 QRコードまたは直後に表示されている secret key をワンタイムパスワードアプリに登録し、 正しく登録できているか試し打ちをします。不要な場合は -1 でスキップできます。

Your new secret key is: ABCDEFGHIJKLMNOPQR123456789
Enter code from app (-1 to skip): -1
Code confirmation skipped
Your emergency scratch codes are:
  12345678
  98765432
  12345678
  98765432
  12345678
  98765432
  12345678
  98765432
  12345678
  98765432
$ 

最後に表示される緊急ログイン用のコードは忘れずに安全な場所に書き留めておきます。 なお、設定ファイル(今回であれば /home/${USER}/.ssh/.google_authenticator )にも記録されています。

sshd の設定

  • チャレンジレスポンス認証を有効にします。*5
    • sudo vim /etc/ssh/sshd_config
# 変更
ChallengeResponseAuthentication yes
#ChallengeResponseAuthentication no
  • 認証に Google Authenticator を利用するように設定します。
    • sudo vim /etc/pam.d/sshd
# 追記
auth required pam_google_authenticator.so nullok echo_verification_code secret=/home/${USER}/.ssh/.google_authenticator
  • 設定を再読み込み
    • sudo systemctl reload sshd *6

以上で設定は完了です。

接続テスト

  • ssh localhost

うまくいくと以下のように接続後のプロンプトが表示されます。確認できたら Ctrl+D でログオフしておきます。

$ ssh localhost
Password: 
Verification code: 123456

Last failed login: Sun May 17 12:34:56 JST 2020 from ::1 on ssh:notty
Last login: Sun May 17 01:23:45 2020 from ::1
$ 

Tera Term の場合は以下のように「キーボードインタラクティブ認証を使う」にチェックを入れて接続します。

f:id:soji256:20200517132654p:plain:w400
Tera Term を使った接続 (1)
f:id:soji256:20200517132508p:plain:w300
Tera Term を使った接続 (2)

付録 A: 認証コードが正しいのにログインできない

他の記事を参考にワンタイムパスワードの設定をした際に、 ワンタイムパスワードのコードが正しいらしいものの、うまく認証されない事象に遭遇しました。

結果的には SELinux の制御によって認証処理の一部が失敗していることが原因でした。 その際の原因調査についてメモを残しておきます。

設定ファイルの確認

まず設定ファイルが正しく更新されているか確認しました。 これについては特に問題ないようでした。

  • 設定ファイルの確認

    • sudo vim /etc/ssh/sshd_config
    • sudo vim /etc/pam.d/sshd
  • sshd に設定を確実に反映するためリスタート

    • sudo systemctl restart sshd *7

ログの確認(ログイン関連)

次にログインに関するログを確認して失敗に関するエラーがないか確認しました。

  • ログの確認
    • sudo tail -n 20 /var/log/secure

sshd 関連のログを探します。

May 17 12:34:18 localhost sshd(pam_google_authenticator)[37213]: Accepted google_authenticator for infected
May 17 12:34:18 localhost sshd(pam_google_authenticator)[37213]: Failed to create tempfile "/home/infected/.google_authenticator~rBs26i": Permission denied
May 17 12:34:18 localhost sshd(pam_google_authenticator)[37213]: Failed to update secret file "/home/infected/.google_authenticator": Permission denied
May 17 12:34:20 localhost sshd[37210]: error: PAM: Authentication failure for infected from ::1
May 17 12:34:22 localhost sshd[37210]: Connection closed by authenticating user infected ::1 port 60116 [preauth]

冒頭に「Accepted google_authenticator」とあるので、認証コードは正しく承認されているように見えます。 しかし、直後に「Failed to create~」「Failed to update」という記述があり、なんらか処理に失敗しているとわかります。 なお、ログ中の infected はユーザ名なので、環境によって異なります。

どうもワンタイムパスワードの設定ファイルに関する処理に失敗しているようです。 当該ファイル「.google_authenticator」のパーミッションは 600 になっていました。 試しに chmod 666 .google_authenticator として 666 に変更してみます。

May 17 12:34:35 localhost sshd(pam_google_authenticator)[36991]: Secret file "/home/infected/.google_authenticator" permissions (0666) are more permissive than 0600
May 17 12:34:35 localhost sshd(pam_google_authenticator)[36991]: No secret configured for user infected, asking for code anyway.
May 17 12:34:48 localhost sshd(pam_google_authenticator)[36991]: Invalid verification code for infected
May 17 12:34:50 localhost sshd[36988]: error: PAM: Authentication failure for infected from ::1
May 17 12:34:50 localhost sshd[36988]: Connection closed by authenticating user infected ::1 port 60114 [preauth]

違うそうじゃないと怒られます。むしろ 666 であることを指摘されているので、 chmod 600 .google_authenticator として 600 に戻しておきました。

ログの確認(SELinux 関連)

以前に SELinux の影響でうまくファイルにアクセスできないことがあったので、SELinux 関連のログを確認しました。

  • ログの確認
    • sudo cat /var/log/messages | grep 'SELinux is preventing' | less
May 17 12:34:18 localhost platform-python[37425]: SELinux is preventing /usr/sbin/sshd from setattr access on the file .google_authenticator~FnhmUt.#012#012*****  Plugin catchall (100. confidence) suggests   **************************#012#012If you believe that sshd should be allowed setattr access on the .google_authenticator~FnhmUt file by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'sshd' --raw | audit2allow -M my-sshd#012# semodule -X 300 -i my-sshd.pp#012

SELinux が .google_authenticator っぽいファイルへのアクセスについて怒っているようなログが残っていました。 少し読みにくいので #012 を改行に変えて書き直します。

May 17 12:34:18 localhost platform-python[37425]: SELinux is preventing /usr/sbin/sshd from setattr access on the file .google_authenticator~FnhmUt.

*****  Plugin catchall (100. confidence) suggests   **************************

If you believe that sshd should be allowed setattr access on the .google_authenticator~FnhmUt file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c 'sshd' --raw | audit2allow -M my-sshd
# semodule -X 300 -i my-sshd.pp#012

sshd が不用意に許可されていないファイルにアクセスしていることを SELinux が検知して止めているようです。

対処方法については具体的なコマンドが示されていました。やさしい。。。

ですが、今回の場合はこの指示のとおりのコマンドを入力してもうまくいきません。

これについて、エラー文字列で調べたところ、以下のサイトに解決方法が記載されていました。

ここに書かれている「Plan B」を試してみます。 要は sshd が既にアクセス許可を得ている.ssh/ 配下にファイルを持ってくればいいという方法です。

以下の通り設定を追加して、設定した場所にファイルを移動させます。

  • sudo vim /etc/pam.d/sshd
    • auth required pam_google_authenticator.so secret=/home/${USER}/.ssh/.google_authenticator

上記の方法で無事にログインできるようになりました。*8

付録 B

google-authenticator のオプションについて

Option Description
-t ワンタイムパスワードを時間ベースの方式 (TOTP) で生成します。
-d 同じワンタイムパスワードの再使用を禁止します。
-R M -r N M 秒あたりのログイン回数を N 回に制限します。
-e 緊急ログイン用コードの生成数を指定します。
-f 設定ファイルの保存確認を省略します。
--secret 設定ファイルの保存場所を指定します。

pam_google_authenticator.so のオプションについて

Option Description
nullok ワンタイムパスワードを未設定(.google_authenticator が存在しないユーザ)の場合は
ワンタイムパスワードによる認証を省略することを許可します。
echo_verification_code ワンタイムパスワードを入力する際、そのまま画面に表示します。
secret ワンタイムパスワードの設定ファイルの場所を指定します。

テストに用いた CentOS のバージョン等

$ cat /etc/redhat-release 
CentOS Linux release 8.0.1905 (Core)

$ uname -a
Linux localhost.localdomain 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

参考文献

 

 

更新履歴

  • 2020/05/17 新規作成
  • 2020/05/17 sshd の設定後に reload するよう追記。アプリの表記を「iPhone」から「iOS」に変更。
  • 2020/05/17 冒頭に Google Authenticator PAM module 配布サイトへのリンクを追加。
  • 2020/05/18 Microsoft Authenticator の公式サイトを本文中から参考文献に移動。
  • 2020/05/18 一部文言を修正。
  • 2020/05/21 Google Authenticator アプリについての注釈を追記。

*1:公開鍵認証を利用している場合は少しだけ設定方法が異なります。参考文献に記載したサイトなどをご参照ください。

*2:QRコード読み取りではなく、表示されるシークレットキーを手打ちすることでも登録できます。

*3:何をどこまで重視するか次第ですが、 Microsoft Authenticator は iCloud などと連携できるので端末変更時の移行が楽です。

*4:Google Authenticator も Android 版のバージョン 5.10 (2020年5月頃リリース)では端末間でのデータ移行ができるようになったようです。なお、記事執筆時点で iOS 向けにはアップデート版のリリースはされていません。

*5:Google Authenticator もチャレンジレスポンス認証の一種ということのよう。

*6:再読み込みしなくても反映されているようにも見えましたが、sshd がどのタイミングで設定情報を確認しているのかまで未確認のため、念を入れて再読み込みしておきます。

*7:設定ファイルの再読み込み sudo systemctl reload sshd でもよいかも。

*8:公式サイトの issue にも同様のトラブルシューティングが記載されています。 -> https://github.com/google/google-authenticator-libpam/issues/101