OpenAMでリスクベースのLDAP + TOTP多要素認証


## Overview

- OpenAMでリスクベースの認証を実装する。
- OTPはTOTP(Time based One-time Password)を使用する。
- OATHの仕様に沿ったTOTPなのでOATH準拠のアプリ(Google Authenticator等)ならどの端末でもワンタイムパスワードを生成出来る。
- リスクベース認証はアダプティブ認証とデバイスプリント認証を組み合わせて、IPが特定の範囲外の場合、ログインに何度も失敗した場合、前回ログイン時刻が一定の時間以上、前回とデバイスの環境が大きく異なる場合などにOTPを要求するようにする。
- マルチレルムで認証データストアや認証方法、使えるリソースをレルムごとに分けれるようにする。

## Requirements

- OpenAM
- Google Authenticator

## Conditions

|項目|値|
|--:|--:|
|  最上位レルム| /|
|  最上位レルムドメイン| openam-proxy.example.com|
|  テスト用レルム | test |
|  テスト用レルムドメイン| openam-proxy-test.example.com|

## Install

OpenAMのインストールは以前書いた手順を参照します。

### TOTP認証の追加

- レルムの追加

既存のレルムで認証方法を追加すると設定を失敗したときにまったくアクセス出来なくなる可能性もあるので別レルムに分ける。
TOPより「アクセス制御」 -> 「新規」を選択。

|項目|値|補足|
|---:|---:|--:|
|名前 |test| 
| 親  | /  |
| レルム状態 | アクティブ |
|レルムまたはDNSのエイリアス|openam, openam-test.example.com, openam-test2.example.com, openam-proxy-test.example.com| このレルムで使用するFQDNのリスト|

「了解」をクリック作成する。

最上位のレルムについてもDNSのエイリアスを設定しておく。ここでアクセスするURLが決まる。
トップレベルドメインやセカンドレベルドメインが違う場合(example.com, example.net, example.org, hoge.com等)は「設定」 -> 「システム」 -> 「プラットフォーム」 -> 「Cookieドメイン」に該当するドメインを追加する。例: .example.org 


- hostsファイル編集

テスト用なので/etc/hostsで解決出来るようにする。

```/etc/hosts
192.168.34.10 openam.example.com
192.168.34.11 openam2.example.com
192.168.34.10 openam-test.example.com
192.168.34.11 openam-test2.example.com
192.168.34.100 openam-proxy.example.com
192.168.34.100 openam-proxy-test.example.com
```

- OATH認証の追加

「アクセス制御」-> レルム一覧から「test」 -> 画面上タブから「認証」-> モジュールインスタンスにOATHを「新規」をクリックし追加する。
作成後「OATH」をクリックしプロパティを編集する。OAuthではないので注意。

|項目|値|補足|
|---:|---:|---:|
|認証レベル | 10| 多要素認証なので高めの10を設定| 
|ワンタイムパスワードの長さ  |6  | ワンタイムパスワードの長さ. Google Authenticatorに合わせて6にする。| 
|秘密鍵の最小桁数 |8 | 格納する秘密鍵の桁数|
|秘密鍵の属性名 |title | 秘密鍵を格納するLDAPの属性名。とりあえずtitleにしたが適切なカスタムattributeを作成して入れた方がいい。| 
|使用する OATH アルゴリズム| TOTP| Time based One-time PasswordなのでTOTP|
|HOTPウインドウサイズ|100||
|カウンタ属性名|(空)||
|チェックサムの数字の追加|いいえ||
|トランケーションオフセット|-1||
|TOTPタイムステップ期間|30||
|TOTPタイムステップ数|2||
|最終ログイン時間属性|description| 最終ログイン時刻(Unixタイム)を格納するLDAP属性名。とりあえずdescriptionにしたが適宜カスタムattribute等追加して対応する|

- 認証連鎖の追加

「アクセス制御」-> レルム一覧から「test」 -> 画面上タブから「認証」-> 認証連鎖の「ldapService」を選択 -> インスタンスに「OATH」を追加 -> 「保存」をクリック。

- ntpdの設定

時刻にもとづいて認証しているのでクライアントとサーバーで大幅にずれると認証が上手くいかなくなる。
数秒なら許容出来るが100秒とかいくと無理だった。VM上で検証していてPCを閉じていたときにずれてハマった。

```bash
$ yum install ntp

$ vim /etc/ntp.conf

  restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap #192.168.1.0/24内からのntpリクエストは受け付ける
  server ntp.nict.jp # デフォルトのcentosのサーバは遅いのでこちらを使う。
  server ntp.jst.mfeed.ad.jp

$ ntpdate ntp.nict.jp
$ service ntpd start
$ chkconfig ntpd on
```

### 冗長化設定

冗長化自体のインストールは以前書いた手順を参照します。

TOPより「設定」 -> 「サーバーとサイト」 で新規サーバーを追加する。
ドメインはそれぞれ重複しないように設定していく。

#### サーバー

|サーバー名|サイト名|
|--:|--:|
|http://openam.example.com:8080/openam|test|
|http://openam2.example.com:8080/openam|test|
|http://openam-test.example.com:8080/openam|testrealm|
|http://openam-test2.example.com:8080/openam|testrealm|

#### サイト

|サイト名|プライマリURL|割り当てられたサーバー|
|--:|--:|--:|
|test|http://openam-proxy.example.com:80/openam|http://openam.example.com:8080/openam, http://openam2.example.com:8080/openam|
|testrealm|http://openam-proxy-test.example.com:80/openam| http://openam-test.example.com:8080/openam, http://openam-test2.example.com:8080/openam |

### リスクベース認証の追加

OpenAMはリスク評価に基づいて認証方法を柔軟に選択出来る。
例えば社外のIPからのアクセスの場合はOTP認証を追加する、過去に認証を失敗している場合は更に認証を要求する等。

#### Device ID(Match)

OpenAM 12.0.0からDevice Identifer情報に基づく認証が追加された。Device ID(Match), Device ID(Save)の２つ。Device Print ModuleはHOPTのみ対応で使い勝手が悪かったけどこちらは他の認証にチェイン出来るので色々使いやすい。
MatchはDeviceID(Save)で保存されたデバイス情報のリストに現在のデバイスがどれくらい合致しているかのチェックを行う。閾値を超えなければ成功、超えれば失敗。なお複数デバイス登録されている場合最も近いデバイスの値が用いられる。
条件は「十分」でサーバに登録されていないデバイスからの接続の場合他のOTP認証やLDAP認証にチェインすると便利。

##### Device ID(Save)

デバイス情報を保存する。条件は「必要」で認証連鎖の最後に追加しておく。これより前のモジュールで認証の成功、失敗に関わらず必ず次に進む条件（「オプション」か「必要」）にしておくと必ず保存出来る。あと最後に置いておくと最初の方に置くより書き込み負荷が小さいから。

#### アダプティブ認証

##### 認証失敗

過去に認証に失敗(一部分ではなく全体として失敗)していた場合リスクスコアが増える。
有効にするにはアカウントロック機能を有効にし、失敗した記録を認識させる必要がある。
アカウントロックは「アクセス制御」-> レルム選択 -> 上部タブ「認証」 -> 「すべてのコア設定」 -> 「アカウントロック」から有効に出来る。アカウントロックを有効にするとロックまでの許容回数、ロック時のメール通知、ロックアウト時間等設定等を出来る。

##### IPアドレスレンジ

特定のIPレンジの場合リスク値を増やす。例: 社外のIPの場合等。
ホワイトリスト形式で許可するIPの一覧を追加する。

設定例

- 10.0.2.15 (単一)
- 192.168.34.0/24 (レンジ)
- 10.0.2.0-10.0.2.100 (レンジ)

IPレンジを認識する点はロードバランサがある場合ロードバランサから見たIP。
ロードバランサの背後にある場合は「サーバーおよびサイト」より
サイト名→高度→com.sun.identity.authentication.client.ipAddressHeaderにX-Forwarded-Forをそれぞれセットする。


###### バグ

- OpenAM v11.0.0においてログレベルを「メッセージ」にしデバッグメッセージも表示させた状態かつIPレンジを有効にした状態でIPレンジに一つだけIPv4アドレスをレンジ指定なしで登録した場合`an IPv6 address should contain 8 shorts`とエラーが出る。

[[#OPENAM-3607] Adaptive IP check fails when message level debug enabled - ForgeRock JIRA](https://bugster.forgerock.org/jira/browse/OPENAM-3607) R7982で修正された模様。

**対策: 「サーバーおよびサイト」より各サーバのログレベルを「エラー」にする。**

##### IPアドレス履歴

ログインしたIPアドレスを記録し、記録にないIPの場合リスク値を増やす。
LDAPの属性名にiphistoryなどの項目を作りデータストアに認識させる必要がある。

|項目|値|
|--:|--:|
|IP履歴チェック|有効|
|プロファイル属性名|iphistory|
|成功したIPアドレスを保存|有効|

##### デバイス Cookie

デバイスにCookieをセットしその値が含まれていればチェックを成功とする。

##### 最終ログインからの経過時間 

最終ログインからの経過時間をCookieに保存し次回ログイン時設定した期間を超えていたらチェックを失敗とする。

##### 位置情報

アクセス元の国からリスク値を算出する。

```bash
curl -L -O http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz
gunzip GeoLite2-Country.mmdb.gz > GeoLite2-Country.mmdb
mv GeoLite2-Country.mmdb /var/lib/
```

- 設定

|項目|値|
|--:|--:|
|位置情報データベースの場所|/var/lib/GeoLite2-Country.mmdb|
|有効な国コード|jp|


- アダプティブ認証の追加

##### ※注意: アダプティブ認証ポストプロセスを追加しないと動作しない

TOPより「アクセス制御」 -> レルム一覧から「test」を選択 -> 
上部タブから「認証」を選択 -> モジュールインスタンスにタイプ「Adaptive」を追加。

**「レルム一覧」から「test」を選択 -> 「すべてのコア設定」 -> 「認証ポストプロセスクラス」に
「org.forgerock.openam.authentication.modules.adaptive.Adaptive」クラスを追加する**


認証連鎖にアダプティブ認証を追加する。条件は十分(認証に成功した場合は次の認証を実行しない、失敗した場合は次の認証を実行する)にする。


#### 認証連鎖のプロパティ例

|インスタンス|条件|オプション|
|--:|--:|--:|
|DataStore|必須||
|アダプティブリスク|十分||
|OATH|必要||

この認証連鎖の意味としては

1. データストア(LDAP)での認証での成功は必須。認証に成功すれば終了し、失敗すれば先へ進まない
2. データストアでの認証に失敗し、アダプティブ認証でリスクが高いと判断したアクセスの場合OTPへ進む
3. OATH認証の条件は必要(必ず成功が必要)なのでここで認証に失敗すると全体として失敗

という具合になる。


## References


[OpenAMが提供する様々な認証方式 （1/4）：CodeZine](http://codezine.jp/article/detail/6853)
[OSSによるアイデンティティ管理（2）：不正ログインを食い止めろ！ OpenAMで認証強化 (1/2) - ＠IT](http://www.atmarkit.co.jp/ait/articles/1310/17/news003.html)