Fully automated Let's Encrypt integration with Cpanel
Applicable To: All servers/hostings with Cpanel and apache/apache2
Updated: Friday, 04. October 2019 04:46 PM UTC
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.
You only need to have certain basic knowledge about managing a server to follow through this tutorial:
Required:
Recommended:
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).
First create a directory named letsacme. We will use this directory to keep necessary files for the letsacme script.
mkdir ~/letsacme
cd ~/letsacme
Run the following command to download letsacme:
wget https://raw.githubusercontent.com/neurobin/letsacme/release/letsacme.py
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.
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.
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
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.
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.
Inside this challenge directory (document root), create another directory named acme-challenge.
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:
We have created two directories so far. In your remote server, directory structure for these two should look like this:
├── /home/user/letsacme
| ├── account.key
| ├── dom.key
| ├── dom.csr
| ├── dom.list
| ├── gencsr
| ├── gencsr.conf
| ├── letsacme.py
| ├── renewcert.sh
| └── sslic.php
Sub-domain document root: /home/user/challenge
├── /home/user/challenge
| └── acme-challenge
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.
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.
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.