はじめに
AWS CDKでリソース作ってますか??
最近、AWS CDKを見ること、触ることが多く、少しずつ仲良くなってきました。そんな中、AWS CDKでAmazon CloudFront オリジンアクセスコントロールをどうやってやるのか、困ったのでご紹介です。
アーキテクチャ
以下の通り、利用者からアクセスされ、オリジンサーバとなるS3から表示用HTMLを利用者に返却するアーキテクチャです。よくあるやつですね。
上記のCloudFrontとS3のアクセス部分でオリジンアクセスコントロール(OAC)を利用します。OACを利用することでS3自体をパブリックにする必要がなくなり、S3への直接的な意図しないアクセスをできない様にします。
下記の赤枠部分です。
従来は、オリジンアクセスアイデンティティ(OAI)を利用していましたが、OACが登場してからはOACが推奨されています。
OACは、S3に対して安全にアクセスする形となり、OAIよりよりセキュアになっています。(OAC自体がよりセキュアでいつくかのオプション設定も可能になっています。)
以下のブログで詳細が紹介されています。
デベロッパーガイドはこちらです。
AWS CDKでのOAC
では、さっそくやってみよう、とAWS CDKのリファレンスを覗きます。
(最初、みんなどうやって作ってるのかな?やっぱリファレンス?)
aws-cdk-lib.aws_cloudfrontです。
そして、眺めると「class OriginAccessIdentity」しかないと、気づきます。。。(2023/3/14時点)
で、いろいろ調べるとここに行きつきました。まだ、L2ではサポートされていないようです。
ということで、L1で頑張って作ることになりました。
AWS CDKでの実装(Typescript)
作ったのが以下です。
各リソースのプロパティは省略しており、デフォルト値が利用される形になっています。利用される場合は、ご注意ください。
import { aws_cloudfront, aws_cloudfront_origins, aws_iam, aws_s3, Stack, StackProps } from 'aws-cdk-lib';
import { BlockPublicAccess } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
interface SubStackProps extends StackProps {
accountId: string;
}
export class FrontendStack extends Stack {
public readonly contentsBucket: aws_s3.IBucket;
public readonly distribution: aws_cloudfront.IDistribution;
constructor(scope: Construct, id: string, props: SubStackProps) {
super(scope, id, props);
// S3
this.contentsBucket = new aws_s3.Bucket(this, 'ContentsBucket', {
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
});
// OAC
// https://github.com/aws/aws-cdk/issues/21771
const cfnOriginAccessControl = new aws_cloudfront.CfnOriginAccessControl(this, 'OriginAccessControl', {
originAccessControlConfig: {
name: 'OriginAccessControlForContentsBucket',
originAccessControlOriginType: 's3',
signingBehavior: 'always',
signingProtocol: 'sigv4',
description: 'Access Control',
},
});
// CloudFront
this.distribution = new aws_cloudfront.Distribution(this, 'Distribution', {
comment: 'distribution.',
defaultBehavior: {
origin: new aws_cloudfront_origins.S3Origin(this.contentsBucket),
allowedMethods: aws_cloudfront.AllowedMethods.ALLOW_ALL,
},
defaultRootObject: 'index.html',
httpVersion: aws_cloudfront.HttpVersion.HTTP2_AND_3,
});
const cfnDistribution = this.distribution.node.defaultChild as aws_cloudfront.CfnDistribution;
// OAI削除(勝手に設定されるため)
cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.S3OriginConfig.OriginAccessIdentity', '');
// OAC設定
cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.OriginAccessControlId', cfnOriginAccessControl.attrId);
// S3 - BucketPolicy
const contentsBucketPolicyStatement = new aws_iam.PolicyStatement({
actions: ['s3:GetObject'],
effect: aws_iam.Effect.ALLOW,
principals: [
new aws_iam.ServicePrincipal('cloudfront.amazonaws.com'),
],
resources: [`${this.contentsBucket.bucketArn}/*`],
});
contentsBucketPolicyStatement.addCondition('StringEquals', {
'AWS:SourceArn': `arn:aws:cloudfront::${props.accountId}:distribution/${this.distribution.distributionId}`
})
this.contentsBucket.addToResourcePolicy(contentsBucketPolicyStatement);
}
}
<注意点>
- aws_cloudfront.DistributionがデフォルトでOAIを設定するようになっていますので、(無理矢理?)OACを利用するようになっています。
- S3のバケットポリシーにOAIの設定が入ってしまいます。削除する部分まで入れていません。
作成されたリソース(OAC)
実際にデプロイすると、CloudFront、S3、OACが作成され、CloudFront distribution設定内にあるS3のオリジン設定を見ると以下の様に構成されます。OACとなっていますね。
まとめ
今回はAWS CDKでCloudFront + S3 + OACを構築しました。やり方わかると意外とあっさりしていました。CloudFormationは対応できているけど、AWS CDKのL2はまだ対応されていない、というものも多々ありますので、参考になれば幸いです。