この記事は 株式会社サイバー・バズ Advent calendar 2021 16日目の記事です。
概要
ECS
やLocal
環境からAWSV4
認証を使用してOpenSearch
にアクセスする機会がありました。
基本的にはこちらの記事を参考にさせていただいています。
目次
- [AWS V4署名](# AWS V4署名)
- [Local環境から](# Local環境から)
- [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
にはV2
とV3
の二つが存在します。
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
です。
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
に存在しているので、roleAssumer
でcredentilas
情報を取得しています。
AssumeRole
については以下の記事が参考になります。(特徴的な図があって分かりやすく、個人的に記憶に残っています。)
https://dev.classmethod.jp/articles/iam-role-passrole-assumerole/
署名を施し、returnします。
本リクエスト部分
続いて署名を使って実際にOpenSearch
へリクエストします。
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
は実際に使用するOpenSearch
のhost
名に変換してください。
OpenSearch
にまつわる詳しいquery
については公式ドキュメントを参考にしてください。
今回は上から10件分document
を取得するquery
です。
そして先ほど作成したcreateSignedRequest
から署名済みのリクエストを取得し、NodeHttpHandler
を使用してOpenSearch
にリクエストしています。
ECS環境から
ECS環境の場合は、createSignedRequest
のSignatureV4
部分に少し修正を入れます。
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]/*"
}
]
}
Role
やdomainName
に関しては適宜変更してください。
今回はAWSV4
署名を使用してOpenSearch
へアクセスを試みました。
他にも良いやり方があるかもしれませんが、一つの参考になれば幸いです。