内容
下記AWS WAF、CloudFront、S3の箇所をCDKで構築します。
- 証明書はACMで払い出し済みのものを使用
- ホストゾーンはRoute53でドメイン取得時に作成されたものを使用
- 環境作成後にCloudFrontのAレコード(Alias)をホストゾーンに追加
WAFスタック
準備
mkdir waf
cd waf
cdk init --language typescript
cdk bootstrap aws://ACCOUNT-NUMBER/us-east-1
npm install @aws-cdk/aws-wafv2
npx tsc -w
作成
- デフォルトブロック、日本からのアクセスを許可するルールを追加
bin/waf.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { WafStack } from '../lib/waf-stack';
const app = new cdk.App();
new WafStack(app, 'WafStack', {
env: {
region: "us-east-1",
},
});
lib/waf-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';
export class WafStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const waf_scope = 'CLOUDFRONT';
const cfnWebACL = new wafv2.CfnWebACL(this, 'WebACL', {
defaultAction: {
block: {
}
},
name: 'waf-web-acl',
scope: waf_scope,
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: 'waf-web-acl',
sampledRequestsEnabled: true,
},
rules: [
{
priority: 0,
name: 'country-rule-set',
action: { allow: {}},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
sampledRequestsEnabled: true,
metricName: 'country-rule-set',
},
statement: {
geoMatchStatement: {
countryCodes: ['JP'],
},
},
},
]
})
};
};
CloudFrontスタック
準備
mkdir cloudfront
cd cloudfront
cdk init --language typescript
cdk bootstrap aws://ACCOUNT-NUMBER/ap-northeast-1
npm install @aws-cdk/aws-s3
npm install @aws-cdk/aws-cloudfront
npm install @aws-cdk/aws-cloudfront-origins
npm install @aws-cdk/aws-iam
npm install @aws-cdk/aws-route53
npm install @aws-cdk/aws-certificatemanager
npx tsc -w
作成
- コンテンツ配置用のS3バケット作成
- CloudFrontのログ保存用のS3バケット作成
- OAIアカウント作成とバケットポリシーに追加
- CloudFrontディストリビューション作成(WAFと紐づけ)
bin/cloudfront.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CloudfrontStack } from '../lib/cloudfront-stack';
const app = new cdk.App();
new CloudfrontStack(app, 'CloudfrontStack', {
env: {
region: 'ap-northeast-1',
},
});
lib/cloudfront-stack.ts
import { aws_route53_targets, RemovalPolicy, Stack, StackProps, } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as acm from 'aws-cdk-lib/aws-certificatemanager'
import * as route53 from 'aws-cdk-lib/aws-route53'
export class CloudfrontStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
//Variable declaration
const app_name:string = 'app-cloudfront';
const create_year:string = String(new Date().getFullYear());
const create_month:string = ('0' + String(new Date().getMonth() + 1)).slice(-2);
const app_bucket_name:string = `${app_name}-backet-${create_year}${create_month}`;
const domain_name:string = 'DOMAIN_NAME';
const cert_arn:string = 'CERT_ARN';
const cfn_webacl_arn:string = 'WAFACL_ARN'
// Create S3 bucket for logging
const log_bucket = new s3.Bucket(this, 'LogBucket',{
bucketName: `${app_bucket_name}-log`,
enforceSSL: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
//Create S3 bucket for App
const app_bucket = new s3.Bucket(this, 'AppBacket',{
bucketName: app_bucket_name,
enforceSSL: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
//Create OAI account & bucket policy
const identity = new cloudfront.OriginAccessIdentity(this, 'OriginAccessIdentity', {
comment: `cloudfront OAI access for bucket: ${app_bucket_name}`,
});
const canonicalUserPrincipal = new iam.CanonicalUserPrincipal(identity.cloudFrontOriginAccessIdentityS3CanonicalUserId);
const oai_bucket_policy = new iam.PolicyStatement({
actions:['S3:GetObject'],
effect: iam.Effect.ALLOW,
principals: [canonicalUserPrincipal],
resources: [`${app_bucket.bucketArn}/*`],
});
app_bucket.addToResourcePolicy(oai_bucket_policy);
//Create CloudFront Distribution
const distribution = new cloudfront.Distribution(this, 'distribution', {
defaultBehavior: {
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD,
cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
origin: new origins.S3Origin(app_bucket, {
originAccessIdentity: identity,
}),
},
enableLogging: true,
logBucket: log_bucket,
logFilePrefix: 'cloudfront_log',
priceClass: cloudfront.PriceClass.PRICE_CLASS_200,
domainNames: [domain_name],
certificate: acm.Certificate.fromCertificateArn(
this,
'Certificate',
cert_arn,
),
comment: `${app_name}-distribution`,
defaultRootObject: 'index.html',
webAclId: cfn_webacl_arn,
});
// Create host zone and A record to Route53
// const host_zone = new route53.PublicHostedZone(this, 'HostedZone', {
// zoneName: 'example.com',
// });
// new route53.ARecord(this, 'CloudFrontARecord', {
// zone: host_zone,
// recordName: 'www',
// target: route53.RecordTarget.fromAlias(new aws_route53_targets.CloudFrontTarget(distribution)),
// })
}
}