限制對 AWS Lambda 函數 URL 原始伺服器的存取 - Amazon CloudFront

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

限制對 AWS Lambda 函數 URL 原始伺服器的存取

CloudFront 提供原始存取控制 (OAC),以限制對 Lambda 函數 URL 原始伺服器的存取。

建立新的 OAC

完成下列主題中所述的步驟,以在 CloudFront 中設定新的 OAC。

重要

如果您搭配 Lambda 函數 URL 使用 PUTPOST方法,您的使用者必須運算內文的 SHA256,並在將請求傳送至 CloudFront 時,在 x-amz-content-sha256標頭中包含請求內文的承載雜湊值。Lambda 不支援未簽署的承載。

先決條件

在建立和設定 OAC 之前,您必須擁有具有 Lambda 函數 URL 做為原始伺服器的 CloudFront 分佈。若要使用 OAC,您必須指定 AWS_IAM做為 AuthType 參數的值。如需詳細資訊,請參閱使用 Lambda 函數 URL

授予 CloudFront 存取 Lambda 函數 URL 的許可

在您建立 OAC 或在 CloudFront 分佈中設定它之前,請確定 CloudFront 具有存取 Lambda 函數 URL 的許可。在建立 CloudFront 分佈之後,但在您將 OAC 新增至分佈組態中的 Lambda 函數 URL 之前,請執行此操作。

注意

若要更新 Lambda 函數 URL 的 IAM 政策,您必須使用 AWS Command Line Interface (AWS CLI)。目前不支援在 Lambda 主控台中編輯 IAM 政策。

下列 AWS CLI 命令會授予 CloudFront 服務主體 (cloudfront.amazonaws.com) 存取 Lambda 函數 URL 的權限。政策中的 Condition元素只有在請求代表包含 Lambda 函數 URL 的 CloudFront 分佈時, CloudFront 才允許 CloudFront 存取 Lambda。這是您要新增 OAC 的 Lambda 函數 URL 原始伺服器分佈。

範例 : AWS CLI 命令更新政策,以允許啟用 OAC 之 CloudFront 分佈的唯讀存取

下列 AWS 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 命令,然後從命令列終端機輸入此命令。如需詳細資訊,請參閱《 AWS Lambda 開發人員指南》中的授予函數對 的存取權 AWS 服務

建立 OAC

若要建立 OAC,您可以使用 AWS Management Console AWS CloudFormation、 AWS CLI、 或 CloudFront API。

Console
建立 OAC
  1. 登入 AWS Management Console ,並在 開啟 CloudFront 主控台https://console.aws.amazon.com/cloudfront/v4/home

  2. 於左側導覽窗格中,選擇 Origin access (原始存取)。

  3. 選擇 Create control setting (建立控制設定)。

  4. 建立新的 OAC 表單上,執行下列動作:

    1. 輸入名稱和 (選擇性) OAC 的描述

    2. 對於簽署行為,我們建議您保留預設設定 (簽署請求 (建議))。如需詳細資訊,請參閱原始存取控制的進階設定

  5. 針對原始伺服器類型,選擇 Lambda

  6. 選擇建立

    提示

    建立 OAC 之後,請記下名稱。您需要於下列程序中進行使用。

將原始存取控制新增至分佈中的 Lambda 函數 URL
  1. https://console.aws.amazon.com/cloudfront/v4/home 中開啟 CloudFront 主控台。

  2. 選擇具有您要新增 OAC 之 Lambda 函數 URL 的分佈,然後選擇原始伺服器索引標籤。

  3. 選取您要新增 OAC 的 Lambda 函數 URL,然後選擇編輯

  4. 在原始伺服器的Protocol (通訊協定) 選取 HTTPS only (僅限 HTTPS)。

  5. 原始存取控制下拉式清單中,選擇您要使用的 OAC 名稱。

  6. 選擇儲存變更

分佈開始部署至所有 CloudFront 邊緣節點。當節點收到新組態時,它會簽署其傳送至 Lambda 函數 URL 的所有請求。

CloudFormation

若要使用 建立 OAC AWS CloudFormation,請使用 AWS::CloudFront::OriginAccessControl 資源類型。下列範例顯示用於建立 OAC 的 YAML 格式 AWS CloudFormation 範本語法。

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

如需詳細資訊,請參閱《AWS CloudFormation 使用者指南》中的 AWS::CloudFront::OriginAccessControl

CLI

若要使用 AWS Command Line Interface (AWS 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. 使用下列命令來儲存您想要新增之 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 呼叫中指定欄位的詳細資訊,請參閱 AWS SDK 或其他 API 用戶端的 API 參考文件。

建立 OAC 之後,您可以使用下列其中一個 API 呼叫,將其連接至分佈中的 Lambda 函數 URL:

對於這兩個 API 呼叫,請在原始伺服器內的 OriginAccessControlId 欄位中提供 OAC ID。如需您在這些 API 呼叫中指定之其他欄位的詳細資訊,請參閱 和 AWS SDK 或其他 API 用戶端的 API 參考文件。

原始存取控制的進階設定

CloudFront OAC 功能包含進階設定,僅適用於特定使用案例。除非您對進階設定有特定需求,否則請使用建議的設定。

OAC 包含名為簽署行為 (在主控台中) 或 SigningBehavior(在 API、CLI 和 中) 的設定 AWS CloudFormation。此設定提供下列選項:

永遠簽署原始請求 (建議設定)

我們建議使用此設定,於主控台中名為Sign requests (recommended) (簽署請求 (建議使用)),或於 API、CLI 和 AWS CloudFormation中的 always。使用此設定,CloudFront 一律會簽署傳送至 Lambda 函數 URL 的所有請求。

絕不簽署原始伺服器請求

此設定於主控台中命名為 Do not sign requests (請勿簽署請求),或 API、CLI 和 AWS CloudFormation中的 never。使用此設定可關閉使用此 OAC 的所有分佈中所有原始伺服器的 OAC。相較於從所有使用它的原始伺服器和分佈逐一移除 OAC,這可以節省時間和精力。使用此設定,CloudFront 不會簽署任何傳送到 Lambda 函數 URL 的請求。

警告

若要使用此設定,Lambda 函數 URL 必須可公開存取。如果您將此設定與不可公開存取的 Lambda 函數 URL 搭配使用,CloudFront 就無法存取原始伺服器。Lambda 函數 URL 會將錯誤傳回 CloudFront,而 CloudFront 會將這些錯誤傳遞給檢視器。如需詳細資訊,請參閱AWS Lambda 《 使用者指南》中的 Lambda 函數 URLs 的安全與驗證模型

請勿覆寫檢視器 (用戶端) Authorization 標題

此設定於主控台中命名為 Do not override authorization header (請勿覆寫授權標頭),或於 API、CLI 和 AWS CloudFormation中的 no-override。如果您想要 CloudFront 僅於對應的檢視器請求不包含 Authorization 標題時簽署原始伺服器請求,請使用此設定。利用此設定,當檢視器請求存在時,CloudFront 會傳遞來自檢視器請求的 Authorization 標題,但在檢視器請求不包含 Authorization 標題時對原始伺服器請求進行簽名 (新增其自己的 Authorization 標題)。

警告
  • 如果您使用此設定,則必須指定 Lambda 函數 URL 的 Signature 第 4 版簽署,而不是 CloudFront 分佈的名稱或 CNAME。當 CloudFront 將Authorization標頭從檢視器請求轉送至 Lambda 函數 URL 時,Lambda 會根據 Lambda URL 網域的主機驗證簽章。如果簽章不是以 Lambda URL 網域為基礎,則簽章中的主機與 Lambda URL 原始伺服器所使用的主機不相符。這表示請求將會失敗,導致簽章驗證錯誤。

  • 若要從檢視器請求傳遞 Authorization標頭,您必須針對所有使用與此原始存取控制相關聯 Lambda 函數 URLs快取行為,將 Authorization標頭新增至快取政策

範本程式碼範例

如果您的 CloudFront 原始伺服器是與 OAC 相關聯的 Lambda 函數 URL,您可以使用下列 Python 指令碼,透過 POST方法將檔案上傳至 Lambda 函數。

此程式碼假設您已將 OAC 預設簽署行為設定為永遠簽署原始伺服器請求,且您未選取請勿覆寫授權標頭設定。

此組態允許 OAC 使用 Lambda 主機名稱,以 Lambda 正確管理 SigV4 授權。承載是從 Lambda 函數 URL 授權的 IAM 身分使用 SigV4 簽署,該身分指定為 IAM_AUTH類型。

範本示範如何處理來自用戶端POST請求的x-amz-content-sha256標頭中簽章的承載雜湊值。具體而言,此範本旨在管理表單資料承載。範本可透過 CloudFront 將安全檔案上傳至 Lambda 函數 URL,並使用 AWS 身分驗證機制來確保只有授權的請求才能存取 Lambda 函數。

此程式碼包含下列功能:
  • 符合在 x-amz-content-sha256標頭中包含承載雜湊的需求

  • 使用 SigV4 身分驗證進行安全 AWS 服務 存取

  • 使用分段表單資料支援檔案上傳

  • 包含請求例外狀況的錯誤處理

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): # AWS 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)