master 358e6ab75ea9 cached
7 files
15.0 KB
4.4k tokens
1 requests
Download .txt
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
}
Download .txt
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.

Copied to clipboard!