Ubuntu 20.04 SSHで公開鍵認証+Google Authenticator
SSHに公開鍵認証をつけているが、二要素認証としてgoogle-authenticator-libpamを設定してみる。
環境
$ uname -a
Linux ursula-T3600 5.13.0-30-generic #33~20.04.1-Ubuntu SMP Mon Feb 7 14:25:10 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.4 LTS"
NAME="Ubuntu"
VERSION="20.04.4 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.4 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
google-authenticator-libpamのインストール
Ubuntuのaptリポジトリからインストール
sudo apt update
sudo apt-get install libpam-google-authenticator
実行結果
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
libqrencode4
以下のパッケージが新たにインストールされます:
libpam-google-authenticator libqrencode4
アップグレード: 0 個、新規インストール: 2 個、削除: 0 個、保留: 76 個。
57.3 kB のアーカイブを取得する必要があります。
この操作後に追加で 190 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://jp.archive.ubuntu.com/ubuntu focal/universe amd64 libqrencode4 amd64 4.0.2-2 [23.6 kB]
取得:2 http://jp.archive.ubuntu.com/ubuntu focal/universe amd64 libpam-google-authenticator amd64 20170702-2 [33.7 kB]
57.3 kB を 0秒 で取得しました (526 kB/s)
以前に未選択のパッケージ libqrencode4:amd64 を選択しています。
(データベースを読み込んでいます ... 現在 369106 個のファイルとディレクトリがインストールされています。)
.../libqrencode4_4.0.2-2_amd64.deb を展開する準備をしています ...
libqrencode4:amd64 (4.0.2-2) を展開しています...
以前に未選択のパッケージ libpam-google-authenticator を選択しています。
.../libpam-google-authenticator_20170702-2_amd64.deb を展開する準備をしています ...
libpam-google-authenticator (20170702-2) を展開しています...
libqrencode4:amd64 (4.0.2-2) を設定しています ...
libpam-google-authenticator (20170702-2) を設定しています ...
man-db (2.9.1-1) のトリガを処理しています ...
libc-bin (2.31-0ubuntu9.7) のトリガを処理しています ...
google-authenticatorの設定
google-authenticator
対話式に質問に答えていくとQRコードが表示されるため、スマートフォンのアプリのGoogle Authenticatorで読み込む
Do you want authentication tokens to be time-based (y/n) y
Warning: pasting the following URL into your browser exposes the OTP secret to Google:
https://www.google.com/chart?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Your new secret key is: XXXXXXXXXXXXXXXXXXXXXXXXXX
Your verification code is XXXXXX
Your emergency scratch codes are:
XXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXXXXX
その後、いくつか設定に関して対話式で質問がくる。全部yesにしたが、DeepLで翻訳しておく。
Do you want me to update your "/home/jlandowner/.google_authenticator" file? (y/n) y
Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y
# 同じ認証トークンの複数回使用を認めないようにしますか?
# この場合、約30秒に1回のログインに制限されますが、
# 中間者攻撃に気づく、あるいは防ぐ可能性があります。
# そうしますか?
By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) y
# デフォルトでは、モバイルアプリによって30秒ごとに新しいトークンが生成されます。
# クライアントとサーバー間で起こりうる時間のズレを補正するために
# 現在時刻の前後の追加トークンを許可します。これにより
# 認証サーバーとクライアントの間で最大30秒のタイムスキューが発生します。もし
# 時刻の同期がうまくいかない場合は、デフォルトの3個の許可コード(1つ前のコード、現在のコード、次のコード)から、
# 17個の許可コード(8個前のコード、現在のコード、および次の8コード)までウィンドウを大きくすることができます。
# これにより、クライアントとサーバーの間で最大4分のタイムスキューを許容することができます。
# そうしますか?
If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y
# ログインしているコンピューターがログインの試行のブルートフォースに対して強化されていない場合
# 認証モジュールのレート制限を有効にすることができます。
# デフォルトでは、攻撃者は30秒に3回までしかログインを試行できないように制限されています。
# レート制限を有効化しますか?
sshdの設定
$ cat /etc/ssh/sshd_config
PasswordAuthentication no
...省略
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
#ChallengeResponseAuthentication no
ChallengeResponseAuthentication yes # 変えた
...省略
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication. Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive # 追加した
...省略
PAM設定(失敗)
いろんなサイトにはPAM設定の末尾に以下の行を設定するといいとあるので追加してみた。
sudo sh -c 'echo "auth required pam_google_authenticator.so echo_verification_code" >> sshd'
しかし、これを設定したら、参考サイトにあるように公開鍵認証+チャレンジレスポンス+Google Authenticatorの三段階認証になってしまった。
パスワード認証時はパスワード認証+Google Authenticatorの二段階認証になるので問題ないが、公開鍵認証ではチャレンジレスポンスをやめたい。
PAMについては以下のサイトが勉強になりました。
https://christina04.hatenablog.com/entry/pluggable-authentication-module
ソースリポジトリのサンプルにはauth required
で設定していたので、auth
でパスワードモジュールを実行しているPAMをpam_google_authenticator.so
に差し替えれば良いと思う。
まずはsshdの全量を見てみます。
$ sudo vim /etc/pam.d/sshd
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
@include common-auth
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
# account required pam_access.so
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login.
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session required pam_env.so # [1]
# In Debian 4.0 (etch), locale-related environment variables were moved to
# /etc/default/locale, so read that as well.
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context. Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password
次の4つのモジュールをインポートしているので中身を見てみる。 @include common-auth @include common-account @include common-session @include common-password
$ grep -v '#' /etc/pam.d/common-* | grep auth
/etc/pam.d/common-account:account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so
/etc/pam.d/common-auth:
/etc/pam.d/common-auth:auth [success=1 default=ignore] pam_unix.so nullok
/etc/pam.d/common-auth:auth requisite pam_deny.so
/etc/pam.d/common-auth:auth required pam_permit.so
/etc/pam.d/common-auth:auth optional pam_cap.so
pam_unix.so
がパスワード認証のモジュールかと思う。調べたらビンゴで/etc/pam.d/common-auth
がパスワード認証を行なっていることがわかった。
PAM設定
パスワード認証設定common-auth
をコピーしてGoogle Authenticator用のPAM設定google-auth
を作成する。
sudo cp -p /etc/pam.d/common-auth /etc/pam.d/google-auth
vim /etc/pam.d/google-auth
次のように修正する。
#
# /etc/pam.d/google-auth - authentication settings for google authenticator
#
# here are the per-package modules (the "Primary" block)
#auth [success=1 default=ignore] pam_unix.so nullok <---コメントアウト
auth [success=1 default=ignore] pam_google_authenticator.so echo_verification_code <---追加
# here's the fallback if no module succeeds
auth requisite pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth required pam_permit.so
# and here are more per-package modules (the "Additional" block)
auth optional pam_cap.so
# end of pam-auth-update config
sshdのPAM設定にパスワード認証の代わりにGoogle Authenticator設定を適用する
sudo vim /etc/pam.d/sshd
次のように修正する。
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
# @include common-auth <---コメントアウト
@include google-auth <---追加
...省略
これで設定を行うことができた。
確認
# 最初のPAM設定ではこのようにPassword: が出力されていた。
$ ssh myserver
Password:
Verification code: XXXXXX
# 正しいPAM設定後はPassword: が出力されず、Verification codeを入力するとちゃんとログインできた。
$ ssh myserver
Verification code: XXXXXX
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.13.0-30-generic x86_64)
# 試しに認証エラーを起こしてみる
$ ssh myserver
Verification code:
Verification code:
Verification code:
Received disconnect from 192.168.0.20 port 22:2: Too many authentication failures
Disconnected from 192.168.0.20 port 22
# ちゃんとロックアウトされる。
$ ssh myserver
jlandowner@myserver: Permission denied (keyboard-interactive).
参考サイト
https://github.com/google/google-authenticator-libpam
https://wiki.archlinux.jp/index.php/Google_Authenticator
https://blog.apar.jp/linux/12502/
https://christina04.hatenablog.com/entry/pluggable-authentication-module