6
Help us understand the problem. What are the problem?

posted at

updated at

AWS CDK構築備忘録①(WAF+CloudFlont+S3)

内容

下記AWS WAF、CloudFront、S3の箇所をCDKで構築します。

  • 証明書はACMで払い出し済みのものを使用
  • ホストゾーンはRoute53でドメイン取得時に作成されたものを使用
  • 環境作成後にCloudFrontのAレコード(Alias)をホストゾーンに追加

cloudfront.png

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)),

    // }) 
  }
}

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
6
Help us understand the problem. What are the problem?