Browse Source

Merge remote-tracking branch 'upstream/master'

pull/1318/head
raidenii 7 years ago
parent
commit
9046509b95
  1. 22
      .travis.yml
  2. 7
      Dockerfile
  3. 177
      README.md
  4. 748
      acme.sh
  5. 46
      deploy/README.md
  6. 29
      deploy/cpanel.sh
  7. 64
      deploy/cpanel_uapi.sh
  8. 108
      deploy/fritzbox.sh
  9. 55
      deploy/strongswan.sh
  10. 100
      deploy/unifi.sh
  11. 230
      dnsapi/README.md
  12. 43
      dnsapi/dns_ali.sh
  13. 264
      dnsapi/dns_autodns.sh
  14. 62
      dnsapi/dns_aws.sh
  15. 249
      dnsapi/dns_azure.sh
  16. 33
      dnsapi/dns_cf.sh
  17. 33
      dnsapi/dns_cloudns.sh
  18. 42
      dnsapi/dns_cx.sh
  19. 82
      dnsapi/dns_dp.sh
  20. 97
      dnsapi/dns_dreamhost.sh
  21. 69
      dnsapi/dns_duckdns.sh
  22. 339
      dnsapi/dns_dyn.sh
  23. 77
      dnsapi/dns_freedns.sh
  24. 2
      dnsapi/dns_gandi_livedns.sh
  25. 67
      dnsapi/dns_gd.sh
  26. 158
      dnsapi/dns_he.sh
  27. 16
      dnsapi/dns_infoblox.sh
  28. 311
      dnsapi/dns_inwx.sh
  29. 1
      dnsapi/dns_ispconfig.sh
  30. 4
      dnsapi/dns_linode.sh
  31. 40
      dnsapi/dns_lua.sh
  32. 137
      dnsapi/dns_namesilo.sh
  33. 6
      dnsapi/dns_nsone.sh
  34. 107
      dnsapi/dns_ovh.sh
  35. 161
      dnsapi/dns_selectel.sh
  36. 170
      dnsapi/dns_servercow.sh
  37. 202
      dnsapi/dns_unoeuro.sh
  38. 106
      dnsapi/dns_yandex.sh
  39. 85
      dnsapi/dns_zonomi.sh

22
.travis.yml

@ -1,10 +1,14 @@
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,18 +22,8 @@ addons:
install: install:
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then - if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
brew update && brew install openssl;
brew info openssl;
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";
brew update && brew install socat;
export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ;
fi fi
script: 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

7
Dockerfile

@ -1,10 +1,10 @@
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/
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 \
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

177
README.md

@ -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)
# [中文说明](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
- 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.
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.
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 \
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
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
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
# 8. Use DNS mode:
Support the `dns-01` challenge.
```bash
acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
```
You should get an output like below:
**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.
We don't want to mess your nginx server, don't worry.**
```
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.**
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 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,13 +314,24 @@ 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
(DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
**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.)**
**More APIs coming soon...** **More APIs coming soon...**
@ -351,6 +340,39 @@ If your DNS provider is not on the supported list above, you can write your own
For more details: [How to use DNS API](dnsapi) 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)
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
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:
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.
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.
[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/)

748
acme.sh
File diff suppressed because it is too large
View File

46
deploy/README.md

@ -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
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.
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"
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
```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
```

29
deploy/cpanel.sh

@ -1,29 +0,0 @@
#!/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
}

64
deploy/cpanel_uapi.sh

@ -0,0 +1,64 @@
#!/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
}

108
deploy/fritzbox.sh

@ -0,0 +1,108 @@
#!/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
}

55
deploy/strongswan.sh

@ -0,0 +1,55 @@
#!/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
}

100
deploy/unifi.sh

@ -0,0 +1,100 @@
#!/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
}

230
dnsapi/README.md

@ -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:
``` ```
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
@ -512,14 +516,11 @@ 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
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
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

43
dnsapi/dns_ali.sh

@ -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() {
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 Ali_Secret "$Ali_Secret"
_saveaccountconf_mutable Ali_Key "$Ali_Key"
_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() {
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() {
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 \")"
if [ -n "$message" ]; then
message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
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() {
} }
_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() {
} }
_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 \")"
printf "%s" "$records" \
| while read -r record_id; do
record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)"
_debug2 record_id "$record_id"
if [ -z "$record_id" ]; then
_debug "record not found, skip"
else
_delete_record_query "$record_id" _delete_record_query "$record_id"
_ali_rest "Delete record $record_id" "ignore" _ali_rest "Delete record $record_id" "ignore"
done
fi
} }
_timestamp() { _timestamp() {

264
dnsapi/dns_autodns.sh

@ -0,0 +1,264 @@
#!/usr/bin/env sh
# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
# This is the InternetX autoDNS xml api wrapper for acme.sh
# Author: auerswald@gmail.com
# Created: 2018-01-14
#
# export AUTODNS_USER="username"
# export AUTODNS_PASSWORD="password"
# export AUTODNS_CONTEXT="context"
#
# Usage:
# acme.sh --issue --dns dns_autodns -d example.com
AUTODNS_API="https://gateway.autodns.com"
# Arguments:
# txtdomain
# txt
dns_autodns_add() {
fulldomain="$1"
txtvalue="$2"
AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
_err "You don't specify autodns user, password and context."
return 1
fi
_saveaccountconf_mutable AUTODNS_USER "$AUTODNS_USER"
_saveaccountconf_mutable AUTODNS_PASSWORD "$AUTODNS_PASSWORD"
_saveaccountconf_mutable AUTODNS_CONTEXT "$AUTODNS_CONTEXT"
_debug "First detect the root zone"
if ! _get_autodns_zone "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _zone "$_zone"
_debug _system_ns "$_system_ns"
_info "Adding TXT record"
autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
if [ "$?" -eq "0" ]; then
_info "Added, OK"
return 0
fi
return 1
}
# Arguments:
# txtdomain
# txt
dns_autodns_rm() {
fulldomain="$1"
txtvalue="$2"
AUTODNS_USER="${AUTODNS_USER:-$(_readaccountconf_mutable AUTODNS_USER)}"
AUTODNS_PASSWORD="${AUTODNS_PASSWORD:-$(_readaccountconf_mutable AUTODNS_PASSWORD)}"
AUTODNS_CONTEXT="${AUTODNS_CONTEXT:-$(_readaccountconf_mutable AUTODNS_CONTEXT)}"
if [ -z "$AUTODNS_USER" ] || [ -z "$AUTODNS_CONTEXT" ] || [ -z "$AUTODNS_PASSWORD" ]; then
_err "You don't specify autodns user, password and context."
return 1
fi
_debug "First detect the root zone"
if ! _get_autodns_zone "$fulldomain"; then
_err "zone not found"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _zone "$_zone"
_debug _system_ns "$_system_ns"
_info "Delete TXT record"
autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
if [ "$?" -eq "0" ]; then
_info "Deleted, OK"
return 0
fi
return 1
}
#################### Private functions below ##################################
# Arguments:
# fulldomain
# Returns:
# _sub_domain=_acme-challenge.www
# _zone=domain.com
# _system_ns
_get_autodns_zone() {
domain="$1"
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
# not valid
return 1
fi
autodns_response="$(_autodns_zone_inquire "$h")"
if [ "$?" -ne "0" ]; then
_err "invalid domain"
return 1
fi
if _contains "$autodns_response" "<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
}

62
dnsapi/dns_aws.sh

@ -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
_saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
_saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
fi
#save for future use
_saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
_saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
@ -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() {
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() {
_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() {
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,24 +137,24 @@ _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
_err "Error, can not get hostedzone."
return 1
fi
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>") _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain=$h
return 0 return 0
fi fi
_err "Can not find domain id: $h"
return 1 return 1
fi fi
fi
p=$i p=$i
i=$(_math "$i" + 1) i=$(_math "$i" + 1)
done done
@ -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() {
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"

249
dnsapi/dns_azure.sh

@ -0,0 +1,249 @@
#!/usr/bin/env sh
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
#
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate
#
dns_azure_add() {
fulldomain=$1
txtvalue=$2
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Subscription ID "
return 1
fi
if [ -z "$AZUREDNS_TENANTID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Tenant ID "
return 1
fi
if [ -z "$AZUREDNS_APPID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure App ID"
return 1
fi
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Client Secret"
return 1
fi
#save account details to account conf file.
_saveaccountconf_mutable AZUREDNS_SUBSCRIPTIONID "$AZUREDNS_SUBSCRIPTIONID"
_saveaccountconf_mutable AZUREDNS_TENANTID "$AZUREDNS_TENANTID"
_saveaccountconf_mutable AZUREDNS_APPID "$AZUREDNS_APPID"
_saveaccountconf_mutable AZUREDNS_CLIENTSECRET "$AZUREDNS_CLIENTSECRET"
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
_debug "$acmeRecordURI"
body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}"
_azure_rest PUT "$acmeRecordURI" "$body" "$accesstoken"
if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
_info "validation record added"
else
_err "error adding validation record ($_code)"
return 1
fi
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
#
# Ref: https://docs.microsoft.com/en-us/rest/api/dns/recordsets/delete
#
dns_azure_rm() {
fulldomain=$1
txtvalue=$2
AZUREDNS_SUBSCRIPTIONID="${AZUREDNS_SUBSCRIPTIONID:-$(_readaccountconf_mutable AZUREDNS_SUBSCRIPTIONID)}"
AZUREDNS_TENANTID="${AZUREDNS_TENANTID:-$(_readaccountconf_mutable AZUREDNS_TENANTID)}"
AZUREDNS_APPID="${AZUREDNS_APPID:-$(_readaccountconf_mutable AZUREDNS_APPID)}"
AZUREDNS_CLIENTSECRET="${AZUREDNS_CLIENTSECRET:-$(_readaccountconf_mutable AZUREDNS_CLIENTSECRET)}"
if [ -z "$AZUREDNS_SUBSCRIPTIONID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Subscription ID "
return 1
fi
if [ -z "$AZUREDNS_TENANTID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Tenant ID "
return 1
fi
if [ -z "$AZUREDNS_APPID" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure App ID"
return 1
fi
if [ -z "$AZUREDNS_CLIENTSECRET" ]; then
AZUREDNS_SUBSCRIPTIONID=""
AZUREDNS_TENANTID=""
AZUREDNS_APPID=""
AZUREDNS_CLIENTSECRET=""
_err "You didn't specify the Azure Client Secret"
return 1
fi
accesstoken=$(_azure_getaccess_token "$AZUREDNS_TENANTID" "$AZUREDNS_APPID" "$AZUREDNS_CLIENTSECRET")
if ! _get_root "$fulldomain" "$AZUREDNS_SUBSCRIPTIONID" "$accesstoken"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
acmeRecordURI="https://management.azure.com$(printf '%s' "$_domain_id" | sed 's/\\//g')/TXT/$_sub_domain?api-version=2017-09-01"
_debug "$acmeRecordURI"
body="{\"properties\": {\"TTL\": 3600, \"TXTRecords\": [{\"value\": [\"$txtvalue\"]}]}}"
_azure_rest DELETE "$acmeRecordURI" "" "$accesstoken"
if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then
_info "validation record removed"
else
_err "error removing validation record ($_code)"
return 1
fi
}
################### Private functions below ##################################
_azure_rest() {
m=$1
ep="$2"
data="$3"
accesstoken="$4"
export _H1="authorization: Bearer $accesstoken"
export _H2="accept: application/json"
export _H3="Content-Type: application/json"
_debug "$ep"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$ep" "" "$m")"
else
response="$(_get "$ep")"
fi
_debug2 response "$response"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
_debug2 "http response code $_code"
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
return 0
}
## Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service#request-an-access-token
_azure_getaccess_token() {
TENANTID=$1
clientID=$2
clientSecret=$3
export _H1="accept: application/json"
export _H2="Content-Type: application/x-www-form-urlencoded"
body="resource=$(printf "%s" 'https://management.core.windows.net/' | _url_encode)&client_id=$(printf "%s" "$clientID" | _url_encode)&client_secret=$(printf "%s" "$clientSecret" | _url_encode)&grant_type=client_credentials"
_debug data "$body"
response="$(_post "$body" "https://login.windows.net/$TENANTID/oauth2/token" "" "POST")"
accesstoken=$(echo "$response" | _egrep_o "\"access_token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
_debug2 "response $response"
if [ -z "$accesstoken" ]; then
_err "no acccess token received"
return 1
fi
if [ "$?" != "0" ]; then
_err "error $response"
return 1
fi
printf "%s" "$accesstoken"
return 0
}
_get_root() {
domain=$1
subscriptionId=$2
accesstoken=$3
i=2
p=1
## Ref: https://docs.microsoft.com/en-us/rest/api/dns/zones/list
## returns up to 100 zones in one response therefore handling more results is not not implemented
## (ZoneListResult with continuation token for the next page of results)
## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways
##
_azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?api-version=2017-09-01" "" "$accesstoken"
# Find matching domain name is Json response
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug2 "Checking domain: $h"
if [ -z "$h" ]; then
#not valid
_err "Invalid domain"
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}

33
dnsapi/dns_cf.sh

@ -51,9 +51,11 @@ dns_cf_add() {
return 1 return 1
fi fi
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
_debug count "$count"
if [ "$count" = "0" ]; then
# For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so
# we can not use updating anymore.
# count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
# _debug count "$count"
# if [ "$count" = "0" ]; then
_info "Adding record" _info "Adding record"
if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
@ -65,19 +67,20 @@ dns_cf_add() {
fi fi
fi fi
_err "Add txt record error." _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
fi
_err "Update error"
return 1 return 1
fi
# 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
} }

33
dnsapi/dns_cloudns.sh

@ -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() {
return 0 return 0
fi fi
if [ -z "$CLOUDNS_AUTH_ID" ]; then
_err "CLOUDNS_AUTH_ID is not configured"
CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}"
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() {
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() {
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")"

42
dnsapi/dns_cx.sh

@ -36,33 +36,18 @@ dns_cx_add() {
return 1 return 1
fi fi
existing_records "$_domain" "$_sub_domain"
_debug count "$count"
if [ "$?" != "0" ]; then
_err "Error get existing records."
return 1
fi
if [ "$count" = "0" ]; then
add_record "$_domain" "$_sub_domain" "$txtvalue" 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"
if ! [ "$record_id" = "" ]; then
existing_records "$_domain" "$_sub_domain" "$txtvalue"
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() {
_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() {
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() {
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

82
dnsapi/dns_dp.sh

@ -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() {
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 DP_Key "$DP_Key"
_saveaccountconf_mutable DP_Id "$DP_Id"
_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() {
return 1 return 1
fi fi
existing_records "$_domain" "$_sub_domain"
_debug count "$count"
if [ "$?" != "0" ]; then
_err "Error get existing records."
return 1
fi
if [ "$count" = "0" ]; then
add_record "$_domain" "$_sub_domain" "$txtvalue" 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() {
} }
#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() {
return 1 return 1
fi fi
if _contains "$response" "Action completed successful"; then
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
_contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
} }
#################### Private functions below ################################## #################### Private functions below ##################################

97
dnsapi/dns_dreamhost.sh

@ -0,0 +1,97 @@
#!/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"
}

69
dnsapi/dns_duckdns.sh

@ -3,11 +3,14 @@
#Created by RaidenII, to use DuckDNS's API to add/remove text records #Created by RaidenII, to use DuckDNS's API to add/remove text records
#06/27/2017 #06/27/2017
# Currently only support single domain access
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure must be used with acme.sh
# Pass credentials before "acme.sh --issue --dns dns_duckdns ..."
# --
# export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
# --
#
# Due to the fact that DuckDNS uses StartSSL as cert provider, --insecure may need to be used with acme.sh
DuckDNS_API="https://www.duckdns.org/update" DuckDNS_API="https://www.duckdns.org/update"
API_Params="domains=$DuckDNS_Domain&token=$DuckDNS_Token"
######## Public functions ##################### ######## Public functions #####################
@ -16,35 +19,36 @@ dns_duckdns_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
# We'll extract the domain/username from full domain
DuckDNS_Domain=$(echo "$fulldomain" | _lower_case | _egrep_o '.[^.]*.duckdns.org' | cut -d . -f 2)
if [ -z "$DuckDNS_Domain" ]; then
_err "Error extracting the domain."
return 1
fi
DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}"
if [ -z "$DuckDNS_Token" ]; then if [ -z "$DuckDNS_Token" ]; then
DuckDNS_Token=""
_err "You must export variable: DuckDNS_Token"
_err "The token for your DuckDNS account is necessary." _err "The token for your DuckDNS account is necessary."
_err "You can look it up in your DuckDNS account." _err "You can look it up in your DuckDNS account."
return 1 return 1
fi fi
# Now save the credentials. # Now save the credentials.
_saveaccountconf DuckDNS_Domain "$DuckDNS_Domain"
_saveaccountconf DuckDNS_Token "$DuckDNS_Token"
_saveaccountconf_mutable DuckDNS_Token "$DuckDNS_Token"
# Unfortunately, DuckDNS does not seems to support lookup domain through API # Unfortunately, DuckDNS does not seems to support lookup domain through API
# So I assume your credentials (which are your domain and token) are correct # So I assume your credentials (which are your domain and token) are correct
# If something goes wrong, we will get a KO response from DuckDNS # If something goes wrong, we will get a KO response from DuckDNS
if ! _duckdns_get_domain; then
return 1
fi
# Now add the TXT record to DuckDNS # Now add the TXT record to DuckDNS
_info "Trying to add TXT record" _info "Trying to add TXT record"
if _duckdns_rest GET "$API_Params&txt=$txtvalue" && [ "$response" = "OK" ]; then
if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=$txtvalue"; then
if [ "$response" = "OK" ]; then
_info "TXT record has been successfully added to your DuckDNS domain." _info "TXT record has been successfully added to your DuckDNS domain."
_info "Note that all subdomains under this domain uses the same TXT record." _info "Note that all subdomains under this domain uses the same TXT record."
return 0 return 0
else
_err "Errors happened during adding the TXT record, response=$response"
return 1
fi
else else
_err "Errors happened during adding the TXT record." _err "Errors happened during adding the TXT record."
return 1 return 1
@ -57,11 +61,28 @@ dns_duckdns_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
DuckDNS_Token="${DuckDNS_Token:-$(_readaccountconf_mutable DuckDNS_Token)}"
if [ -z "$DuckDNS_Token" ]; then
_err "You must export variable: DuckDNS_Token"
_err "The token for your DuckDNS account is necessary."
_err "You can look it up in your DuckDNS account."
return 1
fi
if ! _duckdns_get_domain; then
return 1
fi
# Now remove the TXT record from DuckDNS # Now remove the TXT record from DuckDNS
_info "Trying to remove TXT record" _info "Trying to remove TXT record"
if _duckdns_rest GET "$API_Params&txt=&clear=true" && [ "$response" = "OK" ]; then
if _duckdns_rest GET "domains=$_duckdns_domain&token=$DuckDNS_Token&txt=&clear=true"; then
if [ "$response" = "OK" ]; then
_info "TXT record has been successfully removed from your DuckDNS domain." _info "TXT record has been successfully removed from your DuckDNS domain."
return 0 return 0
else
_err "Errors happened during removing the TXT record, response=$response"
return 1
fi
else else
_err "Errors happened during removing the TXT record." _err "Errors happened during removing the TXT record."
return 1 return 1
@ -70,6 +91,22 @@ dns_duckdns_rm() {
#################### Private functions below ################################## #################### Private functions below ##################################
#fulldomain=_acme-challenge.domain.duckdns.org
#returns
# _duckdns_domain=domain
_duckdns_get_domain() {
# We'll extract the domain/username from full domain
_duckdns_domain="$(printf "%s" "$fulldomain" | _lower_case | _egrep_o '[.][^.][^.]*[.]duckdns.org' | cut -d . -f 2)"
if [ -z "$_duckdns_domain" ]; then
_err "Error extracting the domain."
return 1
fi
return 0
}
#Usage: method URI #Usage: method URI
_duckdns_rest() { _duckdns_rest() {
method=$1 method=$1

339
dnsapi/dns_dyn.sh

@ -0,0 +1,339 @@
#!/usr/bin/env sh
#
# Dyn.com Domain API
#
# Author: Gerd Naschenweng
# https://github.com/magicdude4eva
#
# Dyn Managed DNS API
# https://help.dyn.com/dns-api-knowledge-base/
#
# It is recommended to add a "Dyn Managed DNS" user specific for API access.
# The "Zones & Records Permissions" required by this script are:
# --
# RecordAdd
# RecordUpdate
# RecordDelete
# RecordGet
# ZoneGet
# ZoneAddNode
# ZoneRemoveNode
# ZonePublish
# --
#
# Pass credentials before "acme.sh --issue --dns dns_dyn ..."
# --
# export DYN_Customer="customer"
# export DYN_Username="apiuser"
# export DYN_Password="secret"
# --
DYN_API="https://api.dynect.net/REST"
#REST_API
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "Challenge-code"
dns_dyn_add() {
fulldomain="$1"
txtvalue="$2"
DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}"
DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}"
DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}"
if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then
DYN_Customer=""
DYN_Username=""
DYN_Password=""
_err "You must export variables: DYN_Customer, DYN_Username and DYN_Password"
return 1
fi
#save the config variables to the account conf file.
_saveaccountconf_mutable DYN_Customer "$DYN_Customer"
_saveaccountconf_mutable DYN_Username "$DYN_Username"
_saveaccountconf_mutable DYN_Password "$DYN_Password"
if ! _dyn_get_authtoken; then
return 1
fi
if [ -z "$_dyn_authtoken" ]; then
_dyn_end_session
return 1
fi
if ! _dyn_get_zone; then
_dyn_end_session
return 1
fi
if ! _dyn_add_record; then
_dyn_end_session
return 1
fi
if ! _dyn_publish_zone; then
_dyn_end_session
return 1
fi
_dyn_end_session
return 0
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_dyn_rm() {
fulldomain="$1"
txtvalue="$2"
DYN_Customer="${DYN_Customer:-$(_readaccountconf_mutable DYN_Customer)}"
DYN_Username="${DYN_Username:-$(_readaccountconf_mutable DYN_Username)}"
DYN_Password="${DYN_Password:-$(_readaccountconf_mutable DYN_Password)}"
if [ -z "$DYN_Customer" ] || [ -z "$DYN_Username" ] || [ -z "$DYN_Password" ]; then
DYN_Customer=""
DYN_Username=""
DYN_Password=""
_err "You must export variables: DYN_Customer, DYN_Username and DYN_Password"
return 1
fi
if ! _dyn_get_authtoken; then
return 1
fi
if [ -z "$_dyn_authtoken" ]; then
_dyn_end_session
return 1
fi
if ! _dyn_get_zone; then
_dyn_end_session
return 1
fi
if ! _dyn_get_record_id; then
_dyn_end_session
return 1
fi
if [ -z "$_dyn_record_id" ]; then
_dyn_end_session
return 1
fi
if ! _dyn_rm_record; then
_dyn_end_session
return 1
fi
if ! _dyn_publish_zone; then
_dyn_end_session
return 1
fi
_dyn_end_session
return 0
}
#################### Private functions below ##################################
#get Auth-Token
_dyn_get_authtoken() {
_info "Start Dyn API Session"
data="{\"customer_name\":\"$DYN_Customer\", \"user_name\":\"$DYN_Username\", \"password\":\"$DYN_Password\"}"
dyn_url="$DYN_API/Session/"
method="POST"
_debug data "$data"
_debug dyn_url "$dyn_url"
export _H1="Content-Type: application/json"
response="$(_post "$data" "$dyn_url" "" "$method")"
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
_debug response "$response"
_debug sessionstatus "$sessionstatus"
if [ "$sessionstatus" = "success" ]; then
_dyn_authtoken="$(printf "%s\n" "$response" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')"
_info "Token received"
_debug _dyn_authtoken "$_dyn_authtoken"
return 0
fi
_dyn_authtoken=""
_err "get token failed"
return 1
}
#fulldomain=_acme-challenge.www.domain.com
#returns
# _dyn_zone=domain.com
_dyn_get_zone() {
i=2
while true; do
domain="$(printf "%s" "$fulldomain" | cut -d . -f "$i-100")"
if [ -z "$domain" ]; then
break
fi
dyn_url="$DYN_API/Zone/$domain/"
export _H1="Auth-Token: $_dyn_authtoken"
export _H2="Content-Type: application/json"
response="$(_get "$dyn_url" "" "")"
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
_debug dyn_url "$dyn_url"
_debug response "$response"
_debug sessionstatus "$sessionstatus"
if [ "$sessionstatus" = "success" ]; then
_dyn_zone="$domain"
return 0
fi
i=$(_math "$i" + 1)
done
_dyn_zone=""
_err "get zone failed"
return 1
}
#add TXT record
_dyn_add_record() {
_info "Adding TXT record"
data="{\"rdata\":{\"txtdata\":\"$txtvalue\"},\"ttl\":\"300\"}"
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/"
method="POST"
export _H1="Auth-Token: $_dyn_authtoken"
export _H2="Content-Type: application/json"
response="$(_post "$data" "$dyn_url" "" "$method")"
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
_debug response "$response"
_debug sessionstatus "$sessionstatus"
if [ "$sessionstatus" = "success" ]; then
_info "TXT Record successfully added"
return 0
fi
_err "add TXT record failed"
return 1
}
#publish the zone
_dyn_publish_zone() {
_info "Publishing zone"
data="{\"publish\":\"true\"}"
dyn_url="$DYN_API/Zone/$_dyn_zone/"
method="PUT"
export _H1="Auth-Token: $_dyn_authtoken"
export _H2="Content-Type: application/json"
response="$(_post "$data" "$dyn_url" "" "$method")"
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
_debug response "$response"
_debug sessionstatus "$sessionstatus"
if [ "$sessionstatus" = "success" ]; then
_info "Zone published"
return 0
fi
_err "publish zone failed"
return 1
}
#get record_id of TXT record so we can delete the record
_dyn_get_record_id() {
_info "Getting record_id of TXT record"
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/"
export _H1="Auth-Token: $_dyn_authtoken"
export _H2="Content-Type: application/json"
response="$(_get "$dyn_url" "" "")"
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
_debug response "$response"
_debug sessionstatus "$sessionstatus"
if [ "$sessionstatus" = "success" ]; then
_dyn_record_id="$(printf "%s\n" "$response" | _egrep_o "\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/[^\"]*" | _head_n 1 | sed "s#^\"data\" *: *\[\"/REST/TXTRecord/$_dyn_zone/$fulldomain/##")"
_debug _dyn_record_id "$_dyn_record_id"
return 0
fi
_dyn_record_id=""
_err "getting record_id failed"
return 1
}
#delete TXT record
_dyn_rm_record() {
_info "Deleting TXT record"
dyn_url="$DYN_API/TXTRecord/$_dyn_zone/$fulldomain/$_dyn_record_id/"
method="DELETE"
_debug dyn_url "$dyn_url"
export _H1="Auth-Token: $_dyn_authtoken"
export _H2="Content-Type: application/json"
response="$(_post "" "$dyn_url" "" "$method")"
sessionstatus="$(printf "%s\n" "$response" | _egrep_o '"status" *: *"[^"]*' | _head_n 1 | sed 's#^"status" *: *"##')"
_debug response "$response"
_debug sessionstatus "$sessionstatus"
if [ "$sessionstatus" = "success" ]; then
_info "TXT record successfully deleted"
return 0
fi
_err "delete TXT record failed"
return 1
}
#logout
_dyn_end_session() {
_info "End Dyn API Session"
dyn_url="$DYN_API/Session/"
method="DELETE"
_debug dyn_url "$dyn_url"
export _H1="Auth-Token: $_dyn_authtoken"
export _H2="Content-Type: application/json"
response="$(_post "" "$dyn_url" "" "$method")"
_debug response "$response"
_dyn_authtoken=""
return 0
}

77
dnsapi/dns_freedns.sh

@ -53,6 +53,8 @@ dns_freedns_add() {
i="$(_math "$i" - 1)" i="$(_math "$i" - 1)"
sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
_debug top_domain "$top_domain"
_debug sub_domain "$sub_domain"
# Sometimes FreeDNS does not return the subdomain page but rather # Sometimes FreeDNS does not return the subdomain page but rather
# returns a page regarding becoming a premium member. This usually # returns a page regarding becoming a premium member. This usually
# happens after a period of inactivity. Immediately trying again # happens after a period of inactivity. Immediately trying again
@ -61,7 +63,6 @@ dns_freedns_add() {
attempts=2 attempts=2
while [ "$attempts" -gt "0" ]; do while [ "$attempts" -gt "0" ]; do
attempts="$(_math "$attempts" - 1)" attempts="$(_math "$attempts" - 1)"
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
if [ "$using_cached_cookies" = "true" ]; then if [ "$using_cached_cookies" = "true" ]; then
@ -70,19 +71,11 @@ dns_freedns_add() {
fi fi
return 1 return 1
fi fi
_debug2 htmlpage "$htmlpage"
subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")"
_debug2 subdomain_csv "$subdomain_csv"
# Now convert the tables in the HTML to CSV. This litte gem from
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
subdomain_csv="$(echo "$htmlpage" \
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
| sed 's/^[\ \t]*//g' \
| tr -d '\n' \
| sed 's/<\/TR[^>]*>/\n/Ig' \
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
| grep 'edit.php?' \
| grep "$top_domain")"
# The above beauty ends with striping out rows that do not have an # The above beauty ends with striping out rows that do not have an
# href to edit.php and do not have the top domain we are looking for. # href to edit.php and do not have the top domain we are looking for.
# So all we should be left with is CSV of table of subdomains we are # So all we should be left with is CSV of table of subdomains we are
@ -90,30 +83,32 @@ dns_freedns_add() {
# Now we have to read through this table and extract the data we need # Now we have to read through this table and extract the data we need
lines="$(echo "$subdomain_csv" | wc -l)" lines="$(echo "$subdomain_csv" | wc -l)"
nl='
'
i=0 i=0
found=0 found=0
while [ "$i" -lt "$lines" ]; do while [ "$i" -lt "$lines" ]; do
i="$(_math "$i" + 1)" i="$(_math "$i" + 1)"
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
tmp="$(echo "$line" | cut -d ',' -f 1)"
if [ $found = 0 ] && _startswith "$tmp" "<td>$top_domain"; then
line="$(echo "$subdomain_csv" | sed -n "${i}p")"
_debug2 line "$line"
if [ $found = 0 ] && _contains "$line" "<td>$top_domain</td>"; then
# this line will contain DNSdomainid for the top_domain # this line will contain DNSdomainid for the top_domain
DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')"
DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)"
_debug2 DNSdomainid "$DNSdomainid"
found=1 found=1
else else
# lines contain DNS records for all subdomains # lines contain DNS records for all subdomains
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
DNStype="$(echo "$line" | cut -d ',' -f 3)"
DNSname="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug2 DNSname "$DNSname"
DNStype="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '4p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug2 DNStype "$DNStype"
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)"
# Now get current value for the TXT record. This method may # Now get current value for the TXT record. This method may
# not produce accurate results as the value field is truncated # not produce accurate results as the value field is truncated
# on this webpage. To get full value we would need to load # on this webpage. To get full value we would need to load
# another page. However we don't really need this so long as # another page. However we don't really need this so long as
# there is only one TXT record for the acme challenge subdomain. # there is only one TXT record for the acme challenge subdomain.
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^&quot;]*&quot;//;s/&quot;.*//;s/<\/td>.*//')"
DNSvalue="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '5p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug2 DNSvalue "$DNSvalue"
if [ $found != 0 ]; then if [ $found != 0 ]; then
break break
# we are breaking out of the loop at the first match of DNS name # we are breaking out of the loop at the first match of DNS name
@ -169,8 +164,7 @@ dns_freedns_add() {
return 0 return 0
else else
# Delete the old TXT record (with the wrong value) # Delete the old TXT record (with the wrong value)
_freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"
if [ "$?" = "0" ]; then
if _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"; then
# And add in new TXT record with the value provided # And add in new TXT record with the value provided
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
fi fi
@ -210,18 +204,9 @@ dns_freedns_rm() {
return 1 return 1
fi fi
# Now convert the tables in the HTML to CSV. This litte gem from
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
subdomain_csv="$(echo "$htmlpage" \
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
| sed 's/^[\ \t]*//g' \
| tr -d '\n' \
| sed 's/<\/TR[^>]*>/\n/Ig' \
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
| grep 'edit.php?' \
| grep "$fulldomain")"
subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")"
_debug2 subdomain_csv "$subdomain_csv"
# The above beauty ends with striping out rows that do not have an # The above beauty ends with striping out rows that do not have an
# href to edit.php and do not have the domain name we are looking for. # href to edit.php and do not have the domain name we are looking for.
# So all we should be left with is CSV of table of subdomains we are # So all we should be left with is CSV of table of subdomains we are
@ -229,19 +214,21 @@ dns_freedns_rm() {
# Now we have to read through this table and extract the data we need # Now we have to read through this table and extract the data we need
lines="$(echo "$subdomain_csv" | wc -l)" lines="$(echo "$subdomain_csv" | wc -l)"
nl='
'
i=0 i=0
found=0 found=0
while [ "$i" -lt "$lines" ]; do while [ "$i" -lt "$lines" ]; do
i="$(_math "$i" + 1)" i="$(_math "$i" + 1)"
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
DNStype="$(echo "$line" | cut -d ',' -f 3)"
line="$(echo "$subdomain_csv" | sed -n "${i}p")"
_debug2 line "$line"
DNSname="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug2 DNSname "$DNSname"
DNStype="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '4p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug2 DNStype "$DNStype"
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^&quot;]*&quot;//;s/&quot;.*//;s/<\/td>.*//')"
_debug "DNSvalue: $DNSvalue"
DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)"
_debug2 DNSdataid "$DNSdataid"
DNSvalue="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '5p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug2 DNSvalue "$DNSvalue"
# if [ "$DNSvalue" = "$txtvalue" ]; then # if [ "$DNSvalue" = "$txtvalue" ]; then
# Testing value match fails. Website is truncating the value # Testing value match fails. Website is truncating the value
# field. So for now we will assume that there is only one TXT # field. So for now we will assume that there is only one TXT

2
dnsapi/dns_gandi_livedns.sh

@ -11,7 +11,7 @@
# #
######## Public functions ##################### ######## Public functions #####################
GANDI_LIVEDNS_API="https://dns.beta.gandi.net/api/v5"
GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5"
#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gandi_livedns_add() { dns_gandi_livedns_add() {

67
dnsapi/dns_gd.sh

@ -15,6 +15,8 @@ dns_gd_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}"
GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}"
if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
GD_Key="" GD_Key=""
GD_Secret="" GD_Secret=""
@ -24,8 +26,8 @@ dns_gd_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 GD_Key "$GD_Key"
_saveaccountconf GD_Secret "$GD_Secret"
_saveaccountconf_mutable GD_Key "$GD_Key"
_saveaccountconf_mutable GD_Secret "$GD_Secret"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
@ -36,8 +38,27 @@ dns_gd_add() {
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
_debug "Getting existing records"
if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then
return 1
fi
if _contains "$response" "$txtvalue"; then
_info "The record is existing, skip"
return 0
fi
_add_data="{\"data\":\"$txtvalue\"}"
for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do
_debug2 t "$t"
if [ "$t" ]; then
_add_data="$_add_data,{\"data\":$t}"
fi
done
_debug2 _add_data "$_add_data"
_info "Adding record" _info "Adding record"
if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then
if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then
if [ "$response" = "{}" ]; then if [ "$response" = "{}" ]; then
_info "Added, sleeping 10 seconds" _info "Added, sleeping 10 seconds"
_sleep 10 _sleep 10
@ -56,7 +77,47 @@ dns_gd_add() {
#fulldomain #fulldomain
dns_gd_rm() { dns_gd_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}"
GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting existing records"
if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then
return 1
fi
if ! _contains "$response" "$txtvalue"; then
_info "The record is not existing, skip"
return 0
fi
_add_data=""
for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do
_debug2 t "$t"
if [ "$t" ] && [ "$t" != "\"$txtvalue\"" ]; then
if [ "$_add_data" ]; then
_add_data="$_add_data,{\"data\":$t}"
else
_add_data="{\"data\":$t}"
fi
fi
done
if [ -z "$_add_data" ]; then
_add_data="{\"data\":\"\"}"
fi
_debug2 _add_data "$_add_data"
_gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"
} }
#################### Private functions below ################################## #################### Private functions below ##################################

158
dnsapi/dns_he.sh

@ -0,0 +1,158 @@
#!/usr/bin/env sh
########################################################################
# Hurricane Electric hook script for acme.sh
#
# Environment variables:
#
# - $HE_Username (your dns.he.net username)
# - $HE_Password (your dns.he.net password)
#
# Author: Ondrej Simek <me@ondrejsimek.com>
# Git repo: https://github.com/angel333/acme.sh
#-- dns_he_add() - Add TXT record --------------------------------------
# Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..."
dns_he_add() {
_full_domain=$1
_txt_value=$2
_info "Using DNS-01 Hurricane Electric hook"
HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}"
HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}"
if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then
HE_Username=
HE_Password=
_err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password envoronment variables."
return 1
fi
_saveaccountconf_mutable HE_Username "$HE_Username"
_saveaccountconf_mutable HE_Password "$HE_Password"
# Fills in the $_zone_id
_find_zone "$_full_domain" || return 1
_debug "Zone id \"$_zone_id\" will be used."
body="email=${HE_Username}&pass=${HE_Password}"
body="$body&account="
body="$body&menu=edit_zone"
body="$body&Type=TXT"
body="$body&hosted_dns_zoneid=$_zone_id"
body="$body&hosted_dns_recordid="
body="$body&hosted_dns_editzone=1"
body="$body&Priority="
body="$body&Name=$_full_domain"
body="$body&Content=$_txt_value"
body="$body&TTL=300"
body="$body&hosted_dns_editrecord=Submit"
response="$(_post "$body" "https://dns.he.net/")"
exit_code="$?"
if [ "$exit_code" -eq 0 ]; then
_info "TXT record added successfully."
else
_err "Couldn't add the TXT record."
fi
_debug2 response "$response"
return "$exit_code"
}
#-- dns_he_rm() - Remove TXT record ------------------------------------
# Usage: dns_he_rm _acme-challenge.subdomain.domain.com "XyZ123..."
dns_he_rm() {
_full_domain=$1
_txt_value=$2
_info "Cleaning up after DNS-01 Hurricane Electric hook"
HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}"
HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}"
# fills in the $_zone_id
_find_zone "$_full_domain" || return 1
_debug "Zone id \"$_zone_id\" will be used."
# Find the record id to clean
body="email=${HE_Username}&pass=${HE_Password}"
body="$body&hosted_dns_zoneid=$_zone_id"
body="$body&menu=edit_zone"
body="$body&hosted_dns_editzone="
response="$(_post "$body" "https://dns.he.net/")"
_debug2 "response" "$response"
if ! _contains "$response" "$_txt_value"; then
_debug "The txt record is not found, just skip"
return 0
fi
_record_id="$(echo "$response" | tr -d "#" | sed "s/<tr/#<tr/g" | tr -d "\n" | tr "#" "\n" | grep "$_full_domain" | grep '"dns_tr"' | grep "$_txt_value" | cut -d '"' -f 4)"
_debug2 _record_id "$_record_id"
if [ -z "$_record_id" ]; then
_err "Can not find record id"
return 1
fi
# Remove the record
body="email=${HE_Username}&pass=${HE_Password}"
body="$body&menu=edit_zone"
body="$body&hosted_dns_zoneid=$_zone_id"
body="$body&hosted_dns_recordid=$_record_id"
body="$body&hosted_dns_editzone=1"
body="$body&hosted_dns_delrecord=1"
body="$body&hosted_dns_delconfirm=delete"
_post "$body" "https://dns.he.net/" \
| grep '<div id="dns_status" onClick="hideThis(this);">Successfully removed record.</div>' \
>/dev/null
exit_code="$?"
if [ "$exit_code" -eq 0 ]; then
_info "Record removed successfully."
else
_err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand."
return "$exit_code"
fi
}
########################## PRIVATE FUNCTIONS ###########################
_find_zone() {
_domain="$1"
body="email=${HE_Username}&pass=${HE_Password}"
response="$(_post "$body" "https://dns.he.net/")"
_debug2 response "$response"
_table="$(echo "$response" | tr -d "#" | sed "s/<table/#<table/g" | tr -d "\n" | tr "#" "\n" | grep 'id="domains_table"')"
_debug2 _table "$_table"
_matches="$(echo "$_table" | sed "s/<tr/#<tr/g" | tr "#" "\n" | grep 'alt="edit"' | tr -d " " | sed "s/<td/#<td/g" | tr "#" "\n" | sed -n 3p)"
_debug2 _matches "$_matches"
# Zone names and zone IDs are in same order
_zone_ids=$(echo "$_matches" | _egrep_o "hosted_dns_zoneid=[0-9]*&" | cut -d = -f 2 | tr -d '&')
_zone_names=$(echo "$_matches" | _egrep_o "name=.*onclick" | cut -d '"' -f 2)
_debug2 "These are the zones on this HE account:"
_debug2 "$_zone_names"
_debug2 "And these are their respective IDs:"
_debug2 "$_zone_ids"
if [ -z "$_zone_names" ] || [ -z "$_zone_ids" ]; then
_err "Can not get zone names."
return 1
fi
# Walk through all possible zone names
_strip_counter=1
while true; do
_attempted_zone=$(echo "$_domain" | cut -d . -f ${_strip_counter}-)
# All possible zone names have been tried
if [ -z "$_attempted_zone" ]; then
_err "No zone for domain \"$_domain\" found."
return 1
fi
_debug "Looking for zone \"${_attempted_zone}\""
line_num="$(echo "$_zone_names" | grep -n "$_attempted_zone" | cut -d : -f 1)"
if [ "$line_num" ]; then
_zone_id=$(echo "$_zone_ids" | sed -n "${line_num}p")
_debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"."
return 0
fi
_debug "Zone \"$_attempted_zone\" doesn't exist, let's try a less specific zone."
_strip_counter=$(_math "$_strip_counter" + 1)
done
}
# vim: et:ts=2:sw=2:

16
dnsapi/dns_infoblox.sh

@ -41,10 +41,10 @@ dns_infoblox_add() {
export _H2="Authorization: Basic $Infoblox_CredsEncoded" export _H2="Authorization: Basic $Infoblox_CredsEncoded"
## Add the challenge record to the Infoblox grid member ## Add the challenge record to the Infoblox grid member
result=$(_post "" "$baseurlnObject" "" "POST")
result="$(_post "" "$baseurlnObject" "" "POST")"
## Let's see if we get something intelligible back from the unit ## Let's see if we get something intelligible back from the unit
if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then
if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then
_info "Successfully created the txt record" _info "Successfully created the txt record"
return 0 return 0
else else
@ -66,7 +66,7 @@ dns_infoblox_rm() {
_debug txtvalue "$txtvalue" _debug txtvalue "$txtvalue"
## Base64 encode the credentials ## Base64 encode the credentials
Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64)
Infoblox_CredsEncoded="$(printf "%b" "$Infoblox_Creds" | _base64)"
## Construct the HTTP Authorization header ## Construct the HTTP Authorization header
export _H1="Accept-Language:en-US" export _H1="Accept-Language:en-US"
@ -74,17 +74,17 @@ dns_infoblox_rm() {
## Does the record exist? Let's check. ## Does the record exist? Let's check.
baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View&_return_type=xml-pretty" baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View&_return_type=xml-pretty"
result=$(_get "$baseurlnObject")
result="$(_get "$baseurlnObject")"
## Let's see if we get something intelligible back from the grid ## Let's see if we get something intelligible back from the grid
if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then
if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then
## Extract the object reference ## Extract the object reference
objRef=$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")
objRef="$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")"
objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef" objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef"
## Delete them! All the stale records! ## Delete them! All the stale records!
rmResult=$(_post "" "$objRmUrl" "" "DELETE")
rmResult="$(_post "" "$objRmUrl" "" "DELETE")"
## Let's see if that worked ## Let's see if that worked
if echo "$rmResult" | egrep "record:txt/.*:.*/$Infoblox_View"; then
if [ "$(echo "$rmResult" | _egrep_o "record:txt/.*:.*/$Infoblox_View")" ]; then
_info "Successfully deleted $objRef" _info "Successfully deleted $objRef"
return 0 return 0
else else

311
dnsapi/dns_inwx.sh

@ -0,0 +1,311 @@
#!/usr/bin/env sh
#
#INWX_User="username"
#
#INWX_Password="password"
INWX_Api="https://api.domrobot.com/xmlrpc/"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_inwx_add() {
fulldomain=$1
txtvalue=$2
INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
INWX_User=""
INWX_Password=""
_err "You don't specify inwx user and password yet."
_err "Please create you key and try again."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable INWX_User "$INWX_User"
_saveaccountconf_mutable INWX_Password "$INWX_Password"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
_inwx_add_record "$_domain" "$_sub_domain" "$txtvalue"
}
#fulldomain txtvalue
dns_inwx_rm() {
fulldomain=$1
txtvalue=$2
INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
INWX_User=""
INWX_Password=""
_err "You don't specify inwx user and password yet."
_err "Please create you key and try again."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable INWX_User "$INWX_User"
_saveaccountconf_mutable INWX_Password "$INWX_Password"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>nameserver.info</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>domain</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>type</name>
<value>
<string>TXT</string>
</value>
</member>
<member>
<name>name</name>
<value>
<string>%s</string>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$_domain" "$_sub_domain")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
if ! _contains "$response" "Command completed successfully"; then
_err "Error could not get txt records"
return 1
fi
if ! printf "%s" "$response" | grep "count" >/dev/null; then
_info "Do not need to delete record"
else
_record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+')
_info "Deleting record"
_inwx_delete_record "$_record_id"
fi
}
#################### Private functions below ##################################
_inwx_login() {
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>account.login</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>user</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>pass</name>
<value>
<string>%s</string>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' $INWX_User $INWX_Password)
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')"
}
_get_root() {
domain=$1
_debug "get root"
domain=$1
i=2
p=1
_H1=$(_inwx_login)
export _H1
xml_content='<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>nameserver.list</methodName>
</methodCall>'
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if _contains "$response" "$h"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_inwx_delete_record() {
record_id=$1
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>nameserver.deleteRecord</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>id</name>
<value>
<int>%s</int>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$record_id")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
_err "Error"
return 1
fi
return 0
}
_inwx_update_record() {
record_id=$1
txtval=$2
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>nameserver.updateRecord</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>content</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>id</name>
<value>
<int>%s</int>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$txtval" "$record_id")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
_err "Error"
return 1
fi
return 0
}
_inwx_add_record() {
domain=$1
sub_domain=$2
txtval=$3
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>nameserver.createRecord</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>domain</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>type</name>
<value>
<string>TXT</string>
</value>
</member>
<member>
<name>content</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>name</name>
<value>
<string>%s</string>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$domain" "$txtval" "$sub_domain")
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
_err "Error"
return 1
fi
return 0
}

1
dnsapi/dns_ispconfig.sh

@ -2,7 +2,6 @@
# ISPConfig 3.1 API # ISPConfig 3.1 API
# User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to: # User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to:
# - DNS zone Functions
# - DNS txt Functions # - DNS txt Functions
# Report bugs to https://github.com/sjau/acme.sh # Report bugs to https://github.com/sjau/acme.sh

4
dnsapi/dns_linode.sh

@ -68,7 +68,7 @@ dns_linode_rm() {
_parameters="&DomainID=$_domain_id" _parameters="&DomainID=$_domain_id"
if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")" resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
if [ "$resource" ]; then if [ "$resource" ]; then
@ -128,7 +128,7 @@ _get_root() {
p=1 p=1
if _rest GET "domain.list"; then if _rest GET "domain.list"; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h" _debug h "$h"

40
dnsapi/dns_lua.sh

@ -8,7 +8,6 @@
#LUA_Email="user@luadns.net" #LUA_Email="user@luadns.net"
LUA_Api="https://api.luadns.com/v1" LUA_Api="https://api.luadns.com/v1"
LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
######## Public functions ##################### ######## Public functions #####################
@ -17,6 +16,10 @@ dns_lua_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then
LUA_Key="" LUA_Key=""
LUA_Email="" LUA_Email=""
@ -26,8 +29,8 @@ dns_lua_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 LUA_Key "$LUA_Key"
_saveaccountconf LUA_Email "$LUA_Email"
_saveaccountconf_mutable LUA_Key "$LUA_Key"
_saveaccountconf_mutable LUA_Email "$LUA_Email"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
@ -38,17 +41,6 @@ dns_lua_add() {
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
_debug "Getting txt records"
_LUA_rest GET "zones/${_domain_id}/records"
if ! _contains "$response" "\"id\":"; then
_err "Error"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Adding record" _info "Adding record"
if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
if _contains "$response" "$fulldomain"; then if _contains "$response" "$fulldomain"; then
@ -60,28 +52,16 @@ dns_lua_add() {
return 1 return 1
fi fi
fi fi
_err "Add txt record error."
else
_info "Updating record"
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
_debug "record_id" "$record_id"
_LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}"
if [ "$?" = "0" ] && _contains "$response" "updated_at"; then
_info "Updated!"
#todo: check if the record takes effect
return 0
fi
_err "Update error"
return 1
fi
} }
#fulldomain #fulldomain
dns_lua_rm() { dns_lua_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
_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"

137
dnsapi/dns_namesilo.sh

@ -0,0 +1,137 @@
#!/usr/bin/env sh
#Author: meowthink
#Created 01/14/2017
#Utilize namesilo.com API to finish dns-01 verifications.
Namesilo_API="https://www.namesilo.com/api"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_namesilo_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$Namesilo_Key" ]; then
Namesilo_Key=""
_err "API token for namesilo.com is missing."
_err "Please specify that in your environment variable."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf Namesilo_Key "$Namesilo_Key"
if ! _get_root "$fulldomain"; then
_err "Unable to find domain specified."
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug txtvalue "$txtvalue"
if _namesilo_rest GET "dnsAddRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrtype=TXT&rrhost=$_sub_domain&rrvalue=$txtvalue"; then
retcode=$(printf "%s\n" "$response" | _egrep_o "<code>300")
if [ "$retcode" ]; then
_info "Successfully added TXT record, ready for validation."
return 0
else
_err "Unable to add the DNS record."
return 1
fi
fi
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_namesilo_rm() {
fulldomain=$1
txtvalue=$2
if ! _get_root "$fulldomain"; then
_err "Unable to find domain specified."
return 1
fi
# Get the record id.
if _namesilo_rest GET "dnsListRecords?version=1&type=xml&key=$Namesilo_Key&domain=$_domain"; then
retcode=$(printf "%s\n" "$response" | _egrep_o "<code>300")
if [ "$retcode" ]; then
_record_id=$(printf "%s\n" "$response" | _egrep_o "<record_id>([^<]*)</record_id><type>TXT</type><host>$fulldomain</host>" | _egrep_o "<record_id>([^<]*)</record_id>" | sed -r "s/<record_id>([^<]*)<\/record_id>/\1/" | tail -n 1)
_debug record_id "$_record_id"
_info "Successfully retrieved the record id for ACME challenge."
else
_err "Unable to retrieve the record id."
return 1
fi
fi
# Remove the DNS record using record id.
if _namesilo_rest GET "dnsDeleteRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrid=$_record_id"; then
retcode=$(printf "%s\n" "$response" | _egrep_o "<code>300")
if [ "$retcode" ]; then
_info "Successfully removed the TXT record."
return 0
else
_err "Unable to remove the DNS record."
return 1
fi
fi
}
#################### Private functions below ##################################
# _acme-challenge.www.domain.com
# returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=2
p=1
if ! _namesilo_rest GET "listDomains?version=1&type=xml&key=$Namesilo_Key"; then
return 1
fi
# Need to exclude the last field (tld)
numfields=$(echo "$domain" | _egrep_o "\." | wc -l)
while [ $i -le "$numfields" ]; do
host=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug host "$host"
if [ -z "$host" ]; then
return 1
fi
if _contains "$response" "$host"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$host"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_namesilo_rest() {
method=$1
param=$2
data=$3
if [ "$method" != "GET" ]; then
response="$(_post "$data" "$Namesilo_API/$param" "" "$method")"
else
response="$(_get "$Namesilo_API/$param")"
fi
if [ "$?" != "0" ]; then
_err "error $param"
return 1
fi
_debug2 response "$response"
return 0
}

6
dnsapi/dns_nsone.sh

@ -59,10 +59,10 @@ dns_nsone_add() {
_err "Add txt record error." _err "Add txt record error."
else else
_info "Updating record" _info "Updating record"
record_id=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain.\",[^{]*\"type\":\"TXT\",\"id\":\"[^,]*\"" | _head_n 1 | cut -d: -f7 | cut -d, -f1)
_debug "record_id" "$record_id"
prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1)
_debug "prev_txt" "$prev_txt"
_nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}"
_nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}"
if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then
_info "Updated!" _info "Updated!"
#todo: check if the record takes effect #todo: check if the record takes effect

107
dnsapi/dns_ovh.sh

@ -78,12 +78,9 @@ _ovh_get_api() {
esac esac
} }
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ovh_add() {
fulldomain=$1
txtvalue=$2
_initAuth() {
OVH_AK="${OVH_AK:-$(_readaccountconf_mutable OVH_AK)}"
OVH_AS="${OVH_AS:-$(_readaccountconf_mutable OVH_AS)}"
if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then
OVH_AK="" OVH_AK=""
@ -93,21 +90,26 @@ dns_ovh_add() {
return 1 return 1
fi fi
#save the api key and email to the account conf file.
_saveaccountconf OVH_AK "$OVH_AK"
_saveaccountconf OVH_AS "$OVH_AS"
if [ "$OVH_AK" != "$(_readaccountconf OVH_AK)" ]; then
_info "It seems that your ovh key is changed, let's clear consumer key first."
_clearaccountconf OVH_CK
fi
_saveaccountconf_mutable OVH_AK "$OVH_AK"
_saveaccountconf_mutable OVH_AS "$OVH_AS"
OVH_END_POINT="${OVH_END_POINT:-$(_readaccountconf_mutable OVH_END_POINT)}"
if [ -z "$OVH_END_POINT" ]; then if [ -z "$OVH_END_POINT" ]; then
OVH_END_POINT="ovh-eu" OVH_END_POINT="ovh-eu"
fi fi
_info "Using OVH endpoint: $OVH_END_POINT" _info "Using OVH endpoint: $OVH_END_POINT"
if [ "$OVH_END_POINT" != "ovh-eu" ]; then if [ "$OVH_END_POINT" != "ovh-eu" ]; then
_saveaccountconf OVH_END_POINT "$OVH_END_POINT"
_saveaccountconf_mutable OVH_END_POINT "$OVH_END_POINT"
fi fi
OVH_API="$(_ovh_get_api $OVH_END_POINT)" OVH_API="$(_ovh_get_api $OVH_END_POINT)"
_debug OVH_API "$OVH_API" _debug OVH_API "$OVH_API"
OVH_CK="${OVH_CK:-$(_readaccountconf_mutable OVH_CK)}"
if [ -z "$OVH_CK" ]; then if [ -z "$OVH_CK" ]; then
_info "OVH consumer key is empty, Let's get one:" _info "OVH consumer key is empty, Let's get one:"
if ! _ovh_authentication; then if ! _ovh_authentication; then
@ -119,14 +121,26 @@ dns_ovh_add() {
_info "Checking authentication" _info "Checking authentication"
response="$(_ovh_rest GET "domain")"
if _contains "$response" "INVALID_CREDENTIAL"; then
if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL"; then
_err "The consumer key is invalid: $OVH_CK" _err "The consumer key is invalid: $OVH_CK"
_err "Please retry to create a new one." _err "Please retry to create a new one."
_clearaccountconf OVH_CK _clearaccountconf OVH_CK
return 1 return 1
fi fi
_info "Consumer key is ok." _info "Consumer key is ok."
return 0
}
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ovh_add() {
fulldomain=$1
txtvalue=$2
if ! _initAuth; then
return 1
fi
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
@ -137,49 +151,58 @@ dns_ovh_add() {
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
_debug "Getting txt records"
_ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"
if _contains "$response" '\[\]' || _contains "$response" "This service does not exist"; then
_info "Adding record" _info "Adding record"
if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then
if _contains "$response" "$txtvalue"; then if _contains "$response" "$txtvalue"; then
_ovh_rest POST "domain/zone/$_domain/refresh" _ovh_rest POST "domain/zone/$_domain/refresh"
_debug "Refresh:$response" _debug "Refresh:$response"
_info "Added, sleeping 10 seconds"
sleep 10
_info "Added, sleep 10 seconds."
_sleep 10
return 0 return 0
fi fi
fi fi
_err "Add txt record error." _err "Add txt record error."
else
_info "Updating record"
record_id=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 1)
if [ -z "$record_id" ]; then
_err "Can not get record id."
return 1 return 1
fi
_debug "record_id" "$record_id"
if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then
if _contains "$response" "null"; then
_ovh_rest POST "domain/zone/$_domain/refresh"
_debug "Refresh:$response"
_info "Updated, sleeping 10 seconds"
sleep 10
return 0
fi
fi
_err "Update error"
return 1
fi
} }
#fulldomain #fulldomain
dns_ovh_rm() { dns_ovh_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
if ! _initAuth; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
if ! _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"; then
return 1
fi
for rid in $(echo "$response" | tr '][,' ' '); do
_debug rid "$rid"
if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then
return 1
fi
if _contains "$response" "\"target\":\"$txtvalue\""; then
_debug "Found txt id:$rid"
if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then
return 1
fi
return 0
fi
done
return 1
} }
#################### Private functions below ################################## #################### Private functions below ##################################
@ -191,7 +214,7 @@ _ovh_authentication() {
_H3="" _H3=""
_H4="" _H4=""
_ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}'
_ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"},{"method": "DELETE","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}'
response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" response="$(_post "$_ovhdata" "$OVH_API/auth/credential")"
_debug3 response "$response" _debug3 response "$response"
@ -279,15 +302,15 @@ _ovh_rest() {
export _H3="X-Ovh-Timestamp: $_ovh_t" export _H3="X-Ovh-Timestamp: $_ovh_t"
export _H4="X-Ovh-Consumer: $OVH_CK" export _H4="X-Ovh-Consumer: $OVH_CK"
export _H5="Content-Type: application/json;charset=utf-8" export _H5="Content-Type: application/json;charset=utf-8"
if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then
if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$_ovh_url" "" "$m")" response="$(_post "$data" "$_ovh_url" "" "$m")"
else else
response="$(_get "$_ovh_url")" response="$(_get "$_ovh_url")"
fi fi
if [ "$?" != "0" ]; then
_err "error $ep"
if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then
_err "error $response"
return 1 return 1
fi fi
_debug2 response "$response" _debug2 response "$response"

161
dnsapi/dns_selectel.sh

@ -0,0 +1,161 @@
#!/usr/bin/env sh
#
#SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
SL_Api="https://api.selectel.ru/domains/v1"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_selectel_add() {
fulldomain=$1
txtvalue=$2
SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
if [ -z "$SL_Key" ]; then
SL_Key=""
_err "You don't specify selectel.ru api key yet."
_err "Please create you key and try again."
return 1
fi
#save the api key to the account conf file.
_saveaccountconf_mutable SL_Key "$SL_Key"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then
if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then
_info "Added, OK"
return 0
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_selectel_rm() {
fulldomain=$1
txtvalue=$2
SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
if [ -z "$SL_Key" ]; then
SL_Key=""
_err "You don't specify slectel api key yet."
_err "Please create you key and try again."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_sl_rest GET "/${_domain_id}/records/"
if ! _contains "$response" "$txtvalue"; then
_err "Txt record not found"
return 1
fi
_record_seg="$(echo "$response" | _egrep_o "\"content\" *: *\"$txtvalue\"[^}]*}")"
_debug2 "_record_seg" "$_record_seg"
if [ -z "$_record_seg" ]; then
_err "can not find _record_seg"
return 1
fi
_record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)"
_debug2 "_record_id" "$_record_id"
if [ -z "$_record_id" ]; then
_err "can not find _record_id"
return 1
fi
if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then
_err "Delete record error."
return 1
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
if ! _sl_rest GET "/"; then
return 1
fi
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if _contains "$response" "\"name\": \"$h\","; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
_debug "Getting domain id for $h"
if ! _sl_rest GET "/$h"; then
return 1
fi
_domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_sl_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="X-Token: $SL_Key"
export _H2="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$SL_Api/$ep" "" "$m")"
else
response="$(_get "$SL_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

170
dnsapi/dns_servercow.sh

@ -0,0 +1,170 @@
#!/usr/bin/env sh
##########
# Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/Neilpang/acme.sh)
#
# Usage:
# export SERVERCOW_API_Username=username
# export SERVERCOW_API_Password=password
# acme.sh --issue -d example.com --dns dns_servercow
#
# Issues:
# Any issues / questions / suggestions can be posted here:
# https://github.com/jhartlep/servercow-dns-api/issues
#
# Author: Jens Hartlep
##########
SERVERCOW_API="https://api.servercow.de/dns/v1/domains"
# Usage dns_servercow_add _acme-challenge.www.domain.com "abcdefghijklmnopqrstuvwxyz"
dns_servercow_add() {
fulldomain=$1
txtvalue=$2
_info "Using servercow"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}"
SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}"
if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then
SERVERCOW_API_Username=""
SERVERCOW_API_Password=""
_err "You don't specify servercow api username and password yet."
_err "Please create your username and password and try again."
return 1
fi
# save the credentials to the account conf file
_saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username"
_saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then
if printf -- "%s" "$response" | grep "ok" >/dev/null; then
_info "Added, OK"
return 0
else
_err "add txt record error."
return 1
fi
fi
_err "add txt record error."
return 1
}
# Usage fulldomain txtvalue
# Remove the txt record after validation
dns_servercow_rm() {
fulldomain=$1
txtvalue=$2
_info "Using servercow"
_debug fulldomain "$fulldomain"
_debug txtvalue "$fulldomain"
SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}"
SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}"
if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then
SERVERCOW_API_Username=""
SERVERCOW_API_Password=""
_err "You don't specify servercow api username and password yet."
_err "Please create your username and password and try again."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then
if printf -- "%s" "$response" | grep "ok" >/dev/null; then
_info "Deleted, OK"
_contains "$response" '"message":"ok"'
else
_err "delete txt record error."
return 1
fi
fi
}
#################### Private functions below ##################################
# _acme-challenge.www.domain.com
# returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
fulldomain=$1
i=2
p=1
while true; do
_domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
_debug _domain "$_domain"
if [ -z "$_domain" ]; then
# not valid
return 1
fi
if ! _servercow_api GET "$_domain"; then
return 1
fi
if ! _contains "$response" '"error":"no such domain in user context"' >/dev/null; then
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
if [ -z "$_sub_domain" ]; then
# not valid
return 1
fi
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_servercow_api() {
method=$1
domain=$2
data="$3"
export _H1="Content-Type: application/json"
export _H2="X-Auth-Username: $SERVERCOW_API_Username"
export _H3="X-Auth-Password: $SERVERCOW_API_Password"
if [ "$method" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")"
else
response="$(_get "$SERVERCOW_API/$domain")"
fi
if [ "$?" != "0" ]; then
_err "error $domain"
return 1
fi
_debug2 response "$response"
return 0
}

202
dnsapi/dns_unoeuro.sh

@ -0,0 +1,202 @@
#!/usr/bin/env sh
#
#UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
#UNO_User="UExxxxxx"
Uno_Api="https://api.unoeuro.com/1"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_unoeuro_add() {
fulldomain=$1
txtvalue=$2
UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}"
UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}"
if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then
UNO_Key=""
UNO_User=""
_err "You haven't specified a UnoEuro api key and account yet."
_err "Please create your key and try again."
return 1
fi
if ! _contains "$UNO_User" "UE"; then
_err "It seems that the UNO_User=$UNO_User is not a valid username."
_err "Please check and retry."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable UNO_Key "$UNO_Key"
_saveaccountconf_mutable UNO_User "$UNO_User"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_uno_rest GET "my/products/$h/dns/records"
if ! _contains "$response" "\"status\": 200" >/dev/null; then
_err "Error"
return 1
fi
if ! _contains "$response" "$_sub_domain" >/dev/null; then
_info "Adding record"
if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then
if _contains "$response" "\"status\": 200" >/dev/null; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
else
_info "Updating record"
record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1)
record_line_number=$(_math "$record_line_number" - 1)
record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}")
_debug "record_id" "$record_id"
_uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"
if _contains "$response" "\"status\": 200" >/dev/null; then
_info "Updated, OK"
return 0
fi
_err "Update error"
return 1
fi
}
#fulldomain txtvalue
dns_unoeuro_rm() {
fulldomain=$1
txtvalue=$2
UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}"
UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}"
if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then
UNO_Key=""
UNO_User=""
_err "You haven't specified a UnoEuro api key and account yet."
_err "Please create your key and try again."
return 1
fi
if ! _contains "$UNO_User" "UE"; then
_err "It seems that the UNO_User=$UNO_User is not a valid username."
_err "Please check and retry."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_uno_rest GET "my/products/$h/dns/records"
if ! _contains "$response" "\"status\": 200"; then
_err "Error"
return 1
fi
if ! _contains "$response" "$_sub_domain"; then
_info "Don't need to remove."
else
record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1)
record_line_number=$(_math "$record_line_number" - 1)
record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}")
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" "\"status\": 200"
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
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
if ! _uno_rest GET "my/products/$h/dns/records"; then
return 1
fi
if _contains "$response" "\"status\": 200"; then
_domain_id=$h
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
}
_uno_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$Uno_Api/$UNO_User/$UNO_Key/$ep" "" "$m")"
else
response="$(_get "$Uno_Api/$UNO_User/$UNO_Key/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

106
dnsapi/dns_yandex.sh

@ -0,0 +1,106 @@
#!/usr/bin/env sh
# Author: non7top@gmail.com
# 07 Jul 2017
# report bugs at https://github.com/non7top/acme.sh
# Values to export:
# export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_yandex_add() {
fulldomain="${1}"
txtvalue="${2}"
_debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'"
_PDD_credentials || return 1
export _H1="PddToken: $PDD_Token"
_PDD_get_domain "$fulldomain"
_debug "Found suitable domain in pdd: $curDomain"
curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}"
curUri="https://pddimp.yandex.ru/api2/admin/dns/add"
curResult="$(_post "${curData}" "${curUri}")"
_debug "Result: $curResult"
}
#Usage: dns_myapi_rm _acme-challenge.www.domain.com
dns_yandex_rm() {
fulldomain="${1}"
_debug "Calling: dns_yandex_rm() '${fulldomain}'"
_PDD_credentials || return 1
export _H1="PddToken: $PDD_Token"
record_id=$(pdd_get_record_id "${fulldomain}")
_debug "Result: $record_id"
_PDD_get_domain "$fulldomain"
_debug "Found suitable domain in pdd: $curDomain"
curUri="https://pddimp.yandex.ru/api2/admin/dns/del"
curData="domain=${curDomain}&record_id=${record_id}"
curResult="$(_post "${curData}" "${curUri}")"
_debug "Result: $curResult"
}
#################### Private functions below ##################################
_PDD_get_domain() {
fulldomain="${1}"
__page=1
__last=0
while [ $__last -eq 0 ]; do
uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20"
res1=$(_get "$uri1" | _normalizeJson)
#_debug "$res1"
__found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p')
_debug "found: $__found results on page"
if [ "$__found" -lt 20 ]; then
_debug "last page: $__page"
__last=1
fi
__all_domains="$__all_domains $(echo "$res1" | tr "," "\n" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')"
__page=$(_math $__page + 1)
done
k=2
while [ $k -lt 10 ]; do
__t=$(echo "$fulldomain" | cut -d . -f $k-100)
_debug "finding zone for domain $__t"
for d in $__all_domains; do
if [ "$d" = "$__t" ]; then
p=$(_math $k - 1)
curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")"
curDomain="$__t"
return 0
fi
done
k=$(_math $k + 1)
done
_err "No suitable domain found in your account"
return 1
}
_PDD_credentials() {
if [ -z "${PDD_Token}" ]; then
PDD_Token=""
_err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx"
_err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token"
return 1
else
_saveaccountconf PDD_Token "${PDD_Token}"
fi
}
pdd_get_record_id() {
fulldomain="${1}"
_PDD_get_domain "$fulldomain"
_debug "Found suitable domain in pdd: $curDomain"
curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}"
curResult="$(_get "${curUri}" | _normalizeJson)"
_debug "Result: $curResult"
echo "$curResult" | _egrep_o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p'
}

85
dnsapi/dns_zonomi.sh

@ -0,0 +1,85 @@
#!/usr/bin/env sh
#
#ZM_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
#https://zonomi.com dns api
ZM_Api="https://zonomi.com/app/dns/dyndns.jsp"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_zonomi_add() {
fulldomain=$1
txtvalue=$2
ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}"
if [ -z "$ZM_Key" ]; then
ZM_Key=""
_err "You don't specify zonomi api key yet."
_err "Please create your key and try again."
return 1
fi
#save the api key to the account conf file.
_saveaccountconf_mutable ZM_Key "$ZM_Key"
_info "Get existing txt records for $fulldomain"
if ! _zm_request "action=QUERY&name=$fulldomain"; then
_err "error"
return 1
fi
if _contains "$response" "<record"; then
_debug "get and update records"
_qstr="action[1]=SET&type[1]=TXT&name[1]=$fulldomain&value[1]=$txtvalue"
_qindex=2
for t in $(echo "$response" | tr -d "\r\n" | _egrep_o '<action.*</action>' | tr "<" "\n" | grep record | grep 'type="TXT"' | cut -d '"' -f 6); do
_debug2 t "$t"
_qstr="$_qstr&action[$_qindex]=SET&type[$_qindex]=TXT&name[$_qindex]=$fulldomain&value[$_qindex]=$t"
_qindex="$(_math "$_qindex" + 1)"
done
_zm_request "$_qstr"
else
_debug "Just add record"
_zm_request "action=SET&type=TXT&name=$fulldomain&value=$txtvalue"
fi
}
#fulldomain txtvalue
dns_zonomi_rm() {
fulldomain=$1
txtvalue=$2
ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}"
if [ -z "$ZM_Key" ]; then
ZM_Key=""
_err "You don't specify zonomi api key yet."
_err "Please create your key and try again."
return 1
fi
_zm_request "action=DELETE&type=TXT&name=$fulldomain"
}
#################### Private functions below ##################################
#qstr
_zm_request() {
qstr="$1"
_debug2 "qstr" "$qstr"
_zm_url="$ZM_Api?api_key=$ZM_Key&$qstr"
_debug2 "_zm_url" "$_zm_url"
response="$(_get "$_zm_url")"
if [ "$?" != "0" ]; then
return 1
fi
_debug2 response "$response"
_contains "$response" "<is_ok>OK:"
}
Loading…
Cancel
Save