├── .github └── workflows │ └── release.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── alicdn.sh ├── docker-compose.yml └── entry.sh /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker Image 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | env: 8 | IMAGE_NAME: ${{ github.event.repository.name }} 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | packages: write 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Set up QEMU 20 | uses: docker/setup-qemu-action@v1 21 | 22 | - name: Set up Docker Buildx 23 | uses: docker/setup-buildx-action@v3 24 | 25 | - name: Login to DockerHub 26 | uses: docker/login-action@v3 27 | with: 28 | username: ${{ secrets.DOCKER_USERNAME }} 29 | password: ${{ secrets.DOCKER_TOKEN }} 30 | 31 | - name: Login to ghcr.io 32 | uses: docker/login-action@v3 33 | with: 34 | registry: ghcr.io 35 | username: ${{ github.repository_owner }} 36 | password: ${{ secrets.GITHUB_TOKEN }} 37 | 38 | - name: Build and push to DockerHub 39 | uses: docker/build-push-action@v5 40 | with: 41 | context: . 42 | file: ./Dockerfile 43 | platforms: linux/amd64,linux/arm64 44 | push: true 45 | tags: | 46 | ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest 47 | ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }} 48 | ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest 49 | ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }} 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | /acme -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM neilpang/acme.sh 2 | 3 | # install aliyun-cli 4 | # see: 5 | # [1] https://www.alibabacloud.com/help/en/alibaba-cloud-cli/latest/run-alibaba-cloud-cli-in-a-docker-container 6 | # [2] https://github.com/aliyun/aliyun-cli 7 | RUN apk add --no-cache bash \ 8 | && ln -s /bin/uname /usr/bin/uname \ 9 | && /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/aliyun/aliyun-cli/HEAD/install.sh)" \ 10 | && mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 11 | 12 | # Overwrite the entry.sh 13 | COPY entry.sh /entry.sh 14 | 15 | # Add aliyun.sh 16 | COPY alicdn.sh /root/.acme.sh/deploy/alicdn.sh 17 | 18 | ENTRYPOINT ["/entry.sh"] 19 | CMD ["daemon"] 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # acme-bot 2 | 3 | 一个运行于 Docker 容器中的,基于 acme.sh 的 SSL 证书自动更新和部署机器人。 4 | 5 | ## 为什么需要 acme-bot? 6 | 7 | [acme.sh](https://github.com/acmesh-official/acme.sh) 是一个非常优秀的 ACME 协议客户端,它支持多种 DNS API 和多种 Web 服务器,可以自动申请和更新 SSL 证书。但是,acme.sh 虽然提供了官方的 Docker 镜像,但是此镜像并不能做到基于配置信息自动更新证书和部署证书。 8 | 9 | acme-bot 是一个基于 acme.sh 官方 Docker 镜像的二次封装,它可以做到通过环境变量配置 acme.sh,告诉 acme.sh 你的域名和 DNS API 的信息,acme-bot 会自动申请和更新证书,并且可以自动部署证书到你指定的服务中。因此它特别适合运行在 Docker 容器中,作为一个独立的证书管理机器人。 10 | 11 | ## 如何使用 acme-bot? 12 | 13 | acme-bot 的使用非常简单,只需要几个环境变量即可。下面是一个使用 acme-bot 的例子: 14 | 15 | ```bash 16 | docker run -d \ 17 | --name acme-bot \ 18 | -e EMAIL="hello@example.com" \ 19 | -e DOMAINS="dns_ali:example.com" \ 20 | -e Ali_Key="your_ali_key" \ 21 | -e Ali_Secret="your_ali_secret" \ 22 | -v $PWD/acme:/acme.sh \ 23 | joyqi/acme-bot 24 | ``` 25 | 26 | 你也可以使用 ghcr.io 镜像 `ghcr.io/joyqi/acme-bot`。 27 | 28 | 我同样提供了一个 [docker-compose.yml](./docker-compose.yml) 文件,你可以直接使用 `docker-compose up -d` 来启动 acme-bot。当然,你需要修改 `docker-compose.yml` 文件中的环境变量。 29 | 30 | ## 环境变量 31 | 32 | acme-bot 支持以下环境变量: 33 | 34 | - `EMAIL`:(必须)你的邮箱地址,用于注册 acme.sh 账号。 35 | - `DOMAINS`: (必须)你的域名和 DNS API 的信息,格式为 `dns_api:domain1,*.domain1,...[/deploy_hook]`。其中 `dns_api` 是你的 DNS API 的名称,具体支持的列表请参考 [acme.sh 的文档](https://github.com/acmesh-official/acme.sh/wiki/dnsapi)。`domain1,*.domain1,...` 是你的域名和泛域名列表,用逗号分隔。`/deploy_hook` 是可选的,用于指定证书在发布/更新后的部署方法,具体支持的列表请参考 [acme.sh 的文档](https://github.com/acmesh-official/acme.sh/wiki/deployhooks)。 36 | - `CA`: (可选)acme.sh 的 ACME 服务器,默认为 zerossl,你可以指定其它的 ACME 服务器,具体支持的列表请参考 [acme.sh 的文档](https://github.com/acmesh-official/acme.sh/wiki/Server)。 37 | - `NOTIFY`: (可选)通知方式 `notify-hook`,你可以指定各种通知方式,具体支持的列表请参考 [acme.sh 的文档](https://github.com/acmesh-official/acme.sh/wiki/notify)。多个 `notify-hook` 用逗号分隔。 38 | 39 | ⚠️注意:以上配置信息中的 `dns_api`、`deploy_hook` 和 `notify-hook` 指定后都需要配置对应的环境变量,具体的环境变量请参考 [acme.sh 的文档](https://github.com/acmesh-official/acme.sh/wiki)。 40 | 41 | ## 关于 `alicdn` 42 | 43 | 由于 acme.sh 一直没有处理[关于阿里云 CDN 的 PR](https://github.com/acmesh-official/acme.sh/pull/3375),导致 acme.sh 无法自动部署证书到阿里云 CDN。因此,acme-bot 参考原 PR 提供了一个 `alicdn` 的部署钩子,用于自动部署证书到阿里云 CDN。 44 | 45 | 一般情况下如果你使用了 `dns_ali` 作为 DNS API,那么 `alicdn` 会直接使用 `Ali_Key` 和 `Ali_Secret` 作为阿里云 CDN 的密钥。如果你使用了其它的 DNS API,那么你需要额外配置 `ALI_CDN_KEY` 和 `ALI_CDN_SECRET` 作为阿里云 CDN 的密钥。 46 | 47 | 如果生成的证书包含多个子域名和泛域名,那么你可能需要通过设置 `ALI_CDN_DOMAIN` 来指定 CDN 的域名。 -------------------------------------------------------------------------------- /alicdn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # See: https://github.com/acmesh-official/acme.sh/pull/3375 4 | # Script to create certificate to Aliyun CDN 5 | # 6 | # This deployment required following variables 7 | # export ALI_CDN_KEY="LTqIA87hOKdjevsf5" 8 | # export ALI_CDN_SECRET="0p5EYueFNq501xnCPzKNbx6K51qPH2" 9 | # export ALI_CDN_DOMAIN="cdn.example.com" 10 | # If you have more than one domain, just 11 | # export ALI_CDN_DOMAIN="cdn1.example.com,cdn2.example.com" 12 | # 13 | # If ALI_CDN_KEY and ALI_CDN_SECRET are not set, 14 | # Ali_key and Ali_Secret will be used. (see dns/dns_ali.sh) 15 | # 16 | # AliYun Authentication must have "AliyunCDNFullAccess" permission, 17 | # May also need to "AliyunYundunCertFullAccess" permissions. 18 | # 19 | # Thanks: 20 | # This script references dns/dns_ali.sh and pull request #2772 21 | 22 | ######## Public functions ##################### 23 | ALI_CDN_API="https://cdn.aliyuncs.com/" 24 | 25 | alicdn_deploy() { 26 | _cdomain="$1" 27 | _ckey="$2" 28 | _ccert="$3" 29 | _cca="$4" 30 | _cfullchain="$5" 31 | 32 | _debug _cdomain "$_cdomain" 33 | _debug _ckey "$_ckey" 34 | _debug _ccert "$_ccert" 35 | _debug _cca "$_cca" 36 | _debug _cfullchain "$_cfullchain" 37 | 38 | _info "$(__green "===Starting alicdn deploy===")" 39 | 40 | _getdeployconf ALI_CDN_KEY 41 | _getdeployconf ALI_CDN_SECRET 42 | _getdeployconf ALI_CDN_DOMAIN 43 | 44 | if [ -z "${ALI_CDN_KEY}" ] || [ -z "${ALI_CDN_SECRET}" ]; then 45 | _info "Not set variables ALI_CDN_KEY and ALI_CDN_SECRET" 46 | _info "Will use Ali_Key and Ali_Secret" 47 | ALI_CDN_KEY="$(_readaccountconf_mutable Ali_Key)" 48 | ALI_CDN_SECRET="$(_readaccountconf_mutable Ali_Secret)" 49 | if [ -z "${ALI_CDN_KEY}" ] || [ -z "${ALI_CDN_SECRET}" ]; then 50 | _err "You don't specify aliyun api key and secret yet." 51 | return 1 52 | fi 53 | else 54 | #save ALI_CDN_KEY and ALI_CDN_SECRET. 55 | _savedeployconf ALI_CDN_KEY "$ALI_CDN_KEY" 56 | _savedeployconf ALI_CDN_SECRET "$ALI_CDN_SECRET" 57 | fi 58 | 59 | if [ -z "${ALI_CDN_DOMAIN}" ]; then 60 | ALI_CDN_DOMAIN="$_cdomain" 61 | else 62 | _savedeployconf ALI_CDN_DOMAIN "$ALI_CDN_DOMAIN" 63 | fi 64 | 65 | _debug ALI_CDN_KEY "${ALI_CDN_KEY}" 66 | _debug ALI_CDN_SECRET "$ALI_CDN_SECRET" 67 | _debug ALI_CDN_DOMAIN "$ALI_CDN_DOMAIN" 68 | 69 | ## upload certificate 70 | _Ali_SSLPub=$(grep -Ev '^$' "$_cfullchain" | _ali_url_encode) 71 | _Ali_SSLPri=$(_ali_url_encode <"$_ckey") 72 | 73 | query='' 74 | query=$query'AccessKeyId='${ALI_CDN_KEY} 75 | query=$query'&Action=BatchSetCdnDomainServerCertificate' 76 | query=$query'&CertName='$(_ali_urlencode "$_cdomain") 77 | query=$query'&CertType=upload' 78 | query=$query'&DomainName='$(_ali_urlencode "$ALI_CDN_DOMAIN") 79 | query=$query'&ForceSet=1' 80 | query=$query'&Format=json' 81 | query=$query'&SSLPri='${_Ali_SSLPri} 82 | query=$query'&SSLProtocol=on' 83 | query=$query'&SSLPub='${_Ali_SSLPub} 84 | query=$query'&SignatureMethod=HMAC-SHA1' 85 | query=$query'&SignatureNonce='$(_ali_nonce) 86 | query=$query'&SignatureVersion=1.0' 87 | query=$query'&Timestamp='$(_timestamp) 88 | query=$query'&Version=2018-05-10' 89 | _debug2 signature_source "$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")")" 90 | signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$ALI_CDN_SECRET&" | _hex_dump | tr -d " ")" | _base64) 91 | signature=$(_ali_urlencode "$signature") 92 | url="$ALI_CDN_API?$query&Signature=$signature" 93 | 94 | if ! response="$(_get "$url")"; then 95 | _err "Error <$1>" 96 | return 1 97 | fi 98 | _debug response "$response" 99 | message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2- | tr -d \")" 100 | if [ "$message" ]; then 101 | _err "$message" 102 | return 1 103 | fi 104 | _info "Domain $_cdomain certificate has been deployed successfully" 105 | _info "$(__green "===End alicdn deploy===")" 106 | return 0 107 | } 108 | 109 | #################### Private functions below ################################## 110 | _ali_url_encode() { 111 | _hex_str=$(_hex_dump) 112 | _debug3 "_url_encode" 113 | _debug3 "_hex_str" "$_hex_str" 114 | for _hex_code in $_hex_str; do 115 | #upper case 116 | case "${_hex_code}" in 117 | "41") 118 | printf "%s" "A" 119 | ;; 120 | "42") 121 | printf "%s" "B" 122 | ;; 123 | "43") 124 | printf "%s" "C" 125 | ;; 126 | "44") 127 | printf "%s" "D" 128 | ;; 129 | "45") 130 | printf "%s" "E" 131 | ;; 132 | "46") 133 | printf "%s" "F" 134 | ;; 135 | "47") 136 | printf "%s" "G" 137 | ;; 138 | "48") 139 | printf "%s" "H" 140 | ;; 141 | "49") 142 | printf "%s" "I" 143 | ;; 144 | "4a") 145 | printf "%s" "J" 146 | ;; 147 | "4b") 148 | printf "%s" "K" 149 | ;; 150 | "4c") 151 | printf "%s" "L" 152 | ;; 153 | "4d") 154 | printf "%s" "M" 155 | ;; 156 | "4e") 157 | printf "%s" "N" 158 | ;; 159 | "4f") 160 | printf "%s" "O" 161 | ;; 162 | "50") 163 | printf "%s" "P" 164 | ;; 165 | "51") 166 | printf "%s" "Q" 167 | ;; 168 | "52") 169 | printf "%s" "R" 170 | ;; 171 | "53") 172 | printf "%s" "S" 173 | ;; 174 | "54") 175 | printf "%s" "T" 176 | ;; 177 | "55") 178 | printf "%s" "U" 179 | ;; 180 | "56") 181 | printf "%s" "V" 182 | ;; 183 | "57") 184 | printf "%s" "W" 185 | ;; 186 | "58") 187 | printf "%s" "X" 188 | ;; 189 | "59") 190 | printf "%s" "Y" 191 | ;; 192 | "5a") 193 | printf "%s" "Z" 194 | ;; 195 | 196 | #lower case 197 | "61") 198 | printf "%s" "a" 199 | ;; 200 | "62") 201 | printf "%s" "b" 202 | ;; 203 | "63") 204 | printf "%s" "c" 205 | ;; 206 | "64") 207 | printf "%s" "d" 208 | ;; 209 | "65") 210 | printf "%s" "e" 211 | ;; 212 | "66") 213 | printf "%s" "f" 214 | ;; 215 | "67") 216 | printf "%s" "g" 217 | ;; 218 | "68") 219 | printf "%s" "h" 220 | ;; 221 | "69") 222 | printf "%s" "i" 223 | ;; 224 | "6a") 225 | printf "%s" "j" 226 | ;; 227 | "6b") 228 | printf "%s" "k" 229 | ;; 230 | "6c") 231 | printf "%s" "l" 232 | ;; 233 | "6d") 234 | printf "%s" "m" 235 | ;; 236 | "6e") 237 | printf "%s" "n" 238 | ;; 239 | "6f") 240 | printf "%s" "o" 241 | ;; 242 | "70") 243 | printf "%s" "p" 244 | ;; 245 | "71") 246 | printf "%s" "q" 247 | ;; 248 | "72") 249 | printf "%s" "r" 250 | ;; 251 | "73") 252 | printf "%s" "s" 253 | ;; 254 | "74") 255 | printf "%s" "t" 256 | ;; 257 | "75") 258 | printf "%s" "u" 259 | ;; 260 | "76") 261 | printf "%s" "v" 262 | ;; 263 | "77") 264 | printf "%s" "w" 265 | ;; 266 | "78") 267 | printf "%s" "x" 268 | ;; 269 | "79") 270 | printf "%s" "y" 271 | ;; 272 | "7a") 273 | printf "%s" "z" 274 | ;; 275 | #numbers 276 | "30") 277 | printf "%s" "0" 278 | ;; 279 | "31") 280 | printf "%s" "1" 281 | ;; 282 | "32") 283 | printf "%s" "2" 284 | ;; 285 | "33") 286 | printf "%s" "3" 287 | ;; 288 | "34") 289 | printf "%s" "4" 290 | ;; 291 | "35") 292 | printf "%s" "5" 293 | ;; 294 | "36") 295 | printf "%s" "6" 296 | ;; 297 | "37") 298 | printf "%s" "7" 299 | ;; 300 | "38") 301 | printf "%s" "8" 302 | ;; 303 | "39") 304 | printf "%s" "9" 305 | ;; 306 | "2d") 307 | printf "%s" "-" 308 | ;; 309 | "5f") 310 | printf "%s" "_" 311 | ;; 312 | "2e") 313 | printf "%s" "." 314 | ;; 315 | "7e") 316 | printf "%s" "~" 317 | ;; 318 | #other hex 319 | *) 320 | printf '%%%s' "$_hex_code" | tr '[:lower:]' '[:upper:]' 321 | ;; 322 | esac 323 | done 324 | } 325 | 326 | _ali_urlencode() { 327 | _str=$(printf "%s" "$1" | _ali_url_encode) 328 | printf "%s" "$_str" 329 | } 330 | 331 | _ali_nonce() { 332 | date +"%s%N" 333 | } 334 | 335 | _timestamp() { 336 | date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ" 337 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | 3 | services: 4 | acme: 5 | image: joyqi/acme-bot@latest 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | restart: always 10 | environment: 11 | - EMAIL=hello@example.com 12 | - DOMAINS=dns_ali:example.com 13 | # You can use env_file to load environment variables from a file 14 | #env_file: 15 | # - .env 16 | volumes: 17 | - ${PWD}/acme:/acme.sh -------------------------------------------------------------------------------- /entry.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Load domains from environment variable 4 | # Format: ISSUE_METHOD:DOMAIN1,DOMAIN2/DEPLOY_HOOK;ISSUE_METHOD:DOMAIN3,DOMAIN4/DEPLOY_HOOK 5 | # Issue command: --issue --dns $ISSUE_METHOD -d $DOMAIN1 -d $DOMAIN2 6 | init_domains() { 7 | # Check if the environment variable is set 8 | if [ -z "$DOMAINS" ]; then 9 | return 0 10 | fi 11 | 12 | local domains 13 | domains=$(echo "$DOMAINS" | tr ';' '\n') 14 | for domain in $domains; do 15 | local first_part=$(echo "$domain" | cut -d '/' -f 1) 16 | local last_part=$(echo "$domain" | awk -F"/" '{print (NF>1)?$2:""}') 17 | local issue_method=$(echo "$first_part" | cut -d ':' -f 1) 18 | local domain_list=$(echo "$first_part" | cut -d ':' -f 2) 19 | local domain_args=$(expand_to_args "$domain_list" "-d") 20 | 21 | echo "Issue certificate for $domain_list using $issue_method" 22 | --issue --dns $issue_method $domain_args `$deploy_hook` 23 | 24 | if [ -n "$last_part" ]; then 25 | --deploy $domain_args --deploy-hook $last_part 26 | fi 27 | done 28 | } 29 | 30 | init_ca() { 31 | if [ -z "$CA" ]; then 32 | return 0 33 | fi 34 | 35 | --set-default-ca --server $CA 36 | } 37 | 38 | init_email() { 39 | if [ -z "$EMAIL" ]; then 40 | echo "Please set the EMAIL environment variable" 41 | exit 1 42 | fi 43 | 44 | --register-account -m $EMAIL 45 | } 46 | 47 | init_notify() { 48 | if [ -z "$NOTIFY" ]; then 49 | return 0 50 | fi 51 | 52 | local notify_hook=$(expand_to_args "$NOTIFY" "--notify-hook") 53 | local notify_level="" 54 | local notify_mode="" 55 | local notify_source="" 56 | 57 | if [ -n "$NOTIFY_LEVEL" ]; then 58 | notify_level="--notify-level $NOTIFY_LEVEL" 59 | fi 60 | 61 | if [ -n "$NOTIFY_MODE" ]; then 62 | notify_mode="--notify-mode $NOTIFY_MODE" 63 | fi 64 | 65 | if [ -n "$NOTIFY_SOURCE" ]; then 66 | notify_source="--notify-source $NOTIFY_SOURCE" 67 | fi 68 | 69 | --set-notify $notify_hook $notify_level $notify_mode $notify_source 70 | } 71 | 72 | expand_to_args() { 73 | local domain_list=$(echo "$1" | tr ',' '\n') 74 | for domain in $domain_list; do 75 | echo " $2 $domain" 76 | done 77 | } 78 | 79 | if [ "$1" = "daemon" ]; then 80 | init_ca 81 | init_email 82 | init_notify 83 | init_domains 84 | exec crond -n -s -m off 85 | else 86 | exec -- "$@" 87 | fi 88 | --------------------------------------------------------------------------------