Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions cdk/lib/constructs/cf-lambda-furl-service/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Construct } from 'constructs';
import { Duration } from 'aws-cdk-lib';
import { FunctionUrlAuthType, Function, InvokeMode } from 'aws-cdk-lib/aws-lambda';
import { Aws, Duration } from 'aws-cdk-lib';
import { FunctionUrlAuthType, Function, InvokeMode, CfnPermission } from 'aws-cdk-lib/aws-lambda';
import {
AllowedMethods,
CacheCookieBehavior,
Expand Down Expand Up @@ -120,6 +120,18 @@ export class CloudFrontLambdaFunctionUrlService extends Construct {
minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021,
});

// Starting October 2025, new function URLs require both lambda:InvokeFunctionUrl
// and lambda:InvokeFunction permissions for CloudFront OAC.
// CDK's FunctionUrlOrigin.withOriginAccessControl only adds lambda:InvokeFunctionUrl,
// so we explicitly add lambda:InvokeFunction here.
// See: https://docs.aws.amazon.com/lambda/latest/dg/urls-auth.html
new CfnPermission(this, 'InvokeFunctionPermission', {
action: 'lambda:InvokeFunction',
functionName: handler.functionArn,
principal: 'cloudfront.amazonaws.com',
sourceArn: `arn:${Aws.PARTITION}:cloudfront::${Aws.ACCOUNT_ID}:distribution/${distribution.distributionId}`,
});

if (hostedZone) {
new ARecord(this, 'Record', {
zone: hostedZone,
Expand Down
21 changes: 20 additions & 1 deletion cdk/lib/main-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Construct } from 'constructs';
import { AsyncJob } from './constructs/async-job';
import { Auth } from './constructs/auth/';
import { Database } from './constructs/database';
import { InstanceClass, InstanceSize, InstanceType, NatProvider, Vpc } from 'aws-cdk-lib/aws-ec2';
import { InstanceClass, InstanceSize, InstanceType, NatProvider, UserData, Vpc } from 'aws-cdk-lib/aws-ec2';
import { HostedZone } from 'aws-cdk-lib/aws-route53';
import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
import { Webapp } from './constructs/webapp';
Expand Down Expand Up @@ -56,12 +56,31 @@ export class MainStack extends Stack {
autoDeleteObjects: true,
});

// Custom user data for NAT instance to support Amazon Linux 2023.
// CDK's default user data uses `route` command which requires net-tools package,
// but AL2023 doesn't have net-tools pre-installed. We use `ip route` instead.
// Retry yum install to handle RPM lock conflicts during boot.
const natUserData = UserData.forLinux();
natUserData.addCommands(
// Retry yum install up to 5 times with 10 second intervals
'for i in {1..5}; do yum install iptables-services -y && break || sleep 10; done',
'systemctl enable iptables',
'systemctl start iptables',
'echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf',
'sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf',
"IFACE=$(ip route show default | awk '{print $5}')",
'/sbin/iptables -t nat -A POSTROUTING -o $IFACE -j MASQUERADE',
'/sbin/iptables -F FORWARD',
'service iptables save',
);

const vpc = new Vpc(this, `Vpc`, {
...(useNatInstance
? {
natGatewayProvider: NatProvider.instanceV2({
instanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.NANO),
associatePublicIpAddress: true,
userData: natUserData,
}),
natGateways: 1,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2712,14 +2712,15 @@ exports.handler = async function (event, context) {
],
"UserData": {
"Fn::Base64": "#!/bin/bash
yum install iptables-services -y
for i in {1..5}; do yum install iptables-services -y && break || sleep 10; done
systemctl enable iptables
systemctl start iptables
echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf
sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
sudo /sbin/iptables -t nat -A POSTROUTING -o $(route | awk '/^default/{print $NF}') -j MASQUERADE
sudo /sbin/iptables -F FORWARD
sudo service iptables save",
sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
IFACE=$(ip route show default | awk '{print $5}')
/sbin/iptables -t nat -A POSTROUTING -o $IFACE -j MASQUERADE
/sbin/iptables -F FORWARD
service iptables save",
},
},
"Type": "AWS::EC2::Instance",
Expand Down Expand Up @@ -3684,6 +3685,38 @@ sudo service iptables save",
},
"Type": "AWS::IAM::Policy",
},
"WebappInvokeFunctionPermission8F3F2610": {
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"WebappHandler8DD158A3",
"Arn",
],
},
"Principal": "cloudfront.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition",
},
":cloudfront::",
{
"Ref": "AWS::AccountId",
},
":distribution/",
{
"Ref": "Webapp107041BD",
},
],
],
},
},
"Type": "AWS::Lambda::Permission",
},
"WebappMigrationRunnerAC67C012": {
"DependsOn": [
"VpcPrivateSubnet1DefaultRouteBE02A9ED",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2544,14 +2544,15 @@ exports[`Snapshot test 2`] = `
],
"UserData": {
"Fn::Base64": "#!/bin/bash
yum install iptables-services -y
for i in {1..5}; do yum install iptables-services -y && break || sleep 10; done
systemctl enable iptables
systemctl start iptables
echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/custom-ip-forwarding.conf
sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
sudo /sbin/iptables -t nat -A POSTROUTING -o $(route | awk '/^default/{print $NF}') -j MASQUERADE
sudo /sbin/iptables -F FORWARD
sudo service iptables save",
sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
IFACE=$(ip route show default | awk '{print $5}')
/sbin/iptables -t nat -A POSTROUTING -o $IFACE -j MASQUERADE
/sbin/iptables -F FORWARD
service iptables save",
},
},
"Type": "AWS::EC2::Instance",
Expand Down Expand Up @@ -3490,6 +3491,38 @@ sudo service iptables save",
},
"Type": "AWS::IAM::Policy",
},
"WebappInvokeFunctionPermission8F3F2610": {
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"WebappHandler8DD158A3",
"Arn",
],
},
"Principal": "cloudfront.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition",
},
":cloudfront::",
{
"Ref": "AWS::AccountId",
},
":distribution/",
{
"Ref": "Webapp107041BD",
},
],
],
},
},
"Type": "AWS::Lambda::Permission",
},
"WebappMigrationRunnerAC67C012": {
"DependsOn": [
"VpcPrivateSubnet1DefaultRouteBE02A9ED",
Expand Down