Merge remote-tracking branch 'upstream/dev' into acmedns

This commit is contained in:
Sven Neubauer 2019-04-03 21:39:43 +02:00
commit 877748acd7
58 changed files with 6125 additions and 1655 deletions

View File

@ -1,11 +1,13 @@
FROM alpine:3.6 FROM alpine:3.9
RUN apk update -f \ RUN apk update -f \
&& apk --no-cache add -f \ && apk --no-cache add -f \
openssl \ openssl \
coreutils \ coreutils \
bind-tools \
curl \ curl \
socat \ socat \
tzdata \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
ENV LE_CONFIG_HOME /acme.sh ENV LE_CONFIG_HOME /acme.sh

117
README.md
View File

@ -33,9 +33,9 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt) - [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty) - [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) - [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
- [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html) - [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
- [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) - [archlinux](https://www.archlinux.org/packages/community/any/acme.sh)
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) - [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
- [CentOS Web Panel](http://centos-webpanel.com/) - [CentOS Web Panel](http://centos-webpanel.com/)
- [lnmp.org](https://lnmp.org/) - [lnmp.org](https://lnmp.org/)
@ -70,11 +70,17 @@ For all build statuses, check our [weekly build project](https://github.com/Neil
https://github.com/Neilpang/acmetest https://github.com/Neilpang/acmetest
# Supported CA
- Letsencrypt.org CA(default)
- [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA)
- [Pebble strict Mode](https://github.com/letsencrypt/pebble)
# Supported modes # Supported modes
- Webroot mode - Webroot mode
- Standalone mode - Standalone mode
- Standalone tls-alpn mode
- Apache mode - Apache mode
- Nginx mode - Nginx mode
- DNS mode - DNS mode
@ -221,8 +227,20 @@ acme.sh --issue --standalone -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 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 5. Use Standalone ssl server to issue cert
# 5. Use Apache mode **(requires you to be root/sudoer or have permission to listen on port 443 (TCP))**
Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
```bash
acme.sh --issue --alpn -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
# 6. Use Apache mode
**(requires you to be root/sudoer, since it is required to interact with Apache server)** **(requires you to be root/sudoer, since it is required to interact with Apache server)**
@ -236,13 +254,13 @@ Just set string "apache" as the second argument and it will force use of apache
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. **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. 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.** 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
# 6. Use Nginx mode # 7. Use Nginx mode
**(requires you to be root/sudoer, since it is required to interact with Nginx server)** **(requires you to be root/sudoer, since it is required to interact with Nginx server)**
@ -260,80 +278,23 @@ So, the config is not changed.
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
``` ```
**This nginx mode is only to issue the cert, it will not change your nginx config files. **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. 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.** We don't want to mess your nginx 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. 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.
You don't have to do anything manually! You don't have to do anything manually!
### Currently acme.sh supports: ### Currently acme.sh supports most of the dns providers:
1. CloudFlare.com API https://github.com/Neilpang/acme.sh/wiki/dnsapi
1. DNSPod.cn API
1. CloudXNS.com API
1. GoDaddy.com API
1. PowerDNS.com API
1. OVH, kimsufi, soyoustart and runabove API
1. nsupdate API
1. LuaDNS.com API
1. DNSMadeEasy.com API
1. AWS Route 53
1. aliyun.com(阿里云) API
1. ISPConfig 3.1 API
1. Alwaysdata.com API
1. Linode.com API
1. FreeDNS (https://freedns.afraid.org/)
1. cyon.ch
1. Domain-Offensive/Resellerinterface/Domainrobot API
1. Gandi LiveDNS API
1. Knot DNS API
1. DigitalOcean API (native)
1. ClouDNS.net API
1. Infoblox NIOS API (https://www.infoblox.com/)
1. VSCALE (https://vscale.io/)
1. Dynu API (https://www.dynu.com)
1. DNSimple API
1. NS1.com API
1. DuckDNS.org 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
1. DirectAdmin API
1. KingHost (https://www.kinghost.com.br/)
1. Zilore (https://zilore.com)
1. Loopia.se API
1. acme-dns (https://github.com/joohoi/acme-dns)
1. TELE3 (https://www.tele3.cz)
And: # 9. Use DNS manual mode:
**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...**
If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
For more details: [How to use DNS API](dnsapi)
# 8. Use DNS manual mode:
See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first. See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first.
@ -369,7 +330,7 @@ Ok, it's done.
**Please use dns api mode instead.** **Please use dns api mode instead.**
# 9. Issue ECC certificates # 10. Issue ECC certificates
`Let's Encrypt` can now issue **ECDSA** certificates. `Let's Encrypt` can now issue **ECDSA** certificates.
@ -401,7 +362,7 @@ Valid values are:
# 10. Issue Wildcard certificates # 11. Issue Wildcard certificates
It's simple, just give a wildcard domain as the `-d` parameter. It's simple, just give a wildcard domain as the `-d` parameter.
@ -411,7 +372,7 @@ acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf
# 11. How to renew the certs # 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. No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
@ -428,7 +389,7 @@ acme.sh --renew -d example.com --force --ecc
``` ```
# 12. How to stop cert renewal # 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: To stop renewal of a cert, you can execute the following to remove the cert from the renewal list:
@ -441,7 +402,7 @@ The cert/key file is not removed from the disk.
You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself. You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself.
# 13. How to upgrade `acme.sh` # 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.
@ -466,25 +427,25 @@ acme.sh --upgrade --auto-upgrade 0
``` ```
# 14. 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
# 15. 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:
# 16. 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
# 17. License & Others # 18. License & Others
License is GPLv3 License is GPLv3
@ -493,9 +454,9 @@ 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.
# 18. 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/)
[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) [Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)

753
acme.sh

File diff suppressed because it is too large Load Diff

View File

@ -1,257 +1,6 @@
# Using deploy api # Using deploy 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). deploy hook usage:
Here are the scripts to deploy the certs/key to the server/services. https://github.com/Neilpang/acme.sh/wiki/deployhooks
## 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.)
Then you can deploy now:
```sh
export DEPLOY_CPANEL_USER=myusername
export DEPLOY_CPANEL_PASSWORD=PASSWORD
acme.sh --deploy -d example.com --deploy-hook cpanel
```
## 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).
Currently supports Kong-v0.10.x.
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook kong
```
## 3. Deploy the cert to remote server through SSH access
The ssh deploy plugin allows you to deploy certificates to a remote host
using SSH command to connect to the remote server. The ssh plugin is invoked
with the following command...
```sh
acme.sh --deploy -d example.com --deploy-hook ssh
```
Prior to running this for the first time you must tell the plugin where
and how to deploy the certificates. This is done by exporting the following
environment variables. This is not required for subsequent runs as the
values are stored by acme.sh in the domain configuration files.
Required...
```
export DEPLOY_SSH_USER=username
```
Optional...
```
export DEPLOY_SSH_CMD=custom ssh command
export DEPLOY_SSH_SERVER=url or ip address of remote host
export DEPLOY_SSH_KEYFILE=filename for private key
export DEPLOY_SSH_CERTFILE=filename for certificate file
export DEPLOY_SSH_CAFILE=filename for intermediate CA file
export DEPLOY_SSH_FULLCHAIN=filename for fullchain file
export DEPLOY_SSH_REMOTE_CMD=command to execute on remote host
export DEPLOY_SSH_BACKUP=yes or no
```
**DEPLOY_SSH_USER**
Username at the remote host that SSH will login with. Note that
SSH must be able to login to remote host without a password... SSH Keys
must have been exchanged with the remote host. Validate and test that you
can login to USER@URL from the host running acme.sh before using this script.
The USER@URL at the remote server must also have has permissions to write to
the target location of the certificate files and to execute any commands
(e.g. to stop/start services).
**DEPLOY_SSH_CMD**
You can customize the ssh command used to connect to the remote host. For example
if you need to connect to a specific port at the remote server you can set this
to, for example, "ssh -p 22" or to use `sshpass` to provide password inline
instead of exchanging ssh keys (this is not recommended, using keys is
more secure).
**DEPLOY_SSH_SERVER**
URL or IP Address of the remote server. If not provided then the domain
name provided on the acme.sh --deploy command line is used.
**DEPLOY_SSH_KEYFILE**
Target filename for the private key issued by LetsEncrypt.
**DEPLOY_SSH_CERTFILE**
Target filename for the certificate issued by LetsEncrypt.
If this is the same as the previous filename (for keyfile) then it is
appended to the same file.
**DEPLOY_SSH_CAFILE**
Target filename for the CA intermediate certificate issued by LetsEncrypt.
If this is the same as a previous filename (for keyfile or certfile) then
it is appended to the same file.
**DEPLOY_SSH_FULLCHAIN**
Target filename for the fullchain certificate issued by LetsEncrypt.
If this is the same as a previous filename (for keyfile, certfile or
cafile) then it is appended to the same file.
**DEPLOY_SSH_REMOTE_CMD**
Command to execute on the remote server after copying any certificates. This
could be any additional command required for example to stop and restart
the service.
**DEPLOY_SSH_BACKUP**
Before writing a certificate file to the remote server the existing
certificate will be copied to a backup directory on the remote server.
These are placed in a hidden directory in the home directory of the SSH
user
```sh
~/.acme_ssh_deploy/[domain name]-backup-[timestamp]
```
Any backups older than 180 days will be deleted when new certificates
are deployed. This defaults to "yes" set to "no" to disable backup.
###Examples using SSH deploy
The following example illustrates deploying certificates to a QNAP NAS
(tested with QTS version 4.2.3)
```sh
export DEPLOY_SSH_USER="admin"
export DEPLOY_SSH_KEYFILE="/etc/stunnel/stunnel.pem"
export DEPLOY_SSH_CERTFILE="/etc/stunnel/stunnel.pem"
export DEPLOY_SSH_CAFILE="/etc/stunnel/uca.pem"
export DEPLOY_SSH_REMOTE_CMD="/etc/init.d/stunnel.sh restart"
acme.sh --deploy -d qnap.example.com --deploy-hook ssh
```
Note how in this example both the private key and certificate point to
the same file. This will result in the certificate being appended
to the same file as the private key... a common requirement of several
services.
The next example illustrates deploying certificates to a Unifi
Controller (tested with version 5.4.11).
```sh
export DEPLOY_SSH_USER="root"
export DEPLOY_SSH_KEYFILE="/var/lib/unifi/unifi.example.com.key"
export DEPLOY_SSH_FULLCHAIN="/var/lib/unifi/unifi.example.com.cer"
export DEPLOY_SSH_REMOTE_CMD="openssl pkcs12 -export \
-inkey /var/lib/unifi/unifi.example.com.key \
-in /var/lib/unifi/unifi.example.com.cer \
-out /var/lib/unifi/unifi.example.com.p12 \
-name ubnt -password pass:temppass \
&& keytool -importkeystore -deststorepass aircontrolenterprise \
-destkeypass aircontrolenterprise \
-destkeystore /var/lib/unifi/keystore \
-srckeystore /var/lib/unifi/unifi.example.com.p12 \
-srcstoretype PKCS12 -srcstorepass temppass -alias ubnt -noprompt \
&& service unifi restart"
acme.sh --deploy -d unifi.example.com --deploy-hook ssh
```
In this example we execute several commands on the remote host
after the certificate files have been copied... to generate a pkcs12 file
compatible with Unifi, to import it into the Unifi keystore and then finally
to restart the service.
Note also that once the certificate is imported
into the keystore the individual certificate files are no longer
required. We could if we desired delete those files immediately. If we
do that then we should disable backup at the remote host (as there are
no files to backup -- they were erased during deployment). For example...
```sh
export DEPLOY_SSH_BACKUP=no
# modify the end of the remote command...
&& rm /var/lib/unifi/unifi.example.com.key \
/var/lib/unifi/unifi.example.com.cer \
/var/lib/unifi/unifi.example.com.p12 \
&& service unifi restart
```
## 4. Deploy the cert to local vsftpd server
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
```
The default vsftpd conf file is `/etc/vsftpd.conf`, if your vsftpd conf is not in the default location, you can specify one:
```sh
export DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
```
The default command to restart vsftpd server is `service vsftpd restart`, if it doesn't work, you can specify one:
```sh
export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart"
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
```
## 5. Deploy the cert to local exim4 server
```sh
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```
The default exim4 conf file is `/etc/exim/exim.conf`, if your exim4 conf is not in the default location, you can specify one:
```sh
export DEPLOY_EXIM4_CONF="/etc/exim4/exim4.conf.template"
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```
The default command to restart exim4 server is `service exim4 restart`, if it doesn't work, you can specify one:
```sh
export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart"
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
```
## 6. Deploy the cert to OSX Keychain
```sh
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 separate 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
```

View File

@ -2,8 +2,12 @@
# Here is the script to deploy the cert to your cpanel using the cpanel API. # 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. # Uses command line uapi. --user option is needed only if run as root.
# Returns 0 when success. # Returns 0 when success.
# Written by Santeri Kannisto <santeri.kannisto@2globalnomads.info> #
# Public domain, 2017 # Please note that I am no longer using Github. If you want to report an issue
# or contact me, visit https://forum.webseodesigners.com/web-design-seo-and-hosting-f16/
#
# Written by Santeri Kannisto <santeri.kannisto@webseodesigners.com>
# Public domain, 2017-2018
#export DEPLOY_CPANEL_USER=myusername #export DEPLOY_CPANEL_USER=myusername
@ -28,15 +32,9 @@ cpanel_uapi_deploy() {
_err "The command uapi is not found." _err "The command uapi is not found."
return 1 return 1
fi fi
if ! _exists php; then
_err "The command php is not found."
return 1
fi
# read cert and key files and urlencode both # read cert and key files and urlencode both
_certstr=$(cat "$_ccert") _cert=$(_url_encode <"$_ccert")
_keystr=$(cat "$_ckey") _key=$(_url_encode <"$_ckey")
_cert=$(php -r "echo urlencode(\"$_certstr\");")
_key=$(php -r "echo urlencode(\"$_keystr\");")
_debug _cert "$_cert" _debug _cert "$_cert"
_debug _key "$_key" _debug _key "$_key"

View File

@ -28,8 +28,10 @@ fritzbox_deploy() {
_debug _cfullchain "$_cfullchain" _debug _cfullchain "$_cfullchain"
if ! _exists iconv; then if ! _exists iconv; then
_err "iconv not found" if ! _exists perl; then
return 1 _err "iconv or perl not found"
return 1
fi
fi fi
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}" _fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}"
@ -61,7 +63,11 @@ fritzbox_deploy() {
_info "Log in to the FRITZ!Box" _info "Log in to the FRITZ!Box"
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')" _fritzbox_challenge="$(_get "${_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}')" if _exists iconv; then
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
else
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | perl -p -e 'use Encode qw/encode/; print encode("UTF-16LE","$_"); $_="";' | md5sum | awk '{print $1}')"
fi
_fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')" _fritzbox_sid="$(_get "${_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 if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then

140
deploy/gcore_cdn.sh Normal file
View File

@ -0,0 +1,140 @@
#!/usr/bin/env sh
# Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/).
# Uses command line curl for send requests and jq for parse responses.
# Returns 0 when success.
#
# Written by temoffey <temofffey@gmail.com>
# Public domain, 2019
#export DEPLOY_GCORE_CDN_USERNAME=myusername
#export DEPLOY_GCORE_CDN_PASSWORD=mypassword
######## Public functions #####################
#domain keyfile certfile cafile fullchain
gcore_cdn_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"
_fullchain=$(tr '\n\r' '@#' <"$_cfullchain" | sed 's/@/\\n/g;s/#/\\r/g')
_key=$(tr '\n\r' '@#' <"$_ckey" | sed 's/@/\\n/g;s/#/\\r/g')
_debug _fullchain "$_fullchain"
_debug _key "$_key"
if [ -z "$DEPLOY_GCORE_CDN_USERNAME" ]; then
if [ -z "$Le_Deploy_gcore_cdn_username" ]; then
_err "Please define the target username: export DEPLOY_GCORE_CDN_USERNAME=username"
return 1
fi
else
Le_Deploy_gcore_cdn_username="$DEPLOY_GCORE_CDN_USERNAME"
_savedomainconf Le_Deploy_gcore_cdn_username "$Le_Deploy_gcore_cdn_username"
fi
if [ -z "$DEPLOY_GCORE_CDN_PASSWORD" ]; then
if [ -z "$Le_Deploy_gcore_cdn_password" ]; then
_err "Please define the target password: export DEPLOY_GCORE_CDN_PASSWORD=password"
return 1
fi
else
Le_Deploy_gcore_cdn_password="$DEPLOY_GCORE_CDN_PASSWORD"
_savedomainconf Le_Deploy_gcore_cdn_password "$Le_Deploy_gcore_cdn_password"
fi
_info "Get authorization token"
_request="{\"username\":\"$Le_Deploy_gcore_cdn_username\",\"password\":\"$Le_Deploy_gcore_cdn_password\"}"
_debug _request "$_request"
export _H1="Content-Type:application/json"
_response=$(_post "$_request" "https://api.gcdn.co/auth/signin")
_debug _response "$_response"
_regex=".*\"token\":\"\([-._0-9A-Za-z]*\)\".*$"
_debug _regex "$_regex"
_token=$(echo "$_response" | sed -n "s/$_regex/\1/p")
_debug _token "$_token"
if [ -z "$_token" ]; then
_err "Error G-Core Labs API authorization"
return 1
fi
_info "Find CDN resource with cname $_cdomain"
export _H2="Authorization:Token $_token"
_response=$(_get "https://api.gcdn.co/resources")
_debug _response "$_response"
_regex=".*(\"id\".*?\"cname\":\"$_cdomain\".*?})"
_regex="^.*\"cname\":\"$_cdomain\".*$"
_debug _regex "$_regex"
_resource=$(echo "$_response" | sed 's/},{/},\n{/g' | _egrep_o "$_regex")
_debug _resource "$_resource"
_regex=".*\"id\":\([0-9]*\),.*$"
_debug _regex "$_regex"
_resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
_debug _resourceId "$_resourceId"
_regex=".*\"sslData\":\([0-9]*\)}.*$"
_debug _regex "$_regex"
_sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
_debug _sslDataOld "$_sslDataOld"
_regex=".*\"originGroup\":\([0-9]*\),.*$"
_debug _regex "$_regex"
_originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p")
_debug _originGroup "$_originGroup"
if [ -z "$_resourceId" ] || [ -z "$_originGroup" ]; then
_err "Not found CDN resource with cname $_cdomain"
return 1
fi
_info "Add new SSL certificate"
_date=$(date "+%d.%m.%Y %H:%M:%S")
_request="{\"name\":\"$_cdomain ($_date)\",\"sslCertificate\":\"$_fullchain\",\"sslPrivateKey\":\"$_key\"}"
_debug _request "$_request"
_response=$(_post "$_request" "https://api.gcdn.co/sslData")
_debug _response "$_response"
_regex=".*\"id\":\([0-9]*\),.*$"
_debug _regex "$_regex"
_sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p")
_debug _sslDataAdd "$_sslDataAdd"
if [ -z "$_sslDataAdd" ]; then
_err "Error new SSL certificate add"
return 1
fi
_info "Update CDN resource"
_request="{\"originGroup\":$_originGroup,\"sslData\":$_sslDataAdd}"
_debug _request "$_request"
_response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT")
_debug _response "$_response"
_regex=".*\"sslData\":\([0-9]*\)}.*$"
_debug _regex "$_regex"
_sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p")
_debug _sslDataNew "$_sslDataNew"
if [ "$_sslDataNew" != "$_sslDataAdd" ]; then
_err "Error CDN resource update"
return 1
fi
if [ -z "$_sslDataOld" ] || [ "$_sslDataOld" = "null" ]; then
_info "Not found old SSL certificate"
else
_info "Delete old SSL certificate"
_response=$(_post '' "https://api.gcdn.co/sslData/$_sslDataOld" '' "DELETE")
_debug _response "$_response"
fi
_info "Certificate successfully deployed"
return 0
}

80
deploy/gitlab.sh Normal file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env sh
# Script to deploy certificate to a Gitlab hosted page
# The following variables exported from environment will be used.
# If not set then values previously saved in domain.conf file are used.
# All the variables are required
# export GITLAB_TOKEN="xxxxxxx"
# export GITLAB_PROJECT_ID=012345
# export GITLAB_DOMAIN="mydomain.com"
gitlab_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 [ -z "$GITLAB_TOKEN" ]; then
if [ -z "$Le_Deploy_gitlab_token" ]; then
_err "GITLAB_TOKEN not defined."
return 1
fi
else
Le_Deploy_gitlab_token="$GITLAB_TOKEN"
_savedomainconf Le_Deploy_gitlab_token "$Le_Deploy_gitlab_token"
fi
if [ -z "$GITLAB_PROJECT_ID" ]; then
if [ -z "$Le_Deploy_gitlab_project_id" ]; then
_err "GITLAB_PROJECT_ID not defined."
return 1
fi
else
Le_Deploy_gitlab_project_id="$GITLAB_PROJECT_ID"
_savedomainconf Le_Deploy_gitlab_project_id "$Le_Deploy_gitlab_project_id"
fi
if [ -z "$GITLAB_DOMAIN" ]; then
if [ -z "$Le_Deploy_gitlab_domain" ]; then
_err "GITLAB_DOMAIN not defined."
return 1
fi
else
Le_Deploy_gitlab_domain="$GITLAB_DOMAIN"
_savedomainconf Le_Deploy_gitlab_domain "$Le_Deploy_gitlab_domain"
fi
string_fullchain=$(_url_encode <"$_cfullchain")
string_key=$(_url_encode <"$_ckey")
body="certificate=$string_fullchain&key=$string_key"
export _H1="PRIVATE-TOKEN: $Le_Deploy_gitlab_token"
gitlab_url="https://gitlab.com/api/v4/projects/$Le_Deploy_gitlab_project_id/pages/domains/$Le_Deploy_gitlab_domain"
_response=$(_post "$body" "$gitlab_url" 0 PUT | _dbase64 "multiline")
error_response="error"
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
}

View File

@ -20,7 +20,39 @@ haproxy_deploy() {
_debug _cca "$_cca" _debug _cca "$_cca"
_debug _cfullchain "$_cfullchain" _debug _cfullchain "$_cfullchain"
_err "deploy cert to haproxy server, Not implemented yet" # handle reload preference
return 1 DEFAULT_HAPROXY_RELOAD="/usr/sbin/service haproxy restart"
if [ -z "${DEPLOY_HAPROXY_RELOAD}" ]; then
_reload="${DEFAULT_HAPROXY_RELOAD}"
_cleardomainconf DEPLOY_HAPROXY_RELOAD
else
_reload="${DEPLOY_HAPROXY_RELOAD}"
_savedomainconf DEPLOY_HAPROXY_RELOAD "$DEPLOY_HAPROXY_RELOAD"
fi
_savedomainconf DEPLOY_HAPROXY_PEM_PATH "$DEPLOY_HAPROXY_PEM_PATH"
# work out the path where the PEM file should go
_pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
if [ -z "$_pem_path" ]; then
_err "Path to save PEM file not found. Please define DEPLOY_HAPROXY_PEM_PATH."
return 1
fi
_pem_full_path="$_pem_path/$_cdomain.pem"
_info "Full path to PEM $_pem_full_path"
# combine the key and fullchain into a single pem and install
cat "$_cfullchain" "$_ckey" >"$_pem_full_path"
chmod 600 "$_pem_full_path"
_info "Certificate successfully deployed"
# restart HAProxy
_info "Run reload: $_reload"
if eval "$_reload"; then
_info "Reload success!"
return 0
else
_err "Reload error"
return 1
fi
} }

58
deploy/mailcow.sh Normal file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to mailcow.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
mailcow_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"
_mailcow_path="${DEPLOY_MAILCOW_PATH}"
if [ -z "$_mailcow_path" ]; then
_err "Mailcow path is not found, please define DEPLOY_MAILCOW_PATH."
return 1
fi
_ssl_path="${_mailcow_path}/data/assets/ssl/"
if [ ! -d "$_ssl_path" ]; then
_err "Cannot find mailcow ssl path: $_ssl_path"
return 1
fi
_info "Copying key and cert"
_real_key="$_ssl_path/key.pem"
if ! cat "$_ckey" >"$_real_key"; then
_err "Error: write key file to: $_real_key"
return 1
fi
_real_fullchain="$_ssl_path/cert.pem"
if ! cat "$_cfullchain" >"$_real_fullchain"; then
_err "Error: write cert file to: $_real_fullchain"
return 1
fi
DEFAULT_MAILCOW_RELOAD="cd ${_mailcow_path} && docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow"
_reload="${DEPLOY_MAILCOW_RELOAD:-$DEFAULT_MAILCOW_RELOAD}"
_info "Run reload: $_reload"
if eval "$_reload"; then
_info "Reload success!"
fi
return 0
}

59
deploy/mydevil.sh Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env sh
# MyDevil.net API (2019-02-03)
#
# MyDevil.net already supports automatic Let's Encrypt certificates,
# except for wildcard domains.
#
# This script depends on `devil` command that MyDevil.net provides,
# which means that it works only on server side.
#
# Author: Marcin Konicki <https://ahwayakchih.neoni.net>
#
######## Public functions #####################
# Usage: mydevil_deploy domain keyfile certfile cafile fullchain
mydevil_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
ip=""
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
if ! _exists "devil"; then
_err "Could not find 'devil' command."
return 1
fi
ip=$(mydevil_get_ip "$_cdomain")
if [ -z "$ip" ]; then
_err "Could not find IP for domain $_cdomain."
return 1
fi
# Delete old certificate first
_info "Removing old certificate for $_cdomain at $ip"
devil ssl www del "$ip" "$_cdomain"
# Add new certificate
_info "Adding new certificate for $_cdomain at $ip"
devil ssl www add "$ip" "$_cfullchain" "$_ckey" "$_cdomain" || return 1
return 0
}
#################### Private functions below ##################################
# Usage: ip=$(mydevil_get_ip domain.com)
# echo $ip
mydevil_get_ip() {
devil dns list "$1" | cut -w -s -f 3,7 | grep "^A$(printf '\t')" | cut -w -s -f 2 || return 1
return 0
}

92
deploy/qiniu.sh Normal file
View File

@ -0,0 +1,92 @@
#!/usr/bin/env sh
# Script to create certificate to qiniu.com
#
# This deployment required following variables
# export QINIU_AK="QINIUACCESSKEY"
# export QINIU_SK="QINIUSECRETKEY"
# export QINIU_CDN_DOMAIN="cdn.example.com"
QINIU_API_BASE="https://api.qiniu.com"
qiniu_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 [ -z "$QINIU_AK" ]; then
_err "QINIU_AK is not defined."
return 1
else
_savedomainconf QINIU_AK "$QINIU_AK"
fi
if [ -z "$QINIU_SK" ]; then
_err "QINIU_SK is not defined."
return 1
else
_savedomainconf QINIU_SK "$QINIU_SK"
fi
if [ "$QINIU_CDN_DOMAIN" ]; then
_savedomainconf QINIU_CDN_DOMAIN "$QINIU_CDN_DOMAIN"
else
QINIU_CDN_DOMAIN="$_cdomain"
fi
## upload certificate
string_fullchain=$(sed 's/$/\\n/' "$_cfullchain" | tr -d '\n')
string_key=$(sed 's/$/\\n/' "$_ckey" | tr -d '\n')
sslcert_path="/sslcert"
sslcerl_body="{\"name\":\"$_cdomain\",\"common_name\":\"$QINIU_CDN_DOMAIN\",\"ca\":\"$string_fullchain\",\"pri\":\"$string_key\"}"
sslcert_access_token="$(_make_access_token "$sslcert_path")"
_debug sslcert_access_token "$sslcert_access_token"
export _H1="Authorization: QBox $sslcert_access_token"
sslcert_response=$(_post "$sslcerl_body" "$QINIU_API_BASE$sslcert_path" 0 "POST" "application/json" | _dbase64 "multiline")
if ! _contains "$sslcert_response" "certID"; then
_err "Error in creating certificate:"
_err "$sslcert_response"
return 1
fi
_debug sslcert_response "$sslcert_response"
_info "Certificate successfully uploaded, updating domain $_cdomain"
## extract certId
_certId="$(printf "%s" "$sslcert_response" | _normalizeJson | _egrep_o "certID\": *\"[^\"]*\"" | cut -d : -f 2)"
_debug certId "$_certId"
## update domain ssl config
update_path="/domain/$QINIU_CDN_DOMAIN/httpsconf"
update_body="{\"certid\":$_certId,\"forceHttps\":false}"
update_access_token="$(_make_access_token "$update_path")"
_debug update_access_token "$update_access_token"
export _H1="Authorization: QBox $update_access_token"
update_response=$(_post "$update_body" "$QINIU_API_BASE$update_path" 0 "PUT" "application/json" | _dbase64 "multiline")
if _contains "$update_response" "error"; then
_err "Error in updating domain httpsconf:"
_err "$update_response"
return 1
fi
_debug update_response "$update_response"
_info "Certificate successfully deployed"
return 0
}
_make_access_token() {
_token="$(printf "%s\n" "$1" | _hmac "sha1" "$(printf "%s" "$QINIU_SK" | _hex_dump | tr -d " ")" | _base64 | tr -- '+/' '-_')"
echo "$QINIU_AK:$_token"
}

View File

@ -11,7 +11,7 @@
# #
# Only a username is required. All others are optional. # Only a username is required. All others are optional.
# #
# The following examples are for QNAP NAS running QTS 4.2 # The following examples are for QNAP NAS running QTS 4.2
# export DEPLOY_SSH_CMD="" # defaults to ssh # export DEPLOY_SSH_CMD="" # defaults to ssh
# export DEPLOY_SSH_USER="admin" # required # export DEPLOY_SSH_USER="admin" # required
# export DEPLOY_SSH_SERVER="qnap" # defaults to domain name # export DEPLOY_SSH_SERVER="qnap" # defaults to domain name
@ -101,7 +101,7 @@ ssh_deploy() {
fi fi
# CERTFILE is optional. # CERTFILE is optional.
# If provided then private key will be copied or appended to provided filename. # If provided then certificate will be copied or appended to provided filename.
if [ -n "$DEPLOY_SSH_CERTFILE" ]; then if [ -n "$DEPLOY_SSH_CERTFILE" ]; then
Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE" Le_Deploy_ssh_certfile="$DEPLOY_SSH_CERTFILE"
_savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile" _savedomainconf Le_Deploy_ssh_certfile "$Le_Deploy_ssh_certfile"
@ -190,7 +190,7 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
_info "Backup directories erased after 180 days." _info "Backup directories erased after 180 days."
fi fi
_debug "Remote commands to execute: $_cmdstr" _secure_debug "Remote commands to execute: " "$_cmdstr"
_info "Submitting sequence of commands to remote server by ssh" _info "Submitting sequence of commands to remote server by ssh"
# quotations in bash cmd below intended. Squash travis spellcheck error # quotations in bash cmd below intended. Squash travis spellcheck error
# shellcheck disable=SC2029 # shellcheck disable=SC2029

View File

@ -49,9 +49,13 @@ vault_cli_deploy() {
return 1 return 1
fi fi
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1 if [ -n "$FABIO" ]; then
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1 $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}" cert=@"$_cfullchain" key=@"$_ckey" || return 1
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1 else
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1 $VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.pem" value=@"$_ccert" || return 1
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/cert.key" value=@"$_ckey" || return 1
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/chain.pem" value=@"$_cca" || return 1
$VAULT_CMD write "${VAULT_PREFIX}/${_cdomain}/fullchain.pem" value=@"$_cfullchain" || return 1
fi
} }

View File

@ -1,900 +1,5 @@
# How to use DNS API # How to use DNS API
DNS api usage:
If your dns provider doesn't provide api access, you can use our dns alias mode: https://github.com/Neilpang/acme.sh/wiki/dnsapi
https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode
## 1. Use CloudFlare domain API to automatically issue cert
First you need to login to your CloudFlare account to get your API key.
```
export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export CF_Email="xxxx@sss.com"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_cf -d example.com -d www.example.com
```
The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 2. Use DNSPod.cn domain API to automatically issue cert
First you need to login to your DNSPod account to get your API Key and ID.
```
export DP_Id="1234"
export DP_Key="sADDsdasdgdsf"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_dp -d example.com -d www.example.com
```
The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 3. Use CloudXNS.com domain API to automatically issue cert
First you need to login to your CloudXNS account to get your API Key and Secret.
```
export CX_Key="1234"
export CX_Secret="sADDsdasdgdsf"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_cx -d example.com -d www.example.com
```
The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 4. Use GoDaddy.com domain API to automatically issue cert
First you need to login to your GoDaddy account to get your API Key and Secret.
https://developer.godaddy.com/keys/
Please create a Production key, instead of a Test key.
```
export GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_gd -d example.com -d www.example.com
```
The `GD_Key` and `GD_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 5. Use PowerDNS embedded API to automatically issue cert
First you need to login to your PowerDNS account to enable the API and set your API-Token in the configuration.
https://doc.powerdns.com/md/httpapi/README/
```
export PDNS_Url="http://ns.example.com:8081"
export PDNS_ServerId="localhost"
export PDNS_Token="0123456789ABCDEF"
export PDNS_Ttl=60
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_pdns -d example.com -d www.example.com
```
The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 6. Use OVH/kimsufi/soyoustart/runabove API to automatically issue cert
https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api
## 7. Use nsupdate to automatically issue cert
First, generate a key for updating the zone
```
b=$(dnssec-keygen -a hmac-sha512 -b 512 -n USER -K /tmp foo)
cat > /etc/named/keys/update.key <<EOF
key "update" {
algorithm hmac-sha512;
secret "$(awk '/^Key/{print $2}' /tmp/$b.private)";
};
EOF
rm -f /tmp/$b.{private,key}
```
Include this key in your named configuration
```
include "/etc/named/keys/update.key";
```
Next, configure your zone to allow dynamic updates.
Depending on your named version, use either
```
zone "example.com" {
type master;
allow-update { key "update"; };
};
```
or
```
zone "example.com" {
type master;
update-policy {
grant update subdomain example.com.;
};
}
```
Finally, make the DNS server and update Key available to `acme.sh`
```
export NSUPDATE_SERVER="dns.example.com"
export NSUPDATE_KEY="/path/to/your/nsupdate.key"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_nsupdate -d example.com -d www.example.com
```
The `NSUPDATE_SERVER` and `NSUPDATE_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 8. Use LuaDNS domain API
Get your API token at https://api.luadns.com/settings
```
export LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export LUA_Email="xxxx@sss.com"
```
To issue a cert:
```
acme.sh --issue --dns dns_lua -d example.com -d www.example.com
```
The `LUA_Key` and `LUA_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 9. Use DNSMadeEasy domain API
Get your API credentials at https://cp.dnsmadeeasy.com/account/info
```
export ME_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export ME_Secret="qdfqsdfkjdskfj"
```
To issue a cert:
```
acme.sh --issue --dns dns_me -d example.com -d www.example.com
```
The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 10. Use Amazon Route53 domain API
https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API
```
export AWS_ACCESS_KEY_ID=XXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX
```
To issue a cert:
```
acme.sh --issue --dns dns_aws -d example.com -d www.example.com
```
The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 11. Use Aliyun domain API to automatically issue cert
First you need to login to your Aliyun account to get your API key.
[https://ak-console.aliyun.com/#/accesskey](https://ak-console.aliyun.com/#/accesskey)
```
export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export Ali_Secret="jlsdflanljkljlfdsaklkjflsa"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_ali -d example.com -d www.example.com
```
The `Ali_Key` and `Ali_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 12. Use ISPConfig 3.1 API
This only works for ISPConfig 3.1 (and newer).
Create a Remote User in the ISPConfig Control Panel. The Remote User must have access to at least `DNS zone functions` and `DNS txt functions`.
```
export ISPC_User="xxx"
export ISPC_Password="xxx"
export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
export ISPC_Api_Insecure=1
```
If you have installed ISPConfig on a different port, then alter the 8080 accordingly.
Leaver ISPC_Api_Insecure set to 1 if you have not a valid ssl cert for your installation. Change it to 0 if you have a valid ssl cert.
To issue a cert:
```
acme.sh --issue --dns dns_ispconfig -d example.com -d www.example.com
```
The `ISPC_User`, `ISPC_Password`, `ISPC_Api`and `ISPC_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 13. Use Alwaysdata domain API
First you need to login to your Alwaysdata account to get your API Key.
```sh
export AD_API_KEY="myalwaysdataapikey"
```
Ok, let's issue a cert now:
```sh
acme.sh --issue --dns dns_ad -d example.com -d www.example.com
```
The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused
when needed.
## 14. Use Linode domain API
First you need to login to your Linode account to get your API Key.
[https://manager.linode.com/profile/api](https://manager.linode.com/profile/api)
Then add an API key with label *ACME* and copy the new key.
```sh
export LINODE_API_KEY="..."
```
Due to the reload time of any changes in the DNS records, we have to use the `dnssleep` option to wait at least 15 minutes for the changes to take effect.
Ok, let's issue a cert now:
```sh
acme.sh --issue --dns dns_linode --dnssleep 900 -d example.com -d www.example.com
```
The `LINODE_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 15. Use FreeDNS
FreeDNS (https://freedns.afraid.org/) does not provide an API to update DNS records (other than IPv4 and IPv6
dynamic DNS addresses). The acme.sh plugin therefore retrieves and updates domain TXT records by logging
into the FreeDNS website to read the HTML and posting updates as HTTP. The plugin needs to know your
userid and password for the FreeDNS website.
```sh
export FREEDNS_User="..."
export FREEDNS_Password="..."
```
You need only provide this the first time you run the acme.sh client with FreeDNS validation and then again
whenever you change your password at the FreeDNS site. The acme.sh FreeDNS plugin does not store your userid
or password but rather saves an authentication token returned by FreeDNS in `~/.acme.sh/account.conf` and
reuses that when needed.
Now you can issue a certificate.
```sh
acme.sh --issue --dns dns_freedns -d example.com -d www.example.com
```
Note that you cannot use acme.sh automatic DNS validation for FreeDNS public domains or for a subdomain that
you create under a FreeDNS public domain. You must own the top level domain in order to automatically
validate with acme.sh at FreeDNS.
## 16. Use cyon.ch
You only need to set your cyon.ch login credentials.
If you also have 2 Factor Authentication (OTP) enabled, you need to set your secret token too and have `oathtool` installed.
```
export CY_Username="your_cyon_username"
export CY_Password="your_cyon_password"
export CY_OTP_Secret="your_otp_secret" # Only required if using 2FA
```
To issue a cert:
```
acme.sh --issue --dns dns_cyon -d example.com -d www.example.com
```
The `CY_Username`, `CY_Password` and `CY_OTP_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 17. Use Domain-Offensive/Resellerinterface/Domainrobot API
ATTENTION: You need to be a registered Reseller to be able to use the ResellerInterface. As a normal user you can not use this method.
You will need your login credentials (Partner ID+Password) to the Resellerinterface, and export them before you run `acme.sh`:
```
export DO_PID="KD-1234567"
export DO_PW="cdfkjl3n2"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_do -d example.com -d www.example.com
```
## 18. Use Gandi LiveDNS API
You must enable the new Gandi LiveDNS API first and the create your api key, See: http://doc.livedns.gandi.net/
```
export GANDI_LIVEDNS_KEY="fdmlfsdklmfdkmqsdfk"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_gandi_livedns -d example.com -d www.example.com
```
## 19. Use Knot (knsupdate) DNS API to automatically issue cert
First, generate a TSIG key for updating the zone.
```
keymgr tsig generate -t acme_key hmac-sha512 > /etc/knot/acme.key
```
Include this key in your knot configuration file.
```
include: /etc/knot/acme.key
```
Next, configure your zone to allow dynamic updates.
Dynamic updates for the zone are allowed via proper ACL rule with the `update` action. For in-depth instructions, please see [Knot DNS's documentation](https://www.knot-dns.cz/documentation/).
```
acl:
- id: acme_acl
address: 192.168.1.0/24
key: acme_key
action: update
zone:
- domain: example.com
file: example.com.zone
acl: acme_acl
```
Finally, make the DNS server and TSIG Key available to `acme.sh`
```
export KNOT_SERVER="dns.example.com"
export KNOT_KEY=`grep \# /etc/knot/acme.key | cut -d' ' -f2`
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_knot -d example.com -d www.example.com
```
The `KNOT_SERVER` and `KNOT_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 20. Use DigitalOcean API (native)
You need to obtain a read and write capable API key from your DigitalOcean account. See: https://www.digitalocean.com/help/api/
```
export DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_dgon -d example.com -d www.example.com
```
## 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/. 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.
```
# 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"
```
Ok, let's issue a cert now:
```
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
First you need to create/obtain API credentials on your Infoblox appliance.
```
export Infoblox_Creds="username:password"
export Infoblox_Server="ip or fqdn of infoblox appliance"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_infoblox -d example.com -d www.example.com
```
Note: This script will automatically create and delete the ephemeral txt record.
The `Infoblox_Creds` and `Infoblox_Server` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 23. Use VSCALE API
First you need to create/obtain API tokens on your [settings panel](https://vscale.io/panel/settings/tokens/).
```
VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_vscale -d example.com -d www.example.com
```
## 24. Use Dynu API
First you need to create/obtain API credentials from your Dynu account. See: https://www.dynu.com/resources/api/documentation
```
export Dynu_ClientId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export Dynu_Secret="yyyyyyyyyyyyyyyyyyyyyyyyy"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_dynu -d example.com -d www.example.com
```
The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 25. Use DNSimple API
First you need to login to your DNSimple account and generate a new oauth token.
https://dnsimple.com/a/{your account id}/account/access_tokens
Note that this is an _account_ token and not a user token. The account token is
needed to infer the `account_id` used in requests. A user token will not be able
to determine the correct account to use.
```
export DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
```
To issue the cert just specify the `dns_dnsimple` API.
```
acme.sh --issue --dns dns_dnsimple -d example.com
```
The `DNSimple_OAUTH_TOKEN` will be saved in `~/.acme.sh/account.conf` and will
be reused when needed.
If you have any issues with this integration please report them to
https://github.com/pho3nixf1re/acme.sh/issues.
## 26. Use NS1.com API
```
export NS1_Key="fdmlfsdklmfdkmqsdfk"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_nsone -d example.com -d www.example.com
```
## 27. Use DuckDNS.org API
```
export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
```
Please note that since DuckDNS uses StartSSL as their cert provider, thus
--insecure may need to be used when issuing certs:
```
acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org
```
For issues, please report to https://github.com/raidenii/acme.sh/issues.
## 28. Use Name.com API
Create your API token here: https://www.name.com/account/settings/api
Note: `Namecom_Username` should be your Name.com username and not the token name. If you accidentally run the script with the token name as the username see `~/.acme.sh/account.conf` to fix the issue
```
export Namecom_Username="testuser"
export Namecom_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```
And now you can issue certs with:
```
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.
## 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.
If your account is secured by mobile tan you have also defined the shared secret.
```
export INWX_Shared_Secret="shared secret"
```
You may need to re-enable the mobile tan to gain the shared secret.
## 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.
## 41. Use DirectAdmin API
The DirectAdmin interface has it's own Let's encrypt functionality, but this
script can be used to generate certificates for names which are not hosted on
DirectAdmin
User must provide login data and URL to the DirectAdmin incl. port.
You can create an user which only has access to
- CMD_API_DNS_CONTROL
- CMD_API_SHOW_DOMAINS
By using the Login Keys function.
See also https://www.directadmin.com/api.php and https://www.directadmin.com/features.php?id=1298
```
export DA_Api="https://remoteUser:remotePassword@da.domain.tld:8443"
export DA_Api_Insecure=1
```
Set `DA_Api_Insecure` to 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_da -d example.com -d www.example.com
```
The `DA_Api` and `DA_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 42. Use KingHost DNS API
API access must be enabled at https://painel.kinghost.com.br/painel.api.php
```
export KINGHOST_Username="yourusername"
export KINGHOST_Password="yourpassword"
acme.sh --issue --dns dns_kinghost -d example.com -d *.example.com
```
The `KINGHOST_username` and `KINGHOST_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 43. Use Zilore DNS API
First, get your API key at https://my.zilore.com/account/api
```
export Zilore_Key="5dcad3a2-36cb-50e8-cb92-000002f9"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_zilore -d example.com -d *.example.com
```
The `Zilore_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 44. Use Loopia.se API
User must provide login credentials to the Loopia API.
The user needs the following permissions:
- addSubdomain
- updateZoneRecord
- getDomains
- removeSubdomain
Set the login credentials:
```
export LOOPIA_User="user@loopiaapi"
export LOOPIA_Password="password"
```
And to issue a cert:
```
acme.sh --issue --dns dns_loopia -d example.com -d *.example.com
```
The username and password will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 45. Use ACME DNS API
ACME DNS is a limited DNS server with RESTful HTTP API to handle ACME DNS challenges easily and securely.
https://github.com/joohoi/acme-dns
```
export ACMEDNS_BASE_URL="https://auth.acme-dns.io"
```
And to issue a cert:
```
acme.sh --issue --dns dns_acmedns -d example.com -d *.example.com
```
The needed domain account is automatically created. The script will tell you the needed cname to create.
The credentials will be saved in `~/.acme.sh/#DOMAIN/#DOMAIN.conf` and will
be reused when needed.
## 46. Use TELE3 API
First you need to login to your TELE3 account to set your API-KEY.
https://www.tele3.cz/system-acme-api.html
```
export TELE3_Key="MS2I4uPPaI..."
export TELE3_Secret="kjhOIHGJKHg"
acme.sh --issue --dns dns_tele3 -d example.com -d *.example.com
```
The TELE3_Key and TELE3_Secret will be saved in ~/.acme.sh/account.conf and will be reused when needed.
# Use custom API
If your API is not supported yet, you can write your own DNS API.
Let's assume you want to name it 'myapi':
1. Create a bash script named `~/.acme.sh/dns_myapi.sh`,
2. In the script you must have a function named `dns_myapi_add()` which will be called by acme.sh to add the DNS records.
3. Then you can use your API to issue cert like this:
```
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)
See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
# Use lexicon DNS API
https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api

141
dnsapi/dns_active24.sh Executable file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env sh
#ACTIVE24_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
ACTIVE24_Api="https://api.active24.com"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_active24_add() {
fulldomain=$1
txtvalue=$2
_active24_init
_info "Adding txt record"
if _active24_rest POST "dns/$_domain/txt/v1" "{\"name\":\"$_sub_domain\",\"text\":\"$txtvalue\",\"ttl\":0}"; then
if _contains "$response" "errors"; then
_err "Add txt record error."
return 1
else
_info "Added, OK"
return 0
fi
fi
_err "Add txt record error."
return 1
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_active24_rm() {
fulldomain=$1
txtvalue=$2
_active24_init
_debug "Getting txt records"
_active24_rest GET "dns/$_domain/records/v1"
if _contains "$response" "errors"; then
_err "Error"
return 1
fi
hash_ids=$(echo "$response" | _egrep_o "[^{]+${txtvalue}[^}]+" | _egrep_o "hashId\":\"[^\"]+" | cut -c10-)
for hash_id in $hash_ids; do
_debug "Removing hash_id" "$hash_id"
if _active24_rest DELETE "dns/$_domain/$hash_id/v1" ""; then
if _contains "$response" "errors"; then
_err "Unable to remove txt record."
return 1
else
_info "Removed txt record."
return 0
fi
fi
done
_err "No txt records found."
return 1
}
#################### 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 ! _active24_rest GET "dns/domains/v1"; 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" "\"$h\"" >/dev/null; 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
}
_active24_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Authorization: Bearer $ACTIVE24_Token"
if [ "$m" != "GET" ]; then
_debug "data" "$data"
response="$(_post "$data" "$ACTIVE24_Api/$ep" "" "$m" "application/json")"
else
response="$(_get "$ACTIVE24_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_active24_init() {
ACTIVE24_Token="${ACTIVE24_Token:-$(_readaccountconf_mutable ACTIVE24_Token)}"
if [ -z "$ACTIVE24_Token" ]; then
ACTIVE24_Token=""
_err "You didn't specify a Active24 api token yet."
_err "Please create the token and try again."
return 1
fi
_saveaccountconf_mutable ACTIVE24_Token "ACTIVE24_Token"
_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"
}

View File

@ -29,7 +29,7 @@ dns_aws_add() {
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 haven't specifed the aws route53 api key id and and api key secret yet."
_err "Please create your 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
@ -62,7 +62,7 @@ dns_aws_add() {
fi fi
if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then
_info "The txt record already exists, skip" _info "The TXT record already exists. Skipping."
return 0 return 0
fi fi
@ -71,7 +71,7 @@ dns_aws_add() {
_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>" _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 successfully."
return 0 return 0
fi fi
@ -99,7 +99,7 @@ dns_aws_rm() {
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
_info "Geting existing records for $fulldomain" _info "Getting existing records for $fulldomain"
if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then
return 1 return 1
fi fi
@ -108,14 +108,14 @@ dns_aws_rm() {
_resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")" _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" _debug "_resource_record" "$_resource_record"
else else
_debug "no records exists, skip" _debug "no records exist, skip"
return 0 return 0
fi 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>" _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 successfully."
return 0 return 0
fi fi
@ -163,7 +163,7 @@ _get_root() {
_domain=$h _domain=$h
return 0 return 0
fi fi
_err "Can not find domain id: $h" _err "Can't find domain with id: $h"
return 1 return 1
fi fi
fi fi

View File

@ -316,7 +316,7 @@ _get_root() {
## (ZoneListResult with continuation token for the next page of results) ## (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 ## 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" _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?\$top=500&api-version=2017-09-01" "" "$accesstoken"
# Find matching domain name is Json response # Find matching domain name is Json 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)

View File

@ -19,8 +19,8 @@ dns_cf_add() {
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key="" CF_Key=""
CF_Email="" CF_Email=""
_err "You didn't specify a cloudflare api key and email yet." _err "You didn't specify a Cloudflare api key and email yet."
_err "Please create the key and try again." _err "You can get yours from here https://dash.cloudflare.com/profile."
return 1 return 1
fi fi
@ -58,9 +58,12 @@ dns_cf_add() {
# if [ "$count" = "0" ]; then # 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 _contains "$response" "$fulldomain"; then
_info "Added, OK" _info "Added, OK"
return 0 return 0
elif _contains "$response" "The record already exists"; then
_info "Already exists, OK"
return 0
else else
_err "Add txt record error." _err "Add txt record error."
return 1 return 1
@ -94,8 +97,8 @@ dns_cf_rm() {
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key="" CF_Key=""
CF_Email="" CF_Email=""
_err "You didn't specify a cloudflare api key and email yet." _err "You didn't specify a Cloudflare api key and email yet."
_err "Please create the key and try again." _err "You can get yours from here https://dash.cloudflare.com/profile."
return 1 return 1
fi fi
@ -144,7 +147,7 @@ dns_cf_rm() {
# _domain_id=sdjkglgdfewsdfg # _domain_id=sdjkglgdfewsdfg
_get_root() { _get_root() {
domain=$1 domain=$1
i=2 i=1
p=1 p=1
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
@ -159,7 +162,7 @@ _get_root() {
fi fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \") _domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | 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

157
dnsapi/dns_cn.sh Normal file
View File

@ -0,0 +1,157 @@
#!/usr/bin/env sh
# DNS API for acme.sh for Core-Networks (https://beta.api.core-networks.de/doc/).
# created by 5ll and francis
CN_API="https://beta.api.core-networks.de"
######## Public functions #####################
dns_cn_add() {
fulldomain=$1
txtvalue=$2
if ! _cn_login; then
_err "login failed"
return 1
fi
_debug "First detect the root zone"
if ! _cn_get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug "_sub_domain $_sub_domain"
_debug "_domain $_domain"
_info "Adding record"
curData="{\"name\":\"$_sub_domain\",\"ttl\":120,\"type\":\"TXT\",\"data\":\"$txtvalue\"}"
curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/")"
_debug "curData $curData"
_debug "curResult $curResult"
if _contains "$curResult" ""; then
_info "Added, OK"
if ! _cn_commit; then
_err "commiting changes failed"
return 1
fi
return 0
else
_err "Add txt record error."
_debug "curData is $curData"
_debug "curResult is $curResult"
_err "error adding text record, response was $curResult"
return 1
fi
}
dns_cn_rm() {
fulldomain=$1
txtvalue=$2
if ! _cn_login; then
_err "login failed"
return 1
fi
_debug "First detect the root zone"
if ! _cn_get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_info "Deleting record"
curData="{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\"}"
curResult="$(_post "${curData}" "${CN_API}/dnszones/${_domain}/records/delete")"
_debug curData is "$curData"
_info "commiting changes"
if ! _cn_commit; then
_err "commiting changes failed"
return 1
fi
_info "Deletet txt record"
return 0
}
################### Private functions below ##################################
_cn_login() {
CN_User="${CN_User:-$(_readaccountconf_mutable CN_User)}"
CN_Password="${CN_Password:-$(_readaccountconf_mutable CN_Password)}"
if [ -z "$CN_User" ] || [ -z "$CN_Password" ]; then
CN_User=""
CN_Password=""
_err "You must export variables: CN_User and CN_Password"
return 1
fi
#save the config variables to the account conf file.
_saveaccountconf_mutable CN_User "$CN_User"
_saveaccountconf_mutable CN_Password "$CN_Password"
_info "Getting an AUTH-Token"
curData="{\"login\":\"${CN_User}\",\"password\":\"${CN_Password}\"}"
curResult="$(_post "${curData}" "${CN_API}/auth/token")"
_debug "Calling _CN_login: '${curData}' '${CN_API}/auth/token'"
if _contains "${curResult}" '"token":"'; then
authToken=$(echo "${curResult}" | cut -d ":" -f2 | cut -d "," -f1 | sed 's/^.\(.*\).$/\1/')
export _H1="Authorization: Bearer $authToken"
_info "Successfully acquired AUTH-Token"
_debug "AUTH-Token: '${authToken}'"
_debug "_H1 '${_H1}'"
else
_err "Couldn't acquire an AUTH-Token"
return 1
fi
}
# Commit changes
_cn_commit() {
_info "Commiting changes"
_post "" "${CN_API}/dnszones/$h/records/commit"
}
_cn_get_root() {
domain=$1
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
_debug _H1 "${_H1}"
if [ -z "$h" ]; then
#not valid
return 1
fi
_cn_zonelist="$(_get ${CN_API}/dnszones/)"
_debug _cn_zonelist "${_cn_zonelist}"
if [ "$?" != "0" ]; then
_err "something went wrong while getting the zone list"
return 1
fi
if _contains "$_cn_zonelist" "\"name\":\"$h\"" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
else
_debug "Zonelist does not contain domain - iterating "
fi
p=$i
i=$(_math "$i" + 1)
done
_err "Zonelist does not contain domain - exiting"
return 1
}

253
dnsapi/dns_conoha.sh Executable file
View File

@ -0,0 +1,253 @@
#!/usr/bin/env sh
CONOHA_DNS_EP_PREFIX_REGEXP="https://dns-service\."
######## Public functions #####################
#Usage: dns_conoha_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_conoha_add() {
fulldomain=$1
txtvalue=$2
_info "Using conoha"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug "Check uesrname and password"
CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}"
CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}"
CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}"
CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}"
if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then
CONOHA_Username=""
CONOHA_Password=""
CONOHA_TenantId=""
CONOHA_IdentityServiceApi=""
_err "You didn't specify a conoha api username and password yet."
_err "Please create the user and try again."
return 1
fi
_saveaccountconf_mutable CONOHA_Username "$CONOHA_Username"
_saveaccountconf_mutable CONOHA_Password "$CONOHA_Password"
_saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId"
_saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi"
if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then
accesstoken="$(printf "%s" "$token" | sed -n 1p)"
CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)"
else
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_info "Adding record"
body="{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"data\":\"$txtvalue\",\"ttl\":60}"
if _conoha_rest POST "$CONOHA_Api/v1/domains/$_domain_id/records" "$body" "$accesstoken"; then
if _contains "$response" '"data":"'"$txtvalue"'"'; 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_conoha_rm() {
fulldomain=$1
txtvalue=$2
_info "Using conoha"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug "Check uesrname and password"
CONOHA_Username="${CONOHA_Username:-$(_readaccountconf_mutable CONOHA_Username)}"
CONOHA_Password="${CONOHA_Password:-$(_readaccountconf_mutable CONOHA_Password)}"
CONOHA_TenantId="${CONOHA_TenantId:-$(_readaccountconf_mutable CONOHA_TenantId)}"
CONOHA_IdentityServiceApi="${CONOHA_IdentityServiceApi:-$(_readaccountconf_mutable CONOHA_IdentityServiceApi)}"
if [ -z "$CONOHA_Username" ] || [ -z "$CONOHA_Password" ] || [ -z "$CONOHA_TenantId" ] || [ -z "$CONOHA_IdentityServiceApi" ]; then
CONOHA_Username=""
CONOHA_Password=""
CONOHA_TenantId=""
CONOHA_IdentityServiceApi=""
_err "You didn't specify a conoha api username and password yet."
_err "Please create the user and try again."
return 1
fi
_saveaccountconf_mutable CONOHA_Username "$CONOHA_Username"
_saveaccountconf_mutable CONOHA_Password "$CONOHA_Password"
_saveaccountconf_mutable CONOHA_TenantId "$CONOHA_TenantId"
_saveaccountconf_mutable CONOHA_IdentityServiceApi "$CONOHA_IdentityServiceApi"
if token="$(_conoha_get_accesstoken "$CONOHA_IdentityServiceApi/tokens" "$CONOHA_Username" "$CONOHA_Password" "$CONOHA_TenantId")"; then
accesstoken="$(printf "%s" "$token" | sed -n 1p)"
CONOHA_Api="$(printf "%s" "$token" | sed -n 2p)"
else
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain" "$CONOHA_Api" "$accesstoken"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
if ! _conoha_rest GET "$CONOHA_Api/v1/domains/$_domain_id/records" "" "$accesstoken"; then
_err "Error"
return 1
fi
record_id=$(printf "%s" "$response" | _egrep_o '{[^}]*}' \
| grep '"type":"TXT"' | grep "\"data\":\"$txtvalue\"" | _egrep_o "\"id\":\"[^\"]*\"" \
| _head_n 1 | cut -d : -f 2 | tr -d \")
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
_debug record_id "$record_id"
_info "Removing the txt record"
if ! _conoha_rest DELETE "$CONOHA_Api/v1/domains/$_domain_id/records/$record_id" "" "$accesstoken"; then
_err "Delete record error."
return 1
fi
return 0
}
#################### Private functions below ##################################
_conoha_rest() {
m="$1"
ep="$2"
data="$3"
accesstoken="$4"
export _H1="Accept: application/json"
export _H2="Content-Type: application/json"
if [ -n "$accesstoken" ]; then
export _H3="X-Auth-Token: $accesstoken"
fi
_debug "$ep"
if [ "$m" != "GET" ]; then
_secure_debug2 data "$data"
response="$(_post "$data" "$ep" "" "$m")"
else
response="$(_get "$ep")"
fi
_ret="$?"
_secure_debug2 response "$response"
if [ "$_ret" != "0" ]; then
_err "error $ep"
return 1
fi
response="$(printf "%s" "$response" | _normalizeJson)"
return 0
}
_conoha_get_accesstoken() {
ep="$1"
username="$2"
password="$3"
tenantId="$4"
accesstoken="$(_readaccountconf_mutable conoha_accesstoken)"
expires="$(_readaccountconf_mutable conoha_tokenvalidto)"
CONOHA_Api="$(_readaccountconf_mutable conoha_dns_ep)"
# can we reuse the access token?
if [ -n "$accesstoken" ] && [ -n "$expires" ] && [ -n "$CONOHA_Api" ]; then
utc_date="$(_utc_date | sed "s/ /T/")"
if expr "$utc_date" "<" "$expires" >/dev/null; then
# access token is still valid - reuse it
_debug "reusing access token"
printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api"
return 0
else
_debug "access token expired"
fi
fi
_debug "getting new access token"
body="$(printf '{"auth":{"passwordCredentials":{"username":"%s","password":"%s"},"tenantId":"%s"}}' "$username" "$password" "$tenantId")"
if ! _conoha_rest POST "$ep" "$body" ""; then
_err error "$response"
return 1
fi
accesstoken=$(printf "%s" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
expires=$(printf "%s" "$response" | _egrep_o "\"expires\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2-4 | tr -d \" | tr -d Z) #expect UTC
if [ -z "$accesstoken" ] || [ -z "$expires" ]; then
_err "no acccess token received. Check your Conoha settings see $WIKI"
return 1
fi
_saveaccountconf_mutable conoha_accesstoken "$accesstoken"
_saveaccountconf_mutable conoha_tokenvalidto "$expires"
CONOHA_Api=$(printf "%s" "$response" | _egrep_o 'publicURL":"'"$CONOHA_DNS_EP_PREFIX_REGEXP"'[^"]*"' | _head_n 1 | cut -d : -f 2-3 | tr -d \")
if [ -z "$CONOHA_Api" ]; then
_err "failed to get conoha dns endpoint url"
return 1
fi
_saveaccountconf_mutable conoha_dns_ep "$CONOHA_Api"
printf "%s\n%s\n" "$accesstoken" "$CONOHA_Api"
return 0
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain="$1"
ep="$2"
accesstoken="$3"
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 ! _conoha_rest GET "$ep/v1/domains?name=$h" "" "$accesstoken"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | 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
}

View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
# Cloudxns.com Domain api # CloudXNS Domain api
# #
#CX_Key="1234" #CX_Key="1234"
# #
@ -19,7 +19,7 @@ dns_cx_add() {
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
CX_Key="" CX_Key=""
CX_Secret="" CX_Secret=""
_err "You don't specify cloudxns.com api key or secret yet." _err "You don't specify cloudxns.net api key or secret yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
fi fi

204
dnsapi/dns_desec.sh Normal file
View File

@ -0,0 +1,204 @@
#!/usr/bin/env sh
#
# deSEC.io Domain API
#
# Author: Zheng Qian
#
# deSEC API doc
# https://desec.readthedocs.io/en/latest/
REST_API="https://desec.io/api/v1/domains"
######## Public functions #####################
#Usage: dns_desec_add _acme-challenge.foobar.dedyn.io "d41d8cd98f00b204e9800998ecf8427e"
dns_desec_add() {
fulldomain=$1
txtvalue=$2
_info "Using desec.io api"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}"
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
DEDYN_TOKEN=""
DEDYN_NAME=""
_err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet."
_err "Please create you key and try again."
_err "e.g."
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
_err "export DEDYN_NAME=foobar.dedyn.io"
return 1
fi
#save the api token and name to the account conf file.
_saveaccountconf_mutable DEDYN_TOKEN "$DEDYN_TOKEN"
_saveaccountconf_mutable DEDYN_NAME "$DEDYN_NAME"
_debug "First detect the root zone"
if ! _get_root "$fulldomain" "$REST_API/"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
# Get existing TXT record
_debug "Getting txt records"
txtvalues="\"\\\"$txtvalue\\\"\""
_desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/"
if [ "$_code" = "200" ]; then
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
_debug "existing TXT found"
_debug oldtxtvalues "$oldtxtvalues"
if [ -n "$oldtxtvalues" ]; then
for oldtxtvalue in $oldtxtvalues; do
txtvalues="$txtvalues, \"\\\"$oldtxtvalue\\\"\""
done
fi
fi
_debug txtvalues "$txtvalues"
_info "Adding record"
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]"
if _desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body"; then
if _contains "$response" "$txtvalue"; 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_desec_rm() {
fulldomain=$1
txtvalue=$2
_info "Using desec.io api"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
DEDYN_TOKEN="${DEDYN_TOKEN:-$(_readaccountconf_mutable DEDYN_TOKEN)}"
DEDYN_NAME="${DEDYN_NAME:-$(_readaccountconf_mutable DEDYN_NAME)}"
if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then
DEDYN_TOKEN=""
DEDYN_NAME=""
_err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet."
_err "Please create you key and try again."
_err "e.g."
_err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e"
_err "export DEDYN_NAME=foobar.dedyn.io"
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain" "$REST_API/"; then
_err "invalid domain"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
# Get existing TXT record
_debug "Getting txt records"
txtvalues=""
_desec_rest GET "$REST_API/$DEDYN_NAME/rrsets/$_sub_domain/TXT/"
if [ "$_code" = "200" ]; then
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
_debug "existing TXT found"
_debug oldtxtvalues "$oldtxtvalues"
if [ -n "$oldtxtvalues" ]; then
for oldtxtvalue in $oldtxtvalues; do
if [ "$txtvalue" != "$oldtxtvalue" ]; then
txtvalues="$txtvalues, \"\\\"$oldtxtvalue\\\"\""
fi
done
fi
fi
txtvalues="$(echo "$txtvalues" | cut -c3-)"
_debug txtvalues "$txtvalues"
_info "Deleting record"
body="[{\"subname\":\"$_sub_domain\", \"type\":\"TXT\", \"records\":[$txtvalues], \"ttl\":60}]"
_desec_rest PUT "$REST_API/$DEDYN_NAME/rrsets/" "$body"
if [ "$_code" = "200" ]; then
_info "Deleted, OK"
return 0
fi
_err "Delete txt record error."
return 1
}
#################### Private functions below ##################################
_desec_rest() {
m="$1"
ep="$2"
data="$3"
export _H1="Authorization: Token $DEDYN_TOKEN"
export _H2="Accept: application/json"
export _H3="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_secure_debug2 data "$data"
response="$(_post "$data" "$ep" "" "$m")"
else
response="$(_get "$ep")"
fi
_ret="$?"
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"
_secure_debug2 response "$response"
if [ "$_ret" != "0" ]; then
_err "error $ep"
return 1
fi
response="$(printf "%s" "$response" | _normalizeJson)"
return 0
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain="$1"
ep="$2"
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 ! _desec_rest GET "$ep"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; 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
}

View File

@ -104,48 +104,60 @@ dns_dgon_rm() {
## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}} ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
GURL="https://api.digitalocean.com/v2/domains/$_domain/records" GURL="https://api.digitalocean.com/v2/domains/$_domain/records"
## while we dont have a record ID we keep going ## Get all the matching records
while [ -z "$record" ]; do while true; do
## 1) get the URL ## 1) get the URL
## the create request - get ## the create request - get
## args: URL, [onlyheader, timeout] ## args: URL, [onlyheader, timeout]
domain_list="$(_get "$GURL")" domain_list="$(_get "$GURL")"
## 2) find record
## check for what we are looing for: "type":"A","name":"$_sub_domain" ## check response
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")" if [ "$?" != "0" ]; then
## 3) check record and get next page _err "error in domain_list response: $domain_list"
if [ -z "$record" ]; then return 1
## find the next page if we dont have a match
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
if [ -z "$nextpage" ]; then
_err "no record and no nextpage in digital ocean DNS removal"
return 1
fi
_debug2 nextpage "$nextpage"
GURL="$nextpage"
fi fi
## we break out of the loop when we have a record _debug2 domain_list "$domain_list"
## 2) find records
## check for what we are looking for: "type":"A","name":"$_sub_domain"
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*[0-9]+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
if [ ! -z "$record" ]; then
## we found records
rec_ids="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug rec_ids "$rec_ids"
if [ ! -z "$rec_ids" ]; then
echo "$rec_ids" | while IFS= read -r rec_id; do
## delete the record
## delete URL for removing the one we dont want
DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id"
## the create request - delete
## args: BODY, URL, [need64, httpmethod]
response="$(_post "" "$DURL" "" "DELETE")"
## check response (sort of)
if [ "$?" != "0" ]; then
_err "error in remove response: $response"
return 1
fi
_debug2 response "$response"
done
fi
fi
## 3) find the next page
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
if [ -z "$nextpage" ]; then
break
fi
_debug2 nextpage "$nextpage"
GURL="$nextpage"
done done
## we found the record
rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
_debug rec_id "$rec_id"
## delete the record
## delete URL for removing the one we dont want
DURL="https://api.digitalocean.com/v2/domains/$_domain/records/$rec_id"
## the create request - delete
## args: BODY, URL, [need64, httpmethod]
response="$(_post "" "$DURL" "" "DELETE")"
## check response (sort of)
if [ "$?" != "0" ]; then
_err "error in remove response: $response"
return 1
fi
_debug2 response "$response"
## finished correctly ## finished correctly
return 0 return 0
} }
@ -166,7 +178,7 @@ dns_dgon_rm() {
## _domain="domain.com" ## _domain="domain.com"
_get_base_domain() { _get_base_domain() {
# args # args
fulldomain="$(echo "$1" | tr '[:upper:]' '[:lower:]')" fulldomain="$(echo "$1" | _lower_case)"
_debug fulldomain "$fulldomain" _debug fulldomain "$fulldomain"
# domain max legal length = 253 # domain max legal length = 253
@ -178,44 +190,57 @@ _get_base_domain() {
export _H2="Authorization: Bearer $DO_API_KEY" export _H2="Authorization: Bearer $DO_API_KEY"
_debug DO_API_KEY "$DO_API_KEY" _debug DO_API_KEY "$DO_API_KEY"
## get URL for the list of domains ## get URL for the list of domains
## havent seen this request paginated, tested with 18 domains (more requires manual requests with DO) ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
DOMURL="https://api.digitalocean.com/v2/domains" DOMURL="https://api.digitalocean.com/v2/domains"
## get the domain list (DO gives basically a full XFER!) ## while we dont have a matching domain we keep going
domain_list="$(_get "$DOMURL")" while [ -z "$found" ]; do
## get the domain list (current page)
domain_list="$(_get "$DOMURL")"
## check response ## check response
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_err "error in domain_list response: $domain_list" _err "error in domain_list response: $domain_list"
return 1
fi
_debug2 domain_list "$domain_list"
## for each shortening of our $fulldomain, check if it exists in the $domain_list
## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
i=2
while [ $i -gt 0 ]; do
## get next longest domain
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
## check we got something back from our cut (or are we at the end)
if [ -z "$_domain" ]; then
## we got to the end of the domain - invalid domain
_err "domain not found in DigitalOcean account"
return 1 return 1
fi fi
## we got part of a domain back - grep it out _debug2 domain_list "$domain_list"
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
## check if it exists ## for each shortening of our $fulldomain, check if it exists in the $domain_list
if [ ! -z "$found" ]; then ## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
## exists - exit loop returning the parts i=2
sub_point=$(_math $i - 1) while [ $i -gt 0 ]; do
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point") ## get next longest domain
_debug _domain "$_domain" _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
_debug _sub_domain "$_sub_domain" ## check we got something back from our cut (or are we at the end)
return 0 if [ -z "$_domain" ]; then
break
fi
## we got part of a domain back - grep it out
found="$(echo "$domain_list" | _egrep_o "\"name\"\s*\:\s*\"$_domain\"")"
## check if it exists
if [ ! -z "$found" ]; then
## exists - exit loop returning the parts
sub_point=$(_math $i - 1)
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
_debug _domain "$_domain"
_debug _sub_domain "$_sub_domain"
return 0
fi
## increment cut point $i
i=$(_math $i + 1)
done
if [ -z "$found" ]; then
## find the next page if we dont have a match
nextpage="$(echo "$domain_list" | _egrep_o "\"links\".*" | _egrep_o "\"next\".*" | _egrep_o "http.*page\=[0-9]+")"
if [ -z "$nextpage" ]; then
_err "no record and no nextpage in digital ocean DNS removal"
return 1
fi
_debug2 nextpage "$nextpage"
DOMURL="$nextpage"
fi fi
## increment cut point $i
i=$(_math $i + 1)
done done
## we went through the entire domain zone list and dint find one that matched ## we went through the entire domain zone list and dint find one that matched

View File

@ -152,7 +152,7 @@ _get_records() {
sub_domain=$3 sub_domain=$3
_debug "fetching txt records" _debug "fetching txt records"
_dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100" _dnsimple_rest GET "$account_id/zones/$domain/records?per_page=5000&sort=id:desc"
if ! _contains "$response" "\"id\":"; then if ! _contains "$response" "\"id\":"; then
_err "failed to retrieve records" _err "failed to retrieve records"

59
dnsapi/dns_doapi.sh Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env sh
# Official Let's Encrypt API for do.de / Domain-Offensive
#
# This is different from the dns_do adapter, because dns_do is only usable for enterprise customers
# This API is also available to private customers/individuals
#
# Provide the required LetsEncrypt token like this:
# DO_LETOKEN="FmD408PdqT1E269gUK57"
DO_API="https://www.do.de/api/letsencrypt"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_doapi_add() {
fulldomain=$1
txtvalue=$2
DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}"
if [ -z "$DO_LETOKEN" ]; then
DO_LETOKEN=""
_err "You didn't configure a do.de API token yet."
_err "Please set DO_LETOKEN and try again."
return 1
fi
_saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN"
_info "Adding TXT record to ${fulldomain}"
response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&value=${txtvalue}")"
if _contains "${response}" 'success'; then
return 0
fi
_err "Could not create resource record, check logs"
_err "${response}"
return 1
}
dns_doapi_rm() {
fulldomain=$1
DO_LETOKEN="${DO_LETOKEN:-$(_readaccountconf_mutable DO_LETOKEN)}"
if [ -z "$DO_LETOKEN" ]; then
DO_LETOKEN=""
_err "You didn't configure a do.de API token yet."
_err "Please set DO_LETOKEN and try again."
return 1
fi
_saveaccountconf_mutable DO_LETOKEN "$DO_LETOKEN"
_info "Deleting resource record $fulldomain"
response="$(_get "$DO_API?token=$DO_LETOKEN&domain=${fulldomain}&action=delete")"
if _contains "${response}" 'success'; then
return 0
fi
_err "Could not delete resource record, check logs"
_err "${response}"
return 1
}

View File

@ -63,7 +63,7 @@ dns_dp_rm() {
return 0 return 0
fi fi
record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \") record_id=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2)
_debug record_id "$record_id" _debug record_id "$record_id"
if [ -z "$record_id" ]; then if [ -z "$record_id" ]; then
_err "Can not get record id." _err "Can not get record id."

161
dnsapi/dns_dpi.sh Executable file
View File

@ -0,0 +1,161 @@
#!/usr/bin/env sh
# Dnspod.com Domain api
#
#DPI_Id="1234"
#
#DPI_Key="sADDsdasdgdsf"
REST_API="https://api.dnspod.com"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dpi_add() {
fulldomain=$1
txtvalue=$2
DPI_Id="${DPI_Id:-$(_readaccountconf_mutable DPI_Id)}"
DPI_Key="${DPI_Key:-$(_readaccountconf_mutable DPI_Key)}"
if [ -z "$DPI_Id" ] || [ -z "$DPI_Key" ]; then
DPI_Id=""
DPI_Key=""
_err "You don't specify dnspod api key and key id 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 DPI_Id "$DPI_Id"
_saveaccountconf_mutable DPI_Key "$DPI_Key"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
add_record "$_domain" "$_sub_domain" "$txtvalue"
}
#fulldomain txtvalue
dns_dpi_rm() {
fulldomain=$1
txtvalue=$2
DPI_Id="${DPI_Id:-$(_readaccountconf_mutable DPI_Id)}"
DPI_Key="${DPI_Key:-$(_readaccountconf_mutable DPI_Key)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
if ! _rest POST "Record.List" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
_err "Record.Lis error."
return 1
fi
if _contains "$response" 'No records'; then
_info "Don't need to remove."
return 0
fi
record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \")
_debug record_id "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id."
return 1
fi
if ! _rest POST "Record.Remove" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
_err "Record.Remove error."
return 1
fi
_contains "$response" "Action completed successful"
}
#add the txt record.
#usage: root sub txtvalue
add_record() {
root=$1
sub=$2
txtvalue=$3
fulldomain="$sub.$root"
_info "Adding record"
if ! _rest POST "Record.Create" "user_token=$DPI_Id,$DPI_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=default"; then
return 1
fi
_contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
}
#################### 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)
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _rest POST "Domain.Info" "user_token=$DPI_Id,$DPI_Key&format=json&domain=$h"; then
return 1
fi
if _contains "$response" "Action completed successful"; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_debug _sub_domain "$_sub_domain"
_domain="$h"
_debug _domain "$_domain"
return 0
fi
return 1
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
#Usage: method URI data
_rest() {
m="$1"
ep="$2"
data="$3"
_debug "$ep"
url="$REST_API/$ep"
_debug url "$url"
if [ "$m" = "GET" ]; then
response="$(_get "$url" | tr -d '\r')"
else
_debug2 data "$data"
response="$(_post "$data" "$url" | tr -d '\r')"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@ -10,7 +10,7 @@
Dynu_Token="" Dynu_Token=""
# #
#Endpoint #Endpoint
Dynu_EndPoint="https://api.dynu.com/v1" Dynu_EndPoint="https://api.dynu.com/v2"
# #
#Author: Dynu Systems, Inc. #Author: Dynu Systems, Inc.
#Report Bugs here: https://github.com/shar0119/acme.sh #Report Bugs here: https://github.com/shar0119/acme.sh
@ -51,11 +51,11 @@ dns_dynu_add() {
_debug _domain_name "$_domain_name" _debug _domain_name "$_domain_name"
_info "Creating TXT record." _info "Creating TXT record."
if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then if ! _dynu_rest POST "dns/$dnsId/record" "{\"domainId\":\"$dnsId\",\"nodeName\":\"$_node\",\"recordType\":\"TXT\",\"textData\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then
return 1 return 1
fi fi
if ! _contains "$response" "text_data"; then if ! _contains "$response" "200"; then
_err "Could not add TXT record." _err "Could not add TXT record."
return 1 return 1
fi fi
@ -132,11 +132,12 @@ _get_root() {
return 1 return 1
fi fi
if ! _dynu_rest GET "dns/get/$h"; then if ! _dynu_rest GET "dns/getroot/$h"; then
return 1 return 1
fi fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then if _contains "$response" "\"domainName\":\"$h\"" >/dev/null; then
dnsId=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 2 | cut -d : -f 2)
_domain_name=$h _domain_name=$h
_node=$(printf "%s" "$domain" | cut -d . -f 1-$p) _node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
return 0 return 0
@ -152,7 +153,7 @@ _get_recordid() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then if ! _dynu_rest GET "dns/$dnsId/record"; then
return 1 return 1
fi fi
@ -161,19 +162,18 @@ _get_recordid() {
return 0 return 0
fi fi
_dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | _egrep_o ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2) _dns_record_id=$(printf "%s" "$response" | sed -e 's/[^{]*\({[^}]*}\)[^{]*/\1\n/g' | grep "\"textData\":\"$txtvalue\"" | sed -e 's/.*"id":\([^,]*\).*/\1/')
return 0 return 0
} }
_delete_txt_record() { _delete_txt_record() {
_dns_record_id=$1 _dns_record_id=$1
if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then if ! _dynu_rest DELETE "dns/$dnsId/record/$_dns_record_id"; then
return 1 return 1
fi fi
if ! _contains "$response" "true"; then if ! _contains "$response" "200"; then
return 1 return 1
fi fi
@ -189,7 +189,7 @@ _dynu_rest() {
export _H1="Authorization: Bearer $Dynu_Token" export _H1="Authorization: Bearer $Dynu_Token"
export _H2="Content-Type: application/json" export _H2="Content-Type: application/json"
if [ "$data" ]; then if [ "$data" ] || [ "$m" = "DELETE" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")" response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")"
else else
@ -216,8 +216,8 @@ _dynu_authentication() {
_err "Authentication failed." _err "Authentication failed."
return 1 return 1
fi fi
if _contains "$response" "accessToken"; then if _contains "$response" "access_token"; then
Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2) Dynu_Token=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
fi fi
if _contains "$Dynu_Token" "null"; then if _contains "$Dynu_Token" "null"; then
Dynu_Token="" Dynu_Token=""

358
dnsapi/dns_euserv.sh Normal file
View File

@ -0,0 +1,358 @@
#!/usr/bin/env sh
#This is the euserv.eu api wrapper for acme.sh
#
#Author: Michael Brueckner
#Report Bugs: https://www.github.com/initit/acme.sh or mbr@initit.de
#
#EUSERV_Username="username"
#
#EUSERV_Password="password"
#
# Dependencies:
# -------------
# - none -
EUSERV_Api="https://api.euserv.net"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_euserv_add() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}"
EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}"
if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then
EUSERV_Username=""
EUSERV_Password=""
_err "You don't specify euserv user and password yet."
_err "Please create your key and try again."
return 1
fi
#save the user and email to the account conf file.
_saveaccountconf_mutable EUSERV_Username "$EUSERV_Username"
_saveaccountconf_mutable EUSERV_Password "$EUSERV_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"
if ! _euserv_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
return 1
fi
}
#fulldomain txtvalue
dns_euserv_rm() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
EUSERV_Username="${EUSERV_Username:-$(_readaccountconf_mutable EUSERV_Username)}"
EUSERV_Password="${EUSERV_Password:-$(_readaccountconf_mutable EUSERV_Password)}"
if [ -z "$EUSERV_Username" ] || [ -z "$EUSERV_Password" ]; then
EUSERV_Username=""
EUSERV_Password=""
_err "You don't specify euserv user and password yet."
_err "Please create your key and try again."
return 1
fi
#save the user and email to the account conf file.
_saveaccountconf_mutable EUSERV_Username "$EUSERV_Username"
_saveaccountconf_mutable EUSERV_Password "$EUSERV_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>domain.dns_get_active_records</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>login</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>password</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>domain_id</name>
<value>
<int>%s</int>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id")
export _H1="Content-Type: text/xml"
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
_err "Error could not get txt records"
_debug "xml_content" "$xml_content"
_debug "response" "$response"
return 1
fi
if ! echo "$response" | grep '>dns_record_content<.*>'"$txtvalue"'<' >/dev/null; then
_info "Do not need to delete record"
else
# find XML block where txtvalue is in. The record_id is allways prior this line!
_endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1)
# record_id is the last <name> Tag with a number before the row _endLine, identified by </name><value><struct>
_record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '</name><value><struct>' | _tail_n 1 | sed 's/.*<name>\([0-9]*\)<\/name>.*/\1/')
_info "Deleting record"
_euserv_delete_record "$_record_id"
fi
}
#################### Private functions below ##################################
_get_root() {
domain=$1
_debug "get root"
# Just to read the domain_orders once
domain=$1
i=2
p=1
if ! _euserv_get_domain_orders; then
return 1
fi
# Get saved response with domain_orders
response="$_euserv_domain_orders"
while true; do
h=$(echo "$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=$(echo "$domain" | cut -d . -f 1-$p)
_domain="$h"
if ! _euserv_get_domain_id "$_domain"; then
_err "invalid domain"
return 1
fi
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_euserv_get_domain_orders() {
# returns: _euserv_domain_orders
_debug "get domain_orders"
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>domain.get_domain_orders</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>login</name>
<value><string>%s</string></value>
</member>
<member>
<name>password</name>
<value><string>%s</string></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$EUSERV_Username" "$EUSERV_Password")
export _H1="Content-Type: text/xml"
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
_err "Error could not get domain orders"
_debug "xml_content" "$xml_content"
_debug "response" "$response"
return 1
fi
# save response to reduce API calls
_euserv_domain_orders="$response"
return 0
}
_euserv_get_domain_id() {
# returns: _euserv_domain_id
domain=$1
_debug "get domain_id"
# find line where the domain name is within the $response
_startLine=$(echo "$_euserv_domain_orders" | grep -n '>domain_name<.*>'"$domain"'<' | cut -d ':' -f 1)
# next occurency of domain_id after the domain_name is the correct one
_euserv_domain_id=$(echo "$_euserv_domain_orders" | sed -n "$_startLine"',$p' | grep '>domain_id<' | _head_n 1 | sed 's/.*<i4>\([0-9]*\)<\/i4>.*/\1/')
if [ -z "$_euserv_domain_id" ]; then
_err "Could not find domain_id for domain $domain"
_debug "_euserv_domain_orders" "$_euserv_domain_orders"
return 1
fi
return 0
}
_euserv_delete_record() {
record_id=$1
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>domain.dns_delete_record</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>login</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>password</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>dns_record_id</name>
<value>
<int>%s</int>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$record_id")
export _H1="Content-Type: text/xml"
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
_err "Error deleting record"
_debug "xml_content" "$xml_content"
_debug "response" "$response"
return 1
fi
return 0
}
_euserv_add_record() {
domain=$1
sub_domain=$2
txtval=$3
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>domain.dns_create_record</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>login</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>password</name>
<value>
<string>%s</string></value>
</member>
<member>
<name>domain_id</name>
<value>
<int>%s</int>
</value>
</member>
<member>
<name>dns_record_subdomain</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>dns_record_type</name>
<value>
<string>TXT</string>
</value>
</member>
<member>
<name>dns_record_value</name>
<value>
<string>%s</string>
</value>
</member>
<member>
<name>dns_record_ttl</name>
<value>
<int>300</int>
</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>' "$EUSERV_Username" "$EUSERV_Password" "$_euserv_domain_id" "$sub_domain" "$txtval")
export _H1="Content-Type: text/xml"
response="$(_post "$xml_content" "$EUSERV_Api" "" "POST")"
if ! _contains "$response" "<member><name>status</name><value><i4>100</i4></value></member>"; then
_err "Error could not create record"
_debug "xml_content" "$xml_content"
_debug "response" "$response"
return 1
fi
return 0
}

168
dnsapi/dns_exoscale.sh Executable file
View File

@ -0,0 +1,168 @@
#!/usr/bin/env sh
EXOSCALE_API=https://api.exoscale.com/dns/v1
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record
dns_exoscale_add() {
fulldomain=$1
txtvalue=$2
if ! _checkAuth; 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"
_info "Adding record"
if _exoscale_rest POST "domains/$_domain_id/records" "{\"record\":{\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}}" "$_domain_token"; then
if _contains "$response" "$txtvalue"; then
_info "Added, OK"
return 0
fi
fi
_err "Add txt record error."
return 1
}
# Usage: fulldomain txtvalue
# Used to remove the txt record after validation
dns_exoscale_rm() {
fulldomain=$1
txtvalue=$2
if ! _checkAuth; 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"
_exoscale_rest GET "domains/${_domain_id}/records?type=TXT&name=$_sub_domain" "" "$_domain_token"
if _contains "$response" "\"name\":\"$_sub_domain\"" >/dev/null; then
_record_id=$(echo "$response" | tr '{' "\n" | grep "\"content\":\"$txtvalue\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
fi
if [ -z "$_record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
_debug "Deleting record $_record_id"
if ! _exoscale_rest DELETE "domains/$_domain_id/records/$_record_id" "" "$_domain_token"; then
_err "Delete record error."
return 1
fi
return 0
}
#################### Private functions below ##################################
_checkAuth() {
EXOSCALE_API_KEY="${EXOSCALE_API_KEY:-$(_readaccountconf_mutable EXOSCALE_API_KEY)}"
EXOSCALE_SECRET_KEY="${EXOSCALE_SECRET_KEY:-$(_readaccountconf_mutable EXOSCALE_SECRET_KEY)}"
if [ -z "$EXOSCALE_API_KEY" ] || [ -z "$EXOSCALE_SECRET_KEY" ]; then
EXOSCALE_API_KEY=""
EXOSCALE_SECRET_KEY=""
_err "You don't specify Exoscale application key and application secret yet."
_err "Please create you key and try again."
return 1
fi
_saveaccountconf_mutable EXOSCALE_API_KEY "$EXOSCALE_API_KEY"
_saveaccountconf_mutable EXOSCALE_SECRET_KEY "$EXOSCALE_SECRET_KEY"
return 0
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
# _domain_token=sdjkglgdfewsdfg
_get_root() {
if ! _exoscale_rest GET "domains"; then
return 1
fi
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 _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"id\":[^,]+" | _head_n 1 | cut -d : -f 2 | tr -d \")
_domain_token=$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o "\"token\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_token" ] && [ "$_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
}
# returns response
_exoscale_rest() {
method=$1
path="$2"
data="$3"
token="$4"
request_url="$EXOSCALE_API/$path"
_debug "$path"
export _H1="Accept: application/json"
if [ "$token" ]; then
export _H2="X-DNS-Domain-Token: $token"
else
export _H2="X-DNS-Token: $EXOSCALE_API_KEY:$EXOSCALE_SECRET_KEY"
fi
if [ "$data" ] || [ "$method" = "DELETE" ]; then
export _H3="Content-Type: application/json"
_debug data "$data"
response="$(_post "$data" "$request_url" "" "$method")"
else
response="$(_get "$request_url" "" "" "$method")"
fi
if [ "$?" != "0" ]; then
_err "error $request_url"
return 1
fi
_debug2 response "$response"
return 0
}

60
dnsapi/dns_gandi_livedns.sh Executable file → Normal file
View File

@ -7,6 +7,7 @@
# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
# #
#Author: Frédéric Crozat <fcrozat@suse.com> #Author: Frédéric Crozat <fcrozat@suse.com>
# Dominik Röttsches <drott@google.com>
#Report Bugs here: https://github.com/fcrozat/acme.sh #Report Bugs here: https://github.com/fcrozat/acme.sh
# #
######## Public functions ##################### ######## Public functions #####################
@ -36,9 +37,7 @@ dns_gandi_livedns_add() {
_debug domain "$_domain" _debug domain "$_domain"
_debug sub_domain "$_sub_domain" _debug sub_domain "$_sub_domain"
_gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \ _dns_gandi_append_record "$_domain" "$_sub_domain" "$txtvalue"
&& _contains "$response" '{"message": "DNS Record Created"}' \
&& _info "Add $(__green "success")"
} }
#Usage: fulldomain txtvalue #Usage: fulldomain txtvalue
@ -56,9 +55,23 @@ dns_gandi_livedns_rm() {
_debug fulldomain "$fulldomain" _debug fulldomain "$fulldomain"
_debug domain "$_domain" _debug domain "$_domain"
_debug sub_domain "$_sub_domain" _debug sub_domain "$_sub_domain"
_debug txtvalue "$txtvalue"
_gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" "" if ! _dns_gandi_existing_rrset_values "$_domain" "$_sub_domain"; then
return 1
fi
_new_rrset_values=$(echo "$_rrset_values" | sed "s/...$txtvalue...//g")
# Cleanup dangling commata.
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, ,/ ,/g")
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/, *\]/\]/g")
_new_rrset_values=$(echo "$_new_rrset_values" | sed "s/\[ *,/\[/g")
_debug "New rrset_values" "$_new_rrset_values"
_gandi_livedns_rest PUT \
"domains/$_domain/records/$_sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" \
&& _contains "$response" '{"message": "DNS Record Created"}' \
&& _info "Removing record $(__green "success")"
} }
#################### Private functions below ################################## #################### Private functions below ##################################
@ -98,6 +111,45 @@ _get_root() {
return 1 return 1
} }
_dns_gandi_append_record() {
domain=$1
sub_domain=$2
txtvalue=$3
if _dns_gandi_existing_rrset_values "$domain" "$sub_domain"; then
_debug "Appending new value"
_rrset_values=$(echo "$_rrset_values" | sed "s/\"]/\",\"$txtvalue\"]/")
else
_debug "Creating new record" "$_rrset_values"
_rrset_values="[\"$txtvalue\"]"
fi
_debug new_rrset_values "$_rrset_values"
_gandi_livedns_rest PUT "domains/$_domain/records/$sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_rrset_values}" \
&& _contains "$response" '{"message": "DNS Record Created"}' \
&& _info "Adding record $(__green "success")"
}
_dns_gandi_existing_rrset_values() {
domain=$1
sub_domain=$2
if ! _gandi_livedns_rest GET "domains/$domain/records/$sub_domain"; then
return 1
fi
if ! _contains "$response" '"rrset_type": "TXT"'; then
_debug "Does not have a _acme-challenge TXT record yet."
return 1
fi
if _contains "$response" '"rrset_values": \[\]'; then
_debug "Empty rrset_values for TXT record, no previous TXT record."
return 1
fi
_debug "Already has TXT record."
_rrset_values=$(echo "$response" | _egrep_o 'rrset_values.*\[.*\]' \
| _egrep_o '\[".*\"]')
return 0
}
_gandi_livedns_rest() { _gandi_livedns_rest() {
m=$1 m=$1
ep="$2" ep="$2"

167
dnsapi/dns_gcloud.sh Executable file
View File

@ -0,0 +1,167 @@
#!/usr/bin/env sh
# Author: Janos Lenart <janos@lenart.io>
######## Public functions #####################
# Usage: dns_gcloud_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gcloud_add() {
fulldomain=$1
txtvalue=$2
_info "Using gcloud"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_dns_gcloud_find_zone || return $?
# Add an extra RR
_dns_gcloud_start_tr || return $?
_dns_gcloud_get_rrdatas || return $?
echo "$rrdatas" | _dns_gcloud_remove_rrs || return $?
printf "%s\n%s\n" "$rrdatas" "\"$txtvalue\"" | grep -v '^$' | _dns_gcloud_add_rrs || return $?
_dns_gcloud_execute_tr || return $?
_info "$fulldomain record added"
}
# Usage: dns_gcloud_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Remove the txt record after validation.
dns_gcloud_rm() {
fulldomain=$1
txtvalue=$2
_info "Using gcloud"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_dns_gcloud_find_zone || return $?
# Remove one RR
_dns_gcloud_start_tr || return $?
_dns_gcloud_get_rrdatas || return $?
echo "$rrdatas" | _dns_gcloud_remove_rrs || return $?
echo "$rrdatas" | grep -F -v "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
_dns_gcloud_execute_tr || return $?
_info "$fulldomain record added"
}
#################### Private functions below ##################################
_dns_gcloud_start_tr() {
if ! trd=$(mktemp -d); then
_err "_dns_gcloud_start_tr: failed to create temporary directory"
return 1
fi
tr="$trd/tr.yaml"
_debug tr "$tr"
if ! gcloud dns record-sets transaction start \
--transaction-file="$tr" \
--zone="$managedZone"; then
rm -r "$trd"
_err "_dns_gcloud_start_tr: failed to execute transaction"
return 1
fi
}
_dns_gcloud_execute_tr() {
if ! gcloud dns record-sets transaction execute \
--transaction-file="$tr" \
--zone="$managedZone"; then
_debug tr "$(cat "$tr")"
rm -r "$trd"
_err "_dns_gcloud_execute_tr: failed to execute transaction"
return 1
fi
rm -r "$trd"
for i in $(seq 1 120); do
if gcloud dns record-sets changes list \
--zone="$managedZone" \
--filter='status != done' \
| grep -q '^.*'; then
_info "_dns_gcloud_execute_tr: waiting for transaction to be comitted ($i/120)..."
sleep 5
else
return 0
fi
done
_err "_dns_gcloud_execute_tr: transaction is still pending after 10 minutes"
rm -r "$trd"
return 1
}
_dns_gcloud_remove_rrs() {
if ! xargs --no-run-if-empty gcloud dns record-sets transaction remove \
--name="$fulldomain." \
--ttl="$ttl" \
--type=TXT \
--zone="$managedZone" \
--transaction-file="$tr"; then
_debug tr "$(cat "$tr")"
rm -r "$trd"
_err "_dns_gcloud_remove_rrs: failed to remove RRs"
return 1
fi
}
_dns_gcloud_add_rrs() {
ttl=60
if ! xargs --no-run-if-empty gcloud dns record-sets transaction add \
--name="$fulldomain." \
--ttl="$ttl" \
--type=TXT \
--zone="$managedZone" \
--transaction-file="$tr"; then
_debug tr "$(cat "$tr")"
rm -r "$trd"
_err "_dns_gcloud_add_rrs: failed to add RRs"
return 1
fi
}
_dns_gcloud_find_zone() {
# Prepare a filter that matches zones that are suiteable for this entry.
# For example, _acme-challenge.something.domain.com might need to go into something.domain.com or domain.com;
# this function finds the longest postfix that has a managed zone.
part="$fulldomain"
filter="dnsName=( "
while [ "$part" != "" ]; do
filter="$filter$part. "
part="$(echo "$part" | sed 's/[^.]*\.*//')"
done
filter="$filter)"
_debug filter "$filter"
# List domains and find the longest match (in case of some levels of delegation)
if ! match=$(gcloud dns managed-zones list \
--format="value(name, dnsName)" \
--filter="$filter" \
| while read -r dnsName name; do
printf "%s\t%s\t%s\n" "${#dnsName}" "$dnsName" "$name"
done \
| sort -n -r | _head_n 1 | cut -f2,3 | grep '^.*'); then
_err "_dns_gcloud_find_zone: Can't find a matching managed zone! Perhaps wrong project or gcloud credentials?"
return 1
fi
dnsName=$(echo "$match" | cut -f2)
_debug dnsName "$dnsName"
managedZone=$(echo "$match" | cut -f1)
_debug managedZone "$managedZone"
}
_dns_gcloud_get_rrdatas() {
if ! rrdatas=$(gcloud dns record-sets list \
--zone="$managedZone" \
--name="$fulldomain." \
--type=TXT \
--format="value(ttl,rrdatas)"); then
_err "_dns_gcloud_get_rrdatas: Failed to list record-sets"
rm -r "$trd"
return 1
fi
ttl=$(echo "$rrdatas" | cut -f1)
rrdatas=$(echo "$rrdatas" | cut -f2 | sed 's/","/"\n"/g')
}

168
dnsapi/dns_gdnsdk.sh Executable file
View File

@ -0,0 +1,168 @@
#!/usr/bin/env sh
#Author: Herman Sletteng
#Report Bugs here: https://github.com/loial/acme.sh
#
#
# Note, gratisdns requires a login first, so the script needs to handle
# temporary cookies. Since acme.sh _get/_post currently don't directly support
# cookies, I've defined wrapper functions _myget/_mypost to set the headers
GDNSDK_API="https://admin.gratisdns.com"
######## Public functions #####################
#Usage: dns_gdnsdk_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gdnsdk_add() {
fulldomain=$1
txtvalue=$2
_info "Using gratisdns.dk"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _gratisdns_login; then
_err "Login failed!"
return 1
fi
#finding domain zone
if ! _get_domain; then
_err "No matching root domain for $fulldomain found"
return 1
fi
# adding entry
_info "Adding the entry"
_mypost "action=dns_primary_record_added_txt&user_domain=$_domain&name=$fulldomain&txtdata=$txtvalue&ttl=1"
if _successful_update; then return 0; fi
_err "Couldn't create entry!"
return 1
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_gdnsdk_rm() {
fulldomain=$1
txtvalue=$2
_info "Using gratisdns.dk"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _gratisdns_login; then
_err "Login failed!"
return 1
fi
if ! _get_domain; then
_err "No matching root domain for $fulldomain found"
return 1
fi
_findentry "$fulldomain" "$txtvalue"
if [ -z "$_id" ]; then
_info "Entry doesn't exist, nothing to delete"
return 0
fi
_debug "Deleting record..."
_mypost "action=dns_primary_delete_txt&user_domain=$_domain&id=$_id"
# removing entry
if _successful_update; then return 0; fi
_err "Couldn't delete entry!"
return 1
}
#################### Private functions below ##################################
_checkcredentials() {
GDNSDK_Username="${GDNSDK_Username:-$(_readaccountconf_mutable GDNSDK_Username)}"
GDNSDK_Password="${GDNSDK_Password:-$(_readaccountconf_mutable GDNSDK_Password)}"
if [ -z "$GDNSDK_Username" ] || [ -z "$GDNSDK_Password" ]; then
GDNSDK_Username=""
GDNSDK_Password=""
_err "You haven't specified gratisdns.dk username and password yet."
_err "Please add credentials and try again."
return 1
fi
#save the credentials to the account conf file.
_saveaccountconf_mutable GDNSDK_Username "$GDNSDK_Username"
_saveaccountconf_mutable GDNSDK_Password "$GDNSDK_Password"
return 0
}
_checkcookie() {
GDNSDK_Cookie="${GDNSDK_Cookie:-$(_readaccountconf_mutable GDNSDK_Cookie)}"
if [ -z "$GDNSDK_Cookie" ]; then
_debug "No cached cookie found"
return 1
fi
_myget "action="
if (echo "$_result" | grep -q "logmeout"); then
_debug "Cached cookie still valid"
return 0
fi
_debug "Cached cookie no longer valid"
GDNSDK_Cookie=""
_saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
return 1
}
_gratisdns_login() {
if ! _checkcredentials; then return 1; fi
if _checkcookie; then
_debug "Already logged in"
return 0
fi
_debug "Logging into GratisDNS with user $GDNSDK_Username"
if ! _mypost "login=$GDNSDK_Username&password=$GDNSDK_Password&action=logmein"; then
_err "GratisDNS login failed for user $GDNSDK_Username bad RC from _post"
return 1
fi
GDNSDK_Cookie="$(grep -A 15 '302 Found' "$HTTP_HEADER" | _egrep_o 'Cookie: [^;]*' | _head_n 1 | cut -d ' ' -f2)"
if [ -z "$GDNSDK_Cookie" ]; then
_err "GratisDNS login failed for user $GDNSDK_Username. Check $HTTP_HEADER file"
return 1
fi
export GDNSDK_Cookie
_saveaccountconf_mutable GDNSDK_Cookie "$GDNSDK_Cookie"
return 0
}
_myget() {
#Adds cookie to request
export _H1="Cookie: $GDNSDK_Cookie"
_result=$(_get "$GDNSDK_API?$1")
}
_mypost() {
#Adds cookie to request
export _H1="Cookie: $GDNSDK_Cookie"
_result=$(_post "$1" "$GDNSDK_API")
}
_get_domain() {
_myget 'action=dns_primarydns'
_domains=$(echo "$_result" | _egrep_o ' domain="[[:alnum:]._-]+' | sed 's/^.*"//')
if [ -z "$_domains" ]; then
_err "Primary domain list not found!"
return 1
fi
for _domain in $_domains; do
if (_endswith "$fulldomain" "$_domain"); then
_debug "Root domain: $_domain"
return 0
fi
done
return 1
}
_successful_update() {
if (echo "$_result" | grep -q 'table-success'); then return 0; fi
return 1
}
_findentry() {
#returns id of dns entry, if it exists
_myget "action=dns_primary_changeDNSsetup&user_domain=$_domain"
_id=$(echo "$_result" | _egrep_o "<td>$1</td>\s*<td>$2</td>[^?]*[^&]*&id=[^&]*" | sed 's/^.*=//')
if [ -n "$_id" ]; then
_debug "Entry found with _id=$_id"
return 0
fi
return 1
}

View File

@ -92,7 +92,9 @@ dns_he_rm() {
return 1 return 1
fi fi
# Remove the record # Remove the record
body="email=${HE_Username}&pass=${HE_Password}" username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)"
password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)"
body="email=${username_encoded}&pass=${password_encoded}"
body="$body&menu=edit_zone" body="$body&menu=edit_zone"
body="$body&hosted_dns_zoneid=$_zone_id" body="$body&hosted_dns_zoneid=$_zone_id"
body="$body&hosted_dns_recordid=$_record_id" body="$body&hosted_dns_recordid=$_record_id"

162
dnsapi/dns_hostingde.sh Normal file
View File

@ -0,0 +1,162 @@
#!/usr/bin/env sh
# hosting.de API
# Values to export:
# export HOSTINGDE_ENDPOINT='https://secure.hosting.de'
# export HOSTINGDE_APIKEY='xxxxx'
######## Public functions #####################
dns_hostingde_add() {
fulldomain="${1}"
txtvalue="${2}"
_debug "Calling: _hostingde_addRecord() '${fulldomain}' '${txtvalue}'"
_hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_addRecord
return $?
}
dns_hostingde_rm() {
fulldomain="${1}"
txtvalue="${2}"
_debug "Calling: _hostingde_removeRecord() '${fulldomain}' '${txtvalue}'"
_hostingde_apiKey && _hostingde_getZoneConfig && _hostingde_removeRecord
return $?
}
#################### own Private functions below ##################################
_hostingde_apiKey() {
HOSTINGDE_APIKEY="${HOSTINGDE_APIKEY:-$(_readaccountconf_mutable HOSTINGDE_APIKEY)}"
HOSTINGDE_ENDPOINT="${HOSTINGDE_ENDPOINT:-$(_readaccountconf_mutable HOSTINGDE_ENDPOINT)}"
if [ -z "$HOSTINGDE_APIKEY" ] || [ -z "$HOSTINGDE_ENDPOINT" ]; then
HOSTINGDE_APIKEY=""
HOSTINGDE_ENDPOINT=""
_err "You haven't specified hosting.de API key or endpoint yet."
_err "Please create your key and try again."
return 1
fi
_saveaccountconf_mutable HOSTINGDE_APIKEY "$HOSTINGDE_APIKEY"
_saveaccountconf_mutable HOSTINGDE_ENDPOINT "$HOSTINGDE_ENDPOINT"
}
_hostingde_parse() {
find="${1}"
if [ "${2}" ]; then
notfind="${2}"
fi
if [ "${notfind}" ]; then
_egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d ' '
else
_egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 | tr -d ' '
fi
}
_hostingde_getZoneConfig() {
_info "Getting ZoneConfig"
curZone="${fulldomain#*.}"
returnCode=1
while _contains "${curZone}" "\\."; do
curData="{\"filter\":{\"field\":\"zoneName\",\"value\":\"${curZone}\"},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneConfigsFind")"
_debug "Calling zoneConfigsFind: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneConfigsFind'"
_debug "Result of zoneConfigsFind: '$curResult'"
if _contains "${curResult}" '"status": "error"'; then
if _contains "${curResult}" '"code": 10109'; then
_err "The API-Key is invalid or could not be found"
else
_err "UNKNOWN API ERROR"
fi
returnCode=1
break
fi
if _contains "${curResult}" '"totalEntries": 1'; then
_info "Retrieved zone data."
_debug "Zone data: '${curResult}'"
zoneConfigId=$(echo "${curResult}" | _hostingde_parse "id")
zoneConfigName=$(echo "${curResult}" | _hostingde_parse "name")
zoneConfigType=$(echo "${curResult}" | _hostingde_parse "type" "FindZoneConfigsResult")
zoneConfigExpire=$(echo "${curResult}" | _hostingde_parse "expire")
zoneConfigNegativeTtl=$(echo "${curResult}" | _hostingde_parse "negativeTtl")
zoneConfigRefresh=$(echo "${curResult}" | _hostingde_parse "refresh")
zoneConfigRetry=$(echo "${curResult}" | _hostingde_parse "retry")
zoneConfigTtl=$(echo "${curResult}" | _hostingde_parse "ttl")
zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId")
zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress")
zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode")
if [ "${zoneConfigType}" != "\"NATIVE\"" ]; then
_err "Zone is not native"
returnCode=1
break
fi
_debug "zoneConfigId '${zoneConfigId}'"
returnCode=0
break
fi
curZone="${curZone#*.}"
done
if [ $returnCode -ne 0 ]; then
_info "ZoneEnd reached, Zone ${curZone} not found in hosting.de API"
fi
return $returnCode
}
_hostingde_getZoneStatus() {
_debug "Checking Zone status"
curData="{\"filter\":{\"field\":\"zoneConfigId\",\"value\":${zoneConfigId}},\"limit\":1,\"authToken\":\"${HOSTINGDE_APIKEY}\"}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind")"
_debug "Calling zonesFind '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zonesFind'"
_debug "Result of zonesFind '$curResult'"
zoneStatus=$(echo "${curResult}" | _hostingde_parse "status" "success")
_debug "zoneStatus '${zoneStatus}'"
return 0
}
_hostingde_addRecord() {
_info "Adding record to zone"
_hostingde_getZoneStatus
_debug "Result of zoneStatus: '${zoneStatus}'"
while [ "${zoneStatus}" != "\"active\"" ]; do
_sleep 5
_hostingde_getZoneStatus
_debug "Result of zoneStatus: '${zoneStatus}'"
done
curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToAdd\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\",\"ttl\":3600}]}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")"
_debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'"
_debug "Result of zoneUpdate: '$curResult'"
if _contains "${curResult}" '"status": "error"'; then
if _contains "${curResult}" '"code": 10109'; then
_err "The API-Key is invalid or could not be found"
else
_err "UNKNOWN API ERROR"
fi
return 1
fi
return 0
}
_hostingde_removeRecord() {
_info "Removing record from zone"
_hostingde_getZoneStatus
_debug "Result of zoneStatus: '$zoneStatus'"
while [ "$zoneStatus" != "\"active\"" ]; do
_sleep 5
_hostingde_getZoneStatus
_debug "Result of zoneStatus: '$zoneStatus'"
done
curData="{\"authToken\":\"${HOSTINGDE_APIKEY}\",\"zoneConfig\":{\"id\":${zoneConfigId},\"name\":${zoneConfigName},\"type\":${zoneConfigType},\"dnsServerGroupId\":${zoneConfigDnsServerGroupId},\"dnsSecMode\":${zoneConfigDnsSecMode},\"emailAddress\":${zoneConfigEmailAddress},\"soaValues\":{\"expire\":${zoneConfigExpire},\"negativeTtl\":${zoneConfigNegativeTtl},\"refresh\":${zoneConfigRefresh},\"retry\":${zoneConfigRetry},\"ttl\":${zoneConfigTtl}}},\"recordsToDelete\":[{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"\\\"${txtvalue}\\\"\"}]}"
curResult="$(_post "${curData}" "${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate")"
_debug "Calling zoneUpdate: '${curData}' '${HOSTINGDE_ENDPOINT}/api/dns/v1/json/zoneUpdate'"
_debug "Result of zoneUpdate: '$curResult'"
if _contains "${curResult}" '"status": "error"'; then
if _contains "${curResult}" '"code": 10109'; then
_err "The API-Key is invalid or could not be found"
else
_err "UNKNOWN API ERROR"
fi
return 1
fi
return 0
}

View File

@ -158,7 +158,8 @@ _inwx_login() {
export _H1 export _H1
#https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71 #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71
if _contains "$response" "tfa"; then if _contains "$response" "<member><name>code</name><value><int>1000</int></value></member>" \
&& _contains "$response" "<member><name>tfa</name><value><string>GOOGLE-AUTH</string></value></member>"; then
if [ -z "$INWX_Shared_Secret" ]; then if [ -z "$INWX_Shared_Secret" ]; then
_err "Mobile TAN detected." _err "Mobile TAN detected."
_err "Please define a shared secret." _err "Please define a shared secret."

View File

@ -128,7 +128,7 @@ _ISPC_addTxt() {
curSerial="$(date +%s)" curSerial="$(date +%s)"
curStamp="$(date +'%F %T')" curStamp="$(date +'%F %T')"
params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\"" params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\""
curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}}}" curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}},\"update_serial\":true}"
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")" curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")"
_debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'" _debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'"
_debug "Result of _ISPC_addTxt: '$curResult'" _debug "Result of _ISPC_addTxt: '$curResult'"
@ -160,7 +160,7 @@ _ISPC_rmTxt() {
*) *)
unset IFS unset IFS
_info "Retrieved Record ID." _info "Retrieved Record ID."
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\"}" curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\",\"update_serial\":true}"
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")" curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")"
_debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'" _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'"
_debug "Result of _ISPC_rmTxt: '$curResult'" _debug "Result of _ISPC_rmTxt: '$curResult'"

View File

@ -7,20 +7,13 @@ lexicon_cmd="lexicon"
wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api" wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api"
######## Public functions ##################### _lexicon_init() {
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_lexicon_add() {
fulldomain=$1
txtvalue=$2
domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
if ! _exists "$lexicon_cmd"; then if ! _exists "$lexicon_cmd"; then
_err "Please install $lexicon_cmd first: $wiki" _err "Please install $lexicon_cmd first: $wiki"
return 1 return 1
fi fi
PROVIDER="${PROVIDER:-$(_readdomainconf PROVIDER)}"
if [ -z "$PROVIDER" ]; then if [ -z "$PROVIDER" ]; then
PROVIDER="" PROVIDER=""
_err "Please define env PROVIDER first: $wiki" _err "Please define env PROVIDER first: $wiki"
@ -33,46 +26,78 @@ dns_lexicon_add() {
# e.g. busybox-ash does not know [:upper:] # e.g. busybox-ash does not know [:upper:]
# shellcheck disable=SC2018,SC2019 # shellcheck disable=SC2018,SC2019
Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z') Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z')
eval "$Lx_name=\${$Lx_name:-$(_readaccountconf_mutable "$Lx_name")}"
Lx_name_v=$(eval echo \$"$Lx_name") Lx_name_v=$(eval echo \$"$Lx_name")
_secure_debug "$Lx_name" "$Lx_name_v" _secure_debug "$Lx_name" "$Lx_name_v"
if [ "$Lx_name_v" ]; then if [ "$Lx_name_v" ]; then
_saveaccountconf "$Lx_name" "$Lx_name_v" _saveaccountconf_mutable "$Lx_name" "$Lx_name_v"
eval export "$Lx_name" eval export "$Lx_name"
fi fi
# shellcheck disable=SC2018,SC2019 # shellcheck disable=SC2018,SC2019
Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z') Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z')
eval "$Lx_token=\${$Lx_token:-$(_readaccountconf_mutable "$Lx_token")}"
Lx_token_v=$(eval echo \$"$Lx_token") Lx_token_v=$(eval echo \$"$Lx_token")
_secure_debug "$Lx_token" "$Lx_token_v" _secure_debug "$Lx_token" "$Lx_token_v"
if [ "$Lx_token_v" ]; then if [ "$Lx_token_v" ]; then
_saveaccountconf "$Lx_token" "$Lx_token_v" _saveaccountconf_mutable "$Lx_token" "$Lx_token_v"
eval export "$Lx_token" eval export "$Lx_token"
fi fi
# shellcheck disable=SC2018,SC2019 # shellcheck disable=SC2018,SC2019
Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z') Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z')
eval "$Lx_password=\${$Lx_password:-$(_readaccountconf_mutable "$Lx_password")}"
Lx_password_v=$(eval echo \$"$Lx_password") Lx_password_v=$(eval echo \$"$Lx_password")
_secure_debug "$Lx_password" "$Lx_password_v" _secure_debug "$Lx_password" "$Lx_password_v"
if [ "$Lx_password_v" ]; then if [ "$Lx_password_v" ]; then
_saveaccountconf "$Lx_password" "$Lx_password_v" _saveaccountconf_mutable "$Lx_password" "$Lx_password_v"
eval export "$Lx_password" eval export "$Lx_password"
fi fi
# shellcheck disable=SC2018,SC2019 # shellcheck disable=SC2018,SC2019
Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z') Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z')
eval "$Lx_domaintoken=\${$Lx_domaintoken:-$(_readaccountconf_mutable "$Lx_domaintoken")}"
Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken") Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken")
_secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v" _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v"
if [ "$Lx_domaintoken_v" ]; then if [ "$Lx_domaintoken_v" ]; then
_saveaccountconf_mutable "$Lx_domaintoken" "$Lx_domaintoken_v"
eval export "$Lx_domaintoken" eval export "$Lx_domaintoken"
_saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v" fi
}
######## Public functions #####################
#Usage: dns_lexicon_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_lexicon_add() {
fulldomain=$1
txtvalue=$2
if ! _lexicon_init; then
return 1
fi fi
$lexicon_cmd "$PROVIDER" create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
_secure_debug LEXICON_OPTS "$LEXICON_OPTS"
_savedomainconf LEXICON_OPTS "$LEXICON_OPTS"
# shellcheck disable=SC2086
$lexicon_cmd "$PROVIDER" $LEXICON_OPTS create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}"
} }
#fulldomain #Usage: dns_lexicon_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_lexicon_rm() { dns_lexicon_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
if ! _lexicon_init; then
return 1
fi
domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
# shellcheck disable=SC2086
$lexicon_cmd "$PROVIDER" $LEXICON_OPTS delete "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}"
} }

185
dnsapi/dns_linode_v4.sh Executable file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#Original Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net>
#v4 Update Author: Aaron W. Swenson <aaron@grandmasfridge.org>
LINODE_V4_API_URL="https://api.linode.com/v4/domains"
######## Public functions #####################
#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_linode_v4_add() {
fulldomain="${1}"
txtvalue="${2}"
if ! _Linode_API; then
return 1
fi
_info "Using Linode"
_debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "Domain does not exist."
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_payload="{
\"type\": \"TXT\",
\"name\": \"$_sub_domain\",
\"target\": \"$txtvalue\"
}"
if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then
_resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
_debug _resource_id "$_resource_id"
if [ -z "$_resource_id" ]; then
_err "Error adding the domain resource."
return 1
fi
_info "Domain resource successfully added."
return 0
fi
return 1
}
#Usage: dns_linode_rm _acme-challenge.www.domain.com
dns_linode_v4_rm() {
fulldomain="${1}"
if ! _Linode_API; then
return 1
fi
_info "Using Linode"
_debug "Calling: dns_linode_rm() '${fulldomain}'"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "Domain does not exist."
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then
response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
resource="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$_sub_domain\".*}")"
if [ "$resource" ]; then
_resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_resource_id" ]; then
_debug _resource_id "$_resource_id"
if _rest DELETE "/$_domain_id/records/$_resource_id" && [ -n "$response" ]; then
# On 200/OK, empty set is returned. Check for error, if any.
_error_response=$(printf "%s\n" "$response" | _egrep_o "\"errors\"" | cut -d : -f 2 | tr -d " " | _head_n 1)
if [ -n "$_error_response" ]; then
_err "Error deleting the domain resource: $_error_response"
return 1
fi
_info "Domain resource successfully deleted."
return 0
fi
fi
return 1
fi
return 0
fi
return 1
}
#################### Private functions below ##################################
_Linode_API() {
if [ -z "$LINODE_V4_API_KEY" ]; then
LINODE_V4_API_KEY=""
_err "You didn't specify the Linode v4 API key yet."
_err "Please create your key and try again."
return 1
fi
_saveaccountconf LINODE_V4_API_KEY "$LINODE_V4_API_KEY"
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=12345
_get_root() {
domain=$1
i=2
p=1
if _rest GET; then
response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
hostedzone="$(echo "$response" | _egrep_o "{.*\"domain\":\s*\"$h\".*}")"
if [ "$hostedzone" ]; then
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _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
fi
return 1
}
#method method action data
_rest() {
mtd="$1"
ep="$2"
data="$3"
_debug mtd "$mtd"
_debug ep "$ep"
export _H1="Accept: application/json"
export _H2="Content-Type: application/json"
export _H3="Authorization: Bearer $LINODE_V4_API_KEY"
if [ "$mtd" != "GET" ]; then
# both POST and DELETE.
_debug data "$data"
response="$(_post "$data" "$LINODE_V4_API_URL$ep" "" "$mtd")"
else
response="$(_get "$LINODE_V4_API_URL$ep$data")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

View File

@ -38,8 +38,8 @@ dns_loopia_add() {
_info "Adding record" _info "Adding record"
_loopia_add_record "$_domain" "$_sub_domain" _loopia_add_sub_domain "$_domain" "$_sub_domain"
_loopia_update_record "$_domain" "$_sub_domain" "$txtvalue" _loopia_add_record "$_domain" "$_sub_domain" "$txtvalue"
} }
@ -96,6 +96,37 @@ dns_loopia_rm() {
#################### Private functions below ################################## #################### Private functions below ##################################
_loopia_get_records() {
domain=$1
sub_domain=$2
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>getZoneRecords</methodName>
<params>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain" "$sub_domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if ! _contains "$response" "<array>"; then
_err "Error"
return 1
fi
return 0
}
_get_root() { _get_root() {
domain=$1 domain=$1
_debug "get root" _debug "get root"
@ -137,14 +168,14 @@ _get_root() {
} }
_loopia_update_record() { _loopia_add_record() {
domain=$1 domain=$1
sub_domain=$2 sub_domain=$2
txtval=$3 txtval=$3
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall> <methodCall>
<methodName>updateZoneRecord</methodName> <methodName>addZoneRecord</methodName>
<params> <params>
<param> <param>
<value><string>%s</string></value> <value><string>%s</string></value>
@ -176,10 +207,6 @@ _loopia_update_record() {
<name>rdata</name> <name>rdata</name>
<value><string>%s</string></value> <value><string>%s</string></value>
</member> </member>
<member>
<name>record_id</name>
<value><int>0</int></value>
</member>
</struct> </struct>
</param> </param>
</params> </params>
@ -194,10 +221,42 @@ _loopia_update_record() {
return 0 return 0
} }
_loopia_add_record() { _sub_domain_exists() {
domain=$1 domain=$1
sub_domain=$2 sub_domain=$2
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>getSubdomains</methodName>
<params>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
<param>
<value><string>%s</string></value>
</param>
</params>
</methodCall>' $LOOPIA_User $LOOPIA_Password "$domain")
response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
if _contains "$response" "$sub_domain"; then
return 0
fi
return 1
}
_loopia_add_sub_domain() {
domain=$1
sub_domain=$2
if _sub_domain_exists "$domain" "$sub_domain"; then
return 0
fi
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?> xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
<methodCall> <methodCall>
<methodName>addSubdomain</methodName> <methodName>addSubdomain</methodName>

97
dnsapi/dns_mydevil.sh Executable file
View File

@ -0,0 +1,97 @@
#!/usr/bin/env sh
# MyDevil.net API (2019-02-03)
#
# MyDevil.net already supports automatic Let's Encrypt certificates,
# except for wildcard domains.
#
# This script depends on `devil` command that MyDevil.net provides,
# which means that it works only on server side.
#
# Author: Marcin Konicki <https://ahwayakchih.neoni.net>
#
######## Public functions #####################
#Usage: dns_mydevil_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_mydevil_add() {
fulldomain=$1
txtvalue=$2
domain=""
if ! _exists "devil"; then
_err "Could not find 'devil' command."
return 1
fi
_info "Using mydevil"
domain=$(mydevil_get_domain "$fulldomain")
if [ -z "$domain" ]; then
_err "Invalid domain name: could not find root domain of $fulldomain."
return 1
fi
# No need to check if record name exists, `devil` always adds new record.
# In worst case scenario, we end up with multiple identical records.
_info "Adding $fulldomain record for domain $domain"
if devil dns add "$domain" "$fulldomain" TXT "$txtvalue"; then
_info "Successfully added TXT record, ready for validation."
return 0
else
_err "Unable to add DNS record."
return 1
fi
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_mydevil_rm() {
fulldomain=$1
txtvalue=$2
domain=""
if ! _exists "devil"; then
_err "Could not find 'devil' command."
return 1
fi
_info "Using mydevil"
domain=$(mydevil_get_domain "$fulldomain")
if [ -z "$domain" ]; then
_err "Invalid domain name: could not find root domain of $fulldomain."
return 1
fi
# catch one or more numbers
num='[0-9][0-9]*'
# catch one or more whitespace
w=$(printf '[\t ][\t ]*')
# catch anything, except newline
any='.*'
# filter to make sure we do not delete other records
validRecords="^${num}${w}${fulldomain}${w}TXT${w}${any}${txtvalue}$"
for id in $(devil dns list "$domain" | tail -n+2 | grep "${validRecords}" | cut -w -s -f 1); do
_info "Removing record $id from domain $domain"
devil dns del "$domain" "$id" || _err "Could not remove DNS record."
done
}
#################### Private functions below ##################################
# Usage: domain=$(mydevil_get_domain "_acme-challenge.www.domain.com" || _err "Invalid domain name")
# echo $domain
mydevil_get_domain() {
fulldomain=$1
domain=""
for domain in $(devil dns list | cut -w -s -f 1 | tail -n+2); do
if _endswith "$fulldomain" "$domain"; then
printf -- "%s" "$domain"
return 0
fi
done
return 1
}

210
dnsapi/dns_mydnsjp.sh Executable file
View File

@ -0,0 +1,210 @@
#!/usr/bin/env sh
#Here is a api script for MyDNS.JP.
#This file name is "dns_mydnsjp.sh"
#So, here must be a method dns_mydnsjp_add()
#Which will be called by acme.sh to add the txt record to your api system.
#returns 0 means success, otherwise error.
#
#Author: epgdatacapbon
#Report Bugs here: https://github.com/epgdatacapbon/acme.sh
#
######## Public functions #####################
# Export MyDNS.JP MasterID and Password in following variables...
# MYDNSJP_MasterID=MasterID
# MYDNSJP_Password=Password
MYDNSJP_API="https://www.mydns.jp"
#Usage: dns_mydnsjp_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_mydnsjp_add() {
fulldomain=$1
txtvalue=$2
_info "Using mydnsjp"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
# Load the credentials from the account conf file
MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}"
MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}"
if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then
MYDNSJP_MasterID=""
MYDNSJP_Password=""
_err "You don't specify mydnsjp api MasterID and Password yet."
_err "Please export as MYDNSJP_MasterID / MYDNSJP_Password and try again."
return 1
fi
# Save the credentials to the account conf file
_saveaccountconf_mutable MYDNSJP_MasterID "$MYDNSJP_MasterID"
_saveaccountconf_mutable MYDNSJP_Password "$MYDNSJP_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 _mydnsjp_api "REGIST" "$_domain" "$txtvalue"; 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_mydnsjp_rm() {
fulldomain=$1
txtvalue=$2
_info "Removing TXT record"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
# Load the credentials from the account conf file
MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}"
MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}"
if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then
MYDNSJP_MasterID=""
MYDNSJP_Password=""
_err "You don't specify mydnsjp api MasterID and Password yet."
_err "Please export as MYDNSJP_MasterID / MYDNSJP_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 _mydnsjp_api "DELETE" "$_domain" "$txtvalue"; then
if printf -- "%s" "$response" | grep "OK." >/dev/null; then
_info "Deleted, OK"
return 0
else
_err "Delete txt record error."
return 1
fi
fi
_err "Delete txt record error."
return 1
}
#################### 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
# Get the root domain
_mydnsjp_retrieve_domain
if [ "$?" != "0" ]; then
# not valid
return 1
fi
while true; do
_domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
if [ -z "$_domain" ]; then
# not valid
return 1
fi
if [ "$_domain" = "$_root_domain" ]; then
_sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
# Retrieve the root domain
# returns 0 success
_mydnsjp_retrieve_domain() {
_debug "Login to MyDNS.JP"
response="$(_post "masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/?MENU=100")"
cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
# If cookies is not empty then logon successful
if [ -z "$cookie" ]; then
_err "Fail to get a cookie."
return 1
fi
_debug "Retrieve DOMAIN INFO page"
export _H1="Cookie:${cookie}"
response="$(_get "$MYDNSJP_API/?MENU=300")"
if [ "$?" != "0" ]; then
_err "Fail to retrieve DOMAIN INFO."
return 1
fi
_root_domain=$(echo "$response" | grep "DNSINFO\[domainname\]" | sed 's/^.*value="\([^"]*\)".*/\1/')
# Logout
response="$(_get "$MYDNSJP_API/?MENU=090")"
_debug _root_domain "$_root_domain"
if [ -z "$_root_domain" ]; then
_err "Fail to get the root domain."
return 1
fi
return 0
}
_mydnsjp_api() {
cmd=$1
domain=$2
txtvalue=$3
# Base64 encode the credentials
credentials=$(printf "%s:%s" "$MYDNSJP_MasterID" "$MYDNSJP_Password" | _base64)
# Construct the HTTP Authorization header
export _H1="Content-Type: application/x-www-form-urlencoded"
export _H2="Authorization: Basic ${credentials}"
response="$(_post "CERTBOT_DOMAIN=$domain&CERTBOT_VALIDATION=$txtvalue&EDIT_CMD=$cmd" "$MYDNSJP_API/directedit.html")"
if [ "$?" != "0" ]; then
_err "error $domain"
return 1
fi
_debug2 response "$response"
return 0
}

407
dnsapi/dns_namecheap.sh Executable file
View File

@ -0,0 +1,407 @@
#!/usr/bin/env sh
# Namecheap API
# https://www.namecheap.com/support/api/intro.aspx
#
# Requires Namecheap API key set in
#NAMECHEAP_API_KEY,
#NAMECHEAP_USERNAME,
#NAMECHEAP_SOURCEIP
# Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
######## Public functions #####################
NAMECHEAP_API="https://api.namecheap.com/xml.response"
#Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_namecheap_add() {
fulldomain=$1
txtvalue=$2
if ! _namecheap_check_config; then
_err "$error"
return 1
fi
if ! _namecheap_set_publicip; then
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug domain "$_domain"
_debug sub_domain "$_sub_domain"
_set_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_namecheap_rm() {
fulldomain=$1
txtvalue=$2
if ! _namecheap_set_publicip; then
return 1
fi
if ! _namecheap_check_config; then
_err "$error"
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug domain "$_domain"
_debug sub_domain "$_sub_domain"
_del_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
fulldomain=$1
if ! _get_root_by_getList "$fulldomain"; then
_debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api."
# The above "getList" api will only return hosts *owned* by the calling user. However, if the calling
# user is not the owner, but still has administrative rights, we must query the getHosts api directly.
# See this comment and the official namecheap response: http://disq.us/p/1q6v9x9
if ! _get_root_by_getHosts "$fulldomain"; then
return 1
fi
fi
return 0
}
_get_root_by_getList() {
domain=$1
if ! _namecheap_post "namecheap.domains.getList"; then
_err "$error"
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 "$h" "\\."; then
#not valid
return 1
fi
if ! _contains "$response" "$h"; then
_debug "$h not found"
else
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
_get_root_by_getHosts() {
i=100
p=99
while [ $p -ne 0 ]; do
h=$(printf "%s" "$1" | cut -d . -f $i-100)
if [ -n "$h" ]; then
if _contains "$h" "\\."; then
_debug h "$h"
if _namecheap_set_tld_sld "$h"; then
_sub_domain=$(printf "%s" "$1" | cut -d . -f 1-$p)
_domain="$h"
return 0
else
_debug "$h not found"
fi
fi
fi
i="$p"
p=$(_math "$p" - 1)
done
return 1
}
_namecheap_set_publicip() {
if [ -z "$NAMECHEAP_SOURCEIP" ]; then
_err "No Source IP specified for Namecheap API."
_err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
return 1
else
_saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP"
_debug sourceip "$NAMECHEAP_SOURCEIP"
ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https)://.*')
_debug2 ip "$ip"
_debug2 addr "$addr"
if [ -n "$ip" ]; then
_publicip="$ip"
elif [ -n "$addr" ]; then
_publicip=$(_get "$addr")
else
_err "No Source IP specified for Namecheap API."
_err "Use your public ip address or an url to retrieve it (e.g. https://ipconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
return 1
fi
fi
_debug publicip "$_publicip"
return 0
}
_namecheap_post() {
command=$1
data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}"
_debug2 "_namecheap_post data" "$data"
response="$(_post "$data" "$NAMECHEAP_API" "" "POST")"
_debug2 response "$response"
if _contains "$response" "Status=\"ERROR\"" >/dev/null; then
error=$(echo "$response" | _egrep_o ">.*<\\/Error>" | cut -d '<' -f 1 | tr -d '>')
_err "error $error"
return 1
fi
return 0
}
_namecheap_parse_host() {
_host=$1
_debug _host "$_host"
_hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2)
_hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2)
_hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2)
_hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2)
_hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2)
_hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2)
_debug hostid "$_hostid"
_debug hostname "$_hostname"
_debug hosttype "$_hosttype"
_debug hostaddress "$_hostaddress"
_debug hostmxpref "$_hostmxpref"
_debug hostttl "$_hostttl"
}
_namecheap_check_config() {
if [ -z "$NAMECHEAP_API_KEY" ]; then
_err "No API key specified for Namecheap API."
_err "Create your key and export it as NAMECHEAP_API_KEY"
return 1
fi
if [ -z "$NAMECHEAP_USERNAME" ]; then
_err "No username key specified for Namecheap API."
_err "Create your key and export it as NAMECHEAP_USERNAME"
return 1
fi
_saveaccountconf NAMECHEAP_API_KEY "$NAMECHEAP_API_KEY"
_saveaccountconf NAMECHEAP_USERNAME "$NAMECHEAP_USERNAME"
return 0
}
_set_namecheap_TXT() {
subdomain=$2
txt=$3
if ! _namecheap_set_tld_sld "$1"; then
return 1
fi
request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}"
if ! _namecheap_post "$request"; then
_err "$error"
return 1
fi
hosts=$(echo "$response" | _egrep_o '<host[^>]*')
_debug hosts "$hosts"
if [ -z "$hosts" ]; then
_error "Hosts not found"
return 1
fi
_namecheap_reset_hostList
while read -r host; do
if _contains "$host" "<host"; then
_namecheap_parse_host "$host"
_debug2 _hostname "_hostname"
_debug2 _hosttype "_hosttype"
_debug2 _hostaddress "_hostaddress"
_debug2 _hostmxpref "_hostmxpref"
_hostaddress="$(printf "%s" "$_hostaddress" | _url_encode)"
_debug2 "encoded _hostaddress" "_hostaddress"
_namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
fi
done <<EOT
echo "$hosts"
EOT
_namecheap_add_host "$subdomain" "TXT" "$txt" 10 120
_debug hostrequestfinal "$_hostrequest"
request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}"
if ! _namecheap_post "$request"; then
_err "$error"
return 1
fi
return 0
}
_del_namecheap_TXT() {
subdomain=$2
txt=$3
if ! _namecheap_set_tld_sld "$1"; then
return 1
fi
request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}"
if ! _namecheap_post "$request"; then
_err "$error"
return 1
fi
hosts=$(echo "$response" | _egrep_o '<host[^>]*')
_debug hosts "$hosts"
if [ -z "$hosts" ]; then
_error "Hosts not found"
return 1
fi
_namecheap_reset_hostList
found=0
while read -r host; do
if _contains "$host" "<host"; then
_namecheap_parse_host "$host"
if [ "$_hosttype" = "TXT" ] && [ "$_hostname" = "$subdomain" ] && [ "$_hostaddress" = "$txt" ]; then
_debug "TXT entry found"
found=1
else
_hostaddress="$(printf "%s" "$_hostaddress" | _url_encode)"
_namecheap_add_host "$_hostname" "$_hosttype" "$_hostaddress" "$_hostmxpref" "$_hostttl"
fi
fi
done <<EOT
echo "$hosts"
EOT
if [ $found -eq 0 ]; then
_debug "TXT entry not found"
return 0
fi
_debug hostrequestfinal "$_hostrequest"
request="namecheap.domains.dns.setHosts&SLD=${_sld}&TLD=${_tld}${_hostrequest}"
if ! _namecheap_post "$request"; then
_err "$error"
return 1
fi
return 0
}
_namecheap_reset_hostList() {
_hostindex=0
_hostrequest=""
}
#Usage: _namecheap_add_host HostName RecordType Address MxPref TTL
_namecheap_add_host() {
_hostindex=$(_math "$_hostindex" + 1)
_hostrequest=$(printf '%s&HostName%d=%s&RecordType%d=%s&Address%d=%s&MXPref%d=%d&TTL%d=%d' "$_hostrequest" "$_hostindex" "$1" "$_hostindex" "$2" "$_hostindex" "$3" "$_hostindex" "$4" "$_hostindex" "$5")
}
_namecheap_set_tld_sld() {
domain=$1
_tld=""
_sld=""
i=2
while true; do
_tld=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug tld "$_tld"
if [ -z "$_tld" ]; then
_debug "invalid tld"
return 1
fi
j=$(_math "$i" - 1)
_sld=$(printf "%s" "$domain" | cut -d . -f 1-"$j")
_debug sld "$_sld"
if [ -z "$_sld" ]; then
_debug "invalid sld"
return 1
fi
request="namecheap.domains.dns.getHosts&SLD=$_sld&TLD=$_tld"
if ! _namecheap_post "$request"; then
_debug "sld($_sld)/tld($_tld) not found"
else
_debug "sld($_sld)/tld($_tld) found"
return 0
fi
i=$(_math "$i" + 1)
done
}

View File

@ -13,6 +13,8 @@ dns_namecom_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
Namecom_Username="${Namecom_Username:-$(_readaccountconf_mutable Namecom_Username)}"
Namecom_Token="${Namecom_Token:-$(_readaccountconf_mutable Namecom_Token)}"
# First we need name.com credentials. # First we need name.com credentials.
if [ -z "$Namecom_Username" ]; then if [ -z "$Namecom_Username" ]; then
Namecom_Username="" Namecom_Username=""
@ -27,10 +29,11 @@ dns_namecom_add() {
_err "Please specify that in your environment variable." _err "Please specify that in your environment variable."
return 1 return 1
fi fi
_debug Namecom_Username "$Namecom_Username"
_secure_debug Namecom_Token "$Namecom_Token"
# Save them in configuration. # Save them in configuration.
_saveaccountconf Namecom_Username "$Namecom_Username" _saveaccountconf_mutable Namecom_Username "$Namecom_Username"
_saveaccountconf Namecom_Token "$Namecom_Token" _saveaccountconf_mutable Namecom_Token "$Namecom_Token"
# Login in using API # Login in using API
if ! _namecom_login; then if ! _namecom_login; then
@ -46,7 +49,7 @@ dns_namecom_add() {
# Add TXT record. # Add TXT record.
_namecom_addtxt_json="{\"host\":\"$_sub_domain\",\"type\":\"TXT\",\"answer\":\"$txtvalue\",\"ttl\":\"300\"}" _namecom_addtxt_json="{\"host\":\"$_sub_domain\",\"type\":\"TXT\",\"answer\":\"$txtvalue\",\"ttl\":\"300\"}"
if _namecom_rest POST "domains/$_domain/records" "$_namecom_addtxt_json"; then if _namecom_rest POST "domains/$_domain/records" "$_namecom_addtxt_json"; then
_retvalue=$(printf "%s\n" "$response" | _egrep_o "\"$_sub_domain\"") _retvalue=$(echo "$response" | _egrep_o "\"$_sub_domain\"")
if [ "$_retvalue" ]; then if [ "$_retvalue" ]; then
_info "Successfully added TXT record, ready for validation." _info "Successfully added TXT record, ready for validation."
return 0 return 0
@ -63,6 +66,8 @@ dns_namecom_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
Namecom_Username="${Namecom_Username:-$(_readaccountconf_mutable Namecom_Username)}"
Namecom_Token="${Namecom_Token:-$(_readaccountconf_mutable Namecom_Token)}"
if ! _namecom_login; then if ! _namecom_login; then
return 1 return 1
fi fi
@ -75,7 +80,7 @@ dns_namecom_rm() {
# Get the record id. # Get the record id.
if _namecom_rest GET "domains/$_domain/records"; then if _namecom_rest GET "domains/$_domain/records"; then
_record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+,\"domainName\":\"$_domain\",\"host\":\"$_sub_domain\",\"fqdn\":\"$fulldomain.\",\"type\":\"TXT\",\"answer\":\"$txtvalue\"" | cut -d \" -f 3 | _egrep_o [0-9]+) _record_id=$(echo "$response" | _egrep_o "\"id\":[0-9]+,\"domainName\":\"$_domain\",\"host\":\"$_sub_domain\",\"fqdn\":\"$fulldomain.\",\"type\":\"TXT\",\"answer\":\"$txtvalue\"" | cut -d \" -f 3 | _egrep_o [0-9]+)
_debug record_id "$_record_id" _debug record_id "$_record_id"
if [ "$_record_id" ]; then if [ "$_record_id" ]; then
_info "Successfully retrieved the record id for ACME challenge." _info "Successfully retrieved the record id for ACME challenge."
@ -126,10 +131,12 @@ _namecom_login() {
_namecom_auth=$(printf "%s:%s" "$Namecom_Username" "$Namecom_Token" | _base64) _namecom_auth=$(printf "%s:%s" "$Namecom_Username" "$Namecom_Token" | _base64)
if _namecom_rest GET "hello"; then if _namecom_rest GET "hello"; then
retcode=$(printf "%s\n" "$response" | _egrep_o "\"username\"\:\"$Namecom_Username\"") retcode=$(echo "$response" | _egrep_o "\"username\"\:\"$Namecom_Username\"")
if [ "$retcode" ]; then if [ "$retcode" ]; then
_info "Successfully logged in." _info "Successfully logged in."
else else
_err "$response"
_err "Please add your ip to api whitelist"
_err "Logging in failed." _err "Logging in failed."
return 1 return 1
fi fi

131
dnsapi/dns_nederhost.sh Executable file
View File

@ -0,0 +1,131 @@
#!/usr/bin/env sh
#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghjk"
NederHost_Api="https://api.nederhost.nl/dns/v1"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_nederhost_add() {
fulldomain=$1
txtvalue=$2
NederHost_Key="${NederHost_Key:-$(_readaccountconf_mutable NederHost_Key)}"
if [ -z "$NederHost_Key" ]; then
NederHost_Key=""
_err "You didn't specify a NederHost api key."
_err "You can get yours from https://www.nederhost.nl/mijn_nederhost"
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable NederHost_Key "$NederHost_Key"
_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"
if _nederhost_rest PATCH "zones/$_domain/records/$fulldomain/TXT" "[{\"content\":\"$txtvalue\",\"ttl\":60}]"; then
if _contains "$response" "$fulldomain"; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_nederhost_rm() {
fulldomain=$1
txtvalue=$2
NederHost_Key="${NederHost_Key:-$(_readaccountconf_mutable NederHost_Key)}"
if [ -z "$NederHost_Key" ]; then
NederHost_Key=""
_err "You didn't specify a NederHost api key."
_err "You can get yours from https://www.nederhost.nl/mijn_nederhost"
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 "Removing txt record"
_nederhost_rest DELETE "zones/${_domain}/records/$fulldomain/TXT?content=$txtvalue"
}
#################### 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
while true; do
_domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_debug _domain "$_domain"
if [ -z "$_domain" ]; then
#not valid
return 1
fi
if _nederhost_rest GET "zones/${_domain}"; then
if [ "${_code}" = "204" ]; then
return 0
fi
else
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_nederhost_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Authorization: Bearer $NederHost_Key"
export _H2="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")"
else
response="$(_get "$NederHost_Api/$ep")"
fi
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
_debug "http response code $_code"
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

181
dnsapi/dns_neodigit.sh Normal file
View File

@ -0,0 +1,181 @@
#!/usr/bin/env sh
#
# NEODIGIT_API_TOKEN="jasdfhklsjadhflnhsausdfas"
# This is Neodigit.net api wrapper for acme.sh
#
# Author: Adrian Almenar
# Report Bugs here: https://github.com/tecnocratica/acme.sh
#
NEODIGIT_API_URL="https://api.neodigit.net/v1"
#
######## Public functions #####################
# Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_neodigit_add() {
fulldomain=$1
txtvalue=$2
NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}"
if [ -z "$NEODIGIT_API_TOKEN" ]; then
NEODIGIT_API_TOKEN=""
_err "You haven't specified a Token api key."
_err "Please create the key and try again."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable NEODIGIT_API_TOKEN "$NEODIGIT_API_TOKEN"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug domain "$_domain"
_debug sub_domain "$_sub_domain"
_debug "Getting txt records"
_neo_rest GET "dns/zones/${_domain_id}/records?type=TXT&name=$fulldomain"
_debug _code "$_code"
if [ "$_code" != "200" ]; then
_err "error retrieving data!"
return 1
fi
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_debug domain "$_domain"
_debug sub_domain "$_sub_domain"
_info "Adding record"
if _neo_rest POST "dns/zones/$_domain_id/records" "{\"record\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":60}}"; then
if printf -- "%s" "$response" | grep "$_sub_domain" >/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
}
#fulldomain txtvalue
dns_neodigit_rm() {
fulldomain=$1
txtvalue=$2
NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}"
if [ -z "$NEODIGIT_API_TOKEN" ]; then
NEODIGIT_API_TOKEN=""
_err "You haven't specified a Token api key."
_err "Please create the key and try again."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable NEODIGIT_API_TOKEN "$NEODIGIT_API_TOKEN"
_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"
_neo_rest GET "dns/zones/${_domain_id}/records?type=TXT&name=$fulldomain&content=$txtvalue"
if [ "$_code" != "200" ]; then
_err "error retrieving data!"
return 1
fi
record_id=$(echo "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _neo_rest DELETE "dns/zones/$_domain_id/records/$record_id"; then
_err "Delete record error."
return 1
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=dasfdsafsadg5ythd
_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 ! _neo_rest GET "dns/zones?name=$h"; then
return 1
fi
_debug p "$p"
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
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
}
_neo_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="X-TCPanel-Token: $NEODIGIT_API_TOKEN"
export _H2="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$NEODIGIT_API_URL/$ep" "" "$m")"
else
response="$(_get "$NEODIGIT_API_URL/$ep")"
fi
_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

134
dnsapi/dns_netcup.sh Normal file
View File

@ -0,0 +1,134 @@
#!/usr/bin/env sh
#developed by linux-insideDE
NC_Apikey="${NC_Apikey:-$(_readaccountconf_mutable NC_Apikey)}"
NC_Apipw="${NC_Apipw:-$(_readaccountconf_mutable NC_Apipw)}"
NC_CID="${NC_CID:-$(_readaccountconf_mutable NC_CID)}"
end="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
client=""
dns_netcup_add() {
_debug NC_Apikey "$NC_Apikey"
login
if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then
_err "No Credentials given"
return 1
fi
_saveaccountconf_mutable NC_Apikey "$NC_Apikey"
_saveaccountconf_mutable NC_Apipw "$NC_Apipw"
_saveaccountconf_mutable NC_CID "$NC_CID"
fulldomain=$1
txtvalue=$2
domain=""
exit=$(echo "$fulldomain" | tr -dc '.' | wc -c)
exit=$(_math "$exit" + 1)
i=$exit
while
[ "$exit" -gt 0 ]
do
tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit")
if [ "$(_math "$i" - "$exit")" -eq 0 ]; then
domain="$tmp"
else
domain="$tmp.$domain"
fi
if [ "$(_math "$i" - "$exit")" -ge 1 ]; then
msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST")
_debug "$msg"
if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then
if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$msg"
return 1
else
break
fi
fi
fi
exit=$(_math "$exit" - 1)
done
logout
}
dns_netcup_rm() {
login
fulldomain=$1
txtvalue=$2
domain=""
exit=$(echo "$fulldomain" | tr -dc '.' | wc -c)
exit=$(_math "$exit" + 1)
i=$exit
rec=""
while
[ "$exit" -gt 0 ]
do
tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit")
if [ "$(_math "$i" - "$exit")" -eq 0 ]; then
domain="$tmp"
else
domain="$tmp.$domain"
fi
if [ "$(_math "$i" - "$exit")" -ge 1 ]; then
msg=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$domain\"}}" "$end" "" "POST")
rec=$(echo "$msg" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g')
_debug "$msg"
if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then
if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$msg"
return 1
else
break
fi
fi
fi
exit=$(_math "$exit" - 1)
done
ida=0000
idv=0001
ids=0000000000
i=1
while
[ "$i" -ne 0 ]
do
specrec=$(_getfield "$rec" "$i" ";")
idv="$ida"
ida=$(_getfield "$specrec" "1" "," | sed 's/\"id\":\"//g' | sed 's/\"//g')
txtv=$(_getfield "$specrec" "5" "," | sed 's/\"destination\":\"//g' | sed 's/\"//g')
i=$(_math "$i" + 1)
if [ "$txtvalue" = "$txtv" ]; then
i=0
ids="$ida"
fi
if [ "$ida" = "$idv" ]; then
i=0
fi
done
msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST")
_debug "$msg"
if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$msg"
return 1
fi
logout
}
login() {
tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g')
_debug "$tmp"
if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$msg"
return 1
fi
}
logout() {
tmp=$(_post "{\"action\": \"logout\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
_debug "$tmp"
if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
_err "$msg"
return 1
fi
}

View File

@ -46,7 +46,7 @@ dns_nsone_add() {
if [ "$count" = "0" ]; then if [ "$count" = "0" ]; then
_info "Adding record" _info "Adding record"
if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\"}"; then if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\",\"ttl\":0}"; then
if _contains "$response" "$fulldomain"; then if _contains "$response" "$fulldomain"; then
_info "Added" _info "Added"
#todo: check if the record takes effect #todo: check if the record takes effect
@ -62,7 +62,7 @@ dns_nsone_add() {
prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1) 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" _debug "prev_txt" "$prev_txt"
_nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"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\",\"ttl\":0}"
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

View File

@ -13,12 +13,24 @@ dns_nsupdate_add() {
_saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}" _saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}"
_saveaccountconf NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}" _saveaccountconf NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}"
_saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}" _saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}"
_saveaccountconf NSUPDATE_ZONE "${NSUPDATE_ZONE}"
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\"" _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
nsupdate -k "${NSUPDATE_KEY}" <<EOF [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
update add ${fulldomain}. 60 in txt "${txtvalue}" update add ${fulldomain}. 60 in txt "${txtvalue}"
send send
EOF EOF
else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}.
update add ${fulldomain}. 60 in txt "${txtvalue}"
send
EOF
fi
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
_err "error updating domain" _err "error updating domain"
return 1 return 1
@ -34,11 +46,22 @@ dns_nsupdate_rm() {
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost" [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
[ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53 [ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
_info "removing ${fulldomain}. txt" _info "removing ${fulldomain}. txt"
nsupdate -k "${NSUPDATE_KEY}" <<EOF [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT} server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
update delete ${fulldomain}. txt update delete ${fulldomain}. txt
send send
EOF EOF
else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}.
update delete ${fulldomain}. txt
send
EOF
fi
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
_err "error updating domain" _err "error updating domain"
return 1 return 1

211
dnsapi/dns_nw.sh Normal file
View File

@ -0,0 +1,211 @@
#!/usr/bin/env sh
########################################################################
# NocWorx script for acme.sh
#
# Handles DNS Updates for the Following vendors:
# - Nexcess.net
# - Thermo.io
# - Futurehosting.com
#
# Environment variables:
#
# - NW_API_TOKEN (Your API Token)
# - NW_API_ENDPOINT (One of the following listed below)
#
# Endpoints:
# - https://portal.nexcess.net (default)
# - https://core.thermo.io
# - https://my.futurehosting.com
#
# Note: If you do not have an API token, one can be generated at one
# of the following URLs:
# - https://portal.nexcess.net/api-token
# - https://core.thermo.io/api-token
# - https://my.futurehosting.com/api-token
#
# Author: Frank Laszlo <flaszlo@nexcess.net>
NW_API_VERSION="0"
# dns_nw_add() - Add TXT record
# Usage: dns_nw_add _acme-challenge.subdomain.domain.com "XyZ123..."
dns_nw_add() {
host="${1}"
txtvalue="${2}"
_debug host "${host}"
_debug txtvalue "${txtvalue}"
if ! _check_nw_api_creds; then
return 1
fi
_info "Using NocWorx (${NW_API_ENDPOINT})"
_debug "Calling: dns_nw_add() '${host}' '${txtvalue}'"
_debug "Detecting root zone"
if ! _get_root "${host}"; then
_err "Zone for domain does not exist."
return 1
fi
_debug _zone_id "${_zone_id}"
_debug _sub_domain "${_sub_domain}"
_debug _domain "${_domain}"
_post_data="{\"zone_id\": \"${_zone_id}\", \"type\": \"TXT\", \"host\": \"${host}\", \"target\": \"${txtvalue}\", \"ttl\": \"300\"}"
if _rest POST "dns-record" "${_post_data}" && [ -n "${response}" ]; then
_record_id=$(printf "%s\n" "${response}" | _egrep_o "\"record_id\": *[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
_debug _record_id "${_record_id}"
if [ -z "$_record_id" ]; then
_err "Error adding the TXT record."
return 1
fi
_info "TXT record successfully added."
return 0
fi
return 1
}
# dns_nw_rm() - Remove TXT record
# Usage: dns_nw_rm _acme-challenge.subdomain.domain.com "XyZ123..."
dns_nw_rm() {
host="${1}"
txtvalue="${2}"
_debug host "${host}"
_debug txtvalue "${txtvalue}"
if ! _check_nw_api_creds; then
return 1
fi
_info "Using NocWorx (${NW_API_ENDPOINT})"
_debug "Calling: dns_nw_rm() '${host}'"
_debug "Detecting root zone"
if ! _get_root "${host}"; then
_err "Zone for domain does not exist."
return 1
fi
_debug _zone_id "${_zone_id}"
_debug _sub_domain "${_sub_domain}"
_debug _domain "${_domain}"
_parameters="?zone_id=${_zone_id}"
if _rest GET "dns-record" "${_parameters}" && [ -n "${response}" ]; then
response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"record_id":/|"record_id":/g' | sed 's/|/&{/g' | tr "|" "\n")"
_debug response "${response}"
record="$(echo "${response}" | _egrep_o "{.*\"host\": *\"${_sub_domain}\", *\"target\": *\"${txtvalue}\".*}")"
_debug record "${record}"
if [ "${record}" ]; then
_record_id=$(printf "%s\n" "${record}" | _egrep_o "\"record_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "${_record_id}" ]; then
_debug _record_id "${_record_id}"
_rest DELETE "dns-record/${_record_id}"
_info "TXT record successfully deleted."
return 0
fi
return 1
fi
return 0
fi
return 1
}
_check_nw_api_creds() {
NW_API_TOKEN="${NW_API_TOKEN:-$(_readaccountconf_mutable NW_API_TOKEN)}"
NW_API_ENDPOINT="${NW_API_ENDPOINT:-$(_readaccountconf_mutable NW_API_ENDPOINT)}"
if [ -z "${NW_API_ENDPOINT}" ]; then
NW_API_ENDPOINT="https://portal.nexcess.net"
fi
if [ -z "${NW_API_TOKEN}" ]; then
_err "You have not defined your NW_API_TOKEN."
_err "Please create your token and try again."
_err "If you need to generate a new token, please visit one of the following URLs:"
_err " - https://portal.nexcess.net/api-token"
_err " - https://core.thermo.io/api-token"
_err " - https://my.futurehosting.com/api-token"
return 1
fi
_saveaccountconf_mutable NW_API_TOKEN "${NW_API_TOKEN}"
_saveaccountconf_mutable NW_API_ENDPOINT "${NW_API_ENDPOINT}"
}
_get_root() {
domain="${1}"
i=2
p=1
if _rest GET "dns-zone"; then
response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"zone_id":/|"zone_id":/g' | sed 's/|/&{/g' | tr "|" "\n")"
_debug response "${response}"
while true; do
h=$(printf "%s" "${domain}" | cut -d . -f $i-100)
_debug h "${h}"
if [ -z "${h}" ]; then
#not valid
return 1
fi
hostedzone="$(echo "${response}" | _egrep_o "{.*\"domain\": *\"${h}\".*}")"
if [ "${hostedzone}" ]; then
_zone_id=$(printf "%s\n" "${hostedzone}" | _egrep_o "\"zone_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "${_zone_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
fi
return 1
}
_rest() {
method="${1}"
ep="/${2}"
data="${3}"
_debug method "${method}"
_debug ep "${ep}"
export _H1="Accept: application/json"
export _H2="Content-Type: application/json"
export _H3="Api-Version: ${NW_API_VERSION}"
export _H4="User-Agent: NW-ACME-CLIENT"
export _H5="Authorization: Bearer ${NW_API_TOKEN}"
if [ "${method}" != "GET" ]; then
_debug data "${data}"
response="$(_post "${data}" "${NW_API_ENDPOINT}${ep}" "" "${method}")"
else
response="$(_get "${NW_API_ENDPOINT}${ep}${data}")"
fi
if [ "${?}" != "0" ]; then
_err "error ${ep}"
return 1
fi
_debug2 response "${response}"
return 0
}

217
dnsapi/dns_online.sh Executable file
View File

@ -0,0 +1,217 @@
#!/usr/bin/env sh
# Online API
# https://console.online.net/en/api/
#
# Requires Online API key set in ONLINE_API_KEY
######## Public functions #####################
ONLINE_API="https://api.online.net/api/v1"
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_online_add() {
fulldomain=$1
txtvalue=$2
if ! _online_check_config; 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 _real_dns_version "$_real_dns_version"
_info "Creating temporary zone version"
_online_create_temporary_zone_version
_info "Enabling temporary zone version"
_online_enable_zone "$_temporary_dns_version"
_info "Adding record"
_online_create_TXT_record "$_real_dns_version" "$_sub_domain" "$txtvalue"
_info "Disabling temporary version"
_online_enable_zone "$_real_dns_version"
_info "Destroying temporary version"
_online_destroy_zone "$_temporary_dns_version"
_info "Record added."
return 0
}
#fulldomain
dns_online_rm() {
fulldomain=$1
txtvalue=$2
if ! _online_check_config; 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 _real_dns_version "$_real_dns_version"
_debug "Getting txt records"
if ! _online_rest GET "domain/$_domain/version/active"; then
return 1
fi
rid=$(echo "$response" | _egrep_o "\"id\":[0-9]+,\"name\":\"$_sub_domain\",\"data\":\"\\\u0022$txtvalue\\\u0022\"" | cut -d ':' -f 2 | cut -d ',' -f 1)
_debug rid "$rid"
if [ -z "$rid" ]; then
return 1
fi
_info "Creating temporary zone version"
_online_create_temporary_zone_version
_info "Enabling temporary zone version"
_online_enable_zone "$_temporary_dns_version"
_info "Removing DNS record"
_online_rest DELETE "domain/$_domain/version/$_real_dns_version/zone/$rid"
_info "Disabling temporary version"
_online_enable_zone "$_real_dns_version"
_info "Destroying temporary version"
_online_destroy_zone "$_temporary_dns_version"
return 0
}
#################### Private functions below ##################################
_online_check_config() {
ONLINE_API_KEY="${ONLINE_API_KEY:-$(_readaccountconf_mutable ONLINE_API_KEY)}"
if [ -z "$ONLINE_API_KEY" ]; then
_err "No API key specified for Online API."
_err "Create your key and export it as ONLINE_API_KEY"
return 1
fi
if ! _online_rest GET "domain/"; then
_err "Invalid API key specified for Online API."
return 1
fi
_saveaccountconf_mutable ONLINE_API_KEY "$ONLINE_API_KEY"
return 0
}
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_online_rest GET "domain/$h/version/active"
if ! _contains "$response" "Domain not found" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
_real_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
_err "Unable to retrive DNS zone matching this domain"
return 1
}
# this function create a temporary zone version
# as online.net does not allow updating an active version
_online_create_temporary_zone_version() {
_online_rest POST "domain/$_domain/version" "name=acme.sh"
if [ "$?" != "0" ]; then
return 1
fi
_temporary_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
# Creating a dummy record in this temporary version, because online.net doesn't accept enabling an empty version
_online_create_TXT_record "$_temporary_dns_version" "dummy.acme.sh" "dummy"
return 0
}
_online_destroy_zone() {
version_id=$1
_online_rest DELETE "domain/$_domain/version/$version_id"
if [ "$?" != "0" ]; then
return 1
fi
return 0
}
_online_enable_zone() {
version_id=$1
_online_rest PATCH "domain/$_domain/version/$version_id/enable"
if [ "$?" != "0" ]; then
return 1
fi
return 0
}
_online_create_TXT_record() {
version=$1
txt_name=$2
txt_value=$3
_online_rest POST "domain/$_domain/version/$version/zone" "type=TXT&name=$txt_name&data=%22$txt_value%22&ttl=60&priority=0"
# Note : the normal, expected response SHOULD be "Unknown method".
# this happens because the API HTTP response contains a Location: header, that redirect
# to an unknown online.net endpoint.
if [ "$?" != "0" ] || _contains "$response" "Unknown method" || _contains "$response" "\$ref"; then
return 0
else
_err "error $response"
return 1
fi
}
_online_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
_online_url="$ONLINE_API/$ep"
_debug2 _online_url "$_online_url"
export _H1="Authorization: Bearer $ONLINE_API_KEY"
export _H2="X-Pretty-JSON: 1"
if [ "$data" ] || [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$_online_url" "" "$m")"
else
response="$(_get "$_online_url")"
fi
if [ "$?" != "0" ] || _contains "$response" "invalid_grant" || _contains "$response" "Method not allowed"; then
_err "error $response"
return 1
fi
_debug2 response "$response"
return 0
}

244
dnsapi/dns_openprovider.sh Executable file
View File

@ -0,0 +1,244 @@
#!/usr/bin/env sh
# This is the OpenProvider API wrapper for acme.sh
#
# Author: Sylvia van Os
# Report Bugs here: https://github.com/Neilpang/acme.sh/issues/2104
#
# export OPENPROVIDER_USER="username"
# export OPENPROVIDER_PASSWORDHASH="hashed_password"
#
# Usage:
# acme.sh --issue --dns dns_openprovider -d example.com
OPENPROVIDER_API="https://api.openprovider.eu/"
#OPENPROVIDER_API="https://api.cte.openprovider.eu/" # Test API
######## Public functions #####################
#Usage: dns_openprovider_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_openprovider_add() {
fulldomain="$1"
txtvalue="$2"
OPENPROVIDER_USER="${OPENPROVIDER_USER:-$(_readaccountconf_mutable OPENPROVIDER_USER)}"
OPENPROVIDER_PASSWORDHASH="${OPENPROVIDER_PASSWORDHASH:-$(_readaccountconf_mutable OPENPROVIDER_PASSWORDHASH)}"
if [ -z "$OPENPROVIDER_USER" ] || [ -z "$OPENPROVIDER_PASSWORDHASH" ]; then
_err "You didn't specify the openprovider user and/or password hash."
return 1
fi
# save the username and password to the account conf file.
_saveaccountconf_mutable OPENPROVIDER_USER "$OPENPROVIDER_USER"
_saveaccountconf_mutable OPENPROVIDER_PASSWORDHASH "$OPENPROVIDER_PASSWORDHASH"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_name "$_domain_name"
_debug _domain_extension "$_domain_extension"
_debug "Getting current records"
existing_items=""
results_retrieved=0
while true; do
_openprovider_request "$(printf '<searchZoneRecordDnsRequest><name>%s.%s</name><offset>%s</offset></searchZoneRecordDnsRequest>' "$_domain_name" "$_domain_extension" "$results_retrieved")"
items="$response"
while true; do
item="$(echo "$items" | _egrep_o '<openXML>.*<\/openXML>' | sed -n 's/.*\(<item>.*<\/item>\).*/\1/p')"
_debug existing_items "$existing_items"
_debug results_retrieved "$results_retrieved"
_debug item "$item"
if [ -z "$item" ]; then
break
fi
items="$(echo "$items" | sed "s|${item}||")"
results_retrieved="$(_math "$results_retrieved" + 1)"
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
if [ -z "$new_item" ]; then
# Base record
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
fi
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item"
continue
fi
existing_items="$existing_items$new_item"
done
total="$(echo "$response" | _egrep_o '<total>.*?<\/total>' | sed -n 's/.*<total>\(.*\)<\/total>.*/\1/p')"
_debug total "$total"
if [ "$results_retrieved" -eq "$total" ]; then
break
fi
done
_debug "Creating acme record"
acme_record="$(echo "$fulldomain" | sed -e "s/.$_domain_name.$_domain_extension$//")"
_openprovider_request "$(printf '<modifyZoneDnsRequest><domain><name>%s</name><extension>%s</extension></domain><type>master</type><records><array>%s<item><name>%s</name><type>TXT</type><value>%s</value><ttl>86400</ttl></item></array></records></modifyZoneDnsRequest>' "$_domain_name" "$_domain_extension" "$existing_items" "$acme_record" "$txtvalue")"
return 0
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_openprovider_rm() {
fulldomain="$1"
txtvalue="$2"
OPENPROVIDER_USER="${OPENPROVIDER_USER:-$(_readaccountconf_mutable OPENPROVIDER_USER)}"
OPENPROVIDER_PASSWORDHASH="${OPENPROVIDER_PASSWORDHASH:-$(_readaccountconf_mutable OPENPROVIDER_PASSWORDHASH)}"
if [ -z "$OPENPROVIDER_USER" ] || [ -z "$OPENPROVIDER_PASSWORDHASH" ]; then
_err "You didn't specify the openprovider user and/or password hash."
return 1
fi
# save the username and password to the account conf file.
_saveaccountconf_mutable OPENPROVIDER_USER "$OPENPROVIDER_USER"
_saveaccountconf_mutable OPENPROVIDER_PASSWORDHASH "$OPENPROVIDER_PASSWORDHASH"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_name "$_domain_name"
_debug _domain_extension "$_domain_extension"
_debug "Getting current records"
existing_items=""
results_retrieved=0
while true; do
_openprovider_request "$(printf '<searchZoneRecordDnsRequest><name>%s.%s</name><offset>%s</offset></searchZoneRecordDnsRequest>' "$_domain_name" "$_domain_extension" "$results_retrieved")"
# Remove acme records from items
items="$response"
while true; do
item="$(echo "$items" | _egrep_o '<openXML>.*<\/openXML>' | sed -n 's/.*\(<item>.*<\/item>\).*/\1/p')"
_debug existing_items "$existing_items"
_debug results_retrieved "$results_retrieved"
_debug item "$item"
if [ -z "$item" ]; then
break
fi
items="$(echo "$items" | sed "s|${item}||")"
results_retrieved="$(_math "$results_retrieved" + 1)"
if ! echo "$item" | grep -v "$fulldomain"; then
_debug "acme record, skipping" "$item"
continue
fi
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
if [ -z "$new_item" ]; then
# Base record
new_item="$(echo "$item" | sed -n 's/.*<item>.*\(<name>\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(<type>.*<\/type>\).*\(<value>.*<\/value>\).*\(<prio>.*<\/prio>\).*\(<ttl>.*<\/ttl>\)\).*<\/item>.*/<item><name>\2<\/name>\3\4\5\6<\/item>/p')"
fi
if [ -z "$(echo "$new_item" | _egrep_o ".*<type>(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA)<\/type>.*")" ]; then
_debug "not an allowed record type, skipping" "$new_item"
continue
fi
existing_items="$existing_items$new_item"
done
total="$(echo "$response" | _egrep_o '<total>.*?<\/total>' | sed -n 's/.*<total>\(.*\)<\/total>.*/\1/p')"
_debug total "$total"
if [ "$results_retrieved" -eq "$total" ]; then
break
fi
done
_debug "Removing acme record"
_openprovider_request "$(printf '<modifyZoneDnsRequest><domain><name>%s</name><extension>%s</extension></domain><type>master</type><records><array>%s</array></records></modifyZoneDnsRequest>' "$_domain_name" "$_domain_extension" "$existing_items")"
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _domain_name=domain
# _domain_extension=com
_get_root() {
domain=$1
i=2
results_retrieved=0
while true; do
h=$(echo "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
_openprovider_request "$(printf '<searchDomainRequest><domainNamePattern>%s</domainNamePattern><offset>%s</offset></searchDomainRequest>' "$(echo "$h" | cut -d . -f 1)" "$results_retrieved")"
items="$response"
while true; do
item="$(echo "$items" | _egrep_o '<openXML>.*<\/openXML>' | sed -n 's/.*\(<domain>.*<\/domain>\).*/\1/p')"
_debug existing_items "$existing_items"
_debug results_retrieved "$results_retrieved"
_debug item "$item"
if [ -z "$item" ]; then
break
fi
items="$(echo "$items" | sed "s|${item}||")"
results_retrieved="$(_math "$results_retrieved" + 1)"
_domain_name="$(echo "$item" | sed -n 's/.*<domain>.*<name>\(.*\)<\/name>.*<\/domain>.*/\1/p')"
_domain_extension="$(echo "$item" | sed -n 's/.*<domain>.*<extension>\(.*\)<\/extension>.*<\/domain>.*/\1/p')"
_debug _domain_name "$_domain_name"
_debug _domain_extension "$_domain_extension"
if [ "$_domain_name.$_domain_extension" = "$h" ]; then
return 0
fi
done
total="$(echo "$response" | _egrep_o '<total>.*?<\/total>' | sed -n 's/.*<total>\(.*\)<\/total>.*/\1/p')"
_debug total "$total"
if [ "$results_retrieved" -eq "$total" ]; then
results_retrieved=0
i="$(_math "$i" + 1)"
fi
done
return 1
}
_openprovider_request() {
request_xml=$1
xml_prefix='<?xml version="1.0" encoding="UTF-8"?>'
xml_content=$(printf '<openXML><credentials><username>%s</username><hash>%s</hash></credentials>%s</openXML>' "$OPENPROVIDER_USER" "$OPENPROVIDER_PASSWORDHASH" "$request_xml")
response="$(_post "$(echo "$xml_prefix$xml_content" | tr -d '\n')" "$OPENPROVIDER_API" "" "POST" "application/xml")"
_debug response "$response"
if ! _contains "$response" "<openXML><reply><code>0</code>.*</reply></openXML>"; then
_err "API request failed."
return 1
fi
}

164
dnsapi/dns_pointhq.sh Normal file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env sh
#
#PointHQ_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
#PointHQ_Email="xxxx@sss.com"
PointHQ_Api="https://api.pointhq.com"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_pointhq_add() {
fulldomain=$1
txtvalue=$2
PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}"
PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}"
if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then
PointHQ_Key=""
PointHQ_Email=""
_err "You didn't specify a PointHQ API key and email yet."
_err "Please create the key and try again."
return 1
fi
if ! _contains "$PointHQ_Email" "@"; then
_err "It seems that the PointHQ_Email=$PointHQ_Email is not a valid email address."
_err "Please check and retry."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf_mutable PointHQ_Key "$PointHQ_Key"
_saveaccountconf_mutable PointHQ_Email "$PointHQ_Email"
_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"
if _pointhq_rest POST "zones/$_domain/records" "{\"zone_record\": {\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":3600}}"; then
if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_pointhq_rm() {
fulldomain=$1
txtvalue=$2
PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}"
PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}"
if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then
PointHQ_Key=""
PointHQ_Email=""
_err "You didn't specify a PointHQ API key and email yet."
_err "Please create the 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 _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
_pointhq_rest GET "zones/${_domain}/records?record_type=TXT&name=$_sub_domain"
if ! printf "%s" "$response" | grep "^\[" >/dev/null; then
_err "Error"
return 1
fi
if [ "$response" = "[]" ]; then
_info "No records to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | tr -d \" | head -n 1)
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _pointhq_rest DELETE "zones/$_domain/records/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" '"status":"OK"'
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
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 ! _pointhq_rest GET "zones"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; 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
}
_pointhq_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
_pointhq_auth=$(printf "%s:%s" "$PointHQ_Email" "$PointHQ_Key" | _base64)
export _H1="Authorization: Basic $_pointhq_auth"
export _H2="Content-Type: application/json"
export _H3="Accept: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$PointHQ_Api/$ep" "" "$m")"
else
response="$(_get "$PointHQ_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}

207
dnsapi/dns_rackspace.sh Normal file
View File

@ -0,0 +1,207 @@
#!/usr/bin/env sh
#
#
#RACKSPACE_Username=""
#
#RACKSPACE_Apikey=""
RACKSPACE_Endpoint="https://dns.api.rackspacecloud.com/v1.0"
# 20190213 - The name & id fields swapped in the API response; fix sed
# 20190101 - Duplicating file for new pull request to dev branch
# Original - tcocca:rackspace_dnsapi https://github.com/Neilpang/acme.sh/pull/1297
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_rackspace_add() {
fulldomain="$1"
_debug fulldomain="$fulldomain"
txtvalue="$2"
_debug txtvalue="$txtvalue"
_rackspace_check_auth || return 1
_rackspace_check_rootzone || return 1
_info "Creating TXT record."
if ! _rackspace_rest POST "$RACKSPACE_Tenant/domains/$_domain_id/records" "{\"records\":[{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":300}]}"; then
return 1
fi
_debug2 response "$response"
if ! _contains "$response" "$txtvalue" >/dev/null; then
_err "Could not add TXT record."
return 1
fi
return 0
}
#fulldomain txtvalue
dns_rackspace_rm() {
fulldomain=$1
_debug fulldomain="$fulldomain"
txtvalue=$2
_debug txtvalue="$txtvalue"
_rackspace_check_auth || return 1
_rackspace_check_rootzone || return 1
_info "Checking for TXT record."
if ! _get_recordid "$_domain_id" "$fulldomain" "$txtvalue"; then
_err "Could not get TXT record id."
return 1
fi
if [ "$_dns_record_id" = "" ]; then
_err "TXT record not found."
return 1
fi
_info "Removing TXT record."
if ! _delete_txt_record "$_domain_id" "$_dns_record_id"; then
_err "Could not remove TXT record $_dns_record_id."
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_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
if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains"; then
return 1
fi
_debug2 response "$response"
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
# Response looks like:
# {"ttl":300,"accountId":12345,"id":1111111,"name":"example.com","emailAddress": ...<and so on>
_domain_id=$(echo "$response" | sed -n "s/^.*\"id\":\([^,]*\),\"name\":\"$h\",.*/\1/p")
_debug2 domain_id "$_domain_id"
if [ -n "$_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
}
_get_recordid() {
domainid="$1"
fulldomain="$2"
txtvalue="$3"
if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains/$domainid/records?name=$fulldomain&type=TXT"; then
return 1
fi
_debug response "$response"
if ! _contains "$response" "$txtvalue"; then
_dns_record_id=0
return 0
fi
_dns_record_id=$(echo "$response" | tr '{' "\n" | grep "\"data\":\"$txtvalue\"" | sed -n 's/^.*"id":"\([^"]*\)".*/\1/p')
_debug _dns_record_id "$_dns_record_id"
return 0
}
_delete_txt_record() {
domainid="$1"
_dns_record_id="$2"
if ! _rackspace_rest DELETE "$RACKSPACE_Tenant/domains/$domainid/records?id=$_dns_record_id"; then
return 1
fi
_debug response "$response"
if ! _contains "$response" "RUNNING"; then
return 1
fi
return 0
}
_rackspace_rest() {
m="$1"
ep="$2"
data="$3"
_debug ep "$ep"
export _H1="Accept: application/json"
export _H2="X-Auth-Token: $RACKSPACE_Token"
export _H3="X-Project-Id: $RACKSPACE_Tenant"
export _H4="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$RACKSPACE_Endpoint/$ep" "" "$m")"
retcode=$?
else
_info "Getting $RACKSPACE_Endpoint/$ep"
response="$(_get "$RACKSPACE_Endpoint/$ep")"
retcode=$?
fi
if [ "$retcode" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_rackspace_authorization() {
export _H1="Content-Type: application/json"
data="{\"auth\":{\"RAX-KSKEY:apiKeyCredentials\":{\"username\":\"$RACKSPACE_Username\",\"apiKey\":\"$RACKSPACE_Apikey\"}}}"
_debug data "$data"
response="$(_post "$data" "https://identity.api.rackspacecloud.com/v2.0/tokens" "" "POST")"
retcode=$?
_debug2 response "$response"
if [ "$retcode" != "0" ]; then
_err "Authentication failed."
return 1
fi
if _contains "$response" "token"; then
RACKSPACE_Token="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)",".*/\1/p')"
RACKSPACE_Tenant="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)"}.*/\1/p')"
_debug RACKSPACE_Token "$RACKSPACE_Token"
_debug RACKSPACE_Tenant "$RACKSPACE_Tenant"
fi
return 0
}
_rackspace_check_auth() {
# retrieve the rackspace creds
RACKSPACE_Username="${RACKSPACE_Username:-$(_readaccountconf_mutable RACKSPACE_Username)}"
RACKSPACE_Apikey="${RACKSPACE_Apikey:-$(_readaccountconf_mutable RACKSPACE_Apikey)}"
# check their vals for null
if [ -z "$RACKSPACE_Username" ] || [ -z "$RACKSPACE_Apikey" ]; then
RACKSPACE_Username=""
RACKSPACE_Apikey=""
_err "You didn't specify a Rackspace username and api key."
_err "Please set those values and try again."
return 1
fi
# save the username and api key to the account conf file.
_saveaccountconf_mutable RACKSPACE_Username "$RACKSPACE_Username"
_saveaccountconf_mutable RACKSPACE_Apikey "$RACKSPACE_Apikey"
if [ -z "$RACKSPACE_Token" ]; then
_info "Getting authorization token."
if ! _rackspace_authorization; then
_err "Can not get token."
fi
fi
}
_rackspace_check_rootzone() {
_debug "First detect the root zone"
if ! _get_root_zone "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
}

164
dnsapi/dns_ultra.sh Normal file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env sh
#
# ULTRA_USR="your_user_goes_here"
#
# ULTRA_PWD="some_password_goes_here"
ULTRA_API="https://restapi.ultradns.com/v2/"
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
dns_ultra_add() {
fulldomain=$1
txtvalue=$2
export txtvalue
ULTRA_USR="${ULTRA_USR:-$(_readaccountconf_mutable ULTRA_USR)}"
ULTRA_PWD="${ULTRA_PWD:-$(_readaccountconf_mutable ULTRA_PWD)}"
if [ -z "$ULTRA_USR" ] || [ -z "$ULTRA_PWD" ]; then
ULTRA_USR=""
ULTRA_PWD=""
_err "You didn't specify an UltraDNS username and password yet"
return 1
fi
# save the username and password to the account conf file.
_saveaccountconf_mutable ULTRA_USR "$ULTRA_USR"
_saveaccountconf_mutable ULTRA_PWD "$ULTRA_PWD"
_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"
_ultra_rest GET "zones/${_domain_id}/rrsets/TXT?q=value:${fulldomain}"
if printf "%s" "$response" | grep \"totalCount\" >/dev/null; then
_err "Error, it would appear that this record already exists. Please review existing TXT records for this domain."
return 1
fi
_info "Adding record"
if _ultra_rest POST "zones/$_domain_id/rrsets/TXT/${_sub_domain}" '{"ttl":300,"rdata":["'"${txtvalue}"'"]}'; then
if _contains "$response" "Successful"; then
_info "Added, OK"
return 0
elif _contains "$response" "Resource Record of type 16 with these attributes already exists"; then
_info "Already exists, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
}
dns_ultra_rm() {
fulldomain=$1
txtvalue=$2
export txtvalue
ULTRA_USR="${ULTRA_USR:-$(_readaccountconf_mutable ULTRA_USR)}"
ULTRA_PWD="${ULTRA_PWD:-$(_readaccountconf_mutable ULTRA_PWD)}"
if [ -z "$ULTRA_USR" ] || [ -z "$ULTRA_PWD" ]; then
ULTRA_USR=""
ULTRA_PWD=""
_err "You didn't specify an UltraDNS username and password yet"
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"
_ultra_rest GET "zones/${_domain_id}/rrsets?q=kind:RECORDS+owner:${_sub_domain}"
if ! printf "%s" "$response" | grep \"resultInfo\" >/dev/null; then
_err "There was an error in obtaining the resource records for ${_domain_id}"
return 1
fi
count=$(echo "$response" | _egrep_o "\"returnedCount\":[^,]*" | cut -d: -f2 | cut -d'}' -f1)
_debug count "${count}"
if [ "${count}" = "" ]; then
_info "Text record is not present, will not delete anything."
else
if ! _ultra_rest DELETE "zones/$_domain_id/rrsets/TXT/${_sub_domain}" '{"ttl":300,"rdata":["'"${txtvalue}"'"]}'; then
_err "Deleting the record did not succeed, please verify/check."
return 1
fi
_contains "$response" ""
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"
_debug response "$response"
if [ -z "$h" ]; then
#not valid
return 1
fi
if ! _ultra_rest GET "zones"; then
return 1
fi
if _contains "${response}" "${h}." >/dev/null; then
_domain_id=$(echo "$response" | _egrep_o "${h}")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="${h}"
_debug sub_domain "${_sub_domain}"
_debug domain "${_domain}"
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_ultra_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
_debug TOKEN "${AUTH_TOKEN}"
_ultra_login
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer ${AUTH_TOKEN}"
if [ "$m" != "GET" ]; then
_debug data "${data}"
response="$(_post "${data}" "${ULTRA_API}"/"${ep}" "" "${m}")"
else
response="$(_get "$ULTRA_API/$ep")"
fi
}
_ultra_login() {
export _H1=""
export _H2=""
AUTH_TOKEN=$(_post "grant_type=password&username=${ULTRA_USR}&password=${ULTRA_PWD}" "${ULTRA_API}authorization/token" | cut -d, -f3 | cut -d\" -f4)
export AUTH_TOKEN
}

View File

@ -50,34 +50,16 @@ dns_unoeuro_add() {
_err "Error" _err "Error"
return 1 return 1
fi fi
_info "Adding record"
if ! _contains "$response" "$_sub_domain" >/dev/null; then if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; 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 if _contains "$response" "\"status\": 200" >/dev/null; then
_info "Updated, OK" _info "Added, OK"
return 0 return 0
else
_err "Add txt record error."
return 1
fi fi
_err "Update error"
return 1
fi fi
} }
@ -122,23 +104,24 @@ dns_unoeuro_rm() {
if ! _contains "$response" "$_sub_domain"; then if ! _contains "$response" "$_sub_domain"; then
_info "Don't need to remove." _info "Don't need to remove."
else else
record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1) for record_line_number in $(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1); do
record_line_number=$(_math "$record_line_number" - 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_line_number" "$record_line_number"
_debug "record_id" "$record_id" 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 if [ -z "$record_id" ]; then
_err "Can not get record id to remove." _err "Can not get record id to remove."
return 1 return 1
fi fi
if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then
_err "Delete record error." _err "Delete record error."
return 1 return 1
fi fi
_contains "$response" "\"status\": 200" _contains "$response" "\"status\": 200"
done
fi fi
} }
#################### Private functions below ################################## #################### Private functions below ##################################

149
dnsapi/dns_zone.sh Executable file
View File

@ -0,0 +1,149 @@
#!/usr/bin/env sh
# Zone.ee dns API
# https://help.zone.eu/kb/zoneid-api-v2/
# required ZONE_Username and ZONE_Key
ZONE_Api="https://api.zone.eu/v2"
######## Public functions #####################
#Usage: dns_zone_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_zone_add() {
fulldomain=$1
txtvalue=$2
_info "Using zone.ee dns api"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
ZONE_Username="${ZONE_Username:-$(_readaccountconf_mutable ZONE_Username)}"
ZONE_Key="${ZONE_Key:-$(_readaccountconf_mutable ZONE_Key)}"
if [ -z "$ZONE_Username" ] || [ -z "$ZONE_Key" ]; then
ZONE_Username=""
ZONE_Key=""
_err "Zone api key and username must be present."
return 1
fi
_saveaccountconf_mutable ZONE_Username "$ZONE_Username"
_saveaccountconf_mutable ZONE_Key "$ZONE_Key"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug "Adding txt record"
if _zone_rest POST "dns/${_domain}/txt" "{\"name\": \"$fulldomain\", \"destination\": \"$txtvalue\"}"; then
if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
_info "Added, OK"
return 0
else
_err "Adding txt record error."
return 1
fi
else
_err "Adding txt record error."
fi
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_zone_rm() {
fulldomain=$1
txtvalue=$2
_info "Using zone.ee dns api"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
ZONE_Username="${ZONE_Username:-$(_readaccountconf_mutable ZONE_Username)}"
ZONE_Key="${ZONE_Key:-$(_readaccountconf_mutable ZONE_Key)}"
if [ -z "$ZONE_Username" ] || [ -z "$ZONE_Key" ]; then
ZONE_Username=""
ZONE_Key=""
_err "Zone api key and username must be present."
return 1
fi
_saveaccountconf_mutable ZONE_Username "$ZONE_Username"
_saveaccountconf_mutable ZONE_Key "$ZONE_Key"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug "Getting txt records"
_debug _domain "$_domain"
_zone_rest GET "dns/${_domain}/txt"
if printf "%s" "$response" | grep \"error\" >/dev/null; then
_err "Error"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain\"" | wc -l)
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Nothing to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\",\"resource_url\":\"[^\"]*\",\"name\":\"$fulldomain\"," | cut -d : -f2 | cut -d , -f1 | tr -d \" | _head_n 1)
if [ -z "$record_id" ]; then
_err "No id found to remove."
return 1
fi
if ! _zone_rest DELETE "dns/${_domain}/txt/$record_id"; then
_err "Record deleting error."
return 1
fi
_info "Record deleted"
return 0
fi
}
#################### Private functions below ##################################
_zone_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
realm="$(printf "%s" "$ZONE_Username:$ZONE_Key" | _base64)"
export _H1="Authorization: Basic $realm"
export _H2="Content-Type: application/json"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$ZONE_Api/$ep" "" "$m")"
else
response="$(_get "$ZONE_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_get_root() {
domain=$1
i=2
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
return 1
fi
if ! _zone_rest GET "dns/$h/a"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain=$h
return 0
fi
i=$(_math "$i" + 1)
done
return 0
}