Skip to content

Instantly share code, notes, and snippets.

@catchdave
Last active March 23, 2024 08:20
Show Gist options
  • Star 91 You must be signed in to star a gist
  • Fork 18 You must be signed in to fork a gist
  • Save catchdave/69854624a21ac75194706ec20ca61327 to your computer and use it in GitHub Desktop.
Save catchdave/69854624a21ac75194706ec20ca61327 to your computer and use it in GitHub Desktop.
CLI script to programmatically replace SSL certs on Synology NAS
#!/bin/bash
#
# *** For DSM v7.x ***
#
# How to use this script:
# 1. Get your 3 PEM files ready to copy over from your local machine/update server (privkey.pem, fullchain.pem, cert.pem)
# and put into a directory (this will be $CERT_DIRECTORY).
# Personally, I use this script (https://gist.github.com/catchdave/3f6f412bbf0f0cec32469fb0c9747295) to automate steps 1 & 4.
# 2. Ensure you have a user setup on synology that has ssh access (and ssh access is setup).
# This user will need to be able to sudo as root (i.e. add this line to sudoers, <USER> is the user you create):
# <USER> ALL=(ALL) NOPASSWD: /var/services/homes/<USER>/replace_certs.sh
# 3. Copy this script to Synology: sudo scp replace_synology_ssl_certs.sh $USER@$SYNOLOGY_SERVER:~/
# 4. Call this script as follows:
# sudo bash -c scp ${CERT_DIRECTORY}/{privkey,fullchain,cert}.pem $USER@$SYNOLOGY_SERVER:/tmp/ \
# && ssh $USER@$SYNOLOGY_SERVER 'sudo ./replace_synology_ssl_certs.sh'
# Script start.
DEBUG= # Set to any non-empty value to turn on debug mode
error_exit() { echo "[ERROR] $1"; exit 1; }
warn() { echo "[WARN ] $1"; }
info() { echo "[INFO ] $1"; }
debug() { [[ "${DEBUG}" ]] && echo "[DEBUG ] $1"; }
# 1. Initialization
# =================
[[ "$EUID" -ne 0 ]] && error_exit "Please run as root" # Script only works as root
certs_src_dir="/usr/syno/etc/certificate/system/default"
services_to_restart=("nmbd" "avahi" "ldap-server")
packages_to_restart=("ScsiTarget" "SynologyDrive" "WebDAVServer" "ActiveBackup")
target_cert_dirs=(
"/usr/syno/etc/certificate/system/FQDN"
"/usr/local/etc/certificate/ScsiTarget/pkg-scsi-plugin-server/"
"/usr/local/etc/certificate/SynologyDrive/SynologyDrive/"
"/usr/local/etc/certificate/WebDAVServer/webdav/"
"/usr/local/etc/certificate/ActiveBackup/ActiveBackup/"
"/usr/syno/etc/certificate/smbftpd/ftpd/")
# Add the default directory
default_dir_name=$(</usr/syno/etc/certificate/_archive/DEFAULT)
if [[ -n "$default_dir_name" ]]; then
target_cert_dirs+=("/usr/syno/etc/certificate/_archive/${default_dir_name}")
debug "Default cert directory found: '/usr/syno/etc/certificate/_archive/${default_dir_name}'"
else
warn "No default directory found. Probably unusual? Check: 'cat /usr/syno/etc/certificate/_archive/DEFAULT'"
fi
# Add reverse proxy app directories
for proxy in /usr/syno/etc/certificate/ReverseProxy/*/; do
debug "Found proxy dir: ${proxy}"
target_cert_dirs+=("${proxy}")
done
[[ "${DEBUG}" ]] && set -x
# 2. Move and chown certificates from /tmp to default directory
# =============================================================
mv /tmp/{privkey,fullchain,cert}.pem "${certs_src_dir}/" || error_exit "Halting because of error moving files"
chown root:root "${certs_src_dir}/"{privkey,fullchain,cert}.pem || error_exit "Halting because of error chowning files"
info "Certs moved from /tmp & chowned."
# 3. Copy certificates to target directories if they exist
# ========================================================
for target_dir in "${target_cert_dirs[@]}"; do
if [[ ! -d "$target_dir" ]]; then
debug "Target cert directory '$target_dir' not found, skipping..."
continue
fi
info "Copying certificates to '$target_dir'"
if ! (cp "${certs_src_dir}/"{privkey,fullchain,cert}.pem "$target_dir/" && \
chown root:root "$target_dir/"{privkey,fullchain,cert}.pem); then
warn "Error copying or chowning certs to ${target_dir}"
fi
done
# 4. Restart services & packages
# ==============================
info "Rebooting all the things..."
for service in "${services_to_restart[@]}"; do
/usr/syno/bin/synosystemctl restart "$service"
done
for package in "${packages_to_restart[@]}"; do # Restart packages that are installed & turned on
/usr/syno/bin/synopkg is_onoff "$package" 1>/dev/null && /usr/syno/bin/synopkg restart "$package"
done
# Faster ngnix restart (if certs don't appear to be refreshing, change to synosystemctl
if ! (/usr/syno/bin/synow3tool --gen-all && sudo systemctl reload nginx); then
warn "nginx failed to restart"
fi
info "Completed"
@yauyauwind
Copy link

yauyauwind commented Apr 14, 2021

Hello Catchdave,

Thank you for your script, to let's me know need restart not only nginx
and I make my code to support ReverseProxy and AppPortal

Here is my code "not complete code, just a related RP part"

letpath="/volume1/docker/letencrypt/config/etc/letsencrypt/archive/xxxxx.com"
certname=$(ls -v $letpath/cert*.pem | tail -1)
privname=$(ls -v $letpath/privkey*.pem | tail -1)
fullname=$(ls -v $letpath/fullchain*.pem | tail -1)
chainame=$(ls -v $letpath/chain*.pem | tail -1)
tarpathr="/usr/syno/etc/certificate/ReverseProxy"

cd $tarpathr
lsdirectory=($(ls -d */))
for (( i=0; i<${#lsdirectory[@]}; i++)); do rsync -avh $fullname $tarpathr/${lsdirectory[$i]}/fullchain.pem; chown root:root $tarpathr/${lsdirectory[$i]}/.pem; chmod 400 $tarpathr/${lsdirectory[$i]}/.pem; done

I use array to store the sub-folder which is under the ReverseProxy directory
and use the loop to overwrite each cert

Just for your reference to implement your code, so that can support ReverseProxy and AppProtal

My code is work, but I haven't write the checking statement

Due to I use docker to renew Let's Encrypt Cert. however the updated cert will name to prvikey1,2,3,4,5.pem
I use var to get the latest pem name

Anyway, Just FYI

Thanks~

@cedricmenzi05
Copy link

Hey!

I tried to use this script on my Synology with DSM 7 installed. I copy the files without any problem and set the permission. I then restart the Synology but the certificates don't change....
Is this related to DSM 7 or anything else?
Could you help me please?

Thanks in advance!

@paolopasqua
Copy link

paolopasqua commented Sep 27, 2021

Hey!

I tried to use this script on my Synology with DSM 7 installed. I copy the files without any problem and set the permission. I then restart the Synology but the certificates don't change....
Is this related to DSM 7 or anything else?
Could you help me please?

Thanks in advance!

Hi @cedricmenzi05 I'm doing a similar script to automate the certificate renewal and install it in the Synology. What I've seen (on DSM 7) the original certificates are stored in /usr/syno/etc/certificate/_archive/
Here there's folders containing the certificates and three files:

  • DEFAULT : the hash (folder name) of default certificate to apply.
  • INFO : the list of certificates with description, services list where applied, ...
  • SERVICES : the list of services

I'm quite sure that the other folders in /usr/syno/etc/certificate/ are regenerated every time nginx and other services are restarted.
And again, in DSM 7 changes the restart command: synosystemctl restart xxxx. DSM 7 changes are driving me crazy, I really don't find where to edit the nginx vhost conf :')

@catchdave
Copy link
Author

I don't have DSM7 installed on my NAS yet, so I can't provide any information as to what needs to happen for DSM7. I have used the above successfully on DSM6 for a while now. So I'd say start with what @paolopasqua said and do some google-research :)

@pronetguru
Copy link

Hey!

I tried to use this script on my Synology with DSM 7 installed. I copy the files without any problem and set the permission. I then restart the Synology but the certificates don't change.... Is this related to DSM 7 or anything else? Could you help me please?

Thanks in advance!

Hi @cedricmenzi05,
I was having the same issue after updating to DSM 7 and stumbled upon this thread. @paulopoasqua is absolutely correct in that the certificates are regenerated every time nginx is restarted, which helped me to solve my issue and restore scripted updates of my certificates.

Within the /usr/syno/etc/certificate/_archive/ folder, I also that there were 2 additional folders with 6-character names. You may have more depending on how many different certificates you've installed on your Synology. One of those contains the "default" self-signed certificate, and the other contained the original (old, expired) custom certificate. If you cd to the appropriate folder and cat cert.pem you'll be able to identify the certificate folder you want to update. When you copy over the certificate component files (cert.pem, chain.pem, fullchain.pem, privkey.pem), just drop them into the certificate folder you identified and restart the necessary services. The certificate will re-populate from the .../_archive/XXXXXX/ folder each restart.

Also, as @paolopasqua noted, the command to restart services has changed, so make sure you update that in your script also.

@lawipac
Copy link

lawipac commented Nov 22, 2021

/usr/syno/sbin/synosystemctl restart nginx

For my case, With DSM7, restart Nginx also restart the VM under the VMware manager, Docker, which is quite unexpected. It's kinda a downgrade of the DSM.

/usr/syno/sbin/synosystemctl resload nginx is more appropriate, using reload instead of restart

In addition, I also need to default certificate in 'Control Panel / Security/ Certificate "

@jiquem
Copy link

jiquem commented Feb 2, 2022

I don't get the point : is the script working on DSM7 or not ? If not, is any update planned ?

@catchdave
Copy link
Author

@jiquem : When I upgrade to DSM7, I will rewrite this script to work on DSM7. The purpose of this script was just for my personal use case--shared in case it helps others.
As of this point in time, I don't have current plans to upgrade my DSM to v7.x--doing so will likely mean I will need to set aside a lot of time to rewrite several things like this, so that's a personal barrier for me :)

@catchdave
Copy link
Author

Update: Added support for application portal and reverse proxy certs

@looran
Copy link

looran commented Jun 8, 2022

Hello, I made a script to automate pushing SSL certificates and setting-up htaccess that works on DSM7
https://github.com/looran/synoadm
enjoy

@Rogver
Copy link

Rogver commented Sep 19, 2022

DSM7 only need replace /usr/syno/sbin/synoservice --restart to systemctl restart

@carmatana
Copy link

carmatana commented Dec 4, 2022

In DSM7 to avoid docker and VMM to restart when restarting nginx use:

synow3tool --gen-all && systemctl reload nginx

source:

https://www.reddit.com/r/synology/comments/olve56/comment/h5hsogq/

@catchdave
Copy link
Author

Updated to work with DSM7

@lawipac
Copy link

lawipac commented Dec 9, 2022

@carmatana thank you.

it works like a charm. With the following prompt.

root@acmedns:~# ssh root@nas2022 "/usr/syno/bin/synow3tool --gen-all && systemctl reload nginx "
Sync W3 certificate info successfully
Generate nginx tmp config successfully

I purposely switched to an old cert and switch it back. works as expected.

@MrPeteH
Copy link

MrPeteH commented Jan 2, 2023

@catchdave, I've uploaded an improved version:

  • instructions revised so this can work beyond a DSM update (which removes user edits to sudoers and more :( )
  • includes alt port switches, all four *.pem files (original was missing chain.pem), copy rather than move to make setup diagnosis easier, and support for a number of popular Synology packages that make use of SSL certs

THANK YOU for doing this! A huge help.

@MrPeteH
Copy link

MrPeteH commented Jan 2, 2023

Hello, I made a script to automate pushing SSL certificates and setting-up htaccess that works on DSM7 https://github.com/looran/synoadm enjoy

@looran that script is missing quite a bit on the SSL side. Among other things, synology services use more than just fullchain.pem and privkey.pem ;)

@looran
Copy link

looran commented Jan 11, 2023

Right, I added mention that it has only been tested with the main web interface handled by nginx, since it's the only thing I tested.

@ekchew
Copy link

ekchew commented Feb 3, 2023

Ran into some trouble today with /usr/syno/bin/synosystemctl having seemingly vanished?

I eventually found a workaround in replacing

/usr/syno/bin/synosystemctl restart nginx

with

/usr/syno/sbin/synoservicectl --restart nginx

and likewise for the others. I don't really know what I'm doing though but it seems happy again?

@catchdave
Copy link
Author

@ekchew : synosystemctl is for DSM7.x. I think /usr/syno/sbin/synoservicectl is for DSM 6.x IIRC.
What version of Synology OS are you running?

@DianLiI
Copy link

DianLiI commented Feb 20, 2023

I think there is a bug where line 22 always returns true. Should the line be if [ "$DEFAULT_DIR_NAME" != "" ]; then instead?

@MrPeteH
Copy link

MrPeteH commented Mar 15, 2023

Yes, synosystemctl is DMS7. Here's a link to my DSM7 version. NOTE that some packages do not use either synoservicectl OR synosystemctl... but synopkg instead! https://gist.github.com/MrPeteH/80b487a2f400e5c0d538b18ae2f5dd76

@catchdave
Copy link
Author

Refactored script to be cleaner and added some packages to restart as per @MrPeteH.

@MrPeteH
Copy link

MrPeteH commented Mar 22, 2023

Refactored script to be cleaner and added some packages to restart as per @MrPeteH.*

WONDERFUL refactor job!

Unfortunately, I found some challenges...

First, what appears to be a bug:

  • While it DOES work to copy the three files to the system/default and system/FQDN folders (privkey,fullchain,cert}.pem
    • DSM does some work in those two folders automagically, to create/update four additional files: chain.pem, info (no extn), root.pem, and short-chain.pem
    • But DSM doesn't do that in any of the other folders.
    • SO, this script needs to copy all seven files from the source folder, not just three! (I do not know what process causes the extra updates, nor how quickly. seems very fast...)
    • (Just do find / -name cert.pem to discover all of yours... )

Next, suggestions that relate to the up-front instructions:

  • Many like to configure an alt ssh port. To specify the port in scp, use -P nnnn, in ssh -p nnnn
  • DSM wipes out all customizations to sudoers on update. So it's better to recommend adding a line to sudoers.d/sudo-USER
  • Sadly, brace expansion doesn't work in /bin/sh which can easily be the default shell for embedded scripts (like in ACME actions!) -- best workaround is either spell it all out, or embed the script in a script file with #!/bin/csh or whatever... (I just now discovered this was killing some of my automation :( )

@catchdave
Copy link
Author

catchdave commented Mar 22, 2023

@MrPeteH : Thanks!

  1. Regarding the other files beyond the 3 certs not being copied - have you noticed any issues with them not moving? As I use this script every 90 days (via letsencrypt), I have no problems in SSL certs working in all applications that use SSL certs on my Nas. Perhaps these files don't matter? On my NAS, I have not noticed any issues.
  2. Sudo and ports. Yes true to both - I have only noticed a wipe on a major upgrade (from 6.x to 7.x), minor updates are fine. I can look into testing what works. FWIW, my main focus on this gist on moving certs, I have so far intentionally been vague about managing SSH access (I figure anyone doing this stuff has ssh/sudo figured out already)
  3. Brace expansion. I understand /bin/sh doesn't support it. However, this script is explicit about using bash and is designed to run on synology (which has bash) via a direct ssh call. If you're using parts of this script in somewhere else - that's fine, but not it's not my intention to support uses outside of being executed directly (which will always use /bin/bash). How are you calling this script?

@MrPeteH
Copy link

MrPeteH commented Mar 22, 2023

  1. Brace expansion. I understand /bin/sh doesn't support it. However, this script is explicit about using bash and is designed to run on synology (which has bash) via a direct ssh call. If you're using parts of this script in somewhere else - that's fine, but not it's not my intention to support uses outside of being executed directly (which will always use /bin/bash). How are you calling this script?

I'm talking about the recommended calling usage in the comments at the top... which of course is assumed to be in some OTHER machine, using scp and ssh to get the data to the Synology NAS. :)

The other files: I did see an issue once, but not since. All I know is, DSM creates those other files, and copies them all when installing a package. No idea why they don't get updated when the main system certs update. Perhaps we could call that a DSM bug LOL

@catchdave
Copy link
Author

catchdave commented Mar 23, 2023

I'm talking about the recommended calling usage in the comments at the top... which of course is assumed to be in some OTHER machine, using scp and ssh to get the data to the Synology NAS. :)

Makes sense! Appreciate the explanation :) @MrPeteH

@fhemberger
Copy link

fhemberger commented Apr 6, 2023

Thanks for the script! To make it a bit more dynamic, you can discover the target directories and the services/packages to restart:

# Output *all* parameters handed to the method (required to dump arrays)
error_exit() { echo "[ERROR] $*"; exit 1; }
warn() { echo "[WARN ] $*"; }
info() { echo "[INFO ] $*"; }
debug() { [[ "${DEBUG}" ]] && echo "[DEBUG ] $*"; }

# ...

service_cert_dirs="$(find /usr/syno/etc/certificate -name "cert.pem" -exec dirname {} \;)"
package_cert_dirs="$(find /usr/local/etc/certificate -name "cert.pem" -exec dirname {} \;)"

services_to_restart=()
for service in $service_cert_dirs; do
  service_name="$(echo "$service" | grep -Po '/usr/syno/etc/certificate/\K[^/]+')"
  if [[ "$service_name" != "system" && "$service_name" != "_archive" && "$service_name" != "ReverseProxy" ]]; then
    services_to_restart+=("$service_name")
  fi
done;

packages_to_restart=()
for package in $package_cert_dirs; do
  package_name="$(echo "$package" | grep -Po '/usr/local/etc/certificate/\K[^/]+')"
  packages_to_restart+=("$package_name")
done;

debug "Services to restart: ${services_to_restart[*]}"
debug "Packages to restart: ${packages_to_restart[*]}"

@footswitch
Copy link

footswitch commented Apr 9, 2023

[EDIT] I was using the wrong chain file.

First of all let me thank you for sharing this. I have win-acme (Let's Encrypt) renewing the certificates on a Windows machine, and need a way of replacing the certificates in Synology automatically.

I'm on DSM 7.1-42661 Update 4
I tried, to the best of my abilities, to adapt this script for my NAS (adding packages to restart, and target dirs).
I copied my adaptation to a new Task, running as root, and in the Task Log I can see that the script executes all steps successfully.
(Except Surveillance Station)

[EDIT] reloading nginx instead of restarting, the output is this:

Failed to restart package [SurveillanceStation], err=[272]
Sync W3 certificate info successfully
Generated nginx tmp config is not valid
Job for nginx.service failed. See "systemctl status nginx.service" and "journalctl -xe" for details.

But after completion, I'm always getting all the apps greyed out with an exclamation sign, even if I restart DSM altogether.
After restarting I can no longer access DSM via https, only via http.
And to rectify all that, I have to go into Control Panel -> Security, and re-add (import and replace) the default certificate.

After doing that, I have to restart once more, and one app still needs to be repaired - Surveillance Station.
Note: in the case of Surveillance Station, it has an App Portal configured (Control Panel -> Login Portal).
But I added another loop to replace certificates in AppPortal as well - for proxy in /usr/syno/etc/certificate/AppPortal/*/; do ...

I'm really at a loss here...
Can you share your experience?

@fhemberger
Copy link

@footswitch Have you tried running nginx -t to see what's wrong with the nginx config? Do the certificate and key file have the right user/access rights?

@footswitch
Copy link

footswitch commented Apr 9, 2023

[EDIT] I was using the wrong chain file.

@fhemberger, the files are copied and chowned, and if that wasn't the case, the script would output the warning Error copying or chowning(...)
Querying nginx -t before the script:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

After replacing the cert files and reloading nginx:

nginx: [emerg] SSL_CTX_use_PrivateKey("/usr/syno/etc/www/certificate/system_default/3e35dc1f-4740-487a-b658-90708050766e.pem") failed (SSL: error:0B080074:lib(11):func(128):reason(116))
nginx: configuration file /etc/nginx/nginx.conf test failed

I'm using the exact same files when importing/replacing the certificate via DSM portal.

My Log looks like this:

[INFO ] ok privkey.pem exists.
[INFO ] ok cert.pem exists.
[INFO ] ok fullchain.pem exists.
[INFO ] Certs chowned at source dir.
[INFO ] Copying certificates to '/usr/syno/etc/certificate/system/default'
[INFO ] Copying certificates to '/usr/syno/etc/certificate/smbftpd/ftpd'
[INFO ] Copying certificates to '/usr/local/etc/certificate/HyperBackupVault/HyperBackupVault'
[INFO ] Copying certificates to '/usr/local/etc/certificate/LogCenter/pkg-LogCenter'
[INFO ] Copying certificates to '/usr/local/etc/certificate/PrestoServer/PrestoServer'
[INFO ] Copying certificates to '/usr/local/etc/certificate/ReplicationService/snapshot_receiver'
[INFO ] Copying certificates to '/usr/local/etc/certificate/ScsiTarget/pkg-scsi-plugin-server'
[INFO ] Copying certificates to '/usr/local/etc/certificate/SynologyDrive/SynologyDrive'
[INFO ] Copying certificates to '/usr/local/etc/certificate/VPNCenter/OpenVPN'
[INFO ] Copying certificates to '/usr/syno/etc/certificate/_archive/PrfwoX'
[INFO ] Copying certificates to '/usr/syno/etc/certificate/AppPortal/SurveillanceStation_AltPort/'
[INFO ] Rebooting all the things...
Fail to restart [nmbd]. # this was in the original script so I just left it there anyway.
[avahi] restarted.
Fail to restart [ldap-server]. # this was in the original script so I just left it there anyway.
[ftpd] restarted.
restart package [HyperBackup] successfully
restart package [HyperBackupVault] successfully
restart package [LogCenter] successfully
restart package [PrestoServer] successfully
restart package [ReplicationService] successfully
restart package [ScsiTarget] successfully
restart package [SynologyDrive] successfully
restart package [VPNCenter] successfully
Failed to restart package [SurveillanceStation], err=[272]
Sync W3 certificate info successfully
Generated nginx tmp config is not valid
Job for nginx.service failed. See "systemctl status nginx.service" and "journalctl -xe" for details.
[INFO ] Completed


systemctl status nginx.service

(...)
ExecReload=/usr/syno/bin/synow3tool --nginx=reload (code=exited, status=1/FAILURE)
(...)
Start Nginx Server in Abnormal Mode ......
(...)
nginx.service: control process exited, code=exited status=1
Reload failed for Nginx.

@MrPeteH
Copy link

MrPeteH commented Apr 9, 2023

I'm talking about the recommended calling usage in the comments at the top... which of course is assumed to be in some OTHER machine, using scp and ssh to get the data to the Synology NAS. :)

Makes sense! Appreciate the explanation :) @MrPeteH

FWIW, I just did a deeper dive on why platforms like pfSense don't have bash-type shells at all. It's because the security "attack surface" is too large for such capable shells. So they don't have it and discourage installing it! I can understand that... and in this case, the cost is simply to pre-expand those file lists. :)

@footswitch
Copy link

footswitch commented Apr 13, 2023

[EDIT] I was using the wrong chain file.

So I updated to the latest DSM 7.1.1-42962 Update 4, and I'm still facing the same issues.
After running the script, basically everything stops working, and I have to manually import the certificate files in Control Panel --> Security, to restore functionality.
I even changed the script back, to copy the files first to the default directory - thought it could make a difference.
I really don't get what I'm doing wrong, compared to your positive experiences.

Running Task as root

#!/bin/bash

DEBUG=  # Set to any non-empty value to turn on debug mode
error_exit() { echo "[ERROR] $1"; exit 1; }
warn() { echo "[WARN ] $1"; }
info() { echo "[INFO ] $1"; }
debug() { [[ "${DEBUG}" ]] && echo "[DEBUG ] $1"; }

# 1. Initialization
# =================
[[ "$EUID" -ne 0 ]] && error_exit "Please run as root"  # Script only works as root

certs_src_dir="/volume1/exch-cert" # where the new certificate files are located
certs_default_dir="/usr/syno/etc/certificate/system/default"
services_to_restart=("nmbd" "avahi" "ldap-server" "ftpd")
packages_to_restart=(
	"FQDN"
	"HyperBackup"
	"HyperBackupVault"
	"LogCenter"
	"PrestoServer"
	"ReplicationService"
	"ScsiTarget"
	"SynologyDrive"
	"VPNCenter"
	"WebDAVServer"
	"SurveillanceStation"
	)
	
# "ActiveBackup" # ActiveBackup uses a self signed certificate.

target_cert_dirs=(
    "/usr/syno/etc/certificate/system/FQDN"
	"/usr/syno/etc/certificate/smbftpd/ftpd"
	"/usr/local/etc/certificate/HyperBackupVault/HyperBackupVault"
	"/usr/local/etc/certificate/LogCenter/pkg-LogCenter"
	"/usr/local/etc/certificate/PrestoServer/PrestoServer"
	"/usr/local/etc/certificate/ReplicationService/snapshot_receiver"
	"/usr/local/etc/certificate/ScsiTarget/pkg-scsi-plugin-server"
    "/usr/local/etc/certificate/SynologyDrive/SynologyDrive"
	"/usr/local/etc/certificate/VPNCenter/OpenVPN"
    "/usr/local/etc/certificate/WebDAVServer/webdav"
	)
	
# "/usr/local/etc/certificate/ActiveBackup/ActiveBackup" # ActiveBackup uses a Long Term self signed certificate.


# Check files exist at source dir
if [ -e "${certs_src_dir}/privkey.pem" ]
then
    info "ok privkey.pem exists in source dir."
else
    error_exit "privkey.pem not found in source dir. Exiting with no changes."
fi

if [ -e "${certs_src_dir}/cert.pem" ]
then
    info "ok cert.pem exists in source dir."
else
    error_exit "cert.pem not found in source dir. Exiting with no changes."
fi

if [ -e "${certs_src_dir}/fullchain.pem" ]
then
    info "ok fullchain.pem exists in source dir."
else
    error_exit "fullchain.pem not found in source dir. Exiting with no changes."
fi



# Find the default certificate directory
default_dir_name=$(</usr/syno/etc/certificate/_archive/DEFAULT)
if [[ -n "$default_dir_name" ]]; then
    target_cert_dirs+=("/usr/syno/etc/certificate/_archive/${default_dir_name}")
    debug "Default cert directory found: '/usr/syno/etc/certificate/_archive/${default_dir_name}'"
else
    warn "No default directory found. Probably unusual? Check: 'cat /usr/syno/etc/certificate/_archive/DEFAULT'"
fi

# Add Reverse Proxy App directories
for proxy in /usr/syno/etc/certificate/ReverseProxy/*/; do
    debug "Found ReverseProxy dir: ${proxy}"
    target_cert_dirs+=("${proxy}")
done

# Add AppPortal directories
for proxy in /usr/syno/etc/certificate/AppPortal/*/; do
    debug "Found AppPortal dir: ${proxy}"
    target_cert_dirs+=("${proxy}")
done


[[ "${DEBUG}" ]] && set -x


# 2. root own certificates in source dir
# =============================================================
cp "${certs_src_dir}/"{privkey,fullchain,cert}.pem "${certs_default_dir}/" || error_exit "Halting because of error moving files"
chown root:root "${certs_default_dir}/"{privkey,fullchain,cert}.pem || error_exit "Halting because of error chowning files"
info "Certs copied and chowned at system default dir."

# 3. Copy certificates to target directories if they exist
# ========================================================
for target_dir in "${target_cert_dirs[@]}"; do
    if [[ ! -d "$target_dir" ]]; then
      debug "Target cert directory '$target_dir' not found, skipping..."
      continue
    fi

    info "Copying and chowning certificates to '$target_dir'"
    if ! cp "${certs_default_dir}/"{privkey,fullchain,cert}.pem "$target_dir/" && \
        chown root:root "$target_dir/"{privkey,fullchain,cert}.pem; then
          warn "Error copying or chowning certs to ${target_dir}"
    fi
done


# Remove cert files from source folder
rm "${certs_src_dir}/privkey.pem"
rm "${certs_src_dir}/fullchain.pem"
rm "${certs_src_dir}/last-cert.pem"
# Rename cert.pem to last-cert.pem
mv "${certs_src_dir}/cert.pem" "${certs_src_dir}/last-cert.pem"


# 4. Restart services & packages
# ==============================
info "Rebooting all the things..."
for service in "${services_to_restart[@]}"; do
    /usr/syno/bin/synosystemctl restart "$service"
done

for package in "${packages_to_restart[@]}"; do  # Restart packages that are installed & turned on
    /usr/syno/bin/synopkg is_onoff "$package" 1>/dev/null && /usr/syno/bin/synopkg restart "$package"
done

# Restart nginx !!! WARNING !!! this may behave unexpectedly (for instance restarts running VMs / Docker images)
# /usr/syno/bin/synosystemctl restart nginx

# In DSM7 to avoid docker and VMM to restart when restarting nginx use:
# synow3tool --gen-all && systemctl reload nginx
# source: https://www.reddit.com/r/synology/comments/olve56/comment/h5hsogq/

# Faster ngnix restart (if certs don't appear to be refreshing, change to synosystemctl
if ! /usr/syno/bin/synow3tool --gen-all && sudo systemctl reload nginx; then
    warn "nginx failed to reload"
fi


info "Completed"

@telnetdoogie
Copy link

Been playing with this quite a bit, and note I'm using DSM 7.2 beta right now. Was working "ok" but refreshing the certs remained a problem for me, unless I did the full restart which restarted docker and everything - less than optimal.

Here's my modified section 4 which seems to work and I can consistently reproduce (test is to manually update cert with an old one thru UI, then once I've validated the date of the cert, fully close browser, run the script in command line, wait 20 seconds for all restarts to 'take', and then re-open DSM in the browser and re-check the certificate date. So far working well.

# 4. Restart services & packages
# ==============================
info "Rebooting all the things..."
for service in "${services_to_restart[@]}"; do
        /usr/syno/bin/synosystemctl restart "$service"
done
for package in "${packages_to_restart[@]}"; do  # Restart packages that are installed & turned on
        /usr/syno/bin/synopkg is_onoff "$package" 1>/dev/null && /usr/syno/bin/synopkg restart "$package"
done

# Faster ngnix restart (if certs don't appear to be refreshing, change to synosystemctl
if ! /usr/syno/bin/synow3tool --gen-all ; then
    warn "synow3tool --gen-all failed"
fi
if ! /usr/syno/bin/synow3tool --nginx=reload ; then
    warn "/usr/syno/bin/synow3tool --nginx=reload failed"
fi
if ! /usr/syno/bin/synow3tool --restart-dsm-service; then
    warn "/usr/syno/bin/synow3tool --restart-dsm-service failed"
fi

info "Completed"

@telnetdoogie
Copy link

telnetdoogie commented Apr 16, 2023

If anyone is still watching this thread / script... I did some research on what exactly happens when you run synow3tool --gen-all
Basically this command will take the certs from the /usr/syno/etc/certificate/_archive folder and sync all of the other appropriate folders with that certificate. It:

  • Reads the cert files in /usr/syno/etc/certificate/_archive/{randomchars}
  • Re-creates all of the folders in /usr/syno/etc/certificate/ReverseProxy with completely new folders with the certs, and removes the old ones.
  • Creates a new version of the /usr/syno/etc/certificate/system/FQDN folder (naming it something like FQDN.temp)
  • Updates the certs in /usr/syno/etc/certificate/system/FQDN.temp with the ones from archive
  • Deletes /usr/syno/etc/certificate/system/FQDN
  • Renames /usr/syno/etc/certificate/system/FQDN.temp to /usr/syno/etc/certificate/system/FQDN
  • Creates /usr/syno/etc/certificate/system/default.temp
  • Copy all of the certs from /usr/syno/etc/certificate/_archive/{randomchars} to /usr/syno/etc/certificate/system/default.temp
  • Renames /usr/syno/etc/certificate/system/default.temp to /usr/syno/etc/certificate/system/default

...So... basically it looks like the much simpler option is to drop your new certs in the /usr/syno/etc/certificate/_archive/{randomchars} folder and then run synow3tool --gen-all as root. There's no need to do the ReverseProxy folders or the FQDN folders for example.

Because I have no services installed like HyperBackupVault etc., I can't tell if those are taken care of by the synow3tool as well or not, I'll do more research there.

In my case, the only additional folders I have that don't seem to be taken care of by the synow3tool sync are:

  • /usr/syno/etc/certificate/kmip/kmip (Key Manager)
  • /usr/syno/etc/certificate/smbftpd/ftpd (FTPS)

@footswitch
Copy link

footswitch commented Apr 18, 2023

[EDIT] I was using the wrong chain file.

@telnetdoogie this is still not working for me. But at least now I got more descriptive errors...

[WARN ] synow3tool --gen-all failed
(...)
nginx: [emerg] SSL_CTX_use_PrivateKey("/usr/syno/etc/www/certificate/system_default/b4d6e608-ec11-4368-8219-db76840ed58f.pem") failed (SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)
(...)
/usr/syno/bin/synow3tool: unrecognized option '--restart-dsm-service'

@footswitch
Copy link

footswitch commented Apr 18, 2023

[EDIT] I was using the wrong chain file.

[EDIT] Deleting stuff under /usr/syno/etc/www/certificate/[...] messed up my DSM installation.
So my recommendation is don't do it.

If I remove everything inside /usr/syno/etc/www/certificate/system_default/, then synow3tool --gen-all will generate the necessary certificates in those folders.
Also, the SurveillanceStation folder that was missing from my configuration was also located there /usr/syno/etc/www/certificate/
However, I still get this error Generated nginx tmp config is not valid, which only happens if I change the certificates in the other folders.
I still don't know what am I missing here.

@fhemberger
Copy link

@footswitch The error message hints at the key you are copying doesn't match the underlying CSR of the certificate. It's not a matter of the script.

@footswitch
Copy link

@fhemberger THANK YOU for making me recheck everything. It was painful but I got it now.
DSM was misleading me to use the wrong "chain" file all along.
My certificate files are generated with win-acme, which outputs four files:
...-key.pem --> privkey.pem
...-crt.pem --> cert.pem
...-chain-only.pem --> fullchain.pem (wrong)
...-chain.pem -- NOT USED

When I replace the certificate in the UI (Control Panel --> Security --> Certificates), I select these files above, with ...-chain-only.pem being the intermediate certificate, and the certificate was getting replaced with no errors.

But in order for the script to work, the files must be:
...-key.pem --> privkey.pem
...-crt.pem --> cert.pem
...-chain-only.pem -- NOT USED
...-chain.pem --> fullchain.pem (right)

@carmatana
Copy link

carmatana commented Apr 19, 2023

@footswitch

After reading your last comment, specifically the part of Win-Acme...

you (and others) may benefit of using docker-certbot direct in your synology.

I use the certificates only for the Reverse Proxy (in conjunction to Pomerium) to access my docker containers and the DSM interface

I created the certificates with:

docker run -it --rm --name certbot -v "/volume1/Primero/Certs/certbot/etc/letsencrypt:/etc/letsencrypt" -v "/volume1/Primero/Certs/certbot/var/lib/letsencrypt:/var/lib/letsencrypt" -v "/volume1/Primero/Certs/certbot/log/:/var/log" -v "/volume1/Primero/Certs/certbot/certs_to_syno:/certs" certbot/certbot certonly --agree-tos --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d *.carXXXXXX.xyz -d carXXXXXX.xyz

And then I have a scheduled task in Synology's task scheduler that runs every X days to renovate them with:

docker run --rm --name certbot -v "/volume1/Primero/Certs/certbot/etc/letsencrypt:/etc/letsencrypt" -v "/volume1/Primero/Certs/certbot/var/lib/letsencrypt:/var/lib/letsencrypt" -v "/volume1/Primero/Certs/certbot/log/:/var/log/letsencrypt/" -v "/volume1/Primero/Certs/certbot/certs_to_syno:/certs" certbot/certbot renew --no-random-sleep-on-renew --deploy-hook "cp /etc/letsencrypt/live/caXXXXXX.xyz/*.pem /certs"

With the last part of the renovation command (--deploy-hook "cp...."), if the certificates were renovated (meaning the time to do so has arrived, the standard is 30 days before expiration) , the certificates are copied to a specific directory and then (not included above) copied to the correspondent directory inside the synology directory (in my case: /usr/syno/etc/certificate/_archive/u0rjgL) . I could copy them directly to the final the synology directory but I use this bridge to detect any error in the process.

Then, for me it is enough to have in the script:

synow3tool --gen-all && systemctl reload nginx

to have the new certifcates "activated" for the reverse proxy

With this script I have the full end-to-end process automated: from renovation to activation.

The script has to be run with root privileges.

The complete script does a lot of validations that may be redundant, I am not sharing it here because it does not have a good "coding standard" (i.e. I use hard values instead of variables) and comments are in spanish but happy to share more info if required.

@footswitch
Copy link

@carmatana thank you for your input.
In my case we have the certs in Windows because we need them there to begin with.
In this discussion we all have the same starting point: our certificates aren't managed by DSM.
The only thing I didn't test, is if it's enough to copy just to a single default folder, and run:
synow3tool --gen-all && systemctl reload nginx
Or if we do need to copy to every folder and restart every service.

@telnetdoogie
Copy link

@footswitch dropping the certs, named appropriately, in the …/_archive/{randomletters referred to in DEFAULT} and then running the three commands:

/usr/syno/bin/synow3tool --gen-all
/usr/syno/bin/synow3tool --nginx=reload
/usr/syno/bin/synow3tool --restart-dsm-service

should take care of most needs. But as the script above shows, there are some additional folders where certificates are also stored that the above three commands won’t sync to. My latest thoughts are that it might be a good idea to just replace these folders with symlinks to the default folder so it’s easier to keep everything synced, versus copying the certs into more locations. But I haven’t tried that, nor am I sure that those other locations are actually needed or referenced anywhere.

@telnetdoogie
Copy link

I’ve been keeping my own tweaked version of this awesome script Here which, similar to @carmatana - I generate certs with certbot in docker on the synology on a schedule. I don’t use the scp portion to copy, and I just schedule this script to run every week so I added logic to check to see whether the latest certs differ from those already installed for DSM and only do the updates and sync if they are different.

@MrPeteH
Copy link

MrPeteH commented Apr 21, 2023

@telnetdoogie essentially, your "external" source of certs is the docker.

It's still external; the difference is you have kinda direct access to the Synology filesystem.
Some people have SMB access to the filesystem.
Some have scp access to the filesystem.
It's all good. :-D

@MrPeteH
Copy link

MrPeteH commented Apr 21, 2023

The big picture for this:

  • We all generate the certs somewhere, potentially for use in a number of places on our network.
  • We need to get the certs regularly and automagically installed and running in a Synology

So we

  1. copy updated certs into the Syno
  2. run this script which puts them in all the necessary places
  3. AND it restarts necessary packages/services

@edvalley
Copy link

Sorry, but I think acmesh perfectly fits this use case. That said, of course, this is not the same case where your Synology device has Internet access and you can use Let's Encrypt directly from DSM web interface.

@MrPeteH
Copy link

MrPeteH commented Apr 27, 2023

Sorry, but I think acmesh perfectly fits this use case. That said, of course, this is not the same case where your Synology device has Internet access and you can use Let's Encrypt directly from DSM web interface.

Many of us use exactly that somewhere else. The whole purpose of this script is properly injecting the resulting certs into DSM :)

@edvalley
Copy link

Sorry, but I think acmesh perfectly fits this use case. That said, of course, this is not the same case where your Synology device has Internet access and you can use Let's Encrypt directly from DSM web interface.

Many of us use exactly that somewhere else. The whole purpose of this script is properly injecting the resulting certs into DSM :)

What I meant was that acmesh has a deploy hook for Synology DSM. Read some info here.

@ridv
Copy link

ridv commented May 9, 2023

Highly suggest checking out https://github.com/reddec/syno-cli/tree/master

It uses the same API as acmesh but it's in an easy to consume cli for folks to leverage.

@MrPeteH
Copy link

MrPeteH commented May 15, 2023

@edvalley and @ridv

What I meant was that acmesh has a deploy hook for Synology DSM. Read some info here.

According to those documents, this deploy hook does install the cert into DSM, but does NOT install it into the various packages and services ("Certificate should now show up in "Control Panel" -> "Security" -> "Certificates" and can be assigned to Services..."). This script does it all rather nicely.

I love the exploring being done by everyone! It's true:

  • Those who can use acme.sh deploy hooks can take care of the transfer to DSM more easily that way
  • If synow3tool --gen-all takes care of deploying into all packages and services, then the rest of the work can be avoided

I'll do some exploring on both for my use case, when I get a few more round 'tuits. ;)
(My use case: certs generated in pfSense on another host+VM.)

@telnetdoogie
Copy link

telnetdoogie commented May 15, 2023

  • If synow3tool --gen-all takes care of deploying into all packages and services, then the rest of the work can be avoided

it doesn’t do ALL of them, but does seem to take care of the majority. I added a check script that shows all the locations that still have differing certs and eventually incorporated that into my modified version.

I had a hard time understanding if the ones it “leaves out” are even really used / needed but my use cases aren’t that complex (I don’t use ftp etc)

@telnetdoogie
Copy link

telnetdoogie commented May 15, 2023

BTW @catchdave thank you SO much for this script! It’s made ssl renewals and applications incredibly simple and reliable. I’m all about “no human interaction” automation and this and the derivatives have been key to that for me!

@scarab714
Copy link

scarab714 commented May 30, 2023

Thanks you very much for this scripts which is a great help!!
I managed to make it work on my NAS from an NGINX linux server that is used as reverse proxy for all my services. The wildcard let's encrypt certificate for my domain is generated from that server too which explain why this script is perfect for me.

I have a question (hope not too stupid); how did you managed to avoid having to write the password of the user created on synology each time the commands are sent?

When using this command from my nginx server, the password for the synology user is requested twice (for the 2 actions it does)

sudo bash -c scp ${CERT_DIRECTORY}/{privkey,fullchain,cert}.pem $USER@$SYNOLOGY_SERVER:/tmp/ \
     && ssh $USER@$SYNOLOGY_SERVER 'sudo ./replace_synology_ssl_certs.sh'

I would like to find a way to do it passwordless so that I can put it into a crontab on the nginx server.
How did you guys managed to fully automate it (if some of you did)?

I also noticed on the command that the single quote is missing before scp and after /tmp/ like this. Before finding this, the command was not working for me.

sudo bash -c 'scp ${CERT_DIRECTORY}/{privkey,fullchain,cert}.pem $USER@$SYNOLOGY_SERVER:/tmp/' \
     && ssh $USER@$SYNOLOGY_SERVER 'sudo ./replace_synology_ssl_certs.sh'

@scarab714
Copy link

scarab714 commented May 30, 2023

It was too beautiful to be real... When I execute the script, it reset (hard reboot) all my VMs running on VMM which is a big problem for me :(
I see that footswitch is mentioning using "reload" for nginx but this is already implemented on the script provided by catchdave.
I any case, I tried commenting this on the script but it still resetting all VMs.
I found out that this is the restart of the package ScsiTarget that do it...
Do you think this is important restart that package once the certificate is changed?

If you have any idea , I would be very grateful since this issue is blocking me to use this amazing script.

@telnetdoogie
Copy link

telnetdoogie commented Jun 2, 2023

Do you think this is important restart that package once the certificate is changed?

I'm not convinced that those apps need to be restarted; As a check, I'd recommend you try the scripts with service_to_restart and packages_to_restart empty, as well as emptying target_cert_dirs and then run this script once everything is done. It will identify the folders you need to add back to target_cert_dirs that weren't handled by the synology OS and then you'll be able to populate the target folders and the packages / services to restart based only on those that it finds. Then you can test those apps you depend on to ensure they're working properly.

regarding your SSH login, you should be able to use import your public SSH key from the host you're copying FROM into the ~/.ssh/authorized_keys file on your synology (whichever user you're logging in as) and use key authentication which won't require a password prompt.

For example, if you're copying the files from linuxhost as user bob:
login to linuxhost as bob and copy the contents of the file ~/.ssh/id_rsa.pub
If you're logging INTO the admin user of the synology:
login to synology as admin and edit the file ~/.ssh/authorized_keys
paste the contents from bob's public key into the bottom of the authorized_keys file (should be a single line, don't add any additional line breaks etc)

then when you attempt to ssh into the synology from linuxhost it should use the key instead of a password prompt. Do it manually the first time for a prompt to add the machine to known_hosts and then once it's done, you won't have to use a password any more.

@catchdave
Copy link
Author

catchdave commented Jun 3, 2023

I added the script I use to renew my ssl and copy the cert everywhere here: https://gist.github.com/catchdave/3f6f412bbf0f0cec32469fb0c9747295

Could be a useful starting place for folks to create their own (won't be usable out of the box, since you will have different combinations services & servers). This specific one is controlled by a cronjob on the server that plex runs on.

@scarab714
Copy link

Do you think this is important restart that package once the certificate is changed?

I'm not convinced that those apps need to be restarted; As a check, I'd recommend you try the scripts with service_to_restart and packages_to_restart empty, as well as emptying target_cert_dirs and then run this script once everything is done. It will identify the folders you need to add back to target_cert_dirs that weren't handled by the synology OS and then you'll be able to populate the target folders and the packages / services to restart based only on those that it finds. Then you can test those apps you depend on to ensure they're working properly.

regarding your SSH login, you should be able to use import your public SSH key from the host you're copying FROM into the ~/.ssh/authorized_keys file on your synology (whichever user you're logging in as) and use key authentication which won't require a password prompt.

For example, if you're copying the files from linuxhost as user bob: login to linuxhost as bob and copy the contents of the file ~/.ssh/id_rsa.pub If you're logging INTO the admin user of the synology: login to synology as admin and edit the file ~/.ssh/authorized_keys paste the contents from bob's public key into the bottom of the authorized_keys file (should be a single line, don't add any additional line breaks etc)

then when you attempt to ssh into the synology from linuxhost it should use the key instead of a password prompt. Do it manually the first time for a prompt to add the machine to known_hosts and then once it's done, you won't have to use a password any more.

Thank you very much for the quality of your answer telnetdoogie!

I managed to make passwordless work very well and I finally did my own script to replace the certificate on the synology because my need was not to replace the default certificate for everything, but just to replace the one I use only for synology drive because the port 6690 used by drive cannot be proxied by nginx since it is not a https protocol. So for this port, I do a port forwarding while all the rest is going through my nginx server which has the certbot certificate automatically renewed. This explain why I needed a script to send the certificate to the NAS each time it is renewed on the nginx.
So my need was just to use this certificate for drive and let the default one for all the rest.
I do the same with my firewall which is also used for the SSL vpn. I used a script that use the API of the firewall to send and update the certificate to it each time it is renewed on the nginx.

Thank you for the great job that has been made here. It helped me a lot!

Here is my script if it can help anyone.

So first I have manually uploaded a certificate in to the synology so that it creates the cert folder name which is using random characters. I also configure "Synology Drive Server" to use this certificate so that the link is made on the system.
Then, I just update the script to use that folder name and the script just update the cert and restart the "Synology Drive" service.

I made another script on the nginx that do the action of copying and executing that script into the synology NAS.

@matthenning
Copy link

matthenning commented Jun 12, 2023

In my case too it was enough to run the synow3tool and do an nginx restart, thanks for the hints.
My script to find the correct folder and an opportunity to check nginx config before restarting the server:

cp ~/certs/* /usr/syno/etc/certificate/_archive/$(cat /usr/syno/etc/certificate/_archive/DEFAULT)

/usr/syno/bin/synow3tool --gen-all
nginx -t

read -p "nginx config ok?"

systemctl restart nginx

@DJGauthier
Copy link

Hi Catchdave,

I'm looking for a solution for my DS1010+ with DSM 5.2-5967 Update 9.
I found many article on the net but nothing help me until yet.

Maybe you have already an idee.

Best Regards
DJG.

@catchdave
Copy link
Author

catchdave commented Jun 19, 2023

Hi Catchdave,

I'm looking for a solution for my DS1010+ with DSM 5.2-5967 Update 9. I found many article on the net but nothing help me until yet.

@DJGauthier : I have never had a system with 5.x OS so I simply don't know what it looks like. Fundamentally, you simply need to find where the SSL certs are stored and then find out the right way to restart associated services to use the new certs when you upload them. Perhaps the locations are similar to the 6.x version of this (look at the gist's history). You can probably easily find certs using a find command once you SSH in.

@DJGauthier
Copy link

DJGauthier commented Jun 19, 2023 via email

@catchdave
Copy link
Author

I don't know where you are getting that error from, or what the context is. So I can't really work off just that error (is it on the browser, on the synology box, elsewhere). You could also consider upgrading your OS. version 5.x is a 8 years old.

@DJGauthier
Copy link

DJGauthier commented Jun 21, 2023 via email

@DJGauthier
Copy link

DJGauthier commented Jun 21, 2023 via email

@telnetdoogie
Copy link

@DJGauthier It appears you're running into a port forwarding issue. Have you been able to renew certs successfully using the Synology UI? The address you're using - [mysyno].synology.me will point to your public WAN IP, so you're unlikely to be able to test that locally unless you're doing some hairpin NAT rules on your router. I'd start with validating that LetsEncrypt certificates can be generated via the UI on the Synology NAS before you try to use scripts to do the same.

Alternatively you could switch to certbot using DNS-01 challenge instead of HTTP-01 to avoid the need to have ports open and forwarded. More info here:

https://letsencrypt.org/docs/challenge-types/

@DJGauthier
Copy link

DJGauthier commented Jun 21, 2023 via email

@bogdanrotariu
Copy link

Hi,

Is it necessary to reboot all the services? As the script restarts all the services, docker and virtual machines that are running on the synology are affected.

I guess, some of Step 4. "Rebooting all the things..." can be disabled by default ?

@catchdave
Copy link
Author

I am not sure—this was the easiest method to make sure everything works. Feel free to experiment :)

You definitely need to restart any web servers serving the cert (so nginx at least).

@telnetdoogie
Copy link

telnetdoogie commented Jul 24, 2023

Is it necessary to reboot all the services?

It's not, unless you have a service specifically that seems to need it.

For me, simply running:
/usr/syno/bin/synow3tool --gen-all
/usr/syno/bin/synow3tool --nginx=reload
and /usr/syno/bin/synow3tool --restart-dsm-service

...refreshes certs for DSM and Reverse Proxy setups with no need to bounce apps (like docker, which takes a long time and is disruptive if runnning network services in containers)

@timelordx
Copy link

I use Caddy Web Server as my reverse proxy running inside Container Manager (Docker) on my Synology DSM. Caddy is also great as a certificate manager.

Inspired by you and your code (all of you here), I wrote my own script synology-cert-deploy.sh which takes only the private key and full certificate chain files as input, since those are the only two TLS files coming from Caddy, and deploys them to Synology DSM. As long as it has the new private key and full chain, it can extract all the remaining files from these two files.

@nferch
Copy link

nferch commented Aug 7, 2023

On DSM 7.2-64570 Update 1, the script will update the certificates (as verified by the Certificate tab of the Security applet in the Control Panel), but fail to restart the DSM webserver which runs on port 5001 for me.

Manually issuing the /usr/syno/bin/synow3tool --nginx=reload command seemed to do the trick, though.

@Daermegil
Copy link

Hi,
I installed this script and when running it everything seems fine.
The certificate in the control panel is updated. But when browsing to the DSM webserver on port 5001 it keeps showing the old certificate.
I already rebooted the device, but keeps showing the old certificate.

What am I missing?

Thanks.

@telnetdoogie
Copy link

@Daermegil check in the Certificates pane in DSM and see if you have one certificate set up, and make sure it's this one. It is possible to have multiples and assign a different one to DSM.

@Daermegil
Copy link

@telnetdoogie thank you for your quick reply.
I do have 2 certificates, one is my actual certificate. The other one is for quickconnect, but I can't delete that one. I also don't think it is the one causing problems.

But I think I see the problem. The certificate I'm using is RSA, but the old one from synology is RSA/ECC. The webserver is probally using ECC I guess, because the file ECC-cert.pem is still my old certificate. I'm just going to reset the certificates and readd them. But I have reached my request limit so I'll have to wait a bit.

@telnetdoogie
Copy link

telnetdoogie commented Aug 26, 2023

@Daermegil RSA is fine; that's what I'm using too... I did see in my notes that I needed to manually import the LE certificates into DSM before the automation worked reliably.
When you're prompted for Private Key that's the privkey.pem file. When you're prompted for the Certificate that's cert.pem. When prompted for the Intermediate Certificate that's fullchain.pem.

DSM does accept certs without adding the intermediate cert, but it causes some things to fail.

Try manually adding those and see if that works for you. Once the manual add works, the automation should also work.

I think from my memory, if you upload the wrong files (and it'll still appear to work sometimes) in the wrong inputs, it will actually rename things on the filesystem but when the automation works, it's now updating the wrong files. I'm not stating that right but I definitely had some weirdness early on because I wasn't providing the Intermediate cert; instead I was using fullchain.pem as the Certificate file, which gave me issues with VPN, for example.

@Daermegil
Copy link

@telnetdoogie I added a RSA only certificate and replaced it using the script, everything seems to work brilliantly now!
Thanks for the help!

@Redjard
Copy link

Redjard commented Sep 26, 2023

This appears to miss all "WebStation" pages for me.
I added

# Add WebStation directories
for webstation in /usr/local/etc/certificate/WebStation/*/; do
    debug "Found WebStation dir: ${webstation}"
    target_cert_dirs+=("${webstation}")
done

But those then weren't re"compiled" into the correct folders nginx uses, so I wrote

# somehow WebStation ends up not getting applied later, so lets manually do it (if it works later it will simply get overwritten)
for ws in /usr/syno/etc/www/certificate/WebStation_*/; do
    info "Copying WebStation certificates to '$ws'"
    ws_crt=$( grep -o "$ws"cert.conf -Pe '(?<=ssl_certificate\s)\s*.*(?=;$)' | xargs )
    ws_key=$( grep -o "$ws"cert.conf -Pe '(?<=ssl_certificate_key\s)\s*.*(?=;$)' | xargs )
    ln -f "${certs_src_dir}/"fullchain.pem "$ws_crt"
    ln -f "${certs_src_dir}/"privkey.pem "$ws_key"
done

which appears to be sufficient. I still had to add nginx -s reload to get changes to show up.

Is this really the only way to do this?


In the process I grepped through my /usr and also saw my cert ended up in /usr/local/etc/certificate/LogCenter/pkg-LogCenter/ somehow, so it might be good to add that path as

target_cert_dirs=(
    [...]
    "/usr/local/etc/certificate/LogCenter/pkg-LogCenter/")

@xiaozhuai
Copy link

xiaozhuai commented Sep 28, 2023

# Faster ngnix restart (if certs don't appear to be refreshing, change to synosystemctl
if ! /usr/syno/bin/synow3tool --gen-all && sudo systemctl reload nginx; then
    warn "nginx failed to restart"
fi

The above lines should change to

if ! /usr/syno/bin/synow3tool --gen-all; then
    warn "nginx failed to generate config"
fi

info "Restart nginx"
# Faster ngnix restart (if certs don't appear to be refreshing, change to systemctl restart)
systemctl reload nginx

Otherwise, systemctl reload nginx will not be executed.

@catchdave
Copy link
Author

catchdave commented Oct 3, 2023

Otherwise, systemctl reload nginx will not be executed.

@xiaozhuai: I'm not sure I understand how the above is true (or why you want to separate out the two commands). The intent is that we do not restart if the certs did not generate correctly and instead issue a warning.

systemctl reload nginx will be executed if and only if the synow3tool --gen-all command is executed correctly, which is the intent of the original code.
Could you provide an example where this does not work as intended?

@xiaozhuai
Copy link

xiaozhuai commented Oct 5, 2023

Could you provide an example where this does not work as intended?

@catchdave
That's odd.
I try this script on my machine last time and foud the second command never exec.
And I'm sure the /usr/syno/bin/synow3tool --gen-all is ok.


Confirmed, the script is wrong. You can try below.

#!/usr/bin/env bash

if ! true && echo "Good"; then
    echo "Not good"
fi

It prints nothing.
It should be

#!/usr/bin/env bash

if ! (true && echo "Good"); then
    echo "Not good"
fi

And it prints Good.

So the script should change to

# Faster ngnix restart (if certs don't appear to be refreshing, change to systemctl restart)
if ! (/usr/syno/bin/synow3tool --gen-all && systemctl reload nginx); then
    warn "nginx failed to restart"
fi

@catchdave
Copy link
Author

catchdave commented Oct 5, 2023

Thanks --you are right, it should have parenthesis! I updated the script. Appreciate the suggestion, @xiaozhuai

@raven2cz
Copy link

raven2cz commented Dec 9, 2023

Hello @catchdave. Nice script! I want to test it. But I have DS218+ where in all certs directories are not just 3 files which is copied by your script.
There are these structure:

cert.pem  chain.pem  fullchain.pem  info  privkey.pem  root.pem  short-chain.pem

I can create cert.pem chain.pem fullchain.pem privkey.pem
But I haven't idea about root.pem short-chain.pem.

Do you have same structure? Or is it something new? How to solve it? Thx for info.

@telnetdoogie
Copy link

@raven2cz my advice is to initially manually add your LE cert and make it default. Through the DSM interface, the mapping of LE generated files is:

       	Private Key ---------------> privkey.pem
       	Certificate ---------------> cert.pem
	Intermediate Certificate --> fullchain.pem

Once that's done and the LE cert is now your default certificate, the script should work fine.

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