aws cdk でlambdaにoacを適用しcloud frontからのみ参照出来るようにする

前置き
Next.jsでaws lambda × cloud frontのサーバレス構成にしつつ、aws lambdaを直接参照できないようにしたときの備忘録になります。
実装についてはTypeScriptで記載しています。 環境構築などは出来ているものとして書いているので構築方法などは割愛しています。
ゴール
以下の状態とすることを最終的なゴールとします。
- cloud front経由でaws lambdaの関数URLを実行できる
- aws lambdaの関数URLを直接実行できない
実装
プロジェクトの作成
始めにcdkプロジェクトを作成します。 以降はこのプロジェクト内で作業を行います。
npx cdk init app --language typescript
AWSへデプロイするための準備
プロジェクトを作成した直後の状態だとAWSへの適用ができません。 「/bin/〇〇.ts」で環境情報を設定する必要があるので調整を行います。 ※〇〇はプロジェクトの作成時に指定したフォルダ名とかになっているはず
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { TestStack } from '../lib/test-stack';
const app = new cdk.App();
new TestStack(app, 'TestStack', {
env: {
account: 'aws account id', // AWS認証情報
region: 'ap-northeast-1',
},
});
次にpackage.jsonに以下のscriptを追加します。
{
...,
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"test": "jest",
"cdk": "cdk",
+ "cdk:deploy": "cdk deploy", // これと
+ "cdk:destroy": "cdk destroy" // これ
},
...,
}
上記の実装が終わったら次のコマンドを実行します。
npm run cdk:deploy
コマンド実行後AWSコンソールへログインを行い「CloudFormation」にスタックが作成されていれば準備完了です。

AWS Lambdaを構築するプログラムの実装
「/lib/〇〇-stack.ts」にてAWS環境を構築するためのプログラムを実装するファイルがあるため、Lambdaを構築するための実装を行います。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class TestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Lambda関数の作成
const myLambda = new lambda.Function(this, "HelloWorldFunction", {
runtime: lambda.Runtime.NODEJS_20_X,
handler: "index.handler",
code: lambda.Code.fromInline(`
exports.handler = async function(event) {
return {
statusCode: 200,
body: JSON.stringify('Hello World!'),
};
};
`),
});
// Lambda関数をURLで呼べるように設定
const lambdaUrl = myLambda.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
// Lambda関数URLの出力
new cdk.CfnOutput(this, 'LambdaUrl', {
value: lambdaUrl.url,
description: 'URL of the Lambda function',
exportName: 'LambdaUrl',
});
}
}
一旦この状態で再度デプロイしてAWS Lambdaが動くか確認してみます。 ※コンソールにAWS LambdaのURLが出力されるのでそちらにアクセスします。
"Hello World!" が出力出来たらうまく動いています。
Cloud Front部分の実装とAWS Lambdaの直接実行を無効化
Cloud Frontの構築部分の処理とAWS Lambdaを直リンクで実行できないようにします。 Cloud Front ⇒ AWS Lambda間ではOACを設定し実行できるように調整を行います。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
export class TestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Lambda関数の作成
const myLambda = new lambda.Function(this, "HelloWorldFunction", {
runtime: lambda.Runtime.NODEJS_20_X,
handler: "index.handler",
code: lambda.Code.fromInline(`
exports.handler = async function(event) {
return {
statusCode: 200,
body: JSON.stringify('Hello World!'),
};
};
`),
});
// Lambda関数をURLで呼べるように設定
const lambdaUrl = myLambda.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.AWS_IAM, // NOTEからAWS_IAMに変更
});
// Cloud Frontの作成
const distribution = new cloudfront.Distribution(this, 'MyDistribution', {
defaultBehavior: {
origin: origins.FunctionUrlOrigin.withOriginAccessControl(lambdaUrl),
},
});
// Lambda関数URLの出力
new cdk.CfnOutput(this, 'LambdaUrl', {
value: lambdaUrl.url,
description: 'URL of the Lambda function',
exportName: 'LambdaUrl',
});
// CloudFrontのURLの出力
new cdk.CfnOutput(this, 'CloudFrontUrl', {
value: distribution.distributionDomainName,
description: 'URL of the CloudFront distribution',
exportName: 'CloudFrontUrl',
});
}
}
再度デプロイを行うとCloud FrontのURLとAWS LambdaのURLがログで出力されます。 両方にアクセスしてみて以下の動きになったら成功です。
- Cloud FrontのURLにアクセス⇒"Hello World!"
- AWS LambdaのURLにアクセス⇒{"Message":"Forbidden"}
終わり
以上で「aws cdk でlambdaにoacを適用しcloud frontからのみ参照出来るようにする」実装が出来ました。
この構成はNuxt3やNextをAWS Lambda × Cloud Front(サーバーレス構成)の構成でも使えるので是非参考にしてもらえればと思います。