初めに
本日AWS SAM CLIのv1.80.0
がリリースされました。
以前のアップデートまではSAMテンプレートでLambdaオーソライザーを定義している場合、実際のデプロイにはLambdaオーソライザーが含まれますが、sam local start-api
によるローカルでの実行時にはLambdaオーソライザーが利用されずに無視される仕様となっておりました。
今回のアップデートではsam local start-api
で実行しアクセスした場合にもLambdaオーソライザーが適用されるようになり、SAM環境でもそれを含めた動作確認が可能となります。
なお要望自体については2017年末近くからあったようです。
実行サンプル
Authorization
ヘッダの値がSuccess
の場合のみ許可される処理を実装します。
コード
template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 60
MemorySize: 128
Resources:
Api:
Type: AWS::Serverless::Api
Properties:
StageName: dev
MethodSettings:
- ResourcePath: /
HttpMethod: GET
Auth:
DefaultAuthorizer: LambdaAuthorizer
Authorizers:
LambdaAuthorizer:
FunctionArn: !GetAtt AuthFunction.Arn
FunctionPayloadType: TOKEN
Identity:
Headers:
- Authorization
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.9
Architectures:
- arm64
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
RestApiId: !Ref Api
AuthFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: auth/
Handler: app.lambda_handler
Runtime: python3.9
Architectures:
- arm64
hello_world/app.py
import json
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world",
}),
}
auth/app.py
def lambda_handler(event, context):
auth_result = "Allow" if 'authorizationToken' in event and "Success" == event['authorizationToken'] else "Deny"
return {
'principalId': 'sam-local',
'policyDocument': {
'Version': '2012-10-17',
'Statement': [
{
'Action': 'execute-api:Invoke',
'Effect': auth_result,
'Resource': [
event['methodArn']
]
}
]
}
}
以前
以前まではテンプレート上指定があるにもかかわらずLambdaオーソライザが処理がされないためAuthorization
ヘッダの指定有無に関わらずhello_worldのLambda関数の実行が確認できました。
% sam local start-api
...
#別ウィンドウで実行
% curl http://127.0.0.1:3000/hello
{"message": "hello world"}%
アップデート後
Lambdaオーソライザーが利用されるようになりました。
% sam local start-api
...
# 別ウィンドウで実行
# 成功時
% curl http://127.0.0.1:3000/hello -H "Authorization: Success"
{"message": "hello world"}%
# Authorizationヘッダ未指定時
% curl http://127.0.0.1:3000/hello
{"message":"Unauthorized"}
# Authorizationヘッダの指定誤りで"Deny"が返却される場合
% curl http://127.0.0.1:3000/hello -H "Authorization: Succes"
{"message":"User is not authorized to access this resource"}
実際にデプロイしないと確認できない範囲ということが減った一方で、(見落としがなければ)オプションで有無効の切り替えはできなさそうなのでアプリによっては環境準備に追加の手順が必要な手間が発生するかもしれません。
ハマりどころ
この記事についてはサクッと試してサクッと書こうと思っていたのですが2時間近くはまりました。
v1.80.0
環境上ですので後続バージョンでは異なる可能性がある点はご注意ください。
FunctionPayloadType未指定時の挙動
現状FunctionPayloadType
の値が未設定の場合sam local start-api
環境上ではLambdaオーソライザーが起動しません。
アップデート後キャッシュ削除等を行なってもLambdaオーソライザーが起動すらせず原因特定のために--debug
オプションでログを追っていたところ上記に記載のソースコード部分にあたりました。
情報が足りなかったためaws-sam-cli
のソースコード自体に手を入れてデバッグしたところ、どうもSAMテンプレートでFunctionPayloadType
の値を指定しない場合、本来はTOKEN
の値が設定されるようですが、現状token
の値が指定されていました。
未指定の場合小文字であるtoken
に対してupper()
で大文字にキャストした値で比較をかけるので実質的に規定外の値指定扱いとなりLambdaオーソライザーが利用されません。
おそらく同様の理由でToken
等の場合も規定外の値扱いになるように見えます。
(定数っぽい値側にキャストをかけてるので仕様ではなくバグのような気がします)
Lambdaオーソライザーの返却値
ポリシーステートメントのResource
の値は単一の場合は配列ではなく文字列で指定することも多いですが、今回試していたところSAM環境ではResource
の値は配列である必要がありそうです。
仕様上明確に書かれているというよりは文字列で指定した場合Authorizer 'LambdaAuthorizer' policy document contains an invalid 'Resource'
が発生してしまいどうにも解決できなかったというところですのでもしかしたら自分の方で誤りがあるかもしれません。
このエラー時のLambdaオーソライザーの値の返却値は以下のように指定していました。
return {
'principalId': 'sam-local',
'policyDocument': {
'Version': '2012-10-17',
'Statement': [
{
'Action': 'execute-api:Invoke',
'Effect': auth_result,
'Resource': event['methodArn']
}
]
}
}
今回実装されたテストコード部分を見てみるとResource
の値が配列前提になっていたのでそれに合わせ配列値で返却したところ解決に辿り着きました。
実はLambdaオーソライザーは今回初めて触ったのですが、Amazon API Gateway Lambda オーソライザーからの出力を見る限りResource
の値は文字列で指定されており、文字列で指定しても実際のAWS上では正常に動くためもしかしたらSAM側のバグかもしれません。
なお文字列で指定した場合はInternal server error
扱いとなり関数自体が確認できなくなるので心当たりのある環境の方はバージョンアップの前に使い捨ての環境等で確認した方が良いかもしれません。
% curl http://127.0.0.1:3000/hello -H "Authorization: Success"
{"message":"Internal server error"}
結果的にですがFunctionPayloadType
を未指定にすることでLambdaオーソライザーを使わせずこの現象を回避することができます。
終わりに
今回のアップデートで実際のAWS環境を使わずともSAMの環境内でエミュレートできる範囲がまた1つ広がりました。
ただ少し動作が怪しい部分があるためアップデートの際はまずは戻しのできる環境で一度試してみてください。