Adding SSL To Your Amazon AWS Instance

Tutorial

The last few blog posts have been an in-depth tutorial on how to set up an Amazon AWS Instance.  And, while the basics were more than covered, I had a few reader asking me about a super important topic: SSL and Encryption.

What exactly is SSL Encryption?

The short answer: it’s that fancy green lock on your favorite sites that keeps information secure.

The long answer: it’s a signed certificate that establishes a secure, crytptographically keyed connection between the web server and the client (the person viewing the site). While a certificate can be “self signed”, most people choose to allow an authorized 3rd party (such as Comodo, RapidSSL, or GeoTrust) to verify the encryption.

Why bother with SSL?

These days, you can’t throw a keyboard without hitting someone who’s had their identity stolen. Data transferring between the user and a server by default isn’t safe – it can be sniffed out by dasterly do-baddies who can steal the information being transmitted. Usernames, passwords, credit card numbers; anything’s fair game to a hacker.

An SSL Certificate encrypts the data – encodes it, if you will – as it’s being sent along the information superhighway. The data that comes in would be gibberish to any hacker trying to access it – provided they can even get access at this point. However, upon hitting the destination, the encryption is decoded and served to the user.

I personally won’t shop on a client’s website if there’s no SSL certificate. Even eCommerce sites who use PayPal or some other off-site payment gateway, since there’s still certain information that’s being transmitted that I just don’t want prying eyes seeing.

Installing the Certificate on an Amazon AWS Instance.

If you’ve been following along, we (over the last few blog posts), have successfully set up, installed, and managed an Amazon AWS Instance. Now that it’s up, we can encrypt the data we’re sending out.

Step 0: Sign Up for an SSL Certificate

The process is different for each SSL provider, but I’m going to use NameCheap because it’s my personal go-to for these sorts of things.

Head over to NameCheap (affiliate link) and hit the SSL Certificates section. We just need something simple for our site, but if you need more robust encryption those options are also there.

We’re going to go with “Domain Validation” since we’re just setting up one domain.

You’ll see lots of choices on this page; some more expensive than others. Let’s stick with the basics. The $9.00/year “PositiveSSL” is perfect for encrypting data on non-eCommerce sites, or on sites with low sales volume. The “QuickSSL Premium” is good for larger eCommerce sites, but it’s also more expensive at ~$57/year.

Check out, confirm, and pay. You’ll get your typical login credentials, but you won’t need them just yet.

Step 1: Generate a CSR (Certificate Signing Request)

We’re going to take care of the stuff server-side first. Log in through SSH to your Amazon AWS Instance. This sequence:

$ cd ~
$ openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr

…visits your home directory and begins the process for generating two files needed for the certificate: a private key and the Certificate Signing Request.

You’ll want to have some information on hand for this:

  • Common Name: Domain name of the server in question – ‘mitchcanter.me’
  • Country : Two letter official abbreviation – ‘US’
  • State (or province): Two letter official abbreviation – ‘TN’
  • Locality (or city): City name
  • Organization: Your business name if applicable. Write NA if it’s not.
  • Organizational Unit (Department): Your department. Write NA if you don’t have one or it isn’t applicable to your situation.
  • E-mail address: I’d strongly suggest using the same address as your namecheap account. You *will* receive emails independent of the namecheap account to this, so make sure it’s at least a valid email address.

It’s strongly recommended that you fill in all the fields.  You’ll have two files created when you’re finished, and you’ll need both of them eventually.

Open the CSR with your text editor:

$ sudo nano server.csr

and copy everything you see. It’ll look something like this:

-----BEGIN CERTIFICATE REQUEST-----
MIICvDCCAaQCAQAwdzELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxDzANBgNV
BAcMBkxpbmRvbjEWMBQGA1UECgwNRGlnaUNlcnQgSW5jLjERMA8GA1UECwwIRGln
aUNlcnQxHTAbBgNVBAMMFGV4YW1wbGUuZGlnaWNlcnQuY29tMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8+To7d+2kPWeBv/orU3LVbJwDrSQbeKamCmo
wp5bqDxIwV20zqRb7APUOKYoVEFFOEQs6T6gImnIolhbiH6m4zgZ/CPvWBOkZc+c
1Po2EmvBz+AD5sBdT5kzGQA6NbWyZGldxRthNLOs1efOhdnWFuhI162qmcflgpiI
WDuwq4C9f+YkeJhNn9dF5+owm8cOQmDrV8NNdiTqin8q3qYAHHJRW28glJUCZkTZ
wIaSR6crBQ8TbYNE0dc+Caa3DOIkz1EOsHWzTx+n0zKfqcbgXi4DJx+C1bjptYPR
BPZL8DAeWuA8ebudVT44yEp82G96/Ggcf7F33xMxe0yc+Xa6owIDAQABoAAwDQYJ
KoZIhvcNAQEFBQADggEBAB0kcrFccSmFDmxox0Ne01UIqSsDqHgL+XmHTXJwre6D
hJSZwbvEtOK0G3+dr4Fs11WuUNt5qcLsx5a8uk4G6AKHMzuhLsJ7XZjgmQXGECpY
Q4mC3yT3ZoCGpIXbw+iP3lmEEXgaQL0Tx5LFl/okKbKYwIqNiyKWOMj7ZR/wxWg/
ZDGRs55xuoeLDJ/ZRFf9bI+IaCUd1YrfYcHIl3G87Av+r49YVwqRDT0VDV7uLgqn
29XI1PpVUNCPQGn9p/eX6Qo7vpDaPybRtA2R7XLKjQaF9oXWeCUqy1hvJac9QFO2
97Ob1alpHPoZ7mWiEuJwjBPii6a9M9G30nUo39lBi1w=
-----END CERTIFICATE REQUEST-----

Make sure you grab all of it – including the BEGIN and END lines.

Step 2: Signing the Certificate

(Thanks to Namecheap’s Support Portal for these next screenshots!)

Login to namecheap, and visit your Dashboard:

Look on the left sidebar and find Product List > SSL Certificates:

Next to the SSL Certificate you just purchase will be a big “Activate” button. Click that and paste in that CSR request you just made.

The Web Server is Nginx, so make sure the “Apache, Nginx, cPanel or other” box is selected, and that the information looks correct. Hit submit.

Choose Email Validation. It should give you one of the email addresses you’ve entered in as an option. Otherwise, select an address you can receive mail to and hit Submit.

Verify the Administrative contact information and fill out the fields. Make sure they match the CSR you generated.

Hit Next, and then Confirm on the next screen once you’re satisifed with all of the options.

You should get an email with a link to download a .zip file of the certificate files.  Log in with SFTP and send them all to your home directory. We’ll move them later, but for now it’ll be good to have them all in one place.

Step 3: Installation

You should see a few different files when you upload the contents of the zip file:

  • domain_name.crt
  • domain_name.ca-bundle

Those are both necessary, but we can combine them into a single file in order to keep things easy:

$cat domain_name.crt domain_name.ca bundle >> domain_name_chain.crt

Then, we can move them into position on the server…

$ sudo mv ./domain_name_chain.crt ./server.key /etc/ssl

This moves both the domain certificate and the private key we set up earlier into a permanent home.

We set up Nginx last time to only accept non-ssl traffic, which defaults to port 80. What we need to do now is to redirect all that traffic automatically to the https version of the site. The https site, then, needs to be set up to accept traffic and pass it through like normal.

Note: my original post had two server blocks – one SSL and one non-SSL – and had the non-SSL traffic redirecting to the SSL traffic. Turns out this causes all sorts of problem with Jetpack, and a few other plugins that require port 80 to not be a straight up redirect. So, we’re going to do it a different way instead. We’re going to set up the server block to accept traffic on both ports (443 and 80) and then set WordPress up to handle the SSL so Jetpack doesn’t get confused.

# Define the microcache path.
fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=microcache:100m inactive=60m;

server {
  listen [::]:80;
  listen 80;
  listen 443 ssl;

  server_name site_domain.com;

  # Include defaults for allowed SSL/TLS protocols and handshake caches.
  include h5bp/directive-only/ssl.conf;

  # config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
  # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";

  ssl_certificate_key /etc/ssl/server.key;
  ssl_certificate /etc/ssl/domain_name_chain.crt;

  # Path for static files
  root /sites/site_domain.com/public;

  #Specify a charset
  charset utf-8;

  # Include the basic h5bp config set
  include h5bp/basic.conf;

  location / {
    index index.php;
    try_files $uri $uri/ /index.php?$args;
  }

  location ~ \.php$ {
    fastcgi_cache  microcache;
    fastcgi_cache_key $scheme$host$request_method$request_uri;
    fastcgi_cache_valid 200 304 10m;
    fastcgi_cache_use_stale updating;
    fastcgi_max_temp_file_size 1M;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME   $document_root$fastcgi_script_name;
    include        fastcgi_params;


    # Local variables to track whether to serve a microcached page or not.
    set $no_cache_set 0;
    set $no_cache_get 0;

    # If a request comes in with a X-Nginx-Cache-Purge: 1 header, do not grab from cache
    # But note that we will still store to cache
    # We use this to proactively update items in the cache!
    if ( $http_x_nginx_cache_purge ) {
      set $no_cache_get 1;
    }

    # If the user has a user logged-in cookie, circumvent the microcache.
    if ( $http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
      set $no_cache_set 1;
      set $no_cache_get 1;
    }

    # fastcgi_no_cache means "Do not store this proxy response in the cache"
    fastcgi_no_cache $no_cache_set;
    # fastcgi_cache_bypass means "Do not look in the cache for this request"
    fastcgi_cache_bypass $no_cache_get;
  }
}

If you used the tutorial, this should look familiar; it’s the same code we used (almost) to set up our initial virtual hosts. The only difference is that now we’re redirecting the non-SSL traffic to SSL-encrypted traffic. The other code bits are the same!

Finally, restart nginx:

$ sudo service nginx restart

And there you have it – the SSL certificate signed, sealed, and delivered to you and installed lovingly on your Amazon AWS Server. Now, any traffic that flows through will automatically be encrypted. You can now install shopping carts and provide user accounts knowing that your users data is safe.

Epilogue:

I had to modify this tutorial a bit because Jetpack was throwing an error message because of how the port 80 traffic was redirecting.  Since we’re now including both ports in our server block, we need to do some things in WordPress to get it to force SSL traffic for normal visitors:

  1. Change the site_url and blog_url to https://yourdomain.com. You can do this either in the WordPress Settings > General screen, or by editing your wp-config.php file and adding these variables:
    define('WP_HOME','https://yourdomain.com');
    define('WP_SITEURL','https://yourdomain.com');
  2. Using the WordPress Force SSL to redirect all other traffic (images, links, etc) from non-HTTPS to HTTPS.

This should take care of any issues with Jetpack, and still force SSL for normal traffic and users.

A side note: there’s an initiative called “Let’s Encrypt”that allows users to set up free certificates. It’s still fairly new, and I’m still researching it, but I ultimately want to do another guide that will show how to install those certificates. Keep in mind, though, that you can still pay for a trusted certificate from a vendor like Comodo, and that some people really do care about having that name and/or seal on the sites they visit. That said, don’t run off and ditch your other certificates just yet!

If you run into any snags, let me know in the comments below.  Also, let me know if you’ve successfully installed the certificate! I love it when a good plan comes together!