AWS Cloudfront and ELB Security Groups - amazon-web-services

does anyone know how to add your cloudfront distro into the security group inbound rules for an ELB?
We have a cloudfront distro setup for a new site which has a whitelisted set of security group rules for its origin. I cant see how to configure the security group to allow requests from the cloudfront distro...
any ideas?

If you follow the link provided by Amir Mehler in the comments above, the author of the blog points to an official AWS Lambda function on Github that will update a security group with the CloudFront IPs. I used this, and it works great.
If you don't like Lambda, you could do it manually.
Note When trying to use the sample test config for the first time, update the MD5 to match the hash of the current ip-ranges.json file, or it will error.

Beginning February 2022, you should use AWS Managed Prefix List. They are a list of IPs managed by AWS, and kept up to date by them, that you can use in your route tables and security groups.
Be advised, the Amazon CloudFront managed prefix list counts as 55 rules in a security group. The default quota is 60 rules, leaving room for only 5 additional rules in a security group. You should request a quota increase for this quota. It counts as 55 routes in a route table. The default quota is 50 routes, so you must request a quota increase before you can add the prefix list to a route table.

When you say "add origin" in Cloudfront distribution and click the "Origin Domain Name" box, it lists all your AWS resources from this account (including ELB). You can just choose it.
However the security group associated with your ELB should allow public access (HTTP/HTTPS, 0.0.0.0/0). This is not any less safe, since anyway you want public to access the ELB via cloudfront. Moment you make things available via a CDN, it is for public access. I have been configuring the ELB security groups this way. Open to other suggestions !
Now for the security group of the EC2s behind the ELB: Here you should not allow public access. Instead allow only access from ELB's security group (you can achive this by selecting the ELB security group from the list, instead of keying in an Inbound IP address.
Now, this can be configured little differently if the origin is S3. Here you need not make the bucket public. Instead restrict access to the bucket using Bucket policy (not any security groups here) allowing only IAM origin access identity. More info here - Serving Private Content through CloudFront - Amazon CloudFront

I have solved it with the help of this post: Automatically update security groups for Amazon CloudFront IP ranges using AWS Lambda
It is a step-by-step tutorial, very detailed, a bit outdated already but you won't get lost.
Only drawback is that each time the Lambda function creates a new security group you will have to attach it to your EC2 instance or ELB manually. Maybe this can also be solved in an efficient way automatically, but the blog post doesn't mention it.

This is more of a question than an Answer but embedded in it is how I would do it:
Step 1: Get data from here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/LocationsOfEdgeServers.html -> http://d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips
Step 2: Create Security group with data...
Question part:
Why the hell does the AWS API has a limit on the number of rules that is LESS than the number of endpoints of its services...
This is what happens when you call the API(which is how I ended up here):
HTTP: 400 ->

Related

Allowing custom domain names for tenants on multi tenant SaaS platform hosted on AWS

Overview
I am building a multi tenant SaaS platform on AWS. The deployment architecture for now is very simple.
I have a ELB (all traffic falls here)
I have few EC2 instances (hosting my java + angular application). This is under a Autoscaling group which is linked to the ELB.
I have a MySQL cluster in RDS which the EC2 talks to.
Everything is in a VPC and the EC2 and RDS are in private subnets,
Problem
I would prefer my clients (tenants) to have their own domain name. For e.g. Let's say that right now my application is accessible at the URL http://xyz-elb.amazon.aws.com
I would like to enable my clients to access the application using
http://tenant1.com
http://tenant2.com
and so one. I know this is possible because I have seen this in different multi tenant SaaS applications.
Research I have done till now:
I read about reverse proxy which can accomplish it however, I didn't get any reliable link. Tried reading about HAProxy but I feel that I am going in the wrong direction.
What I need
Expert opinion on different ways to do domain mapping
List & Link of resources which address this particular problem
Any practical experience or case studies by any of you cool guys here
A cheap solution (I don't want to go via Amazon API Gateway / Route53 Policies which doesn't seem to fit my need)
Thank you so much for reading my question. And thanks in advance for your efforts to reply on this.
Just create a DNS CNAME resource record in each tenant domain, pointing to your apps ELB DNS name.
I prefer to alias the ELB DNS name to my service domain, then alias the tenant domain to it. Example:
app.example.com IN CNAME my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com
Then in my tenant DNS:
app.tenant1.com IN CNAME app.example.com.
This aliasing allows you to change your ELB endpoint if required, without having to change the DNS records for all clients.

AWS architecture - Web application (Diagram)

I am in the process of setting up a medium sized AWS infrastructure for a web project. I may be overthinking a couple things and therefore wanted to ask the community for opinions. Any input is appreciated.
Please see the graphic:
here
Explanation (from left to right):
My domain is hosted on GoDaddy and will simply route to Cloudfront in order to globally cache static content.
Cloudfront will point to Route53 which is responsible for routing the user to the closest region based on Geoprximity and/or Latency
Each region will have an availability load balancer pointing to an EC2 instance (different availability zones for disasters fallback)
From there, each EC2 instance writes to a single MySQL database. Static content is loaded from a S3 bucket.
This MySQL database replicates/synchronizes itself across availability zones and regions and creates read-replicas
If an EC2 instance has a read-request, it contacts another Route53 router that forwards the read-request to a load-balancer (in each region) based on where the request is coming from (geoproximity/latency). The only alternative I see here would be to directly point read-requests from a European EC2 instance to a European load-balancer. (vice versa for US)
The load-balancer in each region will then decide from which database to read based on health or amount of requests
Each EC2 instance can also trigger a LAMBDA function through an API Gateway.
What am I missing? Is this too much? What are the ups and downs of this construct?
Thank you all so much!
Things look reasonable up to step 6. There's no need to find the nearest MySQL database, because your instances already know where it is -- it's the one in the local region.
Step 7 is problematic because ELBs can't be used to balance RDS instances. However, with Aurora/MySQL, you get a single cluster "reader" endpoint hostname with a short TTL that load balances across your Aurora replicas. If a replica dies, it's removed from DNS automatically.
Step 8 it's not strictly necessary to use API Gateway -- instances can directly invoke Lambda functions through the Lambda API.
Additionally, there's Lambda#Edge that allows triggering Lambda functions directly from CloudFront -- although if the Lambda function you need is large in size (dependencies) or needs to run inside a VPC, you have to cascade two of them -- the edge function (not in VPC) invokes the regional function (large, or in a VPC) -- but this is still typically less expensive than API Gateway. Edge functions automatically replicate globally and run in the region closest to the CloudFront edge handling the individual request, and within any given function invocation this can be identified by inspecting process.env.AWS_REGION. Edge functions can also be used to change the origin serving the content -- so, e.g. if your function sees that it's been invoked in an EU region, it can rewrite the request so that CloudFront will send S3 requests to an EU bucket.
If your site is at the apex of a domain, e.g. example.com rather than, say, www.example.com, your domain will need to be hosted in Route 53, not Go Daddy, because constraints in the DNS standards do not allow the dynamic behavior required by CloudFront at the apex. You can still have your domain registered with them, but not hosted by them.

Are route53 failover policy records only useful for non aws alias-able resources?

If all my endpoints are AWS services like ELB or S3 "Evaluate Target Health" can be used instead of failover records correct? I can use multiple weighted, geo, or latency records and if I enabled "Evaluate Target Health" it also servers the purpose of failover if one of the resources a record is pointing to is not healthly route53 will not send traffic to it.
The only use I see for failover records with custom healthchecks is for non-aws resources OR if maybe you have a more complex decision you want DNS to make instead of just ELB/S3/etc service health.
EDIT: so it seems while I can get active-active with "Evaluate Target Health" (on alias endpoints) if I want active-passive I have to use a failover policy- is this correct?
Essentially, yes. Evaluating target health makes the records viable candidates for generating responses, only when healthy. Without a failover policy, they're all viable when they're all healthy.
If you do something like latency-based routing and you had two targets, let's say Ohio and London, then you'd essentially have a dual active/passive configuration with reversed roles -- Ohio active and London passive for viewers in North America, and the roles reversed for viewers in Europe. But if you want global active/passive, you'd need a a failover policy.
Note that if you are configuring any kind of high-availability design using Route 53 and target health, your best bet is to do all of this behind CloudFront -- where the viewer always connects to CloudFront and CloudFront does the DNS lookup against Route 53 to find the correct origin based on whatever rules you've created. The reason for this is that CloudFront always respects the DNS TTL values. Browsers, for performance reasons, do not. Your viewers can find themselves stuck with DNS records for a dead target because their browsers don't flush their cached DNS lookups until all tabs in all windows are closed. For users like me, that almost never happens.
This also works with latency-based routes in Route 53 behind CloudFront, because CloudFront has already routed the viewer to its optimal edge, and when that edge does a lookup on a latency-based route in Route 53, it receives the answer that has the lowest latency from the CloudFront edge that's handling the request... so both viewer to CloudFront and CloudFront to origin routes are thus optimal.
Note also that failover routing to S3 with only DNS is not possible, because S3 expects the hostname to match the bucket name, and bucket names are global. An S3 failure is a rare event, but it has happened at least once. When it happened, the impact was limited to a single region, as designed. For a site to survive an S3 regional failure requires additional heroics involving either CloudFront and Lambda#Edge triggers, or EC2-based proxies that can modify the request as needed and send it to the alternate bucket in an alternate region.
Latency-based routing to buckets with Route 53 is also not possible, for the same reason, but can be accomplished by Lambda#Edge origin request triggers. These triggers are aware of the AWS region where a given invocation is running, and thus can swap origin servers based on location.

Setting up a static IP in AWS Security Group while using Cloudflare

I want to set up a security group that will only allow my static IP to hit an EC2 server, but because I use Cloudflare and it uses some IP/reverse proxy magic, the AWS security group only sees the Cloudflare IP and not my actual static IP. I looked all over and can't find any answer to how to do this. Has anyone figured this out?
You can't do this with security group settings.
Caching proxies like Cloudflare create a separate connection to the origin server, using one of their IP addresses.
You'd need to use those addresses in your security group, and maintain the rules if Cloudflare changes the list. But, the list is public.
https://www.cloudflare.com/ips/
Next, you need to configure your web server to deny requests when the CF-Connecting-IP HTTP header doesn't contain your IP address.
https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
You have to do both, because if you don't restrict traffic to the Cloudflare IP address ranges with your security group, then traffic from elsewhere (not via Cloudflare) could forge the header containing the IP address.
Having done those two things, you have almost accomplished your objective, but not quite... because Cloudflare caches responses.
So, the next problem is, you also need to disable caching at Cloudflare, because once you fetch a page, it may be in the Cloudflare cache, where someone else might access it.
https://support.cloudflare.com/hc/en-us/articles/200168306-Is-there-a-tutorial-for-PageRules-
Really, for a case where you need to restrict access to a single IP (or a small set), it doesn't usually make a lot of sense to send the traffic through Cloudflare.
You could setup a lambda function that parse the IP file from Cloudflare and update your security groups dynamically. AWSlabs on github has an example lambda function that does it for CloudFront. Two problems you'll run into are that you'll have to schedule to lambda function to run since you can't subscribe to a queue like in the example and possible running out security group rules, 50 is the limit.

AWS CloudFront and ELB: Is there a way to force connection using only CloudFront?

I have an ELB which balances some EC2 instances.
The ELB exposes the endpoints of the entire system.
Now I am creating a CloudFront distribution over this ELB.
Is there a way to allow users to connect ONLY using CloudFront endpoint and refuse direct connections to ELB?
Thanks
You would have to restrict the security group to the list of IP address ranges used by CloudFront. This is a subset of the list published here.
Unfortunately that list is subject to change, so you can't just set it once and forget it. Amazon has published a tutorial here that walks you through setting up a Lambda function that will automatically update your security group when Amazon publishes an updated IP list.
Unfortunately there is no straight forward way to do that right now.
ELB access can only be limited by IP ranges. You could try to limit the ELB to CloudFront's IP ranges, but this is rather brittle and changes frequently. If a new IP range is introduced, you may end up accidentally blocking CloudFront. I would say that this approach is not advisable, but I've seen it done when the requirement was mandatory. And it did break a few times.
You can set up a automated security group that only allows Cloudfront IP's and let a Lambda function to update it when Cloudfront IP ranges change. On my blog post, you can find a complete Cloudformation template that will set this up for you:
https://medium.com/cagataygurturk/restricting-elb-access-to-cloudfront-8b0990dea69f
If there is no record in R53 that uses your Load Balancer, and only cloudfront defines Alternate Domain Names (CNAMEs) used by your Load Balancer, then you can associate a WAF ACL with your Load Balancer that drops any request that does not match the Alternate Domain Names.
In that case, you force using the CloudFront Distribution for your Load Balancer.
AWS blogs have a solution for this scenario.
What it does is basically creating a lambda function that subscribes to a SNS topic which receives notifications for AWS IP address range changes (this topic is owned by AWS). This lambda then updates the ELB/ALB security group dynamically. Lambda code is available here.
Starting 2022 AWS finally provides a solution for this problem with managed prefix-lists.
You can create an inbound security rule and under source directly specify the prefix list, instead of manually providing IP-Addresses:
To make your server reachable only from Cloudfront Servers follow these steps:
Go to https://console.aws.amazon.com/vpc/home#ManagedPrefixLists
Choose your region (The region of your Load Balancer) and search for "com.amazonaws.global.cloudfront.origin-facing" and copy the id (e.g. "pl-a3a144ca" for europe-central-1)
Edit your security group for the Load-Balancer and add a new Entry with Type: HTTP and as source paste the prefix-list-id from step 2
Now your security group will automatically always use the current IP-Addresses from Cloudfront, now updating necessary. - A caveat: The prefix list counts as ~50 rules against the rules-limit for a security group. If you have a lot of other custom rules, you will likely have to create a second security group with the other rules if this one is full.
As of February 2022 there is a simpler solution. AWS now manages a prefix list for Cloudfront which auto updates.
For details: https://aws.amazon.com/about-aws/whats-new/2022/02/amazon-cloudfront-managed-prefix-list/