Amazon Elasticsearch ServiceのKibana Cognito認証設定をゼロから最小限の設定で実現する


# はじめに
Amazon Elasticsearch ServiceではIAMによる認証機能が提供されています。この記事では、最初にElasticsearch Serviceの認証について概要を説明した後、ElasticsearchのKibana利用時にこの認証機能を設定する手順について説明します。

# Amazon Elasticsearch Service認証機能の理解
ソフトウェアのElasticsearchにはBasic認証などもありますが、Amazon Elasticsearch ServiceにはIAM認証しかありません。セキュリティ上はよいことですが、少し利用の敷居を上げることにはなります。
Amazon Elasticsearch Serviceでは、インフラ操作のAWS APIと、データ操作のElastcisearch APIの2種類のAPIがあり、そのどちらもIAM認証となります。AWS APIの方は、AWS CLIやSDKからの操作となるのでIAM認証もうまい具合に隠蔽してやってくれますが、Elasticsearch APIはクライアント側でIAM認証処理＝HTTPリクエストへの署名をしないといけなく（[Amazon Elasticsearch Service への HTTP リクエストの署名](https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/es-request-signing.html)）、これがめんどうです。かつ、クライアントアプリ側では便利なコンポーネントなどを使うことも多いと思いますが、このコンポーネントがAWSのIAM認証に対応しているかが問題になってきます（現状、ほとんどしていません）。Amazon Elasticsearch ServiceがBasic認証にも対応していたらどれだけ簡単かと思う瞬間です。
たとえば、Elasticsearchのフロントアプリ開発を便利にするReactivesearchはIAM認証に対応していません。正確に言うと、有志によってAmazon API GatewayのIAM認証に対応させているケースはあります（[Support for Signed Requests / AWS Elasticsearch #419](https://github.com/appbaseio/reactivesearch/issues/419#issuecomment-590547126)）。が、やはりめんどうです。
その他、AWS Amplifyを使う方法もあります。AWS Amplifyは認証やバックエンドのめんどうな設定を隠蔽して簡単にしてくれる非常に便利なサービスです。Elasticsearchもバックエンドとして使えます。ただし、使い方はDynamoDB Streamsからのインプットに限定されておりAppSync経由での利用しかできなく、かなり画一的な使い方しかできません。Reactivesearchのようなコンポーネントからも使えません。
詳しくは、「[Amazon Elasticsearch Service の認証・認可に関する面倒くさい仕様をなるべくわかりやすく説明する](https://muziyoshiz.hatenablog.com/entry/2019/11/30/144213)」が参考になります。
この記事では、AWSが標準で用意しているKibanaでのIAM認証設定のみを説明します。

# 作成するもの
Amazon Elasticsearch Serviceには、Access Policy（通常のAWSサービスにおけるリソースポリシーと同等）機能が用意されており、ここでユーザとして（PolicyのPrincipleとして）IAMユーザやIAMロールを指定するとIAMによる認証が有効となります。Access PolicyのPrincipleを"*（アスタリスク）"とすることで認証なしのアクセスとなります。
ただし、実際にIAM認証を機能させるにはクライアント側での対応も必要です。Elasticsearch Serviceのインフラ系を操作する場合はAWS CLIなどがクライアントとなりIAM認証もそのなかで実現してくれます。Elasticsearch APIを利用する場合は、認証のUIをなんらか実現する必要があります。ここでは、Kibana（実際にはElasticsearch Service自体）をクライアントとし、Cognitoと連携して認証UIを提供する構成を示します。また、Kibanaログインユーザは管理者のみが登録して使用する想定とします。
 
![ElasticsearchWithCognito.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603113/fd1847a1-0a25-387f-5a88-c0d5edd2d91e.png)

認証の流れとしては、このようになります
1. Elasticsearch ServiceのKibanaエンドポイント（URL）にアクセスする
2. Cognito User Poolにリダイレクトされる
3. Cognitoの認証画面でユーザID/パスワードを入れて認証する
4. Cognito ID Poolにて、Kibana（CognitoでAPP Clientとして登録）に紐付けられたIAM Roleのセッションキーがブラウザに返される
5. セッションキー等認証情報を持って再度Kibanaエンドポイントにアクセスする
6. Access PolicyのポリシーにしたがってElasticsearchの権限が与えられる

ユーザ側の認証画面と流れは、「[Kibanaにアクセスして確認](#kibanaにアクセスして確認)」のとおり。

# 事前準備
* Amazon Elasticsearch Service環境（参考：「[Amazon Elasticsearch Serviceで検索できる状態まで最速で立ち上げる](https://qiita.com/NorihitoYamamoto/items/5b2c63ee52215014c0f3)」）

# 手順
CognitoでユーザープールとIDプールの入れ物を作り、ユーザープールにユーザ登録し、Elasticsearch ServiceでKibana認証を有効化する（自動的にCognitoに登録される）流れになります。

## Cognitoの設定
### ユーザープールの作成
自己サインアップを禁止し管理者のみにユーザ作成を許可するようにする点と、ドメイン名の設定以外はデフォルトの設定で作成します。

1. AWS管理コンソールにログインし、Cognitoの画面に移動
2. 「ユーザープールの管理」ボタンを押す
3. 「ユーザプールを作成する」ボタンを押す
4. プール名に識別するための任意の名前を入れ「デフォルトを確認する」ボタンを押す
5. 左ペインの「ポリシー」をクリックする
6. 「ユーザーに自己サインアップを許可しますか?」で「管理者のみにユーザーの作成を許可する」の方にチェックを入れ、「変更の保存」ボタンを押す（その他パスワードポリシーも変えたければ変更する）
7. 「プールの作成」ボタンを押す
8. 左ペインの「アプリの統合」にある「ドメイン名」をクリックする
9. ドメイン名のプレフィックスに任意の名前を入れ、「使用可能かチェック」ボタンを押し、OKであれば「変更の保存」ボタンを押す

### IDプールの作成
認証プロバイダーの設定などはあとからElasticsearchの設定のなかで自動で行われるのでここでは設定せず、すべてデフォルトの設定のまま作成します。

1. 左上のグレーになっている「フェデレーティッドアイデンティティ」のリンクをクリックする
2. 「新しいIDプールの作成」ボタンを押す
3. 「IDプール名」に識別するための任意の名前を入れ、それ以外はデフォルトのまま（「認証されていない ID に対してアクセスを有効にする」にチェックを入れたまま、「認証プロバイダー」も設定しないままま）、「プールの作成」ボタンを押す
4. 「Identify the IAM roles to use with your new identity pool」ではそのまま「許可」ボタンを押し、認証ユーザ用ロールと未認証ユーザ用ロールが作成される
5. あとはそのまま「ダッシュボードに移動」ボタンを押し、元の画面に戻る

### ユーザープールへのユーザ登録
Kibanaにアクセスするユーザを登録します。管理者がユーザ管理するよう設定するために、ユーザ自身が管理したいときに使うメールアドレス等も入れません。

1. 左上のCognitoアイコンを押し、その後に出てくる「ユーザープール」ボタンを押す
2. 対象となるユーザープールの名前をクリックする
3. 左ペインの「全般設定」の「ユーザーとグループ」をクリックする
4. 「ユーザーの作成」ボタンを押す
5. ポップアップ画面で、ユーザー名を入れ、「この新規ユーザーに招待を送信しますか?」のチェックを外し、仮パスワードを入れ、電話番号とメールアドレスは空欄で、「電話番号を検証済みにしますか?」と「E メールを検証済みにしますか?」のチェックを外し「ユーザーの作成」を押す
6. アカウントのステータスがFORCE_CHANGE_PASSWORDのユーザが作成される

## Elasticsearch Serviceの設定
### Kibana認証の有効化の設定
1. AWS管理コンソールのElasticsearch Serviceの画面に移動
2. 対象のドメイン名をクリックする
3. 「ドメインの編集」ボタンを押す
4. 「Amazon Cognito 認証」で、「Amazon Cognito 認証を有効化」にチェックを入れ、「リージョン」にアジアパシフィック（東京）を選び、「Cognito ユーザープール」「Cognito ID プール」で上で作成したものを選択し、IAM ロール名やロールポリシーはそのままで「送信」ボタンを押す（*1）
5. ドメインのステータスがアクティブになるまで待つ

*1 この状態では、Cognito側のユーザープールとIDプールにElasticsearchのAPP Clientの設定が入れられるだけで、Kibanaで認証画面が出るようになるわけではありません。Cognitoのユーザープールのアプリクライアント（APP Client）やIDプールの認証プロバイダーにElasticsearchからの設定が入っていることが確認できます。

### アクセスポリシーの設定
Cognitoで認証されたユーザに紐付けられるIAM Roleだけが、Elasticsearch APIを操作できるようにアクセスオリシーを設定します。

1. Cognitoで認証されたユーザに適用されるIAM RoleのARN（識別するID）を確認するために、AWS管理コンソールのIAMの画面に移動
2. 左ペインの「ロール」をクリックし開く
3. デフォルトでは「Cognito_[IDプール名]Auth_Role」という名前でRoleが作られるため、この名前で検索し、「ロールARN」をコピーする
4. AWS管理コンソールのElasticsearch Serviceの画面に移動
5. 対象のドメイン名をクリックする
6. 「アクション」ボタンから「アクセスポリシーの編集」をクリックする
7. 「JSON定義のアクセスポリシー」で、下の[Kibana認証時のアクセスポリシー設定例](#kibana認証時のアクセスポリシー設定例)のとおり記述し、「送信」ボタンを押す
8. ドメインのステータスがアクティブになるまで待つ

### Kibanaにアクセスして確認
1. Elasticsearch Serviceのドメイン画面のKibanaのエンドポイント（URL）にアクセスする
2. 登録したユーザID／パスワードを入れる

<img width="349" alt="login.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603113/0ed7fcaa-7f2f-a0b7-7aea-6fe4eb3082a1.png">
3. 新しいパスワードとメールアドレスをきかれるので入力する（*2）
<img width="350" alt="changepwd.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603113/0162a723-a11b-4e97-a3c0-29f2e7bb6dff.png">
4. Kibanaの画面が表示される

*2 今回の設定とユーザ登録方法では、メールアドレスを入れてもとくにユーザにメールは届きません。またパスワードを忘れた際も、Forgot your password?リンクを押しても対応できません。管理者がAWS管理コンソールのCognito画面から登録し直す必要があります。

### Kibana認証時のアクセスポリシー設定例

```json:アクセスポリシー例

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::[アカウントID]:role/Cognito_[IDプール名]Auth_Role"
      },
      "Action": "es:ESHttp*",
      "Resource": "arn:aws:es:ap-northeast-1:[アカウントID]:domain/[ドメイン名]/*"
    }
  ]
}
```

以上です。

# Amazon Elasticsearch ServiceのIAM認証の制約
「[Amazon Elasticsearch Service認証機能の理解](#amazon-elasticsearch-service認証機能の理解)」で書いたとおり、Elasticsearch Service自体とKibanaのエンドポイントURLが同じで、同じアクセスポリシーが適用されるため、KibanaでIAM認証を有効にすると、Elasticsearch APIもIAM認証となります。Elasticsearchクライアント側でIAM認証対応するのはすこしめんどうですし、自由にフロント側のコンポーネントを使いづらくもなります。
Reactivesearchなどをクライアントとして使う場合は、自分でIAM認証（HTTP署名）を実装するか、API Gateway（+Lambda)経由でのアクセスにしてIAM認証対応するか、認証は諦めてアクセスポリシーでのIPアドレス制限等でのアクセス制御にする必要があります。

# リンク
[KibanaにCognito認証を追加する](https://qiita.com/mdps513/items/d8f365c7899677d6e67b)
マニュアル「[Kibana の Amazon Cognito 認証](https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/es-cognito-auth.html)」