SHIINBLOG

Google Authenticatorを使ってSSHに二段階認証を組み込んでみる

最近、ボットネットによるDDos攻撃とかいろいろ耳にするので、自宅サーバの(SSHの)セキュリティの強化を本気で考えてみる。

とりあえず、Password認証から公開鍵暗号方式に変更。
それだけだと、面白みもないので前々から考えてた二段階認証を組み込んでみる。

二段階認証にはGoogle Authenticatorを使用する。
とはいえ、いきなり自宅サーバに組み込んでログインできませんでした、では洒落にならないのでいったんクラウド上で検証を行った。

検証は以下の環境で行った。

また、構築は以下のサイト(英語記事)を参考にした。

1. Google Authenticator用のPAMのインストール

Google AuthenticatorをSSHで使用するためのPAMはUbuntuリポジトリにあるのでapt-getで普通にインストールできる。

sudo apt-get update
sudo apt-get install libpam-google-authenticator

2. Google Authenticatorの設定ファイルを生成

libpam-google-authenticatorをインストールしたらgoogle-authenticatorコマンドで設定ファイルを対話的に作成する。
ここでは対話をひとつひとつ記述していくことにする。

Do you want authentication tokens to be time-based (y/n) y

二段階認証に使うワンタイムパスワードを時間ベースのものにするかを設定する。
Google Authenticatorのスマホアプリは時間ベースのものなのでyを入力。
ちなみに入力したらターミナルの画面にQRコードが表示されるので、それをGoogle Authenticatorのスマホアプリなどで読み込めば、二段階認証用のワンタイムパスワードを取得できるようになる。

Do you want me to update your "/home/ubuntu/.google_authenticator" file (y/n) y

設定ファイルを書き出すかを設定する。 書き出さないと使うことができないので当然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) n

ワンタイムパスワードを一度しか使用できないようにするかを設定する。
複数セッションターミナルを開くことも想定されるため、ここではnを入力。
(セキュリティを強固にするだけならyの方がいい気もする)

By default, tokens are good for 30 seconds and 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. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

ワンタイムパスワードの有効期限の設定(?)だと思われる。
実は自分もよくわかっていない。
おそらく、有効期限の幅がデフォルトだと1.5分だが、4分に伸ばすかという意味だと思う。自身はない。
参考にしたページではnだったのでnを入力。

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回のレートリミットを設定するかどうか。
同じワンタイムパスワードを複数回使えることにしているので、ここはyでセキュリティを強化しておく。

これで対話式の設定は終了。
設定ファイルはホームディレクトリに.google-authenticatorという隠しファイルで存在する。

3. PAMの設定を変更する

Google Authenticator側の設定ファイルの準備ができたので、次にPAMの設定を変更する。

sudo vi /etc/pam.d/sshd
# Standard Un*x authentication.
#@include common-auth
# Standard Un*x password updating.
@include common-password
auth required pam_google_authenticator.so nullok

まず、4行目の@include common-authコメントアウトする。
理由はよくわからない。わからないが、参考にしたサイトはそう書かれてたんだ。

次に、末尾にauth required pam_google_authenticator.so nullokを追記する。
Google Authenticatorによるワンタイムパスワードによる認証を追加しつつ、nullokオプションをつけることで、Google Authenticatorを設定していないユーザはワンタイムパスワードを使用せずにログインすることができる。

4. SSHの設定を変更する

次にSSHサーバの設定を変更する。

sudo vi /etc/ssh/sshd_config
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
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

まず、49行目のChallengeResponseAuthenticationの値をyesにする。
次に、88行目のUsePAMの値をyesにする。 最後に、末尾にAuthenticationMethodsの値をpublickey,keyboard-interactiveにする。

上二つでPAMによるチャレンジレスポンス認証(ここではワンタイムパスワードによる認証)を可能にし、最後の一つで公開鍵認証とチャレンジレスポンス認証の二つを通らないとログインできないように設定している。
AuthenticationMethodsでは、publickeyが公開鍵認証、keyboard-interactiveがチャレンジレスポンス認証を意味する。
ちなみに、パスワード認証はpasswordと記述する。

5. SSHサーバの再起動

SSHサーバの設定変更を反映させるために、SSHサーバを再起動する。
このとき、かならずSSHのセッションを一つ以上つないだ状態で再起動すること。
最悪の場合、SSHでログインできなくなる可能性があるため、設定をもとに戻すために残しておくこと。
(まぁ、自分はそれが怖かったからこそクラウド上で検証を行ったわけですが)

sudo systemctl restart sshd

6. SSHログイン

SSHを再起動したら、RLoginなどのターミナルエミュレータでログインしてみよう。
Google Authenticatorのスマホアプリなどに表示されるワンタイムパスワードを入力してログインできれば成功だ。
ダメだった場合は、残しておいたセッションから設定ファイルを修正するなり、サーバを一から立て直すなりしてみるといいだろう。

おまけ) Google Authenticatorの設定をコマンドオプションで非対話式に書き出させる。

ChefやAnsibleなどからサーバの自動構築をかける際、対話式では困るのでコマンドオプションを試してみる。 幸い、クラスメソッドの記事の中に、コマンドオプションが載ってたので、google-authenticatorコマンドのヘルプも参考に自分の設定に近いと思われるオプションを試してみた。

google-authenticator -t -D -f -r 3 -R 30 -W

よーしログインしてみるぞ~、っとやってみるけどログインできない。

ということで、対話的に作成した場合と、オプションから作成したときで.google-authenticatorファイルの比較をしてみることにしてみた。

まず、対話的に作成した場合。

2B4W5FOSFMPQJCVN
" RATE_LIMIT 3 30
" TOTP_AUTH
41375110
40302207
67243582
78149499
99954973

そして、コマンドオプションで作成した場合。

3L5WLDSSSXJYCXPI
"RATE_LIMIT 3 30
" WINDOW_SIZE -1
" TOTP_AUTH
61414616
16375568
36481341
59544280
16815552

ん? なんかコマンドオプションのほうは" WINDOW_SIZE -1という行が追加されている。
指定しているオプションから考えると、対話的に作成した場合の4番目の質問、オプションでいう-Wが怪しい。
-w NというオプションでWINDOW_SIZENにできるみたい。
とはいえ、デフォルトの値はなんだ?

Google先生の力によりGoogle AuthenticatorのGithub issueに答えを見つける。

By default, tokens are good for 30 seconds. 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. If you experience problems with poor time synchronization, you can increase the window from its default size of +-1min (window size of 3) to about +-4min (window size of 17 acceptable tokens). Do you want to do so? (y/n)

そうか、デフォルトのWINDOW_SIZEは3なのか。
ということで、再度コマンドオプションを試してみる。

google-authenticator -t -D -f -r 3 -R 30 -w 3

今度はうまくいったので、Chefによる自動構築も夢じゃない、と思う。