Commit 9046509b authored by raidenii's avatar raidenii
Browse files

Merge remote-tracking branch 'upstream/master'

parents 4964e075 432037d2
language: shell language: shell
sudo: required sudo: required
dist: trusty
os: os:
- linux - linux
- osx - osx
services:
- docker
env: env:
global: global:
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 - SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64
...@@ -18,20 +22,10 @@ addons: ...@@ -18,20 +22,10 @@ addons:
install: install:
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
brew update && brew install openssl; brew update && brew install socat;
brew info openssl; export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ;
ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/;
ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/;
ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl;
_old_path="$PATH";
echo "PATH=$PATH";
export PATH="";
export ACME_OPENSSL_BIN="/usr/local/openssl";
openssl version 2>&1 || true;
$ACME_OPENSSL_BIN version 2>&1 || true;
export PATH="$_old_path";
fi fi
script: script:
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
- command -V openssl && openssl version - command -V openssl && openssl version
...@@ -40,10 +34,10 @@ script: ...@@ -40,10 +34,10 @@ script:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi
- cd .. - cd ..
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
......
FROM alpine FROM alpine:3.6
RUN apk update -f \ RUN apk update -f \
&& apk --no-cache add -f \ && apk --no-cache add -f \
openssl \ openssl \
curl \ curl \
netcat-openbsd \ socat \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
ENV LE_CONFIG_HOME /acme.sh ENV LE_CONFIG_HOME /acme.sh
...@@ -16,7 +16,7 @@ ADD ./ /install_acme.sh/ ...@@ -16,7 +16,7 @@ ADD ./ /install_acme.sh/
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/ RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab -
RUN for verb in help \ RUN for verb in help \
version \ version \
...@@ -44,6 +44,7 @@ RUN for verb in help \ ...@@ -44,6 +44,7 @@ RUN for verb in help \
create-domain-key \ create-domain-key \
createCSR \ createCSR \
deactivate \ deactivate \
deactivate-account \
; do \ ; do \
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \ printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
; done ; done
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
[![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](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. - 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 ACME v2 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.
- Simplest shell script for Let's Encrypt free certificate client. - Simplest shell script for Let's Encrypt free certificate client.
...@@ -23,7 +25,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa) ...@@ -23,7 +25,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
# Who are using **acme.sh** # Who:
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) - [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
- [ruby-china.org](https://ruby-china.org/topics/31983) - [ruby-china.org](https://ruby-china.org/topics/31983)
- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer)) - [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer))
...@@ -72,7 +74,7 @@ https://github.com/Neilpang/acmetest ...@@ -72,7 +74,7 @@ https://github.com/Neilpang/acmetest
- Webroot mode - Webroot mode
- Standalone mode - Standalone mode
- Apache mode - Apache mode
- Nginx mode ( Beta ) - Nginx mode
- DNS mode - DNS mode
- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
...@@ -127,7 +129,7 @@ Ok, you are ready to issue certs now. ...@@ -127,7 +129,7 @@ Ok, you are ready to issue certs now.
Show help message: Show help message:
``` ```sh
root@v1:~# acme.sh -h root@v1:~# acme.sh -h
``` ```
...@@ -164,16 +166,16 @@ You must have at least one domain there. ...@@ -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`. 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 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. 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: **Apache** example:
...@@ -195,13 +197,15 @@ acme.sh --install-cert -d example.com \ ...@@ -195,13 +197,15 @@ acme.sh --install-cert -d example.com \
Only the domain is required, all the other parameters are optional. 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`. 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`.
**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewwed cert in 60 days.**
# 4. Use Standalone server to issue cert # 4. Use Standalone server to issue cert
**(requires you to be root/sudoer or have permission to listen on port 80 (TCP))** **(requires you to be root/sudoer or have permission to listen on port 80 (TCP))**
...@@ -236,14 +240,18 @@ More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert ...@@ -236,14 +240,18 @@ More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
Particularly, if you are running an Apache server, you should use Apache mode instead. This mode doesn't write any files to your web root folder. Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
Just set string "apache" as the second argument and it will force use of apache plugin automatically. 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 acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
``` ```
**This apache mode is only to issue the cert, it will not change your apache config files.
You will need to configure your website config files to use the cert by yourself.
We don't want to mess your apache server, don't worry.**
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 7. Use Nginx mode # 7. Use Nginx mode
...@@ -260,47 +268,17 @@ It will configure nginx server automatically to verify the domain and then resto ...@@ -260,47 +268,17 @@ It will configure nginx server automatically to verify the domain and then resto
So, the config is not changed. So, the config is not changed.
``` ```sh
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com 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 **This nginx mode is only to issue the cert, it will not change your nginx config files.
You will need to configure your website config files to use the cert by yourself.
# 8. Use DNS mode: We don't want to mess your nginx server, don't worry.**
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:
``` More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
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. If your DNS provider supports API access, we can use that API to automatically issue the certs.
...@@ -336,21 +314,65 @@ You don't have to do anything manually! ...@@ -336,21 +314,65 @@ You don't have to do anything manually!
1. NS1.com API 1. NS1.com API
1. DuckDNS.org API 1. DuckDNS.org API
1. Name.com API 1. Name.com API
1. Dyn Managed DNS API
1. Yandex PDD API (https://pdd.yandex.ru)
1. Hurricane Electric DNS service (https://dns.he.net)
1. UnoEuro API (https://www.unoeuro.com/)
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. zonomi.com DNS API
1. DreamHost.com API
And: And:
1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api **lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
(DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.) (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)**
**More APIs coming soon...** **More APIs coming soon...**
If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project. If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
For more details: [How to use DNS API](dnsapi) 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 can 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 # 10. Issue ECC certificates
...@@ -358,7 +380,7 @@ For more details: [How to use DNS API](dnsapi) ...@@ -358,7 +380,7 @@ For more details: [How to use DNS API](dnsapi)
And we support them too! And we support them too!
Just set the `length` parameter with a prefix `ec-`. Just set the `keylength` parameter with a prefix `ec-`.
For example: For example:
...@@ -374,7 +396,7 @@ acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 ...@@ -374,7 +396,7 @@ acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
``` ```
Please look at the last parameter above. Please look at the `keylength` parameter above.
Valid values are: Valid values are:
...@@ -383,36 +405,60 @@ Valid values are: ...@@ -383,36 +405,60 @@ Valid values are:
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.)**
# 11. How to renew the issued certs
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. # 11. Issue Wildcard certificates
However, you can also force to renew any cert: 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 a cert:
```sh
acme.sh --renew -d example.com --force acme.sh --renew -d example.com --force
``` ```
or, for ECC cert: or, for ECC cert:
``` ```sh
acme.sh --renew -d example.com --force --ecc 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. 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: You can update acme.sh to the latest code:
``` ```sh
acme.sh --upgrade acme.sh --upgrade
``` ```
You can also enable auto upgrade: You can also enable auto upgrade:
``` ```sh
acme.sh --upgrade --auto-upgrade acme.sh --upgrade --auto-upgrade
``` ```
...@@ -420,31 +466,30 @@ Then **acme.sh** will be kept up to date automatically. ...@@ -420,31 +466,30 @@ Then **acme.sh** will be kept up to date automatically.
Disable auto upgrade: Disable auto upgrade:
``` ```sh
acme.sh --upgrade --auto-upgrade 0 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 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". Speak ACME language using shell, directly to "Let's Encrypt".
TODO: TODO:
# 15. Acknowledgments # 17. Acknowledgments
1. Acme-tiny: https://github.com/diafygi/acme-tiny 1. Acme-tiny: https://github.com/diafygi/acme-tiny
2. ACME protocol: https://github.com/ietf-wg-acme/acme 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 License is GPLv3
...@@ -453,7 +498,7 @@ Please Star and Fork me. ...@@ -453,7 +498,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. [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: Your donation makes **acme.sh** better:
1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) 1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
......
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.7.3 VER=2.7.7
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
...@@ -13,9 +13,14 @@ _SCRIPT_="$0" ...@@ -13,9 +13,14 @@ _SCRIPT_="$0"
_SUB_FOLDERS="dnsapi deploy" _SUB_FOLDERS="dnsapi deploy"
_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org" LETSENCRYPT_CA_V1="https://acme-v01.api.letsencrypt.org/directory"
DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory" LETSENCRYPT_STAGING_CA_V1="https://acme-staging.api.letsencrypt.org/directory"
DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
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_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
DEFAULT_ACCOUNT_EMAIL="" DEFAULT_ACCOUNT_EMAIL=""
...@@ -25,13 +30,13 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048 ...@@ -25,13 +30,13 @@ DEFAULT_DOMAIN_KEY_LENGTH=2048
DEFAULT_OPENSSL_BIN="openssl" 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" _OLD_STAGE_CA_HOST="https://acme-staging.api.letsencrypt.org"
VTYPE_HTTP="http-01" VTYPE_HTTP="http-01"
VTYPE_DNS="dns-01" VTYPE_DNS="dns-01"
VTYPE_TLS="tls-sni-01" VTYPE_TLS="tls-sni-01"
#VTYPE_TLS2="tls-sni-02" VTYPE_TLS2="tls-sni-02"
LOCAL_ANY_ADDRESS="0.0.0.0" LOCAL_ANY_ADDRESS="0.0.0.0"
...@@ -100,6 +105,10 @@ _PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations" ...@@ -100,6 +105,10 @@ _PREPARE_LINK="https://github.com/Neilpang/acme.sh/wiki/Install-preparations"
_STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode" _STATELESS_WIKI="https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode"
_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"
__INTERACTIVE="" __INTERACTIVE=""
if [ -t 1 ]; then if [ -t 1 ]; then
__INTERACTIVE="1" __INTERACTIVE="1"
...@@ -160,11 +169,11 @@ _dlg_versions() { ...@@ -160,11 +169,11 @@ _dlg_versions() {
echo "nginx doesn't exists." echo "nginx doesn't exists."
fi fi
echo "nc:" echo "socat:"
if _exists "nc"; then if _exists "socat"; then
nc -h 2>&1 socat -h 2>&1
else else
_debug "nc doesn't exists." _debug "socat doesn't exists."
fi fi
} }
...@@ -459,8 +468,7 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then ...@@ -459,8 +468,7 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
fi fi
_h2b() { _h2b() {
if _exists xxd; then if _exists xxd && xxd -r -p 2>/dev/null; then
xxd -r -p
return return
fi fi
...@@ -897,17 +905,11 @@ _sign() { ...@@ -897,17 +905,11 @@ _sign() {
fi fi
_sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile " _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
if [ "$alg" = "sha256" ]; then
_sign_openssl="$_sign_openssl -$alg"
else
_err "$alg is not supported yet"
return 1
fi
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
$_sign_openssl | _base64 $_sign_openssl -$alg | _base64
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
if ! _signedECText="$($_sign_openssl | ${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"
_err "Key content:$(wc -l <"$keyfile") lines" _err "Key content:$(wc -l <"$keyfile") lines"
...@@ -926,7 +928,7 @@ _sign() { ...@@ -926,7 +928,7 @@ _sign() {
} }
#keylength #keylength or isEcc flag (empty str => not ecc)
_isEccKey() { _isEccKey() {
_length="$1" _length="$1"
...@@ -995,7 +997,7 @@ _createkey() { ...@@ -995,7 +997,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" ]
} }
...@@ -1048,13 +1050,14 @@ _createcsr() { ...@@ -1048,13 +1050,14 @@ _createcsr() {
if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then if [ -z "$domainlist" ] || [ "$domainlist" = "$NO_VALUE" ]; then
#single domain #single domain
_info "Single domain" "$domain" _info "Single domain" "$domain"
printf -- "\nsubjectAltName=DNS:$domain" >>"$csrconf"
else else
domainlist="$(_idn "$domainlist")" domainlist="$(_idn "$domainlist")"
_debug2 domainlist "$domainlist" _debug2 domainlist "$domainlist"
if _contains "$domainlist" ","; then 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 else
alt="DNS:$domainlist" alt="DNS:$domain,DNS:$domainlist"
fi fi
#multi #multi
_info "Multi domain" "$alt" _info "Multi domain" "$alt"
...@@ -1182,6 +1185,28 @@ _ss() { ...@@ -1182,6 +1185,28 @@ _ss() {
return 1 return 1
} }
#outfile key cert cacert [password [name [caname]]]
_toPkcs() {
_cpfx="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
pfxPassword="$5"
pfxName="$6"
pfxCaname="$7"
if [ "$pfxCaname" ]; then
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName" -caname "$pfxCaname"
elif [ "$pfxName" ]; then
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword" -name "$pfxName"
elif [ "$pfxPassword" ]; then
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca" -password "pass:$pfxPassword"
else
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca"
fi
}
#domain [password] [isEcc] #domain [password] [isEcc]
toPkcs() { toPkcs() {
domain="$1" domain="$1"
...@@ -1195,11 +1220,7 @@ toPkcs() { ...@@ -1195,11 +1220,7 @@ toPkcs() {
_initpath "$domain" "$_isEcc" _initpath "$domain" "$_isEcc"
if [ "$pfxPassword" ]; then _toPkcs "$CERT_PFX_PATH" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$pfxPassword"
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
else
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
fi
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
_info "Success, Pfx is exported to: $CERT_PFX_PATH" _info "Success, Pfx is exported to: $CERT_PFX_PATH"
...@@ -1260,6 +1281,7 @@ _create_account_key() { ...@@ -1260,6 +1281,7 @@ _create_account_key() {
else else
#generate account key #generate account key
_createkey "$length" "$ACCOUNT_KEY_PATH" _createkey "$length" "$ACCOUNT_KEY_PATH"
chmod 600 "$ACCOUNT_KEY_PATH"
fi fi
} }
...@@ -1349,6 +1371,10 @@ _time2str() { ...@@ -1349,6 +1371,10 @@ _time2str() {
echo "$_t_s_a" echo "$_t_s_a"
fi fi
#Busybox
if echo "$1" | awk '{ print strftime("%c", $0); }' 2>/dev/null; then
return
fi
} }
_normalizeJson() { _normalizeJson() {
...@@ -1403,12 +1429,16 @@ _calcjwk() { ...@@ -1403,12 +1429,16 @@ _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": '$jwk'}' JWK_HEADERPLACE_PART2='", "alg": "RS256"'
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; 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"
__ECC_KEY_LEN=$(echo "$crv" | cut -d "-" -f 2)
if [ "$__ECC_KEY_LEN" = "521" ]; then
__ECC_KEY_LEN=512
fi
_debug3 __ECC_KEY_LEN "$__ECC_KEY_LEN"
if [ -z "$crv" ]; then if [ -z "$crv" ]; then
_debug "Let's try ASN1 OID" _debug "Let's try ASN1 OID"
crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")" crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
...@@ -1416,12 +1446,15 @@ _calcjwk() { ...@@ -1416,12 +1446,15 @@ _calcjwk() {
case "${crv_oid}" in case "${crv_oid}" in
"prime256v1") "prime256v1")
crv="P-256" crv="P-256"
__ECC_KEY_LEN=256
;; ;;
"secp384r1") "secp384r1")
crv="P-384" crv="P-384"
__ECC_KEY_LEN=384
;; ;;
"secp521r1") "secp521r1")
crv="P-521" crv="P-521"
__ECC_KEY_LEN=512
;; ;;
*) *)
_err "ECC oid : $crv_oid" _err "ECC oid : $crv_oid"
...@@ -1463,9 +1496,9 @@ _calcjwk() { ...@@ -1463,9 +1496,9 @@ _calcjwk() {
jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}' jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
_debug3 jwk "$jwk" _debug3 jwk "$jwk"
JWK_HEADER='{"alg": "ES256", "jwk": '$jwk'}' JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
JWK_HEADERPLACE_PART1='{"nonce": "' JWK_HEADERPLACE_PART1='{"nonce": "'
JWK_HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}' JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'"'
else else
_err "Only RSA or EC key is supported." _err "Only RSA or EC key is supported."
return 1 return 1
...@@ -1529,6 +1562,9 @@ _inithttp() { ...@@ -1529,6 +1562,9 @@ _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
_ACME_CURL="$_ACME_CURL -g "
fi
fi fi
if [ -z "$_ACME_WGET" ] && _exists "wget"; then if [ -z "$_ACME_WGET" ] && _exists "wget"; then
...@@ -1555,7 +1591,7 @@ _inithttp() { ...@@ -1555,7 +1591,7 @@ _inithttp() {
# body url [needbase64] [POST|PUT] # body url [needbase64] [POST|PUT]
_post() { _post() {
body="$1" body="$1"
url="$2" _post_url="$2"
needbase64="$3" needbase64="$3"
httpmethod="$4" httpmethod="$4"
...@@ -1563,7 +1599,7 @@ _post() { ...@@ -1563,7 +1599,7 @@ _post() {
httpmethod="POST" httpmethod="POST"
fi fi
_debug $httpmethod _debug $httpmethod
_debug "url" "$url" _debug "_post_url" "$_post_url"
_debug2 "body" "$body" _debug2 "body" "$body"
_inithttp _inithttp
...@@ -1575,9 +1611,9 @@ _post() { ...@@ -1575,9 +1611,9 @@ _post() {
fi fi
_debug "_CURL" "$_CURL" _debug "_CURL" "$_CURL"
if [ "$needbase64" ]; then 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 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 fi
_ret="$?" _ret="$?"
if [ "$_ret" != "0" ]; then if [ "$_ret" != "0" ]; then
...@@ -1595,15 +1631,15 @@ _post() { ...@@ -1595,15 +1631,15 @@ _post() {
_debug "_WGET" "$_WGET" _debug "_WGET" "$_WGET"
if [ "$needbase64" ]; then if [ "$needbase64" ]; then
if [ "$httpmethod" = "POST" ]; 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 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 fi
else else
if [ "$httpmethod" = "POST" ]; 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")" 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 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
fi fi
_ret="$?" _ret="$?"
...@@ -1631,7 +1667,7 @@ _get() { ...@@ -1631,7 +1667,7 @@ _get() {
onlyheader="$2" onlyheader="$2"
t="$3" t="$3"
_debug url "$url" _debug url "$url"
_debug "timeout" "$t" _debug "timeout=$t"
_inithttp _inithttp
...@@ -1751,7 +1787,15 @@ _send_signed_request() { ...@@ -1751,7 +1787,15 @@ _send_signed_request() {
nonce="$_CACHED_NONCE" nonce="$_CACHED_NONCE"
_debug2 nonce "$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" _debug3 protected "$protected"
protected64="$(printf "%s" "$protected" | _base64 | _url_replace)" protected64="$(printf "%s" "$protected" | _base64 | _url_replace)"
...@@ -1766,7 +1810,11 @@ _send_signed_request() { ...@@ -1766,7 +1810,11 @@ _send_signed_request() {
sig="$(printf "%s" "$_sig_t" | _url_replace)" sig="$(printf "%s" "$_sig_t" | _url_replace)"
_debug3 sig "$sig" _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" _debug3 body "$body"
response="$(_post "$body" "$url" "$needbase64")" response="$(_post "$body" "$url" "$needbase64")"
...@@ -1788,7 +1836,13 @@ _send_signed_request() { ...@@ -1788,7 +1836,13 @@ _send_signed_request() {
_CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" _CACHED_NONCE="$(echo "$responseHeaders" | grep "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)"
if _contains "$response" "JWS has invalid anti-replay nonce"; then _body="$response"
if [ "$needbase64" ]; then
_body="$(echo "$_body" | _dbase64)"
_debug3 _body "$_body"
fi
if _contains "$_body" "JWS has invalid anti-replay nonce"; then
_info "It seems the CA server is busy now, let's wait and retry." _info "It seems the CA server is busy now, let's wait and retry."
_request_retry_times=$(_math "$_request_retry_times" + 1) _request_retry_times=$(_math "$_request_retry_times" + 1)
_sleep 5 _sleep 5
...@@ -1941,68 +1995,32 @@ _startserver() { ...@@ -1941,68 +1995,32 @@ _startserver() {
_debug "ncaddr" "$ncaddr" _debug "ncaddr" "$ncaddr"
_debug "startserver: $$" _debug "startserver: $$"
nchelp="$(nc -h 2>&1)"
_debug Le_HTTPPort "$Le_HTTPPort" _debug Le_HTTPPort "$Le_HTTPPort"
_debug Le_Listen_V4 "$Le_Listen_V4" _debug Le_Listen_V4 "$Le_Listen_V4"
_debug Le_Listen_V6 "$Le_Listen_V6" _debug Le_Listen_V6 "$Le_Listen_V6"
_NC="nc"
_NC="socat"
if [ "$Le_Listen_V4" ]; then if [ "$Le_Listen_V4" ]; then
_NC="$_NC -4" _NC="$_NC -4"
elif [ "$Le_Listen_V6" ]; then elif [ "$Le_Listen_V6" ]; then
_NC="$_NC -6" _NC="$_NC -6"
fi fi
if [ "$Le_Listen_V4$Le_Listen_V6$ncaddr" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
if ! _contains "$nchelp" "-4"; then _NC="$_NC -d -d -v"
_err "The nc doesn't support '-4', '-6' or local-address, please install 'netcat-openbsd' and try again."
_err "See $(__green $_PREPARE_LINK)"
return 1
fi
fi
if echo "$nchelp" | grep "\-q[ ,]" >/dev/null; then
_NC="$_NC -q 1 -l $ncaddr"
else
if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null; then
_NC="$_NC -c -l $ncaddr"
elif echo "$nchelp" | grep "\-N" | grep "Shutdown the network socket after EOF on stdin" >/dev/null; then
_NC="$_NC -N -l $ncaddr"
else
_NC="$_NC -l $ncaddr"
fi
fi fi
_debug "_NC" "$_NC" SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork
#for centos ncat
if _contains "$nchelp" "nmap.org"; then
_debug "Using ncat: nmap.org"
if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2"; then
_exec_err
return 1
fi
if [ "$DEBUG" ]; then
_exec_err
fi
return
fi
# while true ; do #Adding bind to local-address
if ! _exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC -p \"$Le_HTTPPort\" >&2"; then if [ "$ncaddr" ]; then
_exec "printf \"%s\r\n\r\n%s\" \"HTTP/1.1 200 OK\" \"$content\" | $_NC \"$Le_HTTPPort\" >&2" SOCAT_OPTIONS="$SOCAT_OPTIONS,bind=${ncaddr}"
fi fi
if [ "$?" != "0" ]; then _debug "_NC" "$_NC $SOCAT_OPTIONS"
_err "nc listen error." $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; echo HTTP/1.0 200 OK; echo ; echo $content; echo;" &
_exec_err serverproc="$!"
exit 1
fi
if [ "$DEBUG" ]; then
_exec_err
fi
# done
} }
_stopserver() { _stopserver() {
...@@ -2012,25 +2030,8 @@ _stopserver() { ...@@ -2012,25 +2030,8 @@ _stopserver() {
return return
fi fi
_debug2 "Le_HTTPPort" "$Le_HTTPPort" kill $pid
if [ "$Le_HTTPPort" ]; then
if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
_get "http://localhost:$Le_HTTPPort" "" 1
else
_get "http://localhost:$Le_HTTPPort" "" 1 >/dev/null 2>&1
fi
fi
_debug2 "Le_TLSPort" "$Le_TLSPort"
if [ "$Le_TLSPort" ]; then
if [ "$DEBUG" ] && [ "$DEBUG" -gt "3" ]; then
_get "https://localhost:$Le_TLSPort" "" 1
_get "https://localhost:$Le_TLSPort" "" 1
else
_get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
_get "https://localhost:$Le_TLSPort" "" 1 >/dev/null 2>&1
fi
fi
} }
# sleep sec # sleep sec
...@@ -2085,7 +2086,7 @@ _starttlsserver() { ...@@ -2085,7 +2086,7 @@ _starttlsserver() {
return 1 return 1
fi fi
__S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -cert $TLS_CERT -key $TLS_KEY " __S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY "
if [ "$opaddr" ]; then if [ "$opaddr" ]; then
__S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port" __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
else else
...@@ -2102,9 +2103,9 @@ _starttlsserver() { ...@@ -2102,9 +2103,9 @@ _starttlsserver() {
_debug "$__S_OPENSSL" _debug "$__S_OPENSSL"
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
(printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL -tlsextdebug) & $__S_OPENSSL -tlsextdebug &
else else
(printf "%s\r\n\r\n%s" "HTTP/1.1 200 OK" "$content" | $__S_OPENSSL >/dev/null 2>&1) & $__S_OPENSSL >/dev/null 2>&1 &
fi fi
serverproc="$!" serverproc="$!"
...@@ -2192,17 +2193,6 @@ _initAPI() { ...@@ -2192,17 +2193,6 @@ _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 [ "$_api_server" = "$DEFAULT_CA" ]; then
#just for performance, hardcode the default entry points
export ACME_KEY_CHANGE="https://acme-v01.api.letsencrypt.org/acme/key-change"
export ACME_NEW_AUTHZ="https://acme-v01.api.letsencrypt.org/acme/new-authz"
export ACME_NEW_ORDER="https://acme-v01.api.letsencrypt.org/acme/new-cert"
export ACME_NEW_ORDER_RES="new-cert"
export ACME_NEW_ACCOUNT="https://acme-v01.api.letsencrypt.org/acme/new-reg"
export ACME_NEW_ACCOUNT_RES="new-reg"
export ACME_REVOKE_CERT="https://acme-v01.api.letsencrypt.org/acme/revoke-cert"
fi
if [ -z "$ACME_NEW_ACCOUNT" ]; then if [ -z "$ACME_NEW_ACCOUNT" ]; then
response=$(_get "$_api_server") response=$(_get "$_api_server")
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
...@@ -2213,9 +2203,15 @@ _initAPI() { ...@@ -2213,9 +2203,15 @@ _initAPI() {
_debug2 "response" "$response" _debug2 "response" "$response"
ACME_KEY_CHANGE=$(echo "$response" | _egrep_o 'key-change" *: *"[^"]*"' | cut -d '"' -f 3) 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 export ACME_KEY_CHANGE
ACME_NEW_AUTHZ=$(echo "$response" | _egrep_o 'new-authz" *: *"[^"]*"' | cut -d '"' -f 3) 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 export ACME_NEW_AUTHZ
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-cert" *: *"[^"]*"' | cut -d '"' -f 3)
...@@ -2223,6 +2219,9 @@ _initAPI() { ...@@ -2223,6 +2219,9 @@ _initAPI() {
if [ -z "$ACME_NEW_ORDER" ]; then if [ -z "$ACME_NEW_ORDER" ]; then
ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ORDER=$(echo "$response" | _egrep_o 'new-order" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ORDER_RES="new-order" 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 fi
export ACME_NEW_ORDER export ACME_NEW_ORDER
export ACME_NEW_ORDER_RES export ACME_NEW_ORDER_RES
...@@ -2232,27 +2231,50 @@ _initAPI() { ...@@ -2232,27 +2231,50 @@ _initAPI() {
if [ -z "$ACME_NEW_ACCOUNT" ]; then if [ -z "$ACME_NEW_ACCOUNT" ]; then
ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3) ACME_NEW_ACCOUNT=$(echo "$response" | _egrep_o 'new-account" *: *"[^"]*"' | cut -d '"' -f 3)
ACME_NEW_ACCOUNT_RES="new-account" 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 fi
export ACME_NEW_ACCOUNT export ACME_NEW_ACCOUNT
export ACME_NEW_ACCOUNT_RES export ACME_NEW_ACCOUNT_RES
ACME_REVOKE_CERT=$(echo "$response" | _egrep_o 'revoke-cert" *: *"[^"]*"' | cut -d '"' -f 3) 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 export ACME_REVOKE_CERT
ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3) 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 export ACME_NEW_NONCE
fi 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"
_debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
_debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER"
_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"
_debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE" fi
_debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
_debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER"
_debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
_debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
} }
#[domain] [keylength] #[domain] [keylength or isEcc flag]
_initpath() { _initpath() {
domain="$1"
_ilength="$2"
__initHome __initHome
...@@ -2271,15 +2293,21 @@ _initpath() { ...@@ -2271,15 +2293,21 @@ _initpath() {
CA_HOME="$DEFAULT_CA_HOME" CA_HOME="$DEFAULT_CA_HOME"
fi 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 "$ACME_DIRECTORY" ]; then
if [ -z "$STAGE" ]; then if [ -z "$STAGE" ]; then
ACME_DIRECTORY="$DEFAULT_CA" ACME_DIRECTORY="$DEFAULT_CA"
else else
ACME_DIRECTORY="$STAGE_CA" ACME_DIRECTORY="$DEFAULT_STAGING_CA"
_info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY" _info "Using stage ACME_DIRECTORY: $ACME_DIRECTORY"
fi fi
fi fi
_debug2 ACME_DIRECTORY "$ACME_DIRECTORY"
_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"
...@@ -2334,13 +2362,10 @@ _initpath() { ...@@ -2334,13 +2362,10 @@ _initpath() {
ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN" ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
fi fi
if [ -z "$1" ]; then if [ -z "$domain" ]; then
return 0 return 0
fi fi
domain="$1"
_ilength="$2"
if [ -z "$DOMAIN_PATH" ]; then if [ -z "$DOMAIN_PATH" ]; then
domainhome="$CERT_HOME/$domain" domainhome="$CERT_HOME/$domain"
domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX" domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
...@@ -2586,10 +2611,7 @@ _setNginx() { ...@@ -2586,10 +2611,7 @@ _setNginx() {
_d="$1" _d="$1"
_croot="$2" _croot="$2"
_thumbpt="$3" _thumbpt="$3"
if ! _exists "nginx"; then
_err "nginx command is not found."
return 1
fi
FOUND_REAL_NGINX_CONF="" FOUND_REAL_NGINX_CONF=""
FOUND_REAL_NGINX_CONF_LN="" FOUND_REAL_NGINX_CONF_LN=""
BACKUP_NGINX_CONF="" BACKUP_NGINX_CONF=""
...@@ -2599,6 +2621,10 @@ _setNginx() { ...@@ -2599,6 +2621,10 @@ _setNginx() {
if [ -z "$_start_f" ]; then if [ -z "$_start_f" ]; then
_debug "find start conf from nginx command" _debug "find start conf from nginx command"
if [ -z "$NGINX_CONF" ]; then 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 " ")" NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
_debug NGINX_CONF "$NGINX_CONF" _debug NGINX_CONF "$NGINX_CONF"
NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)" NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
...@@ -2643,6 +2669,10 @@ _setNginx() { ...@@ -2643,6 +2669,10 @@ _setNginx() {
return 1 return 1
fi fi
if ! _exists "nginx"; then
_err "nginx command is not found."
return 1
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 ! _exec "nginx -t" >/dev/null; then
_exec_err _exec_err
...@@ -2735,7 +2765,7 @@ _isRealNginxConf() { ...@@ -2735,7 +2765,7 @@ _isRealNginxConf() {
for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do for _fln in $(tr "\t" ' ' <"$2" | grep -n "^ *server_name.* $1" | cut -d : -f 1); do
_debug _fln "$_fln" _debug _fln "$_fln"
if [ "$_fln" ]; then 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" _debug "_start" "$_start"
_start_n=$(echo "$_start" | cut -d : -f 1) _start_n=$(echo "$_start" | cut -d : -f 1)
_start_nn=$(_math $_start_n + 1) _start_nn=$(_math $_start_n + 1)
...@@ -2744,9 +2774,9 @@ _isRealNginxConf() { ...@@ -2744,9 +2774,9 @@ _isRealNginxConf() {
_left="$(sed -n "${_start_nn},99999p" "$2")" _left="$(sed -n "${_start_nn},99999p" "$2")"
_debug2 _left "$_left" _debug2 _left "$_left"
if echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" >/dev/null; then _end="$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *" | grep -v server_name | _head_n 1)"
_end=$(echo "$_left" | tr "\t" ' ' | grep -n "^ *server *{" | _head_n 1) _debug "_end" "$_end"
_debug "_end" "$_end" if [ "$_end" ]; then
_end_n=$(echo "$_end" | cut -d : -f 1) _end_n=$(echo "$_end" | cut -d : -f 1)
_debug "_end_n" "$_end_n" _debug "_end_n" "$_end_n"
_seg_n=$(echo "$_left" | sed -n "1,${_end_n}p") _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
...@@ -2756,8 +2786,20 @@ _isRealNginxConf() { ...@@ -2756,8 +2786,20 @@ _isRealNginxConf() {
_debug "_seg_n" "$_seg_n" _debug "_seg_n" "$_seg_n"
if [ "$(echo "$_seg_n" | _egrep_o "^ *ssl *on *;")" ] \ _skip_ssl=1
|| [ "$(echo "$_seg_n" | _egrep_o "listen .* ssl[ |;]")" ]; then 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" _debug "ssl on, skip"
else else
FOUND_REAL_NGINX_CONF_LN=$_fln FOUND_REAL_NGINX_CONF_LN=$_fln
...@@ -2856,7 +2898,11 @@ _clearupdns() { ...@@ -2856,7 +2898,11 @@ _clearupdns() {
return 1 return 1
fi 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 if ! $rmcommand "$txtdomain" "$txt"; then
_err "Error removing txt for domain:$txtdomain" _err "Error removing txt for domain:$txtdomain"
...@@ -2917,8 +2963,8 @@ _on_before_issue() { ...@@ -2917,8 +2963,8 @@ _on_before_issue() {
fi fi
if _hasfield "$_chk_web_roots" "$NO_VALUE"; then if _hasfield "$_chk_web_roots" "$NO_VALUE"; then
if ! _exists "nc"; then if ! _exists "socat"; then
_err "Please install netcat(nc) tools first." _err "Please install socat tools first."
return 1 return 1
fi fi
fi fi
...@@ -2989,6 +3035,7 @@ _on_issue_err() { ...@@ -2989,6 +3035,7 @@ _on_issue_err() {
_chk_post_hook="$1" _chk_post_hook="$1"
_chk_vlist="$2" _chk_vlist="$2"
_debug _on_issue_err _debug _on_issue_err
if [ "$LOG_FILE" ]; then if [ "$LOG_FILE" ]; then
_err "Please check log file for more details: $LOG_FILE" _err "Please check log file for more details: $LOG_FILE"
else else
...@@ -3024,6 +3071,10 @@ _on_issue_err() { ...@@ -3024,6 +3071,10 @@ _on_issue_err() {
) )
fi fi
if [ "$IS_RENEW" = "1" ] && _hasfield "$Le_Webroot" "dns"; then
_err "$_DNS_MANUAL_ERR"
fi
if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
_debug "$(_dlg_versions)" _debug "$(_dlg_versions)"
fi fi
...@@ -3056,6 +3107,10 @@ _on_issue_success() { ...@@ -3056,6 +3107,10 @@ _on_issue_success() {
fi fi
fi fi
if _hasfield "$Le_Webroot" "dns"; then
_err "$_DNS_MANUAL_WARN"
fi
} }
updateaccount() { updateaccount() {
...@@ -3081,6 +3136,8 @@ __calc_account_thumbprint() { ...@@ -3081,6 +3136,8 @@ __calc_account_thumbprint() {
_regAccount() { _regAccount() {
_initpath _initpath
_reg_length="$1" _reg_length="$1"
_debug3 _regAccount "$_regAccount"
_initAPI
mkdir -p "$CA_DIR" mkdir -p "$CA_DIR"
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
...@@ -3103,77 +3160,53 @@ _regAccount() { ...@@ -3103,77 +3160,53 @@ _regAccount() {
if ! _calcjwk "$ACCOUNT_KEY_PATH"; then if ! _calcjwk "$ACCOUNT_KEY_PATH"; then
return 1 return 1
fi fi
_initAPI
_updateTos=""
_reg_res="$ACME_NEW_ACCOUNT_RES"
while true; do
_debug AGREEMENT "$AGREEMENT"
regjson='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}'
if [ "$ACME_VERSION" = "2" ]; then
regjson='{"termsOfServiceAgreed": true}'
if [ "$ACCOUNT_EMAIL" ]; then if [ "$ACCOUNT_EMAIL" ]; then
regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' regjson='{"contact": ["mailto: '$ACCOUNT_EMAIL'"], "termsOfServiceAgreed": true}'
fi 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
if [ -z "$_updateTos" ]; then _info "Registering account"
_info "Registering account"
if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then
_err "Register account Error: $response" _err "Register account Error: $response"
return 1 return 1
fi fi
if [ "$code" = "" ] || [ "$code" = '201' ]; then if [ "$code" = "" ] || [ "$code" = '201' ]; then
echo "$response" >"$ACCOUNT_JSON_PATH" echo "$response" >"$ACCOUNT_JSON_PATH"
_info "Registered" _info "Registered"
elif [ "$code" = '409' ]; then elif [ "$code" = '409' ] || [ "$code" = '200' ]; then
_info "Already registered" _info "Already registered"
else else
_err "Register account Error: $response" _err "Register account Error: $response"
return 1 return 1
fi fi
_accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" _accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
_debug "_accUri" "$_accUri" _debug "_accUri" "$_accUri"
_savecaconf "ACCOUNT_URL" "$_accUri" _savecaconf "ACCOUNT_URL" "$_accUri"
_tos="$(echo "$responseHeaders" | grep "^Link:.*rel=\"terms-of-service\"" | _head_n 1 | _egrep_o "<.*>" | tr -d '<>')" export ACCOUNT_URL="$ACCOUNT_URL"
_debug "_tos" "$_tos"
if [ -z "$_tos" ]; then
_debug "Use default tos: $DEFAULT_AGREEMENT"
_tos="$DEFAULT_AGREEMENT"
fi
if [ "$_tos" != "$AGREEMENT" ]; then
_updateTos=1
AGREEMENT="$_tos"
_reg_res="reg"
continue
fi
else CA_KEY_HASH="$(__calcAccountKeyHash)"
_debug "Update tos: $_tos" _debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
if ! _send_signed_request "$_accUri" "$regjson"; then _savecaconf CA_KEY_HASH "$CA_KEY_HASH"
_err "Update tos error."
return 1
fi
if [ "$code" = '202' ]; then
_info "Update account tos info success."
CA_KEY_HASH="$(__calcAccountKeyHash)"
_debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
_savecaconf CA_KEY_HASH "$CA_KEY_HASH"
elif [ "$code" = '403' ]; then
_err "It seems that the account key is already deactivated, please use a new account key."
return 1
else
_err "Update account error."
return 1
fi
fi
ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
_info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
return 0
done
if [ "$code" = '403' ]; then
_err "It seems that the account key is already deactivated, please use a new account key."
return 1
fi
ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
_info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
} }
#Implement deactivate account #Implement deactivate account
...@@ -3209,7 +3242,12 @@ deactivateaccount() { ...@@ -3209,7 +3242,12 @@ deactivateaccount() {
fi fi
_initAPI _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." _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 ' ,')
elif [ "$code" = "403" ]; then elif [ "$code" = "403" ]; then
...@@ -3297,7 +3335,7 @@ __get_domain_new_authz() { ...@@ -3297,7 +3335,7 @@ __get_domain_new_authz() {
_err "new-authz retry reach the max $_Max_new_authz_retry_times times." _err "new-authz retry reach the max $_Max_new_authz_retry_times times."
fi fi
if [ ! -z "$code" ] && [ ! "$code" = '201' ]; then if [ "$code" ] && [ "$code" != '201' ]; then
_err "new-authz error: $response" _err "new-authz error: $response"
return 1 return 1
fi fi
...@@ -3311,7 +3349,11 @@ __trigger_validation() { ...@@ -3311,7 +3349,11 @@ __trigger_validation() {
_debug2 _t_url "$_t_url" _debug2 _t_url "$_t_url"
_t_key_authz="$2" _t_key_authz="$2"
_debug2 _t_key_authz "$_t_key_authz" _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 #webroot, domain domainlist keylength
...@@ -3327,6 +3369,7 @@ issue() { ...@@ -3327,6 +3369,7 @@ issue() {
_web_roots="$1" _web_roots="$1"
_main_domain="$2" _main_domain="$2"
_alt_domains="$3" _alt_domains="$3"
if _contains "$_main_domain" ","; then if _contains "$_main_domain" ","; then
_main_domain=$(echo "$2,$3" | cut -d , -f 1) _main_domain=$(echo "$2,$3" | cut -d , -f 1)
_alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//") _alt_domains=$(echo "$2,$3" | cut -d , -f 2- | sed "s/,${NO_VALUE}$//")
...@@ -3453,32 +3496,109 @@ issue() { ...@@ -3453,32 +3496,109 @@ issue() {
sep='#' sep='#'
dvsep=',' dvsep=','
if [ -z "$vlist" ]; then 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 ',' ' ') alldomains=$(echo "$_main_domain,$_alt_domains" | tr ',' ' ')
_index=1 _index=0
_currentRoot="" _currentRoot=""
for d in $alldomains; do for d in $alldomains; do
_info "Getting webroot for domain" "$d" _info "Getting webroot for domain" "$d"
_index=$(_math $_index + 1)
_w="$(echo $_web_roots | cut -d , -f $_index)" _w="$(echo $_web_roots | cut -d , -f $_index)"
_debug _w "$_w" _debug _w "$_w"
if [ "$_w" ]; then if [ "$_w" ]; then
_currentRoot="$_w" _currentRoot="$_w"
fi fi
_debug "_currentRoot" "$_currentRoot" _debug "_currentRoot" "$_currentRoot"
_index=$(_math $_index + 1)
vtype="$VTYPE_HTTP" vtype="$VTYPE_HTTP"
#todo, v2 wildcard force to use dns
if _startswith "$_currentRoot" "dns"; then if _startswith "$_currentRoot" "dns"; then
vtype="$VTYPE_DNS" vtype="$VTYPE_DNS"
fi fi
if [ "$_currentRoot" = "$W_TLS" ]; then if [ "$_currentRoot" = "$W_TLS" ]; then
vtype="$VTYPE_TLS" if [ "$ACME_VERSION" = "2" ]; then
vtype="$VTYPE_TLS2"
else
vtype="$VTYPE_TLS"
fi
fi fi
if ! __get_domain_new_authz "$d"; then if [ "$ACME_VERSION" = "2" ]; then
_clearup response="$(echo "$_authorizations_map" | grep "^$d," | sed "s/$d,//")"
_on_issue_err "$_post_hook" _debug2 "response" "$response"
return 1 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 fi
if [ -z "$thumbprint" ]; then if [ -z "$thumbprint" ]; then
...@@ -3488,7 +3608,11 @@ issue() { ...@@ -3488,7 +3608,11 @@ issue() {
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry" _debug entry "$entry"
if [ -z "$entry" ]; then if [ -z "$entry" ]; then
_err "Error, can not get domain token $d" _err "Error, can not get domain token entry $d"
_supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
if [ "$_supported_vtypes" ]; then
_err "The supported validation types are: $_supported_vtypes, but you specified: $vtype"
fi
_clearup _clearup
_on_issue_err "$_post_hook" _on_issue_err "$_post_hook"
return 1 return 1
...@@ -3496,14 +3620,30 @@ issue() { ...@@ -3496,14 +3620,30 @@ issue() {
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token" _debug token "$token"
uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')" 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" _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" keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization" _debug keyauthorization "$keyauthorization"
if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then 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" keyauthorization="$STATE_VERIFIED"
_debug keyauthorization "$keyauthorization" _debug keyauthorization "$keyauthorization"
fi fi
...@@ -3523,7 +3663,7 @@ issue() { ...@@ -3523,7 +3663,7 @@ issue() {
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)
_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."
continue continue
...@@ -3531,23 +3671,27 @@ issue() { ...@@ -3531,23 +3671,27 @@ issue() {
if [ "$vtype" = "$VTYPE_DNS" ]; then if [ "$vtype" = "$VTYPE_DNS" ]; then
dnsadded='0' 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" _debug txtdomain "$txtdomain"
txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)" txt="$(printf "%s" "$keyauthorization" | _digest "sha256" | _url_replace)"
_debug txt "$txt" _debug txt "$txt"
d_api="$(_findHook "$d" dnsapi "$_currentRoot")" d_api="$(_findHook "$_dns_root_d" dnsapi "$_currentRoot")"
_debug d_api "$d_api" _debug d_api "$d_api"
if [ "$d_api" ]; then if [ "$d_api" ]; then
_info "Found domain api file: $d_api" _info "Found domain api file: $d_api"
else else
_err "Add the following TXT record:" _info "$(__red "Add the following TXT record:")"
_err "Domain: '$(__green "$txtdomain")'" _info "$(__red "Domain: '$(__green "$txtdomain")'")"
_err "TXT value: '$(__green "$txt")'" _info "$(__red "TXT value: '$(__green "$txt")'")"
_err "Please be aware that you prepend _acme-challenge. before your domain" _info "$(__red "Please be aware that you prepend _acme-challenge. before your domain")"
_err "so the resulting subdomain will be: $txtdomain" _info "$(__red "so the resulting subdomain will be: $txtdomain")"
continue continue
fi fi
...@@ -3583,7 +3727,7 @@ issue() { ...@@ -3583,7 +3727,7 @@ issue() {
_debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit." _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
_err "Please add the TXT records to the domains, and retry again." _err "Please add the TXT records to the domains, and retry again."
_clearup _clearup
_on_issue_err "$_post_hook" "$vlist" _on_issue_err "$_post_hook"
return 1 return 1
fi fi
...@@ -3631,13 +3775,12 @@ issue() { ...@@ -3631,13 +3775,12 @@ issue() {
_info "Standalone mode server" _info "Standalone mode server"
_ncaddr="$(_getfield "$_local_addr" "$_ncIndex")" _ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
_ncIndex="$(_math $_ncIndex + 1)" _ncIndex="$(_math $_ncIndex + 1)"
_startserver "$keyauthorization" "$_ncaddr" & _startserver "$keyauthorization" "$_ncaddr"
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_clearup _clearup
_on_issue_err "$_post_hook" "$vlist" _on_issue_err "$_post_hook" "$vlist"
return 1 return 1
fi fi
serverproc="$!"
sleep 1 sleep 1
_debug serverproc "$serverproc" _debug serverproc "$serverproc"
elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then elif [ "$_currentRoot" = "$MODE_STATELESS" ]; then
...@@ -3746,12 +3889,16 @@ issue() { ...@@ -3746,12 +3889,16 @@ issue() {
return 1 return 1
fi fi
if [ ! -z "$code" ] && [ ! "$code" = '202' ]; then if [ "$code" ] && [ "$code" != '202' ]; then
_err "$d:Challenge error: $response" if [ "$ACME_VERSION" = "2" ] && [ "$code" = '200' ]; then
_clearupwebbroot "$_currentRoot" "$removelevel" "$token" _debug "trigger validation code: $code"
_clearup else
_on_issue_err "$_post_hook" "$vlist" _err "$d:Challenge error: $response"
return 1 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
_on_issue_err "$_post_hook" "$vlist"
return 1
fi
fi fi
waittimes=0 waittimes=0
...@@ -3834,18 +3981,42 @@ issue() { ...@@ -3834,18 +3981,42 @@ issue() {
_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 ! _send_signed_request "${ACME_NEW_ORDER}" "{\"resource\": \"$ACME_NEW_ORDER_RES\", \"csr\": \"$der\"}" "needbase64"; then if [ "$ACME_VERSION" = "2" ]; then
_err "Sign failed." if ! _send_signed_request "${Le_OrderFinalize}" "{\"csr\": \"$der\"}"; then
_on_issue_err "$_post_hook" _err "Sign failed."
return 1 _on_issue_err "$_post_hook"
fi 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" if ! _get "$Le_LinkCert" >"$CERT_PATH"; then
Le_LinkCert="$(grep -i '^Location.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" _err "Sign failed, code is not 200."
_debug "Le_LinkCert" "$Le_LinkCert" _on_issue_err "$_post_hook"
_savedomainconf "Le_LinkCert" "$Le_LinkCert" 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" echo "$BEGIN_CERT" >"$CERT_PATH"
#if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then #if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
...@@ -3859,6 +4030,12 @@ issue() { ...@@ -3859,6 +4030,12 @@ issue() {
fi fi
echo "$END_CERT" >>"$CERT_PATH" echo "$END_CERT" >>"$CERT_PATH"
fi
_debug "Le_LinkCert" "$Le_LinkCert"
_savedomainconf "Le_LinkCert" "$Le_LinkCert"
if [ "$Le_LinkCert" ]; then
_info "$(__green "Cert success.")" _info "$(__green "Cert success.")"
cat "$CERT_PATH" cat "$CERT_PATH"
...@@ -3885,40 +4062,49 @@ issue() { ...@@ -3885,40 +4062,49 @@ issue() {
_cleardomainconf "Le_Vlist" _cleardomainconf "Le_Vlist"
Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>') if [ "$ACME_VERSION" = "2" ]; then
if ! _contains "$Le_LinkIssuer" ":"; then _debug "v2 chain."
_info "$(__red "Relative issuer link found.")" else
Le_LinkIssuer="$_ACME_SERVER_HOST$Le_LinkIssuer" Le_LinkIssuer=$(grep -i '^Link' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | cut -d ';' -f 1 | tr -d '<>')
fi
_debug Le_LinkIssuer "$Le_LinkIssuer" if [ "$Le_LinkIssuer" ]; then
_savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer" if ! _contains "$Le_LinkIssuer" ":"; then
_info "$(__red "Relative issuer link found.")"
if [ "$Le_LinkIssuer" ]; then Le_LinkIssuer="$_ACME_SERVER_HOST$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 _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 fi
_link_issuer_retry=$(_math $_link_issuer_retry + 1) _debug Le_LinkIssuer "$Le_LinkIssuer"
_sleep "$_link_issuer_retry" _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
done
if [ "$_link_issuer_retry" = "$_MAX_ISSUER_RETRY" ]; then _link_issuer_retry=0
_err "Max retry for issuer ca cert is reached." _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
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) Le_CertCreateTime=$(_time)
_savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime" _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
...@@ -3972,7 +4158,10 @@ issue() { ...@@ -3972,7 +4158,10 @@ issue() {
Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400) Le_NextRenewTime=$(_math "$Le_NextRenewTime" - 86400)
_savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime" _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
_on_issue_success "$_post_hook" "$_renew_hook" if ! _on_issue_success "$_post_hook" "$_renew_hook"; then
_err "Call hook error."
return 1
fi
if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
_savedomainconf "Le_RealCertPath" "$_real_cert" _savedomainconf "Le_RealCertPath" "$_real_cert"
...@@ -4015,7 +4204,7 @@ renew() { ...@@ -4015,7 +4204,7 @@ renew() {
_savedomainconf Le_API "$Le_API" _savedomainconf Le_API "$Le_API"
fi fi
if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then if [ "$_OLD_STAGE_CA_HOST" = "$Le_API" ]; then
export Le_API="$STAGE_CA" export Le_API="$DEFAULT_STAGING_CA"
_savedomainconf Le_API "$Le_API" _savedomainconf Le_API "$Le_API"
fi fi
export ACME_DIRECTORY="$Le_API" export ACME_DIRECTORY="$Le_API"
...@@ -4033,6 +4222,11 @@ renew() { ...@@ -4033,6 +4222,11 @@ renew() {
return "$RENEW_SKIP" return "$RENEW_SKIP"
fi fi
if [ "$IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then
_info "Skip invalid cert for: $Le_Domain"
return 0
fi
IS_RENEW="1" IS_RENEW="1"
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" 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"
res="$?" res="$?"
...@@ -4098,8 +4292,6 @@ signcsr() { ...@@ -4098,8 +4292,6 @@ signcsr() {
return 1 return 1
fi fi
_initpath
_csrsubj=$(_readSubjectFromCSR "$_csrfile") _csrsubj=$(_readSubjectFromCSR "$_csrfile")
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_err "Can not read subject from csr: $_csrfile" _err "Can not read subject from csr: $_csrfile"
...@@ -4136,6 +4328,9 @@ signcsr() { ...@@ -4136,6 +4328,9 @@ 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"
...@@ -4361,7 +4556,12 @@ _installcert() { ...@@ -4361,7 +4556,12 @@ _installcert() {
if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
cp "$_real_key" "$_backup_path/key.bak" cp "$_real_key" "$_backup_path/key.bak"
fi fi
cat "$CERT_KEY_PATH" >"$_real_key" if [ -f "$_real_key" ]; then
cat "$CERT_KEY_PATH" >"$_real_key"
else
cat "$CERT_KEY_PATH" >"$_real_key"
chmod 600 "$_real_key"
fi
fi fi
if [ "$_real_fullchain" ]; then if [ "$_real_fullchain" ]; then
...@@ -4394,15 +4594,19 @@ _installcert() { ...@@ -4394,15 +4594,19 @@ _installcert() {
installcronjob() { installcronjob() {
_c_home="$1" _c_home="$1"
_initpath _initpath
if ! _exists "crontab"; then _CRONTAB="crontab"
_err "crontab doesn't exist, so, we can not install cron jobs." if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
_CRONTAB="fcrontab"
fi
if ! _exists "$_CRONTAB"; then
_err "crontab/fcrontab doesn't exist, so, we can not install cron jobs."
_err "All your certs will not be renewed automatically." _err "All your certs will not be renewed automatically."
_err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday." _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
return 1 return 1
fi fi
_info "Installing cron job" _info "Installing cron job"
if ! crontab -l | grep "$PROJECT_ENTRY --cron"; then if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
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 else
...@@ -4416,15 +4620,15 @@ installcronjob() { ...@@ -4416,15 +4620,15 @@ installcronjob() {
_t=$(_time) _t=$(_time)
random_minute=$(_math $_t % 60) random_minute=$(_math $_t % 60)
if _exists uname && uname -a | grep SunOS >/dev/null; then if _exists uname && uname -a | grep SunOS >/dev/null; then
crontab -l | { $_CRONTAB -l | {
cat cat
echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
} | crontab -- } | $_CRONTAB --
else else
crontab -l | { $_CRONTAB -l | {
cat cat
echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null" echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
} | crontab - } | $_CRONTAB -
fi fi
fi fi
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
...@@ -4436,16 +4640,21 @@ installcronjob() { ...@@ -4436,16 +4640,21 @@ installcronjob() {
} }
uninstallcronjob() { uninstallcronjob() {
if ! _exists "crontab"; then _CRONTAB="crontab"
if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
_CRONTAB="fcrontab"
fi
if ! _exists "$_CRONTAB"; then
return return
fi fi
_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 solaris >/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 -
fi fi
LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')" LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
_info LE_WORKING_DIR "$LE_WORKING_DIR" _info LE_WORKING_DIR "$LE_WORKING_DIR"
...@@ -4487,7 +4696,11 @@ revoke() { ...@@ -4487,7 +4696,11 @@ revoke() {
_initAPI _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}" uri="${ACME_REVOKE_CERT}"
if [ -f "$CERT_KEY_PATH" ]; then if [ -f "$CERT_KEY_PATH" ]; then
...@@ -4558,27 +4771,56 @@ _deactivate() { ...@@ -4558,27 +4771,56 @@ _deactivate() {
_d_type="$2" _d_type="$2"
_initpath _initpath
if ! __get_domain_new_authz "$_d_domain"; then if [ "$ACME_VERSION" = "2" ]; then
_err "Can not get domain new authz token." _identifiers="{\"type\":\"dns\",\"value\":\"$_d_domain\"}"
return 1 if ! _send_signed_request "$ACME_NEW_ORDER" "{\"identifiers\": [$_identifiers]}"; then
fi _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="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")" authzUri="$_authorizations_seg"
_debug "authzUri" "$authzUri" _debug2 "authzUri" "$authzUri"
if ! response="$(_get "$authzUri")"; then
_err "get to authz error."
_clearup
_on_issue_err "$_post_hook"
return 1
fi
if [ "$code" ] && [ ! "$code" = '201' ]; then response="$(echo "$response" | _normalizeJson)"
_err "new-authz error: $response" _debug2 response "$response"
return 1 _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 fi
entries="$(echo "$response" | _egrep_o '{ *"type":"[^"]*", *"status": *"valid", *"uri"[^}]*')" 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_HTTP" vtype="$VTYPE_DNS"
entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')" entry="$(printf "%s\n" "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry" _debug entry "$entry"
if [ -z "$entry" ]; then if [ -z "$entry" ]; then
...@@ -4588,7 +4830,7 @@ _deactivate() { ...@@ -4588,7 +4830,7 @@ _deactivate() {
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
_debug token "$token" _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" _debug uri "$uri"
keyauthorization="$token.$thumbprint" keyauthorization="$token.$thumbprint"
...@@ -4614,7 +4856,7 @@ _deactivate() { ...@@ -4614,7 +4856,7 @@ _deactivate() {
_debug _vtype "$_vtype" _debug _vtype "$_vtype"
_info "Found $_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" _debug uri "$uri"
if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then if [ "$_d_type" ] && [ "$_d_type" != "$_vtype" ]; then
...@@ -4624,7 +4866,13 @@ _deactivate() { ...@@ -4624,7 +4866,13 @@ _deactivate() {
_info "Deactivate: $_vtype" _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." _info "Deactivate: $_vtype success."
else else
_err "Can not deactivate $_vtype." _err "Can not deactivate $_vtype."
...@@ -4722,7 +4970,7 @@ _precheck() { ...@@ -4722,7 +4970,7 @@ _precheck() {
fi fi
if [ -z "$_nocron" ]; then if [ -z "$_nocron" ]; then
if ! _exists "crontab"; then if ! _exists "crontab" && ! _exists "fcrontab"; then
_err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
_err "We need to set cron job to renew the certs automatically." _err "We need to set cron job to renew the certs automatically."
_err "Otherwise, your certs will not be able to be renewed automatically." _err "Otherwise, your certs will not be able to be renewed automatically."
...@@ -4740,9 +4988,9 @@ _precheck() { ...@@ -4740,9 +4988,9 @@ _precheck() {
return 1 return 1
fi fi
if ! _exists "nc"; then if ! _exists "socat"; then
_err "It is recommended to install nc first, try to install 'nc' or 'netcat'." _err "It is recommended to install socat first."
_err "We use nc for standalone server if you use standalone mode." _err "We use socat for standalone server if you use standalone mode."
_err "If you don't use standalone mode, just ignore this warning." _err "If you don't use standalone mode, just ignore this warning."
fi fi
...@@ -4825,7 +5073,7 @@ _installalias() { ...@@ -4825,7 +5073,7 @@ _installalias() {
} }
# nocron confighome # nocron confighome noprofile
install() { install() {
if [ -z "$LE_WORKING_DIR" ]; then if [ -z "$LE_WORKING_DIR" ]; then
...@@ -4834,6 +5082,7 @@ install() { ...@@ -4834,6 +5082,7 @@ install() {
_nocron="$1" _nocron="$1"
_c_home="$2" _c_home="$2"
_noprofile="$3"
if ! _initpath; then if ! _initpath; then
_err "Install failed." _err "Install failed."
return 1 return 1
...@@ -4842,9 +5091,11 @@ install() { ...@@ -4842,9 +5091,11 @@ install() {
_debug "Skip install cron job" _debug "Skip install cron job"
fi fi
if ! _precheck "$_nocron"; then if [ "$IN_CRON" != "1" ]; then
_err "Pre-check failed, can not install." if ! _precheck "$_nocron"; then
return 1 _err "Pre-check failed, can not install."
return 1
fi
fi fi
if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then if [ -z "$_c_home" ] && [ "$LE_CONFIG_HOME" != "$LE_WORKING_DIR" ]; then
...@@ -4897,7 +5148,9 @@ install() { ...@@ -4897,7 +5148,9 @@ install() {
_info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
_installalias "$_c_home" if [ "$IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then
_installalias "$_c_home"
fi
for subf in $_SUB_FOLDERS; do for subf in $_SUB_FOLDERS; do
if [ -d "$subf" ]; then if [ -d "$subf" ]; then
...@@ -4929,8 +5182,14 @@ install() { ...@@ -4929,8 +5182,14 @@ install() {
if [ -z "$NO_DETECT_SH" ]; then if [ -z "$NO_DETECT_SH" ]; then
#Modify shebang #Modify shebang
if _exists bash; then 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." _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" _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
for subf in $_SUB_FOLDERS; do for subf in $_SUB_FOLDERS; do
if [ -d "$LE_WORKING_DIR/$subf" ]; then if [ -d "$LE_WORKING_DIR/$subf" ]; then
...@@ -4987,7 +5246,7 @@ _uninstallalias() { ...@@ -4987,7 +5246,7 @@ _uninstallalias() {
} }
cron() { cron() {
IN_CRON=1 export IN_CRON=1
_initpath _initpath
_info "$(__green "===Starting cron===")" _info "$(__green "===Starting cron===")"
if [ "$AUTO_UPGRADE" = "1" ]; then if [ "$AUTO_UPGRADE" = "1" ]; then
...@@ -5035,7 +5294,7 @@ Commands: ...@@ -5035,7 +5294,7 @@ Commands:
--renew, -r Renew a cert. --renew, -r Renew a cert.
--renew-all Renew all the certs. --renew-all Renew all the certs.
--revoke Revoke a cert. --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. --list List all the certs.
--showcsr Show the content of a csr. --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. --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.
...@@ -5115,10 +5374,11 @@ Parameters: ...@@ -5115,10 +5374,11 @@ Parameters:
" "
} }
# nocron # nocron noprofile
_installOnline() { _installOnline() {
_info "Installing from online archive." _info "Installing from online archive."
_nocron="$1" _nocron="$1"
_noprofile="$2"
if [ ! "$BRANCH" ]; then if [ ! "$BRANCH" ]; then
BRANCH="master" BRANCH="master"
fi fi
...@@ -5139,7 +5399,7 @@ _installOnline() { ...@@ -5139,7 +5399,7 @@ _installOnline() {
cd "$PROJECT_NAME-$BRANCH" cd "$PROJECT_NAME-$BRANCH"
chmod +x $PROJECT_ENTRY chmod +x $PROJECT_ENTRY
if ./$PROJECT_ENTRY install "$_nocron"; then if ./$PROJECT_ENTRY install "$_nocron" "" "$_noprofile"; then
_info "Install success!" _info "Install success!"
fi fi
...@@ -5155,7 +5415,7 @@ upgrade() { ...@@ -5155,7 +5415,7 @@ upgrade() {
_initpath _initpath
export LE_WORKING_DIR export LE_WORKING_DIR
cd "$LE_WORKING_DIR" cd "$LE_WORKING_DIR"
_installOnline "nocron" _installOnline "nocron" "noprofile"
); then ); then
_info "Upgrade success!" _info "Upgrade success!"
exit 0 exit 0
...@@ -5342,6 +5602,10 @@ _process() { ...@@ -5342,6 +5602,10 @@ _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
...@@ -5435,7 +5699,7 @@ _process() { ...@@ -5435,7 +5699,7 @@ _process() {
;; ;;
--dns) --dns)
wvalue="dns" wvalue="dns"
if ! _startswith "$2" "-"; then if [ "$2" ] && ! _startswith "$2" "-"; then
wvalue="$2" wvalue="$2"
shift shift
fi fi
...@@ -5545,7 +5809,7 @@ _process() { ...@@ -5545,7 +5809,7 @@ _process() {
HTTPS_INSECURE="1" HTTPS_INSECURE="1"
;; ;;
--ca-bundle) --ca-bundle)
_ca_bundle="$(_readlink -f "$2")" _ca_bundle="$(_readlink "$2")"
CA_BUNDLE="$_ca_bundle" CA_BUNDLE="$_ca_bundle"
shift shift
;; ;;
......
...@@ -4,7 +4,9 @@ Before you can deploy your cert, you must [issue the cert first](https://github. ...@@ -4,7 +4,9 @@ Before you can deploy your cert, you must [issue the cert first](https://github.
Here are the scripts to deploy the certs/key to the server/services. Here are the scripts to deploy the certs/key to the server/services.
## 1. Deploy the certs to your cpanel host. ## 1. Deploy the certs to your cpanel host
If you want to deploy using cpanel UAPI see 7.
(cpanel deploy hook is not finished yet, this is just an example.) (cpanel deploy hook is not finished yet, this is just an example.)
...@@ -18,7 +20,7 @@ export DEPLOY_CPANEL_PASSWORD=PASSWORD ...@@ -18,7 +20,7 @@ export DEPLOY_CPANEL_PASSWORD=PASSWORD
acme.sh --deploy -d example.com --deploy-hook cpanel acme.sh --deploy -d example.com --deploy-hook cpanel
``` ```
## 2. Deploy ssl cert on kong proxy engine based on api. ## 2. Deploy ssl cert on kong proxy engine based on api
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert). Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
Currently supports Kong-v0.10.x. Currently supports Kong-v0.10.x.
...@@ -27,11 +29,11 @@ Currently supports Kong-v0.10.x. ...@@ -27,11 +29,11 @@ Currently supports Kong-v0.10.x.
acme.sh --deploy -d ftp.example.com --deploy-hook kong acme.sh --deploy -d ftp.example.com --deploy-hook kong
``` ```
## 3. Deploy the cert to remote server through SSH access. ## 3. Deploy the cert to remote server through SSH access
(TODO) (TODO)
## 4. Deploy the cert to local vsftpd server. ## 4. Deploy the cert to local vsftpd server
```sh ```sh
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
...@@ -53,7 +55,7 @@ export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart" ...@@ -53,7 +55,7 @@ export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart"
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
``` ```
## 5. Deploy the cert to local exim4 server. ## 5. Deploy the cert to local exim4 server
```sh ```sh
acme.sh --deploy -d ftp.example.com --deploy-hook exim4 acme.sh --deploy -d ftp.example.com --deploy-hook exim4
...@@ -80,3 +82,37 @@ acme.sh --deploy -d ftp.example.com --deploy-hook exim4 ...@@ -80,3 +82,37 @@ acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```sh ```sh
acme.sh --deploy -d ftp.example.com --deploy-hook keychain acme.sh --deploy -d ftp.example.com --deploy-hook keychain
``` ```
## 7. Deploy to cpanel host using UAPI
This hook is using UAPI and works in cPanel & WHM version 56 or newer.
```
acme.sh --deploy -d example.com --deploy-hook cpanel_uapi
```
DEPLOY_CPANEL_USER is required only if you run the script as root and it should contain cpanel username.
```sh
export DEPLOY_CPANEL_USER=username
acme.sh --deploy -d example.com --deploy-hook cpanel_uapi
```
Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain.
## 8. Deploy the cert to your FRITZ!Box router
You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables:
```sh
$ export DEPLOY_FRITZBOX_USERNAME=my_username
$ export DEPLOY_FRITZBOX_PASSWORD=the_password
$ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com
```
After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this:
```sh
acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox
```
## 9. Deploy the cert to strongswan
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook strongswan
```
#!/usr/bin/env sh
#Here is the script to deploy the cert to your cpanel account by the cpanel APIs.
#returns 0 means success, otherwise error.
#export DEPLOY_CPANEL_USER=myusername
#export DEPLOY_CPANEL_PASSWORD=PASSWORD
######## Public functions #####################
#domain keyfile certfile cafile fullchain
cpanel_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"
_err "Not implemented yet"
return 1
}
#!/usr/bin/env sh
# Here is the script to deploy the cert to your cpanel using the cpanel API.
# Uses command line uapi. --user option is needed only if run as root.
# Returns 0 when success.
# Written by Santeri Kannisto <santeri.kannisto@2globalnomads.info>
# Public domain, 2017
#export DEPLOY_CPANEL_USER=myusername
######## Public functions #####################
#domain keyfile certfile cafile fullchain
cpanel_uapi_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"
if ! _exists uapi; then
_err "The command uapi is not found."
return 1
fi
if ! _exists php; then
_err "The command php is not found."
return 1
fi
# read cert and key files and urlencode both
_certstr=$(cat "$_ccert")
_keystr=$(cat "$_ckey")
_cert=$(php -r "echo urlencode(\"$_certstr\");")
_key=$(php -r "echo urlencode(\"$_keystr\");")
_debug _cert "$_cert"
_debug _key "$_key"
if [ "$(id -u)" = 0 ]; then
if [ -z "$DEPLOY_CPANEL_USER" ]; then
_err "It seems that you are root, please define the target user name: export DEPLOY_CPANEL_USER=username"
return 1
fi
_savedomainconf DEPLOY_CPANEL_USER "$DEPLOY_CPANEL_USER"
_response=$(uapi --user="$DEPLOY_CPANEL_USER" SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
else
_response=$(uapi SSL install_ssl domain="$_cdomain" cert="$_cert" key="$_key")
fi
error_response="status: 0"
if test "${_response#*$error_response}" != "$_response"; then
_err "Error in deploying certificate:"
_err "$_response"
return 1
fi
_debug response "$_response"
_info "Certificate successfully deployed"
return 0
}
#!/usr/bin/env sh
#Here is a script to deploy cert to an AVM FRITZ!Box router.
#returns 0 means success, otherwise error.
#DEPLOY_FRITZBOX_USERNAME="username"
#DEPLOY_FRITZBOX_PASSWORD="password"
#DEPLOY_FRITZBOX_URL="https://fritz.box"
# Kudos to wikrie at Github for his FRITZ!Box update script:
# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb
######## Public functions #####################
#domain keyfile certfile cafile fullchain
fritzbox_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"
if ! _exists iconv; then
_err "iconv not found"
return 1
fi
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}"
_fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}"
_fritzbox_url="${DEPLOY_FRITZBOX_URL}"
_debug _fritzbox_url "$_fritzbox_url"
_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."
return 1
fi
if [ -z "$_fritzbox_password" ]; then
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
return 1
fi
if [ -z "$_fritzbox_url" ]; then
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
return 1
fi
_saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}"
_saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}"
_saveaccountconf DEPLOY_FRITZBOX_URL "${_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
export HTTPS_INSECURE=1
_info "Log in to the FRITZ!Box"
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
_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>.*$//')"
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
return 1
fi
_info "Generate form POST request"
_post_request="$(_mktemp)"
_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
# _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it!
_CERTPASSWORD_=
{
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"
printf -- "--"
printf -- "%s\r\n" "${_post_boundary}"
printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"
printf "Content-Type: application/octet-stream\r\n\r\n"
cat "${_ckey}" "${_cfullchain}"
printf "\r\n"
printf -- "--"
printf -- "%s--" "${_post_boundary}"
} >>"${_post_request}"
_info "Upload certificate to the FRITZ!Box"
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
_post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL
retval=$?
if [ $retval = 0 ]; then
_info "Upload successful"
else
_err "Upload failed"
fi
rm "${_post_request}"
return $retval
}
#!/usr/bin/env sh
#Here is a sample custom api script.
#This file name is "myapi.sh"
#So, here must be a method myapi_deploy()
#Which will be called by acme.sh to deploy the cert
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
strongswan_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_info "Using strongswan"
if [ -x /usr/sbin/ipsec ]; then
_ipsec=/usr/sbin/ipsec
elif [ -x /usr/sbin/strongswan ]; then
_ipsec=/usr/sbin/strongswan
elif [ -x /usr/local/sbin/ipsec ]; then
_ipsec=/usr/local/sbin/ipsec
else
_err "no strongswan or ipsec command is detected"
return 1
fi
_info _ipsec "$_ipsec"
_confdir=$($_ipsec --confdir)
if [ $? -ne 0 ] || [ -z "$_confdir" ]; then
_err "no strongswan --confdir is detected"
return 1
fi
_info _confdir "$_confdir"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
cat "$_ckey" >"${_confdir}/ipsec.d/private/$(basename "$_ckey")"
cat "$_ccert" >"${_confdir}/ipsec.d/certs/$(basename "$_ccert")"
cat "$_cca" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cca")"
cat "$_cfullchain" >"${_confdir}/ipsec.d/cacerts/$(basename "$_cfullchain")"
$_ipsec reload
}
#!/usr/bin/env sh
#Here is a script to deploy cert to unifi server.
#returns 0 means success, otherwise error.
#DEPLOY_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
#DEPLOY_UNIFI_KEYPASS="aircontrolenterprise"
#DEPLOY_UNIFI_RELOAD="service unifi restart"
######## Public functions #####################
#domain keyfile certfile cafile fullchain
unifi_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"
if ! _exists keytool; then
_err "keytool not found"
return 1
fi
DEFAULT_UNIFI_KEYSTORE="/usr/lib/unifi/data/keystore"
_unifi_keystore="${DEPLOY_UNIFI_KEYSTORE:-$DEFAULT_UNIFI_KEYSTORE}"
DEFAULT_UNIFI_KEYPASS="aircontrolenterprise"
_unifi_keypass="${DEPLOY_UNIFI_KEYPASS:-$DEFAULT_UNIFI_KEYPASS}"
DEFAULT_UNIFI_RELOAD="service unifi restart"
_reload="${DEPLOY_UNIFI_RELOAD:-$DEFAULT_UNIFI_RELOAD}"
_debug _unifi_keystore "$_unifi_keystore"
if [ ! -f "$_unifi_keystore" ]; then
if [ -z "$DEPLOY_UNIFI_KEYSTORE" ]; then
_err "unifi keystore is not found, please define DEPLOY_UNIFI_KEYSTORE"
return 1
else
_err "It seems that the specified unifi keystore is not valid, please check."
return 1
fi
fi
if [ ! -w "$_unifi_keystore" ]; then
_err "The file $_unifi_keystore is not writable, please change the permission."
return 1
fi
_info "Generate import pkcs12"
_import_pkcs12="$(_mktemp)"
_toPkcs "$_import_pkcs12" "$_ckey" "$_ccert" "$_cca" "$_unifi_keypass" unifi root
if [ "$?" != "0" ]; then
_err "Oops, error creating import pkcs12, please report bug to us."
return 1
fi
_info "Modify unifi keystore: $_unifi_keystore"
if keytool -importkeystore \
-deststorepass "$_unifi_keypass" -destkeypass "$_unifi_keypass" -destkeystore "$_unifi_keystore" \
-srckeystore "$_import_pkcs12" -srcstoretype PKCS12 -srcstorepass "$_unifi_keypass" \
-alias unifi -noprompt; then
_info "Import keystore success!"
rm "$_import_pkcs12"
else
_err "Import unifi keystore error, please report bug to us."
rm "$_import_pkcs12"
return 1
fi
_info "Run reload: $_reload"
if eval "$_reload"; then
_info "Reload success!"
if [ "$DEPLOY_UNIFI_KEYSTORE" ]; then
_savedomainconf DEPLOY_UNIFI_KEYSTORE "$DEPLOY_UNIFI_KEYSTORE"
else
_cleardomainconf DEPLOY_UNIFI_KEYSTORE
fi
if [ "$DEPLOY_UNIFI_KEYPASS" ]; then
_savedomainconf DEPLOY_UNIFI_KEYPASS "$DEPLOY_UNIFI_KEYPASS"
else
_cleardomainconf DEPLOY_UNIFI_KEYPASS
fi
if [ "$DEPLOY_UNIFI_RELOAD" ]; then
_savedomainconf DEPLOY_UNIFI_RELOAD "$DEPLOY_UNIFI_RELOAD"
else
_cleardomainconf DEPLOY_UNIFI_RELOAD
fi
return 0
else
_err "Reload error"
return 1
fi
return 0
}
...@@ -409,10 +409,13 @@ acme.sh --issue --dns dns_dgon -d example.com -d www.example.com ...@@ -409,10 +409,13 @@ acme.sh --issue --dns dns_dgon -d example.com -d www.example.com
## 21. Use ClouDNS.net API ## 21. Use ClouDNS.net API
You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/ You need to set the HTTP API user ID and password credentials. See: https://www.cloudns.net/wiki/article/42/. For security reasons, it's recommended to use a sub user ID that only has access to the necessary zones, as a regular API user has access to your entire account.
``` ```
export CLOUDNS_AUTH_ID=XXXXX # Use this for a sub auth ID
export CLOUDNS_SUB_AUTH_ID=XXXXX
# Use this for a regular auth ID
#export CLOUDNS_AUTH_ID=XXXXX
export CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" export CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
``` ```
...@@ -420,6 +423,7 @@ Ok, let's issue a cert now: ...@@ -420,6 +423,7 @@ Ok, let's issue a cert now:
``` ```
acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com
``` ```
The `CLOUDNS_AUTH_ID` and `CLOUDNS_AUTH_PASSWORD` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 22. Use Infoblox API ## 22. Use Infoblox API
...@@ -511,15 +515,12 @@ acme.sh --issue --dns dns_nsone -d example.com -d www.example.com ...@@ -511,15 +515,12 @@ acme.sh --issue --dns dns_nsone -d example.com -d www.example.com
export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
``` ```
Please note that since DuckDNS uses StartSSL as their cert provider, thus Please note that since DuckDNS uses StartSSL as their cert provider, thus
--insecure must be used when issuing certs: --insecure may need to be used when issuing certs:
``` ```
acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org
``` ```
Also, DuckDNS uses the domain name as username for recording changing, so the
account file will always store the lastly used domain name.
For issues, please report to https://github.com/raidenii/acme.sh/issues. For issues, please report to https://github.com/raidenii/acme.sh/issues.
## 28. Use Name.com API ## 28. Use Name.com API
...@@ -540,6 +541,222 @@ acme.sh --issue --dns dns_namecom -d example.com -d www.example.com ...@@ -540,6 +541,222 @@ acme.sh --issue --dns dns_namecom -d example.com -d www.example.com
For issues, please report to https://github.com/raidenii/acme.sh/issues. For issues, please report to https://github.com/raidenii/acme.sh/issues.
## 29. Use Dyn Managed DNS API to automatically issue cert
First, login to your Dyn Managed DNS account: https://portal.dynect.net/login/
It is recommended to add a new user specific for API access.
The minimum "Zones & Records Permissions" required are:
```
RecordAdd
RecordUpdate
RecordDelete
RecordGet
ZoneGet
ZoneAddNode
ZoneRemoveNode
ZonePublish
```
Pass the API user credentials to the environment:
```
export DYN_Customer="customer"
export DYN_Username="apiuser"
export DYN_Password="secret"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_dyn -d example.com -d www.example.com
```
The `DYN_Customer`, `DYN_Username` and `DYN_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 30. Use pdd.yandex.ru API
```
export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```
Follow these instructions to get the token for your domain https://tech.yandex.com/domain/doc/concepts/access-docpage/
```
acme.sh --issue --dns dns_yandex -d mydomain.example.org
```
For issues, please report to https://github.com/non7top/acme.sh/issues.
## 31. Use Hurricane Electric
Hurricane Electric (https://dns.he.net/) doesn't have an API so just set your login credentials like so:
```
export HE_Username="yourusername"
export HE_Password="password"
```
Then you can issue your certificate:
```
acme.sh --issue --dns dns_he -d example.com -d www.example.com
```
The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
Please report any issues to https://github.com/angel333/acme.sh or to <me@ondrejsimek.com>.
## 32. Use UnoEuro API to automatically issue cert
First you need to login to your UnoEuro account to get your API key.
```
export UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export UNO_User="UExxxxxx"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_unoeuro -d example.com -d www.example.com
```
The `UNO_Key` and `UNO_User` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 33. Use INWX
[INWX](https://www.inwx.de/) offers an [xmlrpc api](https://www.inwx.de/de/help/apidoc) with your standard login credentials, set them like so:
```
export INWX_User="yourusername"
export INWX_Password="password"
```
Then you can issue your certificates with:
```
acme.sh --issue --dns dns_inwx -d example.com -d www.example.com
```
The `INWX_User` and `INWX_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 34. User Servercow API v1
Create a new user from the servercow control center. Don't forget to activate **DNS API** for this user.
```
export SERVERCOW_API_Username=username
export SERVERCOW_API_Password=password
```
Now you cann issue a cert:
```
acme.sh --issue --dns dns_servercow -d example.com -d www.example.com
```
Both, `SERVERCOW_API_Username` and `SERVERCOW_API_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 35. Use Namesilo.com API
You'll need to generate an API key at https://www.namesilo.com/account_api.php
Optionally you may restrict the access to an IP range there.
```
export Namesilo_Key="xxxxxxxxxxxxxxxxxxxxxxxx"
```
And now you can issue certs with:
```
acme.sh --issue --dns dns_namesilo --dnssleep 900 -d example.com -d www.example.com
```
## 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.
## 39. Use zonomi.com domain API to automatically issue cert
First you need to login to your account to find your API key from: http://zonomi.com/app/dns/dyndns.jsp
Your will find your api key in the example urls:
```sh
https://zonomi.com/app/dns/dyndns.jsp?host=example.com&api_key=1063364558943540954358668888888888
```
```sh
export ZM_Key="1063364558943540954358668888888888"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_zonomi -d example.com -d www.example.com
```
The `ZM_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 40. Use DreamHost DNS API
DNS API keys may be created at https://panel.dreamhost.com/?tree=home.api.
Ensure the created key has add and remove privelages.
```
export DH_API_Key="<api key>"
acme.sh --issue --dns dns_dreamhost -d example.com -d www.example.com
```
The 'DH_API_KEY' will be saved in `~/.acme.sh/account.conf` and will
be reused when needed.
# Use custom API # Use custom API
If your API is not supported yet, you can write your own DNS API. If your API is not supported yet, you can write your own DNS API.
...@@ -556,6 +773,7 @@ acme.sh --issue --dns dns_myapi -d example.com -d www.example.com ...@@ -556,6 +773,7 @@ acme.sh --issue --dns dns_myapi -d example.com -d www.example.com
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh) For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
# Use lexicon DNS API # Use lexicon DNS API
......
...@@ -10,6 +10,8 @@ dns_ali_add() { ...@@ -10,6 +10,8 @@ dns_ali_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
Ali_Key="" Ali_Key=""
Ali_Secret="" Ali_Secret=""
...@@ -18,8 +20,8 @@ dns_ali_add() { ...@@ -18,8 +20,8 @@ dns_ali_add() {
fi fi
#save the api key and secret to the account conf file. #save the api key and secret to the account conf file.
_saveaccountconf Ali_Key "$Ali_Key" _saveaccountconf_mutable Ali_Key "$Ali_Key"
_saveaccountconf Ali_Secret "$Ali_Secret" _saveaccountconf_mutable Ali_Secret "$Ali_Secret"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
...@@ -32,6 +34,15 @@ dns_ali_add() { ...@@ -32,6 +34,15 @@ dns_ali_add() {
dns_ali_rm() { dns_ali_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
return 1
fi
_clean _clean
} }
...@@ -76,16 +87,14 @@ _ali_rest() { ...@@ -76,16 +87,14 @@ _ali_rest() {
return 1 return 1
fi fi
_debug2 response "$response"
if [ -z "$2" ]; then if [ -z "$2" ]; then
message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
if [ -n "$message" ]; then if [ "$message" ]; then
_err "$message" _err "$message"
return 1 return 1
fi fi
fi fi
_debug2 response "$response"
return 0
} }
_ali_urlencode() { _ali_urlencode() {
...@@ -112,12 +121,14 @@ _ali_nonce() { ...@@ -112,12 +121,14 @@ _ali_nonce() {
} }
_check_exist_query() { _check_exist_query() {
_qdomain="$1"
_qsubdomain="$2"
query='' query=''
query=$query'AccessKeyId='$Ali_Key query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=DescribeDomainRecords' query=$query'&Action=DescribeDomainRecords'
query=$query'&DomainName='$1 query=$query'&DomainName='$_qdomain
query=$query'&Format=json' query=$query'&Format=json'
query=$query'&RRKeyWord=_acme-challenge' query=$query'&RRKeyWord='$_qsubdomain
query=$query'&SignatureMethod=HMAC-SHA1' query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)" query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0' query=$query'&SignatureVersion=1.0'
...@@ -169,17 +180,21 @@ _describe_records_query() { ...@@ -169,17 +180,21 @@ _describe_records_query() {
} }
_clean() { _clean() {
_check_exist_query "$_domain" _check_exist_query "$_domain" "$_sub_domain"
if ! _ali_rest "Check exist records" "ignore"; then if ! _ali_rest "Check exist records" "ignore"; then
return 1 return 1
fi fi
records="$(echo "$response" -n | _egrep_o "\"RecordId\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")" record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)"
printf "%s" "$records" \ _debug2 record_id "$record_id"
| while read -r record_id; do
_delete_record_query "$record_id" if [ -z "$record_id" ]; then
_ali_rest "Delete record $record_id" "ignore" _debug "record not found, skip"
done else
_delete_record_query "$record_id"
_ali_rest "Delete record $record_id" "ignore"
fi
} }
_timestamp() { _timestamp() {
......
#!/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" "<summary>1</summary>" >/dev/null; then
_zone="$(echo "$autodns_response" | _egrep_o '<name>[^<]*</name>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_system_ns="$(echo "$autodns_response" | _egrep_o '<system_ns>[^<]*</system_ns>' | 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 "<auth>
<user>%s</user>
<password>%s</password>
<context>%s</context>
</auth>" "$AUTODNS_USER" "$AUTODNS_PASSWORD" "$AUTODNS_CONTEXT"
}
# Arguments:
# zone
_build_zone_inquire_xml() {
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<request>
%s
<task>
<code>0205</code>
<view>
<children>1</children>
<limit>1</limit>
</view>
<where>
<key>name</key>
<operator>eq</operator>
<value>%s</value>
</where>
</task>
</request>" "$(_build_request_auth_xml)" "$1"
}
# Arguments:
# zone
# subdomain
# txtvalue
# system_ns
_build_zone_update_xml() {
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<request>
%s
<task>
<code>0202001</code>
<default>
<rr_add>
<name>%s</name>
<ttl>600</ttl>
<type>TXT</type>
<value>%s</value>
</rr_add>
</default>
<zone>
<name>%s</name>
<system_ns>%s</system_ns>
</zone>
</task>
</request>" "$(_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" "<type>success</type>" >/dev/null; then
_info "success"
printf "%s" "$autodns_response"
return 0
fi
return 1
}
...@@ -19,18 +19,19 @@ dns_aws_add() { ...@@ -19,18 +19,19 @@ dns_aws_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 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 if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
AWS_ACCESS_KEY_ID="" AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY="" AWS_SECRET_ACCESS_KEY=""
_err "You don't specify aws route53 api key id and and api key secret yet." _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 return 1
fi fi
if [ -z "$AWS_SESSION_TOKEN" ]; then #save for future use
_saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID" _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
_saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY" _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
fi
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
...@@ -41,7 +42,26 @@ dns_aws_add() { ...@@ -41,7 +42,26 @@ dns_aws_add() {
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>" _info "Geting existing records for $fulldomain"
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
return 1
fi
if _contains "$response" "<Name>$fulldomain.</Name>"; then
_resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
_debug "_resource_record" "$_resource_record"
else
_debug "single new add"
fi
if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then
_info "The txt record already exists, skip"
return 0
fi
_debug "Adding records"
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords>$_resource_record<ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "txt record updated success." _info "txt record updated success."
...@@ -56,6 +76,8 @@ dns_aws_rm() { ...@@ -56,6 +76,8 @@ dns_aws_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 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" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
...@@ -65,7 +87,20 @@ dns_aws_rm() { ...@@ -65,7 +87,20 @@ dns_aws_rm() {
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>" _info "Geting existing records for $fulldomain"
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
return 1
fi
if _contains "$response" "<Name>$fulldomain.</Name>"; then
_resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
_debug "_resource_record" "$_resource_record"
else
_debug "no records exists, skip"
return 0
fi
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords>$_resource_record</ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "txt record deleted success." _info "txt record deleted success."
...@@ -84,9 +119,9 @@ _get_root() { ...@@ -84,9 +119,9 @@ _get_root() {
p=1 p=1
if aws_rest GET "2013-04-01/hostedzone"; then if aws_rest GET "2013-04-01/hostedzone"; then
_debug "response" "$response"
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug2 "Checking domain: $h"
if [ -z "$h" ]; then if [ -z "$h" ]; then
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
_debug "IsTruncated" _debug "IsTruncated"
...@@ -102,23 +137,23 @@ _get_root() { ...@@ -102,23 +137,23 @@ _get_root() {
fi fi
fi fi
#not valid #not valid
_err "Invalid domain"
return 1 return 1
fi fi
if _contains "$response" "<Name>$h.</Name>"; then if _contains "$response" "<Name>$h.</Name>"; then
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")" hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
_debug hostedzone "$hostedzone" _debug hostedzone "$hostedzone"
if [ -z "$hostedzone" ]; then if [ "$hostedzone" ]; then
_err "Error, can not get hostedzone." _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
_err "Can not find domain id: $h"
return 1 return 1
fi fi
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi fi
p=$i p=$i
i=$(_math "$i" + 1) i=$(_math "$i" + 1)
...@@ -208,7 +243,7 @@ aws_rest() { ...@@ -208,7 +243,7 @@ aws_rest() {
kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
_debug2 kServiceH "$kServiceH" _debug2 kServiceH "$kServiceH"
kSigningH="$(printf "aws4_request%s" | _hmac "$Hash" "$kServiceH" hex)" kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)"
_debug2 kSigningH "$kSigningH" _debug2 kSigningH "$kSigningH"
signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
...@@ -232,6 +267,7 @@ aws_rest() { ...@@ -232,6 +267,7 @@ aws_rest() {
fi fi
_ret="$?" _ret="$?"
_debug2 response "$response"
if [ "$_ret" = "0" ]; then if [ "$_ret" = "0" ]; then
if _contains "$response" "<ErrorResponse"; then if _contains "$response" "<ErrorResponse"; then
_err "Response error:$response" _err "Response error:$response"
......
#!/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
}
...@@ -51,33 +51,36 @@ dns_cf_add() { ...@@ -51,33 +51,36 @@ dns_cf_add() {
return 1 return 1
fi fi
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
_debug count "$count" # we can not use updating anymore.
if [ "$count" = "0" ]; then # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
_info "Adding record" # _debug count "$count"
if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then # if [ "$count" = "0" ]; then
if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then _info "Adding record"
_info "Added, OK" if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
return 0 if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
else _info "Added, OK"
_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"
return 0 return 0
else
_err "Add txt record error."
return 1
fi fi
_err "Update error"
return 1
fi 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
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
# Repository: https://github.com/ClouDNS/acme.sh/ # Repository: https://github.com/ClouDNS/acme.sh/
#CLOUDNS_AUTH_ID=XXXXX #CLOUDNS_AUTH_ID=XXXXX
#CLOUDNS_SUB_AUTH_ID=XXXXX
#CLOUDNS_AUTH_PASSWORD="YYYYYYYYY" #CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
CLOUDNS_API="https://api.cloudns.net" CLOUDNS_API="https://api.cloudns.net"
...@@ -96,8 +97,20 @@ _dns_cloudns_init_check() { ...@@ -96,8 +97,20 @@ _dns_cloudns_init_check() {
return 0 return 0
fi fi
if [ -z "$CLOUDNS_AUTH_ID" ]; then CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}"
_err "CLOUDNS_AUTH_ID is not configured" CLOUDNS_SUB_AUTH_ID="${CLOUDNS_SUB_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_SUB_AUTH_ID)}"
CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}"
if [ -z "$CLOUDNS_AUTH_ID$CLOUDNS_SUB_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
CLOUDNS_AUTH_ID=""
CLOUDNS_SUB_AUTH_ID=""
CLOUDNS_AUTH_PASSWORD=""
_err "You don't specify cloudns api id and password yet."
_err "Please create you id and password and try again."
return 1
fi
if [ -z "$CLOUDNS_AUTH_ID" ] && [ -z "$CLOUDNS_SUB_AUTH_ID" ]; then
_err "CLOUDNS_AUTH_ID or CLOUDNS_SUB_AUTH_ID is not configured"
return 1 return 1
fi fi
...@@ -113,6 +126,11 @@ _dns_cloudns_init_check() { ...@@ -113,6 +126,11 @@ _dns_cloudns_init_check() {
return 1 return 1
fi fi
#save the api id and password to the account conf file.
_saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
_saveaccountconf_mutable CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
_saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
CLOUDNS_INIT_CHECK_COMPLETED=1 CLOUDNS_INIT_CHECK_COMPLETED=1
return 0 return 0
...@@ -154,12 +172,19 @@ _dns_cloudns_http_api_call() { ...@@ -154,12 +172,19 @@ _dns_cloudns_http_api_call() {
method=$1 method=$1
_debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID" _debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
_debug CLOUDNS_SUB_AUTH_ID "$CLOUDNS_SUB_AUTH_ID"
_debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD" _debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
if [ ! -z "$CLOUDNS_SUB_AUTH_ID" ]; then
auth_user="sub-auth-id=$CLOUDNS_SUB_AUTH_ID"
else
auth_user="auth-id=$CLOUDNS_AUTH_ID"
fi
if [ -z "$2" ]; then if [ -z "$2" ]; then
data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD" data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD"
else else
data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2" data="$auth_user&auth-password=$CLOUDNS_AUTH_PASSWORD&$2"
fi fi
response="$(_get "$CLOUDNS_API/$method?$data")" response="$(_get "$CLOUDNS_API/$method?$data")"
......
...@@ -36,33 +36,18 @@ dns_cx_add() { ...@@ -36,33 +36,18 @@ dns_cx_add() {
return 1 return 1
fi fi
existing_records "$_domain" "$_sub_domain" add_record "$_domain" "$_sub_domain" "$txtvalue"
_debug count "$count"
if [ "$?" != "0" ]; then
_err "Error get existing records."
return 1
fi
if [ "$count" = "0" ]; then
add_record "$_domain" "$_sub_domain" "$txtvalue"
else
update_record "$_domain" "$_sub_domain" "$txtvalue"
fi
if [ "$?" = "0" ]; then
return 0
fi
return 1
} }
#fulldomain #fulldomain txtvalue
dns_cx_rm() { dns_cx_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
REST_API="$CX_Api" REST_API="$CX_Api"
if _get_root "$fulldomain"; then if _get_root "$fulldomain"; then
record_id="" record_id=""
existing_records "$_domain" "$_sub_domain" existing_records "$_domain" "$_sub_domain" "$txtvalue"
if ! [ "$record_id" = "" ]; then if [ "$record_id" ]; then
_rest DELETE "record/$record_id/$_domain_id" "{}" _rest DELETE "record/$record_id/$_domain_id" "{}"
_info "Deleted record ${fulldomain}" _info "Deleted record ${fulldomain}"
fi fi
...@@ -77,7 +62,6 @@ existing_records() { ...@@ -77,7 +62,6 @@ existing_records() {
_debug "Getting txt records" _debug "Getting txt records"
root=$1 root=$1
sub=$2 sub=$2
count=0
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
return 1 return 1
fi fi
...@@ -89,7 +73,6 @@ existing_records() { ...@@ -89,7 +73,6 @@ existing_records() {
fi fi
if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then
count=1
record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1) record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1)
_debug record_id "$record_id" _debug record_id "$record_id"
return 0 return 0
...@@ -114,23 +97,6 @@ add_record() { ...@@ -114,23 +97,6 @@ add_record() {
return 0 return 0
} }
#update the txt record
#Usage: root sub txtvalue
update_record() {
root=$1
sub=$2
txtvalue=$3
fulldomain="$sub.$root"
_info "Updating record"
if _rest PUT "record/$record_id" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then
return 0
fi
return 1
}
#################### Private functions below ################################## #################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
......
...@@ -15,6 +15,8 @@ dns_dp_add() { ...@@ -15,6 +15,8 @@ dns_dp_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}"
DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}"
if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then
DP_Id="" DP_Id=""
DP_Key="" DP_Key=""
...@@ -24,8 +26,8 @@ dns_dp_add() { ...@@ -24,8 +26,8 @@ dns_dp_add() {
fi fi
#save the api key and email to the account conf file. #save the api key and email to the account conf file.
_saveaccountconf DP_Id "$DP_Id" _saveaccountconf_mutable DP_Id "$DP_Id"
_saveaccountconf DP_Key "$DP_Key" _saveaccountconf_mutable DP_Key "$DP_Key"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
...@@ -33,24 +35,18 @@ dns_dp_add() { ...@@ -33,24 +35,18 @@ dns_dp_add() {
return 1 return 1
fi fi
existing_records "$_domain" "$_sub_domain" add_record "$_domain" "$_sub_domain" "$txtvalue"
_debug count "$count"
if [ "$?" != "0" ]; then
_err "Error get existing records."
return 1
fi
if [ "$count" = "0" ]; then
add_record "$_domain" "$_sub_domain" "$txtvalue"
else
update_record "$_domain" "$_sub_domain" "$txtvalue"
fi
} }
#fulldomain txtvalue #fulldomain txtvalue
dns_dp_rm() { dns_dp_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}"
DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
...@@ -83,37 +79,6 @@ dns_dp_rm() { ...@@ -83,37 +79,6 @@ dns_dp_rm() {
} }
#usage: root sub
#return if the sub record already exists.
#echos the existing records count.
# '0' means doesn't exist
existing_records() {
_debug "Getting txt records"
root=$1
sub=$2
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
return 1
fi
if _contains "$response" 'No records'; then
count=0
return 0
fi
if _contains "$response" "Action completed successful"; then
count=$(printf "%s" "$response" | grep -c '<type>TXT</type>' | tr -d ' ')
record_id=$(printf "%s" "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1)
_debug record_id "$record_id"
return 0
else
_err "get existing records error."
return 1
fi
count=0
}
#add the txt record. #add the txt record.
#usage: root sub txtvalue #usage: root sub txtvalue
add_record() { add_record() {
...@@ -128,34 +93,7 @@ add_record() { ...@@ -128,34 +93,7 @@ add_record() {
return 1 return 1
fi fi
if _contains "$response" "Action completed successful"; then _contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
return 0
fi
return 1 #error
}
#update the txt record
#Usage: root sub txtvalue
update_record() {
root=$1
sub=$2
txtvalue=$3
fulldomain="$sub.$root"
_info "Updating record"
if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then
return 1
fi
if _contains "$response" "Action completed successful"; then
return 0
fi
return 1 #error
} }
#################### Private functions below ################################## #################### Private functions below ##################################
......
#!/usr/bin/env sh
#Author: RhinoLance
#Report Bugs here: https://github.com/RhinoLance/acme.sh
#
#define the api endpoint
DH_API_ENDPOINT="https://api.dreamhost.com/"
querystring=""
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dreamhost_add() {
fulldomain=$1
txtvalue=$2
if ! validate "$fulldomain" "$txtvalue"; then
return 1
fi
querystring="key=$DH_API_KEY&cmd=dns-add_record&record=$fulldomain&type=TXT&value=$txtvalue"
if ! submit "$querystring"; then
return 1
fi
return 0
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_dreamhost_rm() {
fulldomain=$1
txtvalue=$2
if ! validate "$fulldomain" "$txtvalue"; then
return 1
fi
querystring="key=$DH_API_KEY&cmd=dns-remove_record&record=$fulldomain&type=TXT&value=$txtvalue"
if ! submit "$querystring"; then
return 1
fi
return 0
}
#################### Private functions below ##################################
#send the command to the api endpoint.
submit() {
querystring=$1
url="$DH_API_ENDPOINT?$querystring"
_debug url "$url"
if ! response="$(_get "$url")"; then
_err "Error <$1>"
return 1
fi
if [ -z "$2" ]; then
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
if [ -n "$message" ]; then
_err "$message"
return 1
fi
fi
_debug response "$response"
return 0
}
#check that we have a valid API Key
validate() {
fulldomain=$1
txtvalue=$2
_info "Using dreamhost"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
#retrieve the API key from the environment variable if it exists, otherwise look for a saved key.
DH_API_KEY="${DH_API_KEY:-$(_readaccountconf_mutable DH_API_KEY)}"
if [ -z "$DH_API_KEY" ]; then
DH_API_KEY=""
_err "You didn't specify the DreamHost api key yet (export DH_API_KEY=\"<api key>\")"
_err "Please login to your control panel, create a key and try again."
return 1
fi
#save the api key to the account conf file.
_saveaccountconf_mutable DH_API_KEY "$DH_API_KEY"
}
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