はじめに
こんにちは、技術1課の山中です。
前回 に引き続き今回も Amazon API Gateway に関するアップデートです。
HTTP API におけるアクセス制御
これまでも JSON ウェブトークン (JWT) を利用することで HTTP API へのアクセス制御は実現できました。
今回のアップデートで、新たに AWS Lambda と IAM のオーソライザーに対応し、 HTTP API では以下 3 つのアクセス制御が可能となりました。
- Lambda
- JWT
- IAM
本ブログでは HTTP API の AWS Lambda オーソライザーについて確認していきます。
HTTP API の Lambda オーソライザー
Lambda オーソライザーを利用すると Lambda 関数で HTTP API のアクセス制御を行うことが可能です。
API Gateway がクライアントからのリクエストを受け取ると Lambda 関数を呼び出し Lambda 関数からのレスポンスを使用してクライアントが API にアクセスするかどうか判断します。
ペイロード形式のバージョン
Lambda オーソライザーでは、 API Gateway から Lambda へ送信するデータの形式とレスポンスの形式を ペイロード形式のバージョン として指定します。
ペイロードの形式のバージョンを指定すると API Gateway と Lambda 間でやり取りするデータ構造が決定します。
現在 1.0 と 2.0 のバージョンがあり、 REST API との互換性が必要な場合は 1.0 を指定する必要があります。
※ ペイロード形式のバージョンを指定しない場合は、最新バージョンが使用されます。
バージョン 1.0 のデータ構造
API Gateway → Lambda
{ "version": "1.0", "type": "REQUEST", "methodArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/test/GET/request", "identitySource": "user1,123", "authorizationToken": "user1,123", "resource": "/request", "path": "/request", "httpMethod": "GET", "headers": { "X-AMZ-Date": "20170718T062915Z", "Accept": "*/*", "HeaderAuth1": "headerValue1", "CloudFront-Viewer-Country": "US", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Is-Mobile-Viewer": "false", "User-Agent": "..." }, "queryStringParameters": { "QueryString1": "queryValue1" }, "pathParameters": {}, "stageVariables": { "StageVar1": "stageValue1" }, "requestContext": { "path": "/request", "accountId": "123456789012", "resourceId": "05c7jb", "stage": "test", "requestId": "...", "identity": { "apiKey": "...", "sourceIp": "..." }, "resourcePath": "/request", "httpMethod": "GET", "apiId": "abcdef123" } }
Lambda → API Gateway
{ "principalId": "abcdef", // The principal user identification associated with the token sent by the client. "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow|Deny", "Resource": "arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]" } ] }, "context": { "exampleKey": "exampleValue" } }
バージョン 2.0 のデータ構造
API Gateway → Lambda
{ "version": "2.0", "type": "REQUEST", "routeArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/test/GET/request", "identitySource": ["user1", "123"], "routeKey": "$default", "rawPath": "/my/path", "rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", "cookies": [ "cookie1", "cookie2" ], "headers": { "Header1": "value1", "Header2": "value2" }, "queryStringParameters": { "parameter1": "value1,value2", "parameter2": "value" }, "requestContext": { "accountId": "123456789012", "apiId": "api-id", "domainName": "id.execute-api.us-east-1.amazonaws.com", "domainPrefix": "id", "http": { "method": "POST", "path": "/my/path", "protocol": "HTTP/1.1", "sourceIp": "IP", "userAgent": "agent" }, "requestId": "id", "routeKey": "$default", "stage": "$default", "time": "12/Mar/2020:19:03:58 +0000", "timeEpoch": 1583348638390 }, "pathParameters": {"parameter1": "value1"}, "stageVariables": {"stageVariable1": "value1", "stageVariable2": "value2"} }
Lambda → API Gateway
バージョン 2.0 の場合、 Lambda 関数からは単純なブール値もしくは、 IAM ポリシー構文を返すことが可能です。
これはオーソライザーの設定で指定できます。 (Simple もしくは AM Policy )
それぞれのレスポンスモードによるデータ構造は以下のとおりです。
Simple
{ "isAuthorized": true/false, "context": { "exampleKey": "exampleValue" } }
IAM Policy
{ "principalId": "abcdef", // The principal user identification associated with the token sent by the client. "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow|Deny", "Resource": "arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]" } ] }, "context": { "exampleKey": "exampleValue" } }
試してみる
今回は、 前回 のブログで作成した HTTP API に Lambda オーソライザーを利用してアクセス制御を行ってみようと思います。 また、ペイロード形式は最新の 2.0 で Simple と IAM Policy 両方のレスポンスモードを試してみます。
レスポンスモード = Simple
まずはシンプルなレスポンスモードを指定して、オーソライザーを試していきます。
Lambda 関数の作成
オーソライザー用の Lambda 関数を用意します。
ペイロード形式のバージョンとレスポンスモードに合わせて、 http-api-authorizer-ver2-simple
という Lambda 関数を Python 3.8 で作成します。
コードに以下を入力し、保存できたら完成です。
def lambda_handler(event, context): response = { 'isAuthorized': False, 'context': { 'stringKey': 'value', 'numberKey': 1, 'booleanKey': True, 'arrayKey': ['value1', 'value2'], 'mapKey': {'value1': 'value2'} } } if event['headers']['authorization'] == 'secretToken': response = { 'isAuthorized': True, 'context': { 'stringKey': 'value', 'numberKey': 1, 'booleanKey': True, 'arrayKey': ['value1', 'value2'], 'mapKey': {'value1': 'value2'} } } return response
context
についてはオプションですが、コンテキストプロパティを入力することで HTTP API のアクセスログをカスタマイズすることができます。
例えば、ログの設定を以下のようにすると、
CloudWatch Logs にはこのようにログが出力されます。
HTTP API のアクセスログについては、以下を参照ください。
docs.aws.amazon.com
オーソライザーの作成
HTTP API の画面から[認可] - [オーソライザーを管理] を選択して新しいオーソライザーを作成していきます。
オーソライザーの作成画面で、オーソライザータイプ Lambda を選択後、 http-api-authorizer-ver2-simple
を指定してください。
ペイロード形式のバージョンは 2.0 、レスポンスモードは Simple としています。
これで作成は完了です。
オーソライザーのアタッチ
作成したオーソライザーをルートにアタッチします。
オーソライザーを管理タグの横にある オーソライザーをルートにアタッチ タグを選択します。
まだ何もアタッチされていないので、作成したオーソライザーを選び オーソライザーをアタッチ ボタンをクリック。
これでオーソライザーの設定は完了です。
実行
さっそく今回アタッチしたオーソライザーを試してみましょう。 まずは HTTP API に対して誤ったトークンを指定し GET リクエストを送信してみます。
$ curl -X GET https://swdld8wdl6.execute-api.ap-northeast-1.amazonaws.com/appconfig -H "Authorization: invalidSecretToken" {"message":"Forbidden"}
誤ったトークンを指定しているため、エラーが返されデータの取得に失敗しました!
続いて、 Lambda で指定したトークン secretToken
をヘッダにつけてリクエストを送信してみます。
$ curl -X GET https://swdld8wdl6.execute-api.ap-northeast-1.amazonaws.com/appconfig -H "Authorization: secretToken" - PLCRz5JqTKzfn5rWKSfEAceiJWeHAxHThd #30分でわかる AWS UPDATE! - サービスカット版 - - PLCRz5JqTKzfkU-mN5u13OC6OzuXrhhqc2 #30分でわかる AWS UPDATE! - PLCRz5JqTKzfkEmHYlklTY7vHY0vbXMQSE #はじめてのAWS - PLCRz5JqTKzfkAOEjtq2eniDfTCpl4-go3 #初級ハンズオン - PLCRz5JqTKzfmHYSSSQhs2Y-qPFU201s3p #毎日AWS
無事取得することができました!きちんとオーソライザーが機能していることがわかりますね!
レスポンスモード = IAM Policy
続いて IAM Policy のレスポンスモードを試します。
Lambda 関数の作成
先ほどと同様にオーソライザー用の Lambda 関数 http-api-authorizer-ver2-iampolicy
を作成します。
def lambda_handler(event, context): if event['headers']['authorization'] == 'secretToken': print('allowd') return { 'principalId': 'abcdef', 'policyDocument': { 'Version': '2012-10-17', 'Statement': [ { 'Action': 'execute-api:Invoke', 'Effect': 'Allow', 'Resource': event['routeArn'] } ] }, 'context': { 'stringKey': 'value', 'numberKey': 1, 'booleanKey': True, 'arrayKey': ['value1', 'value2'], 'mapKey': {'value1': 'value2'} } } print('denied') return { 'principalId': 'abcdef', 'policyDocument': { 'Version': '2012-10-17', 'Statement': [ { 'Action': 'execute-api:Invoke', 'Effect': 'Deny', 'Resource': event['routeArn'] } ] }, 'context': { 'stringKey': 'value', 'numberKey': 1, 'booleanKey': True, 'arrayKey': ['value1', 'value2'], 'mapKey': {'value1': 'value2'} } }
オーソライザーの作成
まずは、先ほどアタッチしたオーソライザーをデタッチします。
デタッチできたら、新しいオーソライザーを作成します。
ここでは Lambda 関数に http-api-authorizer-ver2-iampolicy
を指定します。
これで作成は完了です。
オーソライザーのアタッチ
作成したオーソライザーをルートにアタッチします。
これでオーソライザーの設定は完了です。
実行
設定が終わったので、こちらも試してみましょう。
まずは間違ったトークン invalidSecretToken
を指定して GET リクエストを送信します。
$ curl -X GET https://swdld8wdl6.execute-api.ap-northeast-1.amazonaws.com/appconfig -H "Authorization: invalidSecretToken" {"message":"Forbidden"}
先ほどと同様、間違ったトークンを指定しているため、エラーが返されデータの取得に失敗しました。
Lambda 関数 http-api-authorizer-ver2-iampolicy
の CloudWatch Logs を確認してみると、トークンが異なるため denied
が出力されています。
続いて、 Lambda で指定したトークン secretToken
をヘッダにつけてリクエストを送信してみます。
$ curl -X GET https://swdld8wdl6.execute-api.ap-northeast-1.amazonaws.com/appconfig -H "Authorization: secretToken" - PLCRz5JqTKzfn5rWKSfEAceiJWeHAxHThd #30分でわかる AWS UPDATE! - サービスカット版 - - PLCRz5JqTKzfkU-mN5u13OC6OzuXrhhqc2 #30分でわかる AWS UPDATE! - PLCRz5JqTKzfkEmHYlklTY7vHY0vbXMQSE #はじめてのAWS - PLCRz5JqTKzfkAOEjtq2eniDfTCpl4-go3 #初級ハンズオン - PLCRz5JqTKzfmHYSSSQhs2Y-qPFU201s3p #毎日AWS
無事取得することができました!CloudWatch Logs を確認してみると、 allowd
が出力されています。
こちらでもきちんとオーソライザーが機能していることがわかりますね!
さいごに
HTTP API のオーソライザーに IAM と Lambda がそれぞれ加わることでこれまで以上により柔軟なアクセス制御ができそうです!
参考
- API Gateway HTTP APIs now supports Lambda and IAM authorization options
- Working with AWS Lambda authorizers for HTTP APIs - Amazon API Gateway
- Customizing HTTP API access logs - Amazon API Gateway
- API Gateway の HTTP API が 5 つの AWS サービスとの統合をサポート - サーバーワークスエンジニアブログ