📚

ALB のカスタムヘッダー認証を VPC Origin に置き換える手順と注意点

に公開

この記事はコインチェック株式会社(以下、コインチェック)のアドベントカレンダー22日目の記事です。
https://qiita.com/advent-calendar/2025/coincheck

目的

今回は、ALB のカスタムヘッダー認証を CloudFront VPC Origin に置き換える手順 と、その構成を組む際に気をつけたい 「ALB 構築時のセキュリティグループの落とし穴(一瞬だけ全公開されちゃう問題)」 について共有します。

特に今回は、「諸事情で ALB 自体はパブリック (Internet-facing) のままにしなければならない」 ケースを想定して構成を組みます。

これまでの課題:カスタムヘッダーによる制限

これまで、CloudFront 経由のアクセスのみを ALB に許可したい場合、一般的に以下の構成をとっていました。

  1. ALB を Internet-facing に配置
  2. アクセス制限の実装(以下のいずれか、または組み合わせ)
    • SG で CloudFront IP 帯を許可 (Managed Prefix List)
    • ALB リスナールールで「特定のカスタムヘッダー」をチェック

解決策:CloudFront VPC Origin への置き換え

CloudFront VPC Origin を利用すると、CloudFront から VPC 内のリソース(ALB や EC2)へ、AWS のプライベートネットワークを通じて直接アクセスできるようになります。

この構成に置き換えることで、以下のようなメリットがあります。

1. カスタムヘッダーが不要になる

通信が VPC 内の ENI 経由で ALB に届きます。経路自体がセキュアであるため、アプリケーション層(L7)でのヘッダー認証が不要になります。
結果として、ALB のリスナールールが本来のルーティング定義だけになり、管理がシンプルになります。

2. パブリック ALB でも「インターネット遮断」が可能

これが今回のポイントです。
ALB 自体はパブリック IP を持っていますが、VPC Origin を利用する場合、ALB のセキュリティグループでインターネット(0.0.0.0/0) や CloudFront の Prefix List を許可する必要がなくなります。

代わりに 「VPC 内(VPC Origin ENI)からの通信のみ許可」 すれば良いため、パブリック ALB でありながら、事実上インターネットからの直接アクセスをネットワーク層(L4)で遮断できます。


構築時の注意点: SG 設定の空白期間と対策

ALB が Internet-facing(パブリック)である場合、構築時のリスクはさらに高まります。手順を間違えると、一瞬でも世界中にポートを開放してしまう可能性があるからです。

危険なパターン:一瞬だけ全公開

AWS CLI や Terraform 等でパブリック ALB を作成する際、もしセキュリティグループ(SG)を明示しない(デフォルトSGが当たる)、あるいは「とりあえず疎通確認用」として 0.0.0.0/0 を許可した SG をアタッチしてしまうとどうなるでしょうか?

安全な構築手順

このリスクを防ぐために、以下の順序で構築することを推奨します。

  1. Ingress(インバウンドルール)が空の SG を作成する
    • まずはどこからの通信も受け付けない SG を作ります。
  2. その SG をアタッチして ALB を構築する
    • この時点で ALB はパブリック IP を持ちますが、SG が閉じているためアクセスできません(安全)。
  3. VPC Origin(ENI)を構築する
  4. SG の Ingress を更新する
    • ここで初めて、VPC Origin の ENI に付与された SG ID からの通信を許可します。

CLI での実装イメージは以下のようになります。

# 1. ルールなしのSG作成(これが重要!)
ALB_SG_ID=$(aws ec2 create-security-group \
    --group-name alb-sg-empty \
    --description "Empty SG for ALB" \
    --vpc-id <VPC_ID> \
    --query 'GroupId' \
    --output text)

# 2. ALB作成 (Internet-facing で作成)
# この時点で Public IP が付与されるが、SG が空なので誰もアクセスできない
aws elbv2 create-load-balancer \
    --name my-alb \
    --scheme internet-facing \
    --security-groups $ALB_SG_ID ...

# 3. VPC Origin の作成 (コンソール等で実施し、自動作成された SG ID を確認)
# 例: VPC Origin 用 SG ID が sg-vpcorigin123 だとする

# 4. VPC Origin SG からの通信のみ許可
# ここで初めて穴を開ける。ソースに SG ID を指定するのがポイント。
aws ec2 authorize-security-group-ingress \
    --group-id $ALB_SG_ID \
    --protocol tcp \
    --port 80 \
    --source-group sg-vpcorigin123

CloudFormation による実装例

最後に、この「安全な構成」を構築する CloudFormation テンプレートを紹介します。

VPC Origin が自動作成するセキュリティグループ ID は、リソース作成前には分からないため、「最初は空パラメータでデプロイし、後から SG ID を入力して更新する」 というフローをとることで、安全かつ厳密な設定が可能になります。

vpc-origin-public-alb.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFront VPC Origin with Internet-facing ALB

Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
  SubnetIds:
    Type: List<AWS::EC2::Subnet::Id>
    Description: Public Subnets for Internet-facing ALB
  
  # 【ポイント】
  # 初回デプロイ時は空欄にしておく。
  # VPC Origin 作成後に、ここに SG ID を入力してスタックを更新する。
  CloudFrontVpcOriginSecurityGroupId:
    Type: String
    Description: (Optional) The Security Group ID of the CloudFront VPC Origin. Leave empty initially.
    Default: ""

Conditions:
  # IDが入力された時だけルールを作成する条件定義
  HasVpcOriginSgId: !Not [!Equals [!Ref CloudFrontVpcOriginSecurityGroupId, ""]]

Resources:
  # ------------------------------------------------------------
  # 1. Security Group for ALB
  # ------------------------------------------------------------
  # ポイント: 初期状態では Ingress ルールを持たない SG
  # これにより ALB 作成時の不正アクセスを防止する
  AlbSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group for ALB
      VpcId: !Ref VpcId
      SecurityGroupIngress: [] 

  # ------------------------------------------------------------
  # 2. Internet-facing ALB
  # ------------------------------------------------------------
  # パブリックALBだが、SGが閉じているので安全
  PublicAlb:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      SecurityGroups:
        - !Ref AlbSecurityGroup
      Subnets: !Ref SubnetIds
      Type: application

  AlbListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref PublicAlb
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: fixed-response
          FixedResponseConfig:
            ContentType: "text/plain"
            MessageBody: "Hello from VPC Origin via Public ALB!"
            StatusCode: "200"

  # ------------------------------------------------------------
  # 3. CloudFront VPC Origin & Distribution
  # ------------------------------------------------------------
  CfVpcOrigin:
    Type: AWS::CloudFront::VpcOrigin
    Properties:
      VpcOriginEndpointConfig:
        Name: MyVpcOrigin
        Arn: !Ref PublicAlb
        HTTPPort: 80
        HTTPSPort: 443
        OriginProtocolPolicy: http-only
        OriginSSLProtocols:
          - TLSv1.2

  CfDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        Origins:
          - Id: MyVpcOriginSource
            DomainName: !GetAtt PublicAlb.DNSName
            VpcOriginConfig:
              VpcOriginId: !GetAtt CfVpcOrigin.Id
        DefaultCacheBehavior:
          TargetOriginId: MyVpcOriginSource
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods: [GET, HEAD]
          CachedMethods: [GET, HEAD]
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 

  # ------------------------------------------------------------
  # 4. Security Group Ingress Rule (後から追加)
  # Condition を使い、ID が渡された時だけ作成される
  # ------------------------------------------------------------
  AlbIngressFromVpcOrigin:
    Type: AWS::EC2::SecurityGroupIngress
    Condition: HasVpcOriginSgId
    Properties:
      GroupId: !Ref AlbSecurityGroup           # ALB側
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      SourceSecurityGroupId: !Ref CloudFrontVpcOriginSecurityGroupId # VPC Origin側
      Description: Allow traffic only from CloudFront VPC Origin

デプロイの手順

  1. 初回デプロイ: CloudFrontVpcOriginSecurityGroupId パラメータは空欄のままデプロイします。
    • -> ALB は作成されますが、アクセスはできません。VPC Origin も同時に作成されます。
  2. SG ID 確認: マネジメントコンソールの CloudFront > VPC origins 画面で、作成された VPC Origin の詳細を確認し、「セキュリティグループ ID」 (例: sg-0123...) をコピーします。
  3. スタック更新: CloudFormation スタックを更新し、パラメータに先ほどの SG ID を入力します。
    • -> AlbIngressFromVpcOrigin リソースが作成され、CloudFront からの通信のみが許可されます。

まとめ

CloudFront VPC Origin は、アーキテクチャをシンプルにしつつ、セキュリティレベルを上げてくれる便利な機能です。

  • カスタムヘッダー運用からの解放
  • パブリック ALB でもインターネットからの直接アクセスを遮断可能
  • VPC Origin SG をソースに指定することで、より厳密なアクセス制御を実現

「諸事情で ALB はパブリックにしておく必要があるが、CloudFront 以外からのアクセスは防ぎたい」というケースにおいて、VPC Origin は非常に強力な選択肢になります。構築の際は SG の設定順序に気をつけてください!

参考文献

Discussion