Amazon Elasticsearch Service を Python クライアントで、IAM アカウントを作ってセキュアにアクセスする

Amazon Elasticsearch Service は、アクセス制御が IAM や IPアドレスでできます。最初は IPアドレスフィルタでいじってたのですが、将来的にドツボにはまりそうだったため IAM で行うようにしました。

なお、Amazon Elasticsearch Service のアクセスポリシーの変更は、指定してから20分ぐらい待たないと反映されないため、面倒です。早めの IAM 化が楽だと思います。(反映待ちの間もサービスは使えます)

# IAM ユーザーの作成

IAM のユーザーページ
https://console.aws.amazon.com/iam/home#users
より「新規ユーザーの作成」
→適当に作ります

完成後、 「ユーザーのセキュリティ認証情報を表示」リンクをクリックして、
アカウント名、**アクセスキー ID**、**シークレットアクセスキー** を記録しておきます。

できたら、左タブより「ユーザー」ページを表示し、先ほど作成したユーザーを選択。
「**ユーザーのARN**」が表示されているので、これもコピーしておきます。

こんなの → `arn:aws:iam::000000000000:user/xxxxxxxxxxxxx`

# Elasticsearch のアクセスポリシーの作成

Amazon Elasticsearch Service dashboard
https://ap-northeast-1.console.aws.amazon.com/es/home

を開き、ドメインを選択。

Modify access policy → Select a template → Allow or deny access to one or more AWS accounts or IAM users を選択。

Effect: Allow にして、 Account ID or ARN  に先ほどの ARN をペースト。

アクセスポリシーの JSON が生成されるので、「Submit」→「OK」とボタンを押します。

再度、Modify access policy ページを開くと、Status が Processing になります。反映されるとここが Active になります(ブラウザリロード不要)。20分ぐらいかかります。

### IPアドレスでも許可する場合

この手順でアクセスポリシーを作った場合、IAM 認証をしないと ES にアクセスできないため、**Kibana をブラウザから見たい場合ちょっと困ります**。そのため、Kibana にアクセスする IPアドレスを OR 条件で許可した方が使い勝手が良いでしょう。

アクセスポリシーの Statement リストに、ポリシーを追加します。
(上のがIPアドレスホワイトリスト、下のが IAM での制御)

```json:Edit_the_access_policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "000.000.000.000"
          ]
        }
      },
      "Resource": "arn:aws:es:ap-northeast-1:000000000000:domain/hogehoge/*"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::000000000000:user/xxxxxxxxxxxxx"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:ap-northeast-1:000000000000:domain/hogehoge/*"
    }
  ]
}
```

# テストスクリプトの作成

https://github.com/elastic/elasticsearch-py/issues/280 この Issue を参考に

## 必要ライブラリのインストール

```bash
$ pip install elasticsearch requests_aws4auth
```

(もしかしたら requests も必要かもしれません。僕の環境では既に入ってたので必要かわかりませんでした)


## コード

```python
import elasticsearch
from requests_aws4auth import AWS4Auth

host = 'xxxxxxxx.ap-northeast-1.es.amazonaws.com'
awsauth = AWS4Auth(
    'アクセスキーID',
    'シークレットアクセスキー', 'ap-northeast-1', 'es')

es = elasticsearch.Elasticsearch(
    hosts=[{'host': host, 'port': 443}],
    http_auth=awsauth,
    use_ssl=True,
    verify_certs=True,
    connection_class=elasticsearch.connection.RequestsHttpConnection
)
print(es.info())
print(es.cluster.health())
```

先ほどの、AWSの Elasticsearch の アクセスポリシーページの、「Status」が「Active」になるまで待ちます。
ページを開きっぱなしで待ってて良いです。(ブラウザのリロードを連打しなくても良い)

Active になる前だと

```
elasticsearch.exceptions.AuthorizationException: TransportError(403, '{"Message":"User: arn:aws:iam::000000000000:user/xxxxxxxxxxxxx is not authorized to perform: es:ESHttpGet on resource: arn:aws:es:ap-northeast-1:000000000000:domain/hogehoge/"}')
```

このエラーになります。

Status が Active になった後は

```
{'version': {'lucene_version': '4.10.4', 'build_timestamp': '2015-04-27T09:21:06Z', 'build_snapshot': False, 'build_hash': '62ff9868b4c8a0c45860bebb259e21980778ab1c', 'number': '1.5.2'}, 'status': 200, 'cluster_name': '000000000000:hedgehoge', 'tagline': 'You Know, for Search', 'name': 'Ororo Munroe'}
{'active_primary_shards': 11, 'number_of_pending_tasks': 0, 'cluster_name': '000000000000:hedgehoge', 'relocating_shards': 0, 'active_shards': 11, 'status': 'yellow', 'timed_out': False, 'number_of_nodes': 1, 'unassigned_shards': 11, 'number_of_data_nodes': 1, 'initializing_shards': 0}
```
結果が返ってきます🎉

…ビルド古い…