IAM Roleを設定したEC2インスタンスでCloudSearchにアクセスできなかったので対応した件

2014.12.09

はじめに

node.jsを用いて、CloudSearch用いる例としては弊社k.ootakiの記事を参照ください。

node.jsでCloudSearchを動かしてみる | Developers.IO

上記の方法では、認証情報(accessKey/secretAccessKey)をconfig.jsonに記載していますが、 EC2上で動かす際は、それらの情報はIAM Role側に持たせることで、認証情報を記述することなく動かすことが可能です。

システムでIAM Roleを用いて動かすといった話は以下の都元の記事を参照ください。

IAMによるAWS権限管理運用ベストプラクティス (2) | Developers.IO

検証を行った環境は以下になります。

  • Amazon Linux AMI 2014.09.1 (HVM)
  • node.js v0.10.32
  • aws-sdk version 2.0.30

ES2上で動かない!?

私の上記のブログを読んだ理解では、IAMRoleを用いて動かすには以下のコードで動くものと想像していました。

sample.js

var AWS = require('aws-sdk');
var config = require('config');
var cloudSearch = new AWS.CloudSearchDomain({
    endpoint: config.endpoint,
    region:config.region
});
var params = {
  query: '角川'
};

cloudSearch.search(params, function (err, data) {
    if (err) {
        console.log('error', err);
    }
    if (data) {
        if (data.hits.found > 0) {
      		console.log(data.hits.hit[0]);
    	} else {
      		console.log('not found');
    	}
    }
});

割り当てたIAM Roleは以下のようになります。

IAM Role

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "cloudsearch:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

上記のコードをIAM Roleを設定したES2で動かそうとすると以下のようになります。

$ node ./sample.js
User: anonymous is not authorized to perform

このコードを動かしてみると、User: anonymous is not authorized to performといったエラーが出てしまいます。

要はCredentialがセットされていないようなので、セットしようとおもいます。

クレデンシャルを探せ!

AccessKey/secretAccessKeyをセットすれば動きはしますが、 ベストプラクティスである”APIキーをシステム内にハードコーディングしない”に反してしまうことになります。

上記の例ではCloudSearchDomainのイニシャライズ時にendpointのみを渡していますが、 その際にパラメータとしてクレデンシャルを渡すことができそうです。

File: README — AWS SDK for JavaScript

以下がそのパラメータ候補になります。

  • credentials (AWS.Credentials) — the AWS credentials to sign requests with. You can either specify this object, or specify the accessKeyId and secretAccessKey options directly.
  • credentialProvider (AWS.CredentialProviderChain) — the provider chain used to resolve credentials if no static credentials property is set.

credentialProviderをセットしてみる。

ベストプラクティスでも紹介されていた、credentialProviderをセットする方法を試してみたいと思います。

sample2.js

var AWS = require('aws-sdk');
var config = require('config');
var chain = new AWS.CredentialProviderChain();
var cloudSearch = new AWS.CloudSearchDomain({
    endpoint: config.endpoint,
    region:config.region,
    credentialProvider:chain
});
var params = {
  query: '角川'
};

cloudSearch.search(params, function (err, data) {
    if (err) {
        console.log('error', err);
    }
    if (data) {
        if (data.hits.found > 0) {
      		console.log(data.hits.hit[0]);
    	} else {
      		console.log('not found');
    	}
    }
});

上記の方法でAWS S3クラス等では、credentialを取得しAPIを実行することができるのですが、 なぜかCloudSearchDomainはだめで結果は最初とかわりません。

Credentinalの情報を取得してセットする。

直接Credential情報をセットすれば動くことはわかっているので、CredentilProviderChainから取得しそれをセットします。

sample3.js

var AWS = require('aws-sdk');
var config = require('config');
var chain = new AWS.CredentialProviderChain();
chain.resolve(function(err, cre) {
    var cloudSearch = new AWS.CloudSearchDomain({
        endpoint: config.endpoint,
        credentials: cre,
        region: config.region

    });
    var params = {
        query: '角川'

    };

    cloudSearch.search(params, function(err, data) {
            if (err) {
                console.log('error', err);

            }
            if (data) {
                if (data.hits.found > 0) {
                    console.log(data.hits.hit[0]);

                } else {
                    console.log('not found');

                }

            });

    });
});

上記のコードを動かすことで、ようやくCloudSearchにアクセスすることができました。

$ $ node sample3.js
{ id: '5',
  fields: { title: [ '新世紀エヴァンゲリオン(13) (角川コミックス・エース)' ] } }

まとめ

ほかの言語で検証していないので、上記の現象がnode.jsのみにおけるものなのか、 それとも他の開発言語でも該当するのかは不明です。

なお、AWS.CloudSearchクラスはIAM Roleが適用されているEC2インスタンス上から node.jsのSDKからAPIを呼ぶことが可能でした。