Skip to content

Instantly share code, notes, and snippets.

@leommoore
Last active August 8, 2018 10:37
Show Gist options
  • Save leommoore/53f3723cc023c39feb46 to your computer and use it in GitHub Desktop.
Save leommoore/53f3723cc023c39feb46 to your computer and use it in GitHub Desktop.
Letsencrypt Ubuntu 14.04 Nginx

#Letsencrypt Ubuntu 14.04 Nginx Letsencrypt (https://letsencrypt.org) is an initative which aims to increase the use of encryption for websites. It basically allows people to apply for free certificates provided that they prove the they control the requested domain.

Note: As of 8th March 2016 letsencrypt is still in public beta.

##Installation To install the client, clone the repostiory from github.

git clone https://github.com/letsencrypt/letsencrypt.git
cd letsencrypt

##Configure Nginx To prove that you have control of the domain for the requested certificate you need to show that you can allow letsencrypt.org to access a given file over the web. Therefore when you ask for a certificate the client will generate a unique id which should be put in a file on the web server. This is then accessed over the web to show that you control the domain.

The first step is to setup nginx so that it will serve up the requested file when asked. letsencrypt.org is expecting to find the file in domain/.well-known/acme-challenge/ location (eg www.example.com/.well-known/acme-challenge). To setup this location, first setup the physical location. Since the default location for nginx is /usr/share/nginx/html, that is a good place. Create a specific location for each site (ie /usr/share/nginx/html/www.example.com).

cd /usr/share/nginx/html/www.example.com
mkdir -p .well-known/acme-challenge
cd .well-known/acme-challenge

This is the location where the file will need to be created, once the cert is requested.

To enable nginx to serve up the site we will need to edit the virtual site which is normally located in /etc/nginx/sites-available/

nano /etc/nginx/sites-available/www.example.com

The config file should be like this:

server {
    listen   80;
    listen   [::]:80;

    root /usr/share/nginx/html/www.example.com;
    server_name www.example.com;

    location /.well-known/acme-challenge {
        default_type text/plain;
    }

    error_page 404 /404.html;
}

Don't forget to reload the web server configuration (service nginx reload or systemctl reload nginx).

##Request the Certificate

./letsencrypt-auto certonly -a manual --rsa-key-size 4096 -d www.example.com

You can request more than one domain

./letsencrypt-auto certonly -a manual --rsa-key-size 4096 -d www.example.com -d example.com

##Verify Domain Ownership

The next step is to provide a file with certain content available under a special URL ie (www.example.com/.well-known/acme-challenge).

The request will generate a similar response like:

Make sure your web server displays the following content at                                                                                                                    
http://www.example.com/.well-known/acme-challenge/A-xjoIljw52X2SSQWqf9P5TQU9vv4HuPDBXN4qFDoRU before continuing:

A-xjoI1jw52X2SSQWqf9P5TQU9vv4HuPDBXN4qFDoRU.eefJYv1muREVb9TpEb3qjr9AQsM8IurTn-Svykj0wN0

If you don't have HTTP server configured, you can run the following
command on the target server (as root):

mkdir -p /tmp/letsencrypt/public_html/.well-known/acme-challenge
cd /tmp/letsencrypt/public_html
printf "%s" A-xjoI1jw52X2SSQWqf9P5TQU9vv4HuPDBXN4qFDoRU.eefJYv1muREVb9TpEb3qjr9AQsM8IurTn-Svykj0wN0 > .well-known/acme-challenge/A-xjoI1jw52X2SSQWqf9P5TQU9vv4HuPDBXN4qFDoRU
# run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
"import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
s.serve_forever()" 
Press ENTER to continue

##Create the Verification File To create the file you will need a new terminal session. Do not click ENTER or it will try and find the file. In a new terminal session:

cd /usr/share/nginx/html/www.example.com
printf "%s" A-xjoI1jw52X2SSQWqf9P5TQU9vv4HuPDBXN4qFDoRU.eefJYv1muREVb9TpEb3qjr9AQsM8IurTn-Svykj0wN0 > .well-known/acme-challenge/A-xjoI1jw52X2SSQWqf9P5TQU9vv4HuPDBXN4qFDoRU

This will create the required file. Now click ENTER in the original terminal session. The correct response will look like:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.example.com/fullchain.pem. Your cert will
   expire on 2016-03-06. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.
 - If 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

##Configuring Nginx to use the SSL Certificate. The www.example.com configuration file should look like:

# NON-SSL
server {
    listen   80;
    listen   [::]:80;
    
    root /usr/share/nginx/html/www.example.com;

    server_name www.example.com;
    
    # Should match this first
    location /.well-known/acme-challenge {
        default_type text/plain;
    }
    
    # Then match this
    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }   
}

# SSL
server {
    root /usr/share/nginx/html/www.example.com;

    listen 443 ssl spdy;
    server_name www.example.com;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;

    ssl_stapling on;
    ssl_stapling_verify on;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    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$
    #ssl_dhparam /etc/nginx/ssl/dhparams.pem;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    
    
    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";            
              
	location ~* /\.\./ {
        deny all;
        return 404;
    }

    location / {
        proxy_pass http://127.0.0.1:4000; #app_servers 
        proxy_set_header Host      $host;
	proxy_set_header X-Real-IP $remote_addr;
    }
    
    location /.well-known/acme-challenge {
        default_type text/plain;
    }

    error_page 404 /404.html;
}

##SSL Check You can use https://www.ssllabs.com/ssltest to test your SSL configuration. The best result is an A+. To get a score higher than a B you will need to setup dhparams.

##dhparams To create a dhparams.pem certificate, first create the directory.

mkdir /etc/nginx/ssl
cd /etc/nginx/ssl

Then run the command to create the cert. Note I have choosen a 4096 bit certificate to make it future proof. This can take a while to run depending on the server.

openssl dhparam -out dhparams.pem 4096

Then add the following to the www.example.com configuration file.

ssl_dhparam /etc/nginx/ssl/dhparams.pem;

You should then be able to get a A+ rating.

##Renewing Your Certs To renew the cert, first stop the nginx server to allow letsencrypt to connect.

service nginx stop

You can do a dry run to check that everything is working. Note you have to use --agree-tos to agree the letsencrypt terms of service and since we are doing a certonly we need to allow manual ip logging using the --manual-public-ip-logging-ok switch and then specify the domain with -d www.example.com.

./letsencrypt-auto certonly --dry-run --agree-tos --manual-public-ip-logging-ok -d www.example.com

If that all works fine the do it for real.

./letsencrypt-auto certonly --agree-tos --manual-public-ip-logging-ok -d www.example.com

Then restart nginx to use the new certificate.

service nginx stop

##Certificate Renewal Notification You can use a really useful site http://certificatemonitor.org/ from Remy van Elst to let you know when the cert is going out of date. Just put in the domain and email to receive the notification.

@adamneilson
Copy link

This line for nginx conf was failing for me:

    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$

I changed it to this which seems to work:

    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;

PS: Thanks for writing this!!!

Glad it helped Adam. I made the change you suggested and it works fine too, so I amended the conf as it looks cleaner the way you suggested. Thanks - Leo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment