Replacing Apache with Nginx for HTTP2 support

Hey guys,

I’ve used letsencrypt to install an SSL cert for the latest nginx on ubuntu.
The setup is fine and works great with the exception of:

I don’t know enough about SSL to know what’s going on but I have a suspicion:
I installed the SSL cert for Apache a while back and just now moved to Nginx for it’s http/2 support. As the nginx plugin is not stable yet I had to install the cert myself and this is what I did:

In my nginx config (/etc/nginx/conf/default.conf) I added:

server {
	listen       80;
	server_name  [domain];
	return 301   https://$host$request_uri;
}

server {
	listen       443 http2;
	listen       [::]:443 http2;
	server_name  [domain];

	ssl on;
	ssl_certificate /etc/letsencrypt/live/[domain]/cert.pem;
	ssl_certificate_key /etc/letsencrypt/live/[domain]/privkey.pem;
}

Is it possible that this breaks the chain somehow? What is the proper way here?

Thanks guys

Use “fullchain.pem” for ssl_certificate. It has the certificate plus the chain back to the CA.

If you want a better score, also look into customizing the nginx SSL configuration so you’re using custom-generated DH parameters.

1 Like

Have a look at https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29 for configuring the cipher suites. There’s also a generator at https://mozilla.github.io/server-side-tls/ssl-config-generator/

1 Like

ssl_ciphers ‘HIGH:!aNULL:!MD5:!kEDH’;

Thanks guys! That helped a lot!

I ended up with these configs:

# Settings
server_tokens off;

server {
	listen       80;
	server_name  [domain];
	return 301   https://$host$request_uri;
}

server {
	listen       443 http2;
	listen       [::]:443 http2;
	server_name  [domain];

	ssl on;
	ssl_certificate /etc/letsencrypt/live/[domain]/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/[domain]/privkey.pem;

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

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_prefer_server_ciphers on;
	ssl_dhparam /etc/nginx/ssl/dhparam.pem;
	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:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM: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-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

	# OCSP Stapling ---
	# fetch OCSP records from URL in ssl_certificate and cache them
	ssl_stapling on;
	ssl_stapling_verify on;

	# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
	add_header Strict-Transport-Security max-age=15768000;
}

and the result is:

1 Like

With HSTS you should usually get an A+. You can add add_header Strict-Transport-Security max-age=15768000 always; if your Nginx version allows for always.

1 Like

I was under the impression you should always list ssl to the listen 443 directive in nginx to enable TLS? Or does HTTP2 imply TLS?

Hmm, the sample configuration in the documentation of nginx about HTTP2 also mentions listen 443 ssl http2;

By the way, Apache has support for HTTP2 too :wink:

HTTP/2 implies TLS in practice, because all browser vendors decided to implement it only with TLS.

I wonder if skipping ssl means that only clients supporting HTTP/2 can use TLS, though. Would be surprising if SSL Labs doesn’t catch that. Might be a good idea for @dominikwilkowski to try the site with a client without HTTP/2 support, just in case.

Hi dominikwilkowski,

Excellent question! :slight_smile:
I had lots of questions like yours, i.e. HTTP/2 with NGINX frequently, so I decided to write a post about it:

The Diffie-Hellman with 1024 and 2048 is considered weak, because using massive precomputation (with clouds like AWS, Azure and Google Cloud) that’s no longer state of the art.

Besides you should also stay away from SSL v2, SSL v3 and TLS 1.0. Secure as of today are only TLS v1.1 and v.1.2!

So in the NGINX conf it looks like this:

ssl_protocols TLSv1.1 TLSv1.2;

How to Activate HTTP/2 with TLS Encryption in NGINX for Secure Connections without a Performance Penalty

I hope it helps! :slight_smile:

Cheers,

1 Like

@kelunik Done that though no change in classification. Still A but I will follow @CloudInsidr post to get a better rating.

1 Like

@pfg No issues when surfing the website with IE9:

So I assume http2 falls back to SSL

@CloudInsidr Thanks. This is a great resource. I will likely also spread the word and, with your permission, will link to your article.

Question though: What is the & db part in sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096 & bg?

1 Like

Ah it does the job in the background… Got it :slight_smile:

I did your changes to this:

# Settings
server_tokens off;

server {
	listen       80;
	server_name  [domain];
	return 301   https://$host$request_uri;
}

server {
	listen       443 http2;
	listen       [::]:443 http2;
	server_name  [domain];
	
	ssl on;
	ssl_certificate /etc/letsencrypt/live/[domain]/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/[domain]/privkey.pem;
	
	ssl_session_timeout 1d;
	ssl_session_cache shared:SSL:50m;
	ssl_session_tickets off;
	
	ssl_protocols TLSv1.1 TLSv1.2;
	ssl_prefer_server_ciphers on;
	ssl_dhparam /etc/nginx/ssl/dhparam.pem;
	ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
	
	# OCSP Stapling ---
	# fetch OCSP records from URL in ssl_certificate and cache them
	ssl_stapling on;
	ssl_stapling_verify on;
	
	# HSTS (requires ngx_http_headers_module) 
	# 15768000 seconds = 6 months
	# in seconds, 365 days, including subdomains
	# do NOT use max-age of zero; it will disable the policy!
	add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always";
}

Unfortunately it still doesn’t give me A+ though:

I tried to disable TLSv1.1 as well and ran the test again. No dice.

1 Like

What’s your domain? Maybe we can find out the difference then if we have a full report.

@kelunik https://gel.westpacgroup.com.au

1 Like

Wow, congratulations, you got on top of it in no time:-)

& bg sends the task to the background (longer keys tend to take some measurable time, so should the remote connection be interrupted, your remote machine will still get to complete the Diffie-Hellman group even though your shell is no longer active).

You may also want to deactivate TLS 1.0, as it is vulnerable to downgrading attacks. Adding security headers and pinning the certificates can also enhance security. Here is more on that:

Glad you liked the other article:-) Hopefully this one will also be of help. Should you have any questions, please feel free to shoot a comment!

Hi Dominik,

With NGINX-specific questions (not related to Letsencrypt), feel free to join the official NGINX group:

https://www.linkedin.com/groups/2000893

and we will do our best to ease your transition:-)
Also, please link to CloudInsidr.com so others may find helpful information.

Anna E. Kobylinska & Filipe Martins

Hi Dominik,

We also implemented HTTP Public Key Pinning (HPKP) as you can see in the attached screenshot and described in:

"Pin your public keys

The Public Key Pinning Extension for HTTP (HPKP) tells a web client to associate a specific cryptographic public key with a certain web server so as to prevent MITM (man-in-the-middle) attacks with forged certificates.(…)"

Here is to do it:

Filipe

There’s no HSTS header sent. Did you forget to reload your server config after adding the header?

Hey

I did but I still have issues with the header. I think @CloudInsidr is right, we need to take this into a NGINX forum rather than here as it has nothing to do with letsencrypt.

Thanks guys for the help.

I’ll post a follow up once I get it all sorted to close this off here.