Amazon Linux 2023 で Google Authenticator を試してみた
はじめに
チームのツールに Amazon Linux 2 で構築した SSH サーバーがあり、公開鍵認証と Google Authenticator の認証で SSH 接続しているのですが、いずれ Amazon Linux 2023 に置き換えねばなるまいということで、ログイン周りの検証を行いました。
Amazon Linux 2 との違い
- pam_tally2.so の代わりに pam_faillock.so を使う
- google-authenticator が標準リポジトリでサポートされた(AL2ではepel)
pam_faillock.so は PAM (Pluggable Authentication Modules for Linux) に組み込んで使うモジュールで、認証失敗時の振る舞いをコントロールします。google-authenticator は 2023 年 9 月のアップデートで入りました。神対応!
検証環境
Amazon Linux 2023 の docker image (2023.3.20240304.0) Arm 版
関連するモジュール
- google-authenticator.aarch64 1.09-5.amzn2023 @amazonlinux
- pam.aarch64 1.5.1-8.amzn2023.0.3 @amazonlinux
- systemd-pam.aarch64 252.16-1.amzn2023.0.1 @amazonlinux
- openssh.aarch64 8.7p1-8.amzn2023.0.8 @amazonlinux
- openssh-clients.aarch64 8.7p1-8.amzn2023.0.8 @amazonlinux
- openssh-server.aarch64 8.7p1-8.amzn2023.0.8 @amazonlinux
ログインユーザーの準備
OS ユーザー
ログインテスト用の OS ユーザーを作成します。
# groupadd classmethod # useradd user1 -d /home/user1 -s /bin/bash -g classmethod
~/.google_authenticator
OS ユーザーのホームディレクトリに Google Authenticator の初期設定をします。
# su - user1 $ google-authenticator -t -d -w 17 -u -f Warning: pasting the following URL into your browser exposes the OTP secret to Google: https://www.google.com/chart?... <<巨大 QR コード>> Your new secret key is: XXXXXXXXXXXXXXXXXXX Enter code from app (-1 to skip):
-1 かワンタイムパスワード(6桁の数字)を入れたら、emergency コードが表示されて終わりです。QR コードは MFA のツールに登録しておきます。(Google Authenticator、1password等)
Code confirmation skipped Your emergency scratch codes are: 17696864 89470026 77017408 51576606 69475647
ホームディレクトリに以下のファイルができます。
$ ls -la (中略) -r-------- 1 user1 classmethod 118 Mar 14 18:25 .google_authenticator
~/.ssh/authorized_keys
OS ユーザーのホームディレクトリに公開鍵の設定をします。
# su - user1 $ mkdir .ssh $ chmod 700 .ssh <<.ssh/authorized_keys に user1 の公開鍵を編集 >> $ chmod 600 .ssh/authorized_keys $ ls -la .ssh total 12 drwx------ 2 user1 classmethod 4096 Mar 12 09:21 . drwx------ 3 user1 classmethod 4096 Mar 14 18:33 .. -rw------- 1 user1 classmethod 107 Mar 14 18:40 authorized_keys
サーバーの準備
/etc/pam.d/google-auth
Google Authenticator 認証時のふるまいをコントロールするファイルを新規作成します。
#%PAM-1.0 auth requisite pam_faillock.so preauth auth [success=1 ignore=1 default=bad] pam_google_authenticator.so nullok debug auth [default=die] pam_faillock.so authfail auth sufficient pam_faillock.so authsucc
- 2行目
- ユーザーがロック状態にあるかチェックします。ロック状態なら認証処理を中止します。
- 3行目
- pam_google_authenticator.so が success か ignore を返せば 1 行飛ばして 5 行目の処理に移り、それ以外は認証失敗として 4 行目の処理に移ります。ignore はユーザーのホームディレクトリにファイル 「.google_authenticator」 が存在しない場合に発生します。
- 4行目
- 認証失敗時に実行されます。認証失敗の記録を保存し、モジュールスタック自体を終了します。
- 5行目
- 認証成功時に実行されます。認証失敗の記録をクリアします。
/etc/pam.d/sshd
既存ファイルの 2 行目を上記ファイルに置き換えます。
$ diff sshd.backup sshd 2c2 < auth substack password-auth --- > auth substack google-auth
/etc/security/faillock.conf
Google Authenticator 認証失敗の定義を決めます。
以下は 「900 秒 (15分) 以内に 6 回続けて認証失敗したら 3600 秒 (1時間) の間ロックアウトする」 という意味になります。audit はログ記録のため、silent はユーザー向けメッセージをオフにするために入れます。
audit silent deny = 6 fail_interval = 900 unlock_time = 3600
/etc/ssh/sshd_config
今回の公開鍵認証 + Google Authenticator 認証に必要な設定を入れておきます。
AuthorizedKeysFile .ssh/authorized_keys PasswordAuthentication no ChallengeResponseAuthentication yes UsePAM yes AuthenticationMethods publickey,keyboard-interactive
以上の設定を終えたら sshd を再起動します。
# systemctl restart sshd
SSH 接続してみる
SSH 接続すると、公開鍵認証、Google Authenticator 認証の順に入力が求められます。
それぞれ正しいパスワードを入れるとログインできました。
$ ssh -i <秘密鍵> user1@localhost Enter passphrase for key '<秘密鍵>': ***(正しいパスワード)*** (user1@localhost) Verification code: ***(正しいワンタイムパスワード)*** , #_ ~\_ ####_ Amazon Linux 2023 ~~ \_#####\ ~~ \###| ~~ \#/ ___ https://aws.amazon.com/linux/amazon-linux-2023 ~~ V~' '-> ~~~ / ~~._. _/ _/ _/ _/m/' Last login: Mon Mar 18 08:12:53 2024 from 127.0.0.1 $
faillock によるロックアウトを試してみる
次に、ワンタイムパスワードを 6 回連続で間違えてみます。(faillock.conf の deny の数)
$ ssh -i <秘密鍵> user1@localhost Enter passphrase for key '<秘密鍵>': ***(正しいパスワード)*** (user1@localhost) Verification code: ***(正しくないワンタイムパスワード)*** (user1@localhost) Verification code: ***(正しくないワンタイムパスワード)*** (user1@localhost) Verification code: ***(正しくないワンタイムパスワード)*** user1@localhost: Permission denied (keyboard-interactive). $ ssh -i <秘密鍵> user1@localhost Enter passphrase for key '<秘密鍵>': ***(正しいパスワード)*** (user1@localhost) Verification code: ***(正しくないワンタイムパスワード)*** (user1@localhost) Verification code: ***(正しくないワンタイムパスワード)*** (user1@localhost) Verification code: ***(正しくないワンタイムパスワード)*** user1@localhost: Permission denied (keyboard-interactive).
6 回間違えたためユーザーはロックアウト状態にあります。さらに接続しようとすると今度は preauth が効いてワンタイムパスワード要求もなく終了しました。
$ ssh -i <秘密鍵> user1@localhost Enter passphrase for key '<秘密鍵>': ***(正しいパスワード)*** user1@localhost: Permission denied (keyboard-interactive).
ユーザーがロックアウトされているかどうかは faillock
コマンドで確認することができます。Valid の V (Valid) はロック回数にカウントされているという意味です。deny の数に達していることが分かります。
$ sudo faillock --user user1 user1: When Type Source Valid 2024-03-18 08:24:56 RHOST 127.0.0.1 V 2024-03-18 08:24:58 RHOST 127.0.0.1 V 2024-03-18 08:25:00 RHOST 127.0.0.1 V 2024-03-18 08:25:15 RHOST 127.0.0.1 V 2024-03-18 08:25:17 RHOST 127.0.0.1 V 2024-03-18 08:25:19 RHOST 127.0.0.1 V
unlock_time (3600秒) が経過するとまたワンタイムパスワード要求が復活しますが、またそこで入力を間違えると以下のように上書きされ、古いレコードは I (Invalid) になります。
$ sudo faillock --user user1 user1: When Type Source Valid 2024-03-18 09:30:22 RHOST 127.0.0.1 V 2024-03-18 08:24:58 RHOST 127.0.0.1 I 2024-03-18 08:25:00 RHOST 127.0.0.1 I 2024-03-18 08:25:15 RHOST 127.0.0.1 I 2024-03-18 08:25:17 RHOST 127.0.0.1 I 2024-03-18 08:25:19 RHOST 127.0.0.1 I
正しいワンタイムパスワードを入れた場合はレコードがクリアされます。
faillock --user ユーザー名 --reset
コマンドでもクリアできます。
$ sudo faillock --user user1 user1: When Type Source Valid
おわりに
最初、ネットの記事(新旧・OSごちゃ混ぜ)を参考にしてたらだいぶ検証が迷走したのですが、わりとしっかり man に書いてありました。
- man pam.d
- man pam_faillock
- man pam_google_authenticator
座右の銘
困ったときは再起動
に追加で
まずは man を読め
を刻み、これからも生きていこうと思います。