Let’s Encrypt on cPanel, automated!

Notes:

  • This will not work if you use cPanel/Bluehost’s EasyApache Symlink patch, as LetsEncrypt creates files in a docroot that’s owned by root. I’d advise looking into a different Symlink Protection method.
  • If you have subdomains that have their own cPanel accounts, this may error if you do not have www CNAME records in place for www.sub.domain.com.

I would recommend ensuring that your DNS zone matches what is provided by WHM/cPanel if you host your DNS somewhere other than the server on which this script will run, or there will be errors if subdomains listed on the server are not reachable over the internet.

There are two scripts and a cronjob involved after to make it all work.

Initial setup:

cd /root/
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
mkdir cpanelautomate
cd cpanelautomate

The main script:

vim letsencrypt-automate-cpanel.sh

Add the following, changing the $email variable to your email address:

#!/bin/bash
# Set script location
scriptloc=/root/letsencrypt/cpanelautomate
# Set admin email address
email=YOUREMAILHERE

# Ensure all files created by this script are empty before we start
cat /dev/null > $scriptloc/letsencrypt-automate-data.txt

# If $scriptloc/domains.txt exists, use it

if [ -e $scriptloc/domains.txt ];
    then
# Find docroots and dump to $scriptloc/letsencrypt-autmate-data.txt

for domain in $(grep -Ev '^[[:blank:]]*#|^[[:blank:]]*;|^[[:blank:]]*$' $scriptloc/domains.txt | awk '{print $1}' $scriptloc/domains.txt);
do
    serveralias=$(grep -e ^[Ss]erver[Aa]lias $(find /var/cpanel/userdata -type f -name $domain) | awk '{for (i=2; i<=NF; i++) print $i}' | grep -v ipv6 | sed ':a;N;$!ba;s/\n/ -d /g' | sed 's/^/-d /g' );
    docroot=$(grep -e ^[Dd]ocument[Rr]oot $(find /var/cpanel/userdata/ -type f -name $domain) | awk '{print $2}');
    if [[ -z "$(grep -e ^[Dd]ocument[Rr]oot $(find /var/cpanel/userdata/ -type f -name $domain))" ]];
        then
        continue
    else
        echo "$domain ;; $serveralias ;; $docroot" >> $scriptloc/letsencrypt-automate-data.txt
    fi
done

# Else do all domains

else
# Find domains and their docroots and dump to $scriptloc/letsencrypt-autmate-data.txt

for domain in $(awk '{print substr($1, 1, length($1)-1)}' /etc/userdomains | grep -v '*');
do
    serveralias=$(grep -e ^[Ss]erver[Aa]lias $(find /var/cpanel/userdata -type f -name $domain) | awk '{for (i=2; i<=NF; i++) print $i}' | grep -v ipv6 | sed ':a;N;$!ba;s/\n/ -d /g' | sed 's/^/-d /g');
    docroot=$(grep -e ^[Dd]ocument[Rr]oot $(find /var/cpanel/userdata/ -type f -name $domain) | awk '{print $2}');
    if [[ -z "$(grep -e ^[Dd]ocument[Rr]oot $(find /var/cpanel/userdata/ -type f -name $domain))" ]];
        then
        continue
    else
        echo "$domain ;; $serveralias ;; $docroot" >> $scriptloc/letsencrypt-automate-data.txt
    fi
done
fi

#Let's Encrypt!

while IFS= read -r line
do
# Check if there is a directory called `public` inside public_html, use it if it does.
    if [ -d $(echo $line | awk -F " ;; " '{print $3}')/public ]; then
        webroot=$(echo $line | awk -F " ;; " '{print $3}')/public
    else
        webroot=$(echo $line | awk -F " ;; " '{print $3}')
    fi

    /root/letsencrypt/letsencrypt-auto --text certonly --agree-tos --email $email --renew-by-default --webroot --webroot-path $webroot -d $(echo $line | awk -F " ;; " '{print $1}') $(echo $line | awk -F " ;; " '{print $2}');
    $scriptloc/installssl.pl $(echo $line | awk -F " ;; " '{print $1}');
done < $scriptloc/letsencrypt-automate-data.txt

#Remove last unneeded file
rm -f $scriptloc/letsencrypt-automate-data.txt;

Make this executable:

chmod +x letsencrypt-automate-cpanel.sh

Create the script to auto-install the SSL's using WHM's API:

Credit for this script goes to the cPanel thread here. Script was edited to use the WHM accesshash over root password for obvious security reasons.

vim installssl.pl

Add the following, changing YOURACCESSHASH to your WHM access hash from:

WHM -> Clusters -> Remote Access Key

#!/usr/local/cpanel/3rdparty/bin/perl
use strict;
use LWP::UserAgent;
use LWP::Protocol::https;
use MIME::Base64;
use IO::Socket::SSL;
use URI::Escape;
my $hash = "YOURACCESSHASH";
$hash =~ s/\n//g;
my $auth = "WHM root:" . $hash;
my $ua = LWP::UserAgent->new(
ssl_opts   => { verify_hostname => 0, SSL_verify_mode => 'SSL_VERIFY_NONE', SSL_use_cert => 0 },
);
my $dom = $ARGV[0];
my $certfile = "/etc/letsencrypt/live/$dom/cert.pem";
my $keyfile = "/etc/letsencrypt/live/$dom/privkey.pem";
my $cafile =  "/etc/letsencrypt/live/$dom/chain.pem";
my $certdata;
my $keydata;
my $cadata;
open(my $certfh, '<', $certfile) or die "cannot open file $certfile";
{
local $/;
$certdata = <$certfh>;
}
close($certfh);
open(my $keyfh, '<', $keyfile) or die "cannot open file $keyfile";
{
local $/;
$keydata = <$keyfh>;
}
close($keyfh);
open(my $cafh, '<', $cafile) or die "cannot open file $cafile";
{
local $/;
$cadata = <$cafh>;
}
close($cafh);
my $cert = uri_escape($certdata);
my $key = uri_escape($keydata);
my $ca = uri_escape($cadata);
my $request = HTTP::Request->new( POST => "https://127.0.0.1:2087/json-api/installssl?api.version=1&domain=$dom&crt=$cert&key=$key&cab=$ca" );
$request->header( Authorization => $auth );
my $response = $ua->request($request);

Make this executable:

chmod +x installssl.pl

How it works:

If the file /root/letsencrypt/cpanelautomate/domains.txt exists, the script will read each line of that, which should have a single domain entered on each line, and create and install SSLs for those domains. If that file does not exist, the script will read /etc/userdomains and install certificates for all main, addon, and parked domains.

This script can be entered into the root user's cron to run every 60 (or some number less than 90) days:

0 0 */60 * * /root/letsencrypt/cpanelautomate/letsencrypt-automate-cpanel.sh > /dev/null 2>&1

How to install CronJobs for root user in cPanel:

Edit the CronJobs file by entering the following command.

PS: This command will run nano for editing the file.

crontab -e

Paste the CronJob at the end of the file and restart crond service.

service crond restart

Copyright © Ozan Kurt 2015.

Scroll to Top