Hosting HTTPS sites with Nginx

Youtube Version !

Indroduction

What is SSL/HTTPS/TLS?

  • SSL - Secure Sockets Layer
    • It is the standard technology used to encrypt the communication between the client and the server.
    • This is done to avoid any man in the middle attacks.
  • HTTPS - Hyper Text Transfer Protocol Secure
    • HTTPS appears in the URL when a website is using SSL or TLS to encrypt communication between the client and the server.
  • TLS - Transport Layer Security
    • It is the updated and more secure version of SSL. However, SSL is still the more commonly used term while the underlying platform may still be using TLS for encryption.
    • Current version of TLS is 1.3. It supports perfect forward secrecy in which a one time key is generated for each network session. This protects from any offline decryption attempts.

Why SSL/HTTPS/TLS?

  • Encryption
    • The communication between the client and the server is encrypted. Even if someone gets in between you two, all that they will see is gibberish text.
  • Data Inegrity
    • It makes sure that the data isn't changed in between. The data sent from the client is what the server is recieving and vice versa.
  • Authentication
    • When you access a website and it presents you with that trusted certificate, then you can be certain that the site is who it claims to be.
  • Trust/Reputation
    • If you are reading an article and the website doesn't have TLS encryption, it doesn't really matter much. However, if you need to send some of your personal information, you shouldn't do it over a plain http website. Users see TLS as something they trust.
  • SEO
    • Google nowadays, when something is searched, kinda prefers a site with TLS encryption.

Hosting HTTPS

  • Force HTTPS
    • Now, users while browsing your website, usually might just enter, www.yoursite.com. It is a http traffic which you need to redirect to https.
#Redirect HTTP traffic to HTTPS (both www and non-www)
server {
	listen 80;
	server_name mydomain.com www.mydomain.com;
	
	#Permanent redirect to HTTPS version with www prefix
	return 301 https://$host$request_uri;
}
  • Redirecting non-www to www version
    • It doesn't usually matter if the user browses the www version or the non-www version as long as you are serving the same content.
server {
	listen 443 ssl;
	server_name example.com;
	
	ssl_certificate /path/to/cert.pem
	ssl_certificate_key /path/to/cert.pem
	
	return 301 $scheme://www.$host$request_uri;
}

Now, sample configuration:

server {
	listen 443 ssl http2;
	listen [::]:443 ssl;
	server_name www.mydomain.com;
	root /usr/share/nginx/html;
	
	ssl_certificate "/etc/pki/nginx/server.crt";
	ssl_certificate_key "/etc/pki/nginx/private/server.key";
	ssl_dhparam /etc/ssl/certs/dhparam.pem;
	
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_session_cache shared:SSL:1m;
	ssl_session_timeout 10m;
	ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
	ssl_prefer_server_ciphers on;
	#ocsp stapling, nginx > 1.3.7
	resolver 8.8.8.8 valid=300s;
	resolver timeout 5;
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_trusted_certificate /etc/pki/nginx/private/chain.pem;
	add_header Strict-Transport-Security "max-age=31536000" always; # 1 year
	add_header X-Frame-Options DENY;
	add_header X-Content-Type-Options nosniff;
	
	location / {
	}
	
	error_page 404 /404.html;
		location = /40x.html {
	}
	
	error_page 500 502 503 504 /50x.html;
		location = /50x.html {
	}
}

Understanding the config file:

The first listen directive listen 443 ssl http2; instructs nginx to listen on port 443 for the ssl and then we are enabling http version 2
The next one listen [::]:443 ssl; is for listening on IPv6. If you do not want to host anything on IPv6, you can skip it.
server_name www.mydomain.com; specifies the name of this server.
root /usr/share/nginx/html; this is the document root.

	ssl_certificate "/etc/pki/nginx/server.crt";
	ssl_certificate_key "/etc/pki/nginx/private/server.key";
	ssl_dhparam /etc/ssl/certs/dhparam.pem;

Here, with ssl_certificate we define the path to the SSL certificate.
With ssl_certificate_key we define the key that was used to generate the certificate.
The next one, ssl_dhparam this tells nginx where to find the dhparam.pem file. This is useful if you are using the Perfect Forward Secrecy algorithm.

ssl_protocols TLSv1.2 TLSv1.3; This specifies the TLS versions that we are using.

	ssl_session_cache shared:SSL:1m;
	ssl_session_timeout 10m;

This sets the cache to 1 minute and the timeout to 10 minutes.
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; Here we are specifiying what ciphers we want to use.
ssl_prefer_server_ciphers on; Here we instruct nginx to prefer the server ciphers. During the negotiation between the client and the server, the client says these are what my supported ciphers are. Here we are telling nginx to pick the common option. Basically saying if the client supports these ciphers then pick the strongest one out of these.

	#ocsp stapling, nginx > 1.3.7
	resolver 8.8.8.8 valid=300s;
	resolver timeout 5;
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_trusted_certificate /etc/pki/nginx/private/chain.pem;

Here, the ocsp stapling is turned on. It requires nginx versions greater than 1.3.7. What it does is, it looks up the certificate validity and creates a timestamp and that is presented to the client so that the client doesn't need to look it up.

add_header Strict-Transport-Security "max-age=31536000" always; # 1 year Here, we are adding a header. What this does is tells the browser to use the HTTPS version of the site for 1 year.

	add_header X-Frame-Options DENY;
	add_header X-Content-Type-Options nosniff;

Here, we are adding a header X-Frame-Options, setting it to DENY and then adding, X-Content-Type-Options to nosniff.


Lab part:

Generating self signed certs:

Note that you cannot use ocsp stapling with these self signed certificates.

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssldemo.key -out /etc/nginx/ssldemo-selfsigned.crt
Here, you will be prompted multiple times.
When you are asked for the Common Name, this is where you give your server name.

openssl dhparam -out /etc/nginx/dhparam.pem 2048

Now, we have everything we need to configure the server!

vi nginx.conf

Now, add,

server {
	listen 443 ssl http2;
	server_name example.com www.example.com;
	root /var/www/html/example.com/;
	
    ssl_certificate "/etc/nginx/ssldemo-selfsigned.crt";
    ssl_certificate_key "/etc/nginx/ssldemo.key";
    ssl_dhparam /etc/nginx/dhparam.pem;
    
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 10m;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_prefer_server_ciphers on;

}

Once that is done, check if everything is configured correctly with:
nginx -t
systemctl restart nginx
mkdir /var/www/html/example.com
vi /var/www/html/example.com/index.html

This is secure example.com

systemctl restart nginx

Now, let's check if it is listening:

netstat -tupan

You should see that it is up and listening.

Now, open the browser and try to connect to it!