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