🐈

私的 CloudFront + S3 環境でおすすめしたい設定

に公開

はじめに

CloudFront + S3 環境で初期構築した際、設定されてると便利だなと思ったことをまとめました。
あわせて参考記事も紹介しますので、試してみてください。

CloudFront Functions(関数)について

この記事では CloudFront Functions を利用します。
設定方法は以下を参考にしてください。

1. サブディレクトリのドキュメントルート指定

S3 の静的ウェブサイトホスティングがルートオブジェクト (https://www.example.com/) にのみインデックスドキュメントを適用し、CloudFront がサブディレクトリ (https://www.example.com/hoge/) の index.html を自動解決せず、Not Found となります。(AWS仕様上の挙動)
以下を CloudFront Functions で設定し解決しました。

function handler(event) {
  var request = event.request;
  var uri = request.uri;

  if (uri.endsWith("/")) {
    // Check whether the URI is missing a file name.
    request.uri += "index.html";
  } else if (!uri.includes(".")) {
    // Check whether the URI is missing a file extension.
    request.uri += "/index.html";
  }

  return request;
}

2. CloudFront のキャッシュ削除

S3 バケット内のオブジェクトが変更された場合に、S3イベントトリガーで Lambda を起動し CloudFront のキャッシュを削除します。
設定方法は以下の記事が分かりやすかったです。
https://qiita.com/hirai-11/items/0aede046a26cc66778ad

3. 監視編

Cloud Watch Alarm を設定すると便利!
各アラームの CloudFormation テンプレート(YAML)を記載します。

3-1. CloudFront の 4XX と 5XX エラー通知

CloudFront で 400 番台と 500 番台のエラーが発生した時にアラームを通知します。
※ AWS Notification - Subscription Confirmation メールが届きますので承認してください。

cloudfront-alarm.yaml(折り畳んでます)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudWatch Alarm for CloudFront 4xx and 5xx errors'

Parameters:
  NotificationEmail:
    Type: String

  DistributionId:
    Type: String

Resources:
  # SNSトピックの作成
  SnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: CloudFront-Errors-Topic

  # SNSトピックのサブスクリプション(メール通知)
  SnsSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: email
      TopicArn: !Ref SnsTopic
      Endpoint: !Ref NotificationEmail

  # CloudFront 4xxエラーのアラーム
  CloudFront4xxErrorAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: CloudFront-4xx-Errors
      AlarmDescription: Alarm when CloudFront returns 4xx errors
      MetricName: 4xxErrorRate
      Namespace: AWS/CloudFront
      Statistic: Average
      Period: 300  # 5分間
      EvaluationPeriods: 1
      Threshold: 5  # エラーレート5%以上で通知
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: DistributionId
          Value: !Ref DistributionId
        - Name: Region
          Value: Global
      AlarmActions:
        - !GetAtt SnsTopic.TopicArn
      OKActions:
        - !GetAtt SnsTopic.TopicArn
      TreatMissingData: notBreaching

  # CloudFront 5xxエラーのアラーム
  CloudFront5xxErrorAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: CloudFront-5xx-Errors
      AlarmDescription: Alarm when CloudFront returns 5xx errors
      MetricName: 5xxErrorRate
      Namespace: AWS/CloudFront
      Statistic: Average
      Period: 300  # 5分間
      EvaluationPeriods: 1
      Threshold: 5  # エラーレート5%以上で通知
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: DistributionId
          Value: !Ref DistributionId
        - Name: Region
          Value: Global
      AlarmActions:
        - !GetAtt SnsTopic.TopicArn
      OKActions:
        - !GetAtt SnsTopic.TopicArn
      TreatMissingData: notBreaching

YAMLパラメーターの解説

パラメーター名 解説
NotificationEmail 通知先メールアドレス
DistributionId 監視する CloudFront の DistributionId

3-2. S3 の 4XX と 5XX エラー通知

S3 で 400 番台と 500 番台のエラーが発生した時にアラームを通知します。
※ AWS Notification - Subscription Confirmation メールが届きますので承認してください。

s3-alarm.yaml(折り畳んでます)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudWatch Alarm for S3 4xx and 5xx errors'

Parameters:
  NotificationEmail:
    Type: String

  BucketName:
    Type: String

Resources:
  # SNSトピックの作成
  SnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: S3-Errors-Topic

  # SNSトピックのサブスクリプション(メール通知)
  SnsSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: email
      TopicArn: !Ref SnsTopic
      Endpoint: !Ref NotificationEmail

  # 4xxエラーのアラーム
  S34xxErrorAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: S3-4xx-Errors
      AlarmDescription: Alarm when S3 returns 4xx errors
      MetricName: 4xxErrors
      Namespace: AWS/S3
      Statistic: Sum
      Period: 300  # 5分間
      EvaluationPeriods: 1
      Threshold: 5  # 5回以上のエラーで通知
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: BucketName
          Value: !Ref BucketName
      AlarmActions:
        - !GetAtt SnsTopic.TopicArn
      OKActions:
        - !GetAtt SnsTopic.TopicArn
      TreatMissingData: notBreaching

  # 5xxエラーのアラーム
  S35xxErrorAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: S3-5xx-Errors
      AlarmDescription: Alarm when S3 returns 5xx errors
      MetricName: 5xxErrors
      Namespace: AWS/S3
      Statistic: Sum
      Period: 300  # 5分間
      EvaluationPeriods: 1
      Threshold: 5  # 5回以上のエラーで通知
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: BucketName
          Value: !Ref BucketName
      AlarmActions:
        - !GetAtt SnsTopic.TopicArn
      OKActions:
        - !GetAtt SnsTopic.TopicArn
      TreatMissingData: notBreaching

YAMLパラメーターの解説

パラメーター名 解説
NotificationEmail 通知先メールアドレス
BucketName 監視する S3 バケット名

3-3. S3 の容量通知

S3 で指定容量を超過した時にアラームを通知します。
※ AWS Notification - Subscription Confirmation メールが届きますので承認してください。

s3-bucket-size-alarm.yaml(折り畳んでます)
AWSTemplateFormatVersion: 2010-09-09
Description: 'CloudWatch Alarm for S3 Bucket Size'

Parameters:
  NotificationEmail:
    Type: String

  BucketName:
    Type: String

  S3BucketSize:
    Type: Number

Resources:
  # SNSトピックの作成
  SnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: S3-Bucket-Size-Errors-Topic

  # SNSトピックのサブスクリプション(メール通知)
  SnsSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: email
      TopicArn: !Ref SnsTopic
      Endpoint: !Ref NotificationEmail

  # バケットサイズ超過のアラーム
  S3BucketSizeAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: S3-Bucket-Size-Errors
      AlarmDescription: Alarm when S3 BucketSizeBytes alarm
      MetricName: BucketSizeBytes
      Namespace: AWS/S3
      Statistic: Average
      Period: 86400  # 24時間
      Threshold: !Ref S3BucketSize
      EvaluationPeriods: 1
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: BucketName
          Value: !Ref BucketName
        - Name: StorageType
          Value: StandardStorage
      AlarmActions:
        - !GetAtt SnsTopic.TopicArn
      OKActions:
        - !GetAtt SnsTopic.TopicArn
      TreatMissingData: notBreaching

YAMLパラメーターの解説

パラメーター名 解説
NotificationEmail 通知先メールアドレス
BucketName 監視する S3 バケット名
S3BucketSize 監視する バケットの容量(Byte)

4. おまけ

4-1. Basic 認証

検証環境など不特定多数に見せたくない時 Basic 認証を設定して認証情報を知ってる人だけ見れるようにします。
以下を CloudFront Functions で設定します。
※ token 値は、echo -n user:pass | base64 コマンドで作成可能ですが、セキュリティの観点から、CloudFront Functions の KeyValueStores などより安全な方法で管理することを推奨します。

function handler(event) {
  var request = event.request;
  var headers = request.headers;

  // echo -n user:pass | base64
  var authString = "Basic <token値>";

  if (
    typeof headers.authorization === "undefined" ||
    headers.authorization.value !== authString
  ) {
    return {
      statusCode: 401,
      statusDescription: "Unauthorized",
      headers: { "www-authenticate": { value: "Basic" } }
    };
  }

  return request;
}

4-2. IP 制限

AWS WAF を導入せずとも CloudFront Functions のみで IP 制限を設定することができます。
以下 xxx.xxx.xxx.xxx 部分を接続許可する IP に変更し、CloudFront Functions を設定します。

function handler(event) {
  var request = event.request;
  var clientIP = event.viewer.ip;
  // 許可するIPを指定
  var IP_WHITE_LIST = ["xxx.xxx.xxx.xxx", "xxx.xxx.xxx.xxx"];
  var allowIP = IP_WHITE_LIST.includes(clientIP);

  if (allowIP) {
    return request;
  } else {
    var response = {
      statusCode: 403,
      statusDescription: "Forbidden",
    };
    return response;
  }
}

まとめ

はじめて CloudFront + S3 を触った時にいろいろ調べたのですが、まとめて紹介しているページが見当たらなかった(探し方が悪い?)ので、自分なりにまとめてみました。
また、参考サイトに記載しました「CloudFront Functions の活用パターン集」では、いろいろな設定ができることを発見できたので、参考になりました!
この記事が、誰かの役に立てば幸いです。

参考サイト

株式会社グローバルネットコア 有志コミュニティ(β)

Discussion