Amazon CloudFront ディストリビューションのオリジンフェイルオーバーの設定について

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。

今回は、Amazon CloudFrontディストリビューションでオリジンフェイルオーバーが設定されていない状態について、そのリスクと対策を解説します。

ポリシーの説明

Amazon CloudFront の Security Hub コントロール – AWS Security Hub

[CloudFront.4] CloudFront ディストリビューションでは、オリジンフェイルオーバーが設定されている必要があります

このコントロールは、Amazon CloudFront ディストリビューションが 2 つ以上のオリジンを使用するオリジングループで構成されているかどうかをチェックします。

リスク

Amazon CloudFrontは、コンテンツ配信ネットワーク (CDN) サービスとして、ウェブコンテンツの高速かつ安全な配信を実現します。CloudFrontは、S3バケット、EC2インスタンス、ELB、カスタムHTTPサーバーなど、さまざまなオリジンからコンテンツを取得します。しかし、単一のオリジンに依存し、オリジンフェイルオーバーが設定されていない場合、以下のような重大な可用性および運用上のリスクが発生します。

  • 単一障害点 (SPOF) の発生: CloudFrontディストリビューションが単一のオリジンに依存している場合、そのオリジンに障害(例: S3バケットのアクセス障害、EC2インスタンスのダウン、アプリケーションサーバーのエラー)が発生すると、CloudFrontはコンテンツを取得できなくなり、エンドユーザーはコンテンツにアクセスできなくなります。これにより、ウェブサイトやアプリケーションが利用不能になり、ビジネスの中断や収益損失につながります。
  • ダウンタイムの増加: オリジンに問題が発生した場合、手動で復旧を待つか、ディストリビューションのオリジン設定を変更するなどの対応が必要です。この手動介入には時間がかかり、ダウンタイムが長期化する可能性があります。
  • ユーザーエクスペリエンスの低下: コンテンツにアクセスできないユーザーは、エラーページを目にすることになり、ウェブサイトやアプリケーションに対する信頼が失われます。これは、顧客満足度の低下やブランドイメージの毀損につながる可能性があります。
  • パフォーマンス問題の長期化: オリジンのパフォーマンスが低下した場合でも、フェイルオーバーがなければ、その低下が直接エンドユーザーに影響します。フェイルオーバー機能があれば、パフォーマンスが劣化したオリジンから健全なオリジンに切り替えることで、パフォーマンス問題を回避できます。
  • 運用上の負担: 単一オリジンの障害を監視し、手動で対応することは、運用チームにとって大きな負担となります。特に、24時間365日の稼働が求められるシステムでは、アラート対応と緊急の構成変更が必要になり、運用コストが増加します。

対策

CloudFrontディストリビューションにオリジンフェイルオーバーを設定することは、オリジンの可用性を大幅に向上させ、コンテンツ配信のレジリエンスを高めるための重要なベストプラクティスです。オリジンフェイルオーバーは、プライマリオリジンが使用できない場合、または特定のHTTPレスポンスステータスコードを返した場合に、自動的にセカンダリーオリジンにトラフィックをリダイレクトします。

  • オリジングループの作成: CloudFrontでオリジンフェイルオーバーを有効にするには、複数のオリジンをまとめた「オリジングループ」を作成します。このグループ内で、1つのオリジンをプライマリとして、別のオリジンをセカンダリとして指定します。
  • プライマリオリジンとセカンダリオリジン:
    • プライマリオリジン: 通常時にトラフィックを処理するオリジンです。
    • セカンダリオリジン: プライマリオリジンに問題が発生した場合にトラフィックを引き継ぐオリジンです。異なるリージョンに配置されたS3バケットやEC2/ELBなどをセカンダリオリジンとして設定することで、災害対策 (DR) のシナリオにも対応できます。
  • フェイルオーバー条件の定義: CloudFrontは、プライマリオリジンからのHTTPレスポンスステータスコード(例: 500, 502, 503, 504)に基づいて、自動的にセカンダリオリジンにフェイルオーバーします。これらのステータスコードは、オリジンのエラーを示します。
  • 自動的なトラフィックルーティング: フェイルオーバー条件が満たされると、CloudFrontは自動的に後続のリクエストをセカンダリオリジンにルーティングします。これにより、エンドユーザーは中断することなくコンテンツにアクセスし続けることができます。
  • 可用性の向上とダウンタイムの削減: オリジンフェイルオーバーにより、単一オリジンの障害から自動的に回復できるため、サービスの可用性が大幅に向上し、ダウンタイムが最小限に抑えられます。
  • ユーザーエクスペリエンスの維持: ユーザーはシームレスにコンテンツにアクセスし続けることができるため、サービスの信頼性が高まり、満足度が維持されます。

修復方法

CloudFrontディストリビューションにオリジンフェイルオーバーを設定する手順は、新しいディストリビューションの作成時、または既存のディストリビューションのオリジン設定の変更時に行えます。

AWSコンソールでの修復手順

AWSコンソールを使用して、CloudFrontディストリビューションにオリジンフェイルオーバーを設定します。

前提:

  • プライマリオリジンとなるS3バケットやELB、EC2インスタンスなどが存在すること。
  • セカンダリオリジンとなる別のS3バケットやELB、EC2インスタンスなどが存在すること(異なるリージョンに配置されていることがDRの観点からは推奨)。

既存のディストリビューションにオリジンフェイルオーバーを設定する場合:

  1. CloudFrontサービスへ移動: AWSコンソールにログインし、Amazon CloudFront サービスを開きます。
  2. 対象のディストリビューションを選択: オリジンフェイルオーバーを設定したいCloudFrontディストリビューションを選択します。
  3. オリジンタブへ移動: ディストリビューションの詳細ページで、「オリジン」タブをクリックします。
  4. オリジングループの作成:
    • オリジンを管理」をクリックし、現在設定されているオリジンがリスト表示されていることを確認します。「オリジングループを作成」をクリックします。オリジングループ名を付けます(例: PrimaryAndSecondaryOriginGroup)。「プライマリオリジン」として、通常時に使用したいオリジンをドロップダウンから選択します。「セカンダリオリジン」として、フェイルオーバー時に使用したいオリジンをドロップダウンから選択します。「フェイルオーバー条件」で、フェイルオーバーをトリガーするHTTPステータスコードを選択します。一般的には、500, 502, 503, 504 を選択します。「オリジングループを作成」をクリックします。
  1. 動作 (Behavior) の更新:
    • 動作」タブに戻ります。
    • 対象のキャッシュ動作(通常は「Default(*)」)を選択し、「編集」をクリックします。
    • オリジン」のドロップダウンメニューから、先ほど作成したオリジングループ名を選択します。
    • その他の設定を確認し、「変更を保存」をクリックします。

これで、CloudFrontディストリビューションはオリジングループを使用し、プライマリオリジンが設定されたHTTPステータスコードを返した場合に、自動的にセカンダリオリジンにフェイルオーバーするようになります。ディストリビューションの更新には時間がかかる場合があります。

Terraformでの修復手順

TerraformでCloudFrontディストリビューションにオリジンフェイルオーバーを設定するには、aws_cloudfront_distribution リソース内で origin_group ブロックを定義し、プライマリおよびセカンダリオリジンを指定します。

# (例) プライマリS3バケットオリジン
resource "aws_s3_bucket" "primary_origin_bucket" {
  bucket = "primary-cloudfront-origin-bucket-unique-20250706" # グローバルで一意の名前に変更
  tags = {
    Name = "CloudFrontPrimaryOrigin"
  }
}

resource "aws_s3_bucket_acl" "primary_origin_acl" {
  bucket = aws_s3_bucket.primary_origin_bucket.id
  acl    = "private"
}

# (例) セカンダリS3バケットオリジン (異なるリージョンに配置することを推奨)
# プロダクションでは、異なるリージョンに同じコンテンツを持つ別のS3バケットを用意します
resource "aws_s3_bucket" "secondary_origin_bucket" {
  bucket = "secondary-cloudfront-origin-bucket-unique-20250706" # グローバルで一意の名前に変更
  # bucket_regional_domain_name は S3バケットの地域に依存
  # example for us-west-2
  # bucket = "secondary-cloudfront-origin-bucket-us-west-2" 
  # region = "us-west-2"
  tags = {
    Name = "CloudFrontSecondaryOrigin"
  }
}

resource "aws_s3_bucket_acl" "secondary_origin_acl" {
  bucket = aws_s3_bucket.secondary_origin_bucket.id
  acl    = "private"
}

# CloudFront オリジンアクセスコントロール (OAC) - プライマリ用
resource "aws_cloudfront_origin_access_control" "primary_s3_oac" {
  name                              = "PrimaryS3OAC"
  description                       = "OAC for Primary S3 origin"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

# CloudFront オリジンアクセスコントロール (OAC) - セカンダリ用
resource "aws_cloudfront_origin_access_control" "secondary_s3_oac" {
  name                              = "SecondaryS3OAC"
  description                       = "OAC for Secondary S3 origin"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

# S3バケットポリシー (CloudFront OACからのアクセスを許可) - プライマリ用
resource "aws_s3_bucket_policy" "allow_primary_cloudfront_oac" {
  bucket = aws_s3_bucket.primary_origin_bucket.id
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Sid       = "AllowCloudFrontOACAccess"
        Effect    = "Allow",
        Principal = {
          Service = "cloudfront.amazonaws.com"
        },
        Action    = "s3:GetObject",
        Resource  = [
          "${aws_s3_bucket.primary_origin_bucket.arn}/*",
          aws_s3_bucket.primary_origin_bucket.arn
        ],
        Condition = {
          StringEquals = {
            "AWS:SourceArn" = aws_cloudfront_distribution.s3_failover_distribution.arn
          }
        }
      }
    ]
  })
}

# S3バケットポリシー (CloudFront OACからのアクセスを許可) - セカンダリ用
resource "aws_s3_bucket_policy" "allow_secondary_cloudfront_oac" {
  bucket = aws_s3_bucket.secondary_origin_bucket.id
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Sid       = "AllowCloudFrontOACAccess"
        Effect    = "Allow",
        Principal = {
          Service = "cloudfront.amazonaws.com"
        },
        Action    = "s3:GetObject",
        Resource  = [
          "${aws_s3_bucket.secondary_origin_bucket.arn}/*",
          aws_s3_bucket.secondary_origin_bucket.arn
        ],
        Condition = {
          StringEquals = {
            "AWS:SourceArn" = aws_cloudfront_distribution.s3_failover_distribution.arn
          }
        }
      }
    ]
  })
}

# CloudFrontディストリビューション (オリジンフェイルオーバー付き)
resource "aws_cloudfront_distribution" "s3_failover_distribution" {
  # プライマリオリジン
  origin {
    domain_name              = aws_s3_bucket.primary_origin_bucket.bucket_regional_domain_name
    origin_id                = "PrimaryS3Origin"
    origin_access_control_id = aws_cloudfront_origin_access_control.primary_s3_oac.id
  }

  # セカンダリオリジン
  origin {
    domain_name              = aws_s3_bucket.secondary_origin_bucket.bucket_regional_domain_name
    origin_id                = "SecondaryS3Origin"
    origin_access_control_id = aws_cloudfront_origin_access_control.secondary_s3_oac.id
  }

  # オリジングループの定義
  origin_group {
    origin_id = "FailoverOriginGroup" # このIDをキャッシュ動作で参照
    failover_criteria {
      # フェイルオーバーをトリガーするHTTPステータスコード
      status_codes = [500, 502, 503, 504]
    }
    member {
      origin_id = "PrimaryS3Origin" # プライマリオリジン
    }
    member {
      origin_id = "SecondaryS3Origin" # セカンダリオリジン
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = "CloudFront distribution with S3 origin failover"
  default_root_object = "index.html"

  default_cache_behavior {
    # オリジングループのIDをターゲットオリジンとして指定
    target_origin_id = "FailoverOriginGroup"

    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400

    forwarded_values {
      query_string = false
      headers      = []
      cookies {
        forward = "none"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }

  tags = {
    Environment = "Production"
  }
}

上記のTerraformコードでは、aws_cloudfront_distribution リソース内で以下の設定を行います。

  1. 複数の origin ブロック: プライマリオリジンとセカンダリオリジンをそれぞれ個別の origin ブロックで定義します。
  2. origin_group ブロック:
    • origin_id: このオリジングループを一意に識別するIDを指定します。
    • failover_criteria.status_codes: プライマリオリジンがこれらのHTTPステータスコードを返した場合にフェイルオーバーをトリガーするように設定します。
    • member: プライマリとセカンダリのオリジンを origin_id で指定します。最初の member がプライマリとして扱われます。
  3. default_cache_behavior.target_origin_id: デフォルトのキャッシュ動作、または特定のパスパターンのキャッシュ動作において、target_origin_id を作成したオリジングループのIDに設定します。

注意点:

  • S3バケットをオリジンとして使用する場合、OAC (Origin Access Control) を設定して、S3バケットへの直接アクセスを防止することを強く推奨します。上記のTerraformコードにもOACとそれに関連するS3バケットポリシーの例を含めています。
  • primary-cloudfront-origin-bucket-unique-20250706secondary-cloudfront-origin-bucket-unique-20250706 などのプレースホルダーは、実際の環境に合わせて修正してください。特にセカンダリバケットは、異なるAWSリージョンに配置することで、リージョン全体の障害に対するDR(災害復旧)対策となります。

最後に

この記事では、CloudFrontディストリビューションにオリジンフェイルオーバーを設定することの重要性について解説しました。オリジンフェイルオーバーは、単一障害点のリスクを排除し、オリジンの障害発生時でもコンテンツ配信を継続するための不可欠な機能です。これにより、アプリケーションの可用性が向上し、ユーザーエクスペリエンスが維持され、運用上の負担も軽減されます。 貴社のCloudFrontディストリビューションでは、オリジンフェイルオーバーが適切に設定されていますか?この機会にぜひ設定を確認・強化してみてください。 こちらの内容の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。運用が非常にラクに出来る製品になっていますのでぜひ興味がある方はお問い合わせお待ちしております。

最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る