Getting an SSL cert into an NGINX Container

Introduction

Some time ago I needed to get an SSL certification into a container cluster I was setting up with InfluxDB and Grafana. Getting it on the container proved to be difficult. Fortunately, there was another option. This option is what I’ll be showing below.

Disclaimer

I’m a strong proponent of testing before implementation. Therefore, I do not take any responsibility for your use of this information on your systems and the consequences that will ensue (good or bad). Remember the golden rules of IT:

1) Have a backup plan
2) Have a data backup

Follow these rules, and you will successfully recover most of the time.

Tools

For the record, I ran this setup in AWS on an EC2 instance. I’ve found it to be easier/faster to get things up and running from a development standpoint that way. There are other, better options with AWS for running containers like Fargate. Do what works best for you and your organization. If you’re following along at home, you can set this up on your linux machine pretty easily for the most part. The biggest difference will likely be finding a way to set up local certs (which I believe certbot can do, but don’t quote me on it) and then mounting the volume as shown below. Or you can acquire an external *static* ip address and point it towards your servers. Whatever works best for you. I won’t cover those options below, however, so your setup may look somewhat different than mine.

Docker Container Startup and Sundry Notes

#!/bin/bash

##crontab on the host machine
# Chronically renews the cert through LetsEncrypt's certbot
#@daily certbot renew --pre-hook "docker stop grafana" --pre-hook "docker rprox stop" --post-hook "docker start grafana" --post-hook "docker rprox start"

## I wasn't using build scripts at this point. Big mistake. Much more work.
## But if you're starting out this will get you running.
docker run -d --network bridge -p 80:80 -p 443:443 -v /etc/letsencrypt:/etc/letsencrypt --name rprox rprox sh /usr/local/bin/start.sh;

## Copying a special Nginx Conf to the container
pushd /nginx/;
docker cp nginx.conf rprox:/etc/nginx/;
popd;

## Did the container come up? Note the sneaky useful "watch" command
watch docker ps

NGINX Conf File

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    error_log /etc/nginx/error_log.log warn;
    client_max_body_size 20m;
    proxy_cache_path /etc/nginx/cache keys_zone=one:500m max_size=1000m;

    server {
        listen 80 default_server;
        server_name _;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443;
        server_name YourServerNameOrIPAddress;
        server_name *.domain.com;  
  
        ssl on;
        ssl_certificate /etc/letsencrypt/live/subdomain.domain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/subdomain.domain.com/privkey.pem;
        ssl_session_cache shared:SSL:10m;

        location / {
            proxy_pass http://domain_name_for_grafana_server:3000/;
            proxy_set_header Host $host;  
            proxy_redirect http:// https://;
        }

        #root /usr/share/nginx/www;
        #index index.html index.htm;
    }
}

Walkthrough

Ok, so a little side bar first.  In the Docker NGINX container set up I would note that I followed the simple instructions on Certbot’s page to install the LetsEncrypt certificate on the local machine. I left the cron job in the script above so that you would have an idea of how to allow the certificate to renew. In this case you’ll notice that I stop the grafana and rprox container. The reason for this is certbot needs port 80 to be open in order to handle the challenge reply and request which subsequently renews the certificate. The post hooks start the services up again. Pretty simple. This cronjob for obvious reasons is set on the host machine. You can follow the format and even copy and paste it into the crontab itself using:

crontab -e

The next section assumes that you’ve built your own nginx container and are calling it. There are premade nginx containers out there by other people, but I tend to be a little funny about that from a security perspective. Especially if it’s something I can do myself.

docker run -d --network bridge -p 80:80 -p 443:443 -v /etc/letsencrypt:/etc/letsencrypt --name rprox rprox sh /usr/local/bin/start.sh;

It’s pretty simple. I set up my nginx container and got the scripts set up for it internally. This is how I can call it. I decided to name it rprox because I was going for a reverse proxy with nginx which is one of nginx’s selling points. The only difference in the script is mounting of the “/etc/letsencrypt” volume to the container’s volume. The next part would have been best done with a build script, but in the absence of this you can do it manually:

pushd /nginx/;
docker cp nginx.conf rprox:/etc/nginx/;
popd;

I had a local directory on my host machine called Nginx. In this directory, I had set up an nginx.conf file that I could alter until I got it things lined up correctly. From there I used the docker copy command and placed it in the containers “/etc/nginx/” directory.

    server {
        listen 443;
        server_name YourServerNameOrIPAddress;
        server_name *.domain.com;  
  
        ssl on;
        ssl_certificate /etc/letsencrypt/live/subdomain.domain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/subdomain.domain.com/privkey.pem;
        ssl_session_cache shared:SSL:10m;

        location / {
            proxy_pass http://domain_name_for_grafana_server:3000/;
            proxy_set_header Host $host;  
            proxy_redirect http:// https://;
        }

This section is what you need to add to your nginx conf file. Whether it’s in “sites-enabled/default” or just the standard nginx.conf, this is some hard-fought magic sauce.

So if you’re not very familiar with domains and subdomains, you might have some questions about “*.domain.com“. This allows for as many subdomains as you have to fall under this SSL certificate assuming you invoked certbot’s wildcard option. At a minimum, if you change your mind about your subdomain name that you originally used, you can easily change it to something else (rerunning certbot of course for the new subdomain name change).

The next subsection in the nginx.conf file documents where the certificate is located. If you remember we mounted the directories that hold the certificate. This makes it simple to make as many different calls as you need similar to what’s in this subsection.

Finally, we call the reverse proxy section. In nginx conf, I’m just saying that whatever the subdomain name is I want to call it and consider it the root. There are some other changes we can make, but that’s for another blog. We finish this out with ensuring that any “http” calls get changed over to “https” calls instead.

Conclusion

I hope you gleaned some useful tools for running reverse proxy nginx with ssl setup. If nothing more maybe this helps clear up some of the confusion that can occur with one of the most powerful opensource tools available.

Share: