Walking With Certificate Manager
Last week, I talked about my SSL setup with Cloudflare. There were a few things I didn’t love about the setup. It caused some funkiness with my Google OAuth2 authentication redirects but more importantly, it terminated the SSL connection before getting to my application servers. In my ongoing pursuit of experimentation, I decided to get a certificate closer to home.
There were several goals I wanted to achieve with the transition.
- Get an SSL certificate on my servers at AWS (either on the load balancer - ELB or on the EC2 instance itself)
- Release my dependency on Cloudflare
- Maintain https always (http should redirect to https)
- Maintain www redirect to root domain (https://ramekintech.com)
As is typical with any technology transition, there were bumps in the road. I didn’t find everything I needed to get it all working in one place, so I put my pieced together solution here.
Ingredients
- 64bit Amazon Linux 2017.03 v2.4.2 running Ruby 2.3 (Puma)
- Nginx 1.10.3
- Rails 5.1.2
- Cloudflare with flexible SSL
Step 1: Get The Certificate
I decided to use Amazon’s Certificate Manager to acquire the certificate mainly because the site is hosted on the AWS platform. It was super easy to get the free certificate as I already had the correct domain email addresses setup (eg. [email protected], [email protected], etc.) Amazon’s documentation was all I needed for this step.
Step 2: Assign The Certificate
With the certificate in hand, the next step was to get the site to use it. Because I’m using the default installation given to me by Elastic Beanstalk, I have an Elastic Load Balancer (ELB) in front of my EC2 instance. This is fortunate because that is one of only two places an Amazon certificate can be placed (the other being CloudFront - not to be confused with Cloudflare).
This step was also fairly uneventful with the aid of the proper
documentation.
After adding the securelistener.config
to my source tree and deploying via
the simple eb deploy
command, I was ready to test.
Step 3: Shutdown Cloadflare
I removed the domain from Cloudflare which meant I had to make sure all of my domain records and name servers were setup correctly at my registrar (I currently use Hover if you’re curious). The micro steps are as follows:
- Move nameservers from Cloudflare back to Hover
- Set the root
CNAME
record to point to the load balancer at Amazon. I don’t love that I have to use aCNAME
. I would prefer anA
record pointed at my elastic IP but unfortunately, that is not an option. - Set the
MX
records so that email continues to flow. - Set another
CNAME
record to route www to the root ramekintech.com
With all of this in place and about 10 minutes of waiting, I was able to hit http://ramekintech.com as well as https://ramekintech.com and the various forms preceded with www. So far so good. I only needed to ensure https was always used without requiring users to put it in manually. I like the idea of making security as easy as possible.
Step 4: Setup the NGINX Redirects
To get https always, I just needed a few lines in my nginx.conf
file located
in /etc/nginx
. Those lines are:
http {
...
server {
...
if ($http_x_forwarded_proto = 'http') {
return 301 https://ramekintech.com$request_uri;
}
...
}
}
Now, to get www to redirect to the root ramekintech.com, a few more lines in
nginx.conf
are needed:
http {
...
server {
server_name www.ramekintech.com;
return 301 https://ramekintech.com$request_uri;
}
...
}
Don’t forget to restart Nginx once the changes are made.
$ sudo service nginx restart
The second server block, by the way, is in addition to the first server. Let me clarify with the full config:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
index index.html index.htm;
upstream myapp {
server unix:/var/run/puma/my_app.sock;
}
server {
server_name www.ramekintech.com;
return 301 https://ramekintech.com$request_uri;
}
server {
listen 80 ;
listen [::]:80 ;
server_name ramekintech.com;
root /usr/share/nginx/html;
if ($http_x_forwarded_proto = 'http') {
return 301 https://ramekintech.com$request_uri;
}
location / {
proxy_pass http://myapp;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-Host $host;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
Wrap Up
That’s all there is to it. It was a fair amount of work but I’m happy with the results. All of my requirements are met and I dropped one dependency. Now if I ever loop Cloudflare back in again, it will be because I actually need/want them for DNS and DDoS protection.