Repository: JessThrysoee/synology-letsencrypt
Branch: master
Commit: 358e6ab75ea9
Files: 7
Total size: 15.0 KB
Directory structure:
gitextract_j5uwdl9i/
├── .gitignore
├── README.md
├── install.sh
├── synology-letsencrypt-lib.sh
├── synology-letsencrypt-reload-services.sh
├── synology-letsencrypt.sh
└── uninstall.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.vscode/
PRIVATE_NOTES.txt
.idea/*
================================================
FILE: README.md
================================================
# synology-letsencrypt
Create and manage a [Let's Encrypt](https://letsencrypt.org/) certificate on a Synology NAS.
This project uses [lego](https://go-acme.github.io/lego/) and the [ACME DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) with any supported [DNS provider](https://go-acme.github.io/lego/dns/).
## Install & Update Script
To **install** or **update** `synology-letsencrypt`, run the [install script](install.sh). You can either download and run the script manually, or use the following curl command:
```sh
curl -sSL https://raw.githubusercontent.com/JessThrysoee/synology-letsencrypt/master/install.sh | bash
```
The script must be run as root. You can SSH into your NAS as an admin user and then run `sudo -i` to become root (using the same password as the admin user).
> [!IMPORTANT]
> Migration from `lego` v4 to v5
If you are updating from a version of `lego` earlier than v5, note that v5 introduces breaking changes to the CLI, directory structure, and JSON file format.
After running the [install script](install.sh) to update `lego` and this repository's scripts to the latest versions, run the new v5 command `lego migrate` before running any other commands. For example:
/usr/local/bin/lego migrate --path /usr/local/etc/synology-letsencrypt/
More information is available in the [v5 blog post](https://ldez.github.io/blog/2026/05/11/lego-v5/).
Also note that the optional environment variables `LEGO_RUN_OPTIONS` and `LEGO_RENEW_OPTIONS` in your `env` file have been replaced with a single optional variable, `LEGO_OPTIONS`.
## Configuration
Update `/usr/local/etc/synology-letsencrypt/env` with your domain(s), email address, and DNS API key:
```sh
DOMAINS=(--domains "example.com" --domains "*.example.com")
EMAIL="user@example.com"
# Specify the DNS provider (this example is from https://go-acme.github.io/lego/dns/simply/)
DNS_PROVIDER="simply"
export SIMPLY_ACCOUNT_NAME=XXXXXXX
export SIMPLY_API_KEY=XXXXXXXXXX
export SIMPLY_PROPAGATION_TIMEOUT=1800
export SIMPLY_POLLING_INTERVAL=30
# Should you need it; additional options can be passed directly to lego
#LEGO_OPTIONS=(--key-type "RSA4096" --ari-disable --server "letsencrypt-staging")
```
You should now be able to run `/usr/local/bin/synology-letsencrypt.sh`.
To schedule a daily task, log in to Synology DSM and add a user-defined script:
Synology DSM -> Control Panel -> Task Scheduler
Create -> Scheduled Task -> User-defined script
General -> User = root
Task Settings -> User-defined script = /bin/bash /usr/local/bin/synology-letsencrypt.sh
To secure services with the certificate, see the [Configure Certificates](https://kb.synology.com/en-global/DSM/help/DSM/AdminCenter/connection_certificate?version=7#b_64) documentation.
### Multiple Certificates
If you need to generate multiple certificates, you can run `synology-letsencrypt.sh` with the path to a certificate-specific configuration:
```shellsession
$ /usr/local/bin/synology-letsencrypt.sh -p /usr/local/etc/synology-letsencrypt/example.com
$ /usr/local/bin/synology-letsencrypt.sh -p /usr/local/etc/synology-letsencrypt/other-example.com
```
This creates a separate configuration in
`/usr/local/etc/synology-letsencrypt/example.com/env` and
`/usr/local/etc/synology-letsencrypt/other-example.com/env`, respectively.
You can then customize each one as needed, including the `hook` file in each configuration.
This is useful if you need more than one certificate on your Synology or want to generate a certificate for another host managed by the Synology.
### Customizing the hook script
By default, `synology-letsencrypt.sh` overwrites any changes you make to the hook script to preserve core functionality.
If you have customized the hook script, you can preserve your changes by adding the `-c` option when running the command:
```shellsession
$ /usr/local/bin/synology-letsencrypt.sh -c
```
## Uninstall
To **uninstall** `synology-letsencrypt`, run the [uninstall script](uninstall.sh). You can either download and run the script manually, or use the following curl command:
```sh
curl -sSL https://raw.githubusercontent.com/JessThrysoee/synology-letsencrypt/master/uninstall.sh | bash
```
## Consider the [acme-dns](https://github.com/joohoi/acme-dns) project
...if your DNS provider is not _directly_ supported by `lego`, or if you want to avoid storing your DNS provider's API keys on your Synology device. `lego` supports [acme-dns](https://go-acme.github.io/lego/dns/acme-dns/).
================================================
FILE: install.sh
================================================
#!/bin/bash
{
arch_map() {
local arch
arch=$(uname -m)
case "$arch" in
x86_64) echo "amd64" ;;
aarch64) echo "arm64" ;;
armv7l) echo "armv7" ;;
i686|i386) echo "386" ;;
*) return 1 ;;
esac
}
while getopts ":a:h" opt; do
case $opt in
a) ARCH="$OPTARG";;
h) echo "Usage: $0 [-a <arch>]"
echo " -a <arch> Architecture of lego to install (auto-detect: $(arch_map || echo "unsupported"))"
exit 0
;;
:) echo "Error: -${OPTARG} requires an argument.";;
\?) echo "Invalid option -$OPTARG" >&2
;;
esac
done
if [[ -z $ARCH ]]; then
ARCH=$(arch_map) || {
echo "Error: Unsupported architecture '$(uname -m)'. Use -a to specify manually." >&2
exit 1
}
fi
permissions() {
local mod="$1"
local path="$2"
sudo chown root:root "$path"
sudo chmod "$mod" "$path"
}
install_lego() {
local path="/usr/local/bin/lego"
local url
url="$(
curl -sSL "https://api.github.com/repos/go-acme/lego/releases/latest" \
| jq --unbuffered -r --arg arch "$ARCH" '.assets[].browser_download_url | select(.|endswith("linux_\($arch).tar.gz"))'
)"
if [[ -z $url ]]; then
echo "Could not find lego download URL for architecture '$ARCH'! Try a different architecture maybe? See '$0 -h'" >&2
exit 1
fi
curl -sSL "$url" \
| sudo tar -zx -C "${path%/*}" -- "${path##*/}"
permissions 755 "$path"
printf "installed: %s\n" "$path"
}
install_script() {
local name="$1"
local path="/usr/local/bin/$name"
sudo curl -sSL -o "$path" "https://raw.githubusercontent.com/JessThrysoee/synology-letsencrypt/master/$name"
permissions 755 "$path"
printf "installed: %s\n" "$path"
}
install_configuration() {
local dir="/usr/local/etc/synology-letsencrypt"
local env="$dir/env"
sudo mkdir -p "$dir"
permissions 700 "$dir"
if [[ ! -s $env ]]; then
sudo tee "$env" > /dev/null <<EOF
DOMAINS=(--domains "example.com" --domains "*.example.com")
EMAIL="user@example.com"
# Specify DNS Provider (this example is from https://go-acme.github.io/lego/dns/simply/)
DNS_PROVIDER="simply"
export SIMPLY_ACCOUNT_NAME=XXXXXXX
export SIMPLY_API_KEY=XXXXXXXXXX
export SIMPLY_PROPAGATION_TIMEOUT=1800
export SIMPLY_POLLING_INTERVAL=30
# Should you need it; additional options can be passed directly to lego
#LEGO_OPTIONS=(--key-type "rsa4096" --server "https://acme-staging-v02.api.letsencrypt.org/directory")
#LEGO_RUN_OPTIONS=()
#LEGO_RENEW_OPTIONS=(--ari-disable)
EOF
fi
permissions 600 "$env"
printf "installed: %s\n" "$env"
cat << EOF
All done!
Check $env and edit as needed.
EOF
}
ensure_usr_local_bin() {
local dir="/usr/local/bin"
if [[ ! -d $dir ]]; then
mkdir -p "$dir"
permissions 755 "$dir"
fi
}
uninstall_deprecated_script() {
local path="/usr/local/bin/$1"
if [[ -f "$path" ]]; then
sudo rm "$path"
printf "uninstalled: %s\n" "$path"
fi
}
install() {
ensure_usr_local_bin
install_lego
install_script "synology-letsencrypt.sh"
install_script "synology-letsencrypt-reload-services.sh"
install_script "synology-letsencrypt-lib.sh"
uninstall_deprecated_script "synology-letsencrypt-make-cert-id.sh"
install_configuration
}
install
}
================================================
FILE: synology-letsencrypt-lib.sh
================================================
#!/bin/bash -e
makeCertId() {
local cert_id_path="$1"
local archive_path="$2"
local cert_id=""
if [[ -s $cert_id_path ]]; then
source "$cert_id_path"
fi
mkdir -p "$archive_path"
if [[ -z $cert_id ]]; then
local archive_cert_path
archive_cert_path=$(mktemp -d "$archive_path"/XXXXXX)
cert_id="${archive_cert_path##*/}"
printf 'cert_id=%s' "$cert_id" > "$cert_id_path"
fi
mkdir -p "$archive_path/$cert_id"
local info="$archive_path/INFO"
if [[ -s $info ]]; then
local has_cert_id
has_cert_id="$(jq --arg cert_id "$cert_id" 'has($cert_id)' "$info")"
if [[ $has_cert_id != true ]]; then
# append
local tmp_info
tmp_info=$(mktemp)
jq --arg cert_id "$cert_id" '.[$cert_id] = { desc: "", services: [] }' < "$info" > "$tmp_info" \
&& \mv "$tmp_info" "$info"
fi
else
# create
jq -n --arg cert_id "$cert_id" '{ ($cert_id) : { desc: "", services: [] } }' > "$info"
fi
}
# implement `lego`SanitizedName [ref.: https://github.com/go-acme/lego/blob/f9f9645cf7be7d399c025ec596484263eb9f963a/cmd/internal/storage/storage_certificates.go#L67]
sanitizedName() {
if command -v python3 &>/dev/null; then
python3 -c '
import sys
name = sys.argv[1]
try:
safe = name.replace(":", "-").replace("*", "_").encode("idna").decode("ascii")
except Exception as e:
sys.stderr.write("Could not sanitize the name: %s\n" % e)
sys.exit(1)
out = "".join(ch for ch in safe if ch.isalnum() or ch in "-_.@")
sys.stdout.write(out)
' "$1" || return $?
else
python2 -c '
import sys
name = sys.argv[1]
try:
safe = name.decode("utf-8").replace(":", "-").replace("*", "_").encode("idna")
except Exception as e:
sys.stderr.write("Could not sanitize the name: %s\n" % e)
sys.exit(1)
out = "".join(ch for ch in safe if ch.isalnum() or ch in "-_.@")
sys.stdout.write(out)
' "$1" || return $?
fi
}
================================================
FILE: synology-letsencrypt-reload-services.sh
================================================
#!/bin/bash -e
[[ $EUID == 0 ]] || { echo >&2 "This script must be run as root"; exit 1; }
# Reload services assigned to the certificate with the key `cert_id` in the INFO file.
# Inspired by https://github.com/bartowl/synology-stuff/blob/master/reload-certs.sh
CERT_ID="$1"
ARCHIVE_PATH="/usr/syno/etc/certificate/_archive"
INFO="$ARCHIVE_PATH/INFO"
get() {
local i="$1" prop="$2"
jq -r --arg cert_id "$CERT_ID" --arg i "$i" --arg prop "$prop" '.[$cert_id].services[$i|tonumber][$prop]' "$INFO"
}
find_exec_path() {
local subscriber="$1"
# search DSM6 and DSM7 paths
for base in /usr/libexec/certificate.d /usr/local/libexec/certificate.d \
/usr/syno/share/certificate.d /usr/local/share/certificate.d
do
script="$base/$subscriber"
if [[ -x "$script" ]]; then
printf '%s' "$script"
break
fi
done
}
find_cert_path() {
local subscriber="$1" service="$2"
for base in /usr/local/etc/certificate /usr/syno/etc/certificate; do
dir="$base/$subscriber/$service"
if [[ -e "$dir" ]]; then
printf '%s' "$dir"
break
fi
done
}
reload_services() {
services_length=$(jq -r --arg cert_id "$CERT_ID" '.[$cert_id].services|length' "$INFO")
for (( i = 0; i < services_length; i++ )); do
subscriber=$(get "$i" subscriber)
service=$(get "$i" service)
cert_path="$(find_cert_path "$subscriber" "$service")"
if [[ -z $cert_path ]]; then
echo >&2 "cert_path not found in for \"$subscriber\" \"$service\""
continue
fi
if diff -q "$ARCHIVE_PATH/$CERT_ID/cert.pem" "$cert_path/cert.pem" >/dev/null; then
continue # no change
fi
cp "$ARCHIVE_PATH/$CERT_ID/"{cert,chain,fullchain,privkey}.pem "$cert_path/"
exec_path="$(find_exec_path "$subscriber")"
if [[ -x $exec_path ]]; then
"$exec_path" "$service"
fi
profile_exec_script="${subscriber}.sh"
if [[ $subscriber == "system" && $service == "default" ]]; then
profile_exec_script="dsm.sh"
fi
profile_exec_path="/usr/libexec/security-profile/tls-profile/$profile_exec_script"
if [[ -x $profile_exec_path ]]; then
"$profile_exec_path"
fi
done
}
reload_services
================================================
FILE: synology-letsencrypt.sh
================================================
#!/bin/bash -e
[[ $EUID == 0 ]] || { echo >&2 "This script must be run as root"; exit 1; }
while getopts ":p:ch" opt; do
case $opt in
p) LEGO_PATH="$OPTARG" ;;
c) CREATE_HOOK=false ;;
h)
echo "Usage: $0 [options]"
echo " -p <lego_path> The path where Lego will install your certs"
echo " -c Suppress [c]reation of the hook scripts, if you have your own"
exit 0
;;
:) echo "Error: -${OPTARG} requires an argument" >&2 ;;
\?) echo "Invalid option -$OPTARG" >&2 ;;
esac
done
source /usr/local/bin/synology-letsencrypt-lib.sh
LEGO_PATH=${LEGO_PATH:-/usr/local/etc/synology-letsencrypt}
CREATE_HOOK=${CREATE_HOOK:-true}
source "$LEGO_PATH/env"
export LEGO_PATH
archive_path="/usr/syno/etc/certificate/_archive"
cert_path="$LEGO_PATH/certificates"
hook_path="$LEGO_PATH/hook"
mkdir -p "$cert_path"
cert_domain="$(sanitizedName "${DOMAINS[1]}")"
## cert_id
cert_id_path="$cert_path/$cert_domain.cert_id"
makeCertId "$cert_id_path" "$archive_path"
source "$cert_id_path"
if [[ -z $cert_id ]]; then
echo >&2 "ID not found in $cert_id_path"
exit 1
fi
## install hook
archive_cert_path="$archive_path/$cert_id"
if [[ ! -d $archive_cert_path ]]; then
mkdir -p "$archive_cert_path"
fi
if [[ ${CREATE_HOOK} == true ]]; then
cat >"$hook_path" <<EOF
#!/bin/bash
cp "${cert_path}/${cert_domain}.crt" "${archive_cert_path}/cert.pem"
cp "${cert_path}/${cert_domain}.crt" "${archive_cert_path}/fullchain.pem"
cp "${cert_path}/${cert_domain}.issuer.crt" "${archive_cert_path}/chain.pem"
cp "${cert_path}/${cert_domain}.key" "${archive_cert_path}/privkey.pem"
/usr/local/bin/synology-letsencrypt-reload-services.sh "$cert_id"
EOF
chmod 700 "$hook_path"
fi
# https://go-acme.github.io/lego/usage/cli/
/usr/local/bin/lego run \
--accept-tos \
--deploy-hook "$hook_path" \
--key-type "RSA4096" \
--email "$EMAIL" \
--dns "$DNS_PROVIDER" \
"${DOMAINS[@]}" \
"${LEGO_OPTIONS[@]}"
================================================
FILE: uninstall.sh
================================================
#!/bin/bash
{
uninstall_script() {
local path="/usr/local/bin/$1"
sudo rm "$path"
printf "uninstalled: %s\n" "$path"
}
uninstall_deprecated_script() {
local path="/usr/local/bin/$1"
if [[ -f "$path" ]]; then
sudo rm "$path"
printf "uninstalled: %s\n" "$path"
fi
}
uninstall_configuration() {
local path="/usr/local/etc/synology-letsencrypt"
sudo rm -r "$path"
printf "uninstalled configuration: %s\n" "$path"
}
uninstall_cert_message() {
echo ""
echo "Remove any Let's Encrypt certificates from:"
echo " Synology DSM -> Control Panel -> Security -> Certificate"
echo ""
}
uninstall() {
uninstall_script "lego"
uninstall_script "synology-letsencrypt.sh"
uninstall_script "synology-letsencrypt-reload-services.sh"
uninstall_script "synology-letsencrypt-lib.sh"
uninstall_deprecated_script "synology-letsencrypt-make-cert-id.sh"
uninstall_configuration
uninstall_cert_message
}
uninstall
}
gitextract_j5uwdl9i/ ├── .gitignore ├── README.md ├── install.sh ├── synology-letsencrypt-lib.sh ├── synology-letsencrypt-reload-services.sh ├── synology-letsencrypt.sh └── uninstall.sh
Condensed preview — 7 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (17K chars).
[
{
"path": ".gitignore",
"chars": 35,
"preview": ".vscode/\nPRIVATE_NOTES.txt\n.idea/*\n"
},
{
"path": "README.md",
"chars": 4546,
"preview": "# synology-letsencrypt\n\nCreate and manage a [Let's Encrypt](https://letsencrypt.org/) certificate on a Synology NAS.\n\nTh"
},
{
"path": "install.sh",
"chars": 3402,
"preview": "#!/bin/bash\n\n{\n\narch_map() {\n local arch\n arch=$(uname -m)\n case \"$arch\" in\n x86_64) echo \"amd64\" "
},
{
"path": "synology-letsencrypt-lib.sh",
"chars": 2020,
"preview": "#!/bin/bash -e\n\nmakeCertId() {\n local cert_id_path=\"$1\"\n local archive_path=\"$2\"\n\n local cert_id=\"\"\n if [[ -"
},
{
"path": "synology-letsencrypt-reload-services.sh",
"chars": 2373,
"preview": "#!/bin/bash -e\n\n[[ $EUID == 0 ]] || { echo >&2 \"This script must be run as root\"; exit 1; }\n\n# Reload services assigned "
},
{
"path": "synology-letsencrypt.sh",
"chars": 2028,
"preview": "#!/bin/bash -e\n\n[[ $EUID == 0 ]] || { echo >&2 \"This script must be run as root\"; exit 1; }\n\nwhile getopts \":p:ch\" opt; "
},
{
"path": "uninstall.sh",
"chars": 998,
"preview": "#!/bin/bash\n\n{\n\nuninstall_script() {\n local path=\"/usr/local/bin/$1\"\n\n sudo rm \"$path\"\n\n printf \"uninstalled: %"
}
]
About this extraction
This page contains the full source code of the JessThrysoee/synology-letsencrypt GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 7 files (15.0 KB), approximately 4.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.