I ran into an unexpected issue when configuring multi domain SSL support for Timelapse. The servers are running behind a load balancer (ELB) and it appears you can only bind a single SSL certificate to an ELB.
Edit: on Oct. 10, 2017, Amazon announced support for multiple certificates on an Application Load Balancer using SNI. The method described below still applies if you’re using a Classic Load Balancer or want to avoid limitations on the number of certificates per load balancer (25).
Apparently, I’m not the only one struggling with this. Amazon Forums and Stack Overflow are full of people asking the same question — “how can I bind multiple SSL certs to a single AWS ELB?” — with posts dating from as far as 2010.
Amazon’s official answer on this is “you can’t, but we do have this in as a feature request, although we can’t comment on when/if this will be supported in the future”.
So you end up with pretty much three options:
Use a multi-domain (SAN) certificate. A good solution if you own all the domains for which you need SSL encryption. A bad solution if you’re let’s say building a SaaS product and SSL is required for your customers’ custom domains. You can’t add a new domain to an existing certificate, so you have to create a new certificate with the revised list of domains and every time you do so, ownership of every domain has to be validated again. Annoying and it doesn’t make much sense to have customers share a single certificate.
Create a new ELB for every additional certificate. Can’t assign more than one certificate to a single load balancer? Create more load balancers! An interesting solution if you need just one extra certificate, but at roughly 18$/month per ELB, it’s a costly solution. Plus, it doesn’t make much sense to end up with more load balancers than actual servers just to handle SSL certificates.
Configure TCP Passthrough on the ELB and let the EC2 instance(s) handle SSL termination. Not a bad solution on a single instance setup (but then why do you need a load balancer?). The problem with this solution is that all EC2 instances have to be kept in sync, so that all certificates can be found on all instances at all time. There are a few solutions to do this, one being to use S3 as your certificates central repository and then making sure the certificates are being synchronized on the instances. You also have to make sure that when a new instance is created, the certificates are downloaded on the instance.
Or, maybe there’s a fourth option. It’s a solution I haven’t seen documented anywhere and it’s actually quite easy and cost effective: use CloudFront as a proxy.
Using CloudFront as a Proxy
CloudFront supports SSL, multiple distributions with no fixed costs and a distribution can be bound to many Amazon services, including an Elastic Load Balancer. Pretty much sounds like what we’re after.
CloudFront’s pricing is based on usage (data out + number of requests), so depending on the distribution’s traffic, it is in many cases a more affordable solution than popping up multiple ELBs.
While CloudFront is mainly used as a Content Delivery Network, it can also be used as a simple proxy. Here’s how you do it.
Create a new CloudFront Web Distribution
When asked for Origin Domain Name, you have two choices: select your ELB’s domain from the list or enter a custom domain name. I strongly suggest a custom domain name, the one used by your app (app.yourapp.com, my.yourapp.com). The distribution will point to your setup (Beanstalk in my case), without being tied to your specific load balancer. So whatever happens to that load balancer in the future, the distribution will keep working. A custom domain is also the only way to ensure traffic is encrypted between CloudFront and the ELB (more on this later).
Origin Protocol Policy. This is for the communication between CloudFront and the origin (ELB/app in our case). If the distribution is linked to your ELB directly, select HTTP only. You won’t be able to have encrypted traffic between CloudFront and the ELB because you can’t issue a certificate for the load balancer’s domain. It will still work, but my preferred solution is to use HTTPS only with a custom distribution (see point #2).
Viewer Protocol Policy. Redirect HTTP to HTTPS.
Allowed HTTP Methods. Select all HTTP allowed method, as this is a proxy, you want CloudFront to forward everything to your app.
Forward Headers. Pick All. This is where you get your proxy.
Also forward all cookies and query strings.
Compress Objects Automatically. This is a matter of preference, but enabling gzip on CloudFront will offload this task from your EC2 instances and reduce your CloudFront bill as it is based on outgoing data.
Alternate Domain Names (CNAMEs). Enter the domain(s) you need SSL Certificates / Encryption for.
SSL Certificate. Pick an existing certificate or request a new one.
Then rest is pretty much standard. Change the domain(s) DNS for the CloudFront’s domain name and you’re all done!
Create a new distribution for any other custom domain you need another certificate for. While this may not be the best solution for thousands of domains, it’s definitely the easiest workaround. Hopefully Amazon will eventually support multiple SSL certificates on a single ELB.