この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
今までALBの前にCloudFrontを置いた設定を行ったことが無かったのでやってみました。
やり方
CloudFront + S3の場合はOAIやOACという設定をCloudFrontで行い、S3バケットポリシーを設定して制限できました。
ALBの場合は以下の方法があります。
- CloudFrontでカスタムヘッダーをつけてALBで制限する方法
- セキュリティグループにCloudFrontのマネージドプレフィックスを設定する方法
CloudFrontでカスタムヘッダーをつけてALBで制限する方法
まずはCloudFrontとALBを作成していきます。
VPCとパブリックサブネットは作成済みのものを使用しています。
今回はCloudFormationを使用してCloudFront、ALB、EC2を作成してみました。
AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFront ALB EC2
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
VolumeSize:
Default: 8
Type: Number
Ec21InstanceType:
Default: t2.micro
Type: String
Vpcid:
Type: AWS::EC2::VPC::Id
Description: Enter VPC ID
PublicSubnet1:
Type: AWS::EC2::Subnet::Id
Description: Enter Subnet ID
PublicSubnet2:
Type: AWS::EC2::Subnet::Id
Description: Enter Subnet ID
Resources:
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------#
Ec2SsmRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
RoleName: EC2SsmRole
Ec2IamInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: Ec2InstanceProfile
Roles:
- !Ref Ec2SsmRole
# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------#
AlbSg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for ALB
GroupName: alb-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
FromPort: 80
IpProtocol: tcp
ToPort: 80
Tags:
- Key: Name
Value: alb-sg
VpcId: !Ref Vpcid
Ec2Sg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for EC2
GroupName: ec2-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- FromPort: 80
IpProtocol: tcp
ToPort: 80
SourceSecurityGroupId: !Ref AlbSg
Tags:
- Key: Name
Value: ec2-sg
VpcId: !Ref Vpcid
# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------#
Ec2:
Type: AWS::EC2::Instance
Properties:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
DeleteOnTermination: true
Encrypted: true
Iops: 3000
VolumeSize: !Ref VolumeSize
VolumeType: gp3
IamInstanceProfile: !Ref Ec2IamInstanceProfile
ImageId: ami-0f36dcfcc94112ea1
InstanceType: !Ref Ec21InstanceType
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeleteOnTermination: true
DeviceIndex: 0
GroupSet:
- !Ref Ec2Sg
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: ec2
UserData: !Base64 |
#!/bin/bash
yum update -y
yum upgrade -y
yum install httpd -y
systemctl enable httpd
systemctl start httpd
touch /var/www/html/index.html
echo "CloudFront -> ALB -> EC2" > /var/www/html/index.html
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: alb
Scheme: internet-facing
SecurityGroups:
- !Ref AlbSg
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Tags:
- Key: Name
Value: alb
Type: application
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckPort: 80
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 5
IpAddressType: ipv4
Matcher:
HttpCode: 200
Name: targetgroup
Port: 80
Protocol: HTTP
ProtocolVersion: HTTP1
Tags:
- Key: Name
Value: targetgroup
Targets:
- Id: !Ref Ec2
Port: 80
TargetType: instance
UnhealthyThresholdCount: 2
VpcId: !Ref Vpcid
AlbListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- FixedResponseConfig:
ContentType: text/plain
MessageBody: Access denied
StatusCode: 403
Type: fixed-response
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
AlbListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
Conditions:
- Field: http-header
HttpHeaderConfig:
HttpHeaderName: Custom-Header
Values:
- kobayashi-riku0226
ListenerArn: !Ref AlbListener
Priority: 1
# ------------------------------------------------------------#
# CloudFront
# ------------------------------------------------------------#
CloudFront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
TargetOriginId: Alb
ViewerProtocolPolicy: allow-all
Enabled: True
HttpVersion: http1.1
Origins:
- CustomOriginConfig:
HTTPPort: 80
OriginProtocolPolicy: http-only
DomainName: !GetAtt Alb.DNSName
Id: Alb
OriginCustomHeaders:
- HeaderName: Custom-Header
HeaderValue: kobayashi-riku0226
PriceClass: PriceClass_200
Outputs:
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
CloudFrontDomain:
Value: !GetAtt CloudFront.DomainName
Export:
Name: CloudFrontDomain
ALBDomain:
Value: !GetAtt Alb.DNSName
Export:
Name: AlbDNSName
以下のコマンドを実行してデプロイしていきます。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=Vpcid,ParameterValue=作成済みのVPC ID ParameterKey=PublicSubnet1,ParameterValue=1つめのパブリックサブネットのID ParameterKey=PublicSubnet2,ParameterValue=2つめのパブリックサブネットのID --capabilities CAPABILITY_NAMED_IAM
テンプレートの説明
CloudFrontでカスタムヘッダーを設定するには229行目にあるOriginsの中で設定します。
以下のようにヘッダー名と値を設定しています。
今回は「Custom-Header」というヘッダー名で値に「kobayashi-riku0226」というものを入れています。
Origins:
- CustomOriginConfig:
HTTPPort: 80
OriginProtocolPolicy: http-only
DomainName: !GetAtt Alb.DNSName
Id: Alb
OriginCustomHeaders:
- HeaderName: Custom-Header
HeaderValue: kobayashi-riku0226
ALBでのアクセス制限は195行目にあるリスナールールのConditionsで設定します。
また、固定レスポンスとして403を返すためにDefaultActionsで設定をしています。
AlbListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- FixedResponseConfig:
ContentType: text/plain
MessageBody: Access denied
StatusCode: 403
Type: fixed-response
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
AlbListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
Conditions:
- Field: http-header
HttpHeaderConfig:
HttpHeaderName: Custom-Header
Values:
- kobayashi-riku0226
動作確認
スタックの作成が完了したら以下のコマンドを実行してALBのDNS名とCloudFrontのDNS名を確認します。
aws cloudformation describe-stacks --stack-name スタック名 --query 'Stacks[0].Outputs[*].OutputValue'
実行後、2つの結果が出力されるのでそれぞれブラウザでアクセスしてみます。
以下の結果はALBのDNS名(末尾が「ap-northeast-1.elb.amazonaws.com」になっているもの)にアクセスした結果になります。
結果は画面に「Access denied」と表示されることが確認できます。
curlコマンドを使用してリクエストヘッダーに「Custom-Header: kobayashi-riku0226」指定して実行するとALBで制御されていることがわかります。
curl -H "Custom-Header: kobayashi-riku0226" http://alb-894067198.ap-northeast-1.elb.amazonaws.com -i
次にCloudFrontのDNS名(末尾が「cloudfront.net」になっているもの)にアクセスしてみます。
結果は画面に「CloudFront -> ALB -> EC2」と表示されEC2までアクセスできていることが確認できます。
この設定で気を付けることはカスタムヘッダーを外部に漏らさないようにしなければならないことです。
以下のAWSのドキュメントにも記載されていますが、カスタムヘッダーが外部に漏れた場合にCloudFrontを経由しないでオリジンに直接アクセスできるようになってしまいます。
Application Load Balancers へのアクセスを制限する
このユースケースは、カスタムヘッダー名と値の機密性維持を信頼しています。ヘッダー名と値が機密でない場合、他の HTTP クライアントは、Application Load Balancer に直接送信するリクエストにヘッダー名や値を含める可能性があります。これにより、リクエストをしていない時に、リクエストが CloudFront から送信されたかのように Application Load Balancer を動作させる可能性があります。これを防ぐためには、カスタムヘッダー名と値を機密にしておきます。
セキュリティグループにCloudFrontのマネージドプレフィックスを設定する方法
こちらの方法はALBに設定するセキュリティグループにCloudFrontのマネージドプレフィックスを設定してアクセスを制限する方法になります。
マネージドプレフィックスとは複数のCIDRをリストとして管理するものです。
これを利用することでセキュリティグループやルートテーブルで1つずつルールを設定する手間を省くことができます。
こちらもCloudFormationを使用して設定してみました。
AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFront ALB EC2
Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
VolumeSize:
Default: 8
Type: Number
Ec21InstanceType:
Default: t2.micro
Type: String
Vpcid:
Type: AWS::EC2::VPC::Id
Description: Enter VPC ID
PublicSubnet1:
Type: AWS::EC2::Subnet::Id
Description: Enter Subnet ID
PublicSubnet2:
Type: AWS::EC2::Subnet::Id
Description: Enter Subnet ID
Resources:
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------#
Ec2SsmRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
RoleName: EC2SsmRole
Ec2IamInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: Ec2InstanceProfile
Roles:
- !Ref Ec2SsmRole
# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------#
AlbSg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for ALB
GroupName: alb-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- SourcePrefixListId: pl-58a04531
FromPort: 80
IpProtocol: tcp
ToPort: 80
Tags:
- Key: Name
Value: alb-sg
VpcId: !Ref Vpcid
Ec2Sg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for EC2
GroupName: ec2-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- FromPort: 80
IpProtocol: tcp
ToPort: 80
SourceSecurityGroupId: !Ref AlbSg
Tags:
- Key: Name
Value: ec2-sg
VpcId: !Ref Vpcid
# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------#
Ec2:
Type: AWS::EC2::Instance
Properties:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
DeleteOnTermination: true
Encrypted: true
Iops: 3000
VolumeSize: !Ref VolumeSize
VolumeType: gp3
IamInstanceProfile: !Ref Ec2IamInstanceProfile
ImageId: ami-0f36dcfcc94112ea1
InstanceType: !Ref Ec21InstanceType
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeleteOnTermination: true
DeviceIndex: 0
GroupSet:
- !Ref Ec2Sg
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: ec2
UserData: !Base64 |
#!/bin/bash
yum update -y
yum upgrade -y
yum install httpd -y
systemctl enable httpd
systemctl start httpd
touch /var/www/html/index.html
echo "CloudFront -> ALB -> EC2" > /var/www/html/index.html
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: alb
Scheme: internet-facing
SecurityGroups:
- !Ref AlbSg
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Tags:
- Key: Name
Value: alb
Type: application
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: /
HealthCheckPort: 80
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 5
IpAddressType: ipv4
Matcher:
HttpCode: 200
Name: targetgroup
Port: 80
Protocol: HTTP
ProtocolVersion: HTTP1
Tags:
- Key: Name
Value: targetgroup
Targets:
- Id: !Ref Ec2
Port: 80
TargetType: instance
UnhealthyThresholdCount: 2
VpcId: !Ref Vpcid
AlbListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
# ------------------------------------------------------------#
# CloudFront
# ------------------------------------------------------------#
CloudFront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
TargetOriginId: Alb
ViewerProtocolPolicy: allow-all
Enabled: True
HttpVersion: http1.1
Origins:
- CustomOriginConfig:
HTTPPort: 80
OriginProtocolPolicy: http-only
DomainName: !GetAtt Alb.DNSName
Id: Alb
PriceClass: PriceClass_200
Outputs:
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
CloudFrontDomain:
Value: !GetAtt CloudFront.DomainName
Export:
Name: CloudFrontDomain
ALBDomain:
Value: !GetAtt Alb.DNSName
Export:
Name: AlbDNSName
CloudFrontでカスタムヘッダーをつけてALBで制限する方法と同じコマンドでデプロイ、DNS名の確認をします。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=Vpcid,ParameterValue=作成済みのVPC ID ParameterKey=PublicSubnet1,ParameterValue=1つめのパブリックサブネットのID ParameterKey=PublicSubnet2,ParameterValue=2つめのパブリックサブネットのID --capabilities CAPABILITY_NAMED_IAM
aws cloudformation describe-stacks --stack-name スタック名 --query 'Stacks[0].Outputs[*].OutputValue'
テンプレート説明
一番上のテンプレートとほとんど同じ内容ですが、59行目のALBのセキュリティグループでCloudFrontのマネージドプレフィックスリストを設定しています。
SourcePrefixListIdでマネージドプレフィックスリストのIDを指定します。
AlbSg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for ALB
GroupName: alb-sg
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
FromPort: -1
IpProtocol: -1
ToPort: -1
SecurityGroupIngress:
- SourcePrefixListId: pl-58a04531
FromPort: 80
IpProtocol: tcp
ToPort: 80
Tags:
- Key: Name
Value: alb-sg
VpcId: !Ref Vpcid
動作確認
ALBのDNS名とCloudFrontのDNS名にブラウザからアクセスしてみます。
ALBの場合は以下の画像のようにタイムアウトになりアクセスに失敗します。
CloudFrontの場合は以下のように接続に成功します。
この設定で気を付けることは以下のドキュメントの通りCloudFrontのマネージドプレフィックスは重みが55なので55個分のルールが設定されるのと同等になります。
デフォルトのセキュリティグループのルール数だと60個までしかルールが設定できないので60個以上設定したい場合は上限緩和で対応する必要があります。
AWS マネージドプレフィックスリストのウェイト
さいごに
設定はどちらも難しくはないですが、カスタムヘッダーを漏洩させないように管理するよりはマネージドプレフィックスリストを設定する方が良いのかなとは思いました。