Google Authenticatorを利用したSSH踏み台サーバを構築する

# 概要
IP制限が出来ない環境化におけるSSHの踏み台サーバ（Bastionサーバ）を構築します。

IP制限が出来ない分、セキュリティを強化する為に二要素認証でログイン出来るように設定します。

認証要素は以下の2つです。

- 公開鍵
- `google-authenticator` が生成するワンタイムパスワード

なお本記事ではAWS上での利用を想定しています。

# 主なユースケース
- リモートワークの際に外部ネットワークから企業が管理するAWSサーバにSSH接続を行う

# 構築環境
サーバ側のOSは `Amazon Linux 2` を利用します。
クライアントのPCはMacで作業を行っている事とします。

# 構築手順

## EC2インスタンスを作成する

EC2インスタンスを作成します。

スペックは最低レベルで十分です。

インスタンスの作成手順は調べるとたくさん出てくるのでここでは割愛させて頂きます。

## OSのアップデート

`sudo yum update` を実行します。

# ユーザーの追加

SSH認証を行う為のユーザーを追加します。

ここではユーザー `keitakn` を作成すると仮定します。

```
sudo useradd keitakn
sudo passwd keitakn
```

## SSH接続用のキーペアを作成（自身が使っているPC上での作業）
自身が使っているPC上でSSH接続用のキーペアを作成します。（Macを利用している想定）

詳しい手順は割愛させて頂きますが `-b 4096` を指定してなるべく暗号強度の高い鍵を生成する事をオススメします。

（参考）[お前らのSSH Keysの作り方は間違っている](https://qiita.com/suthio/items/2760e4cff0e185fe2db9)

```
ssh-keygen -t rsa -b 4096 -C "keitakn@example.com" -f your_key_name.pem
```

上記のコマンドの通りに実行すると以下の2つが生成されます。

- 秘密鍵
  - `~/.ssh/your_key_name.pem`
- 公開鍵
- `~/.ssh/your_key_name.pem.pub`

```
mkdir ~/.ssh
chmod 700 ~/.ssh
chmod 600 ~/.ssh/your_key_name.pem
```

## SSH接続を行うBastionサーバ上での作業（先程作成した `keitakn` ユーザーでの作業）

`~/.ssh/authorized_keys` に公開鍵の内容をコピーして下さい。（新規ファイルを生成）

ローカルPCと同じく権限を変更しておきます。

```
mkdir ~/.ssh
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```

## Google Authenticatorのインストール

`sudo amazon-linux-extras install epel`
`sudo yum install google-authenticator`

## `qrencode-libs` をインストール

QRコードで二要素目の認証を行う為に `qrencode-libs` をインストールします。

`sudo yum install qrencode-libs`

既にインストール済の場合はこの手順をスキップ出来ます。

## google-authenticatorの設定を行う（先程作成した `keitakn` ユーザーでの作業）

`google-authenticator` を実行します。

すると対話式のIFになるので順番に設定を行っていきます。

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

時間ベースのトークンを作成するか聞かれるのでyを設定します。

`Do you want me to update your "/home/{ユーザー名}/.google_authenticator" file? (y/n)`

めちゃくちゃデカイQRコードが出てくるので専用のアプリ（お使いのスマホでGoogle Authenticatorでアプリストアを検索すれば出ます）で読み取ります。

![QRCode.png](https://qiita-image-store.s3.amazonaws.com/0/71899/f7e8562e-082b-01f5-a782-96850a46d340.png)

QRコードの情報と一緒に以下のようなファイルを更新するか聞かれるのでyを設定します。

`Do you want me to update your "/home/{ユーザー名}/.google_authenticator" file? (y/n)`

この時に生成される `~/.google_authenticator` はスマホ端末を無くしたりした際の復旧に必要になるので何らかの手段で控えておきます。

次に以下のメッセージが出ます。これは同じワンタイムパスワードで複数回ログイン出来ないようにするものです。

```
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)
```

これは、時間ベースでワンタイムパスワードを発行するためクライアントとサーバーで時間のズレによってログインできない問題が発生することがあるため、ズレに対応するための余分なワンタイムパスワードを発行してよいか聞かれています。

特に問題ない場合は余分な物は発行しないほうが安全なため、nを選択します。

これは、ログインの試行回数を30秒に3回以下に制限するかの設定です。

```
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を選択します。

## SSHの設定を行う（rootでの作業）

`/etc/pam.d/google-auth` を以下の内容で新規作成します。

```
#%PAM-1.0
auth        required      pam_env.so
auth        sufficient    pam_google_authenticator.so try_first_pass
auth        requisite     pam_succeed_if.so uid &gt;= 500 quiet
auth        required      pam_deny.so
```

`/etc/pam.d/sshd` を修正します。

```
#auth       substack     password-auth # コメントアウト
auth       substack     google-auth  # 追加
```

`/etc/ssh/sshd_config` を変更します。

```
# ChallengeResponseAuthentication no という記述があるので↓に変更
ChallengeResponseAuthentication yes
# 追加↓
AuthenticationMethods publickey,keyboard-interactive
```

設定を反映するためsshdを再起動します。

`sudo systemctl restart sshd.service`

## 接続確認（自身が使っているPC上での作業）

これまでの手順が間違っていなければSSHログインの際にワンタイムパスワードを要求されるハズです。

確認してみましょう。

万が一設定が間違っていると二度とログイン出来なくなるリスクもあるので、現在SSH接続を行っているセッションは切らないほうが良いでしょう。

自身のMacの `~/.ssh/config` に以下の記述を書いておくと便利です。

```
Host bastion-keitakn
    HostName 54.250.39.100
    Port 22
    User keitakn
    IdentityFile ~/.ssh/your_key_name.pem
```

Host: sshコマンドで使う名前、分かりやすければ何でもOK
HostName: サーバのIPアドレス or ホスト名
Port: 通常は22を入れればOK
User: 先程作成したユーザー名
IdentityFile: 作成した秘密鍵のパス

`ssh bastion-keitakn` を実行します。

`Verification code:` を聞かれるのでアプリから6桁のコードを確認し入力します。

6桁の数字が正しければSSHログインが正常に行われるハズです。