
Why add an SSL certificate / use HTTPS?
The HTTP protocol commonly used on the internet transmits data in plaintext. Traffic is “transparent” on the network and can be abused by a man-in-the-middle.
In recent years, ISP hijacking has become more frequent and the barrier to launching attacks has gotten lower. For better security, use HTTPS to access your website. HTTPS provides strong encrypted transport and helps prevent transmitted data from being leaked or tampered with.
Preparation & environment
Server environment: DigitalOcean VPS, SG region, Debian Jessie, AMH hosting panel.
You’ll need:
- A free SSL certificate: Let’s Encrypt
- NGINX
- OpenSSL
Installation
Install & upgrade to OpenSSL 1.0.2
To enable HTTP/2 in NGINX, you must use this version of OpenSSL.
Note that accepting HTTP/2 connections over TLS requires the “Application-Layer Protocol Negotiation” (ALPN) TLS extension support, which is available only since OpenSSL version 1.0.2. Using the “Next Protocol Negotiation” (NPN) TLS extension for this purpose (available since OpenSSL version 1.0.1) is not guaranteed.
Download the OpenSSL source from the official site or GitHub:
wget https://github.com/openssl/openssl/archive/OpenSSL_1_0_2h.tar.gz
tar -zxvf ./OpenSSL_1_0_2h.tar.gz
cd ./openssl-OpenSSL_1_0_2h/
Choose an installation directory. If you’re upgrading, set the --prefix parameter to the directory where OpenSSL is installed:
./config --prefix=/usr/local/openssl --openssldir=/usr/local/ssl
make
make install
mv /usr/bin/openssl /usr/bin/openssl.OFF
mv /usr/include/openssl /usr/include/openssl.OFF
ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/openssl/include/openssl /usr/include/openssl
echo "/usr/local/openssl/lib">>/etc/ld.so.conf
Verify the OpenSSL version is now 1.0.2h:
openssl version -a
Install the Let’s Encrypt client
Fetch source code:
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
Install:
./letsencrypt-auto --help
Issue a certificate. Use -w for your website root path and -d for domain names. The certificate is based on the first domain. See the official documentation for details:
./letsencrypt-auto certonly --webroot -w /www/blog/ -d gorgiaxx.com -d blog.gorgiaxx.com
The certificate is valid for 3 months; you can renew it via a script (cron job):
0 0 1 * * /usr/local/nginx/sbin/nginx -s stop && /opt/letsencrypt-auto certonly --renew-by-default --webroot -w /www/blog/ -d gorgiaxx.com -d blog.gorgiaxx.com && /usr/local/nginx/sbin/nginx
Certificates are generated under this directory by default. NGINX will use the last two files:
root@gorgiaxx-ubuntu:~/tmp# ls /etc/letsencrypt/live/gorgiaxx.com
cert.pem chain.pem fullchain.pem privkey.pem
Enable Certificate Transparency (CT)
Certificate Transparency was led by Google and standardized by the IETF as RFC 6962. The goal of CT is to provide an open auditing and monitoring system that enables any domain owner or CA to verify whether a certificate was mis-issued or maliciously used, thereby improving the security of HTTPS websites.
At the time of writing, CT is only supported by Chrome. Chrome also “knows” that CT adoption is not yet widespread, so the impact for HTTPS sites that don’t provide SCT information is not significant.
For details, see Certificate Transparency 那些事.
With the ct-submit module, you can conveniently submit certificates to CT log servers and obtain SCT responses:
apt-get install golang
wget -O ct-submit.zip -c https://github.com/grahamedgecombe/ct-submit/archive/v1.0.0.zip
unzip ct-submit.zip
cd ct-submit-1.0.0
go build
After building, execute and submit. Known CT log servers can be found here (blocked in some regions):
./ct-submit-1.0.0 ct.googleapis.com/aviator
</etc/letsencrypt/live/gorgiaxx.com/fullchain.pem
>/etc/letsencrypt/live/gorgiaxx.com/scts/aviator.sct
./ct-submit-1.0.0 ct.googleapis.com/pilot
</etc/letsencrypt/live/gorgiaxx.com/fullchain.pem
>/etc/letsencrypt/live/gorgiaxx.com/scts/pilot.sct
./ct-submit-1.0.0 ct.googleapis.com/rocketeer
</etc/letsencrypt/live/gorgiaxx.com/fullchain.pem
>/etc/letsencrypt/live/gorgiaxx.com/scts/rocketeer.sct
Install & upgrade NGINX
Download and extract NGINX and the nginx-ct module:
wget -O nginx-ct.zip https://github.com/grahamedgecombe/nginx-ct/archive/master.zip
unzip nginx-ct.zip
http://nginx.org/download/nginx-1.10.0.tar.gz
tar -zxvf nginx-1.10.0.tar.gz
/usr/local/nginx is my existing NGINX directory. I put installation files under /tmp/ temporarily.
Add brotli compression support for a better compression ratio (compatible with gzip):
git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli
git submodule update --init
./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-http_gzip_static_module --with-http_v2_module --with-http_stub_status_module --add-module=../nginx-ct-1.3.2 --add-module=../ngx_brotli
make
If make fails, add this to configure:
--with-openssl=/tmp/openssl-1.0.2h/
Replace the old binary:
cp -f objs/nginx /usr/local/nginx/sbin/nginx
Upgrade:
make upgrade
Configure NGINX
Change the listen port to 443 and add ssl and http2:
listen 443 ssl http2 default; #listen end
Enable ssl and ssl_ct, and add the SCT directory plus the certificate paths:
ssl on;
ssl_ct on;
ssl_ct_static_scts /etc/letsencrypt/live/gorgiaxx.com/scts/;
ssl_certificate /etc/letsencrypt/live/gorgiaxx.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/gorgiaxx.com/privkey.pem;
Reload NGINX:
/usr/local/nginx/sbin/nginx -s reload
As shown below, HTTP/2 and the CT policy have been enabled:

CT is enabled:

——————— Update (Nov 12, 2016): —————————
Newer versions of Let’s Encrypt use certbot to deploy certificates:
./certbot-auto certonly --standalone --email [email protected] -d gorgiaxx.com -d www.gorgiaxx.com -d blog.gorgiaxx.com -d api.gorgiaxx.com
Automatic renewal:
0 0 1 * * /usr/local/nginx/sbin/nginx -s stop && /opt/certbot-auto renew && /usr/local/nginx/sbin/nginx
——————— Update (Feb 9, 2017): —————————–
I kept getting expiration reminders (╯‵□′)╯︵┻━┻ — turns out certbot auto-updates itself on startup and depends on virtualenv. Since virtualenv wasn’t installed on the VPS, automatic renewal failed.