Had some trouble with this myself, so I thought it would be good to share my findings.
At work I'm currently managing a fairly large estate of websites. Ideally we'd have everything running TLSv1.2+, however there are various legacy applications which require TLSv1.0 due to various reasons.
Ideally we'd either upgrade said applications to support TLSv1.2, or get rid of them altogether, however it sadly takes some time for the gears to turn, so it isn't really an option.
Now the initial thought would be to just set the ssl_protocols
setting on a per server basis. You can try this, reload Nginx and notice there is no change, despite no warning in reload (which should occur here).
Whatever ssl_protocols
Nginx sees first, regardless of server, is what is used for all servers. There is a few similar settings where this is the case, however they are usually flagged as such on reload, this one is not.
From my research, there is three possible solutions to this.
-
Setup a new Nginx server for each different ssl_protocols setup.
This adds a bunch of unneeded complexity to managing the sites, and would also require an additional IP per server. -
Utilize the patch in nginx/trac#844.
This works and does what is expected, however it does have some limitations.- This patch works up to 1.15.2 (2018/07/24), however I did manage to get it working with 1.15.8 (2018/12/25). 1.15.9+ appears to have done some breaking changes which cause the patch to longer work correctly, and I don't have the knowledge enough with C to make it work.
- According to a comment on the issue, this won't be compatible with the upcoming Encrypted SNI, so even if the patch could work on later versions, it would kill this feature.
We've used this option for around a year without much issue, however this is obviously not substainable due to the lack of ability to upgrade.
-
Utilize
ssl_ciphers
to handle protocol restrictions.
This is ultimately the best option, albeit the least obvious one. The only information on this is from a three year old post on StackOverflow.The way this works is pretty simple:
- Set
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
in thehttp
block. - Set protocol specific
ssl_ciphers
in the server block. Also set other relevant SSL settings (prefer ciphers, dhparam).)
- Set
# ...
http {
# ...
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
server {
server_name tls10.example.com;
# ...
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA;
ssl_dhparam /etc/nginx/ssl-certs/dhparam.pem;
# ...
}
server {
server_name tls12.example.com;
# ...
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_dhparam /etc/nginx/ssl-certs/dhparam.pem;
# ...
}
server {
server_name tls13.example.com;
# ...
ssl_prefer_server_ciphers off;
# ssl_ciphers ...; # Default Nginx ciphers should be OK here?
# ...
}
}