API Gateway Lambdaオーソライザーを使ってAPIのアクセス制御を行う方法についてのメモです。実行環境はnode.js(8.10)を使用しました。
アーキテクチャ
API Gatewayを介してオーソライザー用関数(認証する関数)を呼び、認証が通ればAPI(アクセスを制御したい関数)を呼び出す、というフローになります。 ## 構築手順 #### アクセスを制御したいLambda関数を作成 APIにあたるLambda関数を作成しておきます。ここでは「test_api」という関数を作成しました。exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
(自動で生成されるコードから何も変更してません...)
API Gatewayを作成
作成したLambda関数のトリガーとしてAPI Gatewayを選択します。
新規APIを追加します。
以下のようなAPIが自動生成されました。
exports.handler = function(event, context, callback) {
// HTTPヘッダで送られてくるトークンを取得して...
var token = event.authorizationToken;
// 値が allow だったらAPI実行するためのIAMポリシーを生成して返す
if (token.toLowerCase() == 'allow') {
callback(null, generatePolicy('user', 'Allow', event.methodArn));
} else {
callback(null, generatePolicy('user', 'Deny', event.methodArn));
}
};
// IAMポリシーを生成し返却します
var generatePolicy = function(principalId, effect, resource) {
var authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
return authResponse;
}
オーソライザー用関数はIAMポリシーを返却します。
認証が通り、APIの実行を許可する場合は、そのLambda関数を実行するためのIAMポリシーを生成して返してあげる必要があります。
生成したポリシーを受け取った API Gateway がAPIのLambda関数を実行します。
ここでは認証用トークンが「allow」であれば許可し、それ以外は拒否するというBasic認証のようなシンプルな処理になっています。
(トークンはHTTPヘッダとして送信されますが、後ほどAPI Gatewayでヘッダの設定をします。)
API Gatewayのオーソライザーを作成
追加したAPI GatewayのAPI(test_api-API)を開き、オーソライザー > + 新しいオーソライザーの作成 をクリックして、以下のような設定で作成します。
オーソライザーをテストする
オーソライザーを作成したら、オーソライザー用関数を呼び出すテストをしてみます。API Gatewayで作成したオーソライザーに「テスト」というボタンがあるので、それをクリックすると、テスト用のウィンドウがでてきました。
認証トークン(上記の トークンのソース: Auth と設定したやつ)を入力するフィールドがあるので、そちらに「allow」と入力すると、以下のようなレスポンスが返ってきます。上記サンプルコードauth.js
のgeneratePolicy
で生成したやつですね。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:{region}:{accountId}:{appId}/ESTestInvoke-stage/GET/"
}
]
}
APIの実行を許可するIAMポリシーが返ってきたみたいです。
「allow」以外の値を送信すると、
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "arn:aws:execute-api:{region}:{accountId}:{appId}/ESTestInvoke-stage/GET/"
}
]
}
拒否するポリシーが返ってきました。
API Gateway リソースの設定
ところで、Lambdaにおけるトリガーの追加で自動生成したAPIのリソースを見てみると、以下のようになっています。
しかし前項でテストした際に返ってきた"Resource"は.../ステージ(ESTestInvoke-stage)/GET/
に対して許可していますが、リソースのルート「/」直下にGETメソッドはありませんね...。
オーソライザー用関数を変更してもいいですが、ここではIAMポリシーにあわせてリソースを変更しちゃいましょう。
/*/test_api
リソースはとりあえず使用しないので削除しました。
作成したANYメソッドの設定にて、認証を作成したオーソライザーを選択します。
設定したらAPIをデプロイしましょう!これで完成です!
動作確認
API Gateway のステージから確認できる呼び出しURLを実行してみます。
ヘッダに設定した認証用トークンを入れて実行すると、正常に実行されました!
$ curl -H "Auth: allow" https://{api-id}.execute-api.{region}.amazonaws.com/default
"Hello from Lambda!"
不正な認証用トークンをいれてみると、ちゃんと拒否されました。
$ curl -H "Auth: hoge" https://{api-id}.execute-api.{region}.amazonaws.com/default
{"Message":"User is not authorized to access this resource with an explicit deny"}
ヘッダになにも設定せずに呼び出すとUnauthorizedになります。
$ curl https://{api-id}.execute-api.{region}.amazonaws.com/default
{"message":"Unauthorized"}
以上です。