diff --git a/README.md b/README.md
index d535631..b5022c9 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
[](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- An ACME protocol client written purely in Shell (Unix shell) language.
- Full ACME protocol implementation.
+- Support ACME v1 and ACME v2
+- Support ACME v2 wildcard certs
- Simple, powerful and very easy to use. You only need 3 minutes to learn it.
- Bash, dash and sh compatible.
- Simplest shell script for Let's Encrypt free certificate client.
@@ -127,7 +129,7 @@ Ok, you are ready to issue certs now.
Show help message:
-```
+```sh
root@v1:~# acme.sh -h
```
@@ -164,16 +166,16 @@ You must have at least one domain there.
You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`.
-Generated/issued certs will be placed in `~/.acme.sh/example.com/`
+The certs will be placed in `~/.acme.sh/example.com/`
-The issued cert will be renewed automatically every **60** days.
+The certs will be renewed automatically every **60** days.
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
-# 3. Install the issued cert to Apache/Nginx etc.
+# 3. Install the cert to Apache/Nginx etc.
-After you issue a cert, you probably want to install/copy the cert to your Apache/Nginx or other servers.
+After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers.
You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future.
**Apache** example:
@@ -195,9 +197,9 @@ acme.sh --install-cert -d example.com \
Only the domain is required, all the other parameters are optional.
-The ownership and permission info of existing files are preserved. You may want to precreate the files to have defined ownership and permission.
+The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission.
-Install/copy the issued cert/key to the production Apache or Nginx path.
+Install/copy the cert/key to the production Apache or Nginx path.
The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`.
@@ -240,7 +242,7 @@ Particularly, if you are running an Apache server, you should use Apache mode in
Just set string "apache" as the second argument and it will force use of apache plugin automatically.
-```
+```sh
acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
```
@@ -260,47 +262,13 @@ It will configure nginx server automatically to verify the domain and then resto
So, the config is not changed.
-```
+```sh
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
```
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
-# 8. Use DNS mode:
-
-Support the `dns-01` challenge.
-
-```bash
-acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
-```
-
-You should get an output like below:
-
-```
-Add the following txt record:
-Domain:_acme-challenge.example.com
-Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c
-
-Add the following txt record:
-Domain:_acme-challenge.www.example.com
-Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-
-Please add those txt records to the domains. Waiting for the dns to take effect.
-```
-
-Then just rerun with `renew` argument:
-
-```bash
-acme.sh --renew -d example.com
-```
-
-Ok, it's finished.
-
-**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.**
-
-**Please use dns api mode instead.**
-
-# 9. Automatic DNS API integration
+# 8. Automatic DNS API integration
If your DNS provider supports API access, we can use that API to automatically issue the certs.
@@ -343,8 +311,10 @@ You don't have to do anything manually!
1. INWX (https://www.inwx.de/)
1. Servercow (https://servercow.de)
1. Namesilo (https://www.namesilo.com)
+1. InternetX autoDNS API (https://internetx.com)
+1. Azure DNS
+1. selectel.com(selectel.ru) DNS API
1. DNSEver(https://www.dnsever.com)
-
And:
1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
@@ -358,6 +328,39 @@ If your DNS provider is not on the supported list above, you can write your own
For more details: [How to use DNS API](dnsapi)
+# 9. Use DNS manual mode:
+
+If your dns provider doesn't support any api access, you will have to add the txt record by your hand.
+
+```bash
+acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
+```
+
+You should get an output like below:
+
+```sh
+Add the following txt record:
+Domain:_acme-challenge.example.com
+Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c
+
+Add the following txt record:
+Domain:_acme-challenge.www.example.com
+Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+Please add those txt records to the domains. Waiting for the dns to take effect.
+```
+
+Then just rerun with `renew` argument:
+
+```bash
+acme.sh --renew -d example.com
+```
+
+Ok, it's done.
+
+**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.**
+
+**Please use dns api mode instead.**
# 10. Issue ECC certificates
@@ -390,36 +393,60 @@ Valid values are:
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
-# 11. How to renew the issued certs
+
+# 11. Issue Wildcard certificates
+
+It's simple, just give a wildcard domain as the `-d` parameter.
+
+```sh
+acme.sh --issue -d example.com -d *.example.com --dns dns_cf
+```
+
+
+
+# 12. How to renew the certs
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
-However, you can also force to renew any cert:
+However, you can also force to renew a cert:
-```
+```sh
acme.sh --renew -d example.com --force
```
or, for ECC cert:
-```
+```sh
acme.sh --renew -d example.com --force --ecc
```
-# 12. How to upgrade `acme.sh`
+# 13. How to stop cert renewal
+
+To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
+
+```sh
+acme.sh --remove -d example.com [--ecc]
+```
+
+The cert/key file is not removed from the disk.
+
+You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself.
+
+
+# 14. How to upgrade `acme.sh`
acme.sh is in constant development, so it's strongly recommended to use the latest code.
You can update acme.sh to the latest code:
-```
+```sh
acme.sh --upgrade
```
You can also enable auto upgrade:
-```
+```sh
acme.sh --upgrade --auto-upgrade
```
@@ -427,31 +454,30 @@ Then **acme.sh** will be kept up to date automatically.
Disable auto upgrade:
-```
+```sh
acme.sh --upgrade --auto-upgrade 0
```
-# 13. Issue a cert from an existing CSR
+# 15. Issue a cert from an existing CSR
https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
-# 14. Under the Hood
+# 16. Under the Hood
Speak ACME language using shell, directly to "Let's Encrypt".
TODO:
-# 15. Acknowledgments
+# 17. Acknowledgments
1. Acme-tiny: https://github.com/diafygi/acme-tiny
2. ACME protocol: https://github.com/ietf-wg-acme/acme
-3. Certbot: https://github.com/certbot/certbot
-# 16. License & Others
+# 18. License & Others
License is GPLv3
@@ -460,7 +486,7 @@ Please Star and Fork me.
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
-# 17. Donate
+# 19. Donate
Your donation makes **acme.sh** better:
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
diff --git a/acme.sh b/acme.sh
index 472975a..a21b24e 100755
--- a/acme.sh
+++ b/acme.sh
@@ -13,8 +13,14 @@ _SCRIPT_="$0"
_SUB_FOLDERS="dnsapi deploy"
-_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org"
-DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory"
+LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory"
+LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory"
+
+LETSENCRYPT_CA_V2="https://acme-v02.api.letsencrypt.org/directory"
+LETSENCRYPT_STAGING_CA_V2="https://acme-staging-v02.api.letsencrypt.org/directory"
+
+DEFAULT_CA=$LETSENCRYPT_CA_V1
+DEFAULT_STAGING_CA=$LETSENCRYPT_STAGING_CA_V1
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
DEFAULT_ACCOUNT_EMAIL=""
@@ -24,13 +30,13 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048
DEFAULT_OPENSSL_BIN="openssl"
-STAGE_CA="https://acme-staging.api.letsencrypt.org/directory"
+_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org"
_OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org"
VTYPE_HTTP="http-01"
VTYPE_DNS="dns-01"
VTYPE_TLS="tls-sni-01"
-#VTYPE_TLS2="tls-sni-02"
+VTYPE_TLS2="tls-sni-02"
LOCAL_ANY_ADDRESS="0.0.0.0"
@@ -991,7 +997,7 @@ _createkey() {
_is_idn() {
_is_idn_d="$1"
_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"
[ "$_idn_temp" ]
}
@@ -1044,13 +1050,14 @@ _createcsr() {
if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
#single domain
_info "Single domain" "$domain"
+ printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf"
else
domainlist="$(_idn "$domainlist")"
_debug2 domainlist "$domainlist"
if _contains "$domainlist" ","; then
- alt="DNS:$(echo "$domainlist" | sed "s/,/,DNS:/g")"
+ alt="DNS:$domain,DNS:$(echo "$domainlist" | sed "s/,,/,/g" | sed "s/,/,DNS:/g")"
else
- alt="DNS:$domainlist"
+ alt="DNS:$domain,DNS:$domainlist"
fi
#multi
_info "Multi domain" "$alt"
@@ -1421,7 +1428,7 @@ _calcjwk() {
JWK_HEADER='{"alg": "RS256", "jwk": '$jwk'}'
JWK_HEADERPLACE_PART1='{"nonce": "'
- JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
+ JWK_HEADERPLACE_PART2='", "alg": "RS256"'
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
_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")"
@@ -1490,7 +1497,7 @@ _calcjwk() {
JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
JWK_HEADERPLACE_PART1='{"nonce": "'
- JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
+ JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"'
else
_err "Only RSA or EC key is supported."
return 1
@@ -1580,7 +1587,7 @@ _inithttp() {
# body url [needbase64] [POST|PUT]
_post() {
body="$1"
- url="$2"
+ _post_url="$2"
needbase64="$3"
httpmethod="$4"
@@ -1588,7 +1595,7 @@ _post() {
httpmethod="POST"
fi
_debug $httpmethod
- _debug "url" "$url"
+ _debug "_post_url" "$_post_url"
_debug2 "body" "$body"
_inithttp
@@ -1600,9 +1607,9 @@ _post() {
fi
_debug "_CURL" "$_CURL"
if [ "$needbase64" ]; then
- response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url" | _base64)"
+ response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)"
else
- response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$url")"
+ response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")"
fi
_ret="$?"
if [ "$_ret" != "0" ]; then
@@ -1620,15 +1627,15 @@ _post() {
_debug "_WGET" "$_WGET"
if [ "$needbase64" ]; then
if [ "$httpmethod" = "POST" ]; then
- response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
+ response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
else
- response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
+ response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)"
fi
else
if [ "$httpmethod" = "POST" ]; then
- response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")"
+ response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
else
- response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")"
+ response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")"
fi
fi
_ret="$?"
@@ -1656,7 +1663,7 @@ _get() {
onlyheader="$2"
t="$3"
_debug url "$url"
- _debug "timeout" "$t"
+ _debug "timeout=$t"
_inithttp
@@ -1776,7 +1783,15 @@ _send_signed_request() {
nonce="$_CACHED_NONCE"
_debug2 nonce "$nonce"
- protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2"
+ if [ "$ACME_VERSION" = "2" ]; then
+ if [ "$url" = "$ACME_NEW_ACCOUNT" ] || [ "$url" = "$ACME_REVOKE_CERT" ]; then
+ protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
+ else
+ protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"kid\": \"${ACCOUNT_URL}\""'}'
+ fi
+ else
+ protected="$JWK_HEADERPLACE_PART1$nonce\", \"url\": \"${url}$JWK_HEADERPLACE_PART2, \"jwk\": $jwk"'}'
+ fi
_debug3 protected "$protected"
protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
@@ -1791,7 +1806,11 @@ _send_signed_request() {
sig="$(printf "%s" "$_sig_t" | _url_replace)"
_debug3 sig "$sig"
- body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
+ if [ "$ACME_VERSION" = "2" ]; then
+ body="{\"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
+ else
+ body="{\"header\": $JWK_HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
+ fi
_debug3 body "$body"
response="$(_post "$body" "$url" "$needbase64")"
@@ -2170,9 +2189,15 @@ _initAPI() {
_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)
+ fi
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)
+ fi
export ACME_NEW_AUTHZ
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3)
@@ -2180,6 +2205,9 @@ _initAPI() {
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)
+ fi
fi
export ACME_NEW_ORDER
export ACME_NEW_ORDER_RES
@@ -2189,17 +2217,32 @@ _initAPI() {
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)
+ if [ "$ACME_NEW_ACCOUNT" ]; then
+ export ACME_VERSION=2
+ fi
+ fi
fi
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)
+ fi
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)
+ fi
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)
+ fi
export ACME_AGREEMENT
_debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
@@ -2208,12 +2251,16 @@ _initAPI() {
_debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
_debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
_debug "ACME_AGREEMENT" "$ACME_AGREEMENT"
+ _debug "ACME_NEW_NONCE" "$ACME_NEW_NONCE"
+ _debug "ACME_VERSION" "$ACME_VERSION"
fi
}
#[domain] [keylength or isEcc flag]
_initpath() {
+ domain="$1"
+ _ilength="$2"
__initHome
@@ -2232,11 +2279,16 @@ _initpath() {
CA_HOME="$DEFAULT_CA_HOME"
fi
+ if [ "$ACME_VERSION" = "2" ]; then
+ DEFAULT_CA="$LETSENCRYPT_CA_V2"
+ DEFAULT_STAGING_CA="$LETSENCRYPT_STAGING_CA_V2"
+ fi
+
if [ -z "$ACME_DIRECTORY" ]; then
if [ -z "$STAGE" ]; then
ACME_DIRECTORY="$DEFAULT_CA"
else
- ACME_DIRECTORY="$STAGE_CA"
+ ACME_DIRECTORY="$DEFAULT_STAGING_CA"
_info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY"
fi
fi
@@ -2296,13 +2348,10 @@ _initpath() {
ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
fi
- if [ -z "$1" ]; then
+ if [ -z "$domain" ]; then
return 0
fi
- domain="$1"
- _ilength="$2"
-
if [ -z "$DOMAIN_PATH" ]; then
domainhome="$CERT_HOME/$domain"
domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
@@ -2548,10 +2597,7 @@ _setNginx() {
_d="$1"
_croot="$2"
_thumbpt="$3"
- if ! _exists "nginx"; then
- _err "nginx command is not found."
- return 1
- fi
+
FOUND_REAL_NGINX_CONF=""
FOUND_REAL_NGINX_CONF_LN=""
BACKUP_NGINX_CONF=""
@@ -2561,6 +2607,10 @@ _setNginx() {
if [ -z "$_start_f" ]; then
_debug "find start conf from nginx command"
if [ -z "$NGINX_CONF" ]; then
+ if ! _exists "nginx"; then
+ _err "nginx command is not found."
+ return 1
+ fi
NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
_debug NGINX_CONF "$NGINX_CONF"
NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
@@ -2605,6 +2655,10 @@ _setNginx() {
return 1
fi
+ if ! _exists "nginx"; then
+ _err "nginx command is not found."
+ return 1
+ fi
_info "Check the nginx conf before setting up."
if ! _exec "nginx -t" >/dev/null; then
_exec_err
@@ -2697,7 +2751,7 @@ _isRealNginxConf() {
for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do
_debug _fln "$_fln"
if [ "$_fln" ]; then
- _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *{" | _tail_n 1)
+ _start=$(tr "\t" ' ' <"$2" | _head_n "$_fln" | grep -n "^ *server *" | grep -v server_name | _tail_n 1)
_debug "_start" "$_start"
_start_n=$(echo "$_start" | cut -d : -f 1)
_start_nn=$(_math $_start_n + 1)
@@ -2706,8 +2760,8 @@ _isRealNginxConf() {
_left="$(sed -n "${_start_nn},99999p" "$2")"
_debug2 _left "$_left"
- if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then
- _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1)
+ if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" >/dev/null; then
+ _end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | _head_n 1)
_debug "_end" "$_end"
_end_n=$(echo "$_end" | cut -d : -f 1)
_debug "_end_n" "$_end_n"
@@ -2718,8 +2772,20 @@ _isRealNginxConf() {
_debug "_seg_n" "$_seg_n"
- if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ] \
- || [ "$(echo "$_seg_n" | _egrep_o "listen .* ssl[ |;]")" ]; then
+ _skip_ssl=1
+ for _listen_i in $(echo "$_seg_n" | tr "\t" ' ' | grep "^ *listen" | tr -d " "); do
+ if [ "$_listen_i" ]; then
+ if [ "$(echo "$_listen_i" | _egrep_o "listen.*ssl[ |;]")" ]; then
+ _debug2 "$_listen_i is ssl"
+ else
+ _debug2 "$_listen_i is plain text"
+ _skip_ssl=""
+ break
+ fi
+ fi
+ done
+
+ if [ "$_skip_ssl" = "1" ]; then
_debug "ssl on, skip"
else
FOUND_REAL_NGINX_CONF_LN=$_fln
@@ -2818,7 +2884,11 @@ _clearupdns() {
return 1
fi
- txtdomain="_acme-challenge.$d"
+ _dns_root_d="$d"
+ if _startswith "$_dns_root_d" "*."; then
+ _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
+ fi
+ txtdomain="_acme-challenge.$_dns_root_d"
if ! $rmcommand "$txtdomain" "$txt"; then
_err "Error removing txt for domain:$txtdomain"
@@ -2951,6 +3021,7 @@ _on_issue_err() {
_chk_post_hook="$1"
_chk_vlist="$2"
_debug _on_issue_err
+
if [ "$LOG_FILE" ]; then
_err "Please check log file for more details: $LOG_FILE"
else
@@ -3052,6 +3123,8 @@ _regAccount() {
_initpath
_reg_length="$1"
_debug3 _regAccount "$_regAccount"
+ _initAPI
+
mkdir -p "$CA_DIR"
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
@@ -3073,11 +3146,18 @@ _regAccount() {
if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
return 1
fi
- _initAPI
- _reg_res="$ACME_NEW_ACCOUNT_RES"
- regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
- if [ "$ACCOUNT_EMAIL" ]; then
- regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
+
+ if [ "$ACME_VERSION" = "2" ]; then
+ regjson='{"termsOfServiceAgreed": true}'
+ if [ "$ACCOUNT_EMAIL" ]; then
+ regjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}'
+ fi
+ else
+ _reg_res="$ACME_NEW_ACCOUNT_RES"
+ regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
+ if [ "$ACCOUNT_EMAIL" ]; then
+ regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
+ fi
fi
_info "Registering account"
@@ -3090,7 +3170,7 @@ _regAccount() {
if [ "$code" = "" ] || [ "$code" = '201' ]; then
echo "$response" >"$ACCOUNT_JSON_PATH"
_info "Registered"
- elif [ "$code" = '409' ]; then
+ elif [ "$code" = '409' ] || [ "$code" = '200' ]; then
_info "Already registered"
else
_err "Register account Error: $response"
@@ -3100,8 +3180,8 @@ _regAccount() {
_accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
_debug "_accUri" "$_accUri"
_savecaconf "ACCOUNT_URL" "$_accUri"
+ export ACCOUNT_URL="$ACCOUNT_URL"
- echo "$response" >"$ACCOUNT_JSON_PATH"
CA_KEY_HASH="$(__calcAccountKeyHash)"
_debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
_savecaconf CA_KEY_HASH "$CA_KEY_HASH"
@@ -3113,7 +3193,6 @@ _regAccount() {
ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
_info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
-
}
#Implement deactivate account
@@ -3149,7 +3228,12 @@ deactivateaccount() {
fi
_initAPI
- if _send_signed_request "$_accUri" "{\"resource\": \"reg\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
+ if [ "$ACME_VERSION" = "2" ]; then
+ _djson="{\"status\":\"deactivated\"}"
+ else
+ _djson="{\"resource\": \"reg\", \"status\":\"deactivated\"}"
+ fi
+ if _send_signed_request "$_accUri" "$_djson" && _contains "$response" '"deactivated"'; then
_info "Deactivate account success for $_accUri."
_accid=$(echo "$response" | _egrep_o "\"id\" *: *[^,]*," | cut -d : -f 2 | tr -d ' ,')
elif [ "$code" = "403" ]; then
@@ -3237,7 +3321,7 @@ __get_domain_new_authz() {
_err "new-authz retry reach the max $_Max_new_authz_retry_times times."
fi
- if [ ! -z "$code" ] && [ ! "$code" = '201' ]; then
+ if [ "$code" ] && [ "$code" != '201' ]; then
_err "new-authz error: $response"
return 1
fi
@@ -3251,7 +3335,11 @@ __trigger_validation() {
_debug2 _t_url "$_t_url"
_t_key_authz="$2"
_debug2 _t_key_authz "$_t_key_authz"
- _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}"
+ if [ "$ACME_VERSION" = "2" ]; then
+ _send_signed_request "$_t_url" "{\"keyAuthorization\": \"$_t_key_authz\"}"
+ else
+ _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}"
+ fi
}
#webroot, domain domainlist keylength
@@ -3267,6 +3355,7 @@ issue() {
_web_roots="$1"
_main_domain="$2"
_alt_domains="$3"
+
if _contains "$_main_domain" ","; then
_main_domain=$(echo "$2,$3" | cut -d , -f 1)
_alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
@@ -3393,32 +3482,109 @@ issue() {
sep='#'
dvsep=','
if [ -z "$vlist" ]; then
+ if [ "$ACME_VERSION" = "2" ]; then
+ #make new order request
+ _identifiers="{\"type\":\"dns\",\"value\":\"$_main_domain\"}"
+ for d in $(echo "$_alt_domains" | tr ',' ' '); do
+ if [ "$d" ]; then
+ _identifiers="$_identifiers,{\"type\":\"dns\",\"value\":\"$d\"}"
+ fi
+ done
+ _debug2 _identifiers "$_identifiers"
+ if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
+ _err "Create new order error."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+
+ Le_OrderFinalize="$(echo "$response" | tr -d '\r\n' | _egrep_o '"finalize" *: *"[^"]*"' | cut -d '"' -f 4)"
+ _debug Le_OrderFinalize "$Le_OrderFinalize"
+ if [ -z "$Le_OrderFinalize" ]; then
+ _err "Create new order error. Le_OrderFinalize not found. $response"
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+
+ #for dns manual mode
+ _savedomainconf "Le_OrderFinalize" "$Le_OrderFinalize"
+
+ _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
+ _debug2 _authorizations_seg "$_authorizations_seg"
+ if [ -z "$_authorizations_seg" ]; then
+ _err "_authorizations_seg not found."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+
+ #domain and authz map
+ _authorizations_map=""
+ for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do
+ _debug2 "_authz_url" "$_authz_url"
+ if ! response="$(_get "$_authz_url")"; then
+ _err "get to authz error."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+
+ response="$(echo "$response" | _normalizeJson)"
+ _debug2 response "$response"
+ _d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2 | tr -d ' "')"
+ if _contains "$response" "\"wildcard\" *: *true"; then
+ _d="*.$_d"
+ fi
+ _debug2 _d "$_d"
+ _authorizations_map="$_d,$response
+$_authorizations_map"
+ done
+ _debug2 _authorizations_map "$_authorizations_map"
+ fi
+
alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ')
- _index=1
+ _index=0
_currentRoot=""
for d in $alldomains; do
_info "Getting webroot for domain" "$d"
+ _index=$(_math $_index + 1)
_w="$(echo $_web_roots | cut -d , -f $_index)"
_debug _w "$_w"
if [ "$_w" ]; then
_currentRoot="$_w"
fi
_debug "_currentRoot" "$_currentRoot"
- _index=$(_math $_index + 1)
vtype="$VTYPE_HTTP"
+ #todo, v2 wildcard force to use dns
if _startswith "$_currentRoot" "dns"; then
vtype="$VTYPE_DNS"
fi
if [ "$_currentRoot" = "$W_TLS" ]; then
- vtype="$VTYPE_TLS"
+ if [ "$ACME_VERSION" = "2" ]; then
+ vtype="$VTYPE_TLS2"
+ else
+ vtype="$VTYPE_TLS"
+ fi
fi
- if ! __get_domain_new_authz "$d"; then
- _clearup
- _on_issue_err "$_post_hook"
- return 1
+ if [ "$ACME_VERSION" = "2" ]; then
+ response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")"
+ _debug2 "response" "$response"
+ if [ -z "$response" ]; then
+ _err "get to authz error."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+ else
+ if ! __get_domain_new_authz "$d"; then
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
fi
if [ -z "$thumbprint" ]; then
@@ -3428,7 +3594,7 @@ issue() {
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$entry" ]; then
- _err "Error, can not get domain token $d"
+ _err "Error, can not get domain token entry $d"
_clearup
_on_issue_err "$_post_hook"
return 1
@@ -3436,14 +3602,30 @@ issue() {
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token"
- uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
+ if [ -z "$token" ]; then
+ _err "Error, can not get domain token $entry"
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+ if [ "$ACME_VERSION" = "2" ]; then
+ uri="$(printf "%s\n" "$entry" | _egrep_o '"url":"[^"]*' | cut -d '"' -f 4 | _head_n 1)"
+ else
+ uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
+ fi
_debug uri "$uri"
+ if [ -z "$uri" ]; then
+ _err "Error, can not get domain uri. $entry"
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization"
if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
- _debug "$d is already verified, skip."
+ _debug "$d is already verified."
keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization"
fi
@@ -3463,7 +3645,7 @@ issue() {
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
-
+ _debug d "$d"
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_debug "$d is already verified, skip $vtype."
continue
@@ -3471,12 +3653,16 @@ issue() {
if [ "$vtype" = "$VTYPE_DNS" ]; then
dnsadded='0'
- txtdomain="_acme-challenge.$d"
+ _dns_root_d="$d"
+ if _startswith "$_dns_root_d" "*."; then
+ _dns_root_d="$(echo "$_dns_root_d" | sed 's/*.//')"
+ fi
+ txtdomain="_acme-challenge.$_dns_root_d"
_debug txtdomain "$txtdomain"
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
_debug txt "$txt"
- d_api="$(_findHook "$d" dnsapi "$_currentRoot")"
+ d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")"
_debug d_api "$d_api"
@@ -3685,12 +3871,16 @@ issue() {
return 1
fi
- if [ ! -z "$code" ] && [ ! "$code" = '202' ]; then
- _err "$d:Challenge error: $response"
- _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
- _clearup
- _on_issue_err "$_post_hook" "$vlist"
- return 1
+ if [ "$code" ] && [ "$code" != '202' ]; then
+ if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then
+ _debug "trigger validation code: $code"
+ else
+ _err "$d:Challenge error: $response"
+ _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
+ _clearup
+ _on_issue_err "$_post_hook" "$vlist"
+ return 1
+ fi
fi
waittimes=0
@@ -3773,18 +3963,42 @@ issue() {
_info "Verify finished, start to sign."
der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _url_replace)"
- if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
- _err "Sign failed."
- _on_issue_err "$_post_hook"
- return 1
- fi
+ if [ "$ACME_VERSION" = "2" ]; then
+ if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then
+ _err "Sign failed."
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+ if [ "$code" != "200" ]; then
+ _err "Sign failed, code is not 200."
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+ Le_LinkCert="$(echo "$response" | tr -d '\r\n' | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
- _rcert="$response"
- Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
- _debug "Le_LinkCert" "$Le_LinkCert"
- _savedomainconf "Le_LinkCert" "$Le_LinkCert"
+ if ! _get "$Le_LinkCert" >"$CERT_PATH"; then
+ _err "Sign failed, code is not 200."
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
- if [ "$Le_LinkCert" ]; then
+ if [ "$(grep -- "$BEGIN_CERT" "$CERT_PATH" | wc -l)" -gt "1" ]; then
+ _debug "Found cert chain"
+ cat "$CERT_PATH" >"$CERT_FULLCHAIN_PATH"
+ _end_n="$(grep -n -- "$END_CERT" "$CERT_FULLCHAIN_PATH" | _head_n 1 | cut -d : -f 1)"
+ _debug _end_n "$_end_n"
+ sed -n "1,${_end_n}p" "$CERT_FULLCHAIN_PATH" >"$CERT_PATH"
+ _end_n="$(_math $_end_n + 1)"
+ sed -n "${_end_n},9999p" "$CERT_FULLCHAIN_PATH" >"$CA_CERT_PATH"
+ fi
+ else
+ if ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then
+ _err "Sign failed."
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+ _rcert="$response"
+ Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
echo "$BEGIN_CERT" >"$CERT_PATH"
#if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
@@ -3798,6 +4012,12 @@ issue() {
fi
echo "$END_CERT" >>"$CERT_PATH"
+ fi
+
+ _debug "Le_LinkCert" "$Le_LinkCert"
+ _savedomainconf "Le_LinkCert" "$Le_LinkCert"
+
+ if [ "$Le_LinkCert" ]; then
_info "$(__green "Cert success.")"
cat "$CERT_PATH"
@@ -3824,40 +4044,49 @@ issue() {
_cleardomainconf "Le_Vlist"
- Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
- 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"
-
- if [ "$Le_LinkIssuer" ]; then
- _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 _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"
-
- _info "The intermediate CA cert is in $(__green " $CA_CERT_PATH ")"
- cat "$CA_CERT_PATH" >>"$CERT_FULLCHAIN_PATH"
- _info "And the full chain certs is there: $(__green " $CERT_FULLCHAIN_PATH ")"
-
- rm -f "$CA_CERT_PATH.der"
- break
- 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
+ if [ "$ACME_VERSION" = "2" ]; then
+ _debug "v2 chain."
else
- _debug "No Le_LinkIssuer header found."
+ 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 _get "$Le_LinkIssuer" >"$CA_CERT_PATH"; then
+ 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"
+ 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
+ [ -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)
_savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
@@ -3957,7 +4186,7 @@ renew() {
_savedomainconf Le_API "$Le_API"
fi
if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then
- export Le_API="$STAGE_CA"
+ export Le_API="$DEFAULT_STAGING_CA"
_savedomainconf Le_API "$Le_API"
fi
export ACME_DIRECTORY="$Le_API"
@@ -4045,8 +4274,6 @@ signcsr() {
return 1
fi
- _initpath
-
_csrsubj=$(_readSubjectFromCSR "$_csrfile")
if [ "$?" != "0" ]; then
_err "Can not read subject from csr: $_csrfile"
@@ -4083,6 +4310,9 @@ signcsr() {
return 1
fi
+ if [ -z "$ACME_VERSION" ] && _contains "$_csrsubj,$_csrdomainlist" "*."; then
+ export ACME_VERSION=2
+ fi
_initpath "$_csrsubj" "$_csrkeylength"
mkdir -p "$DOMAIN_PATH"
@@ -4448,7 +4678,11 @@ revoke() {
_initAPI
- data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
+ if [ "$ACME_VERSION" = "2" ]; then
+ data="{\"certificate\": \"$cert\"}"
+ else
+ data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
+ fi
uri="${ACME_REVOKE_CERT}"
if [ -f "$CERT_KEY_PATH" ]; then
@@ -4519,27 +4753,56 @@ _deactivate() {
_d_type="$2"
_initpath
- if ! __get_domain_new_authz "$_d_domain"; then
- _err "Can not get domain new authz token."
- return 1
+ if [ "$ACME_VERSION" = "2" ]; then
+ _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}"
+ if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
+ _err "Can not get domain new order."
+ return 1
+ fi
+ _authorizations_seg="$(echo "$response" | tr -d '\r\n' | _egrep_o '"authorizations" *: *\[[^\]*\]' | cut -d '[' -f 2 | tr -d ']' | tr -d '"')"
+ _debug2 _authorizations_seg "$_authorizations_seg"
+ if [ -z "$_authorizations_seg" ]; then
+ _err "_authorizations_seg not found."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+
+ authzUri="$_authorizations_seg"
+ _debug2 "authzUri" "$authzUri"
+ if ! response="$(_get "$authzUri")"; then
+ _err "get to authz error."
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+
+ response="$(echo "$response" | _normalizeJson)"
+ _debug2 response "$response"
+ _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")"
+ _debug "authzUri" "$authzUri"
+ if [ "$code" ] && [ ! "$code" = '201' ]; then
+ _err "new-authz error: $response"
+ return 1
+ fi
+ _URL_NAME="uri"
fi
- authzUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
- _debug "authzUri" "$authzUri"
-
- if [ "$code" ] && [ ! "$code" = '201' ]; then
- _err "new-authz error: $response"
- return 1
- fi
-
- entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')"
+ entries="$(echo "$response" | _egrep_o "{ *\"type\":\"[^\"]*\", *\"status\": *\"valid\", *\"$_URL_NAME\"[^}]*")"
if [ -z "$entries" ]; then
_info "No valid entries found."
if [ -z "$thumbprint" ]; then
thumbprint="$(__calc_account_thumbprint)"
fi
_debug "Trigger validation."
- vtype="$VTYPE_HTTP"
+ vtype="$VTYPE_DNS"
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
if [ -z "$entry" ]; then
@@ -4549,7 +4812,7 @@ _deactivate() {
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token"
- uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
+ uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
_debug uri "$uri"
keyauthorization="$token.$thumbprint"
@@ -4575,7 +4838,7 @@ _deactivate() {
_debug _vtype "$_vtype"
_info "Found $_vtype"
- uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
+ uri="$(printf "%s\n" "$entry" | _egrep_o "\"$_URL_NAME\":\"[^\"]*" | cut -d : -f 2,3 | tr -d '"')"
_debug uri "$uri"
if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
@@ -4585,7 +4848,13 @@ _deactivate() {
_info "Deactivate: $_vtype"
- if _send_signed_request "$authzUri" "{\"resource\": \"authz\", \"status\":\"deactivated\"}" && _contains "$response" '"deactivated"'; then
+ if [ "$ACME_VERSION" = "2" ]; then
+ _djson="{\"status\":\"deactivated\"}"
+ else
+ _djson="{\"resource\": \"authz\", \"status\":\"deactivated\"}"
+ fi
+
+ if _send_signed_request "$authzUri" "$_djson" && _contains "$response" '"deactivated"'; then
_info "Deactivate: $_vtype success."
else
_err "Can not deactivate $_vtype."
@@ -4894,8 +5163,14 @@ install() {
if [ -z "$NO_DETECT_SH" ]; then
#Modify shebang
if _exists bash; then
+ _bash_path="$(bash -c "command -v bash 2>/dev/null")"
+ if [ -z "$_bash_path" ]; then
+ _bash_path="$(bash -c 'echo $SHELL')"
+ fi
+ fi
+ if [ "$_bash_path" ]; then
_info "Good, bash is found, so change the shebang to use bash as preferred."
- _shebang='#!'"$(env bash -c "command -v bash")"
+ _shebang='#!'"$_bash_path"
_setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
for subf in $_SUB_FOLDERS; do
if [ -d "$LE_WORKING_DIR/$subf" ]; then
@@ -5000,7 +5275,7 @@ Commands:
--renew, -r Renew a cert.
--renew-all Renew all the certs.
--revoke Revoke a cert.
- --remove Remove the cert from $PROJECT
+ --remove Remove the cert from list of certs known to $PROJECT_NAME.
--list List all the certs.
--showcsr Show the content of a csr.
--install-cronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
@@ -5307,6 +5582,10 @@ _process() {
return 1
fi
+ if _startswith "$_dvalue" "*."; then
+ _debug "Wildcard domain"
+ export ACME_VERSION=2
+ fi
if [ -z "$_domain" ]; then
_domain="$_dvalue"
else
@@ -5510,7 +5789,7 @@ _process() {
HTTPS_INSECURE="1"
;;
--ca-bundle)
- _ca_bundle="$(_readlink -f "$2")"
+ _ca_bundle="$(_readlink "$2")"
CA_BUNDLE="$_ca_bundle"
shift
;;
diff --git a/dnsapi/README.md b/dnsapi/README.md
index f029b72..40efc9e 100644
--- a/dnsapi/README.md
+++ b/dnsapi/README.md
@@ -679,6 +679,60 @@ acme.sh --issue --dns dns_dnsever -d example.com -d www.example.com
```
The DNSEVER_ID and DNSEVER_PW will be saved in ~/.acme.sh/account.conf and will be reused when needed.
+## 36. Use autoDNS (InternetX)
+
+[InternetX](https://www.internetx.com/) offers an [xml api](https://help.internetx.com/display/API/AutoDNS+XML-API) with your standard login credentials, set them like so:
+
+```
+export AUTODNS_USER="yourusername"
+export AUTODNS_PASSWORD="password"
+export AUTODNS_CONTEXT="context"
+```
+
+Then you can issue your certificates with:
+
+```
+acme.sh --issue --dns dns_autodns -d example.com -d www.example.com
+```
+
+The `AUTODNS_USER`, `AUTODNS_PASSWORD` and `AUTODNS_CONTEXT` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
+## 37. Use Azure DNS
+
+You have to create a service principal first. See:[How to use Azure DNS](../../../wiki/How-to-use-Azure-DNS)
+
+```
+export AZUREDNS_SUBSCRIPTIONID="12345678-9abc-def0-1234-567890abcdef"
+export AZUREDNS_TENANTID="11111111-2222-3333-4444-555555555555"
+export AZUREDNS_APPID="3b5033b5-7a66-43a5-b3b9-a36b9e7c25ed"
+export AZUREDNS_CLIENTSECRET="1b0224ef-34d4-5af9-110f-77f527d561bd"
+```
+
+Then you can issue your certificates with:
+
+```
+acme.sh --issue --dns dns_azure -d example.com -d www.example.com
+```
+
+`AZUREDNS_SUBSCRIPTIONID`, `AZUREDNS_TENANTID`,`AZUREDNS_APPID` and `AZUREDNS_CLIENTSECRET` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
+## 38. Use selectel.com(selectel.ru) domain API to automatically issue cert
+
+First you need to login to your account to get your API key from: https://my.selectel.ru/profile/apikeys.
+
+```sh
+export SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+
+```
+
+Ok, let's issue a cert now:
+```
+acme.sh --issue --dns dns_selectel -d example.com -d www.example.com
+```
+
+The `SL_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
+
# Use custom API
If your API is not supported yet, you can write your own DNS API.
diff --git a/dnsapi/dns_autodns.sh b/dnsapi/dns_autodns.sh
new file mode 100644
index 0000000..9253448
--- /dev/null
+++ b/dnsapi/dns_autodns.sh
@@ -0,0 +1,264 @@
+#!/usr/bin/env sh
+# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
+
+# This is the InternetX autoDNS xml api wrapper for acme.sh
+# Author: auerswald@gmail.com
+# Created: 2018-01-14
+#
+# export AUTODNS_USER="username"
+# export AUTODNS_PASSWORD="password"
+# export AUTODNS_CONTEXT="context"
+#
+# Usage:
+# acme.sh --issue --dns dns_autodns -d example.com
+
+AUTODNS_API="https://gateway.autodns.com"
+
+# Arguments:
+# txtdomain
+# txt
+dns_autodns_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
+ AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
+ AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
+
+ if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
+ _err "You don't specify autodns user, password and context."
+ return 1
+ fi
+
+ _saveaccountconf_mutable AUTODNS_USER "$AUTODNS_USER"
+ _saveaccountconf_mutable AUTODNS_PASSWORD "$AUTODNS_PASSWORD"
+ _saveaccountconf_mutable AUTODNS_CONTEXT "$AUTODNS_CONTEXT"
+
+ _debug "First detect the root zone"
+
+ if ! _get_autodns_zone "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _zone "$_zone"
+ _debug _system_ns "$_system_ns"
+
+ _info "Adding TXT record"
+
+ autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
+
+ if [ "$?" -eq "0" ]; then
+ _info "Added, OK"
+ return 0
+ fi
+
+ return 1
+}
+
+# Arguments:
+# txtdomain
+# txt
+dns_autodns_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
+ AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
+ AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
+
+ if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
+ _err "You don't specify autodns user, password and context."
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+
+ if ! _get_autodns_zone "$fulldomain"; then
+ _err "zone not found"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _zone "$_zone"
+ _debug _system_ns "$_system_ns"
+
+ _info "Delete TXT record"
+
+ autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
+
+ if [ "$?" -eq "0" ]; then
+ _info "Deleted, OK"
+ return 0
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+
+# Arguments:
+# fulldomain
+# Returns:
+# _sub_domain=_acme-challenge.www
+# _zone=domain.com
+# _system_ns
+_get_autodns_zone() {
+ domain="$1"
+
+ i=2
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+
+ if [ -z "$h" ]; then
+ # not valid
+ return 1
+ fi
+
+ autodns_response="$(_autodns_zone_inquire "$h")"
+
+ if [ "$?" -ne "0" ]; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ if _contains "$autodns_response" "1" >/dev/null; then
+ _zone="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
+ _system_ns="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_build_request_auth_xml() {
+ printf "
+ %s
+ %s
+ %s
+ " "$AUTODNS_USER" "$AUTODNS_PASSWORD" "$AUTODNS_CONTEXT"
+}
+
+# Arguments:
+# zone
+_build_zone_inquire_xml() {
+ printf "
+
+ %s
+
+ 0205
+
+ 1
+ 1
+
+
+ name
+ eq
+ %s
+
+
+ " "$(_build_request_auth_xml)" "$1"
+}
+
+# Arguments:
+# zone
+# subdomain
+# txtvalue
+# system_ns
+_build_zone_update_xml() {
+ printf "
+
+ %s
+
+ 0202001
+
+
+ %s
+ 600
+ TXT
+ %s
+
+
+
+ %s
+ %s
+
+
+ " "$(_build_request_auth_xml)" "$2" "$3" "$1" "$4"
+}
+
+# Arguments:
+# zone
+_autodns_zone_inquire() {
+ request_data="$(_build_zone_inquire_xml "$1")"
+ autodns_response="$(_autodns_api_call "$request_data")"
+ ret="$?"
+
+ printf "%s" "$autodns_response"
+ return "$ret"
+}
+
+# Arguments:
+# zone
+# subdomain
+# txtvalue
+# system_ns
+_autodns_zone_update() {
+ request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
+ autodns_response="$(_autodns_api_call "$request_data")"
+ ret="$?"
+
+ printf "%s" "$autodns_response"
+ return "$ret"
+}
+
+# Arguments:
+# zone
+# subdomain
+# txtvalue
+# system_ns
+_autodns_zone_cleanup() {
+ request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
+ # replace 'rr_add>' with 'rr_rem>' in request_data
+ request_data="$(printf -- "%s" "$request_data" | sed 's/rr_add>/rr_rem>/g')"
+ autodns_response="$(_autodns_api_call "$request_data")"
+ ret="$?"
+
+ printf "%s" "$autodns_response"
+ return "$ret"
+}
+
+# Arguments:
+# request_data
+_autodns_api_call() {
+ request_data="$1"
+
+ _debug request_data "$request_data"
+
+ autodns_response="$(_post "$request_data" "$AUTODNS_API")"
+ ret="$?"
+
+ _debug autodns_response "$autodns_response"
+
+ if [ "$ret" -ne "0" ]; then
+ _err "error"
+ return 1
+ fi
+
+ if _contains "$autodns_response" "success" >/dev/null; then
+ _info "success"
+ printf "%s" "$autodns_response"
+ return 0
+ fi
+
+ return 1
+}
diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh
index 5a71651..ed31746 100755
--- a/dnsapi/dns_aws.sh
+++ b/dnsapi/dns_aws.sh
@@ -19,18 +19,19 @@ dns_aws_add() {
fulldomain=$1
txtvalue=$2
+ AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
+ AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
_err "You don't specify aws route53 api key id and and api key secret yet."
- _err "Please create you key and try again. see $(__green $AWS_WIKI)"
+ _err "Please create your key and try again. see $(__green $AWS_WIKI)"
return 1
fi
- if [ -z "$AWS_SESSION_TOKEN" ]; then
- _saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
- _saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
- fi
+ #save for future use
+ _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
+ _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -56,6 +57,8 @@ dns_aws_rm() {
fulldomain=$1
txtvalue=$2
+ AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
+ AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh
new file mode 100644
index 0000000..0834ede
--- /dev/null
+++ b/dnsapi/dns_azure.sh
@@ -0,0 +1,249 @@
+#!/usr/bin/env sh
+
+######## Public functions #####################
+
+# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to add txt record
+#
+# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate
+#
+dns_azure_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
+ AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
+ AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
+ AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
+
+ if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure Subscription ID "
+ return 1
+ fi
+
+ if [ -z "$AZUREDNS_TENANTID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure Tenant ID "
+ return 1
+ fi
+
+ if [ -z "$AZUREDNS_APPID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure App ID"
+ return 1
+ fi
+
+ if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure Client Secret"
+ return 1
+ fi
+ #save account details to account conf file.
+ _saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID"
+ _saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
+ _saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
+ _saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
+
+ accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
+
+ if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
+ _debug "$acmeRecordURI"
+ body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}"
+ _azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken"
+ if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
+ _info "validation record added"
+ else
+ _err "error adding validation record ($_code)"
+ return 1
+ fi
+}
+
+# Usage: fulldomain txtvalue
+# Used to remove the txt record after validation
+#
+# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete
+#
+dns_azure_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
+ AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
+ AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
+ AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
+
+ if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure Subscription ID "
+ return 1
+ fi
+
+ if [ -z "$AZUREDNS_TENANTID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure Tenant ID "
+ return 1
+ fi
+
+ if [ -z "$AZUREDNS_APPID" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure App ID"
+ return 1
+ fi
+
+ if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
+ AZUREDNS_SUBSCRIPTIONID=""
+ AZUREDNS_TENANTID=""
+ AZUREDNS_APPID=""
+ AZUREDNS_CLIENTSECRET=""
+ _err "You didn't specify the Azure Client Secret"
+ return 1
+ fi
+
+ accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
+
+ if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
+ _debug "$acmeRecordURI"
+ body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}"
+ _azure_rest DELETE "$acmeRecordURI" "" "$accesstoken"
+ if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then
+ _info "validation record removed"
+ else
+ _err "error removing validation record ($_code)"
+ return 1
+ fi
+}
+
+################### Private functions below ##################################
+
+_azure_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ accesstoken="$4"
+
+ export _H1="authorization: Bearer $accesstoken"
+ export _H2="accept: application/json"
+ export _H3="Content-Type: application/json"
+
+ _debug "$ep"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$ep" "" "$m")"
+ else
+ response="$(_get "$ep")"
+ fi
+ _debug2 response "$response"
+
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
+ _debug2 "http response code $_code"
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ return 0
+}
+
+## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token
+_azure_getaccess_token() {
+ TENANTID=$1
+ clientID=$2
+ clientSecret=$3
+
+ export _H1="accept: application/json"
+ export _H2="Content-Type: application/x-www-form-urlencoded"
+
+ body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
+ _debug data "$body"
+ response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST")"
+ accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
+ _debug2 "response $response"
+
+ if [ -z "$accesstoken" ]; then
+ _err "no acccess token received"
+ return 1
+ fi
+ if [ "$?" != "0" ]; then
+ _err "error $response"
+ return 1
+ fi
+ printf "%s" "$accesstoken"
+ return 0
+}
+
+_get_root() {
+ domain=$1
+ subscriptionId=$2
+ accesstoken=$3
+ i=2
+ p=1
+
+ ## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list
+ ## returns up to 100 zones in one response therefore handling more results is not not implemented
+ ## (ZoneListResult with continuation token for the next page of results)
+ ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways
+ ##
+ _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" "$accesstoken"
+
+ # Find matching domain name is Json response
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug2 "Checking domain: $h"
+ if [ -z "$h" ]; then
+ #not valid
+ _err "Invalid domain"
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+ _domain_id=$(echo "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \")
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh
index 57a2e88..68264a4 100755
--- a/dnsapi/dns_cf.sh
+++ b/dnsapi/dns_cf.sh
@@ -51,33 +51,36 @@ dns_cf_add() {
return 1
fi
- count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
- _debug count "$count"
- if [ "$count" = "0" ]; then
- _info "Adding record"
- if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
- if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
- _info "Added, OK"
- return 0
- else
- _err "Add txt record error."
- return 1
- fi
- fi
- _err "Add txt record error."
- else
- _info "Updating record"
- record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
- _debug "record_id" "$record_id"
-
- _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
- if [ "$?" = "0" ]; then
- _info "Updated, OK"
+ # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
+ # we can not use updating anymore.
+ # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
+ # _debug count "$count"
+ # if [ "$count" = "0" ]; then
+ _info "Adding record"
+ if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
+ if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
+ _info "Added, OK"
return 0
+ else
+ _err "Add txt record error."
+ return 1
fi
- _err "Update error"
- return 1
fi
+ _err "Add txt record error."
+ return 1
+ # else
+ # _info "Updating record"
+ # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
+ # _debug "record_id" "$record_id"
+ #
+ # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
+ # if [ "$?" = "0" ]; then
+ # _info "Updated, OK"
+ # return 0
+ # fi
+ # _err "Update error"
+ # return 1
+ # fi
}
diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh
index 74440bd..5dfba7d 100644
--- a/dnsapi/dns_inwx.sh
+++ b/dnsapi/dns_inwx.sh
@@ -35,53 +35,9 @@ dns_inwx_add() {
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- _debug "Getting txt records"
- xml_content=$(printf '
-
- nameserver.info
-
-
-
-
-
- domain
-
- %s
-
-
-
- type
-
- TXT
-
-
-
- name
-
- %s
-
-
-
-
-
-
- ' "$_domain" "$_sub_domain")
- response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
-
- if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
- _err "Error could net get txt records"
- return 1
- fi
-
- if ! printf "%s" "$response" | grep "count" >/dev/null; then
- _info "Adding record"
- _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue"
- else
- _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+')
- _info "Updating record"
- _inwx_update_record "$_record_id" "$txtvalue"
- fi
+ _info "Adding record"
+ _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue"
}
@@ -147,7 +103,7 @@ dns_inwx_rm() {
' "$_domain" "$_sub_domain")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
- if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
+ if ! _contains "$response" "Command completed successfully"; then
_err "Error could not get txt records"
return 1
fi
diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh
index eaa90bd..6009473 100755
--- a/dnsapi/dns_ovh.sh
+++ b/dnsapi/dns_ovh.sh
@@ -78,13 +78,7 @@ _ovh_get_api() {
esac
}
-######## Public functions #####################
-
-#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_ovh_add() {
- fulldomain=$1
- txtvalue=$2
-
+_initAuth() {
if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then
OVH_AK=""
OVH_AS=""
@@ -119,14 +113,26 @@ dns_ovh_add() {
_info "Checking authentication"
- response="$(_ovh_rest GET "domain")"
- if _contains "$response" "INVALID_CREDENTIAL"; then
+ if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL"; then
_err "The consumer key is invalid: $OVH_CK"
_err "Please retry to create a new one."
_clearaccountconf OVH_CK
return 1
fi
_info "Consumer key is ok."
+ return 0
+}
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_ovh_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _initAuth; then
+ return 1
+ fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -137,49 +143,58 @@ dns_ovh_add() {
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- _debug "Getting txt records"
- _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"
-
- if _contains "$response" '\[\]' || _contains "$response" "This service does not exist"; then
- _info "Adding record"
- if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then
- if _contains "$response" "$txtvalue"; then
- _ovh_rest POST "domain/zone/$_domain/refresh"
- _debug "Refresh:$response"
- _info "Added, sleeping 10 seconds"
- sleep 10
- return 0
- fi
+ _info "Adding record"
+ if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then
+ if _contains "$response" "$txtvalue"; then
+ _ovh_rest POST "domain/zone/$_domain/refresh"
+ _debug "Refresh:$response"
+ _info "Added, sleep 10 seconds."
+ _sleep 10
+ return 0
fi
- _err "Add txt record error."
- else
- _info "Updating record"
- record_id=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 1)
- if [ -z "$record_id" ]; then
- _err "Can not get record id."
- return 1
- fi
- _debug "record_id" "$record_id"
-
- if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then
- if _contains "$response" "null"; then
- _ovh_rest POST "domain/zone/$_domain/refresh"
- _debug "Refresh:$response"
- _info "Updated, sleeping 10 seconds"
- sleep 10
- return 0
- fi
- fi
- _err "Update error"
- return 1
fi
+ _err "Add txt record error."
+ return 1
}
#fulldomain
dns_ovh_rm() {
fulldomain=$1
+ txtvalue=$2
+ if ! _initAuth; then
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+ _debug "Getting txt records"
+ if ! _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"; then
+ return 1
+ fi
+
+ for rid in $(echo "$response" | tr '][,' ' '); do
+ _debug rid "$rid"
+ if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then
+ return 1
+ fi
+ if _contains "$response" "\"target\":\"$txtvalue\""; then
+ _debug "Found txt id:$rid"
+ if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then
+ return 1
+ fi
+ return 0
+ fi
+ done
+
+ return 1
}
#################### Private functions below ##################################
@@ -191,7 +206,7 @@ _ovh_authentication() {
_H3=""
_H4=""
- _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}'
+ _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"},{"method": "DELETE","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}'
response="$(_post "$_ovhdata" "$OVH_API/auth/credential")"
_debug3 response "$response"
@@ -279,15 +294,15 @@ _ovh_rest() {
export _H3="X-Ovh-Timestamp: $_ovh_t"
export _H4="X-Ovh-Consumer: $OVH_CK"
export _H5="Content-Type: application/json;charset=utf-8"
- if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then
+ if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then
_debug data "$data"
response="$(_post "$data" "$_ovh_url" "" "$m")"
else
response="$(_get "$_ovh_url")"
fi
- if [ "$?" != "0" ]; then
- _err "error $ep"
+ if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then
+ _err "error $response"
return 1
fi
_debug2 response "$response"
diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh
new file mode 100644
index 0000000..94252d8
--- /dev/null
+++ b/dnsapi/dns_selectel.sh
@@ -0,0 +1,161 @@
+#!/usr/bin/env sh
+
+#
+#SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+
+SL_Api="https://api.selectel.ru/domains/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_selectel_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
+
+ if [ -z "$SL_Key" ]; then
+ SL_Key=""
+ _err "You don't specify selectel.ru api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key to the account conf file.
+ _saveaccountconf_mutable SL_Key "$SL_Key"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _info "Adding record"
+ if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then
+ if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then
+ _info "Added, OK"
+ return 0
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_selectel_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
+
+ if [ -z "$SL_Key" ]; then
+ SL_Key=""
+ _err "You don't specify slectel api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _sl_rest GET "/${_domain_id}/records/"
+
+ if ! _contains "$response" "$txtvalue"; then
+ _err "Txt record not found"
+ return 1
+ fi
+
+ _record_seg="$(echo "$response" | _egrep_o "\"content\" *: *\"$txtvalue\"[^}]*}")"
+ _debug2 "_record_seg" "$_record_seg"
+ if [ -z "$_record_seg" ]; then
+ _err "can not find _record_seg"
+ return 1
+ fi
+
+ _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)"
+ _debug2 "_record_id" "$_record_id"
+ if [ -z "$_record_id" ]; then
+ _err "can not find _record_id"
+ return 1
+ fi
+
+ if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+
+ if ! _sl_rest GET "/"; then
+ return 1
+ fi
+
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"name\": \"$h\","; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ _debug "Getting domain id for $h"
+ if ! _sl_rest GET "/$h"; then
+ return 1
+ fi
+ _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_sl_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="X-Token: $SL_Key"
+ export _H2="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$SL_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$SL_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh
index eb60d5a..7ebb15d 100755
--- a/dnsapi/dns_yandex.sh
+++ b/dnsapi/dns_yandex.sh
@@ -18,7 +18,6 @@ dns_yandex_add() {
curDomain=$(_PDD_get_domain "$fulldomain")
_debug "Found suitable domain in pdd: $curDomain"
- curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")"
curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}"
curUri="https://pddimp.yandex.ru/api2/admin/dns/add"
curResult="$(_post "${curData}" "${curUri}")"
@@ -36,7 +35,6 @@ dns_yandex_rm() {
curDomain=$(_PDD_get_domain "$fulldomain")
_debug "Found suitable domain in pdd: $curDomain"
- curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")"
curUri="https://pddimp.yandex.ru/api2/admin/dns/del"
curData="domain=${curDomain}&record_id=${record_id}"
@@ -61,7 +59,7 @@ _PDD_get_domain() {
__last=1
fi
- __all_domains="$__all_domains $(echo "$res1" | sed -e "s@,@\n@g" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')"
+ __all_domains="$__all_domains $(echo "$res1" | tr "," "\n" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')"
__page=$(_math $__page + 1)
done
@@ -72,6 +70,8 @@ _PDD_get_domain() {
_debug "finding zone for domain $__t"
for d in $__all_domains; do
if [ "$d" = "$__t" ]; then
+ p=$(_math $k - 1)
+ curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")"
echo "$__t"
return
fi
@@ -98,7 +98,6 @@ pdd_get_record_id() {
curDomain=$(_PDD_get_domain "$fulldomain")
_debug "Found suitable domain in pdd: $curDomain"
- curSubdomain="$(echo "${fulldomain}" | sed -e "s@.${curDomain}\$@@")"
curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}"
curResult="$(_get "${curUri}" | _normalizeJson)"