1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【AWSCDK】CloudFront+S3でOACを使いたい

Posted at

はじめに

久しぶりにAWSCDKを使って、静的アセットのデプロイを行っていたところ、OAI(Origin Access Identify)がLegacy扱いとなっていたため、OAC(Origin Access Control)に変更したときの備忘録です。

OAIを使った時のCDK

最低限必要なプロパティのみ設定しています。

// s3
const s3 = new cdk.aws_s3.Bucket(this, 'bucket-id', {
    websiteIndexDocument: 'index.html',
    publicReadAccess: true
})

// OAI
const originAccessIdentity = new cdk.aws_cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity')

// cloudfront(distribution)
const distribution = new cdk.aws_cloudfront.Distribution(this, 'distribution-id', {
    defaultRootObject: 'index.html',
    defaultBehavior: {
        origin: new cdk.aws_cloudfront_origins.S3Origin(webSiteBucket, { originAccessIdentity })
    },
})

const bucketPolicyStatement = new cdk.aws_iam.PolicyStatement({
    actions: ['s3:GetObject'],
    effect: cdk.aws_iam.Effect.ALLOW,
    principals: [
        new cdk.aws_iam.CanonicalUserPrincipal(originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId)
    ],
    resources: [`${s3.bucketArn}/*`]
})

s3.addToResourcePolicy(bucketPolicyStatement)

new cdk.aws_s3_deployment.BucketDeployment(this, 'CDKReactDeployPractice', {
    sources: [cdk.aws_s3_deployment.Source.asset('<appPath>')],
    destinationBucket: s3,
    distribution,
    distributionPaths: ['/*']
})

OACに変更する

最低限必要なプロパティのみ設定しています。

// s3
const s3 = new cdk.aws_s3.Bucket(this, 'bucket-id', {
    websiteIndexDocument: 'index.html',
    blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL
})

// OAC
const cfnOriginAccessControl = new cdk.aws_cloudfront.CfnOriginAccessControl(this, 'OriginAccessControl', {
    originAccessControlConfig: {
        name: 'OriginAccessControlForContentsBucket',
        originAccessControlOriginType: 's3',
        signingBehavior: 'always',
        signingProtocol: 'sigv4',
        description: 'Access Control',
    },
});

// cloudfront(distribution)
const distribution = new cdk.aws_cloudfront.Distribution(this, 'distribution-id', {
    defaultRootObject: 'index.html',
    defaultBehavior: {
        origin: new cdk.aws_cloudfront_origins.S3Origin(webSiteBucket)
    },
})

const bucketPolicyStatement = new cdk.aws_iam.PolicyStatement({
    actions: ['s3:GetObject'],
    effect: cdk.aws_iam.Effect.ALLOW,
    principals: [
        new cdk.aws_iam.ServicePrincipal('cloudfront.amazonaws.com')
    ],
    resources: [`${s3.bucketArn}/*`]
})
bucketPolicyStatement.addCondition('StringEquals', {
    'AWS:SourceArn': `arn:aws:cloudfront::${cdk.Stack.of(this).account}:distribution/${distribution.distributionId}`
})

s3.addToResourcePolicy(bucketPolicyStatement)

const cfnDistribution = distribution.node.defaultChild as cdk.aws_cloudfront.CfnDistribution
cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.OriginAccessControlId', cfnOriginAccessControl.getAtt('Id'))
cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.DomainName', s3.bucketRegionalDomainName)
cfnDistribution.addOverride('Properties.DistributionConfig.Origins.0.S3OriginConfig.OriginAccessIdentity', "")
cfnDistribution.addPropertyDeletionOverride('DistributionConfig.Origins.0.CustomOriginConfig')

new cdk.aws_s3_deployment.BucketDeployment(this, 'CDKReactDeployPractice', {
    sources: [cdk.aws_s3_deployment.Source.asset('<appPath>')],
    destinationBucket: s3,
    distribution,
    distributionPaths: ['/*']
})

OACの作成

L2 Constructではまだ用意されていないので、L1 Constructを使って定義します。

const cfnOriginAccessControl = new cdk.aws_cloudfront.CfnOriginAccessControl(this, 'OriginAccessControl', {
    originAccessControlConfig: {
        name: 'OriginAccessControlForContentsBucket',
        originAccessControlOriginType: 's3',
        signingBehavior: 'always',
        signingProtocol: 'sigv4',
        description: 'Access Control',
    },
});

principalの設定

S3ポリシーのprincipalはOACではサービスを対象とします。

principals: [
-    new cdk.aws_iam.CanonicalUserPrincipal(originAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId)
+    new cdk.aws_iam.ServicePrincipal('cloudfront.amazonaws.com')
],

AWS:SourceArnの設定

CloudFrontのArnからのアクセスに対応するよう設定を追加します。

+ bucketPolicyStatement.addCondition('StringEquals', {
+     'AWS:SourceArn': `arn:aws:cloudfront::${cdk.Stack.of(this).account}:distribution/${distribution.distributionId}`
+ })

CloudFrontのオリジン設定を変更

OAIを使うときの設定がデフォルトで行われるため、OACに適応したテンプレートに上書きします。

const cfnDistribution = distribution.node.defaultChild as cdk.aws_cloudfront.CfnDistribution
// OACのidを指定する
cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.OriginAccessControlId', cfnOriginAccessControl.getAtt('Id'))
// デフォルトではs3のWebsiteURLが設定されてエラーとなるため、S3のドメイン名を設定する
cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.DomainName', s3.bucketRegionalDomainName)
// OAIの設定は不要のため、空文字で上書き
cfnDistribution.addOverride('Properties.DistributionConfig.Origins.0.S3OriginConfig.OriginAccessIdentity', "")
// OACではCustomOriginConfigは不要
cfnDistribution.addPropertyDeletionOverride('DistributionConfig.Origins.0.CustomOriginConfig')

S3のドメイン名はs3.bucketDomainNameでも良いが、CloudFrontに反映されるまで時間を要するため、理由がない限りはリージョンを含めたドメイン名を設定するのがよいかと。

トラブルシューティング

OACに変更する際に発生したエラーと解消方法を以下に記載しておきますので、参考になれば幸いです。

The origin type and OAC origin type differ

OACで設定するS3Originの値はWebsiteURLではなく、バケットのドメイン名を設定する必要があります。デフォルトではOAIを使った設定でテンプレートが作成されるので、上書きする必要があります。

cfnDistribution.addPropertyOverride('DistributionConfig.Origins.0.DomainName', s3.bucketRegionalDomainName)

Exactly one of CustomOriginConfig and S3OriginConfig must be specified

S3OriginConfigCustomOriginConfigはOACにおいて不要なので、上書き及び削除の対応が必要になります。

cfnDistribution.addOverride('Properties.DistributionConfig.Origins.0.S3OriginConfig.OriginAccessIdentity', "")
cfnDistribution.addPropertyDeletionOverride('DistributionConfig.Origins.0.CustomOriginConfig')

おわりに

OACがL2 Constructで設定できるようになると、この辺の対応は不要になるかなと。
CDKでどのようにテンプレートが作られているか、深く知ることができたので自分にとっては大きな収穫でした。
S3+CloudFrontでOACを使った構成を考えている方の参考になれば幸いです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?