8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[AWS]V4署名を使ってOpenSearchにアクセスする

Last updated at Posted at 2021-12-15

この記事は 株式会社サイバー・バズ Advent calendar 2021 16日目の記事です。

概要

ECSLocal環境からAWSV4認証を使用してOpenSearchにアクセスする機会がありました。

基本的にはこちらの記事を参考にさせていただいています。

目次

  1. [AWS V4署名](# AWS V4署名)
  2. [Local環境から](# Local環境から)
  3. [ECS環境から](# ECS環境から)

AWS V4署名

AWSV4署名とは、AWSAPIリクエストに認証情報を付与するものです。
AWSへのリクエストは基本的に署名が必要です。

詳しくはドキュメントをご覧ください。
https://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-version-4.html

本記事ではNode.jsからSDKを使用してAWSV4署名の後OpenSearchへアクセスを試みます。

まずは、必要なライブラリをインストールします。

npm install --save @aws-crypto/sha256-browser @aws-sdk/client-sts @aws-sdk/credential-providers @aws-sdk/node-http-handler @aws-sdk/protocol-http @aws-sdk/signature-v4
※注釈

SDKにはV2V3の二つが存在します。

npm install --save aws-sdk

上記のコマンドでinstallするSDKはV2になり、@aws-sdk/xxxxxのように必要なmoduleのみをinstallするV3を使用することが推奨されています。
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html

Local環境から

では早速Local環境からOpenSearchにアクセスしてみます。

署名リクエスト作成

まずは、署名を施すcreateSignedRequest です。

signed.ts
import { Sha256 } from '@aws-crypto/sha256-browser'
import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts'
import { fromIni } from '@aws-sdk/credential-providers'
import { NodeHttpHandler } from '@aws-sdk/node-http-handler'
import { HttpRequest } from '@aws-sdk/protocol-http'
import { SignatureV4 } from '@aws-sdk/signature-v4'

export const createSignedRequest = async ({ host, query }) => {
  const httpRequest = new HttpRequest({
    body: query,
    headers: {
      'content-type': 'application/json',
      host,
    },
    hostname: host,
    method: 'POST',
    path: '[index]/_search',
    port: 443,
  })

  const signer = new SignatureV4({
    credentials: fromIni({
      profile: '[profile-name]',
      roleAssumer: async (credentials, params) => {
        const sts = new STSClient({ credentials })
        const response = await sts.send(new AssumeRoleCommand(params))
        return {
          accessKeyId: response.Credentials.AccessKeyId,
          secretAccessKey: response.Credentials.SecretAccessKey,
          sessionToken: response.Credentials.SessionToken,
          expiration: response.Credentials.Expiration,
        }
      },
    }),
    region: 'ap-northeast-1',
    service: 'es',
    sha256: Sha256,
  })
  return signer.sign(httpRequest) as Promise<HttpRequest>
}

まず、HttpRequestを作成します。

次に、SignatureV4を使ってV4署名のリクエストを作成します。

[profile-name]については、~/.aws/credentialsで定義済のprofileを使用します。

今回は、OpenSearchインスタンスが別Roleに存在しているので、roleAssumercredentilas情報を取得しています。

AssumeRoleについては以下の記事が参考になります。(特徴的な図があって分かりやすく、個人的に記憶に残っています。)
https://dev.classmethod.jp/articles/iam-role-passrole-assumerole/

署名を施し、returnします。

本リクエスト部分

続いて署名を使って実際にOpenSearchへリクエストします。

index.ts
import { NodeHttpHandler } from '@aws-sdk/node-http-handler'
import { createSignedRequest } from './signed'

export const fetch = async <T>(): Promise<T> => {
  const host = 'search-xxxxxxxxxx.ap-northeast-1.es.amazonaws.com'
  const query = {
    query: {
      match_all: {}
    }
  }
  const signedRequest = await createSignedRequest({
    host,
    query,
  })

  const client = new NodeHttpHandler()
  const { response } = await client.handle(signedRequest).then((res) => res)
  const res: T = await new Promise((resolve) => {
    let responseBody = ''
    response.body.on('data', (chunk) => {
      responseBody += chunk
    })
    response.body.on('end', () => {
      resolve(JSON.parse(responseBody))
    })
  })
  return res
}

hostは実際に使用するOpenSearchhost名に変換してください。

OpenSearchにまつわる詳しいqueryについては公式ドキュメントを参考にしてください。

今回は上から10件分documentを取得するqueryです。

そして先ほど作成したcreateSignedRequestから署名済みのリクエストを取得し、NodeHttpHandlerを使用してOpenSearchにリクエストしています。

ECS環境から

ECS環境の場合は、createSignedRequestSignatureV4部分に少し修正を入れます。

signed.ts
import { Sha256 } from '@aws-crypto/sha256-browser'
import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts'
import { fromContainerMetadata } from '@aws-sdk/credential-providers'
import { NodeHttpHandler } from '@aws-sdk/node-http-handler'
import { HttpRequest } from '@aws-sdk/protocol-http'
import { SignatureV4 } from '@aws-sdk/signature-v4'

export const createSignedRequest = async ({ host, query }) => {
  const httpRequest = new HttpRequest({
    body: query,
    headers: {
      'content-type': 'application/json',
      host,
    },
    hostname: host,
    method: 'POST',
    path: '[index]/_search',
    port: 443,
  })

  const signer = new SignatureV4({
    credentials: fromContainerMetadata(),
    region: 'ap-northeast-1',
    service: 'es',
    sha256: Sha256,
  })
  return signer.sign(httpRequest) as Promise<HttpRequest>
}

上記でECSコンテナ自体のRoleを使用します。
そのままではOpenSearchにアクセスできないので、AWSコンソールのセキュリティ設定→アクセスポリシーにECSコンテナからのアクセス許可を追加します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::000000000000:role/ecs-task"
        ]
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:ap-northeast-1:000000000000:domain/[domainName]/*"
    }
  ]
}

RoledomainNameに関しては適宜変更してください。

今回はAWSV4署名を使用してOpenSearchへアクセスを試みました。

他にも良いやり方があるかもしれませんが、一つの参考になれば幸いです。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?