Amazon Web Services ブログ

【寄稿】ElasticsearchからAmazon OpenSearch Serviceへの移行とIAM認証の有効化

この投稿はアクセンチュア株式会社 テクノロジーコンサルティング本部所属のマネージャー 竹内 誠一 氏に、ElasticsearchからAmazon OpenSearch Serviceへの移行とIAM認証の有効化の検証結果について寄稿いただいたものです。


本ブログは、ElasticsearchからAmazon OpenSearch Serviceへの移行検証で得られた知見をご紹介します。

この検証の背景として、あるお客様でオンプレミスの全文検索システムをAWSクラウドへ移行する提案をしており 、ElasticsearchとAmazon OpenSearch Serviceの互換性による課題の早期見極めとIAM認証によるセキュリティ強化が重要な要件でした 。IAM認証に関しては 「AWS アクセスキーを管理するためのベストプラクティス」 に紹介されている、AWSアクセスキーを管理する場合のベストプラクティスへの対応を目指しました。

下図に移行前後の構成イメージを示します。

移行前:
移行前の構成イメージ移行後:
移行後の構成イメージ

現行のElasticsearchサーバおよびLogstashのバージョンはV7.17.7を使用しており、移行後構成の検証は、OpenSearch 2.3 を実行する Amazon OpenSearch Service ドメインと Logstash 8.6.0 で実施しました。

移行上の要点を整理すると次のようになります。

  1. Amazon OpenSearch Service作成時に IAM認証を有効にし、IAMロールをマスターユーザとして構成
  2. Logstash パイプラインにおけるアウトプットプラグインを OpenSearch に変更し、 IAM 認証の設定を追加
  3. JavaアプリケーションでHigh-level Rest Clientを使用している部分をElasticsearch 用ライブラリを使用した実装から OpenSearch 用ライブラリを使用した実装に変更
  4. Javaアプリケーションで基本認証を実装している部分をSDK V2の認証プロバイダ機能によるIAM認証に変更
  5. マスターユーザ以外のIAMロールを追加

以下の各章で、この要点のそれぞれについて詳しく説明します。

1. Amazon OpenSearch Serviceの構成

IAM認証によるセキュリティ強化という要件を踏まえ、Amazon OpenSearch Serviceドメイン作成時にIAM認証を有効にします。マスターユーザにはIAMロールを使用し、ドメインレベル・アクセスポリシーにはそのIAMロールのアクセスを明示的に許可しておきます。

AWSコンソールでAmazon OpenSearch Serviceのドメインを作成する時の該当部分を以下に示します。

Fine-grained access control画面

Access policy画面

ドメイン作成後にSecurity configurationの画面でアクセスポリシーを確認すると、上図の設定を反映したアクセスポリシーができています。下図に例を示します。

Access policy

ドメイン作成時にドメインレベル・アクセスポリシー設定を省略すると、デフォルトで全ユーザのアクセスをDenyするアクセスポリシーができてしまい、そのままではマスターユーザのIAMロールであってもドメインにアクセスできません。その場合はドメイン作成後にアクセスポリシーを編集して、マスターユーザのIAMロールのアクセスを許可しておきます。このときIAMロールのポリシーにはOpenSearchへのアクセスを許可していなくても大丈夫です。

2. Logstashパイプライン構成

Logstashパイプラインの出力先にAmazon OpenSearch Serviceを指定する方法は、 AWSドキュメントの「Logstash を用いて Amazon OpenSearch Service へデータをロードする」 にサンプル付で紹介されています。
このドキュメントからIAM認証を使用する例を以下に引用しました。

output {        
  opensearch {     
    hosts => ["domain-endpoint:443"]             
    auth_type => {    
      type => 'aws_iam'     
      aws_access_key_id => 'your-access-key'     
      aws_secret_access_key => 'your-secret-key'     
      region => 'us-east-1'         
      }         
      index  => "logstash-logs-%{+YYYY.MM.dd}"  
      ecs_compatibility => disabled    
  }            
}

この例はアクセスキーIDとシークレットアクセスキーを明示指定しており、「アクセスキーを直接コードに埋め込まないというベストプラクティス」 上は望ましくありません。対策は簡単で、EC2インスタンスプロファイル及びECSタスクIAMロールを使用する場合は、この指定を取り除くだけでよいことを検証で確認しました。

修正後のパイプライン構成のIAM認証設定部分をあらためて以下に示します。ここでtypeの’aws_iam’は固定値、regionはAmazon OpenSearch Serviceドメインのデプロイ先のリージョンです。

    auth_type => {    
      type => 'aws_iam'     
      region => 'us-east-1'         
      }      

ドキュメントにはアクセスキーIDやシークレットアクセスキーを環境変数に設定する例や、aws configure コマンドで設定する例も紹介されていますが、これらを利用するためには アクセスIDやシークレットアクセスキーを取得して環境変数等にセットしたり、aws configureコマンドを投入したりする仕組みも用意しなければなりません。EC2インスタンスプロファイルやECSタスクIAMロールを使用すればこのような仕組みは不要なので、運用上も EC2インスタンスプロファイルやECSタスクIAMロールのほうが簡単です。
運用上の注意点としては、環境変数やaws configureコマンドのアウトプット(.aws/credentialsファイル)が存在するとそれらが EC2インスタンスプロファイルやECSタスクIAMロールよりも優先されてしまうので、実行環境にこれらが存在しないよう配慮が必要になります。

3. JavaアプリのHigh-level Rest Clientの対応

ElasticsearchのHigh-level Rest Client方式はバージョン7.15.0以降で非推奨(Deprecated)となり、バージョン8.0以降のElasticsearchサーバにHigh-level Rest Client方式でアクセスしたい場合には、Compatibility Modeを明示指定してアクセスするように仕様変更されています。ただし、移行猶予のためと思いますが、バージョン7.11以降のElasticsearchサーバでもCompatibility Modeをサポートしています。

今回移行対象として取り上げたJavaアプリケーションはこのHigh-level Rest Client方式を採用しており、Compatibility Modeを使用するようにコーディングされていました。
参考のため、以下にElasticsearchのHigh-level Rest Clientの非推奨やバージョン8.0への移行に関するドキュメントを紹介しておきます。

Amazon OpenSearch ServiceはElasticsearchから派生したOpenSearch projectのマネージドサービスです。Elasticsearch バージョン7.10.2からOpenSearchが派生した時点では両者に違いはありませんでしたが、High-level Rest Clientの非推奨とCompatibility Modeの導入はOpenSearchプロジェクトの派生後にElasticsearch側のみで行われたため、移行にあたってはこの差分への対応が必要です。
上記で紹介したElasticsearchのドキュメントよりCompatibility Modeのコーディングを以下に抜粋します。お客様のアプリケーションもほぼこれと同じでした。

// Create the low-level client
RestClient httpClient = RestClient.builder(
    new HttpHost("localhost", 9200)
).build();

// Create the HLRC
RestHighLevelClient hlrc = new RestHighLevelClientBuilder(httpClient)
    .setApiCompatibilityMode(true) 
    .build();   

一方、OpenSearchのドキュメントにも 「Java high-level REST client」 が紹介されており、サンプルも掲載されていました。
該当部分を以下に抜粋します。

    //Create a client.
    RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9200, "https"))
      .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
        @Override
        public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
          return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }
          });
    RestHighLevelClient client = new RestHighLevelClient(builder);

ElasticsearchのサンプルもOpenSearchのサンプルも最終的にRestHighLevelClientクラスのインスタンスを作成していますが、インスタンスの作り方と、そこに至る過程に違いがあることがわかります。中でもElasticsearchのサンプルはCompatibility ModeをセットするためにRestHighLevelClientBuilderクラスを使用しているのに対し、OpenSearchのサンプルにはRestHighLevelClientBuilderが登場しない点が顕著な差異であり移行のポイントになります。
OpenSearchのサンプルではsetHttpClientConfigCallbackを使って認証方式をカスタマイズしている点も目を引きますが、これはElasticsearhでも組み込み可能なロジックであり、元のElasticsearchのアプリケーションに含まれていなければ移行後のOpenSearchアプリケーションにも必要ないのでここでは無視することにします。
これらを踏まえてElasticsearchのサンプルをOpenSearch用に書き換えると次のようになります。

// Create the low-level client
RestClientBuilder builder = RestClient.builder(
    new HttpHost({Amazon OpenSearch Service endpoint URL}, 443));

// Create the HLRC
RestHighLevelClient hlrc = new RestHighLevelClient(builder);

サンプルには含まれていませんが、org.elasticsearchライブラリのimportも適宜org.opensearchに置き換えます。お客様アプリケーションでも同様の修正を施して、Amazon OpenSearch Serviceに対して移行前と同様に動作することを確認しました。

4. High-level Rest ClientへのIAM認証の組み込み

High-level Rest Clientにおける認証方法の設定は、RestClientBuilderクラスのsetHttpClientConfigCallbackメソッドを使用します。これは3章で一旦無視したメソッドですが、IAM認証の組み込みには必要なのでこの章であらためて検討します。
一部再掲になりますが、High-level Rest Clientでベーシック認証を明示的に組み込む部分を含めて、OpenSearchのサンプルを以下に抜粋します。

    //Establish credentials to use basic authentication.
    //Only for demo purposes. Don't specify your credentials in code.
    final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();

    credentialsProvider.setCredentials(AuthScope.ANY,
      new UsernamePasswordCredentials("admin", "admin"));

    //Create a client.
    RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9200, "https"))
      .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
        @Override
        public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
          return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }
          });
    RestHighLevelClient client = new RestHighLevelClient(builder);

詳細には触れませんが、BasicCredentialsProviderを使ってベーシック認証を実行するようRestClientBuilderをカスタマイズしていることが読み取れます。

Amazon OpenSearch ServiceのHigh-level Rest ClientでIAM認証を使用する場合も同様にIAM認証用のCredentialsProviderをセットしてやればよいということになります。探してみるとAWS SDK V2を使用したコーディング例が、AWSドキュメントの 「Amazon OpenSearch Service への HTTP リクエストの署名」 に紹介されていました。
表題が「HTTPリクエストの署名(Signing HTTP requests)」なのでわかりにくいのですが、IAM認証情報を使用してHTTPリクエストを署名するということは、すなわちIAM認証を使用するということです。
ドキュメントにはAwsSdk2Transport方式のサンプルが紹介されていますが、High-level Rest Clientについてもその下に「その他の代替手段(Other alternatives)」として触れており、サンプルへの 「リンク」 があります。
今回はこのサンプルを参考にしました。以下にリンク先のサンプルの一部を抜粋します。

    private static final String serviceName = "es";
    private static final String region = "us-west-2";
    private static final String endpoint = "https://search-...us-west-2---es.amazonaws.com";
    private static final String type = "_doc";

    static final AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.create();
    public static RestHighLevelClient getOpenSearchClient() {
        Aws4Signer signer = Aws4Signer.create();
        HttpRequestInterceptor interceptor = new AwsRequestSigningApacheInterceptor(serviceName, signer,
                credentialsProvider, region);
        return new RestHighLevelClient(RestClient.builder(HttpHost.create(endpoint))
                .setHttpClientConfigCallback(hacb -> hacb.addInterceptorLast(interceptor)));
    }

ここではDefaultCredentialsProviderというクラスを使ってRestClientBuilderにIAM認証を組み込んでいます。
DefaultCredentialsProviderの動作はAWSドキュメントの 「認証情報の使用」 に説明されています。
これによると、IAM認証情報の提供方法が6種類想定されており、それらを順番に検索して最初に見つかった認証情報を使用することになっています。EC2インスタンスプロファイルもECSタスクIAMロールもこの6種類に含まれており、 「AWS アクセスキーを管理するためのベストプラクティス」 にも対応できます。
Logstashパイプラインと同様に、DefaultCredentialsProviderの検索順序は環境変数や.aws/credentialsファイル等の認証情報を優先しています。EC2インスタンスプロファイルやECSタスクIAMロールを使用させるためには、それ以外の認証情報の設定を実行環境から取り除いておかなければならないことをここでも留意して下さい。

5. マスターユーザ以外のIAMロールの追加

Amazon OpenSearch ServiceでIAM認証を使用する場合は、Fine-grained Access Control(FGAC)も一緒に有効になっており、OpenSearchへのアクセスはリソースポリシーとFGACの組み合わせで制御されます。
Amazon OpenSearch Serviceドメイン作成時のアクセスポリシーを以下に再掲します。

Access policy再掲

またFGACのロール・マッピングは次のようになっていました。

{
  "security_manager": {
    "hosts": [],
    "users": [],
    "reserved": false,
    "hidden": false,
    "backend_roles": [
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-master"
    ],
    "and_backend_roles": []
  },
  "all_access": {
    "hosts": [],
    "users": [],
    "reserved": false,
    "hidden": false,
    "backend_roles": [
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-master"
    ],
    "and_backend_roles": []
  }
}

FGACではアクセス権限をロールで定義し、ロール・マッピングでロールにユーザを割り当てることで、ユーザにアクセスを許可します。デフォルトではsecurity_managerとall_accessという2種類のロールにマスターユーザが割り当てられていることがわかります。新規ユーザを追加する場合は、アクセスポリシーとFGACのロール・マッピングにIAMロールを追加登録する必要があります。

まずアクセスポリシーから見ていきます。pod-t-role-os-user1というIAMロールを追加した例を以下に示します。アクセスポリシーはAWSコンソールで編集できます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-master",
          "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-user1"
        ]
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-west-2:xxxxxxxxxxxx:domain/pod-t-nehr-iamauth2/*"
    }
  ]
}

次にFAGCのロール・マッピングを見ていきます。デフォルトでは2種類のロールにマスターユーザが割り当てられていましたが、security_managerはAmazon OpenSearch Service独自のロールで、監査ログの詳細設定の権限を許可するものです。マスターユーザには必要な権限ですが、ドメインへのアクセス権限ではないので新規ユーザの登録は必須ではありません。

もうひとつのall_accessはオープンソースのOpenSearchプロジェクトに定義された 「Predefined roles」 の一つで、OpenSearchへのフルアクセス権限を許可します。マスターユーザにはデフォルトでこの権限が与えられています。新規ユーザを追加したいときには、付与したい権限に応じて適切なロールのbackend_rolesにユーザのIAMロール ARNを登録します。all_accessロールに pod-t-role-os-user1のIAMロールを追加登録した場合は以下の例のようになります。

{
  "all_access": {
    "hosts": [],
    "users": [],
    "reserved": false,
    "hidden": false,
    "backend_roles": [
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-master",
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-user1"
    ],
    "and_backend_roles": []
  }
}

Amazon OpenSearch ServiceでFAGCにユーザを追加する方法は以下の2通りありますが、本ブログでは後者のREST APIリクエストを使用する方法を紹介します。

  • OpenSearch DashboardのSecurityメニューでのGUI操作
  • OpenSearch の_plugins/_security/apiへのREST APIリクエスト

マスターユーザとしてIAMロールを使用しているので、REST APIリクエスト実行時にはこのIAMロールの認証情報を渡す手段が必要になります。これは、 「curl コマンドでリクエストの署名ができる –aws-sigv4 オプション」 のcurl コマンド実行例を参考にしました。
この方法ではアクセスキーID等のIAM認証情報が必要ですので、EC2インスタンスプロファイルにIAMロールを関連付けたEC2インスタンス上で一時的な認証情報を取得する方法もご紹介しておきます。

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/IAMロール名

Role Mappingの照会、変更方法は以下のリンク先のドキュメントが参考になります。

前者のAWSドキュメントの「追加のマスターユーザー」の節ではHTTP PUTメソッドでall_accessを置き換える方法を紹介しています。この方法ではall_mapping全体を置き換えるので、先にご紹介した「 all_accessロールに pod-t-role-os-user1のIAMロールを追加登録した場合」の例がインプットとして使用できます。ただし”reserved”と”hidden”は除いておきます。

PUT _plugins/_security/api/rolesmapping/all_access
{ 
    "hosts": [], 
    "users": [], 
    "backend_roles": [ 
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-master", 
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-user1" 
    ], 
    "and_backend_roles": [] 
  }

一方、後者のAPIリファレンスはHTTP PATCHメソッドを使用する方法も紹介しています。PATCHメソッドの場合はall_access全体ではなくbackend_rolesだけを指定して置き換えます。

PATCH _plugins/_security/api/rolesmapping/all_access 
[
{
  "op": "replace", 
  "path": "/backend_roles", 
  "value": [
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-master",
      "arn:aws:iam::xxxxxxxxxxxx:role/pod-t-role-os-user1"
    ] 
  } 
]

All_accessにはusersというセクションもあり、認証方法によってusersとbackend_usersを使い分けます。前者のAWSドキュメントの「ユーザーへのロールのマッピング」の節に説明があったのですが、IAMロールの場合はbackend_usersを使用し、それ以外はusersセクションを使用すると考えればよさそうです。

6. まとめ

Logstashパイプラインでデータを投入し、High-level Rest Clientアプリケーションで検索するというオーソドックスなElasticsearchソリューションを、Amazon OpenSearch Serviceに移行する提案事例の技術的な考慮点と対応方法を整理してご紹介してきました。

ElasticsearchとOpenSearchの互換性の問題はゼロではありませんが、Logstashパイプラインにおいても、High-level Rest Clientベースの検索アプリケーションにおいても、軽微な修正でAmazon OpenSearch Serviceに移行するできること、IAM認証にも対応できることを実証できました。
Amazon OpenSearch Serviceはフルマネージドサービスの恩恵を受けられることはもちろん、OpenSearchのロールベースのアクセス制御にIAMの認証・認可を組み合わせることで、きめ細かくて一貫性のあるアクセス制御を容易に実現できます。

更にこのブログの執筆中にAmazon OpenSearch ServerlessのGAが発表されました。Serverlessではドメインの構成やサイジングを意識する必要がなくなり、完全な従量課金によるコスト削減も期待できます。手軽に始められるセキュアな全文検索エンジンとして、Amazon OpenSearch Serviceはますます注目されそうです。

本ブログがAmazon OpenSearch Service活用の一助になれば幸いです。

著者について

竹内 誠一

竹内 誠一

アクセンチュア株式会社
マネジャー テクノロジーコンサルティング本部
インテリジェントクラウド アンド インフラストラクチャー グループ所属