限制对 Amazon Lambda 函数 URL 源的访问 - Amazon CloudFront
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

限制对 Amazon Lambda 函数 URL 源的访问

CloudFront 提供源访问控制(OAC),以限制对 Lambda 函数 URL 源的访问。

创建新 OAC

完成以下主题中描述的步骤,在 CloudFront 中设置新的 OAC。

重要

如果您在 Lambda 函数 URL 中使用 PUTPOST 方法,则您的用户在向 CloudFront 发送请求时,必须计算正文的 SHA256,并在 x-amz-content-sha256 标头中包含请求正文的有效载荷哈希值。Lambda 不支持未签名的有效负载。

先决条件

在创建和设置 OAC 之前,您必须拥有将 Lambda 函数 URL 作为源的 CloudFront 分配。要使用 OAC,您必须将 AWS_IAM 指定作为 AuthType 参数的值。有关更多信息,请参阅 使用 Lambda 函数 URL

向 CloudFront 授予访问 Lambda 函数 URL 的权限

在 CloudFront 分配中创建 OAC 或对其设置之前,请确保 CloudFront 具有访问 Lambda 函数 URL 的权限。请在创建 CloudFront 分配后,但在分配配置中将 OAC 添加到 Lambda 函数 URL 之前,执行此操作。

注意

要更新 Lambda 函数 URL 的 IAM 策略,您必须使用 Amazon Command Line Interface(Amazon CLI)。目前不支持在 Lambda 控制台中编辑 IAM 策略。

以下 Amazon CLI 命令授予 CloudFront 服务主体(cloudfront.amazonaws.com)访问您的 Lambda 函数 URL 的权限。使用策略中的 Condition 元素, 在该请求代表包含 Lambda 函数 URL 的 CloudFront 分配时,才允许 CloudFront 访问 Lambda。这是您要向其添加 OAC 的具有 Lambda 函数 URL 源的分配。

例 :更新策略以支持启用了 OAC 的 CloudFront 分配进行只读访问的 Amazon CLI 命令

以下 Amazon CLI 命令允许 CloudFront 分配(E1PDK09ESKHJWT)访问您的 Lambda FUNCTION_URL_NAME

aws lambda add-permission \ --statement-id "AllowCloudFrontServicePrincipal" \ --action "lambda:InvokeFunctionUrl" \ --principal "cloudfront.amazonaws.com" \ --source-arn "arn:aws:cloudfront::123456789012:distribution/E1PDK09ESKHJWT" \ --function-name FUNCTION_URL_NAME
注意

如果您创建了一个分配,但它没有访问您的 Lambda 函数 URL 的权限,则可以从 CloudFront 控制台中选择复制 CLI 命令,然后从命令行终端输入此命令。有关更多信息,请参阅《Amazon Lambda 开发人员指南》中的向 Amazon Web Services 服务 授予函数访问权

创建 OAC

要创建 OAC,可以使用 Amazon Web Services Management Console、Amazon CloudFormation、Amazon CLI 或 CloudFront API。

Console
创建 OAC
  1. 登录 Amazon Web Services Management Console,并通过以下网址打开 CloudFront 控制台:https://console.amazonaws.cn/cloudfront/v4/home

  2. 在导航窗格中,选择 Origin access(源访问)。

  3. 选择 Create control setting(创建控制设置)。

  4. 创建新 OAC 页面上,执行以下操作:

    1. 输入 OAC 的名称和(可选)描述

    2. 签名行为中,建议保留默认设置(签署请求(推荐))。有关更多信息,请参阅 源访问控制的高级设置

  5. 对于源类型,请选择 Lambda

  6. 选择创建

    提示

    创建 OAC 后,记下名称。在以下过程中,您需要此名称。

向分配中的 Lambda 函数 URL 添加源访问控制
  1. 通过 https://console.amazonaws.cn/cloudfront/v4/home 打开 CloudFront 控制台

  2. 选择具有要向其中添加 OAC 的 Lambda 函数 URL 的分配,然后选择选项卡。

  3. 选择要向其中添加 OAC 的 Lambda 函数 URL,然后选择编辑

  4. 对于源的协议,请选择仅 HTTPS

  5. 源访问控制下拉菜单中,选择要使用的 OAC。

  6. 选择保存更改

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时,它会签署自己发送到 Lambda 函数 URL 的所有请求。

CloudFormation

要使用 Amazon CloudFormation 创建 OAC,请使用 AWS::CloudFront::OriginAccessControl 资源类型。以下示例演示了 YAML 格式的 Amazon CloudFormation 模板语法,用于创建 OAC。

Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Description: An optional description for the origin access control Name: ExampleOAC OriginAccessControlOriginType: lambda SigningBehavior: always SigningProtocol: sigv4

有关更多信息,请参阅 Amazon CloudFormation 用户指南 中的 AWS::CloudFront::OriginAccessControl

CLI

要使用 Amazon Command Line Interface (Amazon CLI) 创建源访问控制,请使用 aws cloudfront create-origin-access-control 命令。您可以使用输入文件来提供命令的输入参数,而不是将每个单独的参数指定为命令行输入。

创建源访问控制(带输入文件的 CLI)
  1. 使用以下命令创建名为 origin-access-control.yaml 的文件。此文件包含 create-origin-access-control 命令的所有输入参数。

    aws cloudfront create-origin-access-control --generate-cli-skeleton yaml-input > origin-access-control.yaml
  2. 打开刚创建的 origin-access-control.yaml 文件。编辑文件以添加 OAC 的名称、描述(可选),然后将 SigningBehavior 更改为 always。然后保存文件。

    有关其他 OAC 设置的信息,请参阅源访问控制的高级设置

  3. 使用以下命令通过 origin-access-control.yaml 文件中的输入参数创建源访问控制。

    aws cloudfront create-origin-access-control --cli-input-yaml file://origin-access-control.yaml

    记下命令输出中的 Id 值。您需要使用它将 OAC 添加到 CloudFront 分配中的 Lambda 函数 URL。

将 OAC 附加到现有分配中的 Lambda 函数 URL(带输入文件的 CLI)
  1. 使用以下命令保存要向其添加 OAC 的 CloudFront 分配的分配配置。该分配必须将 Lambda 函数 URL 作为源。

    aws cloudfront get-distribution-config --id <CloudFront distribution ID> --output yaml > dist-config.yaml
  2. 打开刚创建的名为 dist-config.yaml 的文件。编辑文件,进行以下更改:

    • Origins 对象中,将 OAC 的 ID 添加到名为 OriginAccessControlId 的字段中。

    • 从名为 OriginAccessIdentity 的字段中移除值(如果存在)。

    • ETag 字段重命名为 IfMatch,但不更改字段的值。

    完成后保存该文件。

  3. 使用以下命令更新分配以使用源访问控制。

    aws cloudfront update-distribution --id <CloudFront distribution ID> --cli-input-yaml file://dist-config.yaml

分配开始部署到所有 CloudFront 边缘站点。当边缘站点收到新配置时,它会签署自己发送到 Lambda 函数 URL 的所有请求。

API

要使用 CloudFront API 创建 OAC,请使用 CreateOriginAccessControl。有关您在此 API 调用中指定的字段的更多信息,请参阅有关 Amazon SDK 或其他 API 客户端的 API 参考文档。

创建 OAC 后,可以使用下面的任何一个 API 调用将其附加到分配中的 Lambda 函数 URL:

对于这两个 API 调用,请在源内的 OriginAccessControlId 字段中提供 OAC ID。有关您在这些 API 调用中指定的其他字段的更多信息,请参阅有关 Amazon SDK 或其他 API 客户端的 API 参考文档。

源访问控制的高级设置

CloudFront OAC 功能包括仅适用于特定使用案例的高级设置。除非您特别需要高级设置,否则请使用推荐的设置。

OAC 包含一个名为签名行为(在控制台中)或 SigningBehavior(在 API、CLI 和 Amazon CloudFormation 中)的设置。此设置提供以下选项:

始终签署源请求(推荐设置)

我们建议使用此设置,此设置在控制台中名为签署请求(推荐),在 API、CLI 和 Amazon CloudFormation 中名为 always。使用此设置,CloudFront 将始终签署自己发送到 Lambda 函数 URL 的所有请求。

切勿签署源请求

此设置在控制台中名为 Do not sign requests(请勿签署请求),在 API、CLI 和 Amazon CloudFormation 中名为 never。使用此设置关闭所有使用此 OAC 的分配中所有源的 OAC。与从所有使用 OAC 的源和分配中逐一删除 OAC 相比,这可以节省时间和精力。使用此设置,CloudFront 不会签署自己发送到 Lambda 函数 URL 的任何请求。

警告

要使用此设置,Lambda 函数 URL 必须可供公开访问。如果您将此设置用于不可公开访问的 Lambda 函数 URL,则 CloudFront 无法访问该源。Lambda 函数 URL 源向 CloudFront 返回错误,而 CloudFront 会将这些错误传递给查看器。有关更多信息,请参阅《Amazon Lambda 用户指南》中的 Lambda 函数 URL 的安全性和身份验证模型

不要改写查看器(客户端)Authorization 标头

此设置在控制台中名为 Do not override authorization header(请勿改写授权标头),在 API、CLI 和 Amazon CloudFormation 中名为 no-override。如果您希望 CloudFront 仅在相应的查看器请求不包含 Authorization 标头时签署源请求,请使用此设置。使用此设置,CloudFront 会在查看器请求中存在 Authorization 标头时传递该标头,但在查看器请求不包含 Authorization 标头时对源请求进行签名(添加自己的 Authorization 标头)。

警告
  • 如果您使用此设置,则必须为 Lambda 函数 URL 指定签名版本 4 签名,而不是您的 CloudFront 分配的名称或 CNAME。当 CloudFront 将查看器请求中的 Authorization 标头转发到 Lambda 函数 URL 时,Lambda 会针对 Lambda URL 域的主机验证签名。如果签名不是基于 Lambda URL 域,签名中的主机将与 Lambda URL 源使用的主机不匹配。这意味着请求将失败,从而导致签名验证错误。

  • 要传递查看器请求的 Authorization 标头,您必须 针对所有使用与此源访问控制关联的 Lambda 函数 URL 的缓存行为,将 Authorization 标头添加到缓存策略中。

示例模板代码

如果您的 CloudFront 源是与 OAC 关联的 Lambda 函数 URL,则可以使用以下 Python 脚本,通过 POST 方法将文件上传到 Lambda 函数。

此代码假设您对 OAC 进行了配置,将默认签名行为设置为始终签署源请求,并且没有选择请勿覆盖授权标头设置。

通过此配置,OAC 可以使用 Lambda 主机名正确管理 Lambda 的 SigV4 授权。有效载荷使用来自 IAM 身份的 SigV4 进行签名,该身份已获得 Lambda 函数 URL 的授权,指定为 IAM_AUTH 类型。

模板针对来自客户端的 POST 请求,演示如何处理 x-amz-content-sha256 标头中已签名的有效载荷哈希值。具体而言,此模板设计用于管理表单数据有效载荷。该模板可以实现通过 CloudFront 将文件安全上传到 Lambda 函数 URL,并使用 Amazon 身份验证机制确保只有经过授权的请求才能访问 Lambda 函数。

代码包括以下功能:
  • 满足在 x-amz-content-sha256 标头中包含有效载荷哈希的要求

  • 使用 SigV4 身份验证安全地访问 Amazon Web Services 服务

  • 支持使用分段表单数据来上传文件

  • 包括请求异常的错误处理

import boto3 from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest import requests import hashlib import os def calculate_body_hash(body): return hashlib.sha256(body).hexdigest() def sign_request(request, credentials, region, service): sigv4 = SigV4Auth(credentials, service, region) sigv4.add_auth(request) def upload_file_to_lambda(cloudfront_url, file_path, region): # Amazon credentials session = boto3.Session() credentials = session.get_credentials() # Prepare the multipart form-data boundary = "------------------------boundary" # Read file content with open(file_path, 'rb') as file: file_content = file.read() # Get the filename from the path filename = os.path.basename(file_path) # Prepare the multipart body body = ( f'--{boundary}\r\n' f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n' f'Content-Type: application/octet-stream\r\n\r\n' ).encode('utf-8') body += file_content body += f'\r\n--{boundary}--\r\n'.encode('utf-8') # Calculate SHA256 hash of the entire body body_hash = calculate_body_hash(body) # Prepare headers headers = { 'Content-Type': f'multipart/form-data; boundary={boundary}', 'x-amz-content-sha256': body_hash } # Create the request request = AWSRequest( method='POST', url=cloudfront_url, data=body, headers=headers ) # Sign the request sign_request(request, credentials, region, 'lambda') # Get the signed headers signed_headers = dict(request.headers) # Print request headers before sending print("Request Headers:") for header, value in signed_headers.items(): print(f"{header}: {value}") try: # Send POST request with signed headers response = requests.post( cloudfront_url, data=body, headers=signed_headers ) # Print response status and content print(f"\nStatus code: {response.status_code}") print("Response:", response.text) # Print response headers print("\nResponse Headers:") for header, value in response.headers.items(): print(f"{header}: {value}") except requests.exceptions.RequestException as e: print(f"An error occurred: {e}") # Usage cloudfront_url = "https://d111111abcdef8.cloudfront.net" file_path = r"filepath" region = "us-east-1" # example: "us-west-2" upload_file_to_lambda(cloudfront_url, file_path, region)