SSL Certs, Docker and Nginx reverse proxy

Posted by Riomhaire Research on Tuesday, June 26, 2018

This article describes how to add SSL certs to websites hosted as docker images started up via Docker-Compose and using a nginx reverse proxy container called “jwilder/nginx-proxy”. SSL has become the default mechanism for most browsers to provide transport level encryption and security against hackers and other entities who may be wanting to ’listen in’ to traffic to and from your site and has been historically a pain to set up and expensive since you would need to buy one or more certificates from trusted sources. Thanks to docker, nginx-proxy and the excellent letsencrypt site this is no longer the case. The main rational were were requiring SSL is that we were playing around with using a browser, webcamera and QR-Codes to log into a website; due to sensible recent changes to chrome, firefox and other browsers the enabling of access to a webcamera is only available to websites loaded via HTTPS - but that is another post and another story.

So lets begin this story by stating we are assuming:

  1. You already have your domains registered and pointing to your external site IP address.
  2. If you are using a router it has been set up to forward port 443 traffic to the machine hosting your website.

If, for whatever reason, you are not into paying for some SSL certificates there is a simple way to get one for your website if you are using the nginx “jwilder/nginx-proxy” reverse proxy image and thats is ’letsencrypt’ in the form of the companion container “jrcs/letsencrypt-nginx-proxy-companion”.

Before Adding SSL

Lets say we have a simple docker-compose file which exposes a simple website ‘acme’ using the “jwilder/nginx-proxy”. The docker-compose file would look something like:

nginx-proxy:
  image: jwilder/nginx-proxy
  ports:
    - "80:80"
  volumes:
    - "/var/run/docker.sock:/tmp/docker.sock:ro"
    - "/etc/nginx/conf.d"
    - "/etc/nginx/vhost.d"
    - "/usr/share/nginx/html"
    - "/etc/nginx/certs:/etc/nginx/certs:ro"

acme:
  image: nginx
  expose:
    - 9100
  environment:
    - "VIRTUAL_HOST=www.acme.com"
    - "VIRTUAL_PORT=9100"
    - LETSENCRYPT_HOST=www.acme.com
    - LETSENCRYPT_EMAIL=acme@acme.com
  volumes:
    -  "/home/acme/websites/acme.com/html:/usr/share/nginx/html:ro"
    -  "/home/acme/websites/acme.com/conf/nginx.conf:/etc/nginx/nginx.conf:ro"

The “jwilder/nginx-proxy” image is listening on 80 and is passing all traffic for the ‘www.acme.com’ to another (non-proxy) nginx docker contain running on port 9100. This is all fairly straight forward by adding the companion container and getting the reverse proxy to become aware of it.

The above docker-compose file becomes:

nginx-proxy-ssl:
  image: jrcs/letsencrypt-nginx-proxy-companion
  volumes:
    - "/var/run/docker.sock:/var/run/docker.sock:ro"
  volumes_from:
    - nginx-proxy

nginx-proxy:
  image: jwilder/nginx-proxy
  labels:
    - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true"
  ports:
    - "443:443"
  volumes:
    - "/var/run/docker.sock:/tmp/docker.sock:ro"
    - "/etc/nginx/conf.d"
    - "/etc/nginx/vhost.d"
    - "/usr/share/nginx/html"
    - "/etc/nginx/certs:/etc/nginx/certs:ro"

acme:
  image: nginx
  expose:
    - 9100
  environment:
    - "VIRTUAL_HOST=www.acme.com"
    - "VIRTUAL_PORT=9100"
    - LETSENCRYPT_HOST=www.acme.com
    - LETSENCRYPT_EMAIL=acme@acme.com
  volumes:
    -  "/home/acme/websites/acme.com/html:/usr/share/nginx/html:ro"
    -  "/home/acme/websites/acme.com/conf/nginx.conf:/etc/nginx/nginx.conf:ro"

The main changes are:

  1. In ’nginx-proxy’ change the ports its listening to from 80 to 443 (if you only want SSL access, otherwise add an extra row with - “443:443”).
  2. In ’nginx-proxy’ add labels section and add the label given.
  3. Add the ’nginx-proxy-ssl’ yaml section.

Thats it. Just build your images and start using the normal docker-compose commands.