Try Let’s Encrypt with custom NGINX installation

what

Let’s Encrypt is an awesome project providing free SSL certs to the world. In Dec 3, 2015, it entered public beta.

It says:

Anyone who owns a domain name can use Let’s Encrypt to obtain a trusted certificate at zero cost.

why

For the glory of Santa of course!

Well… Because security is important, so we should take enabling https into consideration.

why this post

However, the client doesn’t have full support for NGINX yet. Moreover, if admin is using custom installation of Apache or NGINX, the automatic plugins of the client may not work properly. So we want to manually generate and install the certs.

how

Assuming a proper NGINX is working smoothly without HTTPS. The following commands run inside the server runing the website(s) of the domain(s). Take hellossl.example.com for example.

install the client

Firstly, install the client:

git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
sudo ./bootstrap/install-deps.sh
./bootstrap/dev/venv.sh
source ./venv/bin/activate
letsencrypt --help all

generate the certs

Then, use webroot mode to generate the certs (it generates private key as well):

# assuming web root of hellossl.example.com is /var/www/hellossl.example.com/
letsencrypt certonly --email admin@example.com --agree-tos --webroot -w /var/www/hellossl.example.com/ -d hellossl.example.com --rsa-key-size 4096 --text 
## ...
## - Congratulations! Your certificate and chain have been saved at
##    /etc/letsencrypt/live/hellossl.example.com/fullchain.pem. Your cert
##    will expire on 2016-03-03. To obtain a new version of the
##    certificate in the future, simply run Let's Encrypt again.
## - Your account credentials have been saved in your Let's Encrypt
##   configuration directory at /etc/letsencrypt. You should make a
##   secure backup of this folder now. This configuration directory will
##   also contain certificates and private keys obtained by Let's
##   Encrypt so making regular backups of this folder is ideal.
## - If you like Let's Encrypt, please consider supporting our work by:
## 
##    Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
##    Donating to EFF:                    https://eff.org/donate-le

In fact, the client creates a directory in the web root, and generates some important files in its directory:

tree -a /var/www/hellossl.example.com/
## .
## ├── index.html
## ├── ... # something
## └── .well-known
##     └── acme-challenge

ls -al /etc/letsencrypt/live/hellossl.example.com/
## cert.pem  chain.pem  fullchain.pem  privkey.pem

Note: I am confused how the email is used. We could check issue #1310 and post #2603 for details:

tree -a /etc/letsencrypt/accounts
## /etc/letsencrypt/accounts
## └── acme-v01.api.letsencrypt.org
##     └── directory
##         └── <hash>
##             ├── meta.json
##             ├── private_key.json
##             └── regr.json

configure the NGINX

Mozilla provides an excellent tool about configurating SSL.

Cipherli.st provides suggestions on strong ciphers.

We may also want to rewrite the http site to https using rewrite.

So, edit the NGINX virtual host configuration file as follow:

server {
    listen 80;
    #listen [::]:80;
    server_name hellossl.example.com;
    root /var/www/hellossl.example.com;
    location / {
        rewrite ^ https://$server_name$request_uri permanent;
    }
    location /.well-known {
        try_files $uri $uri/ =404;
    }
}

server {
    listen 443 ssl http2; # http2 enabled here
    #listen [::]:443 ssl http2;
    server_name hellossl.example.com;

    # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
    ssl_certificate /etc/letsencrypt/live/hellossl.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hellossl.example.com/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    # Let's Encrypt doesn't care about it here, so comment it here.
    #ssl_dhparam /etc/ssl/certs/dhparam.pem;

    # configuration. tweak to your needs.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_prefer_server_ciphers on;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

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

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

    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /etc/letsencrypt/live/hellossl.example.com/chain.pem;

    #resolver <IP DNS resolver>;

    root /var/www/hellossl.example.com;
    location /.well-known {
        try_files $uri $uri/ =404;
    }
 
    # ...
}

Then reload nginx:

nginx reload

check the result

Go to http://hellossl.example.com, if everything goes right, you should be redirected to https://hellossl.example.com without any errors! WOW!

bonus: ssl_dhparam

ssl_dhparam is a way to enhance security.

Generate dhparam.pem firstly:

# long time to run!!!
openssl dhparam -out dhparam.pem 4096
# place the cert somewhere
sudo mv dhparam.pem /etc/ssl/certs/dhparam.pem

Then uncomment the relatived line in NGINX configuration:

server {
    # ...
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    # ...
}

Then reload the NGINX.

nginx reload

conclusion and other things

It works! And it’s very cool!

And we should notice that the certificate will expire in 90 days. So if we use it in production, we should set up something like crontab running automatically, say, every month, to renew the certificate.

For simplified steps, we could also try less complicated unofficial clients diafygi/acme-tiny, diafygi/letsencrypt-nosudo, or kuba/simp_le.