Fully automated Let's Encrypt integration with Cpanel

Applicable To: All servers/hostings with Cpanel and apache/apache2
Updated: Wed 25 Oct, 2017 GMT

This is a step by step process on how you can automate getting and installing Let's Encrypt SSL/TLS certificate.

Preview

In this tutorial, I am not going to explain things in details. I will only give a step by step and straight forward process and most of the time, without any explanation. If you want to know more about the tools/methods used follow through the corresponding links.

Experiences needed

You only need to have certain basic knowledge about managing a server to follow through this tutorial:

Required:

  1. Download/upload files to server.
  2. Using Cpanel.

Recommended:

  1. Using ssh or equivalent terminal to interact with remote server.

Where to make the changes

All of the files/directories will be created/edited/deleted on remote server. How you do it, that's upto you. You can run the commands on your local machine and then upload the files/directories to remote or do this directly on remote with ssh or equivalent terminal. Whatever you do, be sure to double check that the username and paths correspond to your remote machine (not local).

Steps

  1. Prepare necessary files
    1. Download letsacme.
    2. Create account key
    3. Generate a CSR
    4. Download sslic.php
    5. Prepare the renewcert.sh script
    6. Create an empty sub-domain.
    7. Set up redirect code in .htaccess file.
  2. Review the directory structure.
  3. Setup a cron job.
  4. Trigger the renewcert.sh script once.

Prepare necessary files

First create a directory named letsacme. We will use this directory to keep necessary files for the letsacme script.

mkdir ~/letsacme
cd ~/letsacme

Download letsacme

Run the following command to download letsacme:

wget https://raw.githubusercontent.com/neurobin/letsacme/release/letsacme.py

Create account key

Run the following command to create an account key:

openssl genrsa 4096 > account.key

you can use the same account key for any number of certs or installs.

Generate a CSR

To create a CSR we will use a script gencsr. Download the script:

wget https://raw.githubusercontent.com/neurobin/gencsr/release/gencsr
wget https://raw.githubusercontent.com/neurobin/gencsr/release/gencsr.conf
chmod +x gencsr

Now create a file named dom.list and put all domain names (both www and non-www versions) per line. It will look like this:

example.com
www.example.com
subdomain1.example.com
www.subdomain1.example.com
subdomain2.example.com
www.subdomain2.example.com

Note: Put the non-www version of the root domain at the top.

After saving the file, open the file named gencsr.conf and edit it to put correct information:

############# gencsr config file #####################
# Do not use quotation marks (', "")
# To prevent any entry being included, comment them
# by adding a # at the beginning
######################################################
CountryCode=US                              # Put two character country code
State=My state                              # Put state name
Locality=My city                            # Put city name
Oraganization=My organization               # Put organization name
OraganizationUnit=Technology or whatever    # Put organization unit name
Email=mymail@somedomain.com                 # Put email address

After saving the file, run gencsr:

./gencsr

A CSR file named dom.csr should be created in the same directory.

Download sslic.php

sslic is a PHP script to install SSL certificate using UAPI (Cpanel API). It uses cpanel username and password to authenticate its API call, better look through the source code and make sure it's not using your username and password in an insecure way. The API call uses HTTPS connection and thus it's as secure as it can get.

To download run the following command:

wget https://github.com/neurobin/sslic/raw/release/sslic.php

Prepare the renewcert.sh script

We will run this script with cron. This script will contain sensitive data (your cpanel username and password). Make sure to secure it.

Create a file named renewcert.sh and save with the following contents:

#!/bin/sh
user='your cpanel username here'
pass="you cpanel password here"
email='an email address under your domain'

HOME=/home/user                         # change 'user' to your username
ndir=$HOME/letsacme                     # this directory path
chlng=$HOME/challenge/acme-challenge    # challenge directory path
letsacme_log="$ndir"/letsacme.log       # path to letsacme log file

acc_key="$ndir"/account.key             # path to account key file
key="$ndir"/dom.key                     # path to key file that was used to create CSR
csr="$ndir"/dom.csr                     # path to CSR file
dom_crt="$ndir"/dom.crt                 # path to cert file
chain_crt="$ndir"/chain.crt             # path to chain file
full_crt="$ndir"/fullchain.crt          # path to fullchain file
dom_file="$ndir"/dom.list               # path to a file containing domain names per line

letsacme="$ndir"/letsacme.py            # path to letsacme.py
sslic="$ndir"/sslic.php                 # path to sslic.php

# max try depends on how often you will run this script.
max_try=20 # don't change it unless you know what you are doing

#######################################################################
# You don't need to change anything below this line
#######################################################################

export PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

test=false

if $test; then
    test_flag=--test
else
    test_flag=
fi

print_err(){
    echo "$@" >>/dev/stderr
}

print_out(){
    echo "$@" >>/dev/stdout
}
print_out "### Logs for renewcert script ###"

count=0
st="$(date -u)"

# init log file. Log files will always contain current logs
echo '### Logs for letsacme script ###' > "$letsacme_log"

while true;do
    count=$(expr $count + 1)
    if [ $count -gt $max_try ]; then
        # Failed
        msg="Failed to renrew certificate ($dom_crt) from $st to $(date -u)"
        sub="Failed to renrew certificate"
        echo "$msg" | mail -s "$sub" $email
        print_err "$msg"
        exit 1
    fi
    if python "$letsacme" \
        --account-key "$acc_key" \
        --csr "$csr" \
        --acme-dir "$chlng" \
        --cert-file "$dom_crt.tmp" \
        --chain-file "$chain_crt.tmp" $test_flag \
        > "$full_crt.tmp" \
        2>> "$letsacme_log"
    then
        cp "$dom_crt.tmp" "$dom_crt"
        cp "$chain_crt.tmp" "$chain_crt"
        cp "$full_crt.tmp" "$full_crt"
        # send an email
        msg="Successfully renewed certificate ($dom_crt) @ $(date -u)"
        sub="Renewed SSL certificate"
        echo "$msg" | mail -s "$sub" $email
        # log the success msg
        print_out "$msg"
        # install the certificate

        doms="$(sed -e '/^[[:blank:]]*$/d' \
            -e 's/^[[:blank:]]*//' \
            -e '/^[[:blank:]]*www\..*$/d' "$dom_file")"
        for dom in $doms; do
            USER="$user" PASS="$pass" EMAIL="$email" php "$sslic" "$dom" "$dom_crt" "$key" "$chain_crt"
            sleep 5
        done
        break
    else
        sleep `tr -cd 0-9 </dev/urandom | head -c 4`
        # sleep for max 9999 seconds, then try again
        print_err "Retry triggered at $(date -u)"
    fi
done

Restrict permission to allow only owner to have access to this file:

chmod 600 renewcert.sh

The above command must be run on remote server. If you can't run commands on remote server, then go to file manager in cpanel after uploading the file and change its permission to 600. This is very important, do not skip it. Skipping this security step will not cause any problems in later steps, but it will leave your system open for attackers.

Create a sub-domain

Log in to your cpanel and create a sub-domain named challenge in your home directory i.e put challenge for Document Root in the text box when creating the sub-domain. Do not install SSL in this sub-domain. Also do not install any frameworks. Keep it simple HTTP. Even better, if you dedicate this subdomain for this purpose only and refrain from using it in any other way.

creating sub-domain in cpanel

Inside this challenge directory (document root), create another directory named acme-challenge.

Setup redirect code

Now open the .htaccess (create if doesn't exist) files in every document root for every domain (specified in the dom.list file) and add these lines at the beginning:

RewriteEngine On
RewriteBase /
# change challenge.your-domain.org to your actual domain
RewriteRule ^.well-known/acme-challenge/(.*)$ http://challenge.your-domain.org/acme-challenge/$1 [L,R=302]

Notes:

  1. We are adding those lines at the beginning to make the above rewrite rule the first rewrite rule in the .htaccess file.
  2. challenge.your-domain.org is the subdomain that was created recently.

Review the directory structures

We have created two directories so far. In your remote server, directory structure for these two should look like this:

letsacme directory

├── /home/user/letsacme
|   ├── account.key
|   ├── dom.key
|   ├── dom.csr
|   ├── dom.list
|   ├── gencsr
|   ├── gencsr.conf
|   ├── letsacme.py
|   ├── renewcert.sh
|   └── sslic.php

challenge directory

Sub-domain document root: /home/user/challenge

├── /home/user/challenge
|   └── acme-challenge

Setup a cron job

If you have done all the steps above, it should be OK now to run the renewcert.sh script to get and install the certificate.

To set up a cron job, login to your Cpanel and go to the Cron job section.

cpanel cron job section

cpanel cron job setup

In the command section, put this:

/bin/sh /home/user/letsacme/renewcert.sh >/home/user/letsacme/renewcert.log 2>/home/user/letsacme/renewcert_err.log

Replace 'user' with your actual user name in the above command.

The final cron job will look like this:

0 0 1,15 * * /bin/sh /home/user/letsacme/renewcert.sh >/home/user/letsacme/renewcert.log 2>/home/user/letsacme/renewcert_err.log

Do not forget to replace 'user' with your actual username.

The above is a cron job that will run 1st and 15th day of every month (once in every 15 days) i.e your certificate will be renewed and installed every 15 days.

Trigger the renewcert.sh script

Edit the cron (There's edit/delete option in that cpanel window) to trigger a renew in 1 minute: Change all (Minute, Hour, Day, Month) sections to *. After one minute change it back. You will get email notification about the success or failure messages.

Once the renewcert.sh is run, some additional files will be available in the letsacme directory:

├── /home/user/letsacme
|   ├── chain.crt
|   ├── dom.crt
|   ├── fullchain.crt
|   ├── letsacme.log
|   ├── renewcert.log
|   ├── ...

That's all.. Sit back and enjoy the fruits of automation.

Security measures

Give 700 permission to the letsacme directory and strip all permissions for group and others from all files inside this directory:

The following commands will accomplish that:

cd /home/user/letsacme #change 'user' to your cpanel username
chmod 700 .
chmod -R g=,o= *

Tips for windows users

You may face problems if you edit the files in windows with a rich text editor like wordpad, notepad etc.. Use notepad++ or other pure text editor, otherwise you will have issues regarding CR/LF (the newline character).

To fix this problem you can also use the dos2unix tool that comes by default in most Linux OSs. With dos2unix you can fix such a problematic file with the following command:

dos2unix problematic_file_path