AWS Single Sign-On を 多要素認証(MFA) に対応させる

(2019-10-25 Update)
AWS Single Sign-on 単体でMFAアプリを使用した多要素認証に対応したので、もう別途RADIUSを用意する必要はなさそうです。
https://aws.amazon.com/jp/about-aws/whats-new/2019/10/increase-aws-single-sign-on-security-with-multi-factor-authentication-using-authenticator-apps/


(2019-01-19 Update)
AWS Single Sign-on で ログインの際に E-Mail ベースの検証ができるようになりました。
https://qiita.com/daimatsu/items/666319c2209cbbcd4aa8

---

# 概要

- AWS Single Sign-On にMFA（多要素認証） を追加する方法
- FreeRADIUS と GoogleAuthenticator を利用して実装
   

# 参考資料
RADIUS サーバの構築は以下の AWS 公式ブログを元にしています。
Workspaces の多要素認証に関するものですが、RADIUSサーバの設定に関する情報はそのまま利用可能です。

https://aws.typepad.com/sajp/2014/10/google-authenticator.html


# 構成

- 多要素認証のデバイスとして、Google Authenticator をインストールしたスマホを利用します。
- 認証基盤として、EC2 インスタンス上に構成された Active Directory 環境に AD Connecttor を接続して利用します。
- RADIUSサーバーには FreeRADIUS　を利用します。

![スクリーンショット 0031-01-09 13.56.21.png](https://qiita-image-store.s3.amazonaws.com/0/323706/625346d1-301c-d740-2003-5416cc4cb7dc.png)



# 手順
1. スマホアプリ Google Authenticator のインストール
1. RADIUS Server の構築
2. AWS Directory Service の設定
3. AWS Single Sign-On の設定

## スマホアプリ Google Authenticator のインストール
こちらから、インストールします。

Android: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=ja
iPhone: https://itunes.apple.com/jp/app/google-authenticator/id388497605?mt=8

## RADIUS Server の構築

EC2インスタンス上に、RADIUSサーバーを構築します。


### FreeRADIUSとその他必要なパッケージのインストール

```bash
$ sudo yum -y update
$ sudo yum -y install freeradius freeradius-utils git gcc pam-devel qrencode qrencode-libs qrencode-devel autoconf automake libtool
```

### Google Authenticator PAM module をソースからインストール

GitHub からソースをダウンロードして、インストールします。

```bash
$ git clone https://github.com/google/google-authenticator-libpam.git
$ cd google-authenticator-libpam
$ ./bootstrap.sh  
$ ./configure
$ make
$ sudo make install
```

### OS の設定

- RADIUS 経由の認証を許可するグループを作成します。
- Google Authenticator では登録内容の説明として「ユーザ名＠ホスト名」が表示されるため、ホスト名には分かりやすい名前を付けておきます。 

```bash:グループ作成
$ sudo groupadd radius-enabled
```

```bash:ホスト名変更
# ホスト名は適宜、DNSで名前解決できる必要はありません。
$ sudo hostnamectl set-hostname mfa.example.com
```

```bash:/etc/hosts
# このEC2インスタンスのIPを指定
xxx.xxx.xxx.xxx mfa.example.com 
```

### FreeRADIUS の設定

PAMを使って認証を行うため、rootユーザーで動作させる必要があります。

```bash:/etc/raddb/radiusd.conf
--- radiusd.conf.def    2019-01-07 08:28:13.122132617 +0000
+++ radiusd.conf        2019-01-07 08:29:06.218050333 +0000
@@ -435,8 +435,8 @@
        #  member.  This can allow for some finer-grained access
        #  controls.
        #
-       user = radiusd
-       group = radiusd
+       user = root
+       group = root

        #  Core dumps are a bad thing.  This should only be set to
        #  'yes' if you're debugging a problem with the server.
```

`radius-enabled` グループのみ PAM を使うように設定します。

```bash:etc/raddb/users
--- users.def   2019-01-07 08:34:43.485527661 +0000
+++ users       2019-01-07 08:36:06.921398359 +0000
@@ -64,6 +64,9 @@
 #DEFAULT       Group == "disabled", Auth-Type := Reject
 #              Reply-Message = "Your account has been disabled."
 #
+DEFAULT        Group != "radius-enabled", Auth-Type := Reject
+       Reply-Message = "Your account has been disabled."
+DEFAULT        Auth-Type := PAM

 #
 # This is a complete entry for "steve". Note that there is no Fall-Through
```

```bash:/etc/raddb/sites-available/default 
--- default.def 2019-01-07 08:40:04.841029649 +0000
+++ default     2019-01-07 08:40:24.072999845 +0000
@@ -510,7 +510,7 @@

        #
        #  Pluggable Authentication Modules.
-#      pam
+       pam

        #  Uncomment it if you want to use ldap for authentication
        #
```

```bash
$ sudo ln -s /etc/raddb/mods-available/pam /etc/raddb/mods-enabled/pam
```

- FreeRADIUS が PAM で認証する際に、Google Authenticator の PAM モジュールを使うように設定します。
- `pam_google_authenticator.so` はパスの通っている場所にコピーするか、フルパスで書くなどしてください。

```bash:/etc/pam.d/radiusd 
--- radiusd.def 2019-01-07 08:42:40.052789114 +0000
+++ radiusd     2019-01-07 08:43:28.216714473 +0000
@@ -1,6 +1,9 @@
 #%PAM-1.0
-auth       include     password-auth
-account    required    pam_nologin.so
-account    include     password-auth
-password   include     password-auth
-session    include     password-auth
+#auth       include    password-auth
+#account    required   pam_nologin.so
+#account    include    password-auth
+#password   include    password-auth
+#session    include    password-auth
+auth requisite /usr/local/lib/security/pam_google_authenticator.so
+account required pam_permit.so
+session required pam_permit.so
```

RADIUS プロトコルでの接続を受け付けるサブネットと接続パスワードを設定します。 

```bash:/etc/raddb/clients.conf
--- clients.conf.def    2019-01-07 08:45:45.584501591 +0000
+++ clients.conf        2019-01-07 08:48:22.312258707 +0000
@@ -248,6 +248,11 @@
 #      secret          = testing123-2
 #}

+client shared-service {
+       ipaddr = x.x.x.x/y #実際にお使いの VPC ネットワークアドレス等を指定して下さい
+       secret = xxxxxxxxxx
+}
```

### FreeRADIUS の起動

```bash:
$ sudo systemctl enable radiusd
$ sudo systemctl start radiusd
```

### ユーザー登録
- Active Directory と同名のユーザを作成した上で、Google Authenticatorのユーザ設定を行う必要があります。 
- コマンドを実行すると、幾つか英語で質問が表示されます。全て y で構いません。
- **コンソールに表示される QR コードをスマホアプリの Google Authenticator でスキャンして登録します。**
    - 最初の質問の後に表示される URL をブラウザで開いても QR コードを取得できます。

```bash
$ sudo useradd -g radius-enabled username0 # 実際のユーザー名に合わせてください
$ sudo -u username0 /usr/local/bin/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?xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

[QR コード]

Your new secret key is: xxxxxxxxxxxx
Enter code from app (-1 to skip):
```


いくつかの質問の内容は以下のようになっています。

**Do you want authentication tokens to be time-based (y/n)**
時刻ベースの認証トークンを使用しますか？
    
**Do you want me to update your "/home/XXXXXX/.google_authenticator" file (y/n)**
"/home/XXXXXX/.google_authenticator" ファイルを更新しますか？
更新しない場合、表示されているQRコードでログインできません。

**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)**
同じ認証トークンを複数回利用するのを禁止しますか？この制限により、30秒に1回しかログインできなくなりますが、中間者攻撃に気づいたり禁止できる可能性が高まります。

**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)**
デフォルトでは、トークンは30秒ごとになっており、クライアントとサーバの時刻がずれていても対応できるように、現在時刻より1つ前や後のトークンでも許可します。もし、時刻同期の状況が悪く問題があれば、デフォルトの1分半のウィンドウを約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)**
ログインするコンピュータが総当たり攻撃に対して保護されていなければ、認証モジュールで回数制限を行うことができます。この制限ではデフォルトで30秒に3回より多くログインを試すことができないよう制限します。回数制限を実施しますか？

###ユーザー認証テスト

- [username] には実際のユーザ名を指定して下さい。
- [OneTimePassword] にはスマホアプリの Google Authenticator で表示されたワンタイムパスワードを入力して下さい。
- [IPAddress]　には RADIUS サーバのプライベート IP を入力して下さい。
- [RADIUSPassword]　には /etc/raddb/clients.conf に設定したパスワードを入力して下さい。


```bash
$ radtest [username] [OneTimePassword] [IPAddress]:1812 10 [RADIUSPassword]

Sent Access-Request Id 81 from 0.0.0.0:47762 to [IPAddress]:1812 length 78
        User-Name = "[username]"
        User-Password = "[OneTimePassword]"
        NAS-IP-Address = [IPAddress]
        NAS-Port = 10
        Message-Authenticator = 0x00
        Cleartext-Password = "[OneTimePassword]"
Received Access-Accept Id 81 from [IPAddress]:1812 to 0.0.0.0:0 length 20
```

## Directory Service の構築
- 既存の AD の環境に、AD connecter を接続して利用します。
- AD の環境は設定が終わっているものとします。

### AD Connector の設定
ディレクトリタイプは、「AD Connector」を選択します。
![スクリーンショット 0031-01-08 17.38.17.png](https://qiita-image-store.s3.amazonaws.com/0/323706/e8cb6dcc-fd19-4837-c3ed-d838f3b97fa8.png)

ティレクトリサイズは規模に合わせて選択してください。
![スクリーンショット 0031-01-08 17.38.42.png](https://qiita-image-store.s3.amazonaws.com/0/323706/8a4492a5-9c32-7a1e-f66f-20eab500e04f.png)

VPCとサブネットもご利用の環境に合わせて、選択してください。指定した VPC・サブネットに AD Connector が作成されます。 
![スクリーンショット 0031-01-08 17.39.08.png](https://qiita-image-store.s3.amazonaws.com/0/323706/8275a1bd-cff8-4b1a-7e6e-def2d9db9873.png)

ADへの接続情報を設定します。
![スクリーンショット 0031-01-08 17.39.58_1.png](https://qiita-image-store.s3.amazonaws.com/0/323706/e59e62b4-62ae-192d-1cb3-33fdb5ef720b.png)



###多要素認証の有効化

作成したディレクトリを選択し、「ネットワークとセキュリティ」タブから、多要素認証を有効化します。
![スクリーンショット 0031-01-10 10.14.09.png](https://qiita-image-store.s3.amazonaws.com/0/323706/bb275081-1695-fed0-66b8-36a3d93537b9.png)

RADISUサーバの情報を入力します。

- 表示ラベル、IPアドレス、共有シークレット を適宜設定してください。
- そのほかの項目はデフォルト値で構いません。

![スクリーンショット 0031-01-10 10.16.44_1.png](https://qiita-image-store.s3.amazonaws.com/0/323706/c985125c-365f-eada-fe2f-a8624afc821d.png)




## AWS Single Sign-On の設定

作成した Directory Service (AD Connector) を指定します。
![スクリーンショット 0031-01-10 10.42.20_1.png](https://qiita-image-store.s3.amazonaws.com/0/323706/a4e30211-b3c2-62d8-28bf-2c491763059c.png)

AWSアカウントにユーザーを割り当てます。
![スクリーンショット 0031-01-10 10.45.10_1.png](https://qiita-image-store.s3.amazonaws.com/0/323706/564b0c81-2df9-ed39-d21e-fc11f4aecfde.png)

ユーザーを検索して選択します。
![スクリーンショット 0031-01-10 10.55.36_1.png](https://qiita-image-store.s3.amazonaws.com/0/323706/7cebabbc-cc14-1f14-bb0e-4f3f02251fef.png)

アクセス権限をセットします。
![スクリーンショット 0031-01-10 10.58.31.png](https://qiita-image-store.s3.amazonaws.com/0/323706/da91f7d4-8a10-3acb-596d-8631cc5be986.png)


## ログイン
ユーザーポータルへアクセスし通常通りログインします。
<img width="340" alt="スクリーンショット 2019-01-16 22.39.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/323706/769211e1-6a1b-1e9f-b295-cc4eeb46dbe9.png">

確認コードを求められますので、ここでMFAコードを入力することになります。
<img width="344" alt="スクリーンショット 2019-01-16 22.40.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/323706/1ed37e25-e51e-4c36-1d1a-28b9fd68bce6.png">


#そのほかの情報

**Directory Service について**

- 既存のAD環境があることを想定して、AD connector を利用していますが、AWS Managed Microsoft AD を利用することも可能です。
- VPCとオンプレ間を、Directconnect や IPsecVPN で接続している場合、AD Connector の接続先をオンプレの AD 環境にすることも可能です。
- Simple AD は 多要素認証に対応していません。

**ワンタイムパスワードアプリについて**

- Google Authenticator PAM module を利用していますが、アプリとしては Google Authenticator 以外のものを利用することもできます。
- 例： [Authy](https://authy.com/), [IIJ SmartKey](https://www.iij.ad.jp/smartkey/)