LoginSignup
6
1

More than 3 years have passed since last update.

AWS CDKを使ってCloudFrontとALBの環境構築をしてみた

Last updated at Posted at 2019-12-14

はじめに

この記事はPlayground Advent Calender 2019の12/14の記事です。
初めまして。今年の春からPlaygroundでバックエンドをメインで勉強しているkikilsです。ここでは、ここ最近勉強しているAWS CDKというサービスについての知見を書いていきたいと思います。

行ったこと

CDKでユーザーがECSにアクセスするときにCloudFrontを経由する仕組みを作りました。
image.png

AWS CDK(Cloud Development Kit)とは

今年の7月にGAされたばかりのサービスで、AWSのインフラを TypeScript などの言語を使って定義します。プログラムはAppとStackとConstructという概念で構成します。CDKプログラムを実行することで CloudFormation テンプレートを生成してそのテンプレートを使ってデプロイします。

仕組み

CloudFrontを経由しないALBへのアクセスを拒否するには、ALBのリクエストルーティング機能を使います。ALBのlistenerのルールに「Cloud Frontからのアクセスであるかどうかの判定をしたらECSに転送する」というルールを追加したうえで「それ以外のアクセスは403を返す」というルールもデフォルトアクションにすることで実現します。CloudFrontからのアクセスかどうかの判定は、httpヘッダを読み取り、User-agentがCloudFrontのものであるかとCloudFront側でカスタムヘッダを追加してそれが一致するかどうかを判定します。

コードを書いてみる

まず、FargateとCloudFontを構築するStackを定義します。


const app = new cdk.App();

const fargateStack = new FargateStack(app, `fargate`);
new CloudFrontStack(app, `cloudfront`, {
    loadBalancer: fargateStack.loadBalancer,
});

次に、CloudFrontのStackを作成していきます。AWS CDKの公式APIレファレンスを参照するとCloudFrontWebDistributionクラスがあるのでこれを用います。カスタムオリジンにはfargateStackで渡してあげたALBのDNSを指定しています。また、originProtocolPolicyをhttpにしていますが本番環境ではhttpsに変更したほうがよいです。カスタムヘッダには、任意の文字列を設定します。

cloudFront-stack.ts

interface CloudFrontProps extends cdk.StackProps {
    loadBalancer: ApplicationLoadBalancer;
}

export class CloudFrontStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, props: CloudFrontProps) {
        super(scope, id, props);

     new CloudFrontWebDistribution(this, `distribution`, {
            defaultRootObject: "",
            originConfigs: [
                {
                    customOriginSource: {
                        domainName: props.loadBalancer.loadBalancerDnsName,
                        originProtocolPolicy: OriginProtocolPolicy.HTTP_ONLY
                    },
                    behaviors: [
                        {
                            isDefaultBehavior: true
                        }
                    ],
                    originHeaders: {
                        ["x-pre-shared-key"]: "PRESHAREDKEY"
                    }
                }
            ]
        });
    }
}

次に、fargateを構築します。clusterとtaskDefinitionは各自で当ててください。

fargate-stack.ts

export class FargateStack extends cdk.Stack {
    public readonly loadBalancer: ApplicationLoadBalancer;
    const fargate = new ApplicationLoadBalancedFargateService(
            this,
            "apps",
            {
                cluster: cluster,
                taskDefinition: taskDefinition
            }
        );

        const listener = fargate.listener.node.defaultChild as CfnListener;
        listener.defaultActions = [
            {
                type: "fixed-response",
                fixedResponseConfig: {
                    statusCode: "403",
                    contentType: "text/html",
                    messageBody: "<h1>403 Forbidden</h1>"
                }
            }
        ];

        new CfnListenerRule(this, `listener`, {
            actions: [
                {
                    type: `forward`,
                    targetGroupArn: fargate.targetGroup.targetGroupArn
                }
            ],
            conditions: [
                {
                    field: "http-header",
                    httpHeaderConfig: {
                        httpHeaderName: "User-agent",
                        values: ["Amazon CloudFront"]
                    }
                },
                {
                    field: "http-header",
                    httpHeaderConfig: {
                        httpHeaderName: "x-pre-shared-key",
                        values: ["PRESHAREDKEY"]
                    }
                }
            ],
            listenerArn: listener.ref,
            priority: 1
        });

説明

ApplicationLoadBalancedFargateServiceクラスはクラスターを作りALBとターゲットグループの接続をよしなにしてくれます。

  • listenerのデフォルトアクションを変更する

CDKにはlistenerのデフォルトアクションを設定するパラメータがまだないので、CloudFormationに直接上書きできるCfnListenerクラスを使います。実際に出力されるcloudformationのテンプレ―トは次のようになります。

~~~~~~~~~~
"appsLBPublicListenerEC0E93DB": {
      "Type": "AWS::ElasticLoadBalancingV2::Listener",
      "Properties": {
        "DefaultActions": [
          {
            "FixedResponseConfig": {
              "ContentType": "text/html",
              "MessageBody": "<h1>403 Forbidden</h1>",
              "StatusCode": "403"
            },
            "Type": "fixed-response"
          }
        ],
        "LoadBalancerArn": {
          "Ref": "appsLB2B3DA679"
        },
        "Port": 80,
        "Protocol": "HTTP"
      },
      "Metadata": {
        "aws:cdk:path": "fargate/apps/LB/PublicListener/Resource"
      }
    }
~~~~~~~~~~

このようにDefaultActionsが上書きされていることがわかります。

  • Cloud Frontからのアクセスであるかどうかの判定をしたらECSに転送する

listenerと同じくCDKにはApplicationListenerRuleというクラスが用意されていますがルールの条件にhttpヘッダがサポートされていないのでCloudformationを直に追加できるCfnListerRuleを使います。この時に出力されるテンプレートは次のようになります。


~~~~~~~~~~
"listener": {
      "Type": "AWS::ElasticLoadBalancingV2::ListenerRule",
      "Properties": {
        "Actions": [
          {
            "TargetGroupArn": {
              "Ref": "appsLBPublicListenerECSGroup7B961058"
            },
            "Type": "forward"
          }
        ],
        "Conditions": [
          {
            "Field": "http-header",
            "HttpHeaderConfig": {
              "HttpHeaderName": "User-agent",
              "Values": [
                "Amazon CloudFront"
              ]
            }
          },
          {
            "Field": "http-header",
            "HttpHeaderConfig": {
              "HttpHeaderName": "x-pre-shared-key",
              "Values": [
                "PRESHAREDKEY"
              ]
            }
          }
        ],
        "ListenerArn": {
          "Ref": "appsLBPublicListenerEC0E93DB"
        },
        "Priority": 1
      },
~~~~~~~~~~

このようにCloudFormationのテンプレートに追加されていることがわかります。

まとめ

このようにCDKはAWSサービスが抽象化されて非常にわかりやすくなっており、それだけではなく今回のようにパラメータにない場合でもAWSサービスを構築することができます。

6
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
6
1