Going from an "A" to an "A+" on ssllabs.com

Previously I got docker working with nginx and let’s encrypt; this resulted in an “A” from ssllabs.com. Let’s see about getting that to an “A+”.

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

ssllabs.com score

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.

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 — 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!).

DNSimple add CAA record

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

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:

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:

1
2
3
4
5
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:

1
2
# 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
## 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
## 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:

aww yeah+

Woohoo! A+! Here’s the GitHub PR related to changes.

Related:

Going from an "A" to an "A+" on ssllabs.com

https://blog.kritner.com/2018/09/22/ssllabs-com/

Author

Russ Hammett

Posted on

2018-09-22

Updated on

2022-10-13

Licensed under

Comments