はじめに
2022/4/6 から利用可能になった Lambda function URLs (関数 URL) はリクエストの認証タイプに AWS_IAM
を選択できます。AWS_IAM では IAM エンティティのポリシーと AWS Lambda のリソースベースポリシーに基づいてリクエストが認証されます。
リソースベースポリシーによる制御パターンについていくつか検証してみました。
結論
できたこと
- クロスアカウントの呼び出しを許可するポリシーステートメントの設定
- PrincipalOrgID を指定した、組織内からの呼び出しを許可するポリシーステートメントの設定
- コンソールからは設定できないため、AWS CLI/SDK での AddPermission API の実行が必要
できなかったこと
-
AWS グローバル条件コンテキストキー を使用した IP 制限などの細やかなアクセス制御
- AWS Lambda のリソースベースポリシーは AddPermission API で設定するが、指定可能な要素が限られているため
検証の前提
今回は Lambda function URLs の 認証タイプは AWS_IAM
に設定して検証しています。認証タイプに AWS_IAM
を設定した場合、デフォルトではリソースベースポリシーは設定されません。同じアカウント内で lambda:InvokeFunctionUrl
権限を持つ IAM ユーザーまたはロールのみが関数 URL を呼び出せます。
AWS_IAM 認証によるリクエストを試すには curl 7.75.0 以降で利用可能な --aws-sigv4 オプション がお手軽です。
$ curl -i https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/ \
-H "X-Amz-Security-Token: ${AWS_SESSION_TOKEN}" \
--aws-sigv4 "aws:amz:ap-northeast-1:lambda" \
--user "${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}"
HTTP/1.1 200 OK
Date: Fri, 15 Apr 2022 02:38:38 GMT
Content-Type: application/json
Content-Length: 20
Connection: keep-alive
x-amzn-RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
X-Amzn-Trace-Id: root=x-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx;sampled=0
"Hello from Lambda!"
参考: 認証タイプを NONE
に設定した場合
以下のようなリソースベースポリシーが設定されます。このポリシーにより、関数 URL にパブリックにアクセスできる状態になります。
{
"StatementId": "FunctionURLAllowPublicAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "lambda:InvokeFunctionUrl",
"Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:Lambda_Function_URLs_Policy_Test",
"Condition": {
"StringEquals": {
"lambda:FunctionUrlAuthType": "NONE"
}
}
}
クロスアカウントでの呼び出しを許可する
コンソールの アクセス権限を追加 から関数 URL 用のポリシーステートメントを設定できます。Principal にクロスアカウントでの呼び出しを許可する IAM エンティティの ARN を入力します。許可するアクションは lambda:InvokeFunctionUrl
固定です。以下の例では アカウント ID 111111111111
に対して呼び出しを許可しています。
以下のようなポリシーが設定され、クロスアカウントでの呼び出しができます。
{
"Sid": "x-account",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:root"
},
"Action": "lambda:InvokeFunctionUrl",
"Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:Lambda_Function_URLs_Policy_Test",
"Condition": {
"StringEquals": {
"lambda:FunctionUrlAuthType": "AWS_IAM"
}
}
}
この方式の場合、許可する IAM エンティティごとにポリシーを追加する必要があります。
組織内からの呼び出しを許可する
PrincipalOrgID を指定し、組織内の AWS アカウントの任意の IAM エンティティからの呼び出しを許可する例です。
以下の理由からコンソールからは設定できませんでした。
- 関数 URL のステートメント作成 UI では
PrincipalOrgID
を指定できない - AWS アカウントのステートメント作成 UI では、アクションに
lambda:InvokeFunctionUrl
を選択できない
AWS CLI を使用して AddPermission API を叩いた場合は設定できました。
$ aws lambda add-permission \
--function-name Lambda_Function_URLs_Policy_Test \
--function-url-auth-type AWS_IAM \
--statement-id PrincipalOrgIDExample \
--principal "*" \
--action lambda:InvokeFunctionUrl
--principal-org-id o-a1b2c3e4d5
以下のようなリソースベースポリシーがアタッチされます。
{
"Sid": "PrincipalOrgIDExample",
"Effect": "Allow",
"Principal": "*",
"Action": "lambda:InvokeFunctionUrl",
"Resource": "arn:aws:lambda:ap-northeast-1:123456789012:function:Lambda_Function_URLs_Policy_Test",
"Condition": {
"StringEquals": {
"lambda:FunctionUrlAuthType": "AWS_IAM",
"aws:PrincipalOrgID": "o-a1b2c3e4d5"
}
}
}
Condition 要素を指定した細やかな制御はできない
AWS Lambda のリソースベースポリシーは AddPermission API で設定しますが、前述したとおり指定可能な要素が限られています。そのため、例えば "Condition": {"IpAddress": {"aws:SourceIp": "xxx.xxx.xxx.xxx/24"}}
のように IP アドレス制限などを追加することは現状できません。
仮に IP アドレス制限を実装したい場合は、関数 URL から渡ってくる Event データの中に含まれる sorceIp から独自に実装する必要があります。
{
"version": "2.0",
"routeKey": "$default",
"rawPath": "/my/path",
"rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value",
"cookies": [
"cookie1",
"cookie2"
],
"headers": {
"header1": "value1",
"header2": "value1,value2"
},
"queryStringParameters": {
"parameter1": "value1,value2",
"parameter2": "value"
},
"requestContext": {
"accountId": "123456789012",
"apiId": "<urlid>",
"authentication": null,
"authorizer": {
"iam": {
"accessKey": "AKIA...",
"accountId": "111122223333",
"callerId": "AIDA...",
"cognitoIdentity": null,
"principalOrgId": null,
"userArn": "arn:aws:iam::111122223333:user/example-user",
"userId": "AIDA..."
}
},
"domainName": "<url-id>.lambda-url.us-west-2.on.aws",
"domainPrefix": "<url-id>",
"http": {
"method": "POST",
"path": "/my/path",
"protocol": "HTTP/1.1",
"sourceIp": "123.123.123.123",
"userAgent": "agent"
},
"requestId": "id",
"routeKey": "$default",
"stage": "$default",
"time": "12/Mar/2020:19:03:58 +0000",
"timeEpoch": 1583348638390
},
"body": "Hello from client!",
"pathParameters": null,
"isBase64Encoded": false,
"stageVariables": null
}
こちらは @_kensh さんから Twiiter でコメントいただきました。ありがとうございます。
参考
以上です。
参考になれば幸いです。