AWS CDKでCloudFrontのLambda Function URLsへのOACを設定してみた
AWS CDKでもLambda Function URLsのOACを設定したい
こんにちは、のんピ(@non____97)です。
皆さんはAWS CDKでもLambda Function URLsへのOACを設定したいなと思ったことはありますか? 私はあります。
先日、CloudFrontがLambda Function URLsへのOACをサポートしました。これによりLambda Function URLsへのアクセス制限がやりやすくなりました。詳細は以下記事をご覧ください。
Lambda Function URLsをマネジメントコンソールから設定する際はCloudFront側でOACの設定をした後、表示されたAWS CLIのスクリプトを実行する必要があります。地味に手間です。
ふと、CloudFormationでOACのプロパティを眺めていると、Lambda Function URLsをもうサポートしていました。早いですね。
OriginAccessControlOriginType
The type of origin that this origin access control is for.
Required: Yes
Type: String
Pattern: ^(s3|mediastore|lambda|mediapackagev2)$
Update requires: No interruption
AWS::CloudFront::OriginAccessControl OriginAccessControlConfig - AWS CloudFormation
CloudFormationで設定できるということはAWS CDKでも設定できるということです。
実際にやってみます。
やってみた
AWS CDKのコードの紹介
AWS CDKのコードは以下リポジトリに保存しています。
OACの設定をするにあたって、やっていることは以下の2つです。
- L1 ConstructでOACを作成する
- CloudFront distributionのL2 Constructに対してEscape hatchesで、OACを指定する
- Lambda関数にCloudFront distributionからの
lambda:InvokeFunctionUrl
を許可する
該当のコードは以下です。
// Lambda Function const wasshoiLambda = new cdk.aws_lambda_nodejs.NodejsFunction( this, "WasshoiLambda", { entry: path.join(__dirname, "../src/lambda/wasshoi/index.ts"), runtime: cdk.aws_lambda.Runtime.NODEJS_20_X, bundling: { minify: true, tsconfig: path.join(__dirname, "../src/lambda/tsconfig.json"), format: cdk.aws_lambda_nodejs.OutputFormat.ESM, }, architecture: cdk.aws_lambda.Architecture.ARM_64, loggingFormat: cdk.aws_lambda.LoggingFormat.JSON, } ); // CloudFront Distribution this.distribution = new cdk.aws_cloudfront.Distribution(this, "Default", { defaultBehavior: { origin: new cdk.aws_cloudfront_origins.FunctionUrlOrigin( wasshoiLambda.addFunctionUrl({ authType: cdk.aws_lambda.FunctionUrlAuthType.AWS_IAM, }) ), allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD, cachedMethods: cdk.aws_cloudfront.CachedMethods.CACHE_GET_HEAD, cachePolicy: new cdk.aws_cloudfront.CachePolicy(this, "WasshoiCache", { minTtl: cdk.Duration.seconds(1), maxTtl: cdk.Duration.seconds(31536000), defaultTtl: cdk.Duration.seconds(86400), enableAcceptEncodingBrotli: true, enableAcceptEncodingGzip: true, queryStringBehavior: cdk.aws_cloudfront.CacheQueryStringBehavior.allowList("wasshoi"), }), viewerProtocolPolicy: cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, responseHeadersPolicy: cdk.aws_cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS, }, httpVersion: cdk.aws_cloudfront.HttpVersion.HTTP2_AND_3, priceClass: cdk.aws_cloudfront.PriceClass.PRICE_CLASS_200, domainNames: props.domainName ? [props.domainName] : undefined, certificate: props.domainName ? props.certificateConstruct?.certificate : undefined, logBucket: props.cloudFrontAccessLogBucketConstruct?.bucket, logFilePrefix: props.logFilePrefix, }); // OAC const cfnOriginAccessControl = new cdk.aws_cloudfront.CfnOriginAccessControl( this, "OriginAccessControl", { originAccessControlConfig: { name: "Origin Access Control for Lambda Functions URL", originAccessControlOriginType: "lambda", signingBehavior: "always", signingProtocol: "sigv4", }, } ); const cfnDistribution = this.distribution.node .defaultChild as cdk.aws_cloudfront.CfnDistribution; // Set OAC cfnDistribution.addPropertyOverride( "DistributionConfig.Origins.0.OriginAccessControlId", cfnOriginAccessControl.attrId ); // Add permission Lambda Function URLs wasshoiLambda.addPermission("AllowCloudFrontServicePrincipal", { principal: new cdk.aws_iam.ServicePrincipal("cloudfront.amazonaws.com"), action: "lambda:InvokeFunctionUrl", sourceArn: `arn:aws:cloudfront::${ cdk.Stack.of(this).account }:distribution/${this.distribution.distributionId}`, });
S3バケットのOAC設定をする場合は、問答無用で設定されるOAIを剥がすための記述がいくつか必要でしたが、Lambda Function URLsの場合はシンプルです。
Lambda関数で実行する処理はwasshoi
というクエリで指定した値の数分だけわっしょい!!
を出力するものです。
import { Callback, LambdaFunctionURLEvent, Context } from "aws-lambda"; export const handler = async ( event: LambdaFunctionURLEvent, context: Context, callback: Callback ) => { const wasshoi = Math.round(Number(event.queryStringParameters?.wasshoi || 0)); const message = !Number.isInteger(wasshoi) || wasshoi <= 0 ? "わっしょい! したくないのですか ... ?" : wasshoi >= 10 ? "お静かに" : Array(wasshoi).fill("わっしょい!!").join(" "); return { statusCode: 200, headers: { "Content-Type": "text/plain" }, body: JSON.stringify({ message, }), }; };
デプロイ
実際にデプロイして試してみます。
設定は以下のようにしています。カスタムドメインで試してみたかったのでCloudFrontにlambda-url.non-97.net
という名前でアクセスできるようにしています。
export const lambdaOacStackProperty: LambdaOacStackProperty = { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, }, props: { hostedZone: { zoneName: "lambda-url.non-97.net", }, certificate: { certificateDomainName: "lambda-url.non-97.net", }, contentsDelivery: { domainName: "lambda-url.non-97.net", }, allowDeleteBucketAndObjects: true, cloudFrontAccessLog: { enableAccessLog: true, lifecycleRules: [{ expirationDays: 365 }], }, logAnalytics: { createWorkGroup: true, enableLogAnalytics: ["cloudFrontAccessLog"], }, }, };
大元のAWS CDKのコードは以下記事で紹介したものです。
動作確認
動作確認をします。
$ curl "https://lambda-url.non-97.net/?wasshoi=1" {"message":"わっしょい!!"} $ curl "https://lambda-url.non-97.net/?wasshoi=2" {"message":"わっしょい!! わっしょい!!"} $ curl "https://lambda-url.non-97.net/" {"message":"わっしょい! したくないのですか ... ?"} $ curl "https://lambda-url.non-97.net/?wasshoi=-1" {"message":"わっしょい! したくないのですか ... ?"} $ curl "https://lambda-url.non-97.net/?wasshoi=a" {"message":"わっしょい! したくないのですか ... ?"} $ curl "https://lambda-url.non-97.net/?wasshoi=8" {"message":"わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!!"} $ curl "https://lambda-url.non-97.net/?wasshoi=10" {"message":"お静かに"} $ curl "https://lambda-url.non-97.net/?wasshoi=5.1" {"message":"わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!!"} $ curl "https://lambda-url.non-97.net/?wasshoi=5.5" {"message":"わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!! わっしょい!!"}
wasshoi
で指定した値の数分だけ、わっしょいしていますね。
CloudFormationとAWS CDKでも設定できます
AWS CDKでCloudFrontとLambda Function URLsのOACを設定してみました。
API Gatewayがtoo muchである場合や、API Gatewayの統合リクエストタイムアウト29秒の制約が気になる場合に役立ちそうですね。
CloudFrontがLambda Function URLsへのOACをサポートしたことによるユースケースはwatany(@_watany)さんの以下記事も非常に参考になります。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!