├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── README.md ├── acmelog ├── docker-compose.yml ├── docker-entrypoint.sh ├── sample.env └── stage-env.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # dotenv environment variables file 2 | .env 3 | 4 | # Mount for ACME script 5 | /data 6 | 7 | # Temporary secrets for testing 8 | /secrets 9 | 10 | # Local filesystem files 11 | .DS_Store 12 | 13 | # Docker-compose file for testing 14 | debug.yml -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docker-secret"] 2 | path = docker-secret 3 | url = https://github.com/markdumay/docker-secret.git 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.11 2 | 3 | # Add GNU grep and logrotate on top of neilpang/acme.sh:latest 4 | RUN apk update -f \ 5 | && apk --no-cache add -f \ 6 | openssl \ 7 | openssh-client \ 8 | coreutils \ 9 | bind-tools \ 10 | curl \ 11 | socat \ 12 | tzdata \ 13 | oath-toolkit-oathtool \ 14 | tar \ 15 | grep \ 16 | logrotate \ 17 | && rm -rf /var/cache/apk/* 18 | 19 | ENV LE_CONFIG_HOME /acme.sh 20 | 21 | ENV AUTO_UPGRADE 1 22 | 23 | #Install 24 | ADD ./ /install_acme.sh/ 25 | RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/ 26 | 27 | 28 | RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab - 29 | 30 | RUN for verb in help \ 31 | version \ 32 | install \ 33 | uninstall \ 34 | upgrade \ 35 | issue \ 36 | signcsr \ 37 | deploy \ 38 | install-cert \ 39 | renew \ 40 | renew-all \ 41 | revoke \ 42 | remove \ 43 | list \ 44 | showcsr \ 45 | install-cronjob \ 46 | uninstall-cronjob \ 47 | cron \ 48 | toPkcs \ 49 | toPkcs8 \ 50 | update-account \ 51 | register-account \ 52 | create-account-key \ 53 | create-domain-key \ 54 | createCSR \ 55 | deactivate \ 56 | deactivate-account \ 57 | set-notify \ 58 | ; do \ 59 | printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ 60 | ; done 61 | 62 | RUN printf "%b" '#!'"/usr/bin/env sh\n \ 63 | if [ \"\$1\" = \"daemon\" ]; then \n \ 64 | trap \"echo stop && killall crond && exit 0\" SIGTERM SIGINT \n \ 65 | crond && while true; do sleep 1; done;\n \ 66 | else \n \ 67 | exec -- \"\$@\"\n \ 68 | fi" >/entry.sh && chmod +x /entry.sh 69 | 70 | VOLUME /acme.sh 71 | 72 | 73 | # Add shell script to stage Docker secrets 74 | COPY stage-env.sh /usr/local/bin/stage-env.sh 75 | RUN chmod +x /usr/local/bin/stage-env.sh 76 | 77 | # Add logrotate configuration 78 | COPY acmelog /etc/logrotate.d/acmelog 79 | RUN chmod 644 /etc/logrotate.d/acmelog && chown root:root /etc/logrotate.d/acmelog 80 | 81 | # Override entrypoint with custom script 82 | COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh 83 | RUN chmod +x /usr/local/bin/docker-entrypoint.sh 84 | ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] 85 | 86 | # Execute default command of parent image 87 | CMD ["--help"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mark Dumay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Synology TLS 2 | 3 | 4 | 5 |

6 | Automatically Update Let's Encrypt Wildcard Certificates for Synology NAS 7 |
8 |

9 | 10 | 11 | 12 |

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

32 | 33 | 34 |

35 | About • 36 | Built With • 37 | Prerequisites • 38 | Testing • 39 | Deployment • 40 | Usage • 41 | Contributing • 42 | Credits • 43 | Donate • 44 | License 45 |

46 | 47 | 48 | ## About 49 | [Synology][synology_url] is a popular manufacturer of Network Attached Storage (NAS) devices. It provides a web-based user interface called Disk Station Manager (DSM). Building upon [acme.sh][acmesh_url], *Synology TLS* simplifies the setup of secure access to DSM via HTTPS. It uses Let's Encrypts to automatically issue and renew TLS certificates for a specific internet domain. By using CloudFlare, Synology TLS allows the NAS to stay behind a firewall without exposing ports to the public internet. The package is set up as a Docker image to simplify deployment and uses Docker Swarm secrets to secure credentials. 50 | 51 | 54 | 55 | ## Built With 56 | The project uses the following core software components: 57 | * [acme.sh][acmesh_url] - A pure Unix shell script to automatically issue & renew free certificates from Let's Encrypt 58 | * [Docker][docker_url] - Container platform (including Swarm and Compose) 59 | 60 | 61 | ## Prerequisites 62 | | :warning: At the time of writing, the latest Docker package released by Synology is 18.09.0-513. This package does not expose environment variables to Docker Swarm correctly. To solve the issue, update Docker on your Synology to the latest version using this [script][synology_docker]. | 63 | | --- | 64 | 65 | Synology TLS can run on any Docker-capable host. The setup has been tested locally on macOS Catalina and a Synology NAS running DSM 6.2. Other prerequisites are: 66 | 67 | * **A registered domain name is required** - A domain name is required to configure SSL certificates that will enable secure traffic to your Synology NAS. You should have the ability to configure DNS entries for your domain too. 68 | 69 | * **Docker Compose and Docker Swarm are required** - Synology TLS is to be deployed as Docker container in swarm mode to enable Docker *secrets*. This [reference guide][swarm_init] explains how to initialize Docker Swarm on your host. 70 | 71 | * **A CloudFlare account and token are required** - Synology TLS uses CloudFlare to automate the DNS configuration. CloudFlare offers a free plan that should suffice for most needs. Follow the wizard `+ Add a Site` on the homepage to let CloudFlare manage the DNS of your domain. Once done, you will need to set up an API Token for Synology TLS too. From CloudFlare's homepage, go to `My Profile ➡ API Tokens`. Add a new token with `Zone/Zone/Edit` and `Zone/DNS/Edit` permissions and include your domain in the 'Zone Resources'. Make a safe copy of the token, as it will be displayed only once. 72 | 73 | * **A Synology administrative account is required** - Synology TLS uses an administrative account to deploy the certificates automatically. It is recommended to set up a dedicated account for the renewal of certificates only. You can add a user in DSM via `Control Panel ➡ User ➡ Create`. Make sure the account is a member of the Administrators group. You can set all shared-folder permissions to 'No access' and all application permissions to 'Deny'. 74 | 75 | 76 | ## Testing 77 | It is recommended to test the services locally before deploying them to your NAS. Running the service with `docker-compose` greatly simplifies validating everything is working as expected. Below four steps will allow you to run the services on your local machine and validate it is working correctly. 78 | 79 | ### Step 1 - Clone the Repository 80 | The first step is to clone the repository to a local folder. Assuming you are in the working folder of your choice, clone the repository files. Git automatically creates a new folder `synology-tls` and copies the files to this directory. The option `--recurse-submodules` ensures the embedded submodules are fetched too. Now change your working folder to be prepared for the next steps. 81 | 82 | ```console 83 | git clone --recurse-submodules https://github.com/markdumay/synology-tls.git 84 | cd synology-tls 85 | ``` 86 | 87 | ### Step 2 - Create Docker Secrets 88 | As Docker-compose does not support external Swarm secrets, we will create local secret files for testing purposes. The credentials are stored as plain text, so this is not recommended for production. Replace the values with your CloudFlare (`CF_` prefix) and Synology (`SYNO_`) credentials accordingly (see prerequisites). 89 | 90 | ```console 91 | mkdir secrets 92 | printf mail@example.com > secrets/CF_Email.txt 93 | printf password > secrets/CF_Token.txt 94 | printf admin > secrets/SYNO_Username.txt 95 | printf password > secrets/SYNO_Password.txt 96 | ``` 97 | 98 | ### Step 3 - Update the Environment Variables 99 | The `docker-compose.yml` file uses environment variables to simplify the configuration. You can use the sample file in the repository as a starting point. 100 | 101 | ```console 102 | mv sample.env .env 103 | ``` 104 | 105 | The `.env` file specifies eight variables. Adjust them as needed: 106 | 107 | 108 | | Variable | Default | Description | 109 | |-----------------------|---------------|-------------| 110 | | **CRON_SCHEDULE** |`0 2 * * *` |Defines the schedule for automated renewal and deployment of the certificates. The job also updates the acme.sh script. [Crontab guru][crontab_guru] is an excellent help for defining cron schedules. The default value `0 2 * * *` validates the certificates at 2 am daily.| 111 | | **DOMAIN** |`example.com` |Replace this with your domain name (e.g. `example.com`).| 112 | | **TARGET** |`staging` |Options are `staging` or `production`. Use `staging` for testing purposes to avoid hitting rate limits from Let's Encrypt.| 113 | | **FORCE_RENEW** |`false` |If `true`, forces renewal of the certificates regardless of whether they are still valid. The default value is `false`.| 114 | | **DEPLOY_HOOK** |`synology_dsm` |The `acme.sh` script supports up to 20 different deployment hooks. Synology TLS defaults to `synology_dsm`. Refer to the [wiki][acmesh_deploy] to see the notes on supporting two-factor authentication for your Synology account.| 115 | | **SYNO_Certificate** | |Defines the description to be shown in DSM's `Control Panel ➡ Security ➡ Certificate`.| 116 | | **SYNO_Hostname** |`localhost` |Refers to the local network address of your Synology NAS. Replace this with the local IP address of your NAS. You can find the address in DSM at `Control Panel ➡ Info Center ➡ Network` under `LAN 1`.| 117 | | **SYNO_Port** |`5000` |Captures the HTTP port DSM is listening on, the default value is `5000`. You can find the current value in DSM under `Control Panel ➡ Network ➡ DSM Settings`.| 118 | 119 | 120 | 121 | ### Step 4 - Run Docker Service 122 | Test the Docker service with `docker-compose up`. 123 | 124 | ```console 125 | docker-compose up 126 | ``` 127 | 128 | After pulling the image from the Docker Hub, you should see several messages. Below excerpt shows the key messages per section. 129 | 130 | #### Booting of the Service 131 | During boot, Synology TLS replaces the cronjob of acme.sh with a custom job following the schedule in `.env`. The cronjob writes a log file to `'/var/log/acme.log'`. 132 | ``` 133 | acme_1 | Removing cron job 134 | acme_1 | LE_WORKING_DIR='/root/.acme.sh' 135 | acme_1 | Using stage ACME_DIRECTORY: https://acme-staging-v02.api.letsencrypt.org/directory 136 | acme_1 | Adding custom cron job at '* 2 * * *' 137 | acme_1 | View the cron log in '/var/log/acme.log' 138 | ``` 139 | 140 | #### Updating of acme.sh 141 | The acme.sh script is updated regularly. The latest version is downloaded during booting and will be refreshed by cron too. 142 | ``` 143 | acme_1 | Installing from online archive. 144 | acme_1 | Installed to /root/.acme.sh/acme.sh 145 | acme_1 | Upgrade success! 146 | ``` 147 | 148 | #### Conducting DNS-01 Check 149 | Synology TLS uses a DNS-01 Challenge so Let's Encrypt can validate ownership of your domain. This setup prevents having to expose your NAS to the public internet. The DNS configuration is automated using CloudFlare. By default, Synology TLS requests the main certificate and a wildcard certificate for your domain. 150 | ``` 151 | acme_1 | Create account key ok. 152 | acme_1 | Multi domain='DNS:example.com,DNS:*.example.com' 153 | acme_1 | Let's check each dns records now. Sleep 20 seconds first. 154 | acme_1 | Verifying: example.com 155 | acme_1 | Success 156 | acme_1 | Verifying: *.example.com 157 | acme_1 | Success 158 | ``` 159 | 160 | #### Downloading Let's Encrypt Certificates 161 | With the DNS-01 challenge passed, Synology TLS then downloads the certificates. The certificates and keys are stored in the mounted folder `data/acme/example.com`. 162 | ``` 163 | acme_1 | Download cert, Le_LinkCert: https://acme-staging-v02.api.letsencrypt.org/acme/cert/xxx 164 | acme_1 | Cert success. 165 | acme_1 | Your cert is in /acme.sh/example.com/example.com.cer 166 | ``` 167 | 168 | #### Deploying the Certificates to your NAS 169 | As a final step, the certificates are automatically deployed to your Synology NAS. 170 | ``` 171 | acme_1 | Logging into localhost:5000 172 | acme_1 | Upload certificate to the Synology DSM 173 | acme_1 | Success 174 | ``` 175 | 176 | By default, Synology TLS runs in the background as Daemon and validates the certificates in a daily cron job. The cron log can be viewed with the following command: 177 | 178 | ``` 179 | docker exec synology-tls_acme_1 /bin/sh -c 'cat /var/log/acme.log' 180 | ``` 181 | 182 | 183 | ## Deployment 184 | The steps for deploying in production are slightly different than for local testing. Below four steps highlight the changes compared to the testing walkthrough. 185 | 186 | 187 | ### Step 1 - Clone the Repository 188 | *Unchanged* 189 | 190 | 191 | ### Step 2 - Create Docker Secrets 192 | Instead of file-based secrets, you will now create secure secrets. Docker secrets can be easily created using pipes. Do not forget to include the final `-`, as this instructs Docker to use piped input. Update the credentials as needed. 193 | 194 | ```console 195 | printf mail@example.com | docker secret create CF_Email - 196 | printf password | docker secret create CF_Token - 197 | printf admin | docker secret create SYNO_Username - 198 | printf password | docker secret create SYNO_Password - 199 | ``` 200 | 201 | If you do not feel comfortable copying secrets from your command line, you can use the wrapper `create_secret.sh`. This script prompts for a secret and ensures sensitive data is not displayed in your console. The script is available in the folder `/docker-secret` of your repository. 202 | 203 | ```console 204 | ./create_secret.sh CF_Email 205 | ./create_secret.sh CF_Token 206 | ./create_secret.sh SYNO_Username 207 | ./create_secret.sh SYNO_Password 208 | ``` 209 | 210 | The `docker-compose.yml` in the repository defaults to set up for local testing. Update the `secrets` section to use Docker secrets instead of local files. 211 | 212 | ```Dockerfile 213 | secrets: 214 | CF_Email: 215 | external: true 216 | CF_Token: 217 | external: true 218 | SYNO_Username: 219 | external: true 220 | SYNO_Password: 221 | external: true 222 | ``` 223 | 224 | ### Step 3 - Update the Environment Variables 225 | *Unchanged, however, set TARGET to production once everything is working properly* 226 | 227 | 228 | ### Step 4 - Run Docker Service 229 | The Docker service will deployed to a Docker Stack in production. Unlike Docker Compose, Docker Stack does not automatically create local folders. Create an empty folder for the `acme.sh` data and log data. Next, deploy the Docker Stack using `docker-compose` as input. This ensures the environment variables are parsed correctly. 230 | 231 | ```console 232 | mkdir -p data/acme 233 | mkdir -p data/log 234 | docker-compose config | docker stack deploy -c - synology-tls 235 | ``` 236 | 237 | Run the following command to inspect the status of the Docker Stack. 238 | 239 | ```console 240 | docker stack services synology-tls 241 | ``` 242 | 243 | You should see the value `1/1` for `REPLICAS` for the Synology TLS service if the stack was initialized correctly. It might take a while before the services are up and running, so simply repeat the command after a few minutes if needed. 244 | 245 | ``` 246 | ID NAME MODE REPLICAS IMAGE PORTS 247 | *** synology-tls_acme replicated 1/1 markdumay/synology-tls:2.8.6 248 | ``` 249 | 250 | You can view the service log with `docker service logs synology-tls_acme` once the service is up and running. Refer to the paragraph Step 4 - Run with Docker Compose for validation of the logs. 251 | 252 | Debugging swarm services can be quite challenging. If for some reason your service does not initiate properly, you can get its task ID with `docker service ps synology-tls_acme`. Running `docker inspect ` might give you some clues to what is happening. Use `docker stack rm synology-tls` to remove the docker stack entirely. 253 | 254 | 255 | ## Usage 256 | The installed certificate can be viewed in DSM by navigating to `Control Panel ➡ Security ➡ Certificate`. Click on the button `Configure` to assign the certificate to your services. 257 | 258 | 259 | ## Contributing 260 | 1. Clone the repository and create a new branch 261 | ``` 262 | $ git checkout https://github.com/markdumay/synology-tls.git -b name_for_new_branch 263 | ``` 264 | 2. Make and test the changes 265 | 3. Submit a Pull Request with a comprehensive description of the changes 266 | 267 | ## Credits 268 | Synology TLS is inspired by the following code repositories and blog articles: 269 | * Neilpang - owner and maintainer of [acme.sh][acmesh_url] 270 | * Markus Lippert - [Automatically renew Let's Encrypt certificates on Synology NAS using DNS-01 challenge][markus_renew] 271 | * xFelix - [Synology Letsencrypt DNS-01 cert issue and install][xfelix_letsencrypt] 272 | * Luka Manestar - [Let's Encrypt + Docker = wildcard certs][luka_wildcard] 273 | 274 | 275 | ## Donate 276 | Buy Me A Coffee 277 | 278 | ## License 279 | 280 | 281 | 282 | 283 | Copyright © [Mark Dumay][blog] 284 | 285 | 286 | 287 | 288 | [acmesh_deploy]: https://github.com/acmesh-official/acme.sh/wiki/deployhooks 289 | [acmesh_url]: https://acme.sh 290 | [crontab_guru]: https://crontab.guru 291 | [docker_url]: https://docker.com 292 | [luka_wildcard]: https://www.blackvoid.club/lets-encrypt-docker-wild-card-certs/ 293 | [markus_renew]: https://lippertmarkus.com/2020/03/14/synology-le-dns-auto-renew/ 294 | [synology_url]: https://www.synology.com 295 | [swarm_init]: https://docs.docker.com/engine/reference/commandline/swarm_init/ 296 | [xfelix_letsencrypt]: https://www.xfelix.com/2017/06/synology-letsencrypt-dns-01-cert-issue-and-install/ 297 | 298 | 299 | 302 | [blog]: https://github.com/markdumay 303 | [repository]: https://github.com/markdumay/synology-tls.git 304 | [synology_docker]: https://github.com/markdumay/synology-docker.git 305 | -------------------------------------------------------------------------------- /acmelog: -------------------------------------------------------------------------------- 1 | /var/log/acme.log 2 | { 3 | daily 4 | maxsize 5M 5 | rotate 4 6 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | networks: 4 | dns: 5 | 6 | secrets: 7 | CF_Email: 8 | file: secrets/CF_Email.txt 9 | # external: true 10 | CF_Token: 11 | file: secrets/CF_Token.txt 12 | # external: true 13 | SYNO_Username: 14 | file: secrets/SYNO_Username.txt 15 | # external: true 16 | SYNO_Password: 17 | file: secrets/SYNO_Password.txt 18 | # external: true 19 | 20 | services: 21 | acme: 22 | image: "markdumay/synology-tls:2.8.6" 23 | # Override Synology's default 'db' driver to ensure logs are visible 24 | logging: 25 | driver: "json-file" 26 | options: 27 | max-size: "200k" 28 | max-file: "10" 29 | networks: 30 | dns: 31 | volumes: 32 | # make ACME data persistent (optional) 33 | - ./data/acme:/acme.sh 34 | # make log data persistent to simplify monitoring (optional) 35 | - ./data/log:/var/log 36 | deploy: 37 | restart_policy: 38 | condition: on-failure 39 | delay: 5s 40 | max_attempts: 3 41 | window: 120s 42 | secrets: 43 | - CF_Email 44 | - CF_Token 45 | - SYNO_Username 46 | - SYNO_Password 47 | environment: 48 | # Set to core domain in .env (e.g. example.com), wildcard is added automatically 49 | - DOMAIN=${DOMAIN} 50 | # Set to 'production' in .env to request real certificate, defaults to staging (appends '--test' parameter) 51 | - TARGET=${TARGET} 52 | # Set to 'true' in .env to force renew a cert immediately (appends '--force' parameter) 53 | - FORCE_RENEW=${FORCE_RENEW} 54 | # Deployment settings (using Synology hook) 55 | - DEPLOY_HOOK=${DEPLOY_HOOK} 56 | - SYNO_Certificate=${SYNO_Certificate} 57 | - SYNO_Create=1 58 | - SYNO_Hostname=${SYNO_Hostname} 59 | - SYNO_Port=${SYNO_Port} 60 | # Notification settings (using Slack hook) 61 | - NOTIFY_HOOK=${NOTIFY_HOOK} 62 | - NOTIFY_LEVEL=${NOTIFY_LEVEL} 63 | - SLACK_CHANNEL=${SLACK_CHANNEL} 64 | - SLACK_USERNAME=${SLACK_USERNAME} 65 | # Cron schedule 66 | - CRON_SCHEDULE=${CRON_SCHEDULE} 67 | # Run acme.sh in Daemon mode 68 | command: daemon -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Set default values for flags and variables 4 | TEST_FLAG='--test' 5 | RENEW_FLAG='' 6 | NOTIFY_FLAG='' 7 | DEPLOY_CMD='' 8 | if [ -z "$TARGET" ]; then TARGET='staging'; fi 9 | if [ -z "$FORCE_RENEW" ]; then FORCE_RENEW='false'; fi 10 | if [ -z "$NOTIFY_LEVEL" ]; then NOTIFY_LEVEL='2'; fi 11 | 12 | # Convert selected environment variables to lower case 13 | TARGET=$(echo "$TARGET" | tr -s '[:upper:]' '[:lower:]') 14 | FORCE_RENEW=$(echo "$FORCE_RENEW" | tr -s '[:upper:]' '[:lower:]') 15 | 16 | # Update flags based on environment variables 17 | if [ "$TARGET" = 'production' ] ; then TEST_FLAG=''; fi; 18 | if [ "$FORCE_RENEW" = 'true' ] ; then RENEW_FLAG='--force'; fi; 19 | if [ ! -z "$NOTIFY_HOOK" ] ; then 20 | NOTIFY_FLAG="--set-notify --notify-level $NOTIFY_LEVEL --notify-hook $NOTIFY_HOOK"; 21 | fi; 22 | if [ ! -z "$DEPLOY_HOOK" ] ; then 23 | DEPLOY_CMD="acme.sh -d '$DOMAIN' --deploy --deploy-hook $DEPLOY_HOOK"; 24 | else 25 | DEPLOY_CMD="echo 'Please specify a deploy_hook in your environment'"; 26 | fi; 27 | 28 | # Generate a script to issue / renew certificates 29 | printf "%b" '#!'"/usr/bin/env sh\n \ 30 | . /usr/local/bin/stage-env.sh 31 | acme.sh --issue -d '$DOMAIN' -d '*.$DOMAIN' --dns dns_cf $TEST_FLAG $RENEW_FLAG --server letsencrypt 32 | " >/usr/local/bin/issue.sh && chmod +x /usr/local/bin/issue.sh 33 | 34 | # Generate a script to deploy certificates 35 | printf "%b" '#!'"/usr/bin/env sh\n \ 36 | . /usr/local/bin/stage-env.sh 37 | $DEPLOY_CMD 38 | " >/usr/local/bin/deploy.sh && chmod +x /usr/local/bin/deploy.sh 39 | 40 | # Replace default cronjob when the schedule is specified 41 | if [ ! -z "$CRON_SCHEDULE" ] ; then 42 | # Generate a cron job script (running acme.sh upgrade, issue.sh, and deploy.sh) 43 | printf "%b" '#!'"/usr/bin/env sh\n \ 44 | acme.sh --upgrade && issue.sh && deploy.sh 45 | " >/usr/local/bin/cron.sh && chmod +x /usr/local/bin/cron.sh 46 | 47 | # Remove default cronjob 48 | acme.sh --uninstall-cronjob 49 | 50 | # Install custom cronjob if not added already 51 | echo "[$(date -u)] Adding custom cron job at '$CRON_SCHEDULE'" 52 | echo "[$(date -u)] View the cron log in '/var/log/acme.log'" 53 | ! (crontab -l | grep -q "cron.sh") && (crontab -l; echo "$CRON_SCHEDULE cron.sh >> /var/log/acme.log 2>&1") | crontab - 54 | 55 | # Run cronjob during start-up to immediately see the outcomes in the service log 56 | cron.sh 57 | fi; 58 | 59 | # Call parent's entry script in current script context 60 | . /entry.sh -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | CRON_SCHEDULE=0 2 * * * 2 | DOMAIN=example.com 3 | TARGET=staging 4 | FORCE_RENEW=false 5 | DEPLOY_HOOK=synology_dsm 6 | SYNO_Certificate=description text shown in Control Panel ➡ Security ➡ Certificate 7 | SYNO_Hostname=localhost 8 | SYNO_Port=5000 -------------------------------------------------------------------------------- /stage-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Export Docker secrets as environment variables without displaying any errors / messages 4 | export $(grep -vH --null '^#' /run/secrets/* | tr '\0' '=' | sed 's/^\/run\/secrets\///g') --------------------------------------------------------------------------------