Let’s Encrypt Certificate – Synology NAS Howto

Lets Encrypt

I own a Synology NAS and it now supports the free certificates from Let’s Encrypt.  The only problem with Synology’s implimentation is that they only allow the web (HTTP-01) method of certificate verification.  This means that I have to map port 80 and 443 from my NAS and make it available to the Internet.  Yuch!   This is what I did to use Let’s Encrypt and not open up and web ports to the internet.

Let’s Encrypt also support another method of to verify the certificates.  This method is called DNS-01 and allow Let’s Encrypt to check a DNS TXT record to get the varification.  This does require that your DNS server, that s hosting your domain, be able to be updated dynamically though and API.  Thankfully, the acme.sh script that I will be using support a lot of standard DNS server APIs (including most of the common cloud based services).  I have my own DNS servers the are running BIND 9.11.2 p2 so this documentation is written to support BIND’s dynamic DNS update method.  I am not going to go over how to setup BIND as a dynamic DNS server in this howto, you will already have to have one setup or use one of the acme.sh other methods.  I hope to write a BIND Dynamic DNS howto in the future.

You will need to e logged into your NAS via SSH for most of this install and setup.

Installing acme.sh on your NAS

$ sudo -i
# wget https://github.com/Neilpang/acme.sh/archive/master.tar.gz
# tar xvf master.tar.gz
# cd acme.sh-master/
# ./acme.sh --install --nocron --home /usr/local/share/acme.sh --accountemail "email@gmailcom"

Configuring Let’s Encrypt with your DNS Keys

$ sudo -i
# cd /usr/local/share/acme.sh
# vi account.conf

Add in the following lines:

(change the email and the nsupdate server to your email and server name. Your keys (both the key and private file) need to be already copied over and placed in the /usr/local/share/acme.sh direcory).

ACCOUNT_EMAIL="admin@example.com"
NSUPDATE_SERVER="dns.example.com"
NSUPDATE_KEY="/usr/local/share/acme.sh/Kacme-example.com+123+54321.key"
DEFAULT_DNS_SLEEP="20"

Getting the Cetificate from Let’s Encrypt.

Create the following script (called getcert.sh) in the /usr/local/share/acme.sh directory.
NOTE:  The DSM 6.x and DSM 7.x scripts are shown.  Copy the correct one for the version of DMS you are using.

DSM 6.x Version

#!/bin/bash
export CERT_FOLDER="$(find /usr/syno/etc/certificate/_archive/ -maxdepth 1 -mindepth 1 -type d)"
export CERT_DOMAIN="nas.example.com"

cd /usr/local/share/acme.sh
./acme.sh --issue -d "$CERT_DOMAIN" --dns dns_nsupdate \
    --certpath "$CERT_FOLDER/cert.pem" \
    --keypath  "$CERT_FOLDER/privkey.pem" \
    --fullchainpath "$CERT_FOLDER/fullchain.pem" \
    --capath "$CERT_FOLDER/chain.pem" \
    --reloadcmd "/usr/syno/sbin/synoservicectl --reload nginx"

DSM 7.x Version

#!/bin/bash
export CERT_FOLDER="$(find /usr/syno/etc/certificate/_archive/ -maxdepth 1 -mindepth 1 -type d)"
export CERT_DOMAIN="nas.example.com"

cd /usr/local/share/acme.sh
./acme.sh --issue -d "$CERT_DOMAIN" --dns dns_nsupdate \
    --certpath "$CERT_FOLDER/cert.pem" \
    --keypath  "$CERT_FOLDER/privkey.pem" \
    --fullchainpath "$CERT_FOLDER/fullchain.pem" \
    --capath "$CERT_FOLDER/chain.pem" \
    --reloadcmd "/usr/syno/bin/synosystemctl restart nginx"

(NOTE: for testing, please add in –staging argument to the acme.sh line in the script.  Once you are shure things are working, then remove it)

Change the permissions on the file and then run it to get a certificate.

$ sudo -i
# cd /usr/local/share/acme.sh
# chmod 755 getcert.sh
# ./getcert.sh

log into the NAS web admin page and check the DSM control panel – Security – Sertificates to see if your certificate for Let’s Encrypt is there.

Installing the Certificate.

Create the following script (called replaceCerts.sh) in the /usr/local/share/acme.sh directory. 

NOTE:  The DSM 6.x and DSM 7.x scripts are shown.  Copy the correct one for the version of DMS you are using.

DSM 6.x Version …

# Note: The $FULLCERTDIR tries to find your certificate folder.  If you have issues with this then it may be nessasary to hard code it.

FULLCERTDIR="$(find /usr/syno/etc/certificate/_archive/ -maxdepth 1 -mindepth 1 -type d)"

# do not change anything beyond this line!

CERTROOTDIR="/usr/syno/etc/certificate"
PACKAGECERTROOTDIR="/usr/local/etc/certificate"

# find all subdirectories containing cert.pem files
PEMFILES=$(find $CERTROOTDIR -name cert.pem)
if [ ! -z "$PEMFILES" ]; then
        for DIR in $PEMFILES; do
                # replace all certificates, but not the ones in the _archive folder
                if [[ $DIR != *"/_archive/"* ]]; then
                        rsync -avh "$FULLCERTDIR/" "$(dirname $DIR)/"
                fi
        done
fi

# reload
/usr/syno/sbin/synoservicectl --reload nginx

# update and restart all installed packages
PEMFILES=$(find $PACKAGECERTROOTDIR -name cert.pem)
if [ ! -z "$PEMFILES" ]; then
	for DIR in $PEMFILES; do
		rsync -avh "$FULLCERTDIR/" "$(dirname $DIR)/"
		/usr/syno/bin/synopkg restart $(echo $DIR | awk -F/ '{print $6}')
	done
fi

DSM 7.x Version …

# Note: The $FULLCERTDIR tries to find your certificate folder.  If you have issues with this then it may be nessasary to hard code it.

FULLCERTDIR="$(find /usr/syno/etc/certificate/_archive/ -maxdepth 1 -mindepth 1 -type d)"

# do not change anything beyond this line!

CERTROOTDIR="/usr/syno/etc/certificate"
PACKAGECERTROOTDIR="/usr/local/etc/certificate"

# find all subdirectories containing cert.pem files
PEMFILES=$(find $CERTROOTDIR -name cert.pem)
if [ ! -z "$PEMFILES" ]; then
        for DIR in $PEMFILES; do
                # replace all certificates, but not the ones in the _archive folder
                if [[ $DIR != *"/_archive/"* ]]; then
                        rsync -avh "$FULLCERTDIR/" "$(dirname $DIR)/"
                fi
        done
fi

# reload
/usr/syno/sbin/synosystemctl -restart nginx

# update and restart all installed packages
PEMFILES=$(find $PACKAGECERTROOTDIR -name cert.pem)
if [ ! -z "$PEMFILES" ]; then
	for DIR in $PEMFILES; do
		rsync -avh "$FULLCERTDIR/" "$(dirname $DIR)/"
		/usr/syno/bin/synopkg restart $(echo $DIR | awk -F/ '{print $6}')
	done
fi

Change the permissions on the file and then run it to get a certificate.

$ sudo -i
# cd /usr/local/share/acme.sh
# chmod 755 replaceCerts.sh
# ./replaceCerts.sh

Log into the DSM web page and you should now be using the new Let’s Encrypt certificate.

Scheduling the Certficate Renewal

Let’s Encrypt only issues certificates for 90 days.  This mean you really want a way to script the checking and updating of your certificate so you don’t have to manually apply it each time. The acme.sh script takes care of most f this for you, we just need to add in a way to schedule it.

Manually update the crontab to run acme.sh.  I run mine at 3:00am once a week on Friday.

$ sudo -i
# cd /usr/local/share/acme.sh
# vi /etc/crontab

Add in the following line to the bottom of the crontab.

0    3    *    *    5    root    /usr/local/share/acme.sh/acme.sh --cron --home /usr/local/share/acme.sh/

Creating a DSM Task

The last step is to setup a task to update all the certificates after the acme.sh script fetches a new one. We have already created the script ( replaceCerts.sh ) to do the job, let’s just create the task to run it.  Since I am fetching the certificates at 3:00am, then I will run this task just after at 3:30am.

Login to the Synology DSM web interface.  Go to Control Panel – > Task Scheduler.   Create a new task for a user-defined script.

General Settings Tab

  • Task: Let’s Encrypt Certificate
  • User: root

Schedule Tab

  • Run on the Following days: Friday
  • First Run Time: 03:30
  • Frequency: once a day

Task Settings Tab

  • Check Send run details be email
  • email: admin@example.com
  • User-defined script: /usr/local/share/acme.sh/replaceCerts.sh

Fix after an Upgrade.

When you upgrade the version of DSM on the NAS, it may overwrite some of the environment variables that acme.sh needs.  There is a single line in /root/.profile that looks like the following:

. "/usr/local/share/acme.sh/acme.sh.env"

Check after an upgrade to confirm that the line is still in the /root/.profile file.  If it is not, then you can add it in manually or issue the following command:

$ sudo -i
# cd /usr/local/share/acme.sh 
# ./acme.sh --upgrade --nocron --home /usr/local/share/acme.sh

Leave a Comment

Your email address will not be published. Required fields are marked *