無印吉澤

Site Reliability Engineering(SRE)、ソフトウェア開発、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

Amazon Elasticsearch Service の認証・認可に関する面倒くさい仕様をなるべくわかりやすく説明する

f:id:muziyoshiz:20191130143741p:plain

はじめに

最近、仕事で Amazon Elasticsearch Service(以下、Amazon ES)を本格的に使う機会があって、認証周りの仕様で苦労させられました。

Elasticsearch を本格的に使う人は、自分で Elasticsearch クラスタを立てたり、Elastic Cloud を使ったりしてしまうからか、Amazon ES 周りの情報は意外と見つかりませんでした。せっかくなので、今回の記事では、自分が Amazon ES を使うにあたって調べた情報をまとめてみます。

いろいろな Elasticsearch

Amazon ES は、オープンソースの全文検索エンジン Elasticsearch をベースにしたマネージド型サービスです。

Elasticsearch は、Elasticsearch 社*1が主に開発しているオープンソースソフトウェア(OSS)です。しかし、いまはその派生が色々出てきてしまっているので、この記事では以下のように呼び分けます。

提供形態 名称 概要 この記事での呼称
OSS Elasticsearch Elasticsearch 社が主に開発している OSS。X-Pack と呼ばれる拡張機能があり、その大半は有償オプションで利用できる。 OSS 版
マネージドサービス Elasticsearch Service Elasticsearch 社が提供する Elastic Cloud と呼ばれるサービス群の一つ。AWS, GCP, Azure にデプロイされるマネージドサービス。X-Pack の機能を含む。AWS 等に固有の機能はない。 Elastic Cloud
マネージドサービス Amazon Elasticsearch Service AWS が提供する Elasticsearch のマネージドサービス。AWS のサービス群と統合するために、各種拡張が行われている。X-Pack の機能は含まないが、同等の機能が一部実装されている。 Amazon ES
OSS Open Distro for Elasticsearch 「Elasticsearch へのタダ乗り」との批判を受けて、Amazon ES のソースコードの一部を OSS として公開したもの。Amazon ES と同一ではない、と宣言されている。この記事では触れない。 -

なぜこんな話が必要かというと、Amazon ES と、Elasticsearch 社が提供する OSS 版および Elastic Cloud では、大きく違っている機能が多いからです。この記事で解説する認証・認可の方法も、そのような機能の一つです。

Amazon ES の基本

Amazon ES では、「ドメイン」という概念が新たに導入されています。このドメインと Elasticsearch のクラスタは1対1対応です。なので、Elasticsearch をすでに知ってる人は、Amazon ES のドキュメントでドメインという単語が出てきたら、クラスタと読み替えてもらえればとりあえず OK です(参考:よくある質問 - Amazon Elasticsearch Serviceの「Q: Amazon Elasticsearch Service ドメインとは何ですか?」)。

Amazon ES のドメインを作る際には、パブリックネットワークに設置するか、VPC に設置するかを選ぶ必要があります。それぞれ「パブリックアクセス」、「VPC アクセス」と呼ばれます。

  • パブリックアクセス
    • グローバル IP アドレスを持ち、インターネットから直接アクセス可能なドメインを構築
    • 構築後にパブリックアクセスから VPC アクセスに変更することはできない(逆もできない)
  • VPC アクセス
    • グローバル IP アドレスを持たず、VPC に設置されて、インターネットから直接アクセス不可なドメインを構築
    • 構築時に選んだ VPC とは別の VPC に移すことはできない
    • ドメインの作成時にパブリックサブネットを選択しても、エンドポイントにグローバル IP アドレスを設定することはできない

VPC アクセスには、グローバル IP アドレスの有無以外に、機能面でも少し違いがあります。詳しくは Amazon Elasticsearch Service ドメインの VPC サポート の「制約事項」の項をご覧ください。

で、ここからお話するのは、この「制約事項」の項は書かれておらず、その次の「VPC ドメインのアクセスポリシーについて」の項でわずかに触れられている(しかし結構重要な)認証・認可についての制約事項の話です。

注記

セキュリティグループには IP ベースのアクセス権限ポリシーですでに強化されているため、VPC 内に存在する Amazon ES ドメインに IP ベースのアクセス権限ポリシーを適用することはできません。パブリックエンドポイントを使用する場合、IP ベースのポリシーを引き続き利用できます。

Amazon ES が提供する認証・認可

Amazon ES では、ドメインに対して、セキュリティポリシーとセキュリティグループを設定できます。セキュリティグループは、VPC アクセスの場合のみ設定できます。

さらに、ドメインに対するセキュリティポリシーには、以下の3種類を利用できます。公式ドキュメントでは Amazon Elasticsearch Service の Identity and Access Management に詳しく書かれています。

  • リソースベースのポリシー
    • リクエストの送信先 URL(特定インデックスの URL も指定可能)
  • アイデンティティベースのポリシー
    • リクエストの送信元 IAM ユーザーまたは IAM ロール
  • IP ベースのポリシー
    • リクエストの送信元 IP アドレス

そして、さっき上で引用したように、VPC アクセスの場合は IP ベースのポリシーは使えません。

この関係を図に表すと、以下のようになります。

f:id:muziyoshiz:20191130141129p:plain
Amazon ES の認証・認可

これを見て「なるほど、じゃあパブリックアクセスと VPC アクセスの認証・認可機能は同等なんですね。よかったよかった」と思われましたよね。僕も最初そう思いました。でもそうじゃない、そうじゃないんですよ……。

パブリックアクセスの場合

リソースベース、アイデンティティベース、IP ベースのポリシーは組み合わせて使えます。また、複数のステートメントを並べて書くこともできます。

例えば、「すべてのユーザに test-index の読み込みを許す」かつ「IAM ロール "power-user-role" を持ち、送信元 IP アドレスが 192.0.2.0/24 のユーザに対してのみ、すべての操作を許す」というセキュリティポリシーは、以下のように書けます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "es:es:ESHttpGet"
      ],
      "Resource": "arn:aws:es:us-west-1:987654321098:domain/test-domain/test-index/*"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/power-user-role"
      },
      "Action": [
        "es:*"
      ],
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "192.0.2.0/24"
          ]
        }
      },
      "Resource": "arn:aws:es:us-west-1:987654321098:domain/test-domain/*"
    }
  ]
}

この関係を図に表すとこうなります。

f:id:muziyoshiz:20191130143201p:plain
パブリックアクセスの場合

VPC アクセスの場合

リソースベース、アイデンティティベースのポリシーは組み合わせて使えますが、IP ベースのポリシーは使えます。また、それとは独立に セキュリティグループも設定することができます。つまり図にするとこうなります。

f:id:muziyoshiz:20191130143330p:plain
VPC アクセスの場合

どうですか?

セキュリティポリシーとセキュリティグループは独立なので、例えばこの図でいうと「ステートメント1 AND セキュリティグループ1」という横の繋がりは強制できません。つまり、さっきパブリックアクセスの例に挙げた、以下のようなアクセス制御はできません。

「すべてのユーザに test-index の読み込みを許す」かつ「IAM ロール "power-user-role" を持ち、送信元 IP アドレスが 192.0.2.0/24 のユーザに対してのみ、すべての操作を許す」

IP アドレス以外の認証・認可方法は AWS 署名しかない

Oh... じゃあ仕方ない、VPC アクセスを使う場合は IP アドレスでユーザを識別して認可するのは諦めよう!と考えたとします。すると次に出てくるのは AWS 署名の問題です。

Amazon ES は、IP アドレス以外の認証・認可方法として、AWS 署名のみを許可しています。OSS 版や Elastic Cloud は Basic 認証に対応していますが、Amazon ES は対応してません。 Basic 認証は X-Pack の機能ですが、いまや OSS 版でも使えるというのに!(Security for Elasticsearch is now free | Elastic Blog

認証方法を強制的に IAM に統一できる、というのはもちろん強いメリットと言うこともできます。認証情報は集中管理できたほうがいいですよね。しかし、自分は実際に使ってみて、3つの問題を感じました。

まず1つ目は、Elasticsearch の REST API(以下、Elasticsearch API)の呼び出しも AWS 署名する必要が生じる ことです。

こう言うと、詳しい方は「aws コマンドには es サブコマンドがあるじゃないか」と言うかもしれません。確かに、Amazon ES のドメインを操作するための、AWS が提供する API の呼び出しは、aws コマンドで実行できます。だから、AWS 署名を意識する必要はありません。

しかし、Elasticsearch API は aws コマンドの範囲外です。もちろんライブラリを使えばそういうコードは書けますが、運用中は REST APIs を色々駆使したくなります。各 API に対応するコードを書くのかと言われるとちょっと……。

2つ目は、IAM ユーザや IAM ロールの設定範囲が、Elasticsearch に対して与えたい権限と必ずしも一致しない場合がある ことです。

例えば、既存の EC2 インスタンスに、Amazon ES へのアクセスを新たに追加したい場合に、「インスタンス1にはインデックスAへのアクセスを許可し、インスタンス2にはインデックスBへのアクセスを許可したい」と思ったとします。その場合、IAM ロールの付け替えが必要になります。既存の IAM ロールの ARN をあちこちから参照していたりしたら、付け替えは地獄の作業になります。

そして3つ目は、AWS 署名を必須にすると Kibana を使うために Cognito が必須になる という問題です。

Amazon ES では Elasticsearch API と Kibana のエンドポイントの FQDN は同じです。リソースベースのポリシーはリクエストの送信先 URL なので、当然 Kibana にも影響します。しかし Web ブラウザは(当たり前ですが)HTTP リクエストに AWS 署名を付ける機能なんて持ってません。

じゃあどうしたらいいの?というと、AWS は「Amazon Cognito を使用して Kibana のユーザー名とパスワードを管理する機能」をオプションとして提供しています。

docs.aws.amazon.com

Kibana へのアクセスを認証するためだけに、Cognito のユーザープールやら ID プールやらの設定をしなきゃいけないんですか?? ちょっと、やりたいことに対して Cognito の機能は過剰すぎませんかね……。

Elasticsearch API へのリクエストを AWS 署名する

そろそろ「そこまでして Amazon ES 使うの? Elastic Cloud で良くない?」という声が脳内に直接聞こえてきたのではないでしょうか。とはいえ、AWS ですぐ使えるのはやっぱり便利なので Amazon ES で我慢しよう、という判断をしたとします。

色々調べた結果、AWS 署名についてはこうするのが良いだろう、という自分なりの答えは出たので以下にてご紹介します。もっといい方法があったらぜひ教えてください。

プログラムから Elasticsearch API を呼ぶ

プログラムから Elasticsearch API を呼ぶなら、各種ライブラリが AWS クレデンシャルの取得に対応しているので、それを使うのが一番楽です。

EC2 インスタンスや Lambda 関数でプログラムを動かすなら、IAM ロールに対してアクセス権限を与えれば、ほとんど何も意識せずにコーディングできます。以下のページに公式のサンプルコードがあります。

docs.aws.amazon.com

より使いやすいライブラリもあります。例えば、Scala 用には elastic4s があります。

github.com

Python でのやり方についてはこちらで詳しく説明されていました。

dev.classmethod.jp

IAM ロールの設定範囲が Elasticsearch に対して与えたい権限と一致しない場合は、何の権限もない IAM ユーザを Amazon ES 専用に作るのが良いと思います。

コマンドラインから Elasticsearch API を呼ぶ

Elasticsearch を運用する際には、コマンドラインから curl を使って、REST APIs にある APIs を呼び出したいです。Amazon ES の管理コンソールは Elastic Cloud のものほどリッチではないので尚更です。しかし、curl には AWS 署名機能はありません。

そこで代替手段を色々探したところ、awscurl が、Elasticsearch APIs の利用に必要な機能をすべて備えていました。

github.com

ただし、awscurl の v0.17 までは日本語を含むリクエストの送受信に対応していませんでした。プルリクを送って対応してもらったので、v0.19 以降であれば大丈夫です*2

実際に使う人向け:awscurl に関するもうちょっと詳しい話

awscurl は便利なのですが、一時的セキュリティ認証情報 をサポートしていませんでした。だから、例えば Amazon ES 管理用の EC2 インスタンスを立てて、そこでコマンド実行したい、という場合には不便です。

もう少し詳しく話すと、AWS CLI の動作については AWS CLI の設定 の「構成設定と優先順位」に記載があります。このなかの「6. インスタンスプロファイル認証情報」のサポートに当たるものが awscurl にはありませんでした。

そこで、僕は管理用サーバの /usr/local/bin/awscurl-on-ec2 に、awscurl をラップするシェルスクリプトを置きました。

#!/bin/env bash

ROLE_NAME=`curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/`
CRED=`curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/${ROLE_NAME}`
AWS_ACCESS_KEY_ID=`echo $CRED | jq -r ".AccessKeyId"`
AWS_SECRET_ACCESS_KEY=`echo $CRED | jq -r ".SecretAccessKey"`
AWS_SESSION_TOKEN=`echo $CRED | jq -r ".Token"`

awscurl "$@"

また、awscurl-on-ec2 コマンドの実行時に Endpoint URL をいちいち調べるのは面倒なので、作業用サーバの環境変数に設定しました。

awscurl と curl を比べて、使いやすい点・使いづらい点は次のような感じです。

  • curl より使いやすい点
    • ボディに json を含めるために -H 'Content-Type: application/json' を指定する必要がない
    • 邪魔な表示を消すために -s を指定する必要がない
  • curl より使いづらい点
    • AWS 署名を取得するサービスを特定するために、常に --service es を指定する必要がある
      • awscurl-on-ec2 のようなラッパーのなかで、デフォルトで --service es を付けてもいいと思います
    • AWS 署名を取得するリージョンを特定するために、常に --region リージョン名 を指定する必要がある
      • こちらも、使うリージョンが固定なら、ラッパーに含めてもいいと思います
    • ステータスコードを取得したいときに -w '%{http_code}' が使えない
    • 2xx 以外のときは例外がスローされる(インデックスの存在確認をするときなど、4xx が返されるのが正常な場合もある)

まとめ

今回は Amazon Elasticsearch Service(Amazon ES)を実際に使ってみて、認証・認可周りで困ったことと、どうやって無理やり使い続けているかを紹介しました。長々と書きましたが、まとめるとこんな感じです。

  • Amazon ES にはパブリックアクセスと VPC アクセスがある
  • パブリックアクセスと VPC アクセスでは IP アドレス認証の動作に違いがあり、VPC アクセスのほうが不便
  • Basic 認証は使えなくて、AWS 署名を使う必要がある
  • AWS 署名と Kibana を両方使いたいなら Cognito を強制される
  • AWS 署名ありで運用する場合は awscurl が便利

僕と同じく、Amazon ES を使う決心をした人は頑張りましょう! そしてもっといい方法があったら誰か教えてください!(切実)

更新履歴

  • 2019-12-03:awscurl のバージョンアップに伴って、日本語対応に関する記載を修正しました。

*1:昔は Elastic Co. だったと思うのですが、いまは Elasticsearch B.V. になっている?

*2:v0.18 は? v0.18 は僕のプルリクに別のバグが含まれていて、v0.19 で bugfix リリースしてもらってました。申し訳ない……。