AWS Lambda 関数 URL オリジンへのアクセスを制限する - Amazon CloudFront

AWS Lambda 関数 URL オリジンへのアクセスを制限する

CloudFront には、Lambda 関数 URL オリジンへのアクセスを制限するためのオリジンアクセスコントロール (OAC) が用意されています。

新しい OAC を作成する

CloudFront で新しい OAC を設定するには、以下のトピックに示す手順を実行します。

重要

Lambda 関数 URL で PUT メソッドまたは POST メソッドを使用する場合、ユーザーはリクエストを CloudFront に送信するときにリクエスト本文の SHA256 を計算し、本文のペイロードハッシュ値を x-amz-content-sha256 ヘッダーに含める必要があります。Lambda は、署名されていないペイロードをサポートしていません。

前提条件

OAC を作成して設定する前に、Lambda 関数 URL をオリジンとして持つ CloudFront ディストリビューションが必要です。OAC を使用するには、AuthType パラメータの値として AWS_IAM を指定する必要があります。詳細については、「Lambda 関数 URL を使用する」を参照してください。

Lambda 関数 URL へのアクセス許可を CloudFront に付与する

CloudFront ディストリビューションで OAC を作成または設定する前に、Lambda 関数 URL へのアクセス許可が CloudFront にあることを確認します。これは、CloudFront ディストリビューションを作成した後で、ディストリビューション設定で Lambda 関数 URL に OAC を追加する前に行います。

注記

Lambda 関数 URL の IAM ポリシーを更新するには、AWS Command Line Interface (AWS CLI) を使用する必要があります。現時点では、Lambda コンソールでの IAM ポリシーの編集はサポートされていません。

次の AWS CLI コマンドは、CloudFront サービスプリンシパル (cloudfront.amazonaws.com) に Lambda 関数 URL へのアクセスを許可します。ポリシーの Condition 要素は、Lambda 関数 URL を含む CloudFront ディストリビューションを対象とするリクエストに限り、Lambda へのアクセス許可を CloudFront に付与します。これは、OAC を追加する Lambda 関数 URL オリジンを持つディストリビューションです。

例 : OAC が有効になっている CloudFront ディストリビューションへの読み取り専用アクセスを許可するようにポリシーを更新する AWS CLI コマンド

次の 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にサインインし、https://console.aws.amazon.com/cloudfront/v4/home で CloudFront コンソールを開きます。

  2. ナビゲーションペインで、[オリジンアクセス] を選択します。

  3. [コントロール設定を作成] を選択します。

  4. [新しい OAC を作成] フォームで、次の操作を実行します。

    1. OAC の名前とオプションの説明を入力します。

    2. [署名動作] は、デフォルト設定の [署名リクエスト (推奨)] のままにすることをお勧めします。詳細については、「オリジンアクセスコントロールの詳細設定」を参照してください。

  5. [オリジンタイプ] で、[Lambda] を選択します。

  6. [作成] を選択します。

    ヒント

    OAC を作成したら、名前を書き留めておきます。次の手順では、この操作を行う必要があります。

ディストリビューションの Lambda 関数 URL にオリジンアクセスコントロールを追加するには
  1. CloudFront コンソール (https://console.aws.amazon.com/cloudfront/v4/home) を開きます。

  2. OAC を追加する先の Lambda 関数 URL があるディストリビューションを選択し、[オリジン] タブを選択します。

  3. OAC を追加する先の Lambda 関数 URL を選択し、[編集] を選択します。

  4. オリジンの [Protocol] (プロトコル) として [HTTPS only] (HTTPS のみ) を選択します。

  5. [オリジンアクセスコントロール] ドロップダウンから、使用する OAC 名を選択します。

  6. [Save changes] (変更の保存) をクリックします。

ディストリビューションは、すべての CloudFront エッジロケーションへのデプロイを開始します。エッジロケーションは、新しい設定を受け取ると、Lambda 関数 URL に送信するすべてのリクエストに署名します。

CloudFormation

AWS CloudFormation で OAC を作成するには、AWS::CloudFront::OriginAccessControl リソースタイプを使用します。次の例は、OAC を作成するための AWS CloudFormation テンプレート構文を YAML 形式で示しています。

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 の名前、説明 (オプション) を追加し、SigningBehavioralways に変更します。その後、ファイルを保存します。

    その他の OAC の設定については、「オリジンアクセスコントロールの詳細設定」を参照してください。

  3. 次のコマンドを使用して、origin-access-control.yaml ファイルの入力パラメータを使用し、オリジンアクセスコントロールを作成します。

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

    コマンド出力の Id 値を書き留めます。これは、CloudFront ディストリビューションの Lambda 関数 URL に OAC を追加するために必要です。

既存のディストリビューションの Lambda 関数 URL に OAC をアタッチするには (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 オブジェクトで、OriginAccessControlId という名前のフィールドに OAC の ID を追加します。

    • 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 に OAC をアタッチできます。

  • 既存のディストリビューションにアタッチするには、UpdateDistribution を使用します。

  • 新しいディストリビューションにアタッチするには、CreateDistribution を使用します。

これらの 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 に送信するすべてのリクエストに常に署名します。

オリジンリクエストに署名しない

この設定は、コンソールでは [リクエストに署名しない]、または API、CLI、およびAWS CloudFormation では never です。この設定を使用して、この OAC を使用するすべてのディストリビューションですべてのオリジンの OAC をオフにします。OAC を使用するすべてのオリジンとディストリビューションから OAC を 1 つずつ削除する場合と比べて、この設定で時間と労力を節約できます。この設定の場合、CloudFront は Lambda 関数 URL に送信するいずれのリクエストにも署名しません。

警告

この設定を使用するには、Lambda 関数 URL がパブリックにアクセス可能である必要があります。この設定を、パブリックにアクセスできない Lambda 関数 URL で使用すると、CloudFront はオリジンにアクセスできません。Lambda 関数 URL は、CloudFront にエラーを返し、CloudFront はこれらのエラーをビューワーに渡します。詳細については、「AWS Lambda ユーザーガイド」の「Lambda 関数 URL におけるセキュリティと認証モデル」を参照してください。

ビューワー (クライアント) の Authorization ヘッダーを上書きしない

この設定は、コンソールでは [認可ヘッダーを上書きしない] で、API、CLI、および AWS CloudFormation では no-override です。この設定は、対応するビューワーリクエストに Authorization ヘッダーに含まれていない場合にのみ、CloudFront がオリジンリクエストに署名するよう指定する場合に使用します。この設定では、ビューワーリクエストが存在するが、ビューワーリクエストに Authorization ヘッダーが含まれていないときにオリジンリクエストに署名する (独自の Authorization ヘッダーを追加) ときに、CloudFront はビューワーリクエストから Authorization ヘッダーを渡します。

警告
  • この設定を使用する場合は、CloudFront ディストリビューションの名前または CNAME の代わりに Lambda 関数 URL の署名バージョン 4 の署名を指定する必要があります。CloudFront がビューワーリクエストから Lambda 関数 URL に Authorization ヘッダーを転送すると、Lambda は Lambda URL ドメインのホストに対して署名を検証します。署名が Lambda URL ドメインに基づいていない場合、署名のホストは Lambda URL オリジンで使用するホストと一致しません。つまり、リクエストは失敗し、署名検証エラーが発生します。

  • ビューワーリクエストから Authorization ヘッダーを渡すには、このオリジンアクセスコントロールに関連付けられた Lambda 関数 URL を使用するすべてのキャッシュ動作で、Authorization ヘッダーをキャッシュポリシーに追加する必要があります。

テンプレートのコード例

CloudFront オリジンが OAC に関連付けられている Lambda 関数 URL である場合は、次の Python スクリプトを使用して、POST メソッドで Lambda 関数にファイルをアップロードできます。

このコードでは、OAC の設定時にデフォルトの署名動作を [オリジンリクエストに常に署名する] に設定していること、および [認証ヘッダーを上書きしない] 設定を選択していないことを前提としています。

この設定により、OAC は Lambda ホスト名を使用して Lambda で SigV4 認証を正しく管理できます。ペイロードに署名するには、IAM_AUTH タイプとして指定された、Lambda 関数 URL で認証された IAM アイデンティティからの SigV4 を使用します。

テンプレートは、クライアント側からの 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)