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.

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/

/ -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/

/ -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/

//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