Auto Generate and Update SSL on Shared Hosting

458 views Asked by At

I am trying to auto generate SSL certs and upload them to my shared hosing on namecheap.com

The host does not offer any way to auto manage ssl certs without paying a lot of money.

I am trying to get this script working

https://catelin.net/2018/03/24/fully-automate-ssl-tls-certificate-renewal-with-cpanel/

The idea is to run bash scrips on your own linux box that will update the ssl certs through cpanel.

I am having difficulty with this section of code. In my actual code I have updated the server name info. The main problem is that I have very little experience writing in bash script.

certificate=$(echo |openssl s_client -servername yourserver.com -connect yourserver.com:443 2>/tmp/cert.tmp|openssl x509 -checkend $[86400 * $RENEW] -enddate)
if [ "$certificate" == "" ]; then
  echo "Error: unable to check certificate"
else
  if [[ $certificate =~ (.*)Certificate will expire ]]; then
    echo $certificate
    ...

I am getting an error here (my first of many errors I am sure...)

./certupdate.sh: line 19: syntax error in conditional expression
./certupdate.sh: line 19: syntax error near `will'
./certupdate.sh: line 19: `    if [[ $certificate =~ (.*)Certificate will expire ]]; then'

Any help would be great.

Or, if someone has a better idea on how to update the ssl certs that would be even better. Something in all PHP would be great as I am more familiar with that.

1

There are 1 answers

0
tripleee On BEST ANSWER

The shell parses each line into tokens by splitting on whitespace. The syntax of the [[ built-in with =~ requires one token on each side. You can prevent splitting on whitespace by putting backslashes in front of every whitespace character which is not a token separator, or quoting the sequence which should be a single token.

  if [[ $certificate =~ (.*)"Certificate will expire" ]]; then

That aside, you really don't need a regular expression here. (And if you do use one, the parentheses around .* are superfluous. In fact the whole .* is superfluous.)

  if [[ $certificate = *"Certificate will expire"* ]]; then

That aside, the script you are trying to copy has a number of other issues, albeit more minor ones. Probably just try to find a better blog to copy/paste from.

Here's a quick refactoring to hopefully make the script more idiomatic, but I might have missed some issues, and don't have any way to test this.

#!/bin/bash

# Don't use uppercase for private variables
renew=22

# For diagnostic messages
me=${0##*/}

# Parametrize domain name
dom="yourserver.com"
email="[email protected]"

# Don't echo so much junk
# (I will silently drop the other junk output without comment)
# echo "======================================="
# Fix date formatting, print diagnostic to stderr
date "+$me: %c START" >&2
# Use a unique temp file to avoid symlink attacks and concurrency problems
t=$(mktemp -t letsencryptCA.XXXXXXXXXX.crt) || exit
# Clean it up when we are done
trap 'rm -f "$t"' ERR EXIT
wget -O "$t" https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt

# Avoid obsolete $[ ... ] math syntax
# Avoid superfluous echo |
# Don't write stderr to a junk file
# Maybe add 2>/dev/null on the first openssl if this is too noisy
certificate=$(openssl s_client -servername "$dom" -connect "$dom":443 </dev/null | 
              openssl x509 -checkend $((86400 * $renew)) -enddate)
# Indentation fixes
if [ "$certificate" == "" ]; then
    # Error messages indicate script's name, and go to standard error
    # Include domain name in error message
    # Fixed throughout below
    echo "$me: Error: unable to check $dom certificate" >&2
    # Exit with an error, too
    exit 123
else
    # Quote string
    # Move this outside the conditional, to avoid repeated code
    echo "$certificate"

    # Fix comparison
    if [[ $certificate = *"Certificate will expire"* ]]; then

        echo "$me: $dom certificate needs to be renewed" >&2
    
        # No idea here, assume this is okay
        # Wrap horribly long command though
        certbot certonly --non-interactive --staple-ocsp \
            --email "$email" -d "$dom" -d "www.$dom" \
            --agree-tos --manual \
            --manual-auth-hook /path/toyour/scripts/letsencryptauth.sh \
            --manual-cleanup-hook /path/toyour/scripts/letsencryptclean.sh
        echo "$me: $dom cert process completed, now uploading it to CPanel" >&2
    
        # Weird indentation fixed again
        USER='cpanel username' PASS='cpanelpassword' EMAIL="$email" \
        /usr/bin/php /path/toyour/scripts/sslic.php "$dom" \
            /etc/letsencrypt/live/"$dom"/cert.pem \
            /etc/letsencrypt/live/"$dom"/privkey.pem "$t"
        echo "$me: $dom upload to cpanel process complete" >&2
    
    else
        echo "$me: $dom cert does not need to be renewed" >&2
    fi
fi
# Fix date formatting, print diagnostic to stderr
date "+$me: %c END" >&2

The %c format specifier for date includes the year, where the original code omitted it. I consider this change a feature rather than a bug.

There are still a lot of hard-coded paths etc which should probably be parametrized better.

The stderr output from openssl is modest enough that I don't think we absolutely need to discard it; on the other hand, dumping it in a temporary file will almost certainly hide useful diagnostics when something actually goes wrong (network down or whatever).

tripleee$ openssl s_client -servername www.stackoverflow.com \
>   -connect www.stackoverflow.com:443 </dev/null >/dev/null
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = *.stackexchange.com
verify return:1