Question

I want to let my clients speak directly with ElasticSearch REST API, obviously preventing them from performing any data or configuration change.

I had a look at ElasticSearch REST interface and I noticed the pattern: HTTP GET requests are pretty safe (harmless queries and status of cluster).

So I thought I can use Cloudfront as a CDN/Proxy that only allows GET/HEAD methods (you can impose such restrict it in the main configuration).

So far so good, all is set up. But things don't work because I would need to open my EC2 security group to the world in order to be reachable from Cloudfront! I don't want this, really!

When I use EC2 with RDS, I can simply allow access to my EC2 security group in RDS security groups. Why can't I do this with CloudFront? Or can I?

Ideas?

edit: It's not documented, but ES accepts facets query, which involve a (JSON) body, not only with POST, but also with GET. This simply breaks HTTP recommendation (as for RFC3616) by not ignoring the body for GET request (source). This relates because, as pointed out, exposing ES REST interface directly can lead to easy DOS attacks using complex queries. I'm still convinced though, having one less proxy is still worth it.

edit: Other option for me would be to skip CloudFront and adding a security layer as an ElasticSearch plugin as shown here

Was it helpful?

Solution

I ended coding with my own plugin. Surprisingly there was nothing quite like this around. No proxies, no Jetty, no Tomcat.

Just a the original ES rest module and my RestFilter. Using a minimum of reflection to obtain the remote address of the requests.

enjoy:

https://github.com/sscarduzio/elasticsearch-readonlyrest-plugin

OTHER TIPS

Note that even a GET request can be harmful in Elasticsearch. A query which simply takes up too much resources to compute will bring down your cluster. Facets are a good way to do this.

I'd recommend writing a simple REST API you place in front of ES so you get much more control over what hits your search cluster. If that's not an option you could consider running Nginx on your ES boxes to act as a local reverse proxy, which will give you the same control (and a whole lot more) as CloudFront does. Then you'd only have to open up Nginx to the world, instead of ES.

A way to do this in AWS would be:

  • Set up an Application Load Balancer in front of your ES cluster. Create a TLS cert for the ALB and serve https. Open the ES security group to the ALB.

  • Set up CloudFront and use the ALB as origin. Pass a custom header with a secret value (for WAF, see next point).

  • Set up WAF on your ALB to only allow requests that contain the custom header with the secret value. Now all requests have to go through CloudFront.

  • Set up a Lambda@Edge function on your CloudFront distribution to either remove the body from GET requests, or DENY such requests.

It’s quite some work, but there’s advantages over the plugin, e.g.:

  • CloudFront comes with free network DDOS protection

  • CloudFront gives your users lower latency to ES because of the fast CloudFront network and global PoP’s.

  • Opens many options to use CloudFront, WAF and Lamba@Edge to further protect your ES cluster.

I’m working on sample code in CDK to set all of this up. Will report back when that’s ready.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top