6

Let’s Encrypt on cPanel, automated!

So, I run a cPanel box for my webserver for ease of use with multiple domains, and I like free, legitimate SSLs.

Now that LetsEncrypt is public and available, I decided to see if I could automate it to work with cPanel, for either all domains, or a provided list of domains.

This works for me on CentOS 7.2 with cPanel 54 and Apache 2.4.

IMPORTANT NOTE: I am not responsible if this script has a negative effect of any kind on your server, it may blow up, I’m not sure!

NOTE: 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.

Not-as-important note: 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’s 2 scripts and a cron entry involved after the initial setup.

Contents

Initial setup:

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

Create 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 /root/letsencrypt/letsencrypt-auto --text certonly --agree-tos --email $email --renew-by-default --webroot --webroot-path $(echo $line | awk -F " ;; " '{print $3}') -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 ACCESSHASHHERE 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 = "ACCESSHASHHERE";
$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

tfmm

6 Comments

  1. Hey, thank you for this wonderful tutorial,

    I’ve followed all of your instructions but it look like the script is halting in the
    #find docroots and dump to $scriptloc/letsencrypt-autmate-data.txt

    any suggestions ?

    • Hi Mike, I recently updated the script and reformatted how it works, resolving a few issues, please test the new version, and let me know if you see the same behavior.

  2. Thanks for the scripts. To get letsencrypt-automate-cpanel.sh working, I had to add in a missing semicolon inside the if statements.

    I replaced this section:

    then continue else echo

    with:

    then continue; else echo

    • Thanks Dan, could you try the new version of the script I just posted, I believe it to be working now.

  3. This looks promising, I’m trying to run the script but I’m getting /home/letsencrypt/letsencrypt-auto: No such file or directory error. If I go and manually create the path and file, then I get a permissions error. I tried modifying letsencrypt-automate-cpanel.sh to try and use a letsencrypt-auto file in the same directory as the script to no avail. I get a /root/letsencrypt/cpanelautomate/letsencrypt-auto: Permission denied error.

    Any suggestions?

    • Darlington: sorry! That was my error, new version should resolve that issue. I updated the post.

Leave a Reply to mike Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.