Lambda 関数
概要
IAM Access Analyzer が Lambda 関数の外部アクセスをどのように検出するかを検証する。
Lambda 関数はリソースベースポリシー(add-permission)により外部プリンシパルへのアクセスを許可できる。デフォルトではリソースベースポリシーが存在せず、明示的に追加しない限り外部アクセスは発生しない。リソースベースポリシーに Principal: "*" を設定するとパブリックアクセスとして検出され、特定の外部アカウントを設定するとクロスアカウントアクセスとして検出される。
Lambda 関数の外部アクセス制御レイヤー
| レイヤー | Lambda 関数の状況 |
|---|---|
| デフォルト保護 | なし(リソースベースポリシーがデフォルトで空) |
| 予防(SCP) | CT.LAMBDA.PV.2(add-permission で外部プリンシパルの設定をブロック) |
| Security Hub CSPM | Lambda.1(パブリックアクセスのチェック) |
| IAM Access Analyzer | パブリック + クロスアカウントの到達可能性を分析 |
add-permission API 呼び出しをブロックするが、既に設定されたリソースベースポリシーには影響しない。そのため、SCP を有効化しても、既に外部アクセスが設定された関数への Invoke は引き続き成功する。IAM Access Analyzer の resourceControlPolicyRestriction も NOT_APPLICABLE のまま変わらない。結果
- デフォルトのリソースベースポリシー: Lambda 関数はデフォルトではリソースベースポリシーが存在しない。
add-permissionで明示的に追加しない限り外部アクセスは発生しない - パブリックアクセスの検出: リソースベースポリシーに
Principal: "*"を設定すると、isPublic: trueの finding が生成された。action はlambda:InvokeFunction - クロスアカウントアクセスの検出: 組織外アカウントをリソースベースポリシーに設定すると、
isPublic: falseの finding が生成された。principal に組織外アカウント ID が特定された - 予防コントロール(CT.LAMBDA.PV.2)との連動: CT.LAMBDA.PV.2 は SCP 型であり、RCP 型(S3・KMS・IAM ロール)とは動作が異なる。SCP 有効時でも既存のパブリック関数への Invoke はブロックされず、
resourceControlPolicyRestrictionはNOT_APPLICABLEのまま変わらない。finding ID も変わらない。SCP は新規のadd-permissionAPI 呼び出しのみをブロックする - クリーンアップ:
remove-permissionでリソースベースポリシーを削除すると finding は RESOLVED になる
検証環境
本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。
検証の流れ
flowchart LR
A[1. 既存 finding の<br>確認] --> B[2. テスト用関数<br>作成]
B --> C[3. パブリックアクセス<br>検出の確認]
C --> D[4. クロスアカウント<br>アクセス検出]
D --> E[5. 予防コントロール<br>との連動]
E --> F[6. クリーンアップ]
1. 既存 finding の確認
テスト用関数を作成する前に、既存の Lambda 関数 finding を確認する。
デフォルトのリソースベースポリシーの確認
Lambda 関数はデフォルトではリソースベースポリシーが存在しない。
aws lambda get-policy \
--function-name <任意の既存関数名>An error occurred (ResourceNotFoundException) when calling the GetPolicy operation: The resource you requested does not exist.リソースベースポリシーが存在しないため、add-permission で明示的に追加しない限り外部アクセスは発生しない。
既存 finding の確認
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::Lambda::Function"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]2. テスト用関数の作成
2 つのテスト用 Lambda 関数を作成する。関数のコードは最小限のダミーとし、リソースベースポリシーで外部アクセスを設定する。
Lambda 実行ロールの作成
aws iam create-role \
--role-name iaa-lambda-test-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}
]
}'関数コードの準備
echo 'def handler(event, context): return {"statusCode": 200, "body": "ok"}' > /tmp/lambda_function.py
cd /tmp && zip function.zip lambda_function.pyパブリックアクセス検証用関数
aws lambda create-function \
--function-name iaa-lambda-public-test \
--runtime python3.12 \
--handler lambda_function.handler \
--role arn:aws:iam::<Workload アカウント ID>:role/iaa-lambda-test-role \
--zip-file fileb:///tmp/function.zip{
"FunctionName": "iaa-lambda-public-test",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test",
"State": "Pending"
}リソースベースポリシーに Principal: "*" を設定する。
aws lambda add-permission \
--function-name iaa-lambda-public-test \
--statement-id public-access \
--action lambda:InvokeFunction \
--principal "*"{
"Statement": "{\"Sid\":\"public-access\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test\"}"
}クロスアカウントアクセス検証用関数
aws lambda create-function \
--function-name iaa-lambda-crossaccount-test \
--runtime python3.12 \
--handler lambda_function.handler \
--role arn:aws:iam::<Workload アカウント ID>:role/iaa-lambda-test-role \
--zip-file fileb:///tmp/function.zip{
"FunctionName": "iaa-lambda-crossaccount-test",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test",
"State": "Pending"
}リソースベースポリシーに組織外アカウントを設定する。
aws lambda add-permission \
--function-name iaa-lambda-crossaccount-test \
--statement-id crossaccount-access \
--action lambda:InvokeFunction \
--principal <組織外アカウント ID>{
"Statement": "{\"Sid\":\"crossaccount-access\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::<組織外アカウント ID>:root\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test\"}"
}3. パブリックアクセスの検出
パブリックアクセス検証用関数(Principal: "*")の finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-lambda-public-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<パブリック関数 finding ID>",
"resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test",
"resourceType": "AWS::Lambda::Function",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <パブリック関数 finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": ["lambda:InvokeFunction"],
"condition": {},
"isPublic": true,
"principal": {"AWS": "*"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test",
"status": "ACTIVE",
"resourceType": "AWS::Lambda::Function",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<パブリック関数 finding ID>"
}以下を確認する。
isPublicがtrueprincipalが{"AWS": "*"}actionがlambda:InvokeFunctionresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからパブリック関数への Invoke が成功することを確認する。
aws lambda invoke \
--function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test \
/tmp/lambda-output.json \
--query '{StatusCode:StatusCode}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"StatusCode": 200
}4. クロスアカウントアクセスの検出
クロスアカウントアクセス検証用関数の finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-lambda-crossaccount-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<クロスアカウント関数 finding ID>",
"resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test",
"resourceType": "AWS::Lambda::Function",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <クロスアカウント関数 finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": ["lambda:InvokeFunction"],
"condition": {},
"isPublic": false,
"principal": {"AWS": "<組織外アカウント ID>"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test",
"status": "ACTIVE",
"resourceType": "AWS::Lambda::Function",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<クロスアカウント関数 finding ID>"
}以下を確認する。
isPublicがfalseprincipalに組織外アカウント ID が特定されているactionがlambda:InvokeFunctionresourceControlPolicyRestrictionがNOT_APPLICABLE
組織外アカウントからの実際のアクセス確認
組織外アカウントからクロスアカウント関数への Invoke が成功することを確認する。
aws lambda invoke \
--function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test \
/tmp/lambda-output.json \
--query '{StatusCode:StatusCode}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"StatusCode": 200
}5. 予防コントロール(CT.LAMBDA.PV.2)との連動
add-permission で外部プリンシパルの設定をブロック)が有効な環境で実施する。CT.LAMBDA.PV.2 の詳細は CT.LAMBDA.PV.2 の検証記事 を参照。resourceControlPolicyRestriction が APPLICABLE に変わる。一方、SCP はパブリック・クロスアカウント両方の add-permission API 呼び出しをブロックするが、既に設定されたリソースベースポリシーには影響しない。そのため、SCP を有効化しても、既に外部アクセスが設定された関数への Invoke は引き続き成功し、resourceControlPolicyRestriction も NOT_APPLICABLE のまま変わらない。CT.LAMBDA.PV.2 の有効化確認
CT.LAMBDA.PV.2 が有効であることを確認する。
aws controltower list-enabled-controls \
--target-identifier <対象 OU ARN> \
--query "enabledControls[?controlIdentifier=='arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy'].{controlIdentifier:controlIdentifier,statusSummary:statusSummary}" \
--profile Master[]無効の場合は有効化する。
aws controltower enable-control \
--control-identifier arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy \
--target-identifier <対象 OU ARN> \
--profile Master{
"operationIdentifier": "<オペレーション ID>"
}既存のパブリックアクセスがブロックされないことの確認
CT.LAMBDA.PV.2 が有効な状態でも、既存のリソースベースポリシー(Principal: "*")による Invoke はブロックされないことを確認する。
aws lambda invoke \
--function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test \
/tmp/lambda-output.json \
--query '{StatusCode:StatusCode}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"StatusCode": 200
}SCP は API 呼び出しをブロックするだけで、既存のリソースベースポリシーによるアクセスには遡及しない。
既存のクロスアカウント関数への Invoke も同様にブロックされないことを確認する。
aws lambda invoke \
--function-name arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test \
/tmp/lambda-output.json \
--query '{StatusCode:StatusCode}' \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"StatusCode": 200
}新規の add-permission がブロックされることの確認
CT.LAMBDA.PV.2 が有効な状態で、新規の add-permission(Principal: "*")がブロックされることを確認する。
aws lambda add-permission \
--function-name iaa-lambda-crossaccount-test \
--statement-id public-access-blocked \
--action lambda:InvokeFunction \
--principal "*"An error occurred (AccessDeniedException) when calling the AddPermission operation: User: arn:aws:sts::<Workload アカウント ID>:assumed-role/<ロール名>/<セッション名> is not authorized to perform: lambda:AddPermission on resource: arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-crossaccount-test with an explicit deny in a service control policyresourceControlPolicyRestriction の確認
再スキャンを実行し、SCP 型では resourceControlPolicyRestriction が変わらないことを確認する。
aws accessanalyzer start-resource-scan \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--resource-arn arn:aws:lambda:ap-northeast-1:<Workload アカウント ID>:function:iaa-lambda-public-test \
--resource-owner-account <Workload アカウント ID> \
--region ap-northeast-1 \
--profile Audit
(出力なし)aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <パブリック関数 finding ID> \
--region ap-northeast-1 \
--profile AuditresourceControlPolicyRestriction が NOT_APPLICABLE のまま変わらないことを確認する。SCP 型のコントロールは IAM Access Analyzer の resourceControlPolicyRestriction には反映されない。また、RCP 型では再スキャンにより旧 finding が RESOLVED になり新しい finding ID で再生成されたが、SCP 型では finding ID も変わらない。
6. クリーンアップ
CT.LAMBDA.PV.2 の無効化
aws controltower disable-control \
--control-identifier arn:aws:controlcatalog:::control/aksh80tizisat4i4ou8qg1hdy \
--target-identifier <対象 OU ARN> \
--profile Master{
"operationIdentifier": "<オペレーション ID>"
}リソースベースポリシーの削除
aws lambda remove-permission \
--function-name iaa-lambda-public-test \
--statement-id public-access
(出力なし)aws lambda remove-permission \
--function-name iaa-lambda-crossaccount-test \
--statement-id crossaccount-access
(出力なし)finding の確認
リソースベースポリシー削除後、finding が RESOLVED になることを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"resourceType": {"eq": ["AWS::Lambda::Function"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]テスト用関数の削除
aws lambda delete-function \
--function-name iaa-lambda-public-test
(出力なし)aws lambda delete-function \
--function-name iaa-lambda-crossaccount-test
(出力なし)Lambda 実行ロールの削除
aws iam delete-role \
--role-name iaa-lambda-test-role
(出力なし)