Skip to content

Instantly share code, notes, and snippets.

@andygock
Last active November 15, 2022 00:10
Show Gist options
  • Save andygock/8278c7c3b929d92cc8e67bd6286826d9 to your computer and use it in GitHub Desktop.
Save andygock/8278c7c3b929d92cc8e67bd6286826d9 to your computer and use it in GitHub Desktop.
#!/bin/bash
#
# renew-letsencrypt-certificates.sh DOMAIN [EMAIL]
#
# Copy Let's Encrypt SSL certs from a remote public facing web server to local filesystem
# Look for changes, if any change, restarts the web service
# Useful for using Let's Encrypt with local internal servers, with custom DNS.
# Working "mail" command needed for email alerts
#
if [ -z $1 ]; then
echo "Syntax error, use:"
echo " renew-letsencrypt-certificates.sh DOMAIN [EMAIL]"
exit 1
fi
# SSH options to remote VPS, e.g different port
SSH_OPTS="-p 22"
SCP_OPTS="-P 22"
DOMAIN=$1
# send email message here when a renewal occurs, or on error
EMAIL=$2
# Get SOA DNS server
DNS=$(dig soa $DOMAIN | grep -v ^\; | grep SOA | sed -r 's/^(.*)SOA\t//' | cut -d " " -f 1 | sed -r 's/\.$//')
# Get remote public facing web server IP address
IP_REMOTE=$(dig @$DNS +short $DOMAIN A)
if [[ -z $IP_REMOTE ]]; then
echo "Can not determine remote IP for $DOMAIN"
exit 1
fi
# .pem certificates will be saved here. You must have write permissions here
# and your Apache or nginx .conf files should refer to this path
# Actual cert files will be in a subdir e.g $CERT_PATH/$DOMAIN/*.pem
CERT_PATH=/etc/letsencrypt/live
CERT_PATH_DOMAIN="$CERT_PATH/$DOMAIN"
# e.g httpd, nginx, refer to restart_www() for methods of restart or reload
WEB_SERVER=nginx
DIR=$(dirname $0)
CERT_TARBALL_REMOTE=/root/certs_${DOMAIN}.tar.gz
CERT_TARBALL_LOCAL=/root/certs_${DOMAIN}.tar.gz
# this flag is used by this script, leave this alone!
certs_updated=0
restart_www () {
if [[ "$WEB_SERVER" == "nginx" ]]; then
nginx -s reload
else
systemctl restart $WEB_SERVER
fi
}
# return 0 if renewed, 1 if not renewed for whatever reason
refresh_certificates () {
# create tarball on public server of current letsencrypt certs for desired domain
echo "Create \"$CERT_TARBALL_REMOTE\" on remote..."
ssh $SSH_OPTS root@$IP_REMOTE "rm -f \"$CERT_TARBALL_REMOTE\"; tar -zchf \"$CERT_TARBALL_REMOTE\" -C \"$CERT_PATH\" $DOMAIN"
# copy tarball from public to internal server, remove existing file if it iexsts
[[ -f "$CERT_TARBALL_LOCAL" ]] && rm -f "$CERT_TARBALL_LOCAL"
echo "Copy remote \"$CERT_TARBALL_REMOTE\" to local \"$CERT_TARBALL_LOCAL\"..."
scp $SCP_OPTS root@$IP_REMOTE:"$CERT_TARBALL_REMOTE" "$CERT_TARBALL_LOCAL"
# create cert path if we need to
[[ -d "$CERT_PATH_DOMAIN" ]] || mkdir -p "$CERT_PATH_DOMAIN"
# check local tarball is persent
if [[ ! -f "$CERT_TARBALL_LOCAL" ]]; then
echo "Error: Local \"$CERT_TARBALL_LOCAL\" is missing. The copy or write operation above has failed. Quitting"
exit 1
fi
echo "Extracting certificates to local \"$CERT_PATH_DOMAIN\"..."
tar -zxvf "$CERT_TARBALL_REMOTE" -C "$CERT_PATH_DOMAIN" --strip-components=1 > /dev/null
echo "Delete remote \"$CERT_TARBALL_REMOTE\"..."
ssh $SSH_OPTS root@$IP_REMOTE "rm -f '$CERT_TARBALL_REMOTE'"
# Restart web server only if certs changed since last run of this script
# Although it's probably fine if we just restarted it each time anyway
# Create a CHECKSUMS file in $CERT_PATH_DOMAIN, verify against this in the future to detect changes
pushd . &> /dev/null
cd "$CERT_PATH_DOMAIN"
if [[ -f CHECKSUMS ]]; then
echo -n "Checking whether certificates have changed... "
sha256sum --status -c CHECKSUMS
if [[ $? -ne 0 ]]; then
# checksum is different, certificate has changed, restart web server and reclaculate checksums
echo "Change found. Certificates updated. Restarting or reloading $WEB_SERVER ..."
restart_www
sha256sum *.pem > CHECKSUMS
return 0
else
# No update performed, certficates the same as previous
echo "Certificates not changed."
return 1
fi
else
# no checksum performed yet
echo "Certificates updated. Restarting or reloading $WEB_SERVER ..."
restart_www
sha256sum *.pem > CHECKSUMS
return 0
fi
popd > /dev/null
}
#
# Script starts here
#
# check cert stats by connecting to local web server
cert_stats () {
echo | openssl s_client -showcerts -connect $1:443 2> /dev/null | openssl x509 -noout -dates
}
# we need to copy those certs to this machine at regular machine
refresh_certificates
if [[ $? -eq 0 ]]; then
# updated, both cron and interactive mode
# restart web server
nginx -s reload
echo "SSL cert updated. New certificate validity dates:"
# get SSL cert stats
if [[ ! -z $EMAIL ]]; then
cert_stats $DOMAIN | mail -s "SSL cert updated" $EMAIL
fi
cert_stats $DOMAIN
elif [[ -t 0 ]]; then
# not updated - interactive mode
echo "SSL cert does not need updating. Current certificate validity dates:"
cert_stats $DOMAIN
fi
exit 0
@marcoczen
Copy link

Hi ... great script ... May i use this as reference ?
I dont understand all of it though ... any reason why you did not want to use something like getssl ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment