Restrict access to ALB to via CloudFront
In the following page, we introduced a configuration that specifies ALB as the CloudFront origin.
As explained in the page above, direct access to the ALB is still possible even if the ALB is specified as the CloudFront origin.
In this page, we will check how to restrict direct access to the ALB by setting a custom header, referring to the following page.
Environment
The structure is the same as the page introduced at the beginning of this document.
However, we will restrict direct access to the ALB and force it to go through CloudFront.
Configure custom headers for CloudFront and ALB.
Information about custom headers is stored in the Secrets Manager.
CloudFormation template files
The above configuration is built using CloudFormation.
The CloudFormation template is located at the following URL
https://github.com/awstut-an-r/awstut-fa/tree/main/132
Explanation of key points of template files
This page focuses on how to restrict direct access to the ALB.
For basic information on CloudFront, please refer to the following pages.
For basic information on ALB, please refer to the following pages.
(Reference) Secrets Manager
Resources:
Secret:
Type: AWS::SecretsManager::Secret
Properties:
Description: test secret
GenerateSecretString:
ExcludeCharacters: ""
ExcludeLowercase: false
ExcludeNumbers: false
ExcludePunctuation: false
ExcludeUppercase: false
GenerateStringKey: !Ref CustomHeaderValueJsonKey
IncludeSpace: false
PasswordLength: !Ref CustomHeaderValueLength
RequireEachIncludedType: true
SecretStringTemplate: !Sub '{"${CustomHeaderNameJsonKey}": "${CustomHeaderName}", "${CustomHeaderValueJsonKey}": ""}'
KmsKeyId: alias/aws/secretsmanager
Name: !Ref Prefix
Code language: YAML (yaml)
To restrict direct access to ALB, configure CloudFront and ALB regarding custom headers.
The handling of this custom header information is mentioned in the AWS official documentation as follows
If the header name and value are not secret, other HTTP clients could potentially include them in requests that they send directly to the Application Load Balancer. This can cause the Application Load Balancer to behave as though the requests came from CloudFront when they did not. To prevent this, keep the custom header name and value secret.
Restricting access to Application Load Balancers
Follow the above, this time storing custom header information in the Secrets Manager.
The custom header name is fixed as “X-Custom-Header”.
On the other hand, the custom header value is a random password that is automatically generated when the secret is created.
For more information on how to generate a random password using Secrets Maanger, please see the following page.
The secret information to be created this time, in JSON format, is as follows.
{“CustomHeaderName”: “X-Custom-Header”, “CustomHeaderValue”: “[random-string]”}
CloudFront
Resources:
Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
Compress: true
ForwardedValues:
Cookies:
Forward: none
QueryString: false
TargetOriginId: !Ref ALBDNSName
ViewerProtocolPolicy: allow-all
DefaultTTL: !Ref CacheTTL
MaxTTL: !Ref CacheTTL
MinTTL: !Ref CacheTTL
Enabled: true
Origins:
- CustomOriginConfig:
OriginProtocolPolicy: http-only
DomainName: !Ref ALBDNSName
Id: !Ref ALBDNSName
OriginCustomHeaders:
- HeaderName: !Sub "{{resolve:secretsmanager:${Secret}:SecretString:${CustomHeaderNameJsonKey}}}"
HeaderValue: !Sub "{{resolve:secretsmanager:${Secret}:SecretString:${CustomHeaderValueJsonKey}}}"
PriceClass: PriceClass_All
Code language: YAML (yaml)
The key point is the OriginCustomHeaders property.
The header information set here is added to the traffic between CloudFront and ALB.
The custom headers are set by referencing the aforementioned Secrets Manager values.
Specifically, use the dynamic reference in CloudFormation.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html
The HeaderName property refers to the value of CustomHeaderName from the JSON data in the Secrets Manager.
The HeaderValue property refers to the value of CustomHeaderValue from the JSON data in Secrets Manager.
ALB
Resources:
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub "${Prefix}-ALB"
Scheme: internet-facing
SecurityGroups:
- !Ref ALBSecurityGroup
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Type: application
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref VPC
Name: !Sub "${Prefix}-ALBTargetGroup"
Protocol: HTTP
Port: !Ref HTTPPort
HealthCheckProtocol: HTTP
HealthCheckPath: /
HealthCheckPort: traffic-port
HealthyThresholdCount: !Ref HealthyThresholdCount
UnhealthyThresholdCount: !Ref UnhealthyThresholdCount
HealthCheckTimeoutSeconds: !Ref HealthCheckTimeoutSeconds
HealthCheckIntervalSeconds: !Ref HealthCheckIntervalSeconds
Matcher:
HttpCode: !Ref HttpCode
Targets:
- Id: !Ref Instance1
- Id: !Ref Instance2
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- FixedResponseConfig:
ContentType: text/plain
MessageBody: Access denied
StatusCode: 403
Type: fixed-response
LoadBalancerArn: !Ref ALB
Port: !Ref HTTPPort
Protocol: HTTP
ALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- TargetGroupArn: !Ref ALBTargetGroup
Type: forward
Conditions:
- Field: http-header
HttpHeaderConfig:
HttpHeaderName: !Sub "{{resolve:secretsmanager:${Secret}:SecretString:${CustomHeaderNameJsonKey}}}"
Values:
- !Sub "{{resolve:secretsmanager:${Secret}:SecretString:${CustomHeaderValueJsonKey}}}"
ListenerArn: !Ref ALBListener
Priority: 1
Code language: YAML (yaml)
There are two points.
The first point is the default action of the ALB listener.
In the configuration of the page introduced in the introduction, the default action was set to route to the ALB target group.
The default action in this configuration will be changed to return HTTP status code 403.
The second point is to add a listener rule.
Add a rule to route to the ALB target group.
In the Condition property, set the condition under which this rule is applied.
This condition is based on header information.
Incoming HTTP requests will be routed to the target group only if they have custom headers configured in CloudFront.
As for the custom header information to be checked, it refers to the values in Secrets Manager as well as CloudFront.
To summarize the two points, HTTP requests with custom headers configured in CloudFront will be routed to the ALB target group, while all others will return 403.
Architecting
Using CloudFormation, we will build this environment and check the actual behavior
Create CloudFormation stacks and check the resources in the stacks
Create CloudFormation stacks.
Please refer to the following pages for information on how to create stacks and check each stack
After reviewing the resources in each stack, information on the main resources created in this case is as follows
- Secrets Manager: fa-132
- Instance 1: i-09f7807d448e1b62e
- Instance 2: i-0e734e5fa1b1da141
- DNS name of ALB: fa-132-alb-1742616783.ap-northeast-1.elb.amazonaws.com
- DNS name of CloudFront distribution: dq15nc7eow2lq.cloudfront.net
The AWS Management Console also checks the creation status of each resource.
Check Secrets Manager.
The secret is successfully created.
The stored values show that the header name and header value are stored.
In particular, the header value is automatically set to a random string.
Check the ALB.
The ALB is successfully created.
If you look at the target group of the ALB, you will see that two instances have been registered.
Check the listener rules for this ALB.
Two rules are configured.
The first rule concerns routing to the target group.
Only if the value of the custom header matches will it be routed to the target group.
This setting is based on the values stored in the Secrets Manager described above.
The last rule is for traffic that did not meet the above rules.
For example, if you access the ALB directly, the ALB will return a 403 code because the traffic does not have a custom header setting.
Check CloudFront.
The CloudFront distribution has been successfully created.
The aforementioned ALB is specified as the origin of the distribution.
Check the settings for this origin.
If you look at the section on custom headers, you will see a setting similar to the ALB listener rule.
This means that traffic going through this CloudFront distribution will be given this custom header.
Operation Check
Now that you are ready, access CloudFront.
$ curl https://dq15nc7eow2lq.cloudfront.net
instance-id: i-09f7807d448e1b62e
$ curl https://dq15nc7eow2lq.cloudfront.net
instance-id: i-0e734e5fa1b1da141
Code language: Bash (bash)
Response.
We were able to access the two instances under the ALB.
When accessing via CloudFront, I was able to successfully access the instances under the ALB because a custom header is set on the way.
Then access the ALB directly.
$ curl http://fa-132-ALB-1742616783.ap-northeast-1.elb.amazonaws.com
Access denied
Code language: Shell Session (shell)
Access denied.
A direct access to the ALB returned a 403 in the ALB because the custom header was not set.
Summary
We have identified a way to limit direct access to the ALB by setting a custom header.