Going from an “A” to an “A+” on ssllabs.com
Tags: nginx, tls, softwaredevelopment, letsencrypt
Published: 2018-09-22 20:19:08

---
title: Going from an “A” to an “A+” on ssllabs.com
published: true
tags: nginx,tls,software-development,lets-encrypt
canonical_url: https://medium.com/@kritner/going-from-an-a-to-an-a-on-ssllabs-com-570d2e245100
---

In a [previous post](https://dev.to/kritner/docker-compose-nginx-and-letsencrypt--setting-up-website-to-do-all-the-things-for-that-https-4klk), I went into some (hopefully enough) detail on getting nginx working on docker with Letsencrypt. To wrap up the post, I had ran my website through [ssllabs.com](https://www.ssllabs.com/), and received an “A” score. “A”s are pretty great, but I was hoping it wouldn’t be too difficult to push that “A” up to an “A+”.

Luckily, it was pretty easy!

As a baseline here’s what I’m currently working with from SSL Labs:

![](https://cdn-images-1.medium.com/max/1024/1*902RABH0CKLuJNw_4u54og.png)<figcaption>ssllabs.com score</figcaption>

An “A”, not terrible. This is the first time I’m really going through this report, not really sure what should keep an eye on, but let’s take a look.

![](https://cdn-images-1.medium.com/max/1024/1*MXzlAUsuCxI6JtQqNh9Gwg.png)

Green and yellow — this seems promising! “more info” under DNS CAA points me over to [https://blog.qualys.com/ssllabs/2017/03/13/caa-mandated-by-cabrowser-forum](https://blog.qualys.com/ssllabs/2017/03/13/caa-mandated-by-cabrowser-forum) — tldr seems to be add a record through your DNS that whitelists specific CAs, in my case let’s encrypt.

I use DNSimple as my DNS, and it was pretty straightforward to add the record (and I’m doing this as I type, so hopefully I don’t screw it up!).

![](https://cdn-images-1.medium.com/max/985/1*aX70q7dWLxyfjFPF635b-w.png)<figcaption>DNSimple add CAA record</figcaption>

Okay, so the CAA record is taken care of, let’s see what else is going on in the SSL Labs report…

![](https://cdn-images-1.medium.com/max/1024/1*a1H_VPevIwrKAxosAC5VFw.png)

TLS and SSL are different methods of accomplishing HTTPS, TLS being the successor to SSL. From the looks of this, it’s a good thing my site supports TLS 1.1 and 1.2, a bad thing it supports TLS 1.0, and a good thing all flavors of SSL are not supported. TLS 1.0 being a bad thing to support makes sense — since it was end of lifed on 2018–06–30.

A large remainder of the warnings in the SSL Labs report seems to be centered around the fact that TLS 1.0 is supported:

![](https://cdn-images-1.medium.com/max/981/1*hRbljY1w3p51QJhKdRYyzA.png)

So let’s see about getting TLS 1.0 disabled through my nginx config.

#### Nginx Updates

I recalled an “ssl.conf” file from the mapped volume in the previous post — though I had not made any changes to it up to this point. Since I will likely *need* to make changes to this file (it makes sense that SSL related settings would be here, no?), I will be adding this file to a new docker volume; so I can check in changes to the file.

A snippet of my original docker-compose file:

```yml
volumes:
 # used to have a copy of config on the DOCKER HOST, can mod from there
 - ${DOCKER_KRITNER_NGINX}:/config 
 # the actual configuration file to use for the reverse proxy (located in current repo directory)
 - ./nginxConfig/nginx.conf:/config/nginx/site-confs/default
```

The new addition to the volumes portion of the file:

```yml
# overrides for ssl conf from base image
 - ./nginxConfig/ssl.conf:/config/nginx/ssl.conf
```

This will allow my ssl.conf file to be mapped within the docker container, overwriting the original image’s ssl.conf; no API keys, passwords, etc in this file, so might as well keep its changes in source.

The entire docker-compose file now looks like:

```yml
version: '3.6'
services:

 nginx:
 image: linuxserver/letsencrypt
 ports:
 - "80:80"
 - "443:443"
 volumes:
 # used to have a copy of config on the DOCKER HOST, can mod from there
 - ${DOCKER_KRITNER_NGINX}:/config 
 # the actual configuration file to use for the reverse proxy (localted in current repo directory)
 - ./nginxConfig/nginx.conf:/config/nginx/site-confs/default
 # overrides for ssl conf from base image
 - ./nginxConfig/ssl.conf:/config/nginx/ssl.conf
 depends_on:
 - kritnerwebsite
 networks:
 - frontend
 container_name: nginx
 environment:
 - PUID=1001 # get on dockerhost through command "id <user>""
 - PGID=1001
 - EMAIL=kritner@gmail.com
 - URL=kritner.com
 - SUBDOMAINS=www
 - TZ=America/NewYork
 - VALIDATION=dns # using dns validation
 - DNSPLUGIN=dnsimple # via dnsimple, note there is additional configuration require separate from this file
 # - STAGING=true # this should be uncommented when testing for initial success, to avoid some rate limiting

kritnerwebsite:
 image: ${DOCKER_REGISTRY}/kritnerwebsite
 networks:
 - frontend
 expose:
 - "5000"
 restart: always
 container_name: kritnerwebsite

networks:
 frontend:
```

Next, onto the ssl.conf itself. The original ssl.conf is:

```
## Version 2018/05/31 - Changelog: https://github.com/linuxserver/docker-letsencrypt/commits/master/root/defaults/ssl.conf

# session settings
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

# Diffie-Hellman parameter for DHE cipher suites
ssl_dhparam /config/nginx/dhparams.pem;

# ssl certs
ssl_certificate /config/keys/letsencrypt/fullchain.pem;
ssl_certificate_key /config/keys/letsencrypt/privkey.pem;

# protocols
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

# HSTS, remove # from the line below to enable HSTS
#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;

# Optional additional headers
#add_header Content-Security-Policy "upgrade-insecure-requests";
#add_header X-Frame-Options "SAMEORIGIN" always;
#add_header X-XSS-Protection "1; mode=block" always;
#add_header X-Content-Type-Options "nosniff" always;
#add_header X-UA-Compatible "IE=Edge" always;
#add_header Cache-Control "no-transform" always;
#add_header Referrer-Policy "same-origin" always;
```

This all seems pretty standard and simple to figure out. Under “protocols” you can see there is a TLSv1 listed, which we’ll be removing. There is also a commented out line regarding HSTS, and I can’t really think of a reason to keep that disabled. Those two changes make the entire file look like:

```
## Version 2018/05/31 - Changelog: https://github.com/linuxserver/docker-letsencrypt/commits/master/root/defaults/ssl.conf

# session settings
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

# Diffie-Hellman parameter for DHE cipher suites
ssl_dhparam /config/nginx/dhparams.pem;

# ssl certs
ssl_certificate /config/keys/letsencrypt/fullchain.pem;
ssl_certificate_key /config/keys/letsencrypt/privkey.pem;

# protocols
ssl_protocols TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

# HSTS, remove # from the line below to enable HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;

# Optional additional headers
#add_header Content-Security-Policy "upgrade-insecure-requests";
#add_header X-Frame-Options "SAMEORIGIN" always;
#add_header X-XSS-Protection "1; mode=block" always;
#add_header X-Content-Type-Options "nosniff" always;
#add_header X-UA-Compatible "IE=Edge" always;
#add_header Cache-Control "no-transform" always;
#add_header Referrer-Policy "same-origin" always;
```

I think that’s pretty much everything — we added a CAA record, disabled TLSv1, and made a few modifications to support the TLS change in our docker-compose file.

Let’s see what SSL Labs thinks now:

![](https://cdn-images-1.medium.com/max/1024/1*VigwJNwgZ_d--m17AnnjHQ.png)<figcaption>aww yeah+</figcaption>

Woohoo! A+! Here’s the [GitHub PR](https://github.com/Kritner/KritnerWebsite/pull/25) related to changes.

Related:
* [docker-compose, nginx, and letsencrypt - settings up website to do all the things for that https!](https://dev.to/kritner/docker-compose-nginx-and-letsencrypt--setting-up-website-to-do-all-the-things-for-that-https-4klk)