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 ディストリビューション (
) に Lambda E1PDK09ESKHJWT
へのアクセスを許可します。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-nameFUNCTION_URL_NAME
注記
作成したディストリビューションに Lambda 関数 URL へのアクセス許可がない場合は、CloudFront コンソールで [CLI コマンドをコピー] を選択し、このコマンドをコマンドラインターミナルから入力できます。詳細については、「AWS Lambda デベロッパーガイド」の「AWS のサービスへのアクセス権を関数に付与する」を参照してください。
OAC を作成する
OAC を作成するには、AWS Management Console、AWS CloudFormation、AWS CLI、または CloudFront 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)