Setting up SSL with nginx (using a NameCheap EssentialSSL wildcard certificate on DigitalOcean)

Brief notes from my experience in setting up nginx on a DigitalOcean droplet running Ubuntu 14.04 LTS to use SSL with a Comodo EssentialSSL wildcard certificate from NameCheap.

On your development machine

(You could do this on the server and skip the copying the keys section, below, but I wanted to have a backup of my keys and certificates.)

First off, create a private key — a 2048 bit key should suffice for the time being:

openssl genrsa 2048 > key.pem

While we’re in the command line, let’s also create a key to use when implementing perfect forward secrecy:

openssl dhparam -out dhparams.pem 2048

Next, create the signing request:

openssl req -new -key key.pem -out csr.pem

Now, buy an EssentialSSL wildcard certificate from NameCheap and copy and paste the contents of the csr.pem file to the form on NameCheap when prompted to following the purchase.

Ask for your certificate to be emailed to you. It should arrive in a little while (mine didn’t and I had to contact support and upload a file to my domain after which the support tech provided me the file in the online chat).

Create a certificate bundle

You will get a zipped folder with the following files:

To use these, you have to create a certificate bundle. If Comodo/NameSecure respected the limited time that people have on Earth, they’d create this for you. Apparently, they don’t, so we have to run the following command to do it:

(Note: substitute STAR_yourdomain_ext.crt with the actual name of the file that was in your specific download.)

cat STAR_yourdomain_ext.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > bundle.cer

On your server

Now, you need to place the private key and your certificate bundle on your server.

I am assuming that you have already installed nginx and the instructions below are from an Ubuntu 14.04 LTS installation on Digital Ocean so your mileage may vary. (If you need help in installing nginx on Digital Ocean, here is a tutorial to help you install nginx on Ubuntu 14.04 LTS)

Copying the keys

To copy the keys:

  1. ssh into your server
  2. sudo mkdir /etc/nginx/ssl
  3. sudo nano /etc/nginx/ssl/bundle.cer and copy the bundle.cer file’s contents from your development machine and paste it into this file. Save & exit (Ctrl+X; Yes).
  4. sudo nano /etc/nginx/ssl/key.pem and copy the key.pem file’s contents from your development machine and paste it into this file. Save & exit.
  5. Protect that directory: sudo chmod 600 /etc/nginx/ssl

Update: the way I manage this now is that I’ve put the /etc/nginx directory under Git and I simply git pull and restart nginx to update the configuration. Much easier than messing with nano, etc., on the server, and much less brittle/risky.

Configuring your site

The following steps will set up your site to be served over SSL, with any non-SSL requests (with or without a www prefix) forwarded to the SSL server. (Solution courtesy of Davis.)

(Note: on my machine, nginx is installed at /etc/nginx).

  1. ssh into your server
  2. sudo nano /etc/nginx/sites-available/default to edit the configuration for the default site.
  3. First, let’s create the http server on port 80 and make it forward all requests to https:
    #
    # Rewrite http requests to https
    #
    server {
      listen 80;
      server_name yourdomain.ext;
      return 301 https://yourdomain.ext$request_uri;
    }
  4. Next, let’s create the rules to forward all requests to www.yourdomain.ext to https://yourdomain.ext:
    #
    # Rewrite requests for http://www.yourdomain.ext to https://yourdomain.ext
    #
    server {
      listen 80;
      server_name www.yourdomain.ext;
      return 301 https://yourdomain.ext$request_uri;
    }
  5. Finally, let’s create the actual https server (for this, I edited the default domain server. I’m only showing the top of the definition — only the bits that I altered:
    server {
      listen 443 ssl;
      listen [::]:443 ipv6only=on;
    
      ssl_certificate ssl/bundle.cer;
      ssl_certificate_key ssl/key.pem;
    
      root /path/to/the/root/of/your/site;
      index index.html index.htm;
    
      # Make site accessible from http://localhost/
      server_name yourdomain.ext;
    
      location / {
      # More lines…

Perfect forward secrecy and HSTS

To get an A+ on SSLLabs (because you know you want to), you need to add perfect forward secrecy and HTTP Strict Transport Security (HSTS). The former is based on the cipher suites used and the latter simply requires one line of configuration code:

server {
  # …

  # Perfect forward secrecy
  ssl_prefer_server_ciphers on;
  ssl_dhparam /etc/nginx/ssl/dhparams.pem;
  ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

  # HSTS
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";

  # …
}

The explanation for the HSTS line, from Scott Helme:

The ‘max-age’ values specifies how long, in seconds, you want the client to treat you as a HSTS host. That is, how long you want them to contact you using HTTPS exclusively. The value I’m using here is 1 year and each time the client visits my site and receives the header, the timer is reset back to a year. Assuming your browser is HSTS compliant, after the first page load over HTTPS, you will no longer be able to communicate with me via HTTP, the browser will prevent it. The ‘includeSubdomains’ directive is fairly self explanatory. I don’t have any sub-domains on my blog but I still issue the directive as a precaution. If you want the HSTS policy to be enforced on all of your sub-domains, include the directive in your header.

(Thanking Tom Morris for his help with the HSTS bit.)

Fixing the Poodle

Due to the Poodle vulnerability in SSL 3.0, you must disable SSL 3.0. In your server block add:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

Updating your nginx.conf

There’s just one more thing to do before we can restart the server:

  1. Edit the nginx.conf file: sudo nano /etc/nginx/nginx.conf
  2. Uncomment the line: server_names_hash_bucket_size 64;

That’s it. Now restart nginx with:

sudo service nginx reload

And check that it has restarted:

(Don’t ask me why you don’t get a friendly message telling you it did or a helpful error if it didn’t; in an ideal world, you should.)

ps aux|grep nginx

If all is well, you should see multiple lines of output similar to:

… nginx: master process /usr/sbin/nginx
… nginx: worker process
… nginx: worker process
… nginx: worker process
… nginx: worker process

If all is not well, check the error log for possible reasons:

sudo tail /var/log/nginx/error.log

Troubleshooting

You should not run into these issues if you followed the instructions, above, but I am including them here because I ran into them while stumbling my way towards the solution and it should help others who are stuck to find this page.

could not build the servernameshash, you should increase servernameshashbucketsize: 32

You forgot to uncomment the line raising the names_hash_bucket_size in nginx.conf. What’s a names_hash_bucket_size, you ask? An implementation detail that we probably should never have had to deal with is the first answer that comes to mind.

To fix this, follow the instructions in the Updating your nginx.conf section, above.

Incorrect certificate bundle

If you create your certificate bundle incorrectly, you will get the following error:

SSL_CTX_use_PrivateKey_file("/etc/nginx/ssl/aralbalkan.com.key") failed (SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)

You can check whether your certificate file is correctly formed by running the following command:

(Substitute PRIVATE_KEY_FILE_PATH with the path to your private key file and CERTIFICATE_BUNDLE_FILE_PATH with the path to your certificate bundle file.)

diff <(openssl rsa -in PRIVATE_KEY_FILE_PATH -modulus -noout) <(openssl x509 -in CERTIFICATE_BUNDLE_FILE_PATH -modulus -noout)

If there is any output, this is the problem and you should follow the instructions in the Create a certificate bundle section, above, to fix it.

I’ve installed nginx but where is everything?

Once you’ve installed nginx, here are the main folders you need to know:

nginx’s configuration files are in:
/etc/nginx

nginx config file:
/etc/nginx/nginx.conf

The default site is served from:
/usr/share/nginx/html

Default site config info:
/etc/nginx/sites-available/default

How to I start, stop, and restart nginx?

To start nginx:

sudo service nginx start

To restart nginx:

sudo service nginx reload

To stop nginx (note, restart is safer than start + stop as it will not stop it if there is an error in your config)

sudo service nginx stop