Setting up NameCheap (Comodo PositiveSSL) Certificate in Nginx with Docker

There are two main steps to setting up SSL in Nginx with Docker:

1) Getting and setting up your certificate on nginx in general

2) Deploying your files and key to the relevant container

General Setup

For this writeup, I'm using docker-compose to manage a Docker deployment with a separate web and nginx container. For this one, the web container is running gunicorn on port 8000 while the nginx container is listening on 80 and 443.

For #1, this link was super helpful with information about what filesd NameCheap provides you. They ultimately send you multiple files that you need to combine into a cert bundle.

Make sure that you have generated the proper CSR and submitted it to NameCheap. Once they verify the request and issue you the certificate, they'll email you 4 files in a zip. It should look something like the below after you extract it:

yourdomain_com  
├── AddTrustExternalCARoot.crt
├── COMODORSAAddTrustCA.crt
├── COMODORSADomainValidationSecureServerCA.crt
└── yourdomain_com.crt

You need to combine them into a cert bundle with *.cer extenstion. Just cat the files all together into a file named something.cer.

cat yourdomain_com.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > yourdomain_com.cer  

You can choose whatever filename you want, but make sure it has the *.cer file extension.

Now move this .cer file to the same location that your original key is located. It's probably named something like yourdomain_com.key or something similar. Ultimately it just makes it easier to copy them over to your server or Docker setup.

Basic SSL setup on nginx

Ultimately, the nginx conf file will point to local copies of the cer and key file from above. We're talking about Docker containers, but it'd be the same for a VPS on something like AWS or digitalocean.

To configure Nginx to use the certs, they need to be somewhere on the host box. A common practice is to put the public cert in the /etc/ssl/certs directory and the private in /etc/ssl/private

You can put them anywhere, so long as you keep it straight. The benefit of the setup above is that you can set the permissions to 0755 for the private folder and 0700 for the public. For both of them, the owner/group is root:root.

This will be the structure I'll be using within the nginx container.

Putting it all together in your Dockerfile

Your Dockerfile is going to copy over the certs and keys to the directory structure discussed above on the nginx container.

Then, it will copy over a conf file for Nginx that references the certs. That's it.

Copy Certs in Dockerfile

The relevant line in your Nginx Dockerfile is

ADD ./ssl /etc/ssl  

This copies the contents of the local ssl directory to the host /etc/ssl directory. In this instance, it's to the Nginx container.

This is what our local ./ssl directory looks like:

ssl/  
├── certs
│   └── yourdomain_com.cer
└── private
    └── yourdomain_com.key

Nginx conf file

Last, we need to copy over our config file to tell Nginx to use the ssl certs, so put something like this in your nginx Dockerfile:

ADD sites-enabled/ /etc/nginx/sites-enabled  

Your local sites-enabled folder should look like:

sites-enabled/  
└── yourdomain_com

Your nginx conf file may differ, but here's a sample one entitled yourdomain_com to demonstrate the relevant ssl portion of the block:

server {  
    listen 443;

    ssl on;
    ssl_certificate /etc/ssl/certs/yourdomain_com.cer;
    ssl_certificate_key /etc/ssl/private/yourdomain_com.key;

    server_name yourdomain.com www.yourdomain.com;
    access_log /var/www/yourdomain.com/access.log;
    error_log /var/www/yourdomain.com/error.log;

    location / {
        proxy_pass http://web:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}


server {

    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    charset utf-8;

    location / {
        proxy_pass http://web:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

The important part is that there are two server blocks: one listening on port 443 (ssl) and one on 80.

Note this section though:

ssl on;  
ssl_certificate /etc/ssl/certs/yourdomain_com.cer;  
ssl_certificate_key /etc/ssl/private/yourdomain_com.key;  

First, it sets ssl on and then sets it up to point to the directory on the host (Nginx container) that you copied the files to in your Dockerfile.

Putting it all together, the directory for the nginx container looks like this:

nginx/  
├── Dockerfile
├── sites-enabled
│   └── yourdomain_com
└── ssl
    ├── certs
    │   └── yourdomain_com.cer
    └── private
        └── yourdomain_com.key

Finishing Up

That's it. Now run the usual commands to get up and running

docker-compose build  
docker-compose up  

You should now be able to access your site via http or https. If you want to only serve traffic using SSL, you'll need to alter the server block listening on port 80 above in the nginx conf above to rewrite or redirect all requests to https.