AWS APIGatewayはIAMでアクセスコントロールをすることができます。
AWS_IAMを設定したAPIGatewayのエンドポイントにはAWS署名バージョン4の仕様に沿ったAuthorizationヘッダを付加してリクエストする必要があります。
ところがこの署名を生成するプロセスって結構面倒なのですよね。そんなものAWSSDKで用意されているのだろうと思いきや、
これ、privateやん…というところが動機でした。
npm request が AWS署名をサポートしていた
node.jsにはメジャーなHTTPClientライブラリとして、requestというnpmモジュールがあります。こいつのREADMEをよく眺めてみると、
- aws - object containing AWS signing information. Should have the properties key, secret. Also requires the property bucket, unless you’re specifying your bucket as part of the path, or the request doesn’t use a bucket (i.e. GET Services). If you want to use AWS sign version 4 use the parameter sign_version with value 4 otherwise the default is version 2. Note: you need to npm install aws4 first.
サポートしているではありませんか。
"use strict"
const co = require("co");
const request = require("request-promise-native");
const AWS = require("aws-sdk");
co(function*(){
const cred = yield getCredential();
console.log(cred.accessKeyId, cred.secretAccessKey, cred.sessionToken);
return yield request({
uri: " https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/trial/getinfo/1234",
aws: {
sign_version: 4,
key: cred.accessKeyId,
secret: cred.secretAccessKey,
}
})
}).then(console.log).catch(console.error);
function getCredential() {
const credChain = new AWS.CredentialProviderChain();
return new Promise((resolve, reject) => {
credChain.resolve((err, creds) => {
if(err) {
reject(err);
} else {
resolve(creds);
}
})
})
}
こんなので無事リクエストが受け入れられ、レスポンスが返ってきました。
サンプルソースはPromise
,co
,function*
,yield
をガシガシ使っており、Node.js 4.x前提です。
ちなみに、署名をつけていないと、
[StatusCodeError: 403 - "{\"message\":\"Missing Authentication Token\"}"]
署名が間違っていると、
[StatusCodeError: 403 - "{\"message\":\"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
こんな感じのエラーになります。
しかし、少し不都合がある
二点困ったことがありました。
- APIGatewayにカスタムドメインを設定しているケースではエラーになる
- IAMロールでAPIのInvokeができるように設定したEC2インスタンスからの実行するケースではエラーになる
困ったのでプルリクエスト投げました。
https://github.com/request/request/pull/2339
上記がマージされるまではnpm install newgyu/request
でnpmモジュールをインストールすると上記の問題に対処できます。
"use strict"
const co = require("co");
const request = require("request-promise-native");
const AWS = require("aws-sdk");
co(function*(){
const cred = yield getCredential();
console.log(cred.accessKeyId, cred.secretAccessKey, cred.sessionToken);
return yield request({
uri: "https://api.newgyu.xyz/trial/getinfo/1234",
aws: {
sign_version: 4,
key: cred.accessKeyId,
secret: cred.secretAccessKey,
//トークンの指定
sessionToken: cred.sessionToken,
//service,regionの指定
service: "execute-api", //APIGatewayの場合にはexecute-apiを指定する
region: "ap-northeast-1"
}
})
}).then(console.log).catch(console.error);
function getCredential() {
const credChain = new AWS.CredentialProviderChain();
return new Promise((resolve, reject) => {
credChain.resolve((err, creds) => {
if(err) {
reject(err);
} else {
resolve(creds);
}
})
})
}