OTP＋公開鍵認証でセキュアにSSHログイン

# 実現すること
公開鍵認証＋OTPの二段階認証でパスワードを無くし、楽チンでセキュアなSSHログインができるようになる。

# 前提
* AWSで配布されているAmazon LinuxのAMIを使用（多分他のディストリでも公開鍵認証の設定を追加すれば同じ事はできると思います）。
* スマートフォンかChromeにAuthenticatorをインストールしている事。

# デメリット・注意点
* 秘密鍵＋時刻でパスワードを生成するのでトークンとサーバーで時間が大幅にズレているとログインできない。
* 作業中、ちゃんと動作することを確認するまでSSHを切断しないこと。設定が失敗していると誰もログインできず**仮想文鎮**ができあがる。
* トークンを亡失するとログインできなくなり**仮想文鎮**ができあがる。
  * 使っていないスマートフォン等を活用しバックアップトークンを用意しておくことを推奨。
  * 但しリカバリーコードはある（後述）。
  * 秘密鍵自体のバックアップは厳禁（当たり前）。


# 手順

## インストール
```shell-session
[root@hostname ~]# yum -y install gcc pam-devel
[root@hostname ~]# wget http://google-authenticator.googlecode.com/files/libpam-google-authenticator-1.0-source.tar.bz2
[root@hostname ~]# bunzip2 libpam-google-authenticator-1.0-source.tar.bz2
[root@hostname ~]# tar xf libpam-google-authenticator-1.0-source.tar
[root@hostname ~]# cd libpam-google-authenticator-1.0
[root@hostname ~]# make
[root@hostname ~]# make install
```

## 鍵設定
```shell-session
[root@hostname ~]# su ec2-user
[ec2-user@hostname ~]$ /usr/local/bin/google-authenticator
Do you want authentication tokens to be time-based (y/n) y
　※時刻をベースにOTPを生成するか？なのでy
https://www.google.com/******************
　※QRコード表示用のURLこれをブラウザで開きQRコード（秘密鍵）をスマートフォンのAuthenticatorに読み込ませる
Your new secret key is: ABCDEFG
　※手動で鍵を設定する場合はこれを設定する
Your verification code is 012345
Your emergency scratch codes are:
01234561
01234562
01234563
01234564
01234565
  ※鍵が無くても各1回だけログインできるキーなので一応メモしてベッドの下や糠漬けの中等に隠しておく。
Do you want me to update your "/home/ec2-user/.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
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) y
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
　※とりあえず質問には全てYで答えておけばOK。
　※どんな意味か知りたい人は調べてね。
```

## sshd設定
```shell-session
[ec2-user@hostname ~]$ sudo su -
[root@hostname ~]# vi /etc/ssh/sshd_config
```
```diff
- ChallengeResponseAuthentication no
+ ChallengeResponseAuthentication yes
```

## pam設定
```shell-session
[root@hostname ~]# touch /etc/pam.d/google-auth
[root@hostname ~]# vi /etc/pam.d/google-auth
```
```diff
+ auth        required      pam_env.so
+ auth        sufficient    pam_google_authenticator.so try_first_pass
+ auth        requisite     pam_succeed_if.so uid >= 500 quiet
+ auth        required      pam_deny.so
```
```shell-session
[root@hostname ~]# vi /etc/pam.d/sshd
```
```diff
- auth       substack     password-auth
+ auth       substack     google-auth
```
```shell-session
[root@hostname ~]# sshd -t
[root@hostname ~]# service sshd restart
```

以上の手順で

0. ログインユーザー名入力
0. 公開鍵認証
0. OTP

という流れの二段階認証が実現できる。


#その他
* Ansibleなんかの外からいじるツールを使う場合は /etc/pam.d/sshd で変更した部分を一時的に戻せば二段階認証が働かなくなる。
* OTPは30秒単位で変わっていくが、あくまでサーバー（とトークン）内の時計が基準になるため、時刻にズレがあるとタイミングによってはログインを弾かれる。
* トークンが消滅してログインできなくなった場合、エマージェンシーキーがあれば下記サイトを参考にログインできるので鍵の再発行と再設定を行いましょう。
  * https://support.google.com/accounts/answer/1187538?hl=ja