Unverified Commit cc0be6cd authored by Arnaud Launay's avatar Arnaud Launay Committed by GitHub
Browse files

Merge branch 'acmesh-official:dev' into dev

parents b71a088d a7455d7e
# An ACME Shell script: acme.sh # An ACME Shell script: acme.sh
![LetsEncrypt](https://github.com/acmesh-official/acme.sh/workflows/LetsEncrypt/badge.svg) [![FreeBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)
[![OpenBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)
[![NetBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)
[![MacOS](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)
[![Ubuntu](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)
[![Windows](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
[![Solaris](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)
![Shellcheck](https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg) ![Shellcheck](https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg)
![PebbleStrict](https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/badge.svg) ![PebbleStrict](https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/badge.svg)
![DockerHub](https://github.com/acmesh-official/acme.sh/workflows/Build%20DockerHub/badge.svg) ![DockerHub](https://github.com/acmesh-official/acme.sh/workflows/Build%20DockerHub/badge.svg)
...@@ -15,18 +24,18 @@ ...@@ -15,18 +24,18 @@
- An ACME protocol client written purely in Shell (Unix shell) language. - An ACME protocol client written purely in Shell (Unix shell) language.
- Full ACME protocol implementation. - Full ACME protocol implementation.
- Support ACME v1 and ACME v2 - Support ECDSA certs
- Support ACME v2 wildcard certs - Support SAN and wildcard certs
- Simple, powerful and very easy to use. You only need 3 minutes to learn it. - Simple, powerful and very easy to use. You only need 3 minutes to learn it.
- Bash, dash and sh compatible. - Bash, dash and sh compatible.
- Purely written in Shell with no dependencies on python or the official Let's Encrypt client. - Purely written in Shell with no dependencies on python.
- Just one script to issue, renew and install your certificates automatically. - Just one script to issue, renew and install your certificates automatically.
- DOES NOT require `root/sudoer` access. - DOES NOT require `root/sudoer` access.
- Docker friendly - Docker ready
- IPv6 support - IPv6 ready
- Cron job notifications for renewal or error etc. - Cron job notifications for renewal or error etc.
It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates.
Wiki: https://github.com/acmesh-official/acme.sh/wiki Wiki: https://github.com/acmesh-official/acme.sh/wiki
...@@ -42,14 +51,12 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) ...@@ -42,14 +51,12 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
- [ruby-china.org](https://ruby-china.org/topics/31983) - [ruby-china.org](https://ruby-china.org/topics/31983)
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management) - [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89) - [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty) - [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) - [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html) - [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh)
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) - [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
- [CentOS Web Panel](http://centos-webpanel.com/) - [CentOS Web Panel](https://control-webpanel.com)
- [lnmp.org](https://lnmp.org/) - [lnmp.org](https://lnmp.org/)
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials) - [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
...@@ -57,38 +64,44 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) ...@@ -57,38 +64,44 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
| NO | Status| Platform| | NO | Status| Platform|
|----|-------|---------| |----|-------|---------|
|1|[![MacOS](https://github.com/acmesh-official/acme.sh/workflows/LetsEncrypt/badge.svg)](https://github.com/acmesh-official/acme.sh/actions?query=workflow%3ALetsEncrypt)|Mac OSX |1|[![MacOS](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/MacOS.yml)|Mac OSX
|2|[![Windows](https://github.com/acmesh-official/acme.sh/workflows/LetsEncrypt/badge.svg)](https://github.com/acmesh-official/acme.sh/actions?query=workflow%3ALetsEncrypt)|Windows (cygwin with curl, openssl and crontab included) |2|[![Windows](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)|Windows (cygwin with curl, openssl and crontab included)
|3|[![FreeBSD](https://github.com/acmesh-official/acme.sh/workflows/LetsEncrypt/badge.svg)](https://github.com/acmesh-official/acme.sh/actions?query=workflow%3ALetsEncrypt)|FreeBSD |3|[![FreeBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/FreeBSD.yml)|FreeBSD
|4|[![Ubuntu](https://github.com/acmesh-official/acme.sh/workflows/LetsEncrypt/badge.svg)](https://github.com/acmesh-official/acme.sh/actions?query=workflow%3ALetsEncrypt)| Ubuntu |4|[![Solaris](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)|Solaris
|5|[![](https://acmesh-official.github.io/acmetest/status/pfsense.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|pfsense |5|[![Ubuntu](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Ubuntu.yml)| Ubuntu
|6|[![](https://acmesh-official.github.io/acmetest/status/openbsd.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|OpenBSD |6|NA|pfsense
|7|[![](https://acmesh-official.github.io/acmetest/status/solaris.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|SunOS/Solaris |7|[![OpenBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
|8|[![](https://acmesh-official.github.io/acmetest/status/debian-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)| Debian |8|[![NetBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
|9|[![](https://acmesh-official.github.io/acmetest/status/centos-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|CentOS |9|[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
|10|[![](https://acmesh-official.github.io/acmetest/status/opensuse-leap-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|openSUSE |10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
|11|[![](https://acmesh-official.github.io/acmetest/status/alpine-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Alpine Linux (with curl) |11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
|12|[![](https://acmesh-official.github.io/acmetest/status/archlinux-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Archlinux |12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
|13|[![](https://acmesh-official.github.io/acmetest/status/fedora-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|fedora |13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
|14|[![](https://acmesh-official.github.io/acmetest/status/kalilinux-kali.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Kali Linux |14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
|15|[![](https://acmesh-official.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Oracle Linux |15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
|16|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management) |16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
|17|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111 |17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
|18|[![](https://acmesh-official.github.io/acmetest/status/mageia.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Mageia |18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
|19|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT) |19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
|20|[![](https://acmesh-official.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|Gentoo Linux |10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
|21|[![](https://acmesh-official.github.io/acmetest/status/clearlinux-latest.svg)](https://github.com/acmesh-official/acmetest#here-are-the-latest-status)|ClearLinux |11|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
For all build statuses, check our [weekly build project](https://github.com/acmesh-official/acmetest): |23|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
Check our [testing project](https://github.com/acmesh-official/acmetest):
https://github.com/acmesh-official/acmetest https://github.com/acmesh-official/acmetest
# Supported CA # Supported CA
- Letsencrypt.org CA(default) - [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA)(default)
- [ZeroSSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA) - Letsencrypt.org CA
- [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA) - [BuyPass.com CA](https://github.com/acmesh-official/acme.sh/wiki/BuyPass.com-CA)
- [SSL.com CA](https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA)
- [Google.com Public CA](https://github.com/acmesh-official/acme.sh/wiki/Google-Public-CA)
- [Pebble strict Mode](https://github.com/letsencrypt/pebble) - [Pebble strict Mode](https://github.com/letsencrypt/pebble)
- Any other [RFC8555](https://tools.ietf.org/html/rfc8555)-compliant CA
# Supported modes # Supported modes
...@@ -109,13 +122,13 @@ https://github.com/acmesh-official/acmetest ...@@ -109,13 +122,13 @@ https://github.com/acmesh-official/acmetest
Check this project: https://github.com/acmesh-official/get.acme.sh Check this project: https://github.com/acmesh-official/get.acme.sh
```bash ```bash
curl https://get.acme.sh | sh curl https://get.acme.sh | sh -s email=my@example.com
``` ```
Or: Or:
```bash ```bash
wget -O - https://get.acme.sh | sh wget -O - https://get.acme.sh | sh -s email=my@example.com
``` ```
...@@ -126,7 +139,7 @@ Clone this project and launch installation: ...@@ -126,7 +139,7 @@ Clone this project and launch installation:
```bash ```bash
git clone https://github.com/acmesh-official/acme.sh.git git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh cd ./acme.sh
./acme.sh --install ./acme.sh --install -m my@example.com
``` ```
You `don't have to be root` then, although `it is recommended`. You `don't have to be root` then, although `it is recommended`.
...@@ -346,10 +359,6 @@ Ok, it's done. ...@@ -346,10 +359,6 @@ Ok, it's done.
# 10. Issue ECC certificates # 10. Issue ECC certificates
`Let's Encrypt` can now issue **ECDSA** certificates.
And we support them too!
Just set the `keylength` parameter with a prefix `ec-`. Just set the `keylength` parameter with a prefix `ec-`.
For example: For example:
...@@ -370,10 +379,12 @@ Please look at the `keylength` parameter above. ...@@ -370,10 +379,12 @@ Please look at the `keylength` parameter above.
Valid values are: Valid values are:
1. **ec-256 (prime256v1, "ECDSA P-256")** 1. **ec-256 (prime256v1, "ECDSA P-256", which is the default key type)**
2. **ec-384 (secp384r1, "ECDSA P-384")** 2. **ec-384 (secp384r1, "ECDSA P-384")**
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)** 3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
4. **2048 (RSA2048)**
5. **3072 (RSA3072)**
6. **4096 (RSA4096)**
# 11. Issue Wildcard certificates # 11. Issue Wildcard certificates
...@@ -468,7 +479,7 @@ TODO: ...@@ -468,7 +479,7 @@ TODO:
### Code Contributors ### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. This project exists thanks to all the people who contribute.
<a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a> <a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
### Financial Contributors ### Financial Contributors
...@@ -494,6 +505,12 @@ Support this project with your organization. Your logo will show up here with a ...@@ -494,6 +505,12 @@ Support this project with your organization. Your logo will show up here with a
<a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a> <a href="https://opencollective.com/acmesh/organization/8/website"><img src="https://opencollective.com/acmesh/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a> <a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
#### Sponsors
[![quantumca-acmesh-logo](https://user-images.githubusercontent.com/8305679/183255712-634ee1db-bb61-4c03-bca0-bacce99e078c.svg)](https://www.quantumca.com.cn/?__utm_source=acmesh-donation)
# 19. License & Others # 19. License & Others
License is GPLv3 License is GPLv3
......
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.8.8 VER=3.0.7
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
...@@ -20,9 +20,6 @@ _SUB_FOLDER_DEPLOY="deploy" ...@@ -20,9 +20,6 @@ _SUB_FOLDER_DEPLOY="deploy"
_SUB_FOLDERS="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY" _SUB_FOLDERS="$_SUB_FOLDER_DNSAPI $_SUB_FOLDER_DEPLOY $_SUB_FOLDER_NOTIFY"
LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory"
LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory"
CA_LETSENCRYPT_V2="https://acme-v02.api.letsencrypt.org/directory" CA_LETSENCRYPT_V2="https://acme-v02.api.letsencrypt.org/directory"
CA_LETSENCRYPT_V2_TEST="https://acme-staging-v02.api.letsencrypt.org/directory" CA_LETSENCRYPT_V2_TEST="https://acme-staging-v02.api.letsencrypt.org/directory"
...@@ -30,25 +27,34 @@ CA_BUYPASS="https://api.buypass.com/acme/directory" ...@@ -30,25 +27,34 @@ CA_BUYPASS="https://api.buypass.com/acme/directory"
CA_BUYPASS_TEST="https://api.test4.buypass.no/acme/directory" CA_BUYPASS_TEST="https://api.test4.buypass.no/acme/directory"
CA_ZEROSSL="https://acme.zerossl.com/v2/DV90" CA_ZEROSSL="https://acme.zerossl.com/v2/DV90"
_ZERO_EAB_ENDPOINT="http://api.zerossl.com/acme/eab-credentials-email" _ZERO_EAB_ENDPOINT="https://api.zerossl.com/acme/eab-credentials-email"
CA_SSLCOM_RSA="https://acme.ssl.com/sslcom-dv-rsa"
CA_SSLCOM_ECC="https://acme.ssl.com/sslcom-dv-ecc"
DEFAULT_CA=$CA_LETSENCRYPT_V2 CA_GOOGLE="https://dv.acme-v02.api.pki.goog/directory"
CA_GOOGLE_TEST="https://dv.acme-v02.test-api.pki.goog/directory"
DEFAULT_CA=$CA_ZEROSSL
DEFAULT_STAGING_CA=$CA_LETSENCRYPT_V2_TEST DEFAULT_STAGING_CA=$CA_LETSENCRYPT_V2_TEST
CA_NAMES=" CA_NAMES="
ZeroSSL.com,zerossl
LetsEncrypt.org,letsencrypt LetsEncrypt.org,letsencrypt
LetsEncrypt.org_test,letsencrypt_test,letsencrypttest LetsEncrypt.org_test,letsencrypt_test,letsencrypttest
BuyPass.com,buypass BuyPass.com,buypass
BuyPass.com_test,buypass_test,buypasstest BuyPass.com_test,buypass_test,buypasstest
ZeroSSL.com,zerossl SSL.com,sslcom
Google.com,google
Google.com_test,googletest,google_test
" "
CA_SERVERS="$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_ZEROSSL" CA_SERVERS="$CA_ZEROSSL,$CA_LETSENCRYPT_V2,$CA_LETSENCRYPT_V2_TEST,$CA_BUYPASS,$CA_BUYPASS_TEST,$CA_SSLCOM_RSA,$CA_GOOGLE,$CA_GOOGLE_TEST"
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)" DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
DEFAULT_ACCOUNT_KEY_LENGTH=2048 DEFAULT_ACCOUNT_KEY_LENGTH=ec-256
DEFAULT_DOMAIN_KEY_LENGTH=2048 DEFAULT_DOMAIN_KEY_LENGTH=ec-256
DEFAULT_OPENSSL_BIN="openssl" DEFAULT_OPENSSL_BIN="openssl"
...@@ -56,6 +62,9 @@ VTYPE_HTTP="http-01" ...@@ -56,6 +62,9 @@ VTYPE_HTTP="http-01"
VTYPE_DNS="dns-01" VTYPE_DNS="dns-01"
VTYPE_ALPN="tls-alpn-01" VTYPE_ALPN="tls-alpn-01"
ID_TYPE_DNS="dns"
ID_TYPE_IP="ip"
LOCAL_ANY_ADDRESS="0.0.0.0" LOCAL_ANY_ADDRESS="0.0.0.0"
DEFAULT_RENEW=60 DEFAULT_RENEW=60
...@@ -74,14 +83,15 @@ NGINX="nginx:" ...@@ -74,14 +83,15 @@ NGINX="nginx:"
NGINX_START="#ACME_NGINX_START" NGINX_START="#ACME_NGINX_START"
NGINX_END="#ACME_NGINX_END" NGINX_END="#ACME_NGINX_END"
BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----" BEGIN_CSR="-----BEGIN [NEW ]\{0,4\}CERTIFICATE REQUEST-----"
END_CSR="-----END CERTIFICATE REQUEST-----" END_CSR="-----END [NEW ]\{0,4\}CERTIFICATE REQUEST-----"
BEGIN_CERT="-----BEGIN CERTIFICATE-----" BEGIN_CERT="-----BEGIN CERTIFICATE-----"
END_CERT="-----END CERTIFICATE-----" END_CERT="-----END CERTIFICATE-----"
CONTENT_TYPE_JSON="application/jose+json" CONTENT_TYPE_JSON="application/jose+json"
RENEW_SKIP=2 RENEW_SKIP=2
CODE_DNS_MANUAL=3
B64CONF_START="__ACME_BASE64__START_" B64CONF_START="__ACME_BASE64__START_"
B64CONF_END="__ACME_BASE64__END_" B64CONF_END="__ACME_BASE64__END_"
...@@ -102,6 +112,8 @@ DEBUG_LEVEL_NONE=0 ...@@ -102,6 +112,8 @@ DEBUG_LEVEL_NONE=0
DOH_CLOUDFLARE=1 DOH_CLOUDFLARE=1
DOH_GOOGLE=2 DOH_GOOGLE=2
DOH_ALI=3
DOH_DP=4
HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)" HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)"
...@@ -136,6 +148,8 @@ NOTIFY_MODE_CERT=1 ...@@ -136,6 +148,8 @@ NOTIFY_MODE_CERT=1
NOTIFY_MODE_DEFAULT=$NOTIFY_MODE_BULK NOTIFY_MODE_DEFAULT=$NOTIFY_MODE_BULK
_BASE64_ENCODED_CFGS="Le_PreHook Le_PostHook Le_RenewHook Le_Preferred_Chain Le_ReloadCmd"
_DEBUG_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh" _DEBUG_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh"
_PREPARE_LINK="https://github.com/acmesh-official/acme.sh/wiki/Install-preparations" _PREPARE_LINK="https://github.com/acmesh-official/acme.sh/wiki/Install-preparations"
...@@ -156,10 +170,16 @@ _REVOKE_WIKI="https://github.com/acmesh-official/acme.sh/wiki/revokecert" ...@@ -156,10 +170,16 @@ _REVOKE_WIKI="https://github.com/acmesh-official/acme.sh/wiki/revokecert"
_ZEROSSL_WIKI="https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA" _ZEROSSL_WIKI="https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA"
_SSLCOM_WIKI="https://github.com/acmesh-official/acme.sh/wiki/SSL.com-CA"
_SERVER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Server" _SERVER_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Server"
_PREFERRED_CHAIN_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Preferred-Chain" _PREFERRED_CHAIN_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Preferred-Chain"
_VALIDITY_WIKI="https://github.com/acmesh-official/acme.sh/wiki/Validity"
_DNSCHECK_WIKI="https://github.com/acmesh-official/acme.sh/wiki/dnscheck"
_DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead." _DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead."
_DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR" _DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR"
...@@ -418,18 +438,18 @@ _secure_debug3() { ...@@ -418,18 +438,18 @@ _secure_debug3() {
_upper_case() { _upper_case() {
# shellcheck disable=SC2018,SC2019 # shellcheck disable=SC2018,SC2019
tr 'a-z' 'A-Z' tr '[a-z]' '[A-Z]'
} }
_lower_case() { _lower_case() {
# shellcheck disable=SC2018,SC2019 # shellcheck disable=SC2018,SC2019
tr 'A-Z' 'a-z' tr '[A-Z]' '[a-z]'
} }
_startswith() { _startswith() {
_str="$1" _str="$1"
_sub="$2" _sub="$2"
echo "$_str" | grep "^$_sub" >/dev/null 2>&1 echo "$_str" | grep -- "^$_sub" >/dev/null 2>&1
} }
_endswith() { _endswith() {
...@@ -560,9 +580,17 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then ...@@ -560,9 +580,17 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
fi fi
_h2b() { _h2b() {
if _exists xxd && xxd -r -p 2>/dev/null; then if _exists xxd; then
if _contains "$(xxd --help 2>&1)" "assumes -c30"; then
if xxd -r -p -c 9999 2>/dev/null; then
return
fi
else
if xxd -r -p 2>/dev/null; then
return return
fi fi
fi
fi
hex=$(cat) hex=$(cat)
ic="" ic=""
...@@ -946,9 +974,9 @@ _base64() { ...@@ -946,9 +974,9 @@ _base64() {
#Usage: multiline #Usage: multiline
_dbase64() { _dbase64() {
if [ "$1" ]; then if [ "$1" ]; then
${ACME_OPENSSL_BIN:-openssl} base64 -d -A
else
${ACME_OPENSSL_BIN:-openssl} base64 -d ${ACME_OPENSSL_BIN:-openssl} base64 -d
else
${ACME_OPENSSL_BIN:-openssl} base64 -d -A
fi fi
} }
...@@ -1023,9 +1051,9 @@ _sign() { ...@@ -1023,9 +1051,9 @@ _sign() {
_sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile " _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || grep "BEGIN PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then if _isRSA "$keyfile" >/dev/null 2>&1; then
$_sign_openssl -$alg | _base64 $_sign_openssl -$alg | _base64
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then elif _isEcc "$keyfile" >/dev/null 2>&1; then
if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
_err "Sign failed: $_sign_openssl" _err "Sign failed: $_sign_openssl"
_err "Key file: $keyfile" _err "Key file: $keyfile"
...@@ -1111,6 +1139,7 @@ _createkey() { ...@@ -1111,6 +1139,7 @@ _createkey() {
_debug "Use length $length" _debug "Use length $length"
if ! [ -e "$f" ]; then
if ! touch "$f" >/dev/null 2>&1; then if ! touch "$f" >/dev/null 2>&1; then
_f_path="$(dirname "$f")" _f_path="$(dirname "$f")"
_debug _f_path "$_f_path" _debug _f_path "$_f_path"
...@@ -1119,10 +1148,15 @@ _createkey() { ...@@ -1119,10 +1148,15 @@ _createkey() {
return 1 return 1
fi fi
fi fi
if ! touch "$f" >/dev/null 2>&1; then
return 1
fi
chmod 600 "$f"
fi
if _isEccKey "$length"; then if _isEccKey "$length"; then
_debug "Using ec name: $eccname" _debug "Using ec name: $eccname"
if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -genkey 2>/dev/null)"; then if _opkey="$(${ACME_OPENSSL_BIN:-openssl} ecparam -name "$eccname" -noout -genkey 2>/dev/null)"; then
echo "$_opkey" >"$f" echo "$_opkey" >"$f"
else else
_err "error ecc key name: $eccname" _err "error ecc key name: $eccname"
...@@ -1130,7 +1164,11 @@ _createkey() { ...@@ -1130,7 +1164,11 @@ _createkey() {
fi fi
else else
_debug "Using RSA: $length" _debug "Using RSA: $length"
if _opkey="$(${ACME_OPENSSL_BIN:-openssl} genrsa "$length" 2>/dev/null)"; then __traditional=""
if _contains "$(${ACME_OPENSSL_BIN:-openssl} help genrsa 2>&1)" "-traditional"; then
__traditional="-traditional"
fi
if _opkey="$(${ACME_OPENSSL_BIN:-openssl} genrsa $__traditional "$length" 2>/dev/null)"; then
echo "$_opkey" >"$f" echo "$_opkey" >"$f"
else else
_err "error rsa key: $length" _err "error rsa key: $length"
...@@ -1148,7 +1186,7 @@ _createkey() { ...@@ -1148,7 +1186,7 @@ _createkey() {
_is_idn() { _is_idn() {
_is_idn_d="$1" _is_idn_d="$1"
_debug2 _is_idn_d "$_is_idn_d" _debug2 _is_idn_d "$_is_idn_d"
_idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '0-9' | tr -d 'a-z' | tr -d 'A-Z' | tr -d '*.,-_') _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d '[0-9]' | tr -d '[a-z]' | tr -d '[A-Z]' | tr -d '*.,-_')
_debug2 _idn_temp "$_idn_temp" _debug2 _idn_temp "$_idn_temp"
[ "$_idn_temp" ] [ "$_idn_temp" ]
} }
...@@ -1197,23 +1235,32 @@ _createcsr() { ...@@ -1197,23 +1235,32 @@ _createcsr() {
_debug2 csr "$csr" _debug2 csr "$csr"
_debug2 csrconf "$csrconf" _debug2 csrconf "$csrconf"
printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\n\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment" >"$csrconf" printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\nextendedKeyUsage=serverAuth,clientAuth\n" >"$csrconf"
if [ "$acmeValidationv1" ]; then if [ "$acmeValidationv1" ]; then
domainlist="$(_idn "$domainlist")" domainlist="$(_idn "$domainlist")"
printf -- "\nsubjectAltName=DNS:$domainlist" >>"$csrconf" _debug2 domainlist "$domainlist"
alt=""
for dl in $(echo "$domainlist" | tr "," ' '); do
if [ "$alt" ]; then
alt="$alt,$(_getIdType "$dl" | _upper_case):$dl"
else
alt="$(_getIdType "$dl" | _upper_case):$dl"
fi
done
printf -- "\nsubjectAltName=$alt" >>"$csrconf"
elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then elif [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
#single domain #single domain
_info "Single domain" "$domain" _info "Single domain" "$domain"
printf -- "\nsubjectAltName=DNS:$(_idn "$domain")" >>"$csrconf" printf -- "\nsubjectAltName=$(_getIdType "$domain" | _upper_case):$(_idn "$domain")" >>"$csrconf"
else else
domainlist="$(_idn "$domainlist")" domainlist="$(_idn "$domainlist")"
_debug2 domainlist "$domainlist" _debug2 domainlist "$domainlist"
if _contains "$domainlist" ","; then alt="$(_getIdType "$domain" | _upper_case):$(_idn "$domain")"
alt="DNS:$(_idn "$domain"),DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")" for dl in $(echo "'$domainlist'" | sed "s/,/' '/g"); do
else dl=$(echo "$dl" | tr -d "'")
alt="DNS:$(_idn "$domain"),DNS:$domainlist" alt="$alt,$(_getIdType "$dl" | _upper_case):$dl"
fi done
#multi #multi
_info "Multi domain" "$alt" _info "Multi domain" "$alt"
printf -- "\nsubjectAltName=$alt" >>"$csrconf" printf -- "\nsubjectAltName=$alt" >>"$csrconf"
...@@ -1230,10 +1277,18 @@ _createcsr() { ...@@ -1230,10 +1277,18 @@ _createcsr() {
_csr_cn="$(_idn "$domain")" _csr_cn="$(_idn "$domain")"
_debug2 _csr_cn "$_csr_cn" _debug2 _csr_cn "$_csr_cn"
if _contains "$(uname -a)" "MINGW"; then if _contains "$(uname -a)" "MINGW"; then
if _isIP "$_csr_cn"; then
${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//O=$PROJECT_NAME" -config "$csrconf" -out "$csr"
else
${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr" ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
fi
else
if _isIP "$_csr_cn"; then
${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/O=$PROJECT_NAME" -config "$csrconf" -out "$csr"
else else
${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr" ${ACME_OPENSSL_BIN:-openssl} req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
fi fi
fi
} }
#_signcsr key csr conf cert #_signcsr key csr conf cert
...@@ -1444,7 +1499,6 @@ _create_account_key() { ...@@ -1444,7 +1499,6 @@ _create_account_key() {
else else
#generate account key #generate account key
if _createkey "$length" "$ACCOUNT_KEY_PATH"; then if _createkey "$length" "$ACCOUNT_KEY_PATH"; then
chmod 600 "$ACCOUNT_KEY_PATH"
_info "Create account key ok." _info "Create account key ok."
return 0 return 0
else else
...@@ -1542,23 +1596,22 @@ _durl_replace_base64() { ...@@ -1542,23 +1596,22 @@ _durl_replace_base64() {
_time2str() { _time2str() {
#BSD #BSD
if date -u -r "$1" 2>/dev/null; then if date -u -r "$1" -j "+%Y-%m-%dT%H:%M:%SZ" 2>/dev/null; then
return return
fi fi
#Linux #Linux
if date -u -d@"$1" 2>/dev/null; then if date -u --date=@"$1" "+%Y-%m-%dT%H:%M:%SZ" 2>/dev/null; then
return return
fi fi
#Solaris #Solaris
if _exists adb; then if printf "%(%Y-%m-%dT%H:%M:%SZ)T\n" $1 2>/dev/null; then
_t_s_a=$(echo "0t${1}=Y" | adb) return
echo "$_t_s_a"
fi fi
#Busybox #Busybox
if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then if echo "$1" | awk '{ print strftime("%Y-%m-%dT%H:%M:%SZ", $0); }' 2>/dev/null; then
return return
fi fi
} }
...@@ -1581,6 +1634,24 @@ _stat() { ...@@ -1581,6 +1634,24 @@ _stat() {
return 1 #error, 'stat' not found return 1 #error, 'stat' not found
} }
#keyfile
_isRSA() {
keyfile=$1
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || ${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text 2>&1 | grep "^publicExponent:" 2>&1 >/dev/null; then
return 0
fi
return 1
}
#keyfile
_isEcc() {
keyfile=$1
if grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || ${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" 2>&1 >/dev/null; then
return 0
fi
return 1
}
#keyfile #keyfile
_calcjwk() { _calcjwk() {
keyfile="$1" keyfile="$1"
...@@ -1594,7 +1665,7 @@ _calcjwk() { ...@@ -1594,7 +1665,7 @@ _calcjwk() {
return 0 return 0
fi fi
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then if _isRSA "$keyfile"; then
_debug "RSA key" _debug "RSA key"
pub_exp=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1) pub_exp=$(${ACME_OPENSSL_BIN:-openssl} rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
if [ "${#pub_exp}" = "5" ]; then if [ "${#pub_exp}" = "5" ]; then
...@@ -1616,7 +1687,7 @@ _calcjwk() { ...@@ -1616,7 +1687,7 @@ _calcjwk() {
JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}' JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}'
JWK_HEADERPLACE_PART1='{"nonce": "' JWK_HEADERPLACE_PART1='{"nonce": "'
JWK_HEADERPLACE_PART2='", "alg": "RS256"' JWK_HEADERPLACE_PART2='", "alg": "RS256"'
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then elif _isEcc "$keyfile"; then
_debug "EC key" _debug "EC key"
crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
_debug3 crv "$crv" _debug3 crv "$crv"
...@@ -1673,7 +1744,7 @@ _calcjwk() { ...@@ -1673,7 +1744,7 @@ _calcjwk() {
_debug3 x64 "$x64" _debug3 x64 "$x64"
xend=$(_math "$xend" + 1) xend=$(_math "$xend" + 1)
y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-10000)" y="$(printf "%s" "$pubtext" | cut -d : -f "$xend"-2048)"
_debug3 y "$y" _debug3 y "$y"
y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _url_replace)" y64="$(printf "%s" "$y" | tr -d : | _h2b | _base64 | _url_replace)"
...@@ -1699,6 +1770,27 @@ _time() { ...@@ -1699,6 +1770,27 @@ _time() {
date -u "+%s" date -u "+%s"
} }
#support 2 formats:
# 2022-04-01 08:10:33 to 1648800633
#or 2022-04-01T08:10:33Z to 1648800633
_date2time() {
#Linux
if date -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
fi
#Solaris
if gdate -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
fi
#Mac/BSD
if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
fi
_err "Can not parse _date2time $1"
return 1
}
_utc_date() { _utc_date() {
date -u "+%Y-%m-%d %H:%M:%S" date -u "+%Y-%m-%d %H:%M:%S"
} }
...@@ -1722,6 +1814,14 @@ _mktemp() { ...@@ -1722,6 +1814,14 @@ _mktemp() {
_err "Can not create temp file." _err "Can not create temp file."
} }
#clear all the https envs to cause _inithttp() to run next time.
_resethttp() {
__HTTP_INITIALIZED=""
_ACME_CURL=""
_ACME_WGET=""
ACME_HTTP_NO_REDIRECTS=""
}
_inithttp() { _inithttp() {
if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then
...@@ -1737,8 +1837,11 @@ _inithttp() { ...@@ -1737,8 +1837,11 @@ _inithttp() {
fi fi
if [ -z "$_ACME_CURL" ] && _exists "curl"; then if [ -z "$_ACME_CURL" ] && _exists "curl"; then
_ACME_CURL="curl -L --silent --dump-header $HTTP_HEADER " _ACME_CURL="curl --silent --dump-header $HTTP_HEADER "
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then if [ -z "$ACME_HTTP_NO_REDIRECTS" ]; then
_ACME_CURL="$_ACME_CURL -L "
fi
if [ "$DEBUG" ] && [ "$DEBUG" -ge 2 ]; then
_CURL_DUMP="$(_mktemp)" _CURL_DUMP="$(_mktemp)"
_ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP " _ACME_CURL="$_ACME_CURL --trace-ascii $_CURL_DUMP "
fi fi
...@@ -1749,27 +1852,38 @@ _inithttp() { ...@@ -1749,27 +1852,38 @@ _inithttp() {
_ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE " _ACME_CURL="$_ACME_CURL --cacert $CA_BUNDLE "
fi fi
if _contains "$(curl --help 2>&1)" "--globoff"; then if _contains "$(curl --help 2>&1)" "--globoff" || _contains "$(curl --help curl 2>&1)" "--globoff"; then
_ACME_CURL="$_ACME_CURL -g " _ACME_CURL="$_ACME_CURL -g "
fi fi
#don't use --fail-with-body
##from curl 7.76: return fail on HTTP errors but keep the body
#if _contains "$(curl --help http 2>&1)" "--fail-with-body"; then
# _ACME_CURL="$_ACME_CURL --fail-with-body "
#fi
fi fi
if [ -z "$_ACME_WGET" ] && _exists "wget"; then if [ -z "$_ACME_WGET" ] && _exists "wget"; then
_ACME_WGET="wget -q" _ACME_WGET="wget -q"
if [ "$ACME_HTTP_NO_REDIRECTS" ]; then
_ACME_WGET="$_ACME_WGET --max-redirect 0 "
fi
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--debug"; then
_ACME_WGET="$_ACME_WGET -d " _ACME_WGET="$_ACME_WGET -d "
fi fi
fi
if [ "$CA_PATH" ]; then if [ "$CA_PATH" ]; then
_ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH " _ACME_WGET="$_ACME_WGET --ca-directory=$CA_PATH "
elif [ "$CA_BUNDLE" ]; then elif [ "$CA_BUNDLE" ]; then
_ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE " _ACME_WGET="$_ACME_WGET --ca-certificate=$CA_BUNDLE "
fi fi
fi
#from wget 1.14: do not skip body on 404 error #from wget 1.14: do not skip body on 404 error
if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--content-on-error"; then if _contains "$(wget --help 2>&1)" "--content-on-error"; then
_ACME_WGET="$_ACME_WGET --content-on-error " _ACME_WGET="$_ACME_WGET --content-on-error "
fi fi
fi
__HTTP_INITIALIZED=1 __HTTP_INITIALIZED=1
...@@ -1891,7 +2005,13 @@ _post() { ...@@ -1891,7 +2005,13 @@ _post() {
if [ "$_ret" != "0" ]; then if [ "$_ret" != "0" ]; then
_err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret" _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
fi fi
_sed_i "s/^ *//g" "$HTTP_HEADER" if _contains "$_WGET" " -d "; then
# Demultiplex wget debug output
cat "$HTTP_HEADER" >&2
_sed_i '/^[^ ][^ ]/d; /^ *$/d' "$HTTP_HEADER"
fi
# remove leading whitespaces from header to match curl format
_sed_i 's/^ //g' "$HTTP_HEADER"
else else
_ret="$?" _ret="$?"
_err "Neither curl nor wget is found, can not do $httpmethod." _err "Neither curl nor wget is found, can not do $httpmethod."
...@@ -1944,9 +2064,21 @@ _get() { ...@@ -1944,9 +2064,21 @@ _get() {
fi fi
_debug "_WGET" "$_WGET" _debug "_WGET" "$_WGET"
if [ "$onlyheader" ]; then if [ "$onlyheader" ]; then
$_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null "$url" 2>&1 | sed 's/^[ ]*//g' _wget_out="$($_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null "$url" 2>&1)"
if _contains "$_WGET" " -d "; then
# Demultiplex wget debug output
echo "$_wget_out" >&2
echo "$_wget_out" | sed '/^[^ ][^ ]/d; /^ *$/d; s/^ //g' -
fi
else else
$_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url" $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O - "$url" 2>"$HTTP_HEADER"
if _contains "$_WGET" " -d "; then
# Demultiplex wget debug output
cat "$HTTP_HEADER" >&2
_sed_i '/^[^ ][^ ]/d; /^ *$/d' "$HTTP_HEADER"
fi
# remove leading whitespaces from header to match curl format
_sed_i 's/^ //g' "$HTTP_HEADER"
fi fi
ret=$? ret=$?
if [ "$ret" = "8" ]; then if [ "$ret" = "8" ]; then
...@@ -1984,6 +2116,7 @@ _send_signed_request() { ...@@ -1984,6 +2116,7 @@ _send_signed_request() {
if [ -z "$keyfile" ]; then if [ -z "$keyfile" ]; then
keyfile="$ACCOUNT_KEY_PATH" keyfile="$ACCOUNT_KEY_PATH"
fi fi
_debug "=======Begin Send Signed Request======="
_debug url "$url" _debug url "$url"
_debug payload "$payload" _debug payload "$payload"
...@@ -2010,7 +2143,7 @@ _send_signed_request() { ...@@ -2010,7 +2143,7 @@ _send_signed_request() {
if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type" >/dev/null; then if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type" >/dev/null; then
_headers="$(cat "$HTTP_HEADER")" _headers="$(cat "$HTTP_HEADER")"
_debug2 _headers "$_headers" _debug2 _headers "$_headers"
_CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2 | cut -d , -f 1)"
fi fi
fi fi
if [ -z "$_CACHED_NONCE" ]; then if [ -z "$_CACHED_NONCE" ]; then
...@@ -2042,7 +2175,7 @@ _send_signed_request() { ...@@ -2042,7 +2175,7 @@ _send_signed_request() {
_sleep 2 _sleep 2
continue continue
fi fi
if [ "$ACME_VERSION" = "2" ]; then
if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then if [ "$url" = "$ACME_NEW_ACCOUNT" ]; then
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}' protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
elif [ "$url" = "$ACME_REVOKE_CERT" ] && [ "$keyfile" != "$ACCOUNT_KEY_PATH" ]; then elif [ "$url" = "$ACME_REVOKE_CERT" ] && [ "$keyfile" != "$ACCOUNT_KEY_PATH" ]; then
...@@ -2050,9 +2183,7 @@ _send_signed_request() { ...@@ -2050,9 +2183,7 @@ _send_signed_request() {
else else
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}' protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}'
fi fi
else
protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
fi
_debug3 protected "$protected" _debug3 protected "$protected"
protected64="$(printf "%s" "$protected" | _base64 | _url_replace)" protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
...@@ -2086,11 +2217,11 @@ _send_signed_request() { ...@@ -2086,11 +2217,11 @@ _send_signed_request() {
_debug2 original "$response" _debug2 original "$response"
if echo "$responseHeaders" | grep -i "Content-Type: *application/json" >/dev/null 2>&1; then if echo "$responseHeaders" | grep -i "Content-Type: *application/json" >/dev/null 2>&1; then
response="$(echo "$response" | _normalizeJson | _json_decode)" response="$(echo "$response" | _json_decode | _normalizeJson)"
fi fi
_debug2 response "$response" _debug2 response "$response"
_CACHED_NONCE="$(echo "$responseHeaders" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" _CACHED_NONCE="$(echo "$responseHeaders" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2 | cut -d , -f 1)"
if ! _startswith "$code" "2"; then if ! _startswith "$code" "2"; then
_body="$response" _body="$response"
...@@ -2099,12 +2230,32 @@ _send_signed_request() { ...@@ -2099,12 +2230,32 @@ _send_signed_request() {
_debug3 _body "$_body" _debug3 _body "$_body"
fi fi
_retryafter=$(echo "$responseHeaders" | grep -i "^Retry-After *: *[0-9]\+ *" | cut -d : -f 2 | tr -d ' ' | tr -d '\r')
if [ "$code" = '503' ]; then
_sleep_overload_retry_sec=$_retryafter
if [ -z "$_sleep_overload_retry_sec" ]; then
_sleep_overload_retry_sec=5
fi
if [ $_sleep_overload_retry_sec -le 600 ]; then
_info "It seems the CA server is currently overloaded, let's wait and retry. Sleeping $_sleep_overload_retry_sec seconds."
_sleep $_sleep_overload_retry_sec
continue
else
_info "The retryafter=$_retryafter is too large > 600, not retry anymore."
fi
fi
if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then if _contains "$_body" "JWS has invalid anti-replay nonce" || _contains "$_body" "JWS has an invalid anti-replay nonce"; then
_info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds." _info "It seems the CA server is busy now, let's wait and retry. Sleeping $_sleep_retry_sec seconds."
_CACHED_NONCE="" _CACHED_NONCE=""
_sleep $_sleep_retry_sec _sleep $_sleep_retry_sec
continue continue
fi fi
if _contains "$_body" "The Replay Nonce is not recognized"; then
_info "The replay Nonce is not valid, let's get a new one, Sleeping $_sleep_retry_sec seconds."
_CACHED_NONCE=""
_sleep $_sleep_retry_sec
continue
fi
fi fi
return 0 return 0
done done
...@@ -2127,12 +2278,18 @@ _setopt() { ...@@ -2127,12 +2278,18 @@ _setopt() {
if [ ! -f "$__conf" ]; then if [ ! -f "$__conf" ]; then
touch "$__conf" touch "$__conf"
fi fi
if [ -n "$(tail -c1 <"$__conf")" ]; then
echo >>"$__conf"
fi
if grep -n "^$__opt$__sep" "$__conf" >/dev/null; then if grep -n "^$__opt$__sep" "$__conf" >/dev/null; then
_debug3 OK _debug3 OK
if _contains "$__val" "&"; then if _contains "$__val" "&"; then
__val="$(echo "$__val" | sed 's/&/\\&/g')" __val="$(echo "$__val" | sed 's/&/\\&/g')"
fi fi
if _contains "$__val" "|"; then
__val="$(echo "$__val" | sed 's/|/\\|/g')"
fi
text="$(cat "$__conf")" text="$(cat "$__conf")"
printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf" printf -- "%s\n" "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
...@@ -2140,6 +2297,9 @@ _setopt() { ...@@ -2140,6 +2297,9 @@ _setopt() {
if _contains "$__val" "&"; then if _contains "$__val" "&"; then
__val="$(echo "$__val" | sed 's/&/\\&/g')" __val="$(echo "$__val" | sed 's/&/\\&/g')"
fi fi
if _contains "$__val" "|"; then
__val="$(echo "$__val" | sed 's/|/\\|/g')"
fi
text="$(cat "$__conf")" text="$(cat "$__conf")"
printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf" printf -- "%s\n" "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" >"$__conf"
...@@ -2213,6 +2373,26 @@ _readdomainconf() { ...@@ -2213,6 +2373,26 @@ _readdomainconf() {
_read_conf "$DOMAIN_CONF" "$1" _read_conf "$DOMAIN_CONF" "$1"
} }
#_migratedomainconf oldkey newkey base64encode
_migratedomainconf() {
_old_key="$1"
_new_key="$2"
_b64encode="$3"
_value=$(_readdomainconf "$_old_key")
if [ -z "$_value" ]; then
return 1 # oldkey is not found
fi
_savedomainconf "$_new_key" "$_value" "$_b64encode"
_cleardomainconf "$_old_key"
_debug "Domain config $_old_key has been migrated to $_new_key"
}
#_migratedeployconf oldkey newkey base64encode
_migratedeployconf() {
_migratedomainconf "$1" "SAVED_$2" "$3" ||
_migratedomainconf "SAVED_$1" "SAVED_$2" "$3" # try only when oldkey itself is not found
}
#key value base64encode #key value base64encode
_savedeployconf() { _savedeployconf() {
_savedomainconf "SAVED_$1" "$2" "$3" _savedomainconf "SAVED_$1" "$2" "$3"
...@@ -2227,12 +2407,14 @@ _getdeployconf() { ...@@ -2227,12 +2407,14 @@ _getdeployconf() {
if [ "$_rac_value" ]; then if [ "$_rac_value" ]; then
if _startswith "$_rac_value" '"' && _endswith "$_rac_value" '"'; then if _startswith "$_rac_value" '"' && _endswith "$_rac_value" '"'; then
_debug2 "trim quotation marks" _debug2 "trim quotation marks"
eval "export $_rac_key=$_rac_value" eval $_rac_key=$_rac_value
export $_rac_key
fi fi
return 0 # do nothing return 0 # do nothing
fi fi
_saved=$(_readdomainconf "SAVED_$_rac_key") _saved="$(_readdomainconf "SAVED_$_rac_key")"
eval "export $_rac_key=\"$_saved\"" eval $_rac_key=\$_saved
export $_rac_key
} }
#_saveaccountconf key value base64encode #_saveaccountconf key value base64encode
...@@ -2263,6 +2445,13 @@ _clearaccountconf() { ...@@ -2263,6 +2445,13 @@ _clearaccountconf() {
_clear_conf "$ACCOUNT_CONF_PATH" "$1" _clear_conf "$ACCOUNT_CONF_PATH" "$1"
} }
#key
_clearaccountconf_mutable() {
_clearaccountconf "SAVED_$1"
#remove later
_clearaccountconf "$1"
}
#_savecaconf key value #_savecaconf key value
_savecaconf() { _savecaconf() {
_save_conf "$CA_CONF" "$1" "$2" _save_conf "$CA_CONF" "$1" "$2"
...@@ -2316,7 +2505,7 @@ _startserver() { ...@@ -2316,7 +2505,7 @@ _startserver() {
echo 'HTTP/1.0 200 OK'; \ echo 'HTTP/1.0 200 OK'; \
echo 'Content-Length\: $_content_len'; \ echo 'Content-Length\: $_content_len'; \
echo ''; \ echo ''; \
printf -- '$content';" & printf '%s' '$content';" &
serverproc="$!" serverproc="$!"
} }
...@@ -2442,7 +2631,7 @@ __initHome() { ...@@ -2442,7 +2631,7 @@ __initHome() {
_script_home="$(dirname "$_script")" _script_home="$(dirname "$_script")"
_debug "_script_home" "$_script_home" _debug "_script_home" "$_script_home"
if [ -d "$_script_home" ]; then if [ -d "$_script_home" ]; then
_SCRIPT_HOME="$_script_home" export _SCRIPT_HOME="$_script_home"
else else
_err "It seems the script home is not correct:$_script_home" _err "It seems the script home is not correct:$_script_home"
fi fi
...@@ -2491,76 +2680,56 @@ __initHome() { ...@@ -2491,76 +2680,56 @@ __initHome() {
fi fi
} }
_clearAPI() {
ACME_NEW_ACCOUNT=""
ACME_KEY_CHANGE=""
ACME_NEW_AUTHZ=""
ACME_NEW_ORDER=""
ACME_REVOKE_CERT=""
ACME_NEW_NONCE=""
ACME_AGREEMENT=""
}
#server #server
_initAPI() { _initAPI() {
_api_server="${1:-$ACME_DIRECTORY}" _api_server="${1:-$ACME_DIRECTORY}"
_debug "_init api for server: $_api_server" _debug "_init api for server: $_api_server"
if [ -z "$ACME_NEW_ACCOUNT" ]; then MAX_API_RETRY_TIMES=10
_sleep_retry_sec=10
_request_retry_times=0
while [ -z "$ACME_NEW_ACCOUNT" ] && [ "${_request_retry_times}" -lt "$MAX_API_RETRY_TIMES" ]; do
_request_retry_times=$(_math "$_request_retry_times" + 1)
response=$(_get "$_api_server") response=$(_get "$_api_server")
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_debug2 "response" "$response" _debug2 "response" "$response"
_err "Can not init api." _info "Can not init api for: $_api_server."
return 1 _info "Sleep $_sleep_retry_sec and retry."
_sleep "$_sleep_retry_sec"
continue
fi fi
response=$(echo "$response" | _json_decode) response=$(echo "$response" | _json_decode)
_debug2 "response" "$response" _debug2 "response" "$response"
ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3)
if [ -z "$ACME_KEY_CHANGE" ]; then
ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'keyChange" *: *"[^"]*"' | cut -d '"' -f 3) ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'keyChange" *: *"[^"]*"' | cut -d '"' -f 3)
fi
export ACME_KEY_CHANGE export ACME_KEY_CHANGE
ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3)
if [ -z "$ACME_NEW_AUTHZ" ]; then
ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'newAuthz" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'newAuthz" *: *"[^"]*"' | cut -d '"' -f 3)
fi
export ACME_NEW_AUTHZ export ACME_NEW_AUTHZ
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ORDER_RES="new-cert"
if [ -z "$ACME_NEW_ORDER" ]; then
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ORDER_RES="new-order"
if [ -z "$ACME_NEW_ORDER" ]; then
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'newOrder" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'newOrder" *: *"[^"]*"' | cut -d '"' -f 3)
fi
fi
export ACME_NEW_ORDER export ACME_NEW_ORDER
export ACME_NEW_ORDER_RES
ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-reg" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ACCOUNT_RES="new-reg"
if [ -z "$ACME_NEW_ACCOUNT" ]; then
ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ACCOUNT_RES="new-account"
if [ -z "$ACME_NEW_ACCOUNT" ]; then
ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'newAccount" *: *"[^"]*"' | cut -d '"' -f 3)
if [ "$ACME_NEW_ACCOUNT" ]; then
export ACME_VERSION=2
fi
fi
fi
export ACME_NEW_ACCOUNT export ACME_NEW_ACCOUNT
export ACME_NEW_ACCOUNT_RES
ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3)
if [ -z "$ACME_REVOKE_CERT" ]; then
ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revokeCert" *: *"[^"]*"' | cut -d '"' -f 3) ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revokeCert" *: *"[^"]*"' | cut -d '"' -f 3)
fi
export ACME_REVOKE_CERT export ACME_REVOKE_CERT
ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3)
if [ -z "$ACME_NEW_NONCE" ]; then
ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'newNonce" *: *"[^"]*"' | cut -d '"' -f 3)
fi
export ACME_NEW_NONCE export ACME_NEW_NONCE
ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3)
if [ -z "$ACME_AGREEMENT" ]; then
ACME_AGREEMENT=$(echo "$response" | _egrep_o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3) ACME_AGREEMENT=$(echo "$response" | _egrep_o 'termsOfService" *: *"[^"]*"' | cut -d '"' -f 3)
fi
export ACME_AGREEMENT export ACME_AGREEMENT
_debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" _debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
...@@ -2570,9 +2739,23 @@ _initAPI() { ...@@ -2570,9 +2739,23 @@ _initAPI() {
_debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT" _debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
_debug "ACME_AGREEMENT" "$ACME_AGREEMENT" _debug "ACME_AGREEMENT" "$ACME_AGREEMENT"
_debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE" _debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE"
_debug "ACME_VERSION" "$ACME_VERSION" if [ "$ACME_NEW_ACCOUNT" ] && [ "$ACME_NEW_ORDER" ]; then
return 0
fi
_info "Sleep $_sleep_retry_sec and retry."
_sleep "$_sleep_retry_sec"
done
if [ "$ACME_NEW_ACCOUNT" ] && [ "$ACME_NEW_ORDER" ]; then
return 0
fi fi
_err "Can not init api, for $_api_server"
return 1
}
_clearCA() {
export CA_CONF=
export ACCOUNT_KEY_PATH=
export ACCOUNT_JSON_PATH=
} }
#[domain] [keylength or isEcc flag] #[domain] [keylength or isEcc flag]
...@@ -2616,15 +2799,44 @@ _initpath() { ...@@ -2616,15 +2799,44 @@ _initpath() {
_ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)" _ACME_SERVER_HOST="$(echo "$ACME_DIRECTORY" | cut -d : -f 2 | tr -s / | cut -d / -f 2)"
_debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST" _debug2 "_ACME_SERVER_HOST" "$_ACME_SERVER_HOST"
CA_DIR="$CA_HOME/$_ACME_SERVER_HOST" _ACME_SERVER_PATH="$(echo "$ACME_DIRECTORY" | cut -d : -f 2- | tr -s / | cut -d / -f 3-)"
_debug2 "_ACME_SERVER_PATH" "$_ACME_SERVER_PATH"
CA_DIR="$CA_HOME/$_ACME_SERVER_HOST/$_ACME_SERVER_PATH"
_DEFAULT_CA_CONF="$CA_DIR/ca.conf" _DEFAULT_CA_CONF="$CA_DIR/ca.conf"
if [ -z "$CA_CONF" ]; then if [ -z "$CA_CONF" ]; then
CA_CONF="$_DEFAULT_CA_CONF" CA_CONF="$_DEFAULT_CA_CONF"
fi fi
_debug3 CA_CONF "$CA_CONF" _debug3 CA_CONF "$CA_CONF"
_OLD_CADIR="$CA_HOME/$_ACME_SERVER_HOST"
_OLD_ACCOUNT_KEY="$_OLD_CADIR/account.key"
_OLD_ACCOUNT_JSON="$_OLD_CADIR/account.json"
_OLD_CA_CONF="$_OLD_CADIR/ca.conf"
_DEFAULT_ACCOUNT_KEY_PATH="$CA_DIR/account.key"
_DEFAULT_ACCOUNT_JSON_PATH="$CA_DIR/account.json"
if [ -z "$ACCOUNT_KEY_PATH" ]; then
ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH"
if [ -f "$_OLD_ACCOUNT_KEY" ] && ! [ -f "$ACCOUNT_KEY_PATH" ]; then
mkdir -p "$CA_DIR"
mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
fi
fi
if [ -z "$ACCOUNT_JSON_PATH" ]; then
ACCOUNT_JSON_PATH="$_DEFAULT_ACCOUNT_JSON_PATH"
if [ -f "$_OLD_ACCOUNT_JSON" ] && ! [ -f "$ACCOUNT_JSON_PATH" ]; then
mkdir -p "$CA_DIR"
mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
fi
fi
if [ -f "$_OLD_CA_CONF" ] && ! [ -f "$CA_CONF" ]; then
mkdir -p "$CA_DIR"
mv "$_OLD_CA_CONF" "$CA_CONF"
fi
if [ -f "$CA_CONF" ]; then if [ -f "$CA_CONF" ]; then
. "$CA_CONF" . "$CA_CONF"
fi fi
...@@ -2645,19 +2857,6 @@ _initpath() { ...@@ -2645,19 +2857,6 @@ _initpath() {
HTTP_HEADER="$LE_CONFIG_HOME/http.header" HTTP_HEADER="$LE_CONFIG_HOME/http.header"
fi fi
_OLD_ACCOUNT_KEY="$LE_WORKING_DIR/account.key"
_OLD_ACCOUNT_JSON="$LE_WORKING_DIR/account.json"
_DEFAULT_ACCOUNT_KEY_PATH="$CA_DIR/account.key"
_DEFAULT_ACCOUNT_JSON_PATH="$CA_DIR/account.json"
if [ -z "$ACCOUNT_KEY_PATH" ]; then
ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH"
fi
if [ -z "$ACCOUNT_JSON_PATH" ]; then
ACCOUNT_JSON_PATH="$_DEFAULT_ACCOUNT_JSON_PATH"
fi
_DEFAULT_CERT_HOME="$LE_CONFIG_HOME" _DEFAULT_CERT_HOME="$LE_CONFIG_HOME"
if [ -z "$CERT_HOME" ]; then if [ -z "$CERT_HOME" ]; then
CERT_HOME="$_DEFAULT_CERT_HOME" CERT_HOME="$_DEFAULT_CERT_HOME"
...@@ -2679,12 +2878,14 @@ _initpath() { ...@@ -2679,12 +2878,14 @@ _initpath() {
if _isEccKey "$_ilength"; then if _isEccKey "$_ilength"; then
DOMAIN_PATH="$domainhomeecc" DOMAIN_PATH="$domainhomeecc"
else elif [ -z "$__SELECTED_RSA_KEY" ]; then
if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ]; then if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ]; then
_info "The domain '$domain' seems to have a ECC cert already, please add '$(__red "--ecc")' parameter if you want to use that cert." _info "The domain '$domain' seems to have a ECC cert already, lets use ecc cert."
DOMAIN_PATH="$domainhomeecc"
fi fi
fi fi
_debug DOMAIN_PATH "$DOMAIN_PATH" _debug DOMAIN_PATH "$DOMAIN_PATH"
export DOMAIN_PATH
fi fi
if [ -z "$DOMAIN_BACKUP_PATH" ]; then if [ -z "$DOMAIN_BACKUP_PATH" ]; then
...@@ -2736,22 +2937,6 @@ _initpath() { ...@@ -2736,22 +2937,6 @@ _initpath() {
} }
_exec() {
if [ -z "$_EXEC_TEMP_ERR" ]; then
_EXEC_TEMP_ERR="$(_mktemp)"
fi
if [ "$_EXEC_TEMP_ERR" ]; then
eval "$@ 2>>$_EXEC_TEMP_ERR"
else
eval "$@"
fi
}
_exec_err() {
[ "$_EXEC_TEMP_ERR" ] && _err "$(cat "$_EXEC_TEMP_ERR")" && echo "" >"$_EXEC_TEMP_ERR"
}
_apachePath() { _apachePath() {
_APACHECTL="apachectl" _APACHECTL="apachectl"
if ! _exists apachectl; then if ! _exists apachectl; then
...@@ -2764,8 +2949,7 @@ _apachePath() { ...@@ -2764,8 +2949,7 @@ _apachePath() {
fi fi
fi fi
if ! _exec $_APACHECTL -V >/dev/null; then if ! $_APACHECTL -V >/dev/null; then
_exec_err
return 1 return 1
fi fi
...@@ -2817,8 +3001,7 @@ _restoreApache() { ...@@ -2817,8 +3001,7 @@ _restoreApache() {
cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" >"$httpdconf" cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" >"$httpdconf"
_debug "Restored: $httpdconf." _debug "Restored: $httpdconf."
if ! _exec $_APACHECTL -t; then if ! $_APACHECTL -t; then
_exec_err
_err "Sorry, restore apache config error, please contact me." _err "Sorry, restore apache config error, please contact me."
return 1 return 1
fi fi
...@@ -2836,8 +3019,7 @@ _setApache() { ...@@ -2836,8 +3019,7 @@ _setApache() {
#test the conf first #test the conf first
_info "Checking if there is an error in the apache config file before starting." _info "Checking if there is an error in the apache config file before starting."
if ! _exec "$_APACHECTL" -t >/dev/null; then if ! $_APACHECTL -t >/dev/null; then
_exec_err
_err "The apache config file has error, please fix it first, then try again." _err "The apache config file has error, please fix it first, then try again."
_err "Don't worry, there is nothing changed to your system." _err "Don't worry, there is nothing changed to your system."
return 1 return 1
...@@ -2898,8 +3080,7 @@ Allow from all ...@@ -2898,8 +3080,7 @@ Allow from all
chmod 755 "$ACME_DIR" chmod 755 "$ACME_DIR"
fi fi
if ! _exec "$_APACHECTL" graceful; then if ! $_APACHECTL graceful; then
_exec_err
_err "$_APACHECTL graceful error, please contact me." _err "$_APACHECTL graceful error, please contact me."
_restoreApache _restoreApache
return 1 return 1
...@@ -2984,8 +3165,7 @@ _setNginx() { ...@@ -2984,8 +3165,7 @@ _setNginx() {
return 1 return 1
fi fi
_info "Check the nginx conf before setting up." _info "Check the nginx conf before setting up."
if ! _exec "nginx -t" >/dev/null; then if ! nginx -t >/dev/null; then
_exec_err
return 1 return 1
fi fi
...@@ -3012,16 +3192,14 @@ location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" { ...@@ -3012,16 +3192,14 @@ location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
fi fi
_debug3 "Modified config:$(cat $FOUND_REAL_NGINX_CONF)" _debug3 "Modified config:$(cat $FOUND_REAL_NGINX_CONF)"
_info "nginx conf is done, let's check it again." _info "nginx conf is done, let's check it again."
if ! _exec "nginx -t" >/dev/null; then if ! nginx -t >/dev/null; then
_exec_err
_err "It seems that nginx conf was broken, let's restore." _err "It seems that nginx conf was broken, let's restore."
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF" cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
return 1 return 1
fi fi
_info "Reload nginx" _info "Reload nginx"
if ! _exec "nginx -s reload" >/dev/null; then if ! nginx -s reload >/dev/null; then
_exec_err
_err "It seems that nginx reload error, let's restore." _err "It seems that nginx reload error, let's restore."
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF" cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
return 1 return 1
...@@ -3055,6 +3233,11 @@ _checkConf() { ...@@ -3055,6 +3233,11 @@ _checkConf() {
_debug "Try include files" _debug "Try include files"
for included in $(cat "$2" | tr "\t" " " | grep "^ *include *.*;" | sed "s/include //" | tr -d " ;"); do for included in $(cat "$2" | tr "\t" " " | grep "^ *include *.*;" | sed "s/include //" | tr -d " ;"); do
_debug "check included $included" _debug "check included $included"
if ! _startswith "$included" "/" && _exists dirname; then
_relpath="$(dirname "$2")"
_debug "_relpath" "$_relpath"
included="$_relpath/$included"
fi
if _checkConf "$1" "$included"; then if _checkConf "$1" "$included"; then
return 0 return 0
fi fi
...@@ -3141,8 +3324,7 @@ _restoreNginx() { ...@@ -3141,8 +3324,7 @@ _restoreNginx() {
done done
_info "Reload nginx" _info "Reload nginx"
if ! _exec "nginx -s reload" >/dev/null; then if ! nginx -s reload >/dev/null; then
_exec_err
_err "It seems that nginx reload error, please report bug." _err "It seems that nginx reload error, please report bug."
return 1 return 1
fi fi
...@@ -3265,6 +3447,8 @@ _on_before_issue() { ...@@ -3265,6 +3447,8 @@ _on_before_issue() {
if [ "$_chk_pre_hook" ]; then if [ "$_chk_pre_hook" ]; then
_info "Run pre hook:'$_chk_pre_hook'" _info "Run pre hook:'$_chk_pre_hook'"
if ! ( if ! (
export Le_Domain="$_chk_main_domain"
export Le_Alt="$_chk_alt_domains"
cd "$DOMAIN_PATH" && eval "$_chk_pre_hook" cd "$DOMAIN_PATH" && eval "$_chk_pre_hook"
); then ); then
_err "Error when run pre hook." _err "Error when run pre hook."
...@@ -3326,7 +3510,7 @@ _on_before_issue() { ...@@ -3326,7 +3510,7 @@ _on_before_issue() {
_netprc="$(_ss "$_checkport" | grep "$_checkport")" _netprc="$(_ss "$_checkport" | grep "$_checkport")"
netprc="$(echo "$_netprc" | grep "$_checkaddr")" netprc="$(echo "$_netprc" | grep "$_checkaddr")"
if [ -z "$netprc" ]; then if [ -z "$netprc" ]; then
netprc="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS")" netprc="$(echo "$_netprc" | grep "$LOCAL_ANY_ADDRESS:$_checkport")"
fi fi
if [ "$netprc" ]; then if [ "$netprc" ]; then
_err "$netprc" _err "$netprc"
...@@ -3483,15 +3667,6 @@ _regAccount() { ...@@ -3483,15 +3667,6 @@ _regAccount() {
_initAPI _initAPI
mkdir -p "$CA_DIR" mkdir -p "$CA_DIR"
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
fi
if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
_info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
fi
if [ ! -f "$ACCOUNT_KEY_PATH" ]; then if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
if ! _create_account_key "$_reg_length"; then if ! _create_account_key "$_reg_length"; then
...@@ -3515,13 +3690,15 @@ _regAccount() { ...@@ -3515,13 +3690,15 @@ _regAccount() {
if [ "$_email" ]; then if [ "$_email" ]; then
_savecaconf "CA_EMAIL" "$_email" _savecaconf "CA_EMAIL" "$_email"
fi fi
if [ "$ACME_VERSION" = "2" ]; then
if [ "$ACME_DIRECTORY" = "$CA_ZEROSSL" ]; then if [ "$ACME_DIRECTORY" = "$CA_ZEROSSL" ]; then
if [ -z "$_eab_id" ] || [ -z "$_eab_hmac_key" ]; then if [ -z "$_eab_id" ] || [ -z "$_eab_hmac_key" ]; then
_info "No EAB credentials found for ZeroSSL, let's get one" _info "No EAB credentials found for ZeroSSL, let's get one"
if [ -z "$_email" ]; then if [ -z "$_email" ]; then
_err "Please provide a email address for ZeroSSL account." _info "$(__green "$PROJECT_NAME is using ZeroSSL as default CA now.")"
_err "See ZeroSSL usage: $_ZEROSSL_WIKI" _info "$(__green "Please update your account with an email address first.")"
_info "$(__green "$PROJECT_ENTRY --register-account -m my@example.com")"
_info "See: $(__green "$_ZEROSSL_WIKI")"
return 1 return 1
fi fi
_eabresp=$(_post "email=$_email" $_ZERO_EAB_ENDPOINT) _eabresp=$(_post "email=$_email" $_ZERO_EAB_ENDPOINT)
...@@ -3530,12 +3707,15 @@ _regAccount() { ...@@ -3530,12 +3707,15 @@ _regAccount() {
_err "Can not get EAB credentials from ZeroSSL." _err "Can not get EAB credentials from ZeroSSL."
return 1 return 1
fi fi
_eab_id="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_kid"' | cut -d : -f 2 | tr -d '"')" _secure_debug2 _eabresp "$_eabresp"
_eab_id="$(echo "$_eabresp" | tr ',}' '\n\n' | grep '"eab_kid"' | cut -d : -f 2 | tr -d '"')"
_secure_debug2 _eab_id "$_eab_id"
if [ -z "$_eab_id" ]; then if [ -z "$_eab_id" ]; then
_err "Can not resolve _eab_id" _err "Can not resolve _eab_id"
return 1 return 1
fi fi
_eab_hmac_key="$(echo "$_eabresp" | tr ',}' '\n' | grep '"eab_hmac_key"' | cut -d : -f 2 | tr -d '"')" _eab_hmac_key="$(echo "$_eabresp" | tr ',}' '\n\n' | grep '"eab_hmac_key"' | cut -d : -f 2 | tr -d '"')"
_secure_debug2 _eab_hmac_key "$_eab_hmac_key"
if [ -z "$_eab_hmac_key" ]; then if [ -z "$_eab_hmac_key" ]; then
_err "Can not resolve _eab_hmac_key" _err "Can not resolve _eab_hmac_key"
return 1 return 1
...@@ -3557,7 +3737,7 @@ _regAccount() { ...@@ -3557,7 +3737,7 @@ _regAccount() {
eab_sign_t="$eab_protected64.$eab_payload64" eab_sign_t="$eab_protected64.$eab_payload64"
_debug3 eab_sign_t "$eab_sign_t" _debug3 eab_sign_t "$eab_sign_t"
key_hex="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 | _hex_dump | tr -d ' ')" key_hex="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 multi | _hex_dump | tr -d ' ')"
_debug3 key_hex "$key_hex" _debug3 key_hex "$key_hex"
eab_signature=$(printf "%s" "$eab_sign_t" | _hmac sha256 $key_hex | _base64 | _url_replace) eab_signature=$(printf "%s" "$eab_sign_t" | _hmac sha256 $key_hex | _base64 | _url_replace)
...@@ -3570,13 +3750,6 @@ _regAccount() { ...@@ -3570,13 +3750,6 @@ _regAccount() {
email_sg="\"contact\": [\"mailto:$_email\"], " email_sg="\"contact\": [\"mailto:$_email\"], "
fi fi
regjson="{$email_sg\"termsOfServiceAgreed\": true$externalBinding}" regjson="{$email_sg\"termsOfServiceAgreed\": true$externalBinding}"
else
_reg_res="$ACME_NEW_ACCOUNT_RES"
regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
if [ "$_email" ]; then
regjson='{"resource": "'$_reg_res'", "contact": ["mailto:'$_email'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
fi
fi
_info "Registering account: $ACME_DIRECTORY" _info "Registering account: $ACME_DIRECTORY"
...@@ -3631,16 +3804,6 @@ _regAccount() { ...@@ -3631,16 +3804,6 @@ _regAccount() {
updateaccount() { updateaccount() {
_initpath _initpath
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
fi
if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
_info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
fi
if [ ! -f "$ACCOUNT_KEY_PATH" ]; then if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
_err "Account key is not found at: $ACCOUNT_KEY_PATH" _err "Account key is not found at: $ACCOUNT_KEY_PATH"
return 1 return 1
...@@ -3661,25 +3824,18 @@ updateaccount() { ...@@ -3661,25 +3824,18 @@ updateaccount() {
_initAPI _initAPI
_email="$(_getAccountEmail)" _email="$(_getAccountEmail)"
if [ "$ACME_VERSION" = "2" ]; then
if [ "$ACCOUNT_EMAIL" ]; then if [ "$_email" ]; then
updjson='{"contact": ["mailto:'$_email'"]}' updjson='{"contact": ["mailto:'$_email'"]}'
else else
updjson='{"contact": []}' updjson='{"contact": []}'
fi fi
else
# ACMEv1: Updates happen the same way a registration is done.
# https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-6.3
_regAccount
return
fi
# this part handles ACMEv2 account updates.
_send_signed_request "$_accUri" "$updjson" _send_signed_request "$_accUri" "$updjson"
if [ "$code" = '200' ]; then if [ "$code" = '200' ]; then
echo "$response" >"$ACCOUNT_JSON_PATH" echo "$response" >"$ACCOUNT_JSON_PATH"
_info "account update success for $_accUri." _info "Account update success for $_accUri."
else else
_info "Error. The account was not updated." _info "Error. The account was not updated."
return 1 return 1
...@@ -3690,16 +3846,6 @@ updateaccount() { ...@@ -3690,16 +3846,6 @@ updateaccount() {
deactivateaccount() { deactivateaccount() {
_initpath _initpath
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
mv "$_OLD_ACCOUNT_KEY" "$ACCOUNT_KEY_PATH"
fi
if [ ! -f "$ACCOUNT_JSON_PATH" ] && [ -f "$_OLD_ACCOUNT_JSON" ]; then
_info "mv $_OLD_ACCOUNT_JSON to $ACCOUNT_JSON_PATH"
mv "$_OLD_ACCOUNT_JSON" "$ACCOUNT_JSON_PATH"
fi
if [ ! -f "$ACCOUNT_KEY_PATH" ]; then if [ ! -f "$ACCOUNT_KEY_PATH" ]; then
_err "Account key is not found at: $ACCOUNT_KEY_PATH" _err "Account key is not found at: $ACCOUNT_KEY_PATH"
return 1 return 1
...@@ -3719,11 +3865,8 @@ deactivateaccount() { ...@@ -3719,11 +3865,8 @@ deactivateaccount() {
fi fi
_initAPI _initAPI
if [ "$ACME_VERSION" = "2" ]; then
_djson="{\"status\":\"deactivated\"}" _djson="{\"status\":\"deactivated\"}"
else
_djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}"
fi
if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then
_info "Deactivate account success for $_accUri." _info "Deactivate account success for $_accUri."
_accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,') _accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,')
...@@ -3828,11 +3971,9 @@ __trigger_validation() { ...@@ -3828,11 +3971,9 @@ __trigger_validation() {
_debug2 _t_key_authz "$_t_key_authz" _debug2 _t_key_authz "$_t_key_authz"
_t_vtype="$3" _t_vtype="$3"
_debug2 _t_vtype "$_t_vtype" _debug2 _t_vtype "$_t_vtype"
if [ "$ACME_VERSION" = "2" ]; then
_send_signed_request "$_t_url" "{}" _send_signed_request "$_t_url" "{}"
else
_send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"type\": \"$_t_vtype\", \"keyAuthorization\": \"$_t_key_authz\"}"
fi
} }
#endpoint domain type #endpoint domain type
...@@ -3875,7 +4016,15 @@ _ns_purge_cf() { ...@@ -3875,7 +4016,15 @@ _ns_purge_cf() {
#checks if cf server is available #checks if cf server is available
_ns_is_available_cf() { _ns_is_available_cf() {
if _get "https://cloudflare-dns.com" >/dev/null 2>&1; then if _get "https://cloudflare-dns.com" "" 10 >/dev/null; then
return 0
else
return 1
fi
}
_ns_is_available_google() {
if _get "https://dns.google" "" 10 >/dev/null; then
return 0 return 0
else else
return 1 return 1
...@@ -3890,23 +4039,72 @@ _ns_lookup_google() { ...@@ -3890,23 +4039,72 @@ _ns_lookup_google() {
_ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type" _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
} }
_ns_is_available_ali() {
if _get "https://dns.alidns.com" "" 10 >/dev/null; then
return 0
else
return 1
fi
}
#domain, type #domain, type
_ns_lookup() { _ns_lookup_ali() {
_cf_ld="$1"
_cf_ld_type="$2"
_cf_ep="https://dns.alidns.com/resolve"
_ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
}
_ns_is_available_dp() {
if _get "https://doh.pub" "" 10 >/dev/null; then
return 0
else
return 1
fi
}
#dnspod
_ns_lookup_dp() {
_cf_ld="$1"
_cf_ld_type="$2"
_cf_ep="https://doh.pub/dns-query"
_ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type"
}
_ns_select_doh() {
if [ -z "$DOH_USE" ]; then if [ -z "$DOH_USE" ]; then
_debug "Detect dns server first." _debug "Detect dns server first."
if _ns_is_available_cf; then if _ns_is_available_cf; then
_debug "Use cloudflare doh server" _debug "Use cloudflare doh server"
export DOH_USE=$DOH_CLOUDFLARE export DOH_USE=$DOH_CLOUDFLARE
else elif _ns_is_available_google; then
_debug "Use google doh server" _debug "Use google doh server"
export DOH_USE=$DOH_GOOGLE export DOH_USE=$DOH_GOOGLE
elif _ns_is_available_ali; then
_debug "Use aliyun doh server"
export DOH_USE=$DOH_ALI
elif _ns_is_available_dp; then
_debug "Use dns pod doh server"
export DOH_USE=$DOH_DP
else
_err "No doh"
fi fi
fi fi
}
#domain, type
_ns_lookup() {
_ns_select_doh
if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then
_ns_lookup_cf "$@" _ns_lookup_cf "$@"
else elif [ "$DOH_USE" = "$DOH_GOOGLE" ]; then
_ns_lookup_google "$@" _ns_lookup_google "$@"
elif [ "$DOH_USE" = "$DOH_ALI" ]; then
_ns_lookup_ali "$@"
elif [ "$DOH_USE" = "$DOH_DP" ]; then
_ns_lookup_dp "$@"
else
_err "Unknown doh provider: DOH_USE=$DOH_USE"
fi fi
} }
...@@ -3919,6 +4117,7 @@ __check_txt() { ...@@ -3919,6 +4117,7 @@ __check_txt() {
_debug "_c_txtdomain" "$_c_txtdomain" _debug "_c_txtdomain" "$_c_txtdomain"
_debug "_c_aliasdomain" "$_c_aliasdomain" _debug "_c_aliasdomain" "$_c_aliasdomain"
_debug "_c_txt" "$_c_txt" _debug "_c_txt" "$_c_txt"
_ns_select_doh
_answers="$(_ns_lookup "$_c_aliasdomain" TXT)" _answers="$(_ns_lookup "$_c_aliasdomain" TXT)"
_contains "$_answers" "$_c_txt" _contains "$_answers" "$_c_txt"
...@@ -3931,7 +4130,7 @@ __purge_txt() { ...@@ -3931,7 +4130,7 @@ __purge_txt() {
if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then
_ns_purge_cf "$_p_txtdomain" "TXT" _ns_purge_cf "$_p_txtdomain" "TXT"
else else
_debug "no purge api for google dns api, just sleep 5 secs" _debug "no purge api for this doh api, just sleep 5 secs"
_sleep 5 _sleep 5
fi fi
...@@ -3944,6 +4143,8 @@ _check_dns_entries() { ...@@ -3944,6 +4143,8 @@ _check_dns_entries() {
_end_time="$(_math "$_end_time" + 1200)" #let's check no more than 20 minutes. _end_time="$(_math "$_end_time" + 1200)" #let's check no more than 20 minutes.
while [ "$(_time)" -le "$_end_time" ]; do while [ "$(_time)" -le "$_end_time" ]; do
_info "You can use '--dnssleep' to disable public dns checks."
_info "See: $_DNSCHECK_WIKI"
_left="" _left=""
for entry in $dns_entries; do for entry in $dns_entries; do
d=$(_getfield "$entry" 1) d=$(_getfield "$entry" 1)
...@@ -3991,12 +4192,42 @@ _check_dns_entries() { ...@@ -3991,12 +4192,42 @@ _check_dns_entries() {
} }
#file #file
_get_cert_issuers() { _get_chain_issuers() {
_cfile="$1" _cfile="$1"
if _contains "$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7"; then if _contains "$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -help 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 help 2>&1)" "unknown option help"; then
${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -nocrl -certfile $_cfile | ${ACME_OPENSSL_BIN:-openssl} pkcs7 -print_certs -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 ${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -nocrl -certfile $_cfile | ${ACME_OPENSSL_BIN:-openssl} pkcs7 -print_certs -text -noout | grep -i 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2
else else
${ACME_OPENSSL_BIN:-openssl} x509 -in $_cfile -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 _cindex=1
for _startn in $(grep -n -- "$BEGIN_CERT" "$_cfile" | cut -d : -f 1); do
_endn="$(grep -n -- "$END_CERT" "$_cfile" | cut -d : -f 1 | _head_n $_cindex | _tail_n 1)"
_debug2 "_startn" "$_startn"
_debug2 "_endn" "$_endn"
if [ "$DEBUG" ]; then
_debug2 "cert$_cindex" "$(sed -n "$_startn,${_endn}p" "$_cfile")"
fi
sed -n "$_startn,${_endn}p" "$_cfile" | ${ACME_OPENSSL_BIN:-openssl} x509 -text -noout | grep 'Issuer:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 | sed "s/ *\(.*\)/\1/"
_cindex=$(_math $_cindex + 1)
done
fi
}
#
_get_chain_subjects() {
_cfile="$1"
if _contains "$(${ACME_OPENSSL_BIN:-openssl} help crl2pkcs7 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -help 2>&1)" "Usage: crl2pkcs7" || _contains "$(${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 help 2>&1)" "unknown option help"; then
${ACME_OPENSSL_BIN:-openssl} crl2pkcs7 -nocrl -certfile $_cfile | ${ACME_OPENSSL_BIN:-openssl} pkcs7 -print_certs -text -noout | grep -i 'Subject:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2
else
_cindex=1
for _startn in $(grep -n -- "$BEGIN_CERT" "$_cfile" | cut -d : -f 1); do
_endn="$(grep -n -- "$END_CERT" "$_cfile" | cut -d : -f 1 | _head_n $_cindex | _tail_n 1)"
_debug2 "_startn" "$_startn"
_debug2 "_endn" "$_endn"
if [ "$DEBUG" ]; then
_debug2 "cert$_cindex" "$(sed -n "$_startn,${_endn}p" "$_cfile")"
fi
sed -n "$_startn,${_endn}p" "$_cfile" | ${ACME_OPENSSL_BIN:-openssl} x509 -text -noout | grep -i 'Subject:' | _egrep_o "CN *=[^,]*" | cut -d = -f 2 | sed "s/ *\(.*\)/\1/"
_cindex=$(_math $_cindex + 1)
done
fi fi
} }
...@@ -4004,18 +4235,85 @@ _get_cert_issuers() { ...@@ -4004,18 +4235,85 @@ _get_cert_issuers() {
_match_issuer() { _match_issuer() {
_cfile="$1" _cfile="$1"
_missuer="$2" _missuer="$2"
_fissuers="$(_get_cert_issuers $_cfile)" _fissuers="$(_get_chain_issuers $_cfile)"
_debug2 _fissuers "$_fissuers" _debug2 _fissuers "$_fissuers"
if _contains "$_fissuers" "$_missuer"; then _rootissuer="$(echo "$_fissuers" | _lower_case | _tail_n 1)"
return 0 _debug2 _rootissuer "$_rootissuer"
fi
_fissuers="$(echo "$_fissuers" | _lower_case)"
_missuer="$(echo "$_missuer" | _lower_case)" _missuer="$(echo "$_missuer" | _lower_case)"
_contains "$_fissuers" "$_missuer" _contains "$_rootissuer" "$_missuer"
} }
#webroot, domain domainlist keylength #ip
issue() { _isIPv4() {
for seg in $(echo "$1" | tr '.' ' '); do
_debug2 seg "$seg"
if [ "$(echo "$seg" | tr -d '[0-9]')" ]; then
#not all number
return 1
fi
if [ $seg -ge 0 ] && [ $seg -lt 256 ]; then
continue
fi
return 1
done
return 0
}
#ip6
_isIPv6() {
_contains "$1" ":"
}
#ip
_isIP() {
_isIPv4 "$1" || _isIPv6 "$1"
}
#identifier
_getIdType() {
if _isIP "$1"; then
echo "$ID_TYPE_IP"
else
echo "$ID_TYPE_DNS"
fi
}
# beginTime dateTo
# beginTime is full string format("2022-04-01T08:10:33Z"), beginTime can be empty, to use current time
# dateTo can be ether in full string format("2022-04-01T08:10:33Z") or in delta format(+5d or +20h)
_convertValidaty() {
_beginTime="$1"
_dateTo="$2"
_debug2 "_beginTime" "$_beginTime"
_debug2 "_dateTo" "$_dateTo"
if _startswith "$_dateTo" "+"; then
_v_begin=$(_time)
if [ "$_beginTime" ]; then
_v_begin="$(_date2time "$_beginTime")"
fi
_debug2 "_v_begin" "$_v_begin"
if _endswith "$_dateTo" "h"; then
_v_end=$(_math "$_v_begin + 60 * 60 * $(echo "$_dateTo" | tr -d '+h')")
elif _endswith "$_dateTo" "d"; then
_v_end=$(_math "$_v_begin + 60 * 60 * 24 * $(echo "$_dateTo" | tr -d '+d')")
else
_err "Not recognized format for _dateTo: $_dateTo"
return 1
fi
_debug2 "_v_end" "$_v_end"
_time2str "$_v_end"
else
if [ "$(_time)" -gt "$(_date2time "$_dateTo")" ]; then
_err "The validaty to is in the past: _dateTo = $_dateTo"
return 1
fi
echo "$_dateTo"
fi
}
#webroot, domain domainlist keylength
issue() {
if [ -z "$2" ]; then if [ -z "$2" ]; then
_usage "Usage: $PROJECT_ENTRY --issue --domain <domain.tld> --webroot <directory>" _usage "Usage: $PROJECT_ENTRY --issue --domain <domain.tld> --webroot <directory>"
return 1 return 1
...@@ -4047,10 +4345,16 @@ issue() { ...@@ -4047,10 +4345,16 @@ issue() {
_local_addr="${13}" _local_addr="${13}"
_challenge_alias="${14}" _challenge_alias="${14}"
_preferred_chain="${15}" _preferred_chain="${15}"
_valid_from="${16}"
_valid_to="${17}"
if [ -z "$_ACME_IS_RENEW" ]; then if [ -z "$_ACME_IS_RENEW" ]; then
_initpath "$_main_domain" "$_key_length" _initpath "$_main_domain" "$_key_length"
mkdir -p "$DOMAIN_PATH" mkdir -p "$DOMAIN_PATH"
elif ! _hasfield "$_web_roots" "$W_DNS"; then
Le_OrderFinalize=""
Le_LinkOrder=""
Le_LinkCert=""
fi fi
if _hasfield "$_web_roots" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then if _hasfield "$_web_roots" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
...@@ -4058,19 +4362,28 @@ issue() { ...@@ -4058,19 +4362,28 @@ issue() {
return 1 return 1
fi fi
_debug "Using ACME_DIRECTORY: $ACME_DIRECTORY"
_initAPI
if [ -f "$DOMAIN_CONF" ]; then if [ -f "$DOMAIN_CONF" ]; then
Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime) Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
_debug Le_NextRenewTime "$Le_NextRenewTime" _debug Le_NextRenewTime "$Le_NextRenewTime"
if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
_valid_to_saved=$(_readdomainconf Le_Valid_to)
if [ "$_valid_to_saved" ] && ! _startswith "$_valid_to_saved" "+"; then
_info "The domain is set to be valid to: $_valid_to_saved"
_info "It can not be renewed automatically"
_info "See: $_VALIDITY_WIKI"
return $RENEW_SKIP
fi
_saved_domain=$(_readdomainconf Le_Domain) _saved_domain=$(_readdomainconf Le_Domain)
_debug _saved_domain "$_saved_domain" _debug _saved_domain "$_saved_domain"
_saved_alt=$(_readdomainconf Le_Alt) _saved_alt=$(_readdomainconf Le_Alt)
_debug _saved_alt "$_saved_alt" _debug _saved_alt "$_saved_alt"
if [ "$_saved_domain,$_saved_alt" = "$_main_domain,$_alt_domains" ]; then _normized_saved_domains="$(echo "$_saved_domain,$_saved_alt" | tr "," "\n" | sort | tr '\n' ',')"
_debug _normized_saved_domains "$_normized_saved_domains"
_normized_domains="$(echo "$_main_domain,$_alt_domains" | tr "," "\n" | sort | tr '\n' ',')"
_debug _normized_domains "$_normized_domains"
if [ "$_normized_saved_domains" = "$_normized_domains" ]; then
_info "Domains not changed." _info "Domains not changed."
_info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")" _info "Skip, Next renewal time is: $(__green "$(_readdomainconf Le_NextRenewTimeStr)")"
_info "Add '$(__red '--force')' to force to renew." _info "Add '$(__red '--force')' to force to renew."
...@@ -4081,6 +4394,11 @@ issue() { ...@@ -4081,6 +4394,11 @@ issue() {
fi fi
fi fi
_debug "Using ACME_DIRECTORY: $ACME_DIRECTORY"
if ! _initAPI; then
return 1
fi
_savedomainconf "Le_Domain" "$_main_domain" _savedomainconf "Le_Domain" "$_main_domain"
_savedomainconf "Le_Alt" "$_alt_domains" _savedomainconf "Le_Alt" "$_alt_domains"
_savedomainconf "Le_Webroot" "$_web_roots" _savedomainconf "Le_Webroot" "$_web_roots"
...@@ -4113,10 +4431,6 @@ issue() { ...@@ -4113,10 +4431,6 @@ issue() {
_alt_domains="" _alt_domains=""
fi fi
if [ "$_key_length" = "$NO_VALUE" ]; then
_key_length=""
fi
if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
_err "_on_before_issue." _err "_on_before_issue."
return 1 return 1
...@@ -4134,12 +4448,24 @@ issue() { ...@@ -4134,12 +4448,24 @@ issue() {
_debug "_saved_account_key_hash is not changed, skip register account." _debug "_saved_account_key_hash is not changed, skip register account."
fi fi
export Le_Next_Domain_Key="$CERT_KEY_PATH.next"
if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ]; then if [ -f "$CSR_PATH" ] && [ ! -f "$CERT_KEY_PATH" ]; then
_info "Signing from existing CSR." _info "Signing from existing CSR."
else else
# When renewing from an old version, the empty Le_Keylength means 2048.
# Note, do not use DEFAULT_DOMAIN_KEY_LENGTH as that value may change over
# time but an empty value implies 2048 specifically.
_key=$(_readdomainconf Le_Keylength) _key=$(_readdomainconf Le_Keylength)
if [ -z "$_key" ]; then
_key=2048
fi
_debug "Read key length:$_key" _debug "Read key length:$_key"
if [ ! -f "$CERT_KEY_PATH" ] || [ "$_key_length" != "$_key" ] || [ "$Le_ForceNewDomainKey" = "1" ]; then if [ ! -f "$CERT_KEY_PATH" ] || [ "$_key_length" != "$_key" ] || [ "$Le_ForceNewDomainKey" = "1" ]; then
if [ "$Le_ForceNewDomainKey" = "1" ] && [ -f "$Le_Next_Domain_Key" ]; then
_info "Using pre generated key: $Le_Next_Domain_Key"
cat "$Le_Next_Domain_Key" >"$CERT_KEY_PATH"
echo "" >"$Le_Next_Domain_Key"
else
if ! createDomainKey "$_main_domain" "$_key_length"; then if ! createDomainKey "$_main_domain" "$_key_length"; then
_err "Create domain key error." _err "Create domain key error."
_clearup _clearup
...@@ -4147,7 +4473,18 @@ issue() { ...@@ -4147,7 +4473,18 @@ issue() {
return 1 return 1
fi fi
fi fi
fi
if [ "$Le_ForceNewDomainKey" ]; then
_info "Generate next pre-generate key."
if [ ! -e "$Le_Next_Domain_Key" ]; then
touch "$Le_Next_Domain_Key"
chmod 600 "$Le_Next_Domain_Key"
fi
if ! _createkey "$_key_length" "$Le_Next_Domain_Key"; then
_err "Can not pre generate domain key"
return 1
fi
fi
if ! _createcsr "$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then if ! _createcsr "$_main_domain" "$_alt_domains" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"; then
_err "Create CSR error." _err "Create CSR error."
_clearup _clearup
...@@ -4164,9 +4501,8 @@ issue() { ...@@ -4164,9 +4501,8 @@ issue() {
sep='#' sep='#'
dvsep=',' dvsep=','
if [ -z "$vlist" ]; then if [ -z "$vlist" ]; then
if [ "$ACME_VERSION" = "2" ]; then
#make new order request #make new order request
_identifiers="{\"type\":\"dns\",\"value\":\"$(_idn "$_main_domain")\"}" _identifiers="{\"type\":\"$(_getIdType "$_main_domain")\",\"value\":\"$(_idn "$_main_domain")\"}"
_w_index=1 _w_index=1
while true; do while true; do
d="$(echo "$_alt_domains," | cut -d , -f "$_w_index")" d="$(echo "$_alt_domains," | cut -d , -f "$_w_index")"
...@@ -4175,15 +4511,55 @@ issue() { ...@@ -4175,15 +4511,55 @@ issue() {
if [ -z "$d" ]; then if [ -z "$d" ]; then
break break
fi fi
_identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$(_idn "$d")\"}" _identifiers="$_identifiers,{\"type\":\"$(_getIdType "$d")\",\"value\":\"$(_idn "$d")\"}"
done done
_debug2 _identifiers "$_identifiers" _debug2 _identifiers "$_identifiers"
if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then _notBefore=""
_notAfter=""
if [ "$_valid_from" ]; then
_savedomainconf "Le_Valid_From" "$_valid_from"
_debug2 "_valid_from" "$_valid_from"
_notBefore="$(_convertValidaty "" "$_valid_from")"
if [ "$?" != "0" ]; then
_err "Can not parse _valid_from: $_valid_from"
return 1
fi
if [ "$(_time)" -gt "$(_date2time "$_notBefore")" ]; then
_notBefore=""
fi
else
_cleardomainconf "Le_Valid_From"
fi
_debug2 _notBefore "$_notBefore"
if [ "$_valid_to" ]; then
_debug2 "_valid_to" "$_valid_to"
_savedomainconf "Le_Valid_To" "$_valid_to"
_notAfter="$(_convertValidaty "$_notBefore" "$_valid_to")"
if [ "$?" != "0" ]; then
_err "Can not parse _valid_to: $_valid_to"
return 1
fi
else
_cleardomainconf "Le_Valid_To"
fi
_debug2 "_notAfter" "$_notAfter"
_newOrderObj="{\"identifiers\": [$_identifiers]"
if [ "$_notBefore" ]; then
_newOrderObj="$_newOrderObj,\"notBefore\": \"$_notBefore\""
fi
if [ "$_notAfter" ]; then
_newOrderObj="$_newOrderObj,\"notAfter\": \"$_notAfter\""
fi
if ! _send_signed_request "$ACME_NEW_ORDER" "$_newOrderObj}"; then
_err "Create new order error." _err "Create new order error."
_clearup _clearup
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)" Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)"
_debug Le_LinkOrder "$Le_LinkOrder" _debug Le_LinkOrder "$Le_LinkOrder"
Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)" Le_OrderFinalize="$(echo "$response" | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)"
...@@ -4222,16 +4598,16 @@ issue() { ...@@ -4222,16 +4598,16 @@ issue() {
response="$(echo "$response" | _normalizeJson)" response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response" _debug2 response "$response"
_d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')" _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2- | tr -d ' "')"
if _contains "$response" "\"wildcard\" *: *true"; then if _contains "$response" "\"wildcard\" *: *true"; then
_d="*.$_d" _d="*.$_d"
fi fi
_debug2 _d "$_d" _debug2 _d "$_d"
_authorizations_map="$_d,$response _authorizations_map="$_d,$response#$_authz_url
$_authorizations_map" $_authorizations_map"
done done
_debug2 _authorizations_map "$_authorizations_map" _debug2 _authorizations_map "$_authorizations_map"
fi
_index=0 _index=0
_currentRoot="" _currentRoot=""
...@@ -4262,7 +4638,6 @@ $_authorizations_map" ...@@ -4262,7 +4638,6 @@ $_authorizations_map"
vtype="$VTYPE_ALPN" vtype="$VTYPE_ALPN"
fi fi
if [ "$ACME_VERSION" = "2" ]; then
_idn_d="$(_idn "$d")" _idn_d="$(_idn "$d")"
_candidates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")" _candidates="$(echo "$_authorizations_map" | grep -i "^$_idn_d,")"
_debug2 _candidates "$_candidates" _debug2 _candidates "$_candidates"
...@@ -4283,31 +4658,24 @@ $_authorizations_map" ...@@ -4283,31 +4658,24 @@ $_authorizations_map"
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
else _authz_url="$(echo "$_candidates" | sed "s/$_idn_d,//" | _egrep_o "#.*" | sed "s/^#//")"
if ! __get_domain_new_authz "$d"; then _debug _authz_url "$_authz_url"
_clearup
_on_issue_err "$_post_hook"
return 1
fi
fi
if [ -z "$thumbprint" ]; then if [ -z "$thumbprint" ]; then
thumbprint="$(__calc_account_thumbprint)" thumbprint="$(__calc_account_thumbprint)"
fi fi
entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
keyauthorization="" keyauthorization=""
if [ -z "$entry" ]; then
if ! _startswith "$d" '*.'; then
_debug "Not a wildcard domain, lets check whether the validation is already valid."
if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
_debug "$d is already valid." _debug "$d is already valid."
keyauthorization="$STATE_VERIFIED" keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization" _debug keyauthorization "$keyauthorization"
fi fi
fi
if [ -z "$keyauthorization" ]; then entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$keyauthorization" -a -z "$entry" ]; then
_err "Error, can not get domain token entry $d for $vtype" _err "Error, can not get domain token entry $d for $vtype"
_supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')" _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
if [ "$_supported_vtypes" ]; then if [ "$_supported_vtypes" ]; then
...@@ -4317,7 +4685,6 @@ $_authorizations_map" ...@@ -4317,7 +4685,6 @@ $_authorizations_map"
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
fi
if [ -z "$keyauthorization" ]; then if [ -z "$keyauthorization" ]; then
token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" token="$(echo "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
...@@ -4329,11 +4696,9 @@ $_authorizations_map" ...@@ -4329,11 +4696,9 @@ $_authorizations_map"
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
if [ "$ACME_VERSION" = "2" ]; then
uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)" uri="$(echo "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
else
uri="$(echo "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
fi
_debug uri "$uri" _debug uri "$uri"
if [ -z "$uri" ]; then if [ -z "$uri" ]; then
...@@ -4344,15 +4709,9 @@ $_authorizations_map" ...@@ -4344,15 +4709,9 @@ $_authorizations_map"
fi fi
keyauthorization="$token.$thumbprint" keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization" _debug keyauthorization "$keyauthorization"
if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
_debug "$d is already verified."
keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization"
fi
fi fi
dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot" dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot$sep$_authz_url"
_debug dvlist "$dvlist" _debug dvlist "$dvlist"
vlist="$vlist$dvlist$dvsep" vlist="$vlist$dvlist$dvsep"
...@@ -4369,6 +4728,7 @@ $_authorizations_map" ...@@ -4369,6 +4728,7 @@ $_authorizations_map"
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2) keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4) vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
_authz_url=$(echo "$ventry" | cut -d "$sep" -f 6)
_debug d "$d" _debug d "$d"
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_debug "$d is already verified, skip $vtype." _debug "$d is already verified, skip $vtype."
...@@ -4383,6 +4743,7 @@ $_authorizations_map" ...@@ -4383,6 +4743,7 @@ $_authorizations_map"
_dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')" _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
fi fi
_d_alias="$(_getfield "$_challenge_alias" "$_alias_index")" _d_alias="$(_getfield "$_challenge_alias" "$_alias_index")"
test "$_d_alias" = "$NO_VALUE" && _d_alias=""
_alias_index="$(_math "$_alias_index" + 1)" _alias_index="$(_math "$_alias_index" + 1)"
_debug "_d_alias" "$_d_alias" _debug "_d_alias" "$_d_alias"
if [ "$_d_alias" ]; then if [ "$_d_alias" ]; then
...@@ -4458,7 +4819,9 @@ $_authorizations_map" ...@@ -4458,7 +4819,9 @@ $_authorizations_map"
_err "Please add the TXT records to the domains, and re-run with --renew." _err "Please add the TXT records to the domains, and re-run with --renew."
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
_clearup _clearup
return 1 # If asked to be in manual DNS mode, flag this exit with a separate
# error so it can be distinguished from other failures.
return $CODE_DNS_MANUAL
fi fi
fi fi
...@@ -4491,7 +4854,7 @@ $_authorizations_map" ...@@ -4491,7 +4854,7 @@ $_authorizations_map"
uri=$(echo "$ventry" | cut -d "$sep" -f 3) uri=$(echo "$ventry" | cut -d "$sep" -f 3)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4) vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5) _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
_authz_url=$(echo "$ventry" | cut -d "$sep" -f 6)
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_info "$d is already verified, skip $vtype." _info "$d is already verified, skip $vtype."
continue continue
...@@ -4501,6 +4864,7 @@ $_authorizations_map" ...@@ -4501,6 +4864,7 @@ $_authorizations_map"
_debug "d" "$d" _debug "d" "$d"
_debug "keyauthorization" "$keyauthorization" _debug "keyauthorization" "$keyauthorization"
_debug "uri" "$uri" _debug "uri" "$uri"
_debug "_authz_url" "$_authz_url"
removelevel="" removelevel=""
token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)" token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
...@@ -4567,20 +4931,10 @@ $_authorizations_map" ...@@ -4567,20 +4931,10 @@ $_authorizations_map"
_on_issue_err "$_post_hook" "$vlist" _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
if ! chmod a+r "$wellknown_path/$token"; then
if [ ! "$usingApache" ]; then _debug "chmod failed, but we just continue."
if webroot_owner=$(_stat "$_currentRoot"); then
_debug "Changing owner/group of .well-known to $webroot_owner"
if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then
_debug "$(cat "$_EXEC_TEMP_ERR")"
_exec_err >/dev/null 2>&1
fi
else
_debug "not changing owner/group of webroot"
fi fi
fi fi
fi
elif [ "$vtype" = "$VTYPE_ALPN" ]; then elif [ "$vtype" = "$VTYPE_ALPN" ]; then
acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")" acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
_debug acmevalidationv1 "$acmevalidationv1" _debug acmevalidationv1 "$acmevalidationv1"
...@@ -4618,6 +4972,7 @@ $_authorizations_map" ...@@ -4618,6 +4972,7 @@ $_authorizations_map"
MAX_RETRY_TIMES=30 MAX_RETRY_TIMES=30
fi fi
_debug "Lets check the status of the authz"
while true; do while true; do
waittimes=$(_math "$waittimes" + 1) waittimes=$(_math "$waittimes" + 1)
if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ]; then if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ]; then
...@@ -4628,36 +4983,14 @@ $_authorizations_map" ...@@ -4628,36 +4983,14 @@ $_authorizations_map"
return 1 return 1
fi fi
_debug "sleep 2 secs to verify"
sleep 2
_debug "checking"
if [ "$ACME_VERSION" = "2" ]; then
_send_signed_request "$uri"
else
response="$(_get "$uri")"
fi
if [ "$?" != "0" ]; then
_err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
_on_issue_err "$_post_hook" "$vlist"
return 1
fi
_debug2 original "$response" _debug2 original "$response"
response="$(echo "$response" | _normalizeJson)" response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response" _debug2 response "$response"
status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"') status=$(echo "$response" | _egrep_o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
if [ "$status" = "valid" ]; then _debug2 status "$status"
_info "$(__green Success)" if _contains "$status" "invalid"; then
_stopserver "$serverproc"
serverproc=""
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
break
fi
if [ "$status" = "invalid" ]; then
error="$(echo "$response" | _egrep_o '"error":\{[^\}]*')" error="$(echo "$response" | _egrep_o '"error":\{[^\}]*')"
_debug2 error "$error" _debug2 error "$error"
errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)" errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
...@@ -4679,10 +5012,18 @@ $_authorizations_map" ...@@ -4679,10 +5012,18 @@ $_authorizations_map"
return 1 return 1
fi fi
if [ "$status" = "pending" ]; then if _contains "$status" "valid"; then
_info "Pending" _info "$(__green Success)"
elif [ "$status" = "processing" ]; then _stopserver "$serverproc"
_info "Processing" serverproc=""
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
break
fi
if _contains "$status" "pending"; then
_info "Pending, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
elif _contains "$status" "processing"; then
_info "Processing, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
else else
_err "$d:Verify error:$response" _err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
...@@ -4690,7 +5031,19 @@ $_authorizations_map" ...@@ -4690,7 +5031,19 @@ $_authorizations_map"
_on_issue_err "$_post_hook" "$vlist" _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
_debug "sleep 2 secs to verify again"
_sleep 2
_debug "checking"
_send_signed_request "$_authz_url"
if [ "$?" != "0" ]; then
_err "$d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
_on_issue_err "$_post_hook" "$vlist"
return 1
fi
done done
done done
...@@ -4699,7 +5052,6 @@ $_authorizations_map" ...@@ -4699,7 +5052,6 @@ $_authorizations_map"
_info "Verify finished, start to sign." _info "Verify finished, start to sign."
der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)" der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
if [ "$ACME_VERSION" = "2" ]; then
_info "Lets finalize the order." _info "Lets finalize the order."
_info "Le_OrderFinalize" "$Le_OrderFinalize" _info "Le_OrderFinalize" "$Le_OrderFinalize"
if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then
...@@ -4783,8 +5135,13 @@ $_authorizations_map" ...@@ -4783,8 +5135,13 @@ $_authorizations_map"
echo "$response" >"$CERT_PATH" echo "$response" >"$CERT_PATH"
_split_cert_chain "$CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CA_CERT_PATH" _split_cert_chain "$CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CA_CERT_PATH"
if [ -z "$_preferred_chain" ]; then
_preferred_chain=$(_readcaconf DEFAULT_PREFERRED_CHAIN)
fi
if [ "$_preferred_chain" ] && [ -f "$CERT_FULLCHAIN_PATH" ]; then if [ "$_preferred_chain" ] && [ -f "$CERT_FULLCHAIN_PATH" ]; then
if [ "$DEBUG" ]; then
_debug "default chain issuers: " "$(_get_chain_issuers "$CERT_FULLCHAIN_PATH")"
fi
if ! _match_issuer "$CERT_FULLCHAIN_PATH" "$_preferred_chain"; then if ! _match_issuer "$CERT_FULLCHAIN_PATH" "$_preferred_chain"; then
rels="$(echo "$responseHeaders" | tr -d ' <>' | grep -i "^link:" | grep -i 'rel="alternate"' | cut -d : -f 2- | cut -d ';' -f 1)" rels="$(echo "$responseHeaders" | tr -d ' <>' | grep -i "^link:" | grep -i 'rel="alternate"' | cut -d : -f 2- | cut -d ';' -f 1)"
_debug2 "rels" "$rels" _debug2 "rels" "$rels"
...@@ -4800,38 +5157,25 @@ $_authorizations_map" ...@@ -4800,38 +5157,25 @@ $_authorizations_map"
_relca="$CA_CERT_PATH.alt" _relca="$CA_CERT_PATH.alt"
echo "$response" >"$_relcert" echo "$response" >"$_relcert"
_split_cert_chain "$_relcert" "$_relfullchain" "$_relca" _split_cert_chain "$_relcert" "$_relfullchain" "$_relca"
if [ "$DEBUG" ]; then
_debug "rel chain issuers: " "$(_get_chain_issuers "$_relfullchain")"
fi
if _match_issuer "$_relfullchain" "$_preferred_chain"; then if _match_issuer "$_relfullchain" "$_preferred_chain"; then
_info "Matched issuer in: $rel" _info "Matched issuer in: $rel"
cat $_relcert >"$CERT_PATH" cat $_relcert >"$CERT_PATH"
cat $_relfullchain >"$CERT_FULLCHAIN_PATH" cat $_relfullchain >"$CERT_FULLCHAIN_PATH"
cat $_relca >"$CA_CERT_PATH" cat $_relca >"$CA_CERT_PATH"
rm -f "$_relcert"
rm -f "$_relfullchain"
rm -f "$_relca"
break break
fi fi
rm -f "$_relcert"
rm -f "$_relfullchain"
rm -f "$_relca"
done done
fi fi
fi fi
else
if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
_err "Sign failed. $response"
_on_issue_err "$_post_hook"
return 1
fi
_rcert="$response"
Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _tail_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
echo "$BEGIN_CERT" >"$CERT_PATH"
#if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
# _debug "Get cert failed. Let's try last response."
# printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
#fi
if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
_debug "Try cert link."
_get "$Le_LinkCert" | _base64 "multiline" >>"$CERT_PATH"
fi
echo "$END_CERT" >>"$CERT_PATH"
fi
_debug "Le_LinkCert" "$Le_LinkCert" _debug "Le_LinkCert" "$Le_LinkCert"
_savedomainconf "Le_LinkCert" "$Le_LinkCert" _savedomainconf "Le_LinkCert" "$Le_LinkCert"
...@@ -4847,10 +5191,10 @@ $_authorizations_map" ...@@ -4847,10 +5191,10 @@ $_authorizations_map"
_info "$(__green "Cert success.")" _info "$(__green "Cert success.")"
cat "$CERT_PATH" cat "$CERT_PATH"
_info "Your cert is in $(__green " $CERT_PATH ")" _info "Your cert is in: $(__green "$CERT_PATH")"
if [ -f "$CERT_KEY_PATH" ]; then if [ -f "$CERT_KEY_PATH" ]; then
_info "Your cert key is in $(__green " $CERT_KEY_PATH ")" _info "Your cert key is in: $(__green "$CERT_KEY_PATH")"
fi fi
if [ ! "$USER_PATH" ] || [ ! "$_ACME_IN_CRON" ]; then if [ ! "$USER_PATH" ] || [ ! "$_ACME_IN_CRON" ]; then
...@@ -4859,60 +5203,16 @@ $_authorizations_map" ...@@ -4859,60 +5203,16 @@ $_authorizations_map"
fi fi
fi fi
if [ "$ACME_VERSION" = "2" ]; then [ -f "$CA_CERT_PATH" ] && _info "The intermediate CA cert is in: $(__green "$CA_CERT_PATH")"
_debug "v2 chain." [ -f "$CERT_FULLCHAIN_PATH" ] && _info "And the full chain certs is there: $(__green "$CERT_FULLCHAIN_PATH")"
else if [ "$Le_ForceNewDomainKey" ] && [ -e "$Le_Next_Domain_Key" ]; then
cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH" _info "Your pre-generated next key for future cert key change is in: $(__green "$Le_Next_Domain_Key")"
Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
if [ "$Le_LinkIssuer" ]; then
if ! _contains "$Le_LinkIssuer" ":"; then
_info "$(__red "Relative issuer link found.")"
Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer"
fi
_debug Le_LinkIssuer "$Le_LinkIssuer"
_savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
_link_issuer_retry=0
_MAX_ISSUER_RETRY=5
while [ "$_link_issuer_retry" -lt "$_MAX_ISSUER_RETRY" ]; do
_debug _link_issuer_retry "$_link_issuer_retry"
if [ "$ACME_VERSION" = "2" ]; then
if _send_signed_request "$Le_LinkIssuer"; then
echo "$response" >"$CA_CERT_PATH"
break
fi
else
if _get "$Le_LinkIssuer" >"$CA_CERT_PATH.der"; then
echo "$BEGIN_CERT" >"$CA_CERT_PATH"
_base64 "multiline" <"$CA_CERT_PATH.der" >>"$CA_CERT_PATH"
echo "$END_CERT" >>"$CA_CERT_PATH"
if ! _checkcert "$CA_CERT_PATH"; then
_err "Can not get the ca cert."
break
fi
cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
rm -f "$CA_CERT_PATH.der"
break
fi
fi
_link_issuer_retry=$(_math $_link_issuer_retry + 1)
_sleep "$_link_issuer_retry"
done
if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then
_err "Max retry for issuer ca cert is reached."
fi
else
_debug "No Le_LinkIssuer header found."
fi fi
fi
[ -f "$CA_CERT_PATH" ] && _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
[ -f "$CERT_FULLCHAIN_PATH" ] && _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
Le_CertCreateTime=$(_time) Le_CertCreateTime=$(_time)
_savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime" _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
Le_CertCreateTimeStr=$(date -u) Le_CertCreateTimeStr=$(_time2str "$Le_CertCreateTime")
_savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr" _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ]; then if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ]; then
...@@ -4952,13 +5252,34 @@ $_authorizations_map" ...@@ -4952,13 +5252,34 @@ $_authorizations_map"
else else
_cleardomainconf Le_ForceNewDomainKey _cleardomainconf Le_ForceNewDomainKey
fi fi
if [ "$_notAfter" ]; then
Le_NextRenewTime=$(_date2time "$_notAfter")
Le_NextRenewTimeStr="$_notAfter"
if [ "$_valid_to" ] && ! _startswith "$_valid_to" "+"; then
_info "The domain is set to be valid to: $_valid_to"
_info "It can not be renewed automatically"
_info "See: $_VALIDITY_WIKI"
else
_now=$(_time)
_debug2 "_now" "$_now"
_lifetime=$(_math $Le_NextRenewTime - $_now)
_debug2 "_lifetime" "$_lifetime"
if [ $_lifetime -gt 86400 ]; then
#if lifetime is logner than one day, it will renew one day before
Le_NextRenewTime=$(_math $Le_NextRenewTime - 86400)
Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
else
#if lifetime is less than 24 hours, it will renew one hour before
Le_NextRenewTime=$(_math $Le_NextRenewTime - 3600)
Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
fi
fi
else
Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60) Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime") Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
fi
_savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr" _savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
_savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime" _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
...@@ -4994,18 +5315,21 @@ _split_cert_chain() { ...@@ -4994,18 +5315,21 @@ _split_cert_chain() {
fi fi
} }
#domain [isEcc] #domain [isEcc] [server]
renew() { renew() {
Le_Domain="$1" Le_Domain="$1"
if [ -z "$Le_Domain" ]; then if [ -z "$Le_Domain" ]; then
_usage "Usage: $PROJECT_ENTRY --renew --domain <domain.tld> [--ecc]" _usage "Usage: $PROJECT_ENTRY --renew --domain <domain.tld> [--ecc] [--server server]"
return 1 return 1
fi fi
_isEcc="$2" _isEcc="$2"
_renewServer="$3"
_debug "_renewServer" "$_renewServer"
_initpath "$Le_Domain" "$_isEcc" _initpath "$Le_Domain" "$_isEcc"
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
_info "$(__green "Renew: '$Le_Domain'")" _info "$(__green "Renew: '$Le_Domain'")"
if [ ! -f "$DOMAIN_CONF" ]; then if [ ! -f "$DOMAIN_CONF" ]; then
_info "'$Le_Domain' is not an issued domain, skip." _info "'$Le_Domain' is not an issued domain, skip."
...@@ -5019,28 +5343,42 @@ renew() { ...@@ -5019,28 +5343,42 @@ renew() {
. "$DOMAIN_CONF" . "$DOMAIN_CONF"
_debug Le_API "$Le_API" _debug Le_API "$Le_API"
if [ "$Le_API" = "$LETSENCRYPT_CA_V1" ]; then case "$Le_API" in
_cleardomainconf Le_API "$CA_LETSENCRYPT_V2_TEST")
Le_API="$DEFAULT_CA" _info "Switching back to $CA_LETSENCRYPT_V2"
fi Le_API="$CA_LETSENCRYPT_V2"
if [ "$Le_API" = "$LETSENCRYPT_STAGING_CA_V1" ]; then ;;
_cleardomainconf Le_API "$CA_BUYPASS_TEST")
Le_API="$DEFAULT_STAGING_CA" _info "Switching back to $CA_BUYPASS"
Le_API="$CA_BUYPASS"
;;
"$CA_GOOGLE_TEST")
_info "Switching back to $CA_GOOGLE"
Le_API="$CA_GOOGLE"
;;
esac
if [ "$_server" ]; then
Le_API="$_server"
fi fi
_info "Renew to Le_API=$Le_API"
if [ "$Le_API" ]; then _clearAPI
_clearCA
export ACME_DIRECTORY="$Le_API" export ACME_DIRECTORY="$Le_API"
#reload ca configs #reload ca configs
ACCOUNT_KEY_PATH="" _debug2 "initpath again."
ACCOUNT_JSON_PATH=""
CA_CONF=""
_debug3 "initpath again."
_initpath "$Le_Domain" "$_isEcc" _initpath "$Le_Domain" "$_isEcc"
fi
if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then
_info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")" _info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")"
_info "Add '$(__red '--force')' to force to renew." _info "Add '$(__red '--force')' to force to renew."
if [ -z "$_ACME_IN_RENEWALL" ]; then
if [ $_set_level -ge $NOTIFY_LEVEL_SKIP ]; then
_send_notify "Renew $Le_Domain skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP"
fi
fi
return "$RENEW_SKIP" return "$RENEW_SKIP"
fi fi
...@@ -5055,7 +5393,14 @@ renew() { ...@@ -5055,7 +5393,14 @@ renew() {
Le_PostHook="$(_readdomainconf Le_PostHook)" Le_PostHook="$(_readdomainconf Le_PostHook)"
Le_RenewHook="$(_readdomainconf Le_RenewHook)" Le_RenewHook="$(_readdomainconf Le_RenewHook)"
Le_Preferred_Chain="$(_readdomainconf Le_Preferred_Chain)" Le_Preferred_Chain="$(_readdomainconf Le_Preferred_Chain)"
issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" "$Le_ChallengeAlias" "$Le_Preferred_Chain" # When renewing from an old version, the empty Le_Keylength means 2048.
# Note, do not use DEFAULT_DOMAIN_KEY_LENGTH as that value may change over
# time but an empty value implies 2048 specifically.
Le_Keylength="$(_readdomainconf Le_Keylength)"
if [ -z "$Le_Keylength" ]; then
Le_Keylength=2048
fi
issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" "$Le_PreHook" "$Le_PostHook" "$Le_RenewHook" "$Le_LocalAddress" "$Le_ChallengeAlias" "$Le_Preferred_Chain" "$Le_Valid_From" "$Le_Valid_To"
res="$?" res="$?"
if [ "$res" != "0" ]; then if [ "$res" != "0" ]; then
return "$res" return "$res"
...@@ -5067,15 +5412,31 @@ renew() { ...@@ -5067,15 +5412,31 @@ renew() {
fi fi
_ACME_IS_RENEW="" _ACME_IS_RENEW=""
if [ -z "$_ACME_IN_RENEWALL" ]; then
if [ "$res" = "0" ]; then
if [ $_set_level -ge $NOTIFY_LEVEL_RENEW ]; then
_send_notify "Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0
fi
else
if [ $_set_level -ge $NOTIFY_LEVEL_ERROR ]; then
_send_notify "Renew $d error" "There is an error." "$NOTIFY_HOOK" 1
fi
fi
fi
return "$res" return "$res"
} }
#renewAll [stopRenewOnError] #renewAll [stopRenewOnError] [server]
renewAll() { renewAll() {
_initpath _initpath
_clearCA
_stopRenewOnError="$1" _stopRenewOnError="$1"
_debug "_stopRenewOnError" "$_stopRenewOnError" _debug "_stopRenewOnError" "$_stopRenewOnError"
_server="$2"
_debug "_server" "$_server"
_ret="0" _ret="0"
_success_msg="" _success_msg=""
_error_msg="" _error_msg=""
...@@ -5084,6 +5445,7 @@ renewAll() { ...@@ -5084,6 +5445,7 @@ renewAll() {
_notify_code=$RENEW_SKIP _notify_code=$RENEW_SKIP
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT} _set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
_debug "_set_level" "$_set_level" _debug "_set_level" "$_set_level"
export _ACME_IN_RENEWALL=1
for di in "${CERT_HOME}"/*.*/; do for di in "${CERT_HOME}"/*.*/; do
_debug di "$di" _debug di "$di"
if ! [ -d "$di" ]; then if ! [ -d "$di" ]; then
...@@ -5097,7 +5459,7 @@ renewAll() { ...@@ -5097,7 +5459,7 @@ renewAll() {
_isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2) _isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2)
d=$(echo "$d" | cut -d "$ECC_SEP" -f 1) d=$(echo "$d" | cut -d "$ECC_SEP" -f 1)
fi fi
renew "$d" "$_isEcc" renew "$d" "$_isEcc" "$_server"
) )
rc="$?" rc="$?"
_debug "Return code: $rc" _debug "Return code: $rc"
...@@ -5106,13 +5468,13 @@ renewAll() { ...@@ -5106,13 +5468,13 @@ renewAll() {
_error_level="$NOTIFY_LEVEL_RENEW" _error_level="$NOTIFY_LEVEL_RENEW"
_notify_code=0 _notify_code=0
fi fi
if [ "$_ACME_IN_CRON" ]; then
if [ $_set_level -ge $NOTIFY_LEVEL_RENEW ]; then if [ $_set_level -ge $NOTIFY_LEVEL_RENEW ]; then
if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
_send_notify "Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0 _send_notify "Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0
fi fi
fi fi
fi
_success_msg="${_success_msg} $d _success_msg="${_success_msg} $d
" "
elif [ "$rc" = "$RENEW_SKIP" ]; then elif [ "$rc" = "$RENEW_SKIP" ]; then
...@@ -5120,13 +5482,13 @@ renewAll() { ...@@ -5120,13 +5482,13 @@ renewAll() {
_error_level="$NOTIFY_LEVEL_SKIP" _error_level="$NOTIFY_LEVEL_SKIP"
_notify_code=$RENEW_SKIP _notify_code=$RENEW_SKIP
fi fi
if [ "$_ACME_IN_CRON" ]; then
if [ $_set_level -ge $NOTIFY_LEVEL_SKIP ]; then if [ $_set_level -ge $NOTIFY_LEVEL_SKIP ]; then
if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
_send_notify "Renew $d skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP" _send_notify "Renew $d skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP"
fi fi
fi fi
fi
_info "Skipped $d" _info "Skipped $d"
_skipped_msg="${_skipped_msg} $d _skipped_msg="${_skipped_msg} $d
" "
...@@ -5135,13 +5497,13 @@ renewAll() { ...@@ -5135,13 +5497,13 @@ renewAll() {
_error_level="$NOTIFY_LEVEL_ERROR" _error_level="$NOTIFY_LEVEL_ERROR"
_notify_code=1 _notify_code=1
fi fi
if [ "$_ACME_IN_CRON" ]; then
if [ $_set_level -ge $NOTIFY_LEVEL_ERROR ]; then if [ $_set_level -ge $NOTIFY_LEVEL_ERROR ]; then
if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then
_send_notify "Renew $d error" "There is an error." "$NOTIFY_HOOK" 1 _send_notify "Renew $d error" "There is an error." "$NOTIFY_HOOK" 1
fi fi
fi fi
fi
_error_msg="${_error_msg} $d _error_msg="${_error_msg} $d
" "
if [ "$_stopRenewOnError" ]; then if [ "$_stopRenewOnError" ]; then
...@@ -5156,7 +5518,7 @@ renewAll() { ...@@ -5156,7 +5518,7 @@ renewAll() {
done done
_debug _error_level "$_error_level" _debug _error_level "$_error_level"
_debug _set_level "$_set_level" _debug _set_level "$_set_level"
if [ "$_ACME_IN_CRON" ] && [ $_error_level -le $_set_level ]; then if [ $_error_level -le $_set_level ]; then
if [ -z "$NOTIFY_MODE" ] || [ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then if [ -z "$NOTIFY_MODE" ] || [ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then
_msg_subject="Renew" _msg_subject="Renew"
if [ "$_error_msg" ]; then if [ "$_error_msg" ]; then
...@@ -5204,6 +5566,7 @@ signcsr() { ...@@ -5204,6 +5566,7 @@ signcsr() {
_renew_hook="${10}" _renew_hook="${10}"
_local_addr="${11}" _local_addr="${11}"
_challenge_alias="${12}" _challenge_alias="${12}"
_preferred_chain="${13}"
_csrsubj=$(_readSubjectFromCSR "$_csrfile") _csrsubj=$(_readSubjectFromCSR "$_csrfile")
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
...@@ -5241,16 +5604,13 @@ signcsr() { ...@@ -5241,16 +5604,13 @@ signcsr() {
return 1 return 1
fi fi
if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then
export ACME_VERSION=2
fi
_initpath "$_csrsubj" "$_csrkeylength" _initpath "$_csrsubj" "$_csrkeylength"
mkdir -p "$DOMAIN_PATH" mkdir -p "$DOMAIN_PATH"
_info "Copy csr to: $CSR_PATH" _info "Copy csr to: $CSR_PATH"
cp "$_csrfile" "$CSR_PATH" cp "$_csrfile" "$CSR_PATH"
issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias" issue "$_csrW" "$_csrsubj" "$_csrdomainlist" "$_csrkeylength" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_addr" "$_challenge_alias" "$_preferred_chain"
} }
...@@ -5265,10 +5625,13 @@ showcsr() { ...@@ -5265,10 +5625,13 @@ showcsr() {
_initpath _initpath
_csrsubj=$(_readSubjectFromCSR "$_csrfile") _csrsubj=$(_readSubjectFromCSR "$_csrfile")
if [ "$?" != "0" ] || [ -z "$_csrsubj" ]; then if [ "$?" != "0" ]; then
_err "Can not read subject from csr: $_csrfile" _err "Can not read subject from csr: $_csrfile"
return 1 return 1
fi fi
if [ -z "$_csrsubj" ]; then
_info "The Subject is empty"
fi
_info "Subject=$_csrsubj" _info "Subject=$_csrsubj"
...@@ -5386,6 +5749,7 @@ deploy() { ...@@ -5386,6 +5749,7 @@ deploy() {
return 1 return 1
fi fi
_debug2 DOMAIN_CONF "$DOMAIN_CONF"
. "$DOMAIN_CONF" . "$DOMAIN_CONF"
_savedomainconf Le_DeployHook "$_hooks" _savedomainconf Le_DeployHook "$_hooks"
...@@ -5419,7 +5783,8 @@ installcert() { ...@@ -5419,7 +5783,8 @@ installcert() {
_savedomainconf "Le_RealKeyPath" "$_real_key" _savedomainconf "Le_RealKeyPath" "$_real_key"
_savedomainconf "Le_ReloadCmd" "$_reload_cmd" "base64" _savedomainconf "Le_ReloadCmd" "$_reload_cmd" "base64"
_savedomainconf "Le_RealFullChainPath" "$_real_fullchain" _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
export Le_ForceNewDomainKey="$(_readdomainconf Le_ForceNewDomainKey)"
export Le_Next_Domain_Key
_installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd" _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
} }
...@@ -5453,15 +5818,17 @@ _installcert() { ...@@ -5453,15 +5818,17 @@ _installcert() {
mkdir -p "$_backup_path" mkdir -p "$_backup_path"
if [ "$_real_cert" ]; then if [ "$_real_cert" ]; then
_info "Installing cert to:$_real_cert" _info "Installing cert to: $_real_cert"
if [ -f "$_real_cert" ] && [ ! "$_ACME_IS_RENEW" ]; then if [ -f "$_real_cert" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_cert" "$_backup_path/cert.bak" cp "$_real_cert" "$_backup_path/cert.bak"
fi fi
if [ "$CERT_PATH" != "$_real_cert" ]; then
cat "$CERT_PATH" >"$_real_cert" || return 1 cat "$CERT_PATH" >"$_real_cert" || return 1
fi fi
fi
if [ "$_real_ca" ]; then if [ "$_real_ca" ]; then
_info "Installing CA to:$_real_ca" _info "Installing CA to: $_real_ca"
if [ "$_real_ca" = "$_real_cert" ]; then if [ "$_real_ca" = "$_real_cert" ]; then
echo "" >>"$_real_ca" echo "" >>"$_real_ca"
cat "$CA_CERT_PATH" >>"$_real_ca" || return 1 cat "$CA_CERT_PATH" >>"$_real_ca" || return 1
...@@ -5469,30 +5836,37 @@ _installcert() { ...@@ -5469,30 +5836,37 @@ _installcert() {
if [ -f "$_real_ca" ] && [ ! "$_ACME_IS_RENEW" ]; then if [ -f "$_real_ca" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_ca" "$_backup_path/ca.bak" cp "$_real_ca" "$_backup_path/ca.bak"
fi fi
if [ "$CA_CERT_PATH" != "$_real_ca" ]; then
cat "$CA_CERT_PATH" >"$_real_ca" || return 1 cat "$CA_CERT_PATH" >"$_real_ca" || return 1
fi fi
fi fi
fi
if [ "$_real_key" ]; then if [ "$_real_key" ]; then
_info "Installing key to:$_real_key" _info "Installing key to: $_real_key"
if [ -f "$_real_key" ] && [ ! "$_ACME_IS_RENEW" ]; then if [ -f "$_real_key" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_key" "$_backup_path/key.bak" cp "$_real_key" "$_backup_path/key.bak"
fi fi
if [ "$CERT_KEY_PATH" != "$_real_key" ]; then
if [ -f "$_real_key" ]; then if [ -f "$_real_key" ]; then
cat "$CERT_KEY_PATH" >"$_real_key" || return 1 cat "$CERT_KEY_PATH" >"$_real_key" || return 1
else else
cat "$CERT_KEY_PATH" >"$_real_key" || return 1 touch "$_real_key" || return 1
chmod 600 "$_real_key" chmod 600 "$_real_key"
cat "$CERT_KEY_PATH" >"$_real_key" || return 1
fi
fi fi
fi fi
if [ "$_real_fullchain" ]; then if [ "$_real_fullchain" ]; then
_info "Installing full chain to:$_real_fullchain" _info "Installing full chain to: $_real_fullchain"
if [ -f "$_real_fullchain" ] && [ ! "$_ACME_IS_RENEW" ]; then if [ -f "$_real_fullchain" ] && [ ! "$_ACME_IS_RENEW" ]; then
cp "$_real_fullchain" "$_backup_path/fullchain.bak" cp "$_real_fullchain" "$_backup_path/fullchain.bak"
fi fi
if [ "$_real_fullchain" != "$CERT_FULLCHAIN_PATH" ]; then
cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" || return 1 cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain" || return 1
fi fi
fi
if [ "$_reload_cmd" ]; then if [ "$_reload_cmd" ]; then
_info "Run reload cmd: $_reload_cmd" _info "Run reload cmd: $_reload_cmd"
...@@ -5502,6 +5876,8 @@ _installcert() { ...@@ -5502,6 +5876,8 @@ _installcert() {
export CA_CERT_PATH export CA_CERT_PATH
export CERT_FULLCHAIN_PATH export CERT_FULLCHAIN_PATH
export Le_Domain="$_main_domain" export Le_Domain="$_main_domain"
export Le_ForceNewDomainKey
export Le_Next_Domain_Key
cd "$DOMAIN_PATH" && eval "$_reload_cmd" cd "$DOMAIN_PATH" && eval "$_reload_cmd"
); then ); then
_info "$(__green "Reload success")" _info "$(__green "Reload success")"
...@@ -5581,10 +5957,18 @@ installcronjob() { ...@@ -5581,10 +5957,18 @@ installcronjob() {
_CRONTAB="crontab" _CRONTAB="crontab"
if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]; then
lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
else
_debug "_SCRIPT_" "$_SCRIPT_"
_script="$(_readlink "$_SCRIPT_")"
_debug _script "$_script"
if [ -f "$_script" ]; then
_info "Using the current script from: $_script"
lesh="$_script"
else else
_err "Can not install cronjob, $PROJECT_ENTRY not found." _err "Can not install cronjob, $PROJECT_ENTRY not found."
return 1 return 1
fi fi
fi
if [ "$_c_home" ]; then if [ "$_c_home" ]; then
_c_entry="--config-home \"$_c_home\" " _c_entry="--config-home \"$_c_home\" "
fi fi
...@@ -5655,7 +6039,7 @@ uninstallcronjob() { ...@@ -5655,7 +6039,7 @@ uninstallcronjob() {
_info "Removing cron job" _info "Removing cron job"
cr="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")" cr="$($_CRONTAB -l | grep "$PROJECT_ENTRY --cron")"
if [ "$cr" ]; then if [ "$cr" ]; then
if _exists uname && uname -a | grep solaris >/dev/null; then if _exists uname && uname -a | grep SunOS >/dev/null; then
$_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB -- $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB --
else else
$_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB - $_CRONTAB -l | sed "/$PROJECT_ENTRY --cron/d" | $_CRONTAB -
...@@ -5695,6 +6079,23 @@ revoke() { ...@@ -5695,6 +6079,23 @@ revoke() {
return 1 return 1
fi fi
. "$DOMAIN_CONF"
_debug Le_API "$Le_API"
if [ "$Le_API" ]; then
if [ "$Le_API" != "$ACME_DIRECTORY" ]; then
_clearAPI
fi
export ACME_DIRECTORY="$Le_API"
#reload ca configs
ACCOUNT_KEY_PATH=""
ACCOUNT_JSON_PATH=""
CA_CONF=""
_debug3 "initpath again."
_initpath "$Le_Domain" "$_isEcc"
_initAPI
fi
cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)" cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}" | tr -d "\r\n" | _url_replace)"
if [ -z "$cert" ]; then if [ -z "$cert" ]; then
...@@ -5704,40 +6105,40 @@ revoke() { ...@@ -5704,40 +6105,40 @@ revoke() {
_initAPI _initAPI
if [ "$ACME_VERSION" = "2" ]; then
data="{\"certificate\": \"$cert\",\"reason\":$_reason}" data="{\"certificate\": \"$cert\",\"reason\":$_reason}"
else
data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
fi
uri="${ACME_REVOKE_CERT}" uri="${ACME_REVOKE_CERT}"
if [ -f "$CERT_KEY_PATH" ]; then _info "Try account key first."
_info "Try domain key first." if _send_signed_request "$uri" "$data" "" "$ACCOUNT_KEY_PATH"; then
if _send_signed_request "$uri" "$data" "" "$CERT_KEY_PATH"; then
if [ -z "$response" ]; then if [ -z "$response" ]; then
_info "Revoke success." _info "Revoke success."
rm -f "$CERT_PATH" rm -f "$CERT_PATH"
cat "$CERT_KEY_PATH" >"$CERT_KEY_PATH.revoked"
cat "$CSR_PATH" >"$CSR_PATH.revoked"
return 0 return 0
else else
_err "Revoke error by domain key." _err "Revoke error."
_err "$response" _debug "$response"
fi
fi fi
else
_info "Domain key file doesn't exist."
fi fi
_info "Try account key." if [ -f "$CERT_KEY_PATH" ]; then
_info "Try domain key."
if _send_signed_request "$uri" "$data" "" "$ACCOUNT_KEY_PATH"; then if _send_signed_request "$uri" "$data" "" "$CERT_KEY_PATH"; then
if [ -z "$response" ]; then if [ -z "$response" ]; then
_info "Revoke success." _info "Revoke success."
rm -f "$CERT_PATH" rm -f "$CERT_PATH"
cat "$CERT_KEY_PATH" >"$CERT_KEY_PATH.revoked"
cat "$CSR_PATH" >"$CSR_PATH.revoked"
return 0 return 0
else else
_err "Revoke error." _err "Revoke error by domain key."
_debug "$response" _err "$response"
fi
fi fi
else
_info "Domain key file doesn't exist."
fi fi
return 1 return 1
} }
...@@ -5777,10 +6178,26 @@ remove() { ...@@ -5777,10 +6178,26 @@ remove() {
_deactivate() { _deactivate() {
_d_domain="$1" _d_domain="$1"
_d_type="$2" _d_type="$2"
_initpath _initpath "$_d_domain" "$_d_type"
if [ "$ACME_VERSION" = "2" ]; then . "$DOMAIN_CONF"
_identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}" _debug Le_API "$Le_API"
if [ "$Le_API" ]; then
if [ "$Le_API" != "$ACME_DIRECTORY" ]; then
_clearAPI
fi
export ACME_DIRECTORY="$Le_API"
#reload ca configs
ACCOUNT_KEY_PATH=""
ACCOUNT_JSON_PATH=""
CA_CONF=""
_debug3 "initpath again."
_initpath "$Le_Domain" "$_d_type"
_initAPI
fi
_identifiers="{\"type\":\"$(_getIdType "$_d_domain")\",\"value\":\"$_d_domain\"}"
if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
_err "Can not get domain new order." _err "Can not get domain new order."
return 1 return 1
...@@ -5808,29 +6225,15 @@ _deactivate() { ...@@ -5808,29 +6225,15 @@ _deactivate() {
response="$(echo "$response" | _normalizeJson)" response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response" _debug2 response "$response"
_URL_NAME="url" _URL_NAME="url"
else
if ! __get_domain_new_authz "$_d_domain"; then
_err "Can not get domain new authz token."
return 1
fi
authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")" entries="$(echo "$response" | tr '][' '==' | _egrep_o "challenges\": *=[^=]*=" | tr '}{' '\n\n' | grep "\"status\": *\"valid\"")"
_debug "authzUri" "$authzUri"
if [ "$code" ] && [ ! "$code" = '201' ]; then
_err "new-authz error: $response"
return 1
fi
_URL_NAME="uri"
fi
entries="$(echo "$response" | _egrep_o "[^{]*\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
if [ -z "$entries" ]; then if [ -z "$entries" ]; then
_info "No valid entries found." _info "No valid entries found."
if [ -z "$thumbprint" ]; then if [ -z "$thumbprint" ]; then
thumbprint="$(__calc_account_thumbprint)" thumbprint="$(__calc_account_thumbprint)"
fi fi
_debug "Trigger validation." _debug "Trigger validation."
vtype="$VTYPE_DNS" vtype="$(_getIdType "$_d_domain")"
entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry" _debug entry "$entry"
if [ -z "$entry" ]; then if [ -z "$entry" ]; then
...@@ -5866,7 +6269,7 @@ _deactivate() { ...@@ -5866,7 +6269,7 @@ _deactivate() {
_debug _vtype "$_vtype" _debug _vtype "$_vtype"
_info "Found $_vtype" _info "Found $_vtype"
uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')" uri="$(echo "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*\"" | tr -d '" ' | cut -d : -f 2-)"
_debug uri "$uri" _debug uri "$uri"
if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
...@@ -5876,11 +6279,7 @@ _deactivate() { ...@@ -5876,11 +6279,7 @@ _deactivate() {
_info "Deactivate: $_vtype" _info "Deactivate: $_vtype"
if [ "$ACME_VERSION" = "2" ]; then
_djson="{\"status\":\"deactivated\"}" _djson="{\"status\":\"deactivated\"}"
else
_djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}"
fi
if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then
_info "Deactivate: $_vtype success." _info "Deactivate: $_vtype success."
...@@ -6087,7 +6486,7 @@ _installalias() { ...@@ -6087,7 +6486,7 @@ _installalias() {
} }
# nocron confighome noprofile # nocron confighome noprofile accountemail
install() { install() {
if [ -z "$LE_WORKING_DIR" ]; then if [ -z "$LE_WORKING_DIR" ]; then
...@@ -6097,6 +6496,8 @@ install() { ...@@ -6097,6 +6496,8 @@ install() {
_nocron="$1" _nocron="$1"
_c_home="$2" _c_home="$2"
_noprofile="$3" _noprofile="$3"
_accountemail="$4"
if ! _initpath; then if ! _initpath; then
_err "Install failed." _err "Install failed."
return 1 return 1
...@@ -6215,6 +6616,10 @@ install() { ...@@ -6215,6 +6616,10 @@ install() {
fi fi
fi fi
if [ "$_accountemail" ]; then
_saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
fi
_saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
_info OK _info OK
} }
...@@ -6308,6 +6713,13 @@ _send_notify() { ...@@ -6308,6 +6713,13 @@ _send_notify() {
return 0 return 0
fi fi
_nsource="$NOTIFY_SOURCE"
if [ -z "$_nsource" ]; then
_nsource="$(hostname)"
fi
_nsubject="$_nsubject by $_nsource"
_send_err=0 _send_err=0
for _n_hook in $(echo "$_nhooks" | tr ',' " "); do for _n_hook in $(echo "$_nhooks" | tr ',' " "); do
_n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")" _n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")"
...@@ -6362,11 +6774,12 @@ setnotify() { ...@@ -6362,11 +6774,12 @@ setnotify() {
_nhook="$1" _nhook="$1"
_nlevel="$2" _nlevel="$2"
_nmode="$3" _nmode="$3"
_nsource="$4"
_initpath _initpath
if [ -z "$_nhook$_nlevel$_nmode" ]; then if [ -z "$_nhook$_nlevel$_nmode$_nsource" ]; then
_usage "Usage: $PROJECT_ENTRY --set-notify [--notify-hook <hookname>] [--notify-level <0|1|2|3>] [--notify-mode <0|1>]" _usage "Usage: $PROJECT_ENTRY --set-notify [--notify-hook <hookname>] [--notify-level <0|1|2|3>] [--notify-mode <0|1>] [--notify-source <hostname>]"
_usage "$_NOTIFY_WIKI" _usage "$_NOTIFY_WIKI"
return 1 return 1
fi fi
...@@ -6383,6 +6796,12 @@ setnotify() { ...@@ -6383,6 +6796,12 @@ setnotify() {
_saveaccountconf "NOTIFY_MODE" "$NOTIFY_MODE" _saveaccountconf "NOTIFY_MODE" "$NOTIFY_MODE"
fi fi
if [ "$_nsource" ]; then
_info "Set notify source to: $_nsource"
export "NOTIFY_SOURCE=$_nsource"
_saveaccountconf "NOTIFY_SOURCE" "$NOTIFY_SOURCE"
fi
if [ "$_nhook" ]; then if [ "$_nhook" ]; then
_info "Set notify hook to: $_nhook" _info "Set notify hook to: $_nhook"
if [ "$_nhook" = "$NO_VALUE" ]; then if [ "$_nhook" = "$NO_VALUE" ]; then
...@@ -6420,6 +6839,7 @@ Commands: ...@@ -6420,6 +6839,7 @@ Commands:
--revoke Revoke a cert. --revoke Revoke a cert.
--remove Remove the cert from list of certs known to $PROJECT_NAME. --remove Remove the cert from list of certs known to $PROJECT_NAME.
--list List all the certs. --list List all the certs.
--info Show the $PROJECT_NAME configs, or the configs for a domain with [-d domain] parameter.
--to-pkcs12 Export the certificate and key to a pfx file. --to-pkcs12 Export the certificate and key to a pfx file.
--to-pkcs8 Convert to pkcs8 format. --to-pkcs8 Convert to pkcs8 format.
--sign-csr Issue a cert from an existing csr. --sign-csr Issue a cert from an existing csr.
...@@ -6437,6 +6857,8 @@ Commands: ...@@ -6437,6 +6857,8 @@ Commands:
--deactivate Deactivate the domain authz, professional use. --deactivate Deactivate the domain authz, professional use.
--set-default-ca Used with '--server', Set the default CA to use. --set-default-ca Used with '--server', Set the default CA to use.
See: $_SERVER_WIKI See: $_SERVER_WIKI
--set-default-chain Set the default preferred chain for a CA.
See: $_PREFERRED_CHAIN_WIKI
Parameters: Parameters:
...@@ -6451,6 +6873,11 @@ Parameters: ...@@ -6451,6 +6873,11 @@ Parameters:
If no match, the default offered chain will be used. (default: empty) If no match, the default offered chain will be used. (default: empty)
See: $_PREFERRED_CHAIN_WIKI See: $_PREFERRED_CHAIN_WIKI
--valid-to <date-time> Request the NotAfter field of the cert.
See: $_VALIDITY_WIKI
--valid-from <date-time> Request the NotBefore field of the cert.
See: $_VALIDITY_WIKI
-f, --force Force install, force cert renewal or override sudo restrictions. -f, --force Force install, force cert renewal or override sudo restrictions.
--staging, --test Use staging server, for testing. --staging, --test Use staging server, for testing.
--debug [0|1|2|3] Output debug info. Defaults to 1 if argument is omitted. --debug [0|1|2|3] Output debug info. Defaults to 1 if argument is omitted.
...@@ -6493,7 +6920,7 @@ Parameters: ...@@ -6493,7 +6920,7 @@ Parameters:
--cert-home <directory> Specifies the home dir to save all the certs, only valid for '--install' command. --cert-home <directory> Specifies the home dir to save all the certs, only valid for '--install' command.
--config-home <directory> Specifies the home dir to save all the configurations. --config-home <directory> Specifies the home dir to save all the configurations.
--useragent <string> Specifies the user agent string. it will be saved for future use too. --useragent <string> Specifies the user agent string. it will be saved for future use too.
-m, --accountemail <email> Specifies the account email, only valid for the '--install' and '--update-account' command. -m, --email <email> Specifies the account email, only valid for the '--install' and '--update-account' command.
--accountkey <file> Specifies the account key path, only valid for the '--install' command. --accountkey <file> Specifies the account key path, only valid for the '--install' command.
--days <ndays> Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_RENEW days. --days <ndays> Specifies the days to renew the cert when using '--issue' command. The default value is $DEFAULT_RENEW days.
--httpport <port> Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer. --httpport <port> Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
...@@ -6504,9 +6931,9 @@ Parameters: ...@@ -6504,9 +6931,9 @@ Parameters:
--insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted. --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
--ca-bundle <file> Specifies the path to the CA certificate bundle to verify api server's certificate. --ca-bundle <file> Specifies the path to the CA certificate bundle to verify api server's certificate.
--ca-path <directory> Specifies directory containing CA certificates in PEM format, used by wget or curl. --ca-path <directory> Specifies directory containing CA certificates in PEM format, used by wget or curl.
--nocron Only valid for '--install' command, which means: do not install the default cron job. --no-cron Only valid for '--install' command, which means: do not install the default cron job.
In this case, the certs will not be renewed automatically. In this case, the certs will not be renewed automatically.
--noprofile Only valid for '--install' command, which means: do not install aliases to user profile. --no-profile Only valid for '--install' command, which means: do not install aliases to user profile.
--no-color Do not output color text. --no-color Do not output color text.
--force-color Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails. --force-color Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails.
--ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--to-pkcs12' and '--create-csr' --ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--to-pkcs12' and '--create-csr'
...@@ -6535,6 +6962,7 @@ Parameters: ...@@ -6535,6 +6962,7 @@ Parameters:
0: Bulk mode. Send all the domain's notifications in one message(mail). 0: Bulk mode. Send all the domain's notifications in one message(mail).
1: Cert mode. Send a message for every single cert. 1: Cert mode. Send a message for every single cert.
--notify-hook <hookname> Set the notify hook --notify-hook <hookname> Set the notify hook
--notify-source <server name> Set the server name in the notification message
--revoke-reason <0-10> The reason for revocation, can be used in conjunction with the '--revoke' command. --revoke-reason <0-10> The reason for revocation, can be used in conjunction with the '--revoke' command.
See: $_REVOKE_WIKI See: $_REVOKE_WIKI
...@@ -6544,18 +6972,17 @@ Parameters: ...@@ -6544,18 +6972,17 @@ Parameters:
" "
} }
# nocron noprofile installOnline() {
_installOnline() {
_info "Installing from online archive." _info "Installing from online archive."
_nocron="$1"
_noprofile="$2" _branch="$BRANCH"
if [ ! "$BRANCH" ]; then if [ -z "$_branch" ]; then
BRANCH="master" _branch="master"
fi fi
target="$PROJECT/archive/$BRANCH.tar.gz" target="$PROJECT/archive/$_branch.tar.gz"
_info "Downloading $target" _info "Downloading $target"
localname="$BRANCH.tar.gz" localname="$_branch.tar.gz"
if ! _get "$target" >$localname; then if ! _get "$target" >$localname; then
_err "Download error." _err "Download error."
return 1 return 1
...@@ -6567,17 +6994,15 @@ _installOnline() { ...@@ -6567,17 +6994,15 @@ _installOnline() {
exit 1 exit 1
fi fi
cd "$PROJECT_NAME-$BRANCH" cd "$PROJECT_NAME-$_branch"
chmod +x $PROJECT_ENTRY chmod +x $PROJECT_ENTRY
if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then if ./$PROJECT_ENTRY --install "$@"; then
_info "Install success!" _info "Install success!"
_initpath
_saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
fi fi
cd .. cd ..
rm -rf "$PROJECT_NAME-$BRANCH" rm -rf "$PROJECT_NAME-$_branch"
rm -f "$localname" rm -f "$localname"
) )
} }
...@@ -6586,7 +7011,7 @@ _getRepoHash() { ...@@ -6586,7 +7011,7 @@ _getRepoHash() {
_hash_path=$1 _hash_path=$1
shift shift
_hash_url="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/$_hash_path" _hash_url="https://api.github.com/repos/acmesh-official/$PROJECT_NAME/git/refs/$_hash_path"
_get $_hash_url | tr -d "\r\n" | tr '{},' '\n' | grep '"sha":' | cut -d '"' -f 4 _get $_hash_url | tr -d "\r\n" | tr '{},' '\n\n\n' | grep '"sha":' | cut -d '"' -f 4
} }
_getUpgradeHash() { _getUpgradeHash() {
...@@ -6605,7 +7030,7 @@ upgrade() { ...@@ -6605,7 +7030,7 @@ upgrade() {
[ -z "$FORCE" ] && [ "$(_getUpgradeHash)" = "$(_readaccountconf "UPGRADE_HASH")" ] && _info "Already uptodate!" && exit 0 [ -z "$FORCE" ] && [ "$(_getUpgradeHash)" = "$(_readaccountconf "UPGRADE_HASH")" ] && _info "Already uptodate!" && exit 0
export LE_WORKING_DIR export LE_WORKING_DIR
cd "$LE_WORKING_DIR" cd "$LE_WORKING_DIR"
_installOnline "nocron" "noprofile" installOnline "--nocron" "--noprofile"
); then ); then
_info "Upgrade success!" _info "Upgrade success!"
exit 0 exit 0
...@@ -6643,14 +7068,18 @@ _processAccountConf() { ...@@ -6643,14 +7068,18 @@ _processAccountConf() {
} }
_checkSudo() { _checkSudo() {
if [ -z "__INTERACTIVE" ]; then
#don't check if it's not in an interactive shell
return 0
fi
if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then
if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then
#it's root using sudo, no matter it's using sudo or not, just fine #it's root using sudo, no matter it's using sudo or not, just fine
return 0 return 0
fi fi
if [ -n "$SUDO_COMMAND" ]; then if [ -n "$SUDO_COMMAND" ]; then
#it's a normal user doing "sudo su", or `sudo -i` or `sudo -s` #it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`, or `sudo su acmeuser1`
_endswith "$SUDO_COMMAND" /bin/su || grep "^$SUDO_COMMAND\$" /etc/shells >/dev/null 2>&1 _endswith "$SUDO_COMMAND" /bin/su || _contains "$SUDO_COMMAND" "/bin/su " || grep "^$SUDO_COMMAND\$" /etc/shells >/dev/null 2>&1
return $? return $?
fi fi
#otherwise #otherwise
...@@ -6659,9 +7088,10 @@ _checkSudo() { ...@@ -6659,9 +7088,10 @@ _checkSudo() {
return 0 return 0
} }
#server #server #keylength
_selectServer() { _selectServer() {
_server="$1" _server="$1"
_skeylength="$2"
_server_lower="$(echo "$_server" | _lower_case)" _server_lower="$(echo "$_server" | _lower_case)"
_sindex=0 _sindex=0
for snames in $CA_NAMES; do for snames in $CA_NAMES; do
...@@ -6672,6 +7102,9 @@ _selectServer() { ...@@ -6672,6 +7102,9 @@ _selectServer() {
if [ "$_server_lower" = "$sname" ]; then if [ "$_server_lower" = "$sname" ]; then
_debug2 "_selectServer match $sname" _debug2 "_selectServer match $sname"
_serverdir="$(_getfield "$CA_SERVERS" $_sindex)" _serverdir="$(_getfield "$CA_SERVERS" $_sindex)"
if [ "$_serverdir" = "$CA_SSLCOM_RSA" ] && _isEccKey "$_skeylength"; then
_serverdir="$CA_SSLCOM_ECC"
fi
_debug "Selected server: $_serverdir" _debug "Selected server: $_serverdir"
ACME_DIRECTORY="$_serverdir" ACME_DIRECTORY="$_serverdir"
export ACME_DIRECTORY export ACME_DIRECTORY
...@@ -6687,7 +7120,12 @@ _selectServer() { ...@@ -6687,7 +7120,12 @@ _selectServer() {
_getCAShortName() { _getCAShortName() {
caurl="$1" caurl="$1"
if [ -z "$caurl" ]; then if [ -z "$caurl" ]; then
caurl="$DEFAULT_CA" #use letsencrypt as default value if the Le_API is empty
#this case can only come from the old upgrading.
caurl="$CA_LETSENCRYPT_V2"
fi
if [ "$CA_SSLCOM_ECC" = "$caurl" ]; then
caurl="$CA_SSLCOM_RSA" #just hack to get the short name
fi fi
caurl_lower="$(echo $caurl | _lower_case)" caurl_lower="$(echo $caurl | _lower_case)"
_sindex=0 _sindex=0
...@@ -6717,14 +7155,48 @@ setdefaultca() { ...@@ -6717,14 +7155,48 @@ setdefaultca() {
_info "Changed default CA to: $(__green "$ACME_DIRECTORY")" _info "Changed default CA to: $(__green "$ACME_DIRECTORY")"
} }
#preferred-chain
setdefaultchain() {
_initpath
_preferred_chain="$1"
if [ -z "$_preferred_chain" ]; then
_err "Please give a '--preferred-chain value' value."
return 1
fi
mkdir -p "$CA_DIR"
_savecaconf "DEFAULT_PREFERRED_CHAIN" "$_preferred_chain"
}
#domain ecc
info() {
_domain="$1"
_ecc="$2"
_initpath
if [ -z "$_domain" ]; then
_debug "Show global configs"
echo "LE_WORKING_DIR=$LE_WORKING_DIR"
echo "LE_CONFIG_HOME=$LE_CONFIG_HOME"
cat "$ACCOUNT_CONF_PATH"
else
_debug "Show domain configs"
(
_initpath "$_domain" "$_ecc"
echo "DOMAIN_CONF=$DOMAIN_CONF"
for seg in $(cat $DOMAIN_CONF | cut -d = -f 1); do
echo "$seg=$(_readdomainconf "$seg")"
done
)
fi
}
_process() { _process() {
_CMD="" _CMD=""
_domain="" _domain=""
_altdomains="$NO_VALUE" _altdomains="$NO_VALUE"
_webroot="" _webroot=""
_challenge_alias="" _challenge_alias=""
_keylength="" _keylength="$DEFAULT_DOMAIN_KEY_LENGTH"
_accountkeylength="" _accountkeylength="$DEFAULT_ACCOUNT_KEY_LENGTH"
_cert_file="" _cert_file=""
_key_file="" _key_file=""
_ca_file="" _ca_file=""
...@@ -6767,10 +7239,13 @@ _process() { ...@@ -6767,10 +7239,13 @@ _process() {
_notify_hook="" _notify_hook=""
_notify_level="" _notify_level=""
_notify_mode="" _notify_mode=""
_notify_source=""
_revoke_reason="" _revoke_reason=""
_eab_kid="" _eab_kid=""
_eab_hmac_key="" _eab_hmac_key=""
_preferred_chain="" _preferred_chain=""
_valid_from=""
_valid_to=""
while [ ${#} -gt 0 ]; do while [ ${#} -gt 0 ]; do
case "${1}" in case "${1}" in
...@@ -6785,6 +7260,11 @@ _process() { ...@@ -6785,6 +7260,11 @@ _process() {
--install) --install)
_CMD="install" _CMD="install"
;; ;;
--install-online)
shift
installOnline "$@"
return
;;
--uninstall) --uninstall)
_CMD="uninstall" _CMD="uninstall"
;; ;;
...@@ -6821,6 +7301,9 @@ _process() { ...@@ -6821,6 +7301,9 @@ _process() {
--list) --list)
_CMD="list" _CMD="list"
;; ;;
--info)
_CMD="info"
;;
--install-cronjob | --installcronjob) --install-cronjob | --installcronjob)
_CMD="installcronjob" _CMD="installcronjob"
;; ;;
...@@ -6863,6 +7346,9 @@ _process() { ...@@ -6863,6 +7346,9 @@ _process() {
--set-default-ca) --set-default-ca)
_CMD="setdefaultca" _CMD="setdefaultca"
;; ;;
--set-default-chain)
_CMD="setdefaultchain"
;;
-d | --domain) -d | --domain)
_dvalue="$2" _dvalue="$2"
...@@ -6876,10 +7362,6 @@ _process() { ...@@ -6876,10 +7362,6 @@ _process() {
return 1 return 1
fi fi
if _startswith "$_dvalue" "*."; then
_debug "Wildcard domain"
export ACME_VERSION=2
fi
if [ -z "$_domain" ]; then if [ -z "$_domain" ]; then
_domain="$_dvalue" _domain="$_dvalue"
else else
...@@ -6902,7 +7384,6 @@ _process() { ...@@ -6902,7 +7384,6 @@ _process() {
;; ;;
--server) --server)
_server="$2" _server="$2"
_selectServer "$_server"
shift shift
;; ;;
--debug) --debug)
...@@ -7001,16 +7482,17 @@ _process() { ...@@ -7001,16 +7482,17 @@ _process() {
Le_DNSSleep="$_dnssleep" Le_DNSSleep="$_dnssleep"
shift shift
;; ;;
--keylength | -k) --keylength | -k)
_keylength="$2" _keylength="$2"
shift shift
if [ "$_keylength" ] && ! _isEccKey "$_keylength"; then
export __SELECTED_RSA_KEY=1
fi
;; ;;
-ak | --accountkeylength) -ak | --accountkeylength)
_accountkeylength="$2" _accountkeylength="$2"
shift shift
;; ;;
--cert-file | --certpath) --cert-file | --certpath)
_cert_file="$2" _cert_file="$2"
shift shift
...@@ -7041,17 +7523,17 @@ _process() { ...@@ -7041,17 +7523,17 @@ _process() {
shift shift
;; ;;
--home) --home)
LE_WORKING_DIR="$2" export LE_WORKING_DIR="$(echo "$2" | sed 's|/$||')"
shift shift
;; ;;
--cert-home | --certhome) --cert-home | --certhome)
_certhome="$2" _certhome="$2"
CERT_HOME="$_certhome" export CERT_HOME="$_certhome"
shift shift
;; ;;
--config-home) --config-home)
_confighome="$2" _confighome="$2"
LE_CONFIG_HOME="$_confighome" export LE_CONFIG_HOME="$_confighome"
shift shift
;; ;;
--useragent) --useragent)
...@@ -7059,9 +7541,9 @@ _process() { ...@@ -7059,9 +7541,9 @@ _process() {
USER_AGENT="$_useragent" USER_AGENT="$_useragent"
shift shift
;; ;;
-m | --accountemail) -m | --email | --accountemail)
_accountemail="$2" _accountemail="$2"
ACCOUNT_EMAIL="$_accountemail" export ACCOUNT_EMAIL="$_accountemail"
shift shift
;; ;;
--accountkey) --accountkey)
...@@ -7074,6 +7556,14 @@ _process() { ...@@ -7074,6 +7556,14 @@ _process() {
Le_RenewalDays="$_days" Le_RenewalDays="$_days"
shift shift
;; ;;
--valid-from)
_valid_from="$2"
shift
;;
--valid-to)
_valid_to="$2"
shift
;;
--httpport) --httpport)
_httpport="$2" _httpport="$2"
Le_HTTPPort="$_httpport" Le_HTTPPort="$_httpport"
...@@ -7104,10 +7594,10 @@ _process() { ...@@ -7104,10 +7594,10 @@ _process() {
CA_PATH="$_ca_path" CA_PATH="$_ca_path"
shift shift
;; ;;
--nocron) --no-cron | --nocron)
_nocron="1" _nocron="1"
;; ;;
--noprofile) --no-profile | --noprofile)
_noprofile="1" _noprofile="1"
;; ;;
--no-color) --no-color)
...@@ -7245,6 +7735,15 @@ _process() { ...@@ -7245,6 +7735,15 @@ _process() {
_notify_mode="$_nmode" _notify_mode="$_nmode"
shift shift
;; ;;
--notify-source)
_nsource="$2"
if _startswith "$_nsource" "-"; then
_err "'$_nsource' is not valid host name for '$1'"
return 1
fi
_notify_source="$_nsource"
shift
;;
--revoke-reason) --revoke-reason)
_revoke_reason="$2" _revoke_reason="$2"
if _startswith "$_revoke_reason" "-"; then if _startswith "$_revoke_reason" "-"; then
...@@ -7274,6 +7773,11 @@ _process() { ...@@ -7274,6 +7773,11 @@ _process() {
shift 1 shift 1
done done
if [ "$_server" ]; then
_selectServer "$_server" "${_ecc:-$_keylength}"
_server="$ACME_DIRECTORY"
fi
if [ "${_CMD}" != "install" ]; then if [ "${_CMD}" != "install" ]; then
if [ "$__INTERACTIVE" ] && ! _checkSudo; then if [ "$__INTERACTIVE" ] && ! _checkSudo; then
if [ -z "$FORCE" ]; then if [ -z "$FORCE" ]; then
...@@ -7327,17 +7831,17 @@ _process() { ...@@ -7327,17 +7831,17 @@ _process() {
fi fi
_debug "Running cmd: ${_CMD}" _debug "Running cmd: ${_CMD}"
case "${_CMD}" in case "${_CMD}" in
install) install "$_nocron" "$_confighome" "$_noprofile" ;; install) install "$_nocron" "$_confighome" "$_noprofile" "$_accountemail" ;;
uninstall) uninstall "$_nocron" ;; uninstall) uninstall "$_nocron" ;;
upgrade) upgrade ;; upgrade) upgrade ;;
issue) issue)
issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain" issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain" "$_valid_from" "$_valid_to"
;; ;;
deploy) deploy)
deploy "$_domain" "$_deploy_hook" "$_ecc" deploy "$_domain" "$_deploy_hook" "$_ecc"
;; ;;
signcsr) signcsr)
signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" signcsr "$_csr" "$_webroot" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_pre_hook" "$_post_hook" "$_renew_hook" "$_local_address" "$_challenge_alias" "$_preferred_chain"
;; ;;
showcsr) showcsr)
showcsr "$_csr" "$_domain" showcsr "$_csr" "$_domain"
...@@ -7346,10 +7850,10 @@ _process() { ...@@ -7346,10 +7850,10 @@ _process() {
installcert "$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc" installcert "$_domain" "$_cert_file" "$_key_file" "$_ca_file" "$_reloadcmd" "$_fullchain_file" "$_ecc"
;; ;;
renew) renew)
renew "$_domain" "$_ecc" renew "$_domain" "$_ecc" "$_server"
;; ;;
renewAll) renewAll)
renewAll "$_stopRenewOnError" renewAll "$_stopRenewOnError" "$_server"
;; ;;
revoke) revoke)
revoke "$_domain" "$_ecc" "$_revoke_reason" revoke "$_domain" "$_ecc" "$_revoke_reason"
...@@ -7372,6 +7876,9 @@ _process() { ...@@ -7372,6 +7876,9 @@ _process() {
list) list)
list "$_listraw" "$_domain" list "$_listraw" "$_domain"
;; ;;
info)
info "$_domain" "$_ecc"
;;
installcronjob) installcronjob "$_confighome" ;; installcronjob) installcronjob "$_confighome" ;;
uninstallcronjob) uninstallcronjob ;; uninstallcronjob) uninstallcronjob ;;
cron) cron ;; cron) cron ;;
...@@ -7391,11 +7898,14 @@ _process() { ...@@ -7391,11 +7898,14 @@ _process() {
createCSR "$_domain" "$_altdomains" "$_ecc" createCSR "$_domain" "$_altdomains" "$_ecc"
;; ;;
setnotify) setnotify)
setnotify "$_notify_hook" "$_notify_level" "$_notify_mode" setnotify "$_notify_hook" "$_notify_level" "$_notify_mode" "$_notify_source"
;; ;;
setdefaultca) setdefaultca)
setdefaultca setdefaultca
;; ;;
setdefaultchain)
setdefaultchain "$_preferred_chain"
;;
*) *)
if [ "$_CMD" ]; then if [ "$_CMD" ]; then
_err "Invalid command: $_CMD" _err "Invalid command: $_CMD"
...@@ -7440,12 +7950,6 @@ _process() { ...@@ -7440,12 +7950,6 @@ _process() {
} }
if [ "$INSTALLONLINE" ]; then
INSTALLONLINE=""
_installOnline
exit
fi
main() { main() {
[ -z "$1" ] && showhelp && return [ -z "$1" ] && showhelp && return
if _startswith "$1" '-'; then _process "$@"; else "$@"; fi if _startswith "$1" '-'; then _process "$@"; else "$@"; fi
......
#!/usr/bin/env sh
# Here is the script to deploy the cert to your CleverReach Account using the CleverReach REST API.
# Your OAuth needs the right scope, please contact CleverReach support for that.
#
# Written by Jan-Philipp Benecke <github@bnck.me>
# Public domain, 2020
#
# Following environment variables must be set:
#
#export DEPLOY_CLEVERREACH_CLIENT_ID=myid
#export DEPLOY_CLEVERREACH_CLIENT_SECRET=mysecret
cleverreach_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_rest_endpoint="https://rest.cleverreach.com"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_ID
_getdeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET
_getdeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_ID}" ]; then
_err "CleverReach Client ID is not found, please define DEPLOY_CLEVERREACH_CLIENT_ID."
return 1
fi
if [ -z "${DEPLOY_CLEVERREACH_CLIENT_SECRET}" ]; then
_err "CleverReach client secret is not found, please define DEPLOY_CLEVERREACH_CLIENT_SECRET."
return 1
fi
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_ID "${DEPLOY_CLEVERREACH_CLIENT_ID}"
_savedeployconf DEPLOY_CLEVERREACH_CLIENT_SECRET "${DEPLOY_CLEVERREACH_CLIENT_SECRET}"
_savedeployconf DEPLOY_CLEVERREACH_SUBCLIENT_ID "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
_info "Obtaining a CleverReach access token"
_data="{\"grant_type\": \"client_credentials\", \"client_id\": \"${DEPLOY_CLEVERREACH_CLIENT_ID}\", \"client_secret\": \"${DEPLOY_CLEVERREACH_CLIENT_SECRET}\"}"
_auth_result="$(_post "$_data" "$_rest_endpoint/oauth/token.php" "" "POST" "application/json")"
_debug _data "$_data"
_debug _auth_result "$_auth_result"
_regex=".*\"access_token\":\"\([-._0-9A-Za-z]*\)\".*$"
_debug _regex "$_regex"
_access_token=$(echo "$_auth_result" | _json_decode | sed -n "s/$_regex/\1/p")
_debug _subclient "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
if [ -n "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
_info "Obtaining token for sub-client ${DEPLOY_CLEVERREACH_SUBCLIENT_ID}"
export _H1="Authorization: Bearer ${_access_token}"
_subclient_token_result="$(_get "$_rest_endpoint/v3/clients/$DEPLOY_CLEVERREACH_SUBCLIENT_ID/token")"
_access_token=$(echo "$_subclient_token_result" | sed -n "s/\"//p")
_debug _subclient_token_result "$_access_token"
_info "Destroying parent token at CleverReach, as it not needed anymore"
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
_debug _destroy_result "$_destroy_result"
fi
_info "Uploading certificate and key to CleverReach"
_certData="{\"cert\":\"$(_json_encode <"$_cfullchain")\", \"key\":\"$(_json_encode <"$_ckey")\"}"
export _H1="Authorization: Bearer ${_access_token}"
_add_cert_result="$(_post "$_certData" "$_rest_endpoint/v3/ssl" "" "POST" "application/json")"
if [ -z "${DEPLOY_CLEVERREACH_SUBCLIENT_ID}" ]; then
_info "Destroying token at CleverReach, as it not needed anymore"
_destroy_result="$(_post "" "$_rest_endpoint/v3/oauth/token.json" "" "DELETE" "application/json")"
_debug _destroy_result "$_destroy_result"
fi
if ! echo "$_add_cert_result" | grep '"error":' >/dev/null; then
_info "Uploaded certificate successfully"
return 0
else
_debug _add_cert_result "$_add_cert_result"
_err "Unable to update certificate"
return 1
fi
}
#!/usr/bin/env sh
# Here is a script to deploy cert to hashicorp consul using curl
# (https://www.consul.io/)
#
# it requires following environment variables:
#
# CONSUL_PREFIX - this contains the prefix path in consul
# CONSUL_HTTP_ADDR - consul requires this to find your consul server
#
# additionally, you need to ensure that CONSUL_HTTP_TOKEN is available
# to access the consul server
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
consul_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
# validate required env vars
_getdeployconf CONSUL_PREFIX
if [ -z "$CONSUL_PREFIX" ]; then
_err "CONSUL_PREFIX needs to be defined (contains prefix path in vault)"
return 1
fi
_savedeployconf CONSUL_PREFIX "$CONSUL_PREFIX"
_getdeployconf CONSUL_HTTP_ADDR
if [ -z "$CONSUL_HTTP_ADDR" ]; then
_err "CONSUL_HTTP_ADDR needs to be defined (contains consul connection address)"
return 1
fi
_savedeployconf CONSUL_HTTP_ADDR "$CONSUL_HTTP_ADDR"
CONSUL_CMD=$(command -v consul)
# force CLI, but the binary does not exist => error
if [ -n "$USE_CLI" ] && [ -z "$CONSUL_CMD" ]; then
_err "Cannot find the consul binary!"
return 1
fi
# use the CLI first
if [ -n "$USE_CLI" ] || [ -n "$CONSUL_CMD" ]; then
_info "Found consul binary, deploying with CLI"
consul_deploy_cli "$CONSUL_CMD" "$CONSUL_PREFIX"
else
_info "Did not find consul binary, deploying with API"
consul_deploy_api "$CONSUL_HTTP_ADDR" "$CONSUL_PREFIX" "$CONSUL_HTTP_TOKEN"
fi
}
consul_deploy_api() {
CONSUL_HTTP_ADDR="$1"
CONSUL_PREFIX="$2"
CONSUL_HTTP_TOKEN="$3"
URL="$CONSUL_HTTP_ADDR/v1/kv/$CONSUL_PREFIX"
export _H1="X-Consul-Token: $CONSUL_HTTP_TOKEN"
if [ -n "$FABIO" ]; then
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}-cert.pem" '' "PUT" || return 1
_post "$(cat "$_ckey")" "$URL/${_cdomain}-key.pem" '' "PUT" || return 1
else
_post "$(cat "$_ccert")" "$URL/${_cdomain}/cert.pem" '' "PUT" || return 1
_post "$(cat "$_ckey")" "$URL/${_cdomain}/cert.key" '' "PUT" || return 1
_post "$(cat "$_cca")" "$URL/${_cdomain}/chain.pem" '' "PUT" || return 1
_post "$(cat "$_cfullchain")" "$URL/${_cdomain}/fullchain.pem" '' "PUT" || return 1
fi
}
consul_deploy_cli() {
CONSUL_CMD="$1"
CONSUL_PREFIX="$2"
if [ -n "$FABIO" ]; then
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-cert.pem" @"$_cfullchain" || return 1
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}-key.pem" @"$_ckey" || return 1
else
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
$CONSUL_CMD kv put "${CONSUL_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
fi
}
...@@ -3,18 +3,29 @@ ...@@ -3,18 +3,29 @@
# Uses command line uapi. --user option is needed only if run as root. # Uses command line uapi. --user option is needed only if run as root.
# Returns 0 when success. # Returns 0 when success.
# #
# Configure DEPLOY_CPANEL_AUTO_<...> options to enable or restrict automatic
# detection of deployment targets through UAPI (if not set, defaults below are used.)
# - ENABLED : 'true' for multi-site / wildcard capability; otherwise single-site mode.
# - NOMATCH : 'true' to allow deployment to sites that do not match the certificate.
# - INCLUDE : Comma-separated list - sites must match this field.
# - EXCLUDE : Comma-separated list - sites must NOT match this field.
# INCLUDE/EXCLUDE both support non-lexical, glob-style matches using '*'
#
# Please note that I am no longer using Github. If you want to report an issue # Please note that I am no longer using Github. If you want to report an issue
# or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/ # or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/
# #
# Written by Santeri Kannisto <santeri.kannisto@webseodesigners.com> # Written by Santeri Kannisto <santeri.kannisto@webseodesigners.com>
# Public domain, 2017-2018 # Public domain, 2017-2018
#
#export DEPLOY_CPANEL_USER=myusername # export DEPLOY_CPANEL_USER=myusername
# export DEPLOY_CPANEL_AUTO_ENABLED='true'
# export DEPLOY_CPANEL_AUTO_NOMATCH='false'
# export DEPLOY_CPANEL_AUTO_INCLUDE='*'
# export DEPLOY_CPANEL_AUTO_EXCLUDE=''
######## Public functions ##################### ######## Public functions #####################
#domain keyfile certfile cafile fullchain #domain keyfile certfile cafile fullchain
cpanel_uapi_deploy() { cpanel_uapi_deploy() {
_cdomain="$1" _cdomain="$1"
_ckey="$2" _ckey="$2"
...@@ -22,6 +33,9 @@ cpanel_uapi_deploy() { ...@@ -22,6 +33,9 @@ cpanel_uapi_deploy() {
_cca="$4" _cca="$4"
_cfullchain="$5" _cfullchain="$5"
# re-declare vars inherited from acme.sh but not passed to make ShellCheck happy
: "${Le_Alt:=""}"
_debug _cdomain "$_cdomain" _debug _cdomain "$_cdomain"
_debug _ckey "$_ckey" _debug _ckey "$_ckey"
_debug _ccert "$_ccert" _debug _ccert "$_ccert"
...@@ -32,25 +46,120 @@ cpanel_uapi_deploy() { ...@@ -32,25 +46,120 @@ cpanel_uapi_deploy() {
_err "The command uapi is not found." _err "The command uapi is not found."
return 1 return 1
fi fi
# declare useful constants
uapi_error_response='status: 0'
# read cert and key files and urlencode both # read cert and key files and urlencode both
_cert=$(_url_encode <"$_ccert") _cert=$(_url_encode <"$_ccert")
_key=$(_url_encode <"$_ckey") _key=$(_url_encode <"$_ckey")
_debug _cert "$_cert" _debug2 _cert "$_cert"
_debug _key "$_key" _debug2 _key "$_key"
if [ "$(id -u)" = 0 ]; then if [ "$(id -u)" = 0 ]; then
if [ -z "$DEPLOY_CPANEL_USER" ]; then _getdeployconf DEPLOY_CPANEL_USER
# fallback to _readdomainconf for old installs
if [ -z "${DEPLOY_CPANEL_USER:=$(_readdomainconf DEPLOY_CPANEL_USER)}" ]; then
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username" _err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
return 1 return 1
fi fi
_savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER" _debug DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
_response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") _savedeployconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
_uapi_user="$DEPLOY_CPANEL_USER"
fi
# Load all AUTO envars and set defaults - see above for usage
__cpanel_initautoparam ENABLED 'true'
__cpanel_initautoparam NOMATCH 'false'
__cpanel_initautoparam INCLUDE '*'
__cpanel_initautoparam EXCLUDE ''
# Auto mode
if [ "$DEPLOY_CPANEL_AUTO_ENABLED" = "true" ]; then
# call API for site config
_response=$(uapi DomainInfo list_domains)
# exit if error in response
if [ -z "$_response" ] || [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
_err "Error in deploying certificate - cannot retrieve sitelist:"
_err "\n$_response"
return 1
fi
# parse response to create site list
sitelist=$(__cpanel_parse_response "$_response")
_debug "UAPI sites found: $sitelist"
# filter sitelist using configured domains
# skip if NOMATCH is "true"
if [ "$DEPLOY_CPANEL_AUTO_NOMATCH" = "true" ]; then
_debug "DEPLOY_CPANEL_AUTO_NOMATCH is true"
_info "UAPI nomatch mode is enabled - Will not validate sites are valid for the certificate"
else
_debug "DEPLOY_CPANEL_AUTO_NOMATCH is false"
d="$(echo "${Le_Alt}," | sed -e "s/^$_cdomain,//" -e "s/,$_cdomain,/,/")"
d="$(echo "$_cdomain,$d" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\[\^\.\]\*/g')"
sitelist="$(echo "$sitelist" | grep -ix "$d")"
_debug2 "Matched UAPI sites: $sitelist"
fi
# filter sites that do not match $DEPLOY_CPANEL_AUTO_INCLUDE
_info "Applying sitelist filter DEPLOY_CPANEL_AUTO_INCLUDE: $DEPLOY_CPANEL_AUTO_INCLUDE"
sitelist="$(echo "$sitelist" | grep -ix "$(echo "$DEPLOY_CPANEL_AUTO_INCLUDE" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\.\*/g')")"
_debug2 "Remaining sites: $sitelist"
# filter sites that match $DEPLOY_CPANEL_AUTO_EXCLUDE
_info "Applying sitelist filter DEPLOY_CPANEL_AUTO_EXCLUDE: $DEPLOY_CPANEL_AUTO_EXCLUDE"
sitelist="$(echo "$sitelist" | grep -vix "$(echo "$DEPLOY_CPANEL_AUTO_EXCLUDE" | tr ',' '\n' | sed -e 's/\./\\./g' -e 's/\*/\.\*/g')")"
_debug2 "Remaining sites: $sitelist"
# counter for success / failure check
successes=0
if [ -n "$sitelist" ]; then
sitetotal="$(echo "$sitelist" | wc -l)"
_debug "$sitetotal sites to deploy"
else
sitetotal=0
_debug "No sites to deploy"
fi
# for each site: call uapi to publish cert and log result. Only return failure if all fail
for site in $sitelist; do
# call uapi to publish cert, check response for errors and log them.
if [ -n "$_uapi_user" ]; then
_response=$(uapi --user="$_uapi_user" SSL install_ssl domain="$site" cert="$_cert" key="$_key")
else
_response=$(uapi SSL install_ssl domain="$site" cert="$_cert" key="$_key")
fi
if [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
_err "Error in deploying certificate to $site:"
_err "$_response"
else
successes=$((successes + 1))
_debug "$_response"
_info "Succcessfully deployed to $site"
fi
done
# Raise error if all updates fail
if [ "$sitetotal" -gt 0 ] && [ "$successes" -eq 0 ]; then
_err "Could not deploy to any of $sitetotal sites via UAPI"
_debug "successes: $successes, sitetotal: $sitetotal"
return 1
fi
_info "Successfully deployed certificate to $successes of $sitetotal sites via UAPI"
return 0
else
# "classic" mode - will only try to deploy to the primary domain; will not check UAPI first
if [ -n "$_uapi_user" ]; then
_response=$(uapi --user="$_uapi_user" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
else else
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key") _response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
fi fi
error_response="status: 0"
if test "${_response#*$error_response}" != "$_response"; then if [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
_err "Error in deploying certificate:" _err "Error in deploying certificate:"
_err "$_response" _err "$_response"
return 1 return 1
...@@ -59,4 +168,44 @@ cpanel_uapi_deploy() { ...@@ -59,4 +168,44 @@ cpanel_uapi_deploy() {
_debug response "$_response" _debug response "$_response"
_info "Certificate successfully deployed" _info "Certificate successfully deployed"
return 0 return 0
fi
}
######## Private functions #####################
# Internal utility to process YML from UAPI - looks at main_domain, sub_domains, addon domains and parked domains
#[response]
__cpanel_parse_response() {
if [ $# -gt 0 ]; then resp="$*"; else resp="$(cat)"; fi
echo "$resp" |
sed -En \
-e 's/\r$//' \
-e 's/^( *)([_.[:alnum:]]+) *: *(.*)/\1,\2,\3/p' \
-e 's/^( *)- (.*)/\1,-,\2/p' |
awk -F, '{
level = length($1)/2;
section[level] = $2;
for (i in section) {if (i > level) {delete section[i]}}
if (length($3) > 0) {
prefix="";
for (i=0; i < level; i++)
{ prefix = (prefix)(section[i])("/") }
printf("%s%s=%s\n", prefix, $2, $3);
}
}' |
sed -En -e 's/^result\/data\/(main_domain|sub_domains\/-|addon_domains\/-|parked_domains\/-)=(.*)$/\2/p'
}
# Load parameter by prefix+name - fallback to default if not set, and save to config
#pname pdefault
__cpanel_initautoparam() {
pname="$1"
pdefault="$2"
pkey="DEPLOY_CPANEL_AUTO_$pname"
_getdeployconf "$pkey"
[ -n "$(eval echo "\"\$$pkey\"")" ] || eval "$pkey=\"$pdefault\""
_debug2 "$pkey" "$(eval echo "\"\$$pkey\"")"
_savedeployconf "$pkey" "$(eval echo "\"\$$pkey\"")"
} }
...@@ -273,15 +273,27 @@ _check_curl_version() { ...@@ -273,15 +273,27 @@ _check_curl_version() {
_minor="$(_getfield "$_cversion" 2 '.')" _minor="$(_getfield "$_cversion" 2 '.')"
_debug2 "_minor" "$_minor" _debug2 "_minor" "$_minor"
if [ "$_major$_minor" -lt "740" ]; then if [ "$_major" -ge "8" ]; then
#ok
return 0
fi
if [ "$_major" = "7" ]; then
if [ "$_minor" -lt "40" ]; then
_err "curl v$_cversion doesn't support unit socket" _err "curl v$_cversion doesn't support unit socket"
_err "Please upgrade to curl 7.40 or later."
return 1 return 1
fi fi
if [ "$_major$_minor" -lt "750" ]; then if [ "$_minor" -lt "50" ]; then
_debug "Use short host name" _debug "Use short host name"
export _CURL_NO_HOST=1 export _CURL_NO_HOST=1
else else
export _CURL_NO_HOST= export _CURL_NO_HOST=
fi fi
return 0 return 0
else
_err "curl v$_cversion doesn't support unit socket"
_err "Please upgrade to curl 7.40 or later."
return 1
fi
} }
...@@ -28,47 +28,59 @@ fritzbox_deploy() { ...@@ -28,47 +28,59 @@ fritzbox_deploy() {
_debug _cfullchain "$_cfullchain" _debug _cfullchain "$_cfullchain"
if ! _exists iconv; then if ! _exists iconv; then
if ! _exists uconv; then
if ! _exists perl; then if ! _exists perl; then
_err "iconv or perl not found" _err "iconv or uconv or perl not found"
return 1 return 1
fi fi
fi fi
fi
# Clear traces of incorrectly stored values
_clearaccountconf DEPLOY_FRITZBOX_USERNAME
_clearaccountconf DEPLOY_FRITZBOX_PASSWORD
_clearaccountconf DEPLOY_FRITZBOX_URL
# Read config from saved values or env
_getdeployconf DEPLOY_FRITZBOX_USERNAME
_getdeployconf DEPLOY_FRITZBOX_PASSWORD
_getdeployconf DEPLOY_FRITZBOX_URL
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" _debug DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
_fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}" _debug DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
_fritzbox_url="${DEPLOY_FRITZBOX_URL}" _secure_debug DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
_debug _fritzbox_url "$_fritzbox_url" if [ -z "$DEPLOY_FRITZBOX_USERNAME" ]; then
_debug _fritzbox_username "$_fritzbox_username"
_secure_debug _fritzbox_password "$_fritzbox_password"
if [ -z "$_fritzbox_username" ]; then
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME." _err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
return 1 return 1
fi fi
if [ -z "$_fritzbox_password" ]; then if [ -z "$DEPLOY_FRITZBOX_PASSWORD" ]; then
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD." _err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
return 1 return 1
fi fi
if [ -z "$_fritzbox_url" ]; then if [ -z "$DEPLOY_FRITZBOX_URL" ]; then
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL." _err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
return 1 return 1
fi fi
_saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}" # Save current values
_saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}" _savedeployconf DEPLOY_FRITZBOX_USERNAME "$DEPLOY_FRITZBOX_USERNAME"
_saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}" _savedeployconf DEPLOY_FRITZBOX_PASSWORD "$DEPLOY_FRITZBOX_PASSWORD"
_savedeployconf DEPLOY_FRITZBOX_URL "$DEPLOY_FRITZBOX_URL"
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate # Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
export HTTPS_INSECURE=1 export HTTPS_INSECURE=1
_info "Log in to the FRITZ!Box" _info "Log in to the FRITZ!Box"
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')" _fritzbox_challenge="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
if _exists iconv; then if _exists iconv; then
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')" _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | iconv -f ASCII -t UTF16LE | _digest md5 hex)"
elif _exists uconv; then
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | uconv -f ASCII -t UTF16LE | _digest md5 hex)"
else else
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | md5sum | awk '{print $1}')" _fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${DEPLOY_FRITZBOX_PASSWORD}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | _digest md5 hex)"
fi fi
_fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')" _fritzbox_sid="$(_get "${DEPLOY_FRITZBOX_URL}/login_sid.lua?sid=0000000000000000&username=${DEPLOY_FRITZBOX_USERNAME}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL." _err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
...@@ -100,7 +112,7 @@ fritzbox_deploy() { ...@@ -100,7 +112,7 @@ fritzbox_deploy() {
_info "Upload certificate to the FRITZ!Box" _info "Upload certificate to the FRITZ!Box"
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}" export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
_post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL _post "$(cat "${_post_request}")" "${DEPLOY_FRITZBOX_URL}/cgi-bin/firmwarecfg" | grep SSL
retval=$? retval=$?
if [ $retval = 0 ]; then if [ $retval = 0 ]; then
......
#!/usr/bin/env sh #!/usr/bin/env sh
# Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). # Here is the script to deploy the cert to G-Core CDN service (https://gcore.com/) using the G-Core Labs API (https://apidocs.gcore.com/cdn).
# Returns 0 when success. # Returns 0 when success.
# #
# Written by temoffey <temofffey@gmail.com> # Written by temoffey <temofffey@gmail.com>
# Public domain, 2019 # Public domain, 2019
# Update by DreamOfIce <admin@dreamofice.cn> in 2023
#export DEPLOY_GCORE_CDN_USERNAME=myusername #export DEPLOY_GCORE_CDN_USERNAME=myusername
#export DEPLOY_GCORE_CDN_PASSWORD=mypassword #export DEPLOY_GCORE_CDN_PASSWORD=mypassword
...@@ -56,9 +57,9 @@ gcore_cdn_deploy() { ...@@ -56,9 +57,9 @@ gcore_cdn_deploy() {
_request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}" _request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}"
_debug _request "$_request" _debug _request "$_request"
export _H1="Content-Type:application/json" export _H1="Content-Type:application/json"
_response=$(_post "$_request" "https://api.gcdn.co/auth/signin") _response=$(_post "$_request" "https://api.gcore.com/auth/jwt/login")
_debug _response "$_response" _debug _response "$_response"
_regex=".*\"token\":\"\([-._0-9A-Za-z]*\)\".*$" _regex=".*\"access\":\"\([-._0-9A-Za-z]*\)\".*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_token=$(echo "$_response" | sed -n "s/$_regex/\1/p") _token=$(echo "$_response" | sed -n "s/$_regex/\1/p")
_debug _token "$_token" _debug _token "$_token"
...@@ -69,15 +70,18 @@ gcore_cdn_deploy() { ...@@ -69,15 +70,18 @@ gcore_cdn_deploy() {
fi fi
_info "Find CDN resource with cname $_cdomain" _info "Find CDN resource with cname $_cdomain"
export _H2="Authorization:Token $_token" export _H2="Authorization:Bearer $_token"
_response=$(_get "https://api.gcdn.co/resources") _response=$(_get "https://api.gcore.com/cdn/resources")
_debug _response "$_response"
_regex="\"primary_resource\":null},"
_debug _regex "$_regex"
_response=$(echo "$_response" | sed "s/$_regex/$_regex\n/g")
_debug _response "$_response" _debug _response "$_response"
_regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})"
_regex="^.*\"cname\":\"$_cdomain\".*$" _regex="^.*\"cname\":\"$_cdomain\".*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_resource=$(echo "$_response" | sed 's/},{/},\n{/g' | _egrep_o "$_regex") _resource=$(echo "$_response" | _egrep_o "$_regex")
_debug _resource "$_resource" _debug _resource "$_resource"
_regex=".*\"id\":\([0-9]*\).*\"rules\".*$" _regex=".*\"id\":\([0-9]*\).*$"
_debug _regex "$_regex" _debug _regex "$_regex"
_resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
_debug _resourceId "$_resourceId" _debug _resourceId "$_resourceId"
...@@ -99,7 +103,7 @@ gcore_cdn_deploy() { ...@@ -99,7 +103,7 @@ gcore_cdn_deploy() {
_date=$(date "+%d.%m.%Y %H:%M:%S") _date=$(date "+%d.%m.%Y %H:%M:%S")
_request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}" _request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
_debug _request "$_request" _debug _request "$_request"
_response=$(_post "$_request" "https://api.gcdn.co/sslData") _response=$(_post "$_request" "https://api.gcore.com/cdn/sslData")
_debug _response "$_response" _debug _response "$_response"
_regex=".*\"id\":\([0-9]*\).*$" _regex=".*\"id\":\([0-9]*\).*$"
_debug _regex "$_regex" _debug _regex "$_regex"
...@@ -114,7 +118,7 @@ gcore_cdn_deploy() { ...@@ -114,7 +118,7 @@ gcore_cdn_deploy() {
_info "Update CDN resource" _info "Update CDN resource"
_request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}" _request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
_debug _request "$_request" _debug _request "$_request"
_response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _response=$(_post "$_request" "https://api.gcore.com/cdn/resources/$_resourceId" '' "PUT")
_debug _response "$_response" _debug _response "$_response"
_regex=".*\"sslData\":\([0-9]*\).*$" _regex=".*\"sslData\":\([0-9]*\).*$"
_debug _regex "$_regex" _debug _regex "$_regex"
...@@ -130,7 +134,7 @@ gcore_cdn_deploy() { ...@@ -130,7 +134,7 @@ gcore_cdn_deploy() {
_info "Not found old SSL certificate" _info "Not found old SSL certificate"
else else
_info "Delete old SSL certificate" _info "Delete old SSL certificate"
_response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE") _response=$(_post '' "https://api.gcore.com/cdn/sslData/$_sslDataOld" '' "DELETE")
_debug _response "$_response" _debug _response "$_response"
fi fi
......
...@@ -67,7 +67,7 @@ gitlab_deploy() { ...@@ -67,7 +67,7 @@ gitlab_deploy() {
error_response="error" error_response="error"
if test "${_response#*$error_response}" != "$_response"; then if test "${_response#*"$error_response"}" != "$_response"; then
_err "Error in deploying certificate:" _err "Error in deploying certificate:"
_err "$_response" _err "$_response"
return 1 return 1
......
...@@ -54,11 +54,6 @@ haproxy_deploy() { ...@@ -54,11 +54,6 @@ haproxy_deploy() {
DEPLOY_HAPROXY_ISSUER_DEFAULT="no" DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
DEPLOY_HAPROXY_RELOAD_DEFAULT="true" DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
if [ -f "${DOMAIN_CONF}" ]; then
# shellcheck disable=SC1090
. "${DOMAIN_CONF}"
fi
_debug _cdomain "${_cdomain}" _debug _cdomain "${_cdomain}"
_debug _ckey "${_ckey}" _debug _ckey "${_ckey}"
_debug _ccert "${_ccert}" _debug _ccert "${_ccert}"
...@@ -66,6 +61,8 @@ haproxy_deploy() { ...@@ -66,6 +61,8 @@ haproxy_deploy() {
_debug _cfullchain "${_cfullchain}" _debug _cfullchain "${_cfullchain}"
# PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}" # PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
_getdeployconf DEPLOY_HAPROXY_PEM_PATH
_debug2 DEPLOY_HAPROXY_PEM_PATH "${DEPLOY_HAPROXY_PEM_PATH}"
if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then
Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}" Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
_savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}" _savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}"
...@@ -82,6 +79,8 @@ haproxy_deploy() { ...@@ -82,6 +79,8 @@ haproxy_deploy() {
fi fi
# PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" # PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
_getdeployconf DEPLOY_HAPROXY_PEM_NAME
_debug2 DEPLOY_HAPROXY_PEM_NAME "${DEPLOY_HAPROXY_PEM_NAME}"
if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}" Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}"
_savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}" _savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
...@@ -90,6 +89,8 @@ haproxy_deploy() { ...@@ -90,6 +89,8 @@ haproxy_deploy() {
fi fi
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
_getdeployconf DEPLOY_HAPROXY_BUNDLE
_debug2 DEPLOY_HAPROXY_BUNDLE "${DEPLOY_HAPROXY_BUNDLE}"
if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then
Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}" Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}"
_savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}" _savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}"
...@@ -98,6 +99,8 @@ haproxy_deploy() { ...@@ -98,6 +99,8 @@ haproxy_deploy() {
fi fi
# ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}" # ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
_getdeployconf DEPLOY_HAPROXY_ISSUER
_debug2 DEPLOY_HAPROXY_ISSUER "${DEPLOY_HAPROXY_ISSUER}"
if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then
Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}" Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}"
_savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}" _savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}"
...@@ -106,6 +109,8 @@ haproxy_deploy() { ...@@ -106,6 +109,8 @@ haproxy_deploy() {
fi fi
# RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}" # RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
_getdeployconf DEPLOY_HAPROXY_RELOAD
_debug2 DEPLOY_HAPROXY_RELOAD "${DEPLOY_HAPROXY_RELOAD}"
if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}" Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}"
_savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}" _savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}"
...@@ -190,7 +195,7 @@ haproxy_deploy() { ...@@ -190,7 +195,7 @@ haproxy_deploy() {
_info "Updating OCSP stapling info" _info "Updating OCSP stapling info"
_debug _ocsp "${_ocsp}" _debug _ocsp "${_ocsp}"
_info "Extracting OCSP URL" _info "Extracting OCSP URL"
_ocsp_url=$(openssl x509 -noout -ocsp_uri -in "${_pem}") _ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
_debug _ocsp_url "${_ocsp_url}" _debug _ocsp_url "${_ocsp_url}"
# Only process OCSP if URL was present # Only process OCSP if URL was present
...@@ -203,9 +208,9 @@ haproxy_deploy() { ...@@ -203,9 +208,9 @@ haproxy_deploy() {
# Only process the certificate if we have a .issuer file # Only process the certificate if we have a .issuer file
if [ -r "${_issuer}" ]; then if [ -r "${_issuer}" ]; then
# Check if issuer cert is also a root CA cert # Check if issuer cert is also a root CA cert
_subjectdn=$(openssl x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) _subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
_debug _subjectdn "${_subjectdn}" _debug _subjectdn "${_subjectdn}"
_issuerdn=$(openssl x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) _issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
_debug _issuerdn "${_issuerdn}" _debug _issuerdn "${_issuerdn}"
_info "Requesting OCSP response" _info "Requesting OCSP response"
# If the issuer is a CA cert then our command line has "-CAfile" added # If the issuer is a CA cert then our command line has "-CAfile" added
...@@ -216,7 +221,7 @@ haproxy_deploy() { ...@@ -216,7 +221,7 @@ haproxy_deploy() {
fi fi
_debug _cafile_argument "${_cafile_argument}" _debug _cafile_argument "${_cafile_argument}"
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed # if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
_openssl_version=$(openssl version | cut -d' ' -f2) _openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
_debug _openssl_version "${_openssl_version}" _debug _openssl_version "${_openssl_version}"
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1) _openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2) _openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
...@@ -226,7 +231,7 @@ haproxy_deploy() { ...@@ -226,7 +231,7 @@ haproxy_deploy() {
_header_sep=" " _header_sep=" "
fi fi
# Request the OCSP response from the issuer and store it # Request the OCSP response from the issuer and store it
_openssl_ocsp_cmd="openssl ocsp \ _openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
-issuer \"${_issuer}\" \ -issuer \"${_issuer}\" \
-cert \"${_pem}\" \ -cert \"${_pem}\" \
-url \"${_ocsp_url}\" \ -url \"${_ocsp_url}\" \
......
...@@ -45,7 +45,7 @@ kong_deploy() { ...@@ -45,7 +45,7 @@ kong_deploy() {
#Generate data for request (Multipart/form-data with mixed content) #Generate data for request (Multipart/form-data with mixed content)
if [ -z "$ssl_uuid" ]; then if [ -z "$ssl_uuid" ]; then
#set sni to domain #set sni to domain
content="--$delim${nl}Content-Disposition: form-data; name=\"snis\"${nl}${nl}$_cdomain" content="--$delim${nl}Content-Disposition: form-data; name=\"snis[]\"${nl}${nl}$_cdomain"
fi fi
#add key #add key
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")" content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
......
#!/usr/bin/env sh
# Script for acme.sh to deploy certificates to lighttpd
#
# The following variables can be exported:
#
# export DEPLOY_LIGHTTPD_PEM_NAME="${domain}.pem"
#
# Defines the name of the PEM file.
# Defaults to "<domain>.pem"
#
# export DEPLOY_LIGHTTPD_PEM_PATH="/etc/lighttpd"
#
# Defines location of PEM file for Lighttpd.
# Defaults to /etc/lighttpd
#
# export DEPLOY_LIGHTTPD_RELOAD="systemctl reload lighttpd"
#
# OPTIONAL: Reload command used post deploy
# This defaults to be a no-op (ie "true").
# It is strongly recommended to set this something that makes sense
# for your distro.
#
# export DEPLOY_LIGHTTPD_ISSUER="yes"
#
# OPTIONAL: Places CA file as "${DEPLOY_LIGHTTPD_PEM}.issuer"
# Note: Required for OCSP stapling to work
#
# export DEPLOY_LIGHTTPD_BUNDLE="no"
#
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle
# This adds a suffix to the certificate based on the certificate type
# eg RSA certificates will have .rsa as a suffix to the file name
# Lighttpd will load all certificates and provide one or the other
# depending on client capabilities
# Note: This functionality requires Lighttpd was compiled against
# a version of OpenSSL that supports this.
#
######## Public functions #####################
#domain keyfile certfile cafile fullchain
lighttpd_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
# Some defaults
DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT="/etc/lighttpd"
DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT="${_cdomain}.pem"
DEPLOY_LIGHTTPD_BUNDLE_DEFAULT="no"
DEPLOY_LIGHTTPD_ISSUER_DEFAULT="yes"
DEPLOY_LIGHTTPD_RELOAD_DEFAULT="true"
_debug _cdomain "${_cdomain}"
_debug _ckey "${_ckey}"
_debug _ccert "${_ccert}"
_debug _cca "${_cca}"
_debug _cfullchain "${_cfullchain}"
# PEM_PATH is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
_getdeployconf DEPLOY_LIGHTTPD_PEM_PATH
_debug2 DEPLOY_LIGHTTPD_PEM_PATH "${DEPLOY_LIGHTTPD_PEM_PATH}"
if [ -n "${DEPLOY_LIGHTTPD_PEM_PATH}" ]; then
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH}"
_savedomainconf Le_Deploy_lighttpd_pem_path "${Le_Deploy_lighttpd_pem_path}"
elif [ -z "${Le_Deploy_lighttpd_pem_path}" ]; then
Le_Deploy_lighttpd_pem_path="${DEPLOY_LIGHTTPD_PEM_PATH_DEFAULT}"
fi
# Ensure PEM_PATH exists
if [ -d "${Le_Deploy_lighttpd_pem_path}" ]; then
_debug "PEM_PATH ${Le_Deploy_lighttpd_pem_path} exists"
else
_err "PEM_PATH ${Le_Deploy_lighttpd_pem_path} does not exist"
return 1
fi
# PEM_NAME is optional. If not provided then assume "${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
_getdeployconf DEPLOY_LIGHTTPD_PEM_NAME
_debug2 DEPLOY_LIGHTTPD_PEM_NAME "${DEPLOY_LIGHTTPD_PEM_NAME}"
if [ -n "${DEPLOY_LIGHTTPD_PEM_NAME}" ]; then
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME}"
_savedomainconf Le_Deploy_lighttpd_pem_name "${Le_Deploy_lighttpd_pem_name}"
elif [ -z "${Le_Deploy_lighttpd_pem_name}" ]; then
Le_Deploy_lighttpd_pem_name="${DEPLOY_LIGHTTPD_PEM_NAME_DEFAULT}"
fi
# BUNDLE is optional. If not provided then assume "${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
_getdeployconf DEPLOY_LIGHTTPD_BUNDLE
_debug2 DEPLOY_LIGHTTPD_BUNDLE "${DEPLOY_LIGHTTPD_BUNDLE}"
if [ -n "${DEPLOY_LIGHTTPD_BUNDLE}" ]; then
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE}"
_savedomainconf Le_Deploy_lighttpd_bundle "${Le_Deploy_lighttpd_bundle}"
elif [ -z "${Le_Deploy_lighttpd_bundle}" ]; then
Le_Deploy_lighttpd_bundle="${DEPLOY_LIGHTTPD_BUNDLE_DEFAULT}"
fi
# ISSUER is optional. If not provided then assume "${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
_getdeployconf DEPLOY_LIGHTTPD_ISSUER
_debug2 DEPLOY_LIGHTTPD_ISSUER "${DEPLOY_LIGHTTPD_ISSUER}"
if [ -n "${DEPLOY_LIGHTTPD_ISSUER}" ]; then
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER}"
_savedomainconf Le_Deploy_lighttpd_issuer "${Le_Deploy_lighttpd_issuer}"
elif [ -z "${Le_Deploy_lighttpd_issuer}" ]; then
Le_Deploy_lighttpd_issuer="${DEPLOY_LIGHTTPD_ISSUER_DEFAULT}"
fi
# RELOAD is optional. If not provided then assume "${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
_getdeployconf DEPLOY_LIGHTTPD_RELOAD
_debug2 DEPLOY_LIGHTTPD_RELOAD "${DEPLOY_LIGHTTPD_RELOAD}"
if [ -n "${DEPLOY_LIGHTTPD_RELOAD}" ]; then
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD}"
_savedomainconf Le_Deploy_lighttpd_reload "${Le_Deploy_lighttpd_reload}"
elif [ -z "${Le_Deploy_lighttpd_reload}" ]; then
Le_Deploy_lighttpd_reload="${DEPLOY_LIGHTTPD_RELOAD_DEFAULT}"
fi
# Set the suffix depending if we are creating a bundle or not
if [ "${Le_Deploy_lighttpd_bundle}" = "yes" ]; then
_info "Bundle creation requested"
# Initialise $Le_Keylength if its not already set
if [ -z "${Le_Keylength}" ]; then
Le_Keylength=""
fi
if _isEccKey "${Le_Keylength}"; then
_info "ECC key type detected"
_suffix=".ecdsa"
else
_info "RSA key type detected"
_suffix=".rsa"
fi
else
_suffix=""
fi
_debug _suffix "${_suffix}"
# Set variables for later
_pem="${Le_Deploy_lighttpd_pem_path}/${Le_Deploy_lighttpd_pem_name}${_suffix}"
_issuer="${_pem}.issuer"
_ocsp="${_pem}.ocsp"
_reload="${Le_Deploy_lighttpd_reload}"
_info "Deploying PEM file"
# Create a temporary PEM file
_temppem="$(_mktemp)"
_debug _temppem "${_temppem}"
cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
_ret="$?"
# Check that we could create the temporary file
if [ "${_ret}" != "0" ]; then
_err "Error code ${_ret} returned during PEM file creation"
[ -f "${_temppem}" ] && rm -f "${_temppem}"
return ${_ret}
fi
# Move PEM file into place
_info "Moving new certificate into place"
_debug _pem "${_pem}"
cat "${_temppem}" >"${_pem}"
_ret=$?
# Clean up temp file
[ -f "${_temppem}" ] && rm -f "${_temppem}"
# Deal with any failure of moving PEM file into place
if [ "${_ret}" != "0" ]; then
_err "Error code ${_ret} returned while moving new certificate into place"
return ${_ret}
fi
# Update .issuer file if requested
if [ "${Le_Deploy_lighttpd_issuer}" = "yes" ]; then
_info "Updating .issuer file"
_debug _issuer "${_issuer}"
cat "${_cca}" >"${_issuer}"
_ret="$?"
if [ "${_ret}" != "0" ]; then
_err "Error code ${_ret} returned while copying issuer/CA certificate into place"
return ${_ret}
fi
else
[ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists"
fi
# Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
if [ -z "${Le_OCSP_Staple}" ]; then
Le_OCSP_Staple="0"
fi
if [ "${Le_OCSP_Staple}" = "1" ]; then
_info "Updating OCSP stapling info"
_debug _ocsp "${_ocsp}"
_info "Extracting OCSP URL"
_ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}")
_debug _ocsp_url "${_ocsp_url}"
# Only process OCSP if URL was present
if [ "${_ocsp_url}" != "" ]; then
# Extract the hostname from the OCSP URL
_info "Extracting OCSP URL"
_ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3)
_debug _ocsp_host "${_ocsp_host}"
# Only process the certificate if we have a .issuer file
if [ -r "${_issuer}" ]; then
# Check if issuer cert is also a root CA cert
_subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
_debug _subjectdn "${_subjectdn}"
_issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
_debug _issuerdn "${_issuerdn}"
_info "Requesting OCSP response"
# If the issuer is a CA cert then our command line has "-CAfile" added
if [ "${_subjectdn}" = "${_issuerdn}" ]; then
_cafile_argument="-CAfile \"${_issuer}\""
else
_cafile_argument=""
fi
_debug _cafile_argument "${_cafile_argument}"
# if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
_openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2)
_debug _openssl_version "${_openssl_version}"
_openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1)
_openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2)
if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
_header_sep="="
else
_header_sep=" "
fi
# Request the OCSP response from the issuer and store it
_openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \
-issuer \"${_issuer}\" \
-cert \"${_pem}\" \
-url \"${_ocsp_url}\" \
-header Host${_header_sep}\"${_ocsp_host}\" \
-respout \"${_ocsp}\" \
-verify_other \"${_issuer}\" \
${_cafile_argument} \
| grep -q \"${_pem}: good\""
_debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}"
eval "${_openssl_ocsp_cmd}"
_ret=$?
else
# Non fatal: No issuer file was present so no OCSP stapling file created
_err "OCSP stapling in use but no .issuer file was present"
fi
else
# Non fatal: No OCSP url was found int the certificate
_err "OCSP update requested but no OCSP URL was found in certificate"
fi
# Non fatal: Check return code of openssl command
if [ "${_ret}" != "0" ]; then
_err "Updating OCSP stapling failed with return code ${_ret}"
fi
else
# An OCSP file was already present but certificate did not have OCSP extension
if [ -f "${_ocsp}" ]; then
_err "OCSP was not requested but .ocsp file exists."
# Could remove the file at this step, although Lighttpd just ignores it in this case
# rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
fi
fi
# Reload Lighttpd
_debug _reload "${_reload}"
eval "${_reload}"
_ret=$?
if [ "${_ret}" != "0" ]; then
_err "Error code ${_ret} during reload"
return ${_ret}
else
_info "Reload successful"
fi
return 0
}
...@@ -20,14 +20,25 @@ mailcow_deploy() { ...@@ -20,14 +20,25 @@ mailcow_deploy() {
_debug _cca "$_cca" _debug _cca "$_cca"
_debug _cfullchain "$_cfullchain" _debug _cfullchain "$_cfullchain"
_mailcow_path="${DEPLOY_MAILCOW_PATH}" _getdeployconf DEPLOY_MAILCOW_PATH
_getdeployconf DEPLOY_MAILCOW_RELOAD
if [ -z "$_mailcow_path" ]; then _debug DEPLOY_MAILCOW_PATH "$DEPLOY_MAILCOW_PATH"
_debug DEPLOY_MAILCOW_RELOAD "$DEPLOY_MAILCOW_RELOAD"
if [ -z "$DEPLOY_MAILCOW_PATH" ]; then
_err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH." _err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH."
return 1 return 1
fi fi
_ssl_path="${_mailcow_path}/data/assets/ssl/" _savedeployconf DEPLOY_MAILCOW_PATH "$DEPLOY_MAILCOW_PATH"
[ -n "$DEPLOY_MAILCOW_RELOAD" ] && _savedeployconf DEPLOY_MAILCOW_RELOAD "$DEPLOY_MAILCOW_RELOAD"
_ssl_path="$DEPLOY_MAILCOW_PATH"
if [ -f "$DEPLOY_MAILCOW_PATH/generate_config.sh" ]; then
_ssl_path="$DEPLOY_MAILCOW_PATH/data/assets/ssl/"
fi
if [ ! -d "$_ssl_path" ]; then if [ ! -d "$_ssl_path" ]; then
_err "Cannot find mailcow ssl path: $_ssl_path" _err "Cannot find mailcow ssl path: $_ssl_path"
return 1 return 1
...@@ -46,7 +57,7 @@ mailcow_deploy() { ...@@ -46,7 +57,7 @@ mailcow_deploy() {
return 1 return 1
fi fi
DEFAULT_MAILCOW_RELOAD="cd ${_mailcow_path} && docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow" DEFAULT_MAILCOW_RELOAD="docker restart \$(docker ps --quiet --filter name=nginx-mailcow --filter name=dovecot-mailcow --filter name=postfix-mailcow)"
_reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}" _reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}"
_info "Run reload: $_reload" _info "Run reload: $_reload"
......
#!/usr/bin/env sh
# This deploy hook is tested on OpenMediaVault 5.x. It supports both local and remote deployment.
# The way it works is that if a cert with the matching domain name is not found, it will firstly create a dummy cert to get its uuid, and then replace it with your cert.
#
# DEPLOY_OMV_WEBUI_ADMIN - This is OMV web gui admin account. Default value is admin. It's required as the user parameter (-u) for the omv-rpc command.
# DEPLOY_OMV_HOST and DEPLOY_OMV_SSH_USER are optional. They are used for remote deployment through ssh (support public key authentication only). Per design, OMV web gui admin doesn't have ssh permission, so another account is needed for ssh.
#
# returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
openmediavault_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_getdeployconf DEPLOY_OMV_WEBUI_ADMIN
if [ -z "$DEPLOY_OMV_WEBUI_ADMIN" ]; then
DEPLOY_OMV_WEBUI_ADMIN="admin"
fi
_savedeployconf DEPLOY_OMV_WEBUI_ADMIN "$DEPLOY_OMV_WEBUI_ADMIN"
_getdeployconf DEPLOY_OMV_HOST
_getdeployconf DEPLOY_OMV_SSH_USER
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
_info "[OMV deploy-hook] Deploy certificate remotely through ssh."
_savedeployconf DEPLOY_OMV_HOST "$DEPLOY_OMV_HOST"
_savedeployconf DEPLOY_OMV_SSH_USER "$DEPLOY_OMV_SSH_USER"
else
_info "[OMV deploy-hook] Deploy certificate locally."
fi
if [ -n "$DEPLOY_OMV_HOST" ] && [ -n "$DEPLOY_OMV_SSH_USER" ]; then
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{\"start\": 0, \"limit\": -1}' | jq -r '.data[] | select(.name==\"/CN='$_cdomain'\") | .uuid'"
# shellcheck disable=SC2029
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
_debug _command "$_command"
if [ -z "$_uuid" ]; then
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{\"cn\": \"test.example.com\", \"size\": 4096, \"days\": 3650, \"c\": \"\", \"st\": \"\", \"l\": \"\", \"o\": \"\", \"ou\": \"\", \"email\": \"\"}' | jq -r '.uuid'"
# shellcheck disable=SC2029
_uuid=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
_debug _command "$_command"
if [ -z "$_uuid" ]; then
_err "[OMV deploy-hook] An error occured while creating the certificate"
return 1
fi
fi
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
_fullchain=$(jq <"$_cfullchain" -aRs .)
_key=$(jq <"$_ckey" -aRs .)
_debug _fullchain "$_fullchain"
_debug _key "$_key"
_info "[OMV deploy-hook] Updating key and certificate in openmediavault"
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
# shellcheck disable=SC2029
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
_debug _command "$_command"
_debug _result "$_result"
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
# shellcheck disable=SC2029
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
_debug _command "$_command"
_debug _result "$_result"
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
# shellcheck disable=SC2029
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
_debug _command "$_command"
_debug _result "$_result"
_info "[OMV deploy-hook] Asking nginx to reload"
_command="nginx -s reload"
# shellcheck disable=SC2029
_result=$(ssh "$DEPLOY_OMV_SSH_USER@$DEPLOY_OMV_HOST" "$_command")
_debug _command "$_command"
_debug _result "$_result"
else
# shellcheck disable=SC2086
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'getList' '{"start": 0, "limit": -1}' | jq -r '.data[] | select(.name=="/CN='$_cdomain'") | .uuid')
if [ -z "$_uuid" ]; then
_info "[OMV deploy-hook] Domain $_cdomain has no certificate in openmediavault, creating it!"
# shellcheck disable=SC2086
_uuid=$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'create' '{"cn": "test.example.com", "size": 4096, "days": 3650, "c": "", "st": "", "l": "", "o": "", "ou": "", "email": ""}' | jq -r '.uuid')
if [ -z "$_uuid" ]; then
_err "[OMB deploy-hook] An error occured while creating the certificate"
return 1
fi
fi
_info "[OMV deploy-hook] Domain $_cdomain has uuid: $_uuid"
_fullchain=$(jq <"$_cfullchain" -aRs .)
_key=$(jq <"$_ckey" -aRs .)
_debug _fullchain "$_fullchain"
_debug _key "$_key"
_info "[OMV deploy-hook] Updating key and certificate in openmediavault"
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'CertificateMgmt' 'set' '{\"uuid\":\"$_uuid\", \"certificate\":$_fullchain, \"privatekey\":$_key, \"comment\":\"acme.sh deployed $(date)\"}'"
_result=$(eval "$_command")
_debug _command "$_command"
_debug _result "$_result"
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'setSettings' \$(omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'WebGui' 'getSettings' | jq -c '.sslcertificateref=\"$_uuid\"')"
_result=$(eval "$_command")
_debug _command "$_command"
_debug _result "$_result"
_info "[OMV deploy-hook] Asking openmediavault to apply changes... (this could take some time, hang in there)"
_command="omv-rpc -u $DEPLOY_OMV_WEBUI_ADMIN 'Config' 'applyChanges' '{\"modules\":[], \"force\": false}'"
_result=$(eval "$_command")
_debug _command "$_command"
_debug _result "$_result"
_info "[OMV deploy-hook] Asking nginx to reload"
_command="nginx -s reload"
_result=$(eval "$_command")
_debug _command "$_command"
_debug _result "$_result"
fi
return 0
}
#!/usr/bin/env sh
# Script to deploy cert to Peplink Routers
#
# The following environment variables must be set:
#
# PEPLINK_Hostname - Peplink hostname
# PEPLINK_Username - Peplink username to login
# PEPLINK_Password - Peplink password to login
#
# The following environmental variables may be set if you don't like their
# default values:
#
# PEPLINK_Certtype - Certificate type to target for replacement
# defaults to "webadmin", can be one of:
# * "chub" (ContentHub)
# * "openvpn" (OpenVPN CA)
# * "portal" (Captive Portal SSL)
# * "webadmin" (Web Admin SSL)
# * "webproxy" (Proxy Root CA)
# * "wwan_ca" (Wi-Fi WAN CA)
# * "wwan_client" (Wi-Fi WAN Client)
# PEPLINK_Scheme - defaults to "https"
# PEPLINK_Port - defaults to "443"
#
#returns 0 means success, otherwise error.
######## Public functions #####################
_peplink_get_cookie_data() {
grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';'
}
#domain keyfile certfile cafile fullchain
peplink_deploy() {
_cdomain="$1"
_ckey="$2"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _cfullchain "$_cfullchain"
_debug _ckey "$_ckey"
# Get Hostname, Username and Password, but don't save until we successfully authenticate
_getdeployconf PEPLINK_Hostname
_getdeployconf PEPLINK_Username
_getdeployconf PEPLINK_Password
if [ -z "${PEPLINK_Hostname:-}" ] || [ -z "${PEPLINK_Username:-}" ] || [ -z "${PEPLINK_Password:-}" ]; then
_err "PEPLINK_Hostname & PEPLINK_Username & PEPLINK_Password must be set"
return 1
fi
_debug2 PEPLINK_Hostname "$PEPLINK_Hostname"
_debug2 PEPLINK_Username "$PEPLINK_Username"
_secure_debug2 PEPLINK_Password "$PEPLINK_Password"
# Optional certificate type, scheme, and port for Peplink
_getdeployconf PEPLINK_Certtype
_getdeployconf PEPLINK_Scheme
_getdeployconf PEPLINK_Port
# Don't save the certificate type until we verify it exists and is supported
_savedeployconf PEPLINK_Scheme "$PEPLINK_Scheme"
_savedeployconf PEPLINK_Port "$PEPLINK_Port"
# Default vaules for certificate type, scheme, and port
[ -n "${PEPLINK_Certtype}" ] || PEPLINK_Certtype="webadmin"
[ -n "${PEPLINK_Scheme}" ] || PEPLINK_Scheme="https"
[ -n "${PEPLINK_Port}" ] || PEPLINK_Port="443"
_debug2 PEPLINK_Certtype "$PEPLINK_Certtype"
_debug2 PEPLINK_Scheme "$PEPLINK_Scheme"
_debug2 PEPLINK_Port "$PEPLINK_Port"
_base_url="$PEPLINK_Scheme://$PEPLINK_Hostname:$PEPLINK_Port"
_debug _base_url "$_base_url"
# Login, get the auth token from the cookie
_info "Logging into $PEPLINK_Hostname:$PEPLINK_Port"
encoded_username="$(printf "%s" "$PEPLINK_Username" | _url_encode)"
encoded_password="$(printf "%s" "$PEPLINK_Password" | _url_encode)"
response=$(_post "func=login&username=$encoded_username&password=$encoded_password" "$_base_url/cgi-bin/MANGA/api.cgi")
auth_token=$(_peplink_get_cookie_data "bauth" <"$HTTP_HEADER")
_debug3 response "$response"
_debug auth_token "$auth_token"
if [ -z "$auth_token" ]; then
_err "Unable to authenticate to $PEPLINK_Hostname:$PEPLINK_Port using $PEPLINK_Scheme."
_err "Check your username and password."
return 1
fi
_H1="Cookie: $auth_token"
export _H1
_debug2 H1 "${_H1}"
# Now that we know the hostnameusername and password are good, save them
_savedeployconf PEPLINK_Hostname "$PEPLINK_Hostname"
_savedeployconf PEPLINK_Username "$PEPLINK_Username"
_savedeployconf PEPLINK_Password "$PEPLINK_Password"
_info "Generate form POST request"
encoded_key="$(_url_encode <"$_ckey")"
encoded_fullchain="$(_url_encode <"$_cfullchain")"
body="cert_type=$PEPLINK_Certtype&cert_uid=&section=CERT_modify&key_pem=$encoded_key&key_pem_passphrase=&key_pem_passphrase_confirm=&cert_pem=$encoded_fullchain"
_debug3 body "$body"
_info "Upload $PEPLINK_Certtype certificate to the Peplink"
response=$(_post "$body" "$_base_url/cgi-bin/MANGA/admin.cgi")
_debug3 response "$response"
if echo "$response" | grep 'Success' >/dev/null; then
# We've verified this certificate type is valid, so save it
_savedeployconf PEPLINK_Certtype "$PEPLINK_Certtype"
_info "Certificate was updated"
return 0
else
_err "Unable to update certificate, error code $response"
return 1
fi
}
#!/usr/bin/env sh
# Deploy certificates to a proxmox virtual environment node using the API.
#
# Environment variables that can be set are:
# `DEPLOY_PROXMOXVE_SERVER`: The hostname of the proxmox ve node. Defaults to
# _cdomain.
# `DEPLOY_PROXMOXVE_SERVER_PORT`: The port number the management interface is on.
# Defaults to 8006.
# `DEPLOY_PROXMOXVE_NODE_NAME`: The name of the node we'll be connecting to.
# Defaults to the host portion of the server
# domain name.
# `DEPLOY_PROXMOXVE_USER`: The user we'll connect as. Defaults to root.
# `DEPLOY_PROXMOXVE_USER_REALM`: The authentication realm the user authenticates
# with. Defaults to pam.
# `DEPLOY_PROXMOXVE_API_TOKEN_NAME`: The name of the API token created for the
# user account. Defaults to acme.
# `DEPLOY_PROXMOXVE_API_TOKEN_KEY`: The API token. Required.
proxmoxve_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug2 _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
# "Sane" defaults.
_getdeployconf DEPLOY_PROXMOXVE_SERVER
if [ -z "$DEPLOY_PROXMOXVE_SERVER" ]; then
_target_hostname="$_cdomain"
else
_target_hostname="$DEPLOY_PROXMOXVE_SERVER"
_savedeployconf DEPLOY_PROXMOXVE_SERVER "$DEPLOY_PROXMOXVE_SERVER"
fi
_debug2 DEPLOY_PROXMOXVE_SERVER "$_target_hostname"
_getdeployconf DEPLOY_PROXMOXVE_SERVER_PORT
if [ -z "$DEPLOY_PROXMOXVE_SERVER_PORT" ]; then
_target_port="8006"
else
_target_port="$DEPLOY_PROXMOXVE_SERVER_PORT"
_savedeployconf DEPLOY_PROXMOXVE_SERVER_PORT "$DEPLOY_PROXMOXVE_SERVER_PORT"
fi
_debug2 DEPLOY_PROXMOXVE_SERVER_PORT "$_target_port"
_getdeployconf DEPLOY_PROXMOXVE_NODE_NAME
if [ -z "$DEPLOY_PROXMOXVE_NODE_NAME" ]; then
_node_name=$(echo "$_target_hostname" | cut -d. -f1)
else
_node_name="$DEPLOY_PROXMOXVE_NODE_NAME"
_savedeployconf DEPLOY_PROXMOXVE_NODE_NAME "$DEPLOY_PROXMOXVE_NODE_NAME"
fi
_debug2 DEPLOY_PROXMOXVE_NODE_NAME "$_node_name"
# Complete URL.
_target_url="https://${_target_hostname}:${_target_port}/api2/json/nodes/${_node_name}/certificates/custom"
_debug TARGET_URL "$_target_url"
# More "sane" defaults.
_getdeployconf DEPLOY_PROXMOXVE_USER
if [ -z "$DEPLOY_PROXMOXVE_USER" ]; then
_proxmoxve_user="root"
else
_proxmoxve_user="$DEPLOY_PROXMOXVE_USER"
_savedeployconf DEPLOY_PROXMOXVE_USER "$DEPLOY_PROXMOXVE_USER"
fi
_debug2 DEPLOY_PROXMOXVE_USER "$_proxmoxve_user"
_getdeployconf DEPLOY_PROXMOXVE_USER_REALM
if [ -z "$DEPLOY_PROXMOXVE_USER_REALM" ]; then
_proxmoxve_user_realm="pam"
else
_proxmoxve_user_realm="$DEPLOY_PROXMOXVE_USER_REALM"
_savedeployconf DEPLOY_PROXMOXVE_USER_REALM "$DEPLOY_PROXMOXVE_USER_REALM"
fi
_debug2 DEPLOY_PROXMOXVE_USER_REALM "$_proxmoxve_user_realm"
_getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME
if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_NAME" ]; then
_proxmoxve_api_token_name="acme"
else
_proxmoxve_api_token_name="$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
_savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_NAME "$DEPLOY_PROXMOXVE_API_TOKEN_NAME"
fi
_debug2 DEPLOY_PROXMOXVE_API_TOKEN_NAME "$_proxmoxve_api_token_name"
# This is required.
_getdeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY
if [ -z "$DEPLOY_PROXMOXVE_API_TOKEN_KEY" ]; then
_err "API key not provided."
return 1
else
_proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
_savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
fi
_debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY _proxmoxve_api_token_key
# PVE API Token header value. Used in "Authorization: PVEAPIToken".
_proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
_debug2 "Auth Header" _proxmoxve_header_api_token
# Ugly. I hate putting heredocs inside functions because heredocs don't
# account for whitespace correctly but it _does_ work and is several times
# cleaner than anything else I had here.
#
# This dumps the json payload to a variable that should be passable to the
# _psot function.
_json_payload=$(
cat <<HEREDOC
{
"certificates": "$(tr '\n' ':' <"$_cfullchain" | sed 's/:/\\n/g')",
"key": "$(tr '\n' ':' <"$_ckey" | sed 's/:/\\n/g')",
"node":"$_node_name",
"restart":"1",
"force":"1"
}
HEREDOC
)
_debug2 Payload "$_json_payload"
# Push certificates to server.
export _HTTPS_INSECURE=1
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
_post "$_json_payload" "$_target_url" "" POST "application/json"
}
...@@ -53,7 +53,7 @@ qiniu_deploy() { ...@@ -53,7 +53,7 @@ qiniu_deploy() {
sslcert_access_token="$(_make_access_token "$sslcert_path")" sslcert_access_token="$(_make_access_token "$sslcert_path")"
_debug sslcert_access_token "$sslcert_access_token" _debug sslcert_access_token "$sslcert_access_token"
export _H1="Authorization: QBox $sslcert_access_token" export _H1="Authorization: QBox $sslcert_access_token"
sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline") sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64)
if ! _contains "$sslcert_response" "certID"; then if ! _contains "$sslcert_response" "certID"; then
_err "Error in creating certificate:" _err "Error in creating certificate:"
...@@ -75,7 +75,7 @@ qiniu_deploy() { ...@@ -75,7 +75,7 @@ qiniu_deploy() {
update_access_token="$(_make_access_token "$update_path")" update_access_token="$(_make_access_token "$update_path")"
_debug update_access_token "$update_access_token" _debug update_access_token "$update_access_token"
export _H1="Authorization: QBox $update_access_token" export _H1="Authorization: QBox $update_access_token"
update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline") update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64)
if _contains "$update_response" "error"; then if _contains "$update_response" "error"; then
_err "Error in updating domain $domain httpsconf:" _err "Error in updating domain $domain httpsconf:"
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
# ```sh # ```sh
# export ROUTER_OS_USERNAME=certuser # export ROUTER_OS_USERNAME=certuser
# export ROUTER_OS_HOST=router.example.com # export ROUTER_OS_HOST=router.example.com
# export ROUTER_OS_PORT=22
# #
# acme.sh --deploy -d ftp.example.com --deploy-hook routeros # acme.sh --deploy -d ftp.example.com --deploy-hook routeros
# ``` # ```
...@@ -48,6 +49,16 @@ ...@@ -48,6 +49,16 @@
# One optional thing to do as well is to create a script that updates # One optional thing to do as well is to create a script that updates
# all the required services and run that script in a single command. # all the required services and run that script in a single command.
# #
# To adopt parameters to `scp` and/or `ssh` set the optional
# `ROUTER_OS_SSH_CMD` and `ROUTER_OS_SCP_CMD` variables accordingly,
# see ssh(1) and scp(1) for parameters to those commands.
#
# Example:
# ```ssh
# export ROUTER_OS_SSH_CMD="ssh -i /acme.sh/.ssh/router.example.com -o UserKnownHostsFile=/acme.sh/.ssh/known_hosts"
# export ROUTER_OS_SCP_CMD="scp -i /acme.sh/.ssh/router.example.com -o UserKnownHostsFile=/acme.sh/.ssh/known_hosts"
# ````
#
# returns 0 means success, otherwise error. # returns 0 means success, otherwise error.
######## Public functions ##################### ######## Public functions #####################
...@@ -59,6 +70,7 @@ routeros_deploy() { ...@@ -59,6 +70,7 @@ routeros_deploy() {
_ccert="$3" _ccert="$3"
_cca="$4" _cca="$4"
_cfullchain="$5" _cfullchain="$5"
_err_code=0
_debug _cdomain "$_cdomain" _debug _cdomain "$_cdomain"
_debug _ckey "$_ckey" _debug _ckey "$_ckey"
...@@ -66,29 +78,70 @@ routeros_deploy() { ...@@ -66,29 +78,70 @@ routeros_deploy() {
_debug _cca "$_cca" _debug _cca "$_cca"
_debug _cfullchain "$_cfullchain" _debug _cfullchain "$_cfullchain"
_getdeployconf ROUTER_OS_HOST
if [ -z "$ROUTER_OS_HOST" ]; then if [ -z "$ROUTER_OS_HOST" ]; then
_debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct." _debug "Using _cdomain as ROUTER_OS_HOST, please set if not correct."
ROUTER_OS_HOST="$_cdomain" ROUTER_OS_HOST="$_cdomain"
fi fi
_getdeployconf ROUTER_OS_USERNAME
if [ -z "$ROUTER_OS_USERNAME" ]; then if [ -z "$ROUTER_OS_USERNAME" ]; then
_err "Need to set the env variable ROUTER_OS_USERNAME" _err "Need to set the env variable ROUTER_OS_USERNAME"
return 1 return 1
fi fi
_getdeployconf ROUTER_OS_PORT
if [ -z "$ROUTER_OS_PORT" ]; then
_debug "Using default port 22 as ROUTER_OS_PORT, please set if not correct."
ROUTER_OS_PORT=22
fi
_getdeployconf ROUTER_OS_SSH_CMD
if [ -z "$ROUTER_OS_SSH_CMD" ]; then
_debug "Use default ssh setup."
ROUTER_OS_SSH_CMD="ssh -p $ROUTER_OS_PORT"
fi
_getdeployconf ROUTER_OS_SCP_CMD
if [ -z "$ROUTER_OS_SCP_CMD" ]; then
_debug "USe default scp setup."
ROUTER_OS_SCP_CMD="scp -P $ROUTER_OS_PORT"
fi
_getdeployconf ROUTER_OS_ADDITIONAL_SERVICES
if [ -z "$ROUTER_OS_ADDITIONAL_SERVICES" ]; then if [ -z "$ROUTER_OS_ADDITIONAL_SERVICES" ]; then
_debug "Not enabling additional services" _debug "Not enabling additional services"
ROUTER_OS_ADDITIONAL_SERVICES="" ROUTER_OS_ADDITIONAL_SERVICES=""
fi fi
_info "Trying to push key '$_ckey' to router" _savedeployconf ROUTER_OS_HOST "$ROUTER_OS_HOST"
scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key" _savedeployconf ROUTER_OS_USERNAME "$ROUTER_OS_USERNAME"
_info "Trying to push cert '$_cfullchain' to router" _savedeployconf ROUTER_OS_PORT "$ROUTER_OS_PORT"
scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer" _savedeployconf ROUTER_OS_SSH_CMD "$ROUTER_OS_SSH_CMD"
DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive \ _savedeployconf ROUTER_OS_SCP_CMD "$ROUTER_OS_SCP_CMD"
source=\"## generated by routeros deploy script in acme.sh;\ _savedeployconf ROUTER_OS_ADDITIONAL_SERVICES "$ROUTER_OS_ADDITIONAL_SERVICES"
\n/certificate remove [ find name=$_cdomain.cer_0 ];\
# push key to routeros
if ! _scp_certificate "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"; then
return $_err_code
fi
# push certificate chain to routeros
if ! _scp_certificate "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer"; then
return $_err_code
fi
DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=$ROUTER_OS_USERNAME \
comment=\"generated by routeros deploy script in acme.sh\" \
source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
\n/certificate remove [ find name=$_cdomain.cer_1 ];\ \n/certificate remove [ find name=$_cdomain.cer_1 ];\
\n/certificate remove [ find name=$_cdomain.cer_2 ];\
\ndelay 1;\ \ndelay 1;\
\n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\";\ \n/certificate import file-name=$_cdomain.cer passphrase=\\\"\\\";\
\n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\";\ \n/certificate import file-name=$_cdomain.key passphrase=\\\"\\\";\
...@@ -100,12 +153,51 @@ source=\"## generated by routeros deploy script in acme.sh;\ ...@@ -100,12 +153,51 @@ source=\"## generated by routeros deploy script in acme.sh;\
\n$ROUTER_OS_ADDITIONAL_SERVICES;\ \n$ROUTER_OS_ADDITIONAL_SERVICES;\
\n\" \n\"
" "
# shellcheck disable=SC2029
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "$DEPLOY_SCRIPT_CMD" if ! _ssh_remote_cmd "$DEPLOY_SCRIPT_CMD"; then
# shellcheck disable=SC2029 return $_err_code
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script run \"LE Cert Deploy - $_cdomain\"" fi
# shellcheck disable=SC2029
ssh "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "/system script remove \"LE Cert Deploy - $_cdomain\"" if ! _ssh_remote_cmd "/system script run \"LE Cert Deploy - $_cdomain\""; then
return $_err_code
fi
if ! _ssh_remote_cmd "/system script remove \"LE Cert Deploy - $_cdomain\""; then
return $_err_code
fi
return 0 return 0
} }
# inspired by deploy/ssh.sh
_ssh_remote_cmd() {
_cmd="$1"
_secure_debug "Remote commands to execute: $_cmd"
_info "Submitting sequence of commands to routeros"
# quotations in bash cmd below intended. Squash travis spellcheck error
# shellcheck disable=SC2029
$ROUTER_OS_SSH_CMD "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST" "$_cmd"
_err_code="$?"
if [ "$_err_code" != "0" ]; then
_err "Error code $_err_code returned from routeros"
fi
return $_err_code
}
_scp_certificate() {
_src="$1"
_dst="$2"
_secure_debug "scp '$_src' to '$_dst'"
_info "Push key '$_src' to routeros"
$ROUTER_OS_SCP_CMD "$_src" "$_dst"
_err_code="$?"
if [ "$_err_code" != "0" ]; then
_err "Error code $_err_code returned from scp"
fi
return $_err_code
}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
# The following examples are for QNAP NAS running QTS 4.2 # The following examples are for QNAP NAS running QTS 4.2
# export DEPLOY_SSH_CMD="" # defaults to "ssh -T" # export DEPLOY_SSH_CMD="" # defaults to "ssh -T"
# export DEPLOY_SSH_USER="admin" # required # export DEPLOY_SSH_USER="admin" # required
# export DEPLOY_SSH_SERVER="qnap" # defaults to domain name # export DEPLOY_SSH_SERVER="host1 host2:8022 192.168.0.1:9022" # defaults to domain name, support multiple servers with optional port
# export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem" # export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
# export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem" # export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem"
# export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem" # export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
# export DEPLOY_SSH_BACKUP="" # yes or no, default to yes or previously saved value # export DEPLOY_SSH_BACKUP="" # yes or no, default to yes or previously saved value
# export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy" # path on remote system. Defaults to .acme_ssh_deploy # export DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy" # path on remote system. Defaults to .acme_ssh_deploy
# export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value # export DEPLOY_SSH_MULTI_CALL="" # yes or no, default to no or previously saved value
# export DEPLOY_SSH_USE_SCP="" yes or no, default to no
# export DEPLOY_SSH_SCP_CMD="" defaults to "scp -q"
# #
######## Public functions ##################### ######## Public functions #####################
...@@ -35,11 +37,6 @@ ssh_deploy() { ...@@ -35,11 +37,6 @@ ssh_deploy() {
_cfullchain="$5" _cfullchain="$5"
_deploy_ssh_servers="" _deploy_ssh_servers=""
if [ -f "$DOMAIN_CONF" ]; then
# shellcheck disable=SC1090
. "$DOMAIN_CONF"
fi
_debug _cdomain "$_cdomain" _debug _cdomain "$_cdomain"
_debug _ckey "$_ckey" _debug _ckey "$_ckey"
_debug _ccert "$_ccert" _debug _ccert "$_ccert"
...@@ -47,60 +44,134 @@ ssh_deploy() { ...@@ -47,60 +44,134 @@ ssh_deploy() {
_debug _cfullchain "$_cfullchain" _debug _cfullchain "$_cfullchain"
# USER is required to login by SSH to remote host. # USER is required to login by SSH to remote host.
_migratedeployconf Le_Deploy_ssh_user DEPLOY_SSH_USER
_getdeployconf DEPLOY_SSH_USER
_debug2 DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
if [ -z "$DEPLOY_SSH_USER" ]; then if [ -z "$DEPLOY_SSH_USER" ]; then
if [ -z "$Le_Deploy_ssh_user" ]; then
_err "DEPLOY_SSH_USER not defined." _err "DEPLOY_SSH_USER not defined."
return 1 return 1
fi fi
else _savedeployconf DEPLOY_SSH_USER "$DEPLOY_SSH_USER"
Le_Deploy_ssh_user="$DEPLOY_SSH_USER"
_savedomainconf Le_Deploy_ssh_user "$Le_Deploy_ssh_user"
fi
# SERVER is optional. If not provided then use _cdomain # SERVER is optional. If not provided then use _cdomain
if [ -n "$DEPLOY_SSH_SERVER" ]; then _migratedeployconf Le_Deploy_ssh_server DEPLOY_SSH_SERVER
Le_Deploy_ssh_server="$DEPLOY_SSH_SERVER" _getdeployconf DEPLOY_SSH_SERVER
_savedomainconf Le_Deploy_ssh_server "$Le_Deploy_ssh_server" _debug2 DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
elif [ -z "$Le_Deploy_ssh_server" ]; then if [ -z "$DEPLOY_SSH_SERVER" ]; then
Le_Deploy_ssh_server="$_cdomain" DEPLOY_SSH_SERVER="$_cdomain"
fi fi
_savedeployconf DEPLOY_SSH_SERVER "$DEPLOY_SSH_SERVER"
# CMD is optional. If not provided then use ssh # CMD is optional. If not provided then use ssh
if [ -n "$DEPLOY_SSH_CMD" ]; then _migratedeployconf Le_Deploy_ssh_cmd DEPLOY_SSH_CMD
Le_Deploy_ssh_cmd="$DEPLOY_SSH_CMD" _getdeployconf DEPLOY_SSH_CMD
_savedomainconf Le_Deploy_ssh_cmd "$Le_Deploy_ssh_cmd" _debug2 DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
elif [ -z "$Le_Deploy_ssh_cmd" ]; then if [ -z "$DEPLOY_SSH_CMD" ]; then
Le_Deploy_ssh_cmd="ssh -T" DEPLOY_SSH_CMD="ssh -T"
fi fi
_savedeployconf DEPLOY_SSH_CMD "$DEPLOY_SSH_CMD"
# BACKUP is optional. If not provided then default to previously saved value or yes. # BACKUP is optional. If not provided then default to previously saved value or yes.
if [ "$DEPLOY_SSH_BACKUP" = "no" ]; then _migratedeployconf Le_Deploy_ssh_backup DEPLOY_SSH_BACKUP
Le_Deploy_ssh_backup="no" _getdeployconf DEPLOY_SSH_BACKUP
elif [ -z "$Le_Deploy_ssh_backup" ] || [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then _debug2 DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
Le_Deploy_ssh_backup="yes" if [ -z "$DEPLOY_SSH_BACKUP" ]; then
DEPLOY_SSH_BACKUP="yes"
fi fi
_savedomainconf Le_Deploy_ssh_backup "$Le_Deploy_ssh_backup" _savedeployconf DEPLOY_SSH_BACKUP "$DEPLOY_SSH_BACKUP"
# BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy # BACKUP_PATH is optional. If not provided then default to previously saved value or .acme_ssh_deploy
if [ -n "$DEPLOY_SSH_BACKUP_PATH" ]; then _migratedeployconf Le_Deploy_ssh_backup_path DEPLOY_SSH_BACKUP_PATH
Le_Deploy_ssh_backup_path="$DEPLOY_SSH_BACKUP_PATH" _getdeployconf DEPLOY_SSH_BACKUP_PATH
elif [ -z "$Le_Deploy_ssh_backup_path" ]; then _debug2 DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
Le_Deploy_ssh_backup_path=".acme_ssh_deploy" if [ -z "$DEPLOY_SSH_BACKUP_PATH" ]; then
DEPLOY_SSH_BACKUP_PATH=".acme_ssh_deploy"
fi fi
_savedomainconf Le_Deploy_ssh_backup_path "$Le_Deploy_ssh_backup_path" _savedeployconf DEPLOY_SSH_BACKUP_PATH "$DEPLOY_SSH_BACKUP_PATH"
# MULTI_CALL is optional. If not provided then default to previously saved # MULTI_CALL is optional. If not provided then default to previously saved
# value (which may be undefined... equivalent to "no"). # value (which may be undefined... equivalent to "no").
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then _migratedeployconf Le_Deploy_ssh_multi_call DEPLOY_SSH_MULTI_CALL
Le_Deploy_ssh_multi_call="yes" _getdeployconf DEPLOY_SSH_MULTI_CALL
_savedomainconf Le_Deploy_ssh_multi_call "$Le_Deploy_ssh_multi_call" _debug2 DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
elif [ "$DEPLOY_SSH_MULTI_CALL" = "no" ]; then if [ -z "$DEPLOY_SSH_MULTI_CALL" ]; then
Le_Deploy_ssh_multi_call="" DEPLOY_SSH_MULTI_CALL="no"
_cleardomainconf Le_Deploy_ssh_multi_call fi
_savedeployconf DEPLOY_SSH_MULTI_CALL "$DEPLOY_SSH_MULTI_CALL"
# KEYFILE is optional.
# If provided then private key will be copied to provided filename.
_migratedeployconf Le_Deploy_ssh_keyfile DEPLOY_SSH_KEYFILE
_getdeployconf DEPLOY_SSH_KEYFILE
_debug2 DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
_savedeployconf DEPLOY_SSH_KEYFILE "$DEPLOY_SSH_KEYFILE"
fi
# CERTFILE is optional.
# If provided then certificate will be copied or appended to provided filename.
_migratedeployconf Le_Deploy_ssh_certfile DEPLOY_SSH_CERTFILE
_getdeployconf DEPLOY_SSH_CERTFILE
_debug2 DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
_savedeployconf DEPLOY_SSH_CERTFILE "$DEPLOY_SSH_CERTFILE"
fi
# CAFILE is optional.
# If provided then CA intermediate certificate will be copied or appended to provided filename.
_migratedeployconf Le_Deploy_ssh_cafile DEPLOY_SSH_CAFILE
_getdeployconf DEPLOY_SSH_CAFILE
_debug2 DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
if [ -n "$DEPLOY_SSH_CAFILE" ]; then
_savedeployconf DEPLOY_SSH_CAFILE "$DEPLOY_SSH_CAFILE"
fi
# FULLCHAIN is optional.
# If provided then fullchain certificate will be copied or appended to provided filename.
_migratedeployconf Le_Deploy_ssh_fullchain DEPLOY_SSH_FULLCHAIN
_getdeployconf DEPLOY_SSH_FULLCHAIN
_debug2 DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
_savedeployconf DEPLOY_SSH_FULLCHAIN "$DEPLOY_SSH_FULLCHAIN"
fi
# REMOTE_CMD is optional.
# If provided then this command will be executed on remote host.
_migratedeployconf Le_Deploy_ssh_remote_cmd DEPLOY_SSH_REMOTE_CMD
_getdeployconf DEPLOY_SSH_REMOTE_CMD
_debug2 DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
_savedeployconf DEPLOY_SSH_REMOTE_CMD "$DEPLOY_SSH_REMOTE_CMD"
fi
# USE_SCP is optional. If not provided then default to previously saved
# value (which may be undefined... equivalent to "no").
_getdeployconf DEPLOY_SSH_USE_SCP
_debug2 DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
if [ -z "$DEPLOY_SSH_USE_SCP" ]; then
DEPLOY_SSH_USE_SCP="no"
fi
_savedeployconf DEPLOY_SSH_USE_SCP "$DEPLOY_SSH_USE_SCP"
# SCP_CMD is optional. If not provided then use scp
_getdeployconf DEPLOY_SSH_SCP_CMD
_debug2 DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
if [ -z "$DEPLOY_SSH_SCP_CMD" ]; then
DEPLOY_SSH_SCP_CMD="scp -q"
fi fi
_savedeployconf DEPLOY_SSH_SCP_CMD "$DEPLOY_SSH_SCP_CMD"
_deploy_ssh_servers=$Le_Deploy_ssh_server if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
for Le_Deploy_ssh_server in $_deploy_ssh_servers; do DEPLOY_SSH_MULTI_CALL="yes"
_info "Using scp as alternate method for copying files. Multicall Mode is implicit"
elif [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
_info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host"
else
_info "Required commands batched and sent in single call to remote host"
fi
_deploy_ssh_servers="$DEPLOY_SSH_SERVER"
for DEPLOY_SSH_SERVER in $_deploy_ssh_servers; do
_ssh_deploy _ssh_deploy
done done
} }
...@@ -110,16 +181,25 @@ _ssh_deploy() { ...@@ -110,16 +181,25 @@ _ssh_deploy() {
_cmdstr="" _cmdstr=""
_backupprefix="" _backupprefix=""
_backupdir="" _backupdir=""
_local_cert_file=""
_local_ca_file=""
_local_full_file=""
_info "Deploy certificates to remote server $Le_Deploy_ssh_user@$Le_Deploy_ssh_server" case $DEPLOY_SSH_SERVER in
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then *:*)
_info "Using MULTI_CALL mode... Required commands sent in multiple calls to remote host" _host=${DEPLOY_SSH_SERVER%:*}
else _port=${DEPLOY_SSH_SERVER##*:}
_info "Required commands batched and sent in single call to remote host" ;;
fi *)
_host=$DEPLOY_SSH_SERVER
_port=
;;
esac
_info "Deploy certificates to remote server $DEPLOY_SSH_USER@$_host:$_port"
if [ "$Le_Deploy_ssh_backup" = "yes" ]; then if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
_backupprefix="$Le_Deploy_ssh_backup_path/$_cdomain-backup" _backupprefix="$DEPLOY_SSH_BACKUP_PATH/$_cdomain-backup"
_backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')" _backupdir="$_backupprefix-$(_utc_date | tr ' ' '-')"
# run cleanup on the backup directory, erase all older # run cleanup on the backup directory, erase all older
# than 180 days (15552000 seconds). # than 180 days (15552000 seconds).
...@@ -131,7 +211,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d ...@@ -131,7 +211,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
_cmdstr="mkdir -p $_backupdir; $_cmdstr" _cmdstr="mkdir -p $_backupdir; $_cmdstr"
_info "Backup of old certificate files will be placed in remote directory $_backupdir" _info "Backup of old certificate files will be placed in remote directory $_backupdir"
_info "Backup directories erased after 180 days." _info "Backup directories erased after 180 days."
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code return $_err_code
fi fi
...@@ -139,119 +219,184 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d ...@@ -139,119 +219,184 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
fi fi
fi fi
# KEYFILE is optional.
# If provided then private key will be copied to provided filename.
if [ -n "$DEPLOY_SSH_KEYFILE" ]; then if [ -n "$DEPLOY_SSH_KEYFILE" ]; then
Le_Deploy_ssh_keyfile="$DEPLOY_SSH_KEYFILE" if [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
_savedomainconf Le_Deploy_ssh_keyfile "$Le_Deploy_ssh_keyfile"
fi
if [ -n "$Le_Deploy_ssh_keyfile" ]; then
if [ "$Le_Deploy_ssh_backup" = "yes" ]; then
# backup file we are about to overwrite. # backup file we are about to overwrite.
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_keyfile $_backupdir >/dev/null;" _cmdstr="$_cmdstr cp $DEPLOY_SSH_KEYFILE $_backupdir >/dev/null;"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi fi
# copy new certificate into file. _cmdstr=""
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $Le_Deploy_ssh_keyfile;" fi
_info "will copy private key to remote file $Le_Deploy_ssh_keyfile" fi
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
# copy new key into file.
if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
# scp the file
if ! _scp_remote_cmd "$_ckey" "$DEPLOY_SSH_KEYFILE"; then
return $_err_code
fi
else
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
_info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code return $_err_code
fi fi
_cmdstr="" _cmdstr=""
fi fi
fi fi
fi
# CERTFILE is optional.
# If provided then certificate will be copied or appended to provided filename.
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE"
_savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile"
fi
if [ -n "$Le_Deploy_ssh_certfile" ]; then
_pipe=">" _pipe=">"
if [ "$Le_Deploy_ssh_certfile" = "$Le_Deploy_ssh_keyfile" ]; then if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
# if filename is same as previous file then append. # if filename is same as previous file then append.
_pipe=">>" _pipe=">>"
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
# backup file we are about to overwrite. # backup file we are about to overwrite.
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_certfile $_backupdir >/dev/null;" _cmdstr="$_cmdstr cp $DEPLOY_SSH_CERTFILE $_backupdir >/dev/null;"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi fi
_cmdstr=""
fi
fi
# copy new certificate into file. # copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $Le_Deploy_ssh_certfile;" if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
_info "will copy certificate to remote file $Le_Deploy_ssh_certfile" # scp the file
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then _local_cert_file=$(_mktemp)
if [ "$DEPLOY_SSH_CERTFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
cat "$_ckey" >>"$_local_cert_file"
fi
cat "$_ccert" >>"$_local_cert_file"
if ! _scp_remote_cmd "$_local_cert_file" "$DEPLOY_SSH_CERTFILE"; then
return $_err_code
fi
else
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_ccert")\" $_pipe $DEPLOY_SSH_CERTFILE;"
_info "will copy certificate to remote file $DEPLOY_SSH_CERTFILE"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code return $_err_code
fi fi
_cmdstr="" _cmdstr=""
fi fi
fi fi
fi
# CAFILE is optional.
# If provided then CA intermediate certificate will be copied or appended to provided filename.
if [ -n "$DEPLOY_SSH_CAFILE" ]; then if [ -n "$DEPLOY_SSH_CAFILE" ]; then
Le_Deploy_ssh_cafile="$DEPLOY_SSH_CAFILE"
_savedomainconf Le_Deploy_ssh_cafile "$Le_Deploy_ssh_cafile"
fi
if [ -n "$Le_Deploy_ssh_cafile" ]; then
_pipe=">" _pipe=">"
if [ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_keyfile" ] || if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ] ||
[ "$Le_Deploy_ssh_cafile" = "$Le_Deploy_ssh_certfile" ]; then [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
# if filename is same as previous file then append. # if filename is same as previous file then append.
_pipe=">>" _pipe=">>"
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
# backup file we are about to overwrite. # backup file we are about to overwrite.
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_cafile $_backupdir >/dev/null;" _cmdstr="$_cmdstr cp $DEPLOY_SSH_CAFILE $_backupdir >/dev/null;"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi fi
fi
# copy new certificate into file. # copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $Le_Deploy_ssh_cafile;" if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
_info "will copy CA file to remote file $Le_Deploy_ssh_cafile" # scp the file
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then _local_ca_file=$(_mktemp)
if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_KEYFILE" ]; then
cat "$_ckey" >>"$_local_ca_file"
fi
if [ "$DEPLOY_SSH_CAFILE" = "$DEPLOY_SSH_CERTFILE" ]; then
cat "$_ccert" >>"$_local_ca_file"
fi
cat "$_cca" >>"$_local_ca_file"
if ! _scp_remote_cmd "$_local_ca_file" "$DEPLOY_SSH_CAFILE"; then
return $_err_code
fi
else
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_cca")\" $_pipe $DEPLOY_SSH_CAFILE;"
_info "will copy CA file to remote file $DEPLOY_SSH_CAFILE"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code return $_err_code
fi fi
_cmdstr="" _cmdstr=""
fi fi
fi fi
fi
# FULLCHAIN is optional.
# If provided then fullchain certificate will be copied or appended to provided filename.
if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then if [ -n "$DEPLOY_SSH_FULLCHAIN" ]; then
Le_Deploy_ssh_fullchain="$DEPLOY_SSH_FULLCHAIN"
_savedomainconf Le_Deploy_ssh_fullchain "$Le_Deploy_ssh_fullchain"
fi
if [ -n "$Le_Deploy_ssh_fullchain" ]; then
_pipe=">" _pipe=">"
if [ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_keyfile" ] || if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ] ||
[ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_certfile" ] || [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ] ||
[ "$Le_Deploy_ssh_fullchain" = "$Le_Deploy_ssh_cafile" ]; then [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
# if filename is same as previous file then append. # if filename is same as previous file then append.
_pipe=">>" _pipe=">>"
elif [ "$Le_Deploy_ssh_backup" = "yes" ]; then elif [ "$DEPLOY_SSH_BACKUP" = "yes" ]; then
# backup file we are about to overwrite. # backup file we are about to overwrite.
_cmdstr="$_cmdstr cp $Le_Deploy_ssh_fullchain $_backupdir >/dev/null;" _cmdstr="$_cmdstr cp $DEPLOY_SSH_FULLCHAIN $_backupdir >/dev/null;"
if [ "$DEPLOY_SSH_FULLCHAIN" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code
fi
_cmdstr=""
fi
fi fi
# copy new certificate into file. # copy new certificate into file.
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $Le_Deploy_ssh_fullchain;" if [ "$DEPLOY_SSH_USE_SCP" = "yes" ]; then
_info "will copy fullchain to remote file $Le_Deploy_ssh_fullchain" # scp the file
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then _local_full_file=$(_mktemp)
if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_KEYFILE" ]; then
cat "$_ckey" >>"$_local_full_file"
fi
if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CERTFILE" ]; then
cat "$_ccert" >>"$_local_full_file"
fi
if [ "$DEPLOY_SSH_FULLCHAIN" = "$DEPLOY_SSH_CAFILE" ]; then
cat "$_cca" >>"$_local_full_file"
fi
cat "$_cfullchain" >>"$_local_full_file"
if ! _scp_remote_cmd "$_local_full_file" "$DEPLOY_SSH_FULLCHAIN"; then
return $_err_code
fi
else
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_cfullchain")\" $_pipe $DEPLOY_SSH_FULLCHAIN;"
_info "will copy fullchain to remote file $DEPLOY_SSH_FULLCHAIN"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code return $_err_code
fi fi
_cmdstr="" _cmdstr=""
fi fi
fi fi
fi
# REMOTE_CMD is optional. # cleanup local files if any
# If provided then this command will be executed on remote host. if [ -f "$_local_cert_file" ]; then
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then rm -f "$_local_cert_file"
Le_Deploy_ssh_remote_cmd="$DEPLOY_SSH_REMOTE_CMD" fi
_savedomainconf Le_Deploy_ssh_remote_cmd "$Le_Deploy_ssh_remote_cmd" if [ -f "$_local_ca_file" ]; then
rm -f "$_local_ca_file"
fi fi
if [ -n "$Le_Deploy_ssh_remote_cmd" ]; then if [ -f "$_local_full_file" ]; then
_cmdstr="$_cmdstr $Le_Deploy_ssh_remote_cmd;" rm -f "$_local_full_file"
_info "Will execute remote command $Le_Deploy_ssh_remote_cmd" fi
if [ "$Le_Deploy_ssh_multi_call" = "yes" ]; then
if [ -n "$DEPLOY_SSH_REMOTE_CMD" ]; then
_cmdstr="$_cmdstr $DEPLOY_SSH_REMOTE_CMD;"
_info "Will execute remote command $DEPLOY_SSH_REMOTE_CMD"
if [ "$DEPLOY_SSH_MULTI_CALL" = "yes" ]; then
if ! _ssh_remote_cmd "$_cmdstr"; then if ! _ssh_remote_cmd "$_cmdstr"; then
return $_err_code return $_err_code
fi fi
...@@ -265,17 +410,25 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d ...@@ -265,17 +410,25 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
return $_err_code return $_err_code
fi fi
fi fi
# cleanup in case all is ok
return 0 return 0
} }
#cmd #cmd
_ssh_remote_cmd() { _ssh_remote_cmd() {
_cmd="$1" _cmd="$1"
_ssh_cmd="$DEPLOY_SSH_CMD"
if [ -n "$_port" ]; then
_ssh_cmd="$_ssh_cmd -p $_port"
fi
_secure_debug "Remote commands to execute: $_cmd" _secure_debug "Remote commands to execute: $_cmd"
_info "Submitting sequence of commands to remote server by ssh" _info "Submitting sequence of commands to remote server by $_ssh_cmd"
# quotations in bash cmd below intended. Squash travis spellcheck error # quotations in bash cmd below intended. Squash travis spellcheck error
# shellcheck disable=SC2029 # shellcheck disable=SC2029
$Le_Deploy_ssh_cmd "$Le_Deploy_ssh_user@$Le_Deploy_ssh_server" sh -c "'$_cmd'" $_ssh_cmd "$DEPLOY_SSH_USER@$_host" sh -c "'$_cmd'"
_err_code="$?" _err_code="$?"
if [ "$_err_code" != "0" ]; then if [ "$_err_code" != "0" ]; then
...@@ -284,3 +437,26 @@ _ssh_remote_cmd() { ...@@ -284,3 +437,26 @@ _ssh_remote_cmd() {
return $_err_code return $_err_code
} }
# cmd scp
_scp_remote_cmd() {
_src=$1
_dest=$2
_scp_cmd="$DEPLOY_SSH_SCP_CMD"
if [ -n "$_port" ]; then
_scp_cmd="$_scp_cmd -P $_port"
fi
_secure_debug "Remote copy source $_src to destination $_dest"
_info "Submitting secure copy by $_scp_cmd"
$_scp_cmd "$_src" "$DEPLOY_SSH_USER"@"$_host":"$_dest"
_err_code="$?"
if [ "$_err_code" != "0" ]; then
_err "Error code $_err_code returned from scp"
fi
return $_err_code
}
#!/usr/bin/env sh #!/bin/bash
# Here is a script to deploy cert to Synology DSM ################################################################################
# # ACME.sh 3rd party deploy plugin for Synology DSM
# it requires the jq and curl are in the $PATH and the following ################################################################################
# environment variables must be set: # Authors: Brian Hartvigsen (creator), https://github.com/tresni
# # Martin Arndt (contributor), https://troublezone.net/
# SYNO_Username - Synology Username to login (must be an administrator) # Updated: 2023-07-03
# SYNO_Password - Synology Password to login # Issues: https://github.com/acmesh-official/acme.sh/issues/2727
# SYNO_Certificate - Certificate description to target for replacement ################################################################################
# # Usage:
# The following environmental variables may be set if you don't like their # 1. export SYNO_Username="adminUser"
# default values: # 2. export SYNO_Password="adminPassword"
# # Optional exports (shown values are the defaults):
# SYNO_Scheme - defaults to http # - export SYNO_Certificate="" to replace a specific certificate via description
# SYNO_Hostname - defaults to localhost # - export SYNO_Scheme="http"
# SYNO_Port - defaults to 5000 # - export SYNO_Hostname="localhost"
# SYNO_DID - device ID to skip OTP - defaults to empty # - export SYNO_Port="5000"
# # - export SYNO_Device_Name="CertRenewal" - required for skipping 2FA-OTP
#returns 0 means success, otherwise error. # - export SYNO_Device_ID="" - required for skipping 2FA-OTP
# 3. acme.sh --deploy --deploy-hook synology_dsm -d example.com
######## Public functions ##################### ################################################################################
# Dependencies:
_syno_get_cookie_data() { # - jq & curl
grep -i "\W$1=" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o "$1=[^;]*;" | tr -d ';' ################################################################################
} # Return value:
# 0 means success, otherwise error.
################################################################################
########## Public functions ####################################################
#domain keyfile certfile cafile fullchain #domain keyfile certfile cafile fullchain
synology_dsm_deploy() { synology_dsm_deploy() {
_cdomain="$1" _cdomain="$1"
_ckey="$2" _ckey="$2"
_ccert="$3" _ccert="$3"
...@@ -35,91 +37,146 @@ synology_dsm_deploy() { ...@@ -35,91 +37,146 @@ synology_dsm_deploy() {
_debug _cdomain "$_cdomain" _debug _cdomain "$_cdomain"
# Get Username and Password, but don't save until we successfully authenticate # Get username & password, but don't save until we authenticated successfully
_getdeployconf SYNO_Username _getdeployconf SYNO_Username
_getdeployconf SYNO_Password _getdeployconf SYNO_Password
_getdeployconf SYNO_Create _getdeployconf SYNO_Create
_getdeployconf SYNO_DID _getdeployconf SYNO_DID
_getdeployconf SYNO_TOTP_SECRET
_getdeployconf SYNO_Device_Name
_getdeployconf SYNO_Device_ID
if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then
_err "SYNO_Username & SYNO_Password must be set" _err "SYNO_Username & SYNO_Password must be set"
return 1 return 1
fi fi
if [ -n "${SYNO_Device_Name:-}" ] && [ -z "${SYNO_Device_ID:-}" ]; then
_err "SYNO_Device_Name set, but SYNO_Device_ID is empty"
return 1
fi
_debug2 SYNO_Username "$SYNO_Username" _debug2 SYNO_Username "$SYNO_Username"
_secure_debug2 SYNO_Password "$SYNO_Password" _secure_debug2 SYNO_Password "$SYNO_Password"
_debug2 SYNO_Create "$SYNO_Create"
_debug2 SYNO_Device_Name "$SYNO_Device_Name"
_secure_debug2 SYNO_Device_ID "$SYNO_Device_ID"
# Optional scheme, hostname, and port for Synology DSM # Optional scheme, hostname & port for Synology DSM
_getdeployconf SYNO_Scheme _getdeployconf SYNO_Scheme
_getdeployconf SYNO_Hostname _getdeployconf SYNO_Hostname
_getdeployconf SYNO_Port _getdeployconf SYNO_Port
# default vaules for scheme, hostname, and port # Default values for scheme, hostname & port
# defaulting to localhost and http because it's localhost... # Defaulting to localhost & http, because it's localhost
[ -n "${SYNO_Scheme}" ] || SYNO_Scheme="http" [ -n "${SYNO_Scheme}" ] || SYNO_Scheme="http"
[ -n "${SYNO_Hostname}" ] || SYNO_Hostname="localhost" [ -n "${SYNO_Hostname}" ] || SYNO_Hostname="localhost"
[ -n "${SYNO_Port}" ] || SYNO_Port="5000" [ -n "${SYNO_Port}" ] || SYNO_Port="5000"
_savedeployconf SYNO_Scheme "$SYNO_Scheme" _savedeployconf SYNO_Scheme "$SYNO_Scheme"
_savedeployconf SYNO_Hostname "$SYNO_Hostname" _savedeployconf SYNO_Hostname "$SYNO_Hostname"
_savedeployconf SYNO_Port "$SYNO_Port" _savedeployconf SYNO_Port "$SYNO_Port"
_debug2 SYNO_Scheme "$SYNO_Scheme" _debug2 SYNO_Scheme "$SYNO_Scheme"
_debug2 SYNO_Hostname "$SYNO_Hostname" _debug2 SYNO_Hostname "$SYNO_Hostname"
_debug2 SYNO_Port "$SYNO_Port" _debug2 SYNO_Port "$SYNO_Port"
# Get the certificate description, but don't save it until we verfiy it's real # Get the certificate description, but don't save it until we verify it's real
_getdeployconf SYNO_Certificate _getdeployconf SYNO_Certificate
_debug SYNO_Certificate "${SYNO_Certificate:-}" _debug SYNO_Certificate "${SYNO_Certificate:-}"
# shellcheck disable=SC1003 # We are not trying to escape a single quote
if printf "%s" "$SYNO_Certificate" | grep '\\'; then
_err "Do not use a backslash (\) in your certificate description"
return 1
fi
_base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port" _base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port"
_debug _base_url "$_base_url" _debug _base_url "$_base_url"
# Login, get the token from JSON and session id from cookie _debug "Getting API version"
response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
_debug3 response "$response"
_debug3 api_version "$api_version"
# Login, get the session ID & SynoToken from JSON
_info "Logging into $SYNO_Hostname:$SYNO_Port" _info "Logging into $SYNO_Hostname:$SYNO_Port"
encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)" encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)"
encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)" encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)"
encoded_did="$(printf "%s" "$SYNO_DID" | _url_encode)"
response=$(_get "$_base_url/webman/login.cgi?username=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_id=$encoded_did" 1) otp_code=""
token=$(echo "$response" | grep -i "X-SYNO-TOKEN:" | sed -n 's/^X-SYNO-TOKEN: \(.*\)$/\1/pI' | tr -d "\r\n") # START - DEPRECATED, only kept for legacy compatibility reasons
if [ -n "$SYNO_TOTP_SECRET" ]; then
_info "WARNING: Usage of SYNO_TOTP_SECRET is deprecated!"
_info " See synology_dsm.sh script or ACME.sh Wiki page for details:"
_info " https://github.com/acmesh-official/acme.sh/wiki/Synology-NAS-Guide"
DEPRECATED_otp_code=""
if _exists oathtool; then
DEPRECATED_otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)"
else
_err "oathtool could not be found, install oathtool to use SYNO_TOTP_SECRET"
return 1
fi
if [ -n "$SYNO_DID" ]; then
_H1="Cookie: did=$SYNO_DID"
export _H1
_debug3 H1 "${_H1}"
fi
response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$DEPRECATED_otp_code&device_name=certrenewal&device_id=$SYNO_DID" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
_debug3 response "$response" _debug3 response "$response"
_debug token "$token" # END - DEPRECATED, only kept for legacy compatibility reasons
# Get device ID if still empty first, otherwise log in right away
elif [ -z "${SYNO_Device_ID:-}" ]; then
printf "Enter OTP code for user '%s': " "$SYNO_Username"
read -r otp_code
if [ -z "${SYNO_Device_Name:-}" ]; then
printf "Enter device name or leave empty for default (CertRenewal): "
read -r SYNO_Device_Name
[ -n "${SYNO_Device_Name}" ] || SYNO_Device_Name="CertRenewal"
fi
response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name")
_debug3 response "$response"
SYNO_Device_ID=$(echo "$response" | grep "device_id" | sed -n 's/.*"device_id" *: *"\([^"]*\).*/\1/p')
_secure_debug2 SYNO_Device_ID "$SYNO_Device_ID"
else
response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID")
_debug3 response "$response"
fi
if [ -z "$token" ]; then sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p')
_err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme." token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
_err "Check your username and password." _debug "Session ID" "$sid"
_debug SynoToken "$token"
if [ -z "$SYNO_DID" ] && [ -z "$SYNO_Device_ID" ] || [ -z "$sid" ] || [ -z "$token" ]; then
_err "Unable to authenticate to $_base_url - check your username & password."
_err "If two-factor authentication is enabled for the user, set SYNO_Device_ID."
return 1 return 1
fi fi
_H1="Cookie: $(echo "$response" | _syno_get_cookie_data "id"); $(echo "$response" | _syno_get_cookie_data "smid")" _H1="X-SYNO-TOKEN: $token"
_H2="X-SYNO-TOKEN: $token"
export _H1 export _H1
export _H2
_debug2 H1 "${_H1}" _debug2 H1 "${_H1}"
_debug2 H2 "${_H2}"
# Now that we know the username and password are good, save them # Now that we know the username & password are good, save them
_savedeployconf SYNO_Username "$SYNO_Username" _savedeployconf SYNO_Username "$SYNO_Username"
_savedeployconf SYNO_Password "$SYNO_Password" _savedeployconf SYNO_Password "$SYNO_Password"
_savedeployconf SYNO_DID "$SYNO_DID" _savedeployconf SYNO_Device_Name "$SYNO_Device_Name"
_savedeployconf SYNO_Device_ID "$SYNO_Device_ID"
_info "Getting certificates in Synology DSM" _info "Getting certificates in Synology DSM"
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1" "$_base_url/webapi/entry.cgi") response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi")
_debug3 response "$response" _debug3 response "$response"
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$SYNO_Certificate\",\"id\":\"\([^\"]*\).*/\1/p") escaped_certificate="$(printf "%s" "$SYNO_Certificate" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
_debug escaped_certificate "$escaped_certificate"
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
_debug2 id "$id" _debug2 id "$id"
if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then
_err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set" _err "Unable to find certificate: $SYNO_Certificate & \$SYNO_Create is not set"
return 1 return 1
fi fi
# we've verified this certificate description is a thing, so save it # We've verified this certificate description is a thing, so save it
_savedeployconf SYNO_Certificate "$SYNO_Certificate" _savedeployconf SYNO_Certificate "$SYNO_Certificate" "base64"
default=false
if echo "$response" | sed -n "s/.*\"desc\":\"$SYNO_Certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
default=true
fi
_debug2 default "$default"
_info "Generate form POST request" _info "Generate form POST request"
nl="\0015\0012" nl="\0015\0012"
...@@ -129,24 +186,39 @@ synology_dsm_deploy() { ...@@ -129,24 +186,39 @@ synology_dsm_deploy() {
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012" content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id" content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_Certificate}" content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_Certificate}"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}${default}" if echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
_debug2 default "This is the default certificate"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true"
else
_debug2 default "This is NOT the default certificate"
fi
content="$content${nl}--$delim--${nl}" content="$content${nl}--$delim--${nl}"
content="$(printf "%b_" "$content")" content="$(printf "%b_" "$content")"
content="${content%_}" # protect trailing \n content="${content%_}" # protect trailing \n
_info "Upload certificate to the Synology DSM" _info "Upload certificate to the Synology DSM"
response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token" "" "POST" "multipart/form-data; boundary=${delim}") response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token&_sid=$sid" "" "POST" "multipart/form-data; boundary=${delim}")
_debug3 response "$response" _debug3 response "$response"
if ! echo "$response" | grep '"error":' >/dev/null; then if ! echo "$response" | grep '"error":' >/dev/null; then
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
_info "http services were restarted" _info "Restarting HTTP services succeeded"
else else
_info "http services were NOT restarted" _info "Restarting HTTP services failed"
fi fi
_logout
return 0 return 0
else else
_err "Unable to update certificate, error code $response" _err "Unable to update certificate, error code $response"
_logout
return 1 return 1
fi fi
} }
#################### Private functions below ##################################
_logout() {
# Logout to not occupy a permanent session, e.g. in DSM's "Connected Users" widget
response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=logout")
_debug3 response "$response"
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment