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.
Related
I have a site running on ECS in AWS with the DNS being handled by Route53.
There are two network interfaces which I can use the Public IP addresses of to access the site. The problem is Google has indexed this IP addresses as well as the domain name.
How do I "redirect" the IP addresses to the domain name?
There are 2 aspects in your question:
You want your access using IP to resolve to your domain name
Google indexing your site with the wrong URL
For your 1st challenge, there isn't a native way to force browsers from IP back to URLs. You have to handle this in your application or infrastructure. Since you're running in ECS, that could either be a rule in a Load Balancer (e.g. if using Application Load Balancer, a rule that checks if Host-Header doesn't match your domain then redirect to your domain URL), or you could write the logic into your container.
Regarding the #2 problem, that might be because your IP was added to some site in the internet (remember that IPs are re-used in AWS), and as a result Google has the IP in the cache and it's refreshing it. To handle this problem, you might need to review if it's possible using Google's Removals Tool.
I would like to block all access to our demo site for some time with the exception of a group of listed IPs & IP ranges. Could you advise on how to do this?
I am hosting on digital ocean and I have cloudflare activated.
Thanks for the help anyways
This should be straightforward to achieve:
Ensure that the DNS record for your demo site is proxied by Cloudflare. You can check this in the Cloudflare Dashboard for your domain, in the DNS tab.
Add a Firewall Rule that will block all the traffic for your demo site unless it is coming from a list of allowed IP addresses. For example, using an expression such as:
(not ip.src in {192.0.2.1 192.0.2.2 192.0.2.3} and http.host eq "demo.example.com")
You can also define an IP List and reference to it in your Firewall rule - this would make it easier to maintain the list over time in a single place.
You can then turn the rule ON and OFF when needed.
There are two IP addresses (172.31.42.243 and 172.31.19.188) that are hitting my site in AWS (Beanstalk hosted) repeatedly with garbage requests.
I have tried to block them using ACL deny rules. If I add only one I'm still able to hit my site. If I add both then I'm not able to hit my site.
Both the deny rules are using /32 for the CIDR notation and the deny rules I added are lower numbers than the default allow all rule. What am I doing wrong?
Those aren't the IP addresses of the machines that are attacking you; those are the internal IP addresses of your ELB. That's also why you can't access your site after blocking them - no one can!
You need to look at the access logs on the ELB, not on your application server, to determine which IP address(es) are attacking you. You can configure access logging on your ELB on the "Description" tab, under "Attributes".
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/
I need to use AWS WAF for my web application hosted on AWS to provide additional rule based security to it. I couldnt find any way to directly use WAF with ELB and WAF needs Cloudfront to add WEB ACL to block actions based on rules.
So, I added my Application ELB CNAME to cloudfront, only the domain name, WebACL with an IP block rule and HTTPS protocol was updated with cloudfront. Rest all has been left default. once both WAF and Cloudfront with ELB CNAME was added, i tried to access the CNAME ELB from one of the ip address that is in the block ip rule in WAF. I am still able to access my web application from that IP address. Also, I tried to check cloudwatch metrics for Web ACL created and I see its not even being hit.
First, is there any good way to achieve what I am doing and second, is there a specific way to add ELB CNAME on cloudfront.
Thanks and Regards,
Jay
Service update: The orignal, extended answer below was correct at the time it was written, but is now primarily applicable to Classic ELB, because -- as of 2016-12-07 -- Application Load Balancers (elbv2) can now be directly integrated with Web Application Firewall (Amazon WAF).
Starting [2016-12-07] AWS WAF (Web Application Firewall) is available on the Application Load Balancer (ALB). You can now use AWS WAF directly on Application Load Balancers (both internal and external) in a VPC, to protect your websites and web services. With this launch customers can now use AWS WAF on both Amazon CloudFront and Application Load Balancer.
https://aws.amazon.com/about-aws/whats-new/2016/12/AWS-WAF-now-available-on-Application-Load-Balancer/
It seems like you do need some clarification on how these pieces fit together.
So let's say your actual site that you want to secure is app.example.com.
It sounds as if you have a CNAME elb.example.com pointing to the assigned hostname of the ELB, which is something like example-123456789.us-west-2.elb.amazonaws.com. If you access either of these hostnames, you're connecting directly to the ELB -- regardless of what's configured in CloudFront or WAF. These machines are still accessible over the Internet.
The trick here is to route the traffic to CloudFront, where it can be firewalled by WAF, which means a couple of additional things have to happen: first, this means an additional hostname is needed, so you configure app.example.com in DNS as a CNAME (or Alias, if you're using Route 53) pointing to the dxxxexample.cloudfront.net hostname assigned to your distribution.
You can also access your sitr using the assigned CloudFront hostname, directly, for testing. Accessing this endpoint from the blocked IP address should indeed result in the request being denied, now.
So, the CloudFront endpoint is where you need to send your traffic -- not directly to the ELB.
Doesn't that leave your ELB still exposed?
Yes, it does... so the next step is to plug that hole.
If you're using a custom origin, you can use custom headers to prevent users from bypassing CloudFront and requesting content directly from your origin.
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/forward-custom-headers.html
The idea here is that you will establish a secret value known only to your servers and CloudFront. CloudFront will send this in the headers along with every request, and your servers will require that value to be present or else they will play dumb and throw an error -- such as 503 Service Unavailable or 403 Forbidden or even 404 Not Found.
So, you make up a header name, like X-My-CloudFront-Secret-String and a random string, like o+mJeNieamgKKS0Uu0A1Fqk7sOqa6Mlc3 and configure this as a Custom Origin Header in CloudFront. The values shown here are arbitrary examples -- this can be anything.
Then configure your application web server to deny any request where this header and the matching value are not present -- because this is how you know the request came from your specific CloudFront distribution. Anything else (other than ELB health checks, for which you need to make an exception) is not from your CloudFront distribution, and is therefore unauthorized by definition, so your server needs to deny it with an error, but without explaining too much in the error message.
This header and its expected value remains a secret because it will not be sent back to the browser by CloudFront -- it's only sent in the forward direction, in the requests that CloudFront sends to your ELB.
Note that you should get an SSL cert for your ELB (for the elb.example.com hostname) and configure CloudFront to forward all requests to your ELB using HTTPS. The likelihood of interception of traffic between CloudFront and ELB is low, but this is a protection you should consider implenting.
You can optionally also reduce (but not eliminate) most unauthorized access by blocking all requests that don't arrive from CloudFront by only allowing the CloudFront IP address ranges in the ELB security group -- the CloudFront address ranges are documented (search the JSON for blocks designated as CLOUDFRONT, and allow only these in the ELB security group) but note that if you do this, you still need to set up the custom origin header configuration, discussed above, because if you only block at the IP level, you're still technically allowing anybody's CloudFront distribution to access your ELB. Your CloudFront distribution shares IP addresses in a pool with other CloudFront distribution, so the fact that the request arrives from CloudFront is not a sufficient guarantee that it is from your CloudFront distribution. Note also that you need to sign up for change notifications so that if new address ranges are added to CloudFront, then you'll know to add them to your security group.