Let’s Encrypt + Plesk

Let’s encrypt issues SSL-Certificates for free – meanwhile most Systems trust their CA. Let’s Encrypt features a CLI to request, update and install certificates – which work’s nicely as long as your server’s setup is compatible. A Plesk based setup, however, is not.

Luckily, Plesk features it’s own CLI – so let’s make a short script to renew and update certificates. Here’s the script, I’ll explain later. It requires Let’s Encrypt’s “certbot” to be installed in $HOME.

#!/bin/sh

IP="your.ip.v4.address"
IP6="your:ip:v6:address"

#Domains, format: domain.tld sub1.domain.tld sub2.domain.tld"
Domains=("domain.tld sub1.domain.tld sub2.domain.tld" "domain2.tld2")
#"Main" domain - the name of the certificate that will be associated to the IPs above...
MainDomain="domain.tld"

echo "Certificates will be renewed and installed in Plesk:"
for dom in "${Domains[@]}"; do
  echo " $dom"
done
if [ -n "$1" ]; then
  echo "Renew will be issued for all, however only domain $1 will be updated in Plesk!"
fi
echo;
echo " IPv4: $IP"
echo " IPv6: $IP6"

#"MonthName" should be something that is unique between subsequent calles of the script
# date +%B should give the name of the current month, for more frequent calls use e.g.
# date +%Y-%M-%d_%H-%m-%S
MonthName="$(date +%Y-%m-%d_%H-%M-%S)"
echo " Unique part for each certificate's name: $MonthName"

echo;

read -p "This might brake your setup. Type YES to continue: " Keypress

if [ "$Keypress" != "YES" ]; then
  echo "I asked for \"YES\", you gave me \"$Keypress\". Exiting..." 
  exit 1
fi

echo;

read -p "Call letsencrypt-auto ? [y/N] " Keypress

if [ "$Keypress" = "y" ]; then
  ~/certbot/letsencrypt-auto renew
  echo;
  echo "Done."
fi
echo;

function installcert {
  Certname="LetsEncrypt-Auto_($MonthName)_$1"
  echo;
  echo "Installing certifikate \"$Certname\" to domain repository..."
  key_file="/etc/letsencrypt/live/$1/privkey.pem"
  cert_file="/etc/letsencrypt/live/$1/cert.pem"
  cacert_file="/etc/letsencrypt/live/$1/fullchain.pem"
  /opt/psa/bin/certificate -c $Certname -domain $1 -key-file $key_file -cert-file $cert_file -cacert-file $cacert_file
}

function installcert_admin {
        Certname="LetsEncrypt-Auto_($MonthName)_admin_$1"
        echo;
        echo "Installing certifikate \"$Certname\" to admin's repository..."
        key_file="/etc/letsencrypt/live/$1/privkey.pem"
        cert_file="/etc/letsencrypt/live/$1/cert.pem"
        cacert_file="/etc/letsencrypt/live/$1/fullchain.pem"
        /opt/psa/bin/certificate -c $Certname -admin -key-file $key_file -cert-file $cert_file -cacert-file $cacert_file
}


function assigncert {
  Certname="LetsEncrypt-Auto_($MonthName)_$1"
  /opt/psa/bin/subscription -u $2 -certificate-name $Certname
}

read -p "Install certificates in Plesk? [y/N] " Keypress

if [ "$Keypress" = "y" ]; then
  for domain in "${Domains[@]}"; do
    #Split into another array
    subs=($domain)
    main="${subs[0]}"
    echo "Working on domain $main..."
    #Domain $domain will only been updated if either no arguments are given, or the one and only argument matches $domain
    if ( [ "$1" == "$main" ] ) || ( [ -z "$1" ] ); then
      installcert "$main"
      #Then assign all certificates
      for sub in "${subs[@]}"; do
        assigncert "$main" "$sub"
      done
    else
      echo "==> Skipped."
    fi
  done

  echo;
  echo "Done."
  echo;
fi

read -p "Install & Assign SSL-Certificate of $MainDomain for IPs (Admin's repository)? [y/N] " Keypress

if [ "$Keypress" = "y" ]; then
  installcert_admin "$MainDomain"
  /opt/psa/bin/certificate -ac "LetsEncrypt-Auto_($MonthName)_admin_$MainDomain" -admin -ip $IP
  /opt/psa/bin/certificate -ac "LetsEncrypt-Auto_($MonthName)_admin_$MainDomain" -admin -ip $IP6 
fi

function mailcert {
  Uhr="$(date +%Y-%m-%d_%H-%M-%S)"
  key_file="/etc/letsencrypt/live/$1/privkey.pem"
        cert_file="/etc/letsencrypt/live/$1/cert.pem"
        cacert_file="/etc/letsencrypt/live/$1/fullchain.pem"
        tar -chjf Cert-Backup-$Uhr.tar.bz2 /etc/postfix/postfix_default.pem /usr/share/imapd.pem /usr/share/pop3d.pem
        #Concat certificate for eMail...
  cat $key_file >/tmp/newcert.pem
  cat $cert_file >>/tmp/newcert.pem
  cat $cacert_file >>/tmp/newcert.pem
  cp /tmp/newcert.pem /etc/postfix/postfix_default.pem
  cp /tmp/newcert.pem /usr/share/imapd.pem
  cp /tmp/newcert.pem /usr/share/pop3d.pem
  chmod 400 /usr/share/imapd.pem
  chmod 400 /usr/share/pop3d.pem
  chmod 600 /etc/postfix/postfix_default.pem
  rm /tmp/newcert.pem
  /usr/local/psa/admin/sbin/mailmng --restart-service
}

read -p "Install certificate of $MainDomain to Postfix/IMAP/POP ? [y/N] "

if [ "$Keypress" = "y" ]; then
  mailcert "$MainDomain"
fi

The script has a short configuration section at the top, most notably the domains you want to work on. They are give in a string array, containing the domain name given first when registering a certificate with LetsEncrypt as the first token, and all sub domains you want to secure with the same certificate separated with spaces. More precisely: Let’s encrypt stores your certificates into /etc/letsencrypt/live/<domain>, the first token is used to generate this path. I recommend using your domain without any subdomain for this purpose, i.e. “domain.tld sub.domain.tld sub2.domain.tld”. To achieve this, when calling lestencrypt-auto, give this domain as the first one, i.e.

./letsencrypt-auto certonly --webroot -w /var/www/httpdocs -d domain.tld -d sub.domain.tld

This, of course, requires your domains to be set up in Plesk the same way, i.e. in Plesk, domains with the names “domain.tld”, “sub.domain.tld” and “sub2.domain.tld” must exist. The Plesk-CLI commands used to register and setup are:

  • /opt/psa/bin/certificate -c <certname> -domain <domaine> -key-file <key_file> -cert-file <cert_file> -cacert-file <cacert_file>
  • /opt/psa/bin/subscription -u <domain> -certificate-name <certname>

If you want your IP-Adresses given a default certificate, you should set both IP-Adress variables AND the “main domain” setting. The script will look for a certificate issued for this domain, register it in the admin’s repositiry and the assign it to both addresses. The respective commands issued are:

  • /opt/psa/bin/certificate -c <certname> -admin -key-file <key_file> -cert-file <cert_file> -cacert-file <cacert_file>
  • /opt/psa/bin/certificate -ac <certname> -admin -ip <IP>

If you want your email communication to be secured with your Lets Encrypt certificates, you have to request a certificate for your “main domain” that includes your MX subdomain. For example, if “main domain” is set to “example.com” and MX for this domain is “mail.example.com”, your certificate must include this subdomain. Securing a Plesk-based system’s email with your own SSL-Certificates has been described by Jay Versluis on wpguru.co.uk, and I simply scripted his approach.