AWS ElasticSearch Service の認証にIAM Roleを使う [PHP編]
こんにちは、井上です。
AWS ElasticSearch Serviceを使ってみました。
ElasticSearchはVPC外に置かれるとのことで、アクセス管理はどうするんだろうと思っていたら、AccessPolicyにて何かしら制限しないといけないようです。(VPC内に置いて欲しいなぁ…)
制限方法としては、
- IPアドレスによる制限
- アカウントIDによる制限
- IAM Roleによる制限
などができるようです。
試しに、IAM Roleで制限をかけたEndpointにcurlでアクセスするとエラーがでることは確認できました。
$ curl search-xxxxxxx.ap-northeast-1.es.amazonaws.com
{"Message":"User: anonymous is not authorized to perform: es:ESHttpGet on resource: arn:aws:es:ap-northeast-1:xxxx:domain/xxxxx/"}
ではどうやって認証を通るようにするかを探していてたどり着いたページ、
Amazon Elasticsearch ServiceのIAM Roleによるアクセス制御 | Developers.IO を見ると、
> 今までElasticsearch標準のライブラリ、SDKを利用していた方はどうするんだろ?(ラッパのSDKが公開されるのかな)
と、書かれていてメンドクサそうだな、と思ったのですが、下記にライブラリと共存させるやり方が書いてありました。
Signing an Amazon Elasticsearch Service Search Request — AWS SDK for PHP documentation
ElasticsearchのClientBuilderのHandler を定義することで、リクエスト署名を挿入してリクエストを送ることが出来るようです。
<?php
require 'vendor/autoload.php';
use Aws\Credentials\CredentialProvider;
use Aws\Signature\SignatureV4;
use Elasticsearch\ClientBuilder;
use GuzzleHttp\Ring\Future\CompletedFutureArray;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\ResponseInterface;
$psr7Handler = Aws\default_http_handler();
$signer = new SignatureV4('es', 'us-west-2');
$credentialProvider = CredentialProvider::defaultProvider();
// Construct the handler that will be used by Elasticsearch-PHP
$handler = function (array $request) use (
$psr7Handler,
$signer,
$credentialProvider
) {
// Amazon ES listens on standard ports (443 for HTTPS, 80 for HTTP).
$request['headers']['host'][0]
= parse_url($request['headers']['host'][0], PHP_URL_HOST);
// Create a PSR-7 request from the array passed to the handler
$psr7Request = new Request(
$request['http_method'],
(new Uri($request['uri']))
->withScheme($request['scheme'])
->withHost($request['headers']['host'][0]),
$request['headers'],
$request['body']
);
// Sign the PSR-7 request with credentials from the environment
$signedRequest = $signer->signRequest(
$psr7Request,
call_user_func($credentialProvider)->wait()
);
// Send the signed request to Amazon ES
/** @var ResponseInterface $response */
$response = $psr7Handler($signedRequest)->wait();
// Convert the PSR-7 response to a RingPHP response
return new CompletedFutureArray([
'status' => $response->getStatusCode(),
'headers' => $response->getHeaders(),
'body' => $response->getBody()->detach(),
'transfer_stats' => ['total_time' => 0],
'effective_url' => (string) $psr7Request->getUri(),
]);
};
// Use the \Elasticsearch\ClientBuilder class to inject the handler into a
// new Elasticsearch-PHP client:
$client = ClientBuilder::create()
->setHandler($handler)
->setHosts(['https://<your-amazon-es-domain>:443'])
->build();
$params = [
'index' => 'inoue_test'
];
// Create the index
$response = $client->indices()->create($params);
var_dump($response);
これを es_aws_test.php というファイル名で保存します。
$ php es_aws_test.php
array(1) {
["acknowledged"]=>
bool(true)
}
inoue_test というインデックスが作られていることが確認できました。
この一連の手続きは結構長いので、この部分は Clientを生成する共通処理として作っておくとよさそうです。
あと、Node.js で Lambdaから ElasticSearch を更新するサンプルが awslabs に上がっていました。
amazon-elasticsearch-lambda-samples/s3_lambda_es.js at master · awslabs/amazon-elasticsearch-lambda-samples
実際に社内でも、Lambda からElasticSearchを更新する処理は稼働中とのことでした。早い!
Lambdaと同様に、ElasticSearch もVPC対応、期待しています。
[2016/01/28 追記]
アイキャッチ画像がクラスメソッドさん作成の画像との指摘を受け変更いたしました。ご迷惑をおかけしました。