Compare commits

...
539 Commits
44 changed files with 5153 additions and 530 deletions
+2 -2
View File
@@ -1,4 +1,6 @@
<!-- <!--
请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试输出,我帮不了你.
如何调试 https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh
If it is a bug report: If it is a bug report:
- make sure you are able to repro it on the latest released version. - make sure you are able to repro it on the latest released version.
@@ -8,13 +10,11 @@ You can install the latest version by: `acme.sh --upgrade`
- Refer to the [WIKI](https://wiki.acme.sh). - Refer to the [WIKI](https://wiki.acme.sh).
- Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh). - Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh).
--> -->
Steps to reproduce Steps to reproduce
------------------ ------------------
Debug log Debug log
----------------- -----------------
+4 -5
View File
@@ -26,14 +26,13 @@ install:
_old_path="$PATH"; _old_path="$PATH";
echo "PATH=$PATH"; echo "PATH=$PATH";
export PATH=""; export PATH="";
export OPENSSL_BIN="/usr/local/openssl"; export ACME_OPENSSL_BIN="/usr/local/openssl";
openssl version 2>&1 || true; openssl version 2>&1 || true;
$OPENSSL_BIN version 2>&1 || true; $ACME_OPENSSL_BIN version 2>&1 || true;
export PATH="$_old_path"; export PATH="$_old_path";
fi fi
script: script:
- echo "TEST_LOCAL=$TEST_LOCAL"
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)" - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
- command -V openssl && openssl version - command -V openssl && openssl version
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi
@@ -44,8 +43,8 @@ script:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi
- cd .. - cd ..
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
matrix: matrix:
+61
View File
@@ -0,0 +1,61 @@
FROM alpine
RUN apk update -f \
&& apk --no-cache add -f \
openssl \
curl \
netcat-openbsd \
&& rm -rf /var/cache/apk/*
ENV LE_CONFIG_HOME /acme.sh
ENV AUTO_UPGRADE 1
#Install
ADD ./ /install_acme.sh/
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh
RUN for verb in help \
version \
install \
uninstall \
upgrade \
issue \
signcsr \
deploy \
install-cert \
renew \
renew-all \
revoke \
remove \
list \
showcsr \
install-cronjob \
uninstall-cronjob \
cron \
toPkcs \
toPkcs8 \
update-account \
register-account \
create-account-key \
create-domain-key \
createCSR \
deactivate \
; do \
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
; done
RUN printf "%b" '#!'"/usr/bin/env sh\n \
if [ \"\$1\" = \"daemon\" ]; then \n \
crond -f\n \
else \n \
/root/.acme.sh/acme.sh --config-home /acme.sh \"\$@\"\n \
fi" >/entry.sh && chmod +x /entry.sh
VOLUME /acme.sh
ENTRYPOINT ["/entry.sh"]
CMD ["--help"]
+104 -29
View File
@@ -1,4 +1,6 @@
# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) # An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)
[![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- An ACME protocol client written purely in Shell (Unix shell) language. - An ACME protocol client written purely in Shell (Unix shell) language.
- Full ACME protocol implementation. - Full ACME protocol implementation.
- Simple, powerful and very easy to use. You only need 3 minutes to learn it. - Simple, powerful and very easy to use. You only need 3 minutes to learn it.
@@ -7,14 +9,33 @@
- Purely written in Shell with no dependencies on python or the official Let's Encrypt client. - Purely written in Shell with no dependencies on python or the official Let's Encrypt client.
- Just one script to issue, renew and install your certificates automatically. - Just one script to issue, renew and install your certificates automatically.
- DOES NOT require `root/sudoer` access. - DOES NOT require `root/sudoer` access.
- Docker friendly
- IPv6 support
It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
Wiki: https://github.com/Neilpang/acme.sh/wiki Wiki: https://github.com/Neilpang/acme.sh/wiki
For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker)
Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) # [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E)
# Who are using **acme.sh**
- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/)
- [ruby-china.org](https://ruby-china.org/topics/31983)
- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer))
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
- [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html)
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
- [archlinux](https://aur.archlinux.org/packages/acme.sh-git/)
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials)
# Tested OS # Tested OS
@@ -41,7 +62,7 @@ Wiki: https://github.com/Neilpang/acme.sh/wiki
|19|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux |19|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux
|20|[![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX |20|[![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX
For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest): For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest):
https://github.com/Neilpang/acmetest https://github.com/Neilpang/acmetest
@@ -51,7 +72,9 @@ https://github.com/Neilpang/acmetest
- Webroot mode - Webroot mode
- Standalone mode - Standalone mode
- Apache mode - Apache mode
- Nginx mode ( Beta )
- DNS mode - DNS mode
- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
# 1. How to install # 1. How to install
@@ -116,13 +139,25 @@ root@v1:~# acme.sh -h
acme.sh --issue -d example.com -w /home/wwwroot/example.com acme.sh --issue -d example.com -w /home/wwwroot/example.com
``` ```
or:
```bash
acme.sh --issue -d example.com -w /home/username/public_html
```
or:
```bash
acme.sh --issue -d example.com -w /var/www/html
```
**Example 2:** Multiple domains in the same cert. **Example 2:** Multiple domains in the same cert.
```bash ```bash
acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com
``` ```
The parameter `/home/wwwroot/example.com` is the web root folder. You **MUST** have `write access` to this folder. The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder.
Second argument **"example.com"** is the main domain you want to issue the cert for. Second argument **"example.com"** is the main domain you want to issue the cert for.
You must have at least one domain there. You must have at least one domain there.
@@ -143,18 +178,18 @@ You **MUST** use this command to copy the certs to the target files, **DO NOT**
**Apache** example: **Apache** example:
```bash ```bash
acme.sh --installcert -d example.com \ acme.sh --install-cert -d example.com \
--certpath /path/to/certfile/in/apache/cert.pem \ --cert-file /path/to/certfile/in/apache/cert.pem \
--keypath /path/to/keyfile/in/apache/key.pem \ --key-file /path/to/keyfile/in/apache/key.pem \
--fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \ --fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd "service apache2 force-reload" --reloadcmd "service apache2 force-reload"
``` ```
**Nginx** example: **Nginx** example:
```bash ```bash
acme.sh --installcert -d example.com \ acme.sh --install-cert -d example.com \
--keypath /path/to/keyfile/in/nginx/key.pem \ --key-file /path/to/keyfile/in/nginx/key.pem \
--fullchainpath /path/to/fullchain/nginx/cert.pem \ --fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd "service nginx force-reload" --reloadcmd "service nginx force-reload"
``` ```
@@ -164,7 +199,7 @@ The ownership and permission info of existing files are preserved. You may want
Install/copy the issued cert/key to the production Apache or Nginx path. Install/copy the issued cert/key to the production Apache or Nginx path.
The cert will be `renewed every **60** days by default` (which is configurable). Once the cert is renewed, the Apache/Nginx service will be restarted automatically by the command: `service apache2 restart` or `service nginx restart`. The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`.
# 4. Use Standalone server to issue cert # 4. Use Standalone server to issue cert
@@ -211,8 +246,27 @@ acme.sh --issue --apache -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
# 7. Use Nginx mode
# 7. Use DNS mode: **(requires you to be root/sudoer, since it is required to interact with Nginx server)**
If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
Just set string "nginx" as the second argument.
It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
So, the config is not changed.
```
acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
```
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 8. Use DNS mode:
Support the `dns-01` challenge. Support the `dns-01` challenge.
@@ -243,7 +297,7 @@ acme.sh --renew -d example.com
Ok, it's finished. Ok, it's finished.
# 8. Automatic DNS API integration # 9. 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.
@@ -255,18 +309,38 @@ You don't have to do anything manually!
1. DNSPod.cn API 1. DNSPod.cn API
1. CloudXNS.com API 1. CloudXNS.com API
1. GoDaddy.com API 1. GoDaddy.com API
1. OVH, kimsufi, soyoustart and runabove API
1. AWS Route 53
1. PowerDNS.com API 1. PowerDNS.com API
1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api 1. OVH, kimsufi, soyoustart and runabove API
(DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.) 1. nsupdate API
1. LuaDNS.com API 1. LuaDNS.com API
1. DNSMadeEasy.com API 1. DNSMadeEasy.com API
1. nsupdate API 1. AWS Route 53
1. aliyun.com(阿里云) API 1. aliyun.com(阿里云) API
1. ISPConfig 3.1 API 1. ISPConfig 3.1 API
1. Alwaysdata.com 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
And:
1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
(DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
**More APIs coming soon...** **More APIs coming soon...**
If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project. If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
@@ -274,7 +348,7 @@ If your DNS provider is not on the supported list above, you can write your own
For more details: [How to use DNS API](dnsapi) For more details: [How to use DNS API](dnsapi)
# 9. Issue ECC certificates # 10. Issue ECC certificates
`Let's Encrypt` can now issue **ECDSA** certificates. `Let's Encrypt` can now issue **ECDSA** certificates.
@@ -284,7 +358,7 @@ Just set the `length` parameter with a prefix `ec-`.
For example: For example:
### Single domain ECC cerfiticate ### Single domain ECC certificate
```bash ```bash
acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
@@ -305,7 +379,7 @@ Valid values are:
3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)** 3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
# 10. How to renew the issued certs # 11. How to renew the issued certs
No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
@@ -322,7 +396,7 @@ acme.sh --renew -d example.com --force --ecc
``` ```
# 11. How to upgrade `acme.sh` # 12. 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.
@@ -347,26 +421,26 @@ acme.sh --upgrade --auto-upgrade 0
``` ```
# 12. Issue a cert from an existing CSR # 13. 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
# Under the Hood # 14. 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:
# Acknowledgments # 15. Acknowledgments
1. Acme-tiny: https://github.com/diafygi/acme-tiny 1. Acme-tiny: https://github.com/diafygi/acme-tiny
2. ACME protocol: https://github.com/ietf-wg-acme/acme 2. ACME protocol: https://github.com/ietf-wg-acme/acme
3. Certbot: https://github.com/certbot/certbot 3. Certbot: https://github.com/certbot/certbot
# License & Others # 16. License & Others
License is GPLv3 License is GPLv3
@@ -375,8 +449,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.
# Donate # 17. Donate
Your donation makes **acme.sh** better:
1. PayPal: 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)
+1504 -456
View File
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
# 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).
Here are the scripts to deploy the certs/key to the server/services.
## 1. Deploy the certs to your cpanel host.
(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.
(TODO)
## 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
```
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to apache server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
apache_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "Deploy cert to apache server, Not implemented yet"
return 1
}
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env sh
#Here is the script to deploy the cert to your cpanel account by the cpanel APIs.
#returns 0 means success, otherwise error.
#export DEPLOY_CPANEL_USER=myusername
#export DEPLOY_CPANEL_PASSWORD=PASSWORD
######## Public functions #####################
#domain keyfile certfile cafile fullchain
cpanel_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "Not implemented yet"
return 1
}
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to dovecot server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
dovecot_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "Not implemented yet"
return 1
}
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to exim4 server.
#returns 0 means success, otherwise error.
#DEPLOY_EXIM4_CONF="/etc/exim/exim.conf"
#DEPLOY_EXIM4_RELOAD="service exim4 restart"
######## Public functions #####################
#domain keyfile certfile cafile fullchain
exim4_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"
_ssl_path="/etc/acme.sh/exim4"
if ! mkdir -p "$_ssl_path"; then
_err "Can not create folder:$_ssl_path"
return 1
fi
_info "Copying key and cert"
_real_key="$_ssl_path/exim4.key"
if ! cat "$_ckey" >"$_real_key"; then
_err "Error: write key file to: $_real_key"
return 1
fi
_real_fullchain="$_ssl_path/exim4.pem"
if ! cat "$_cfullchain" >"$_real_fullchain"; then
_err "Error: write key file to: $_real_fullchain"
return 1
fi
DEFAULT_EXIM4_RELOAD="service exim4 restart"
_reload="${DEPLOY_EXIM4_RELOAD:-$DEFAULT_EXIM4_RELOAD}"
if [ -z "$IS_RENEW" ]; then
DEFAULT_EXIM4_CONF="/etc/exim/exim.conf"
if [ ! -f "$DEFAULT_EXIM4_CONF" ]; then
DEFAULT_EXIM4_CONF="/etc/exim4/exim4.conf.template"
fi
_exim4_conf="${DEPLOY_EXIM4_CONF:-$DEFAULT_EXIM4_CONF}"
_debug _exim4_conf "$_exim4_conf"
if [ ! -f "$_exim4_conf" ]; then
if [ -z "$DEPLOY_EXIM4_CONF" ]; then
_err "exim4 conf is not found, please define DEPLOY_EXIM4_CONF"
return 1
else
_err "It seems that the specified exim4 conf is not valid, please check."
return 1
fi
fi
if [ ! -w "$_exim4_conf" ]; then
_err "The file $_exim4_conf is not writable, please change the permission."
return 1
fi
_backup_conf="$DOMAIN_BACKUP_PATH/exim4.conf.bak"
_info "Backup $_exim4_conf to $_backup_conf"
cp "$_exim4_conf" "$_backup_conf"
_info "Modify exim4 conf: $_exim4_conf"
if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" \
&& _setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then
_info "Set config success!"
else
_err "Config exim4 server error, please report bug to us."
_info "Restoring exim4 conf"
if cat "$_backup_conf" >"$_exim4_conf"; then
_info "Restore conf success"
eval "$_reload"
else
_err "Oops, error restore exim4 conf, please report bug to us."
fi
return 1
fi
fi
_info "Run reload: $_reload"
if eval "$_reload"; then
_info "Reload success!"
if [ "$DEPLOY_EXIM4_CONF" ]; then
_savedomainconf DEPLOY_EXIM4_CONF "$DEPLOY_EXIM4_CONF"
else
_cleardomainconf DEPLOY_EXIM4_CONF
fi
if [ "$DEPLOY_EXIM4_RELOAD" ]; then
_savedomainconf DEPLOY_EXIM4_RELOAD "$DEPLOY_EXIM4_RELOAD"
else
_cleardomainconf DEPLOY_EXIM4_RELOAD
fi
return 0
else
_err "Reload error, restoring"
if cat "$_backup_conf" >"$_exim4_conf"; then
_info "Restore conf success"
eval "$_reload"
else
_err "Oops, error restore exim4 conf, please report bug to us."
fi
return 1
fi
return 0
}
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to haproxy server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
haproxy_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to haproxy server, Not implemented yet"
return 1
}
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env sh
#Here is a sample custom api script.
#This file name is "myapi.sh"
#So, here must be a method myapi_deploy()
#Which will be called by acme.sh to deploy the cert
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
keychain_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"
/usr/bin/security import "$_ckey" -k "/Library/Keychains/System.keychain"
/usr/bin/security import "$_ccert" -k "/Library/Keychains/System.keychain"
/usr/bin/security import "$_cca" -k "/Library/Keychains/System.keychain"
/usr/bin/security import "$_cfullchain" -k "/Library/Keychains/System.keychain"
return 0
}
Executable
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env sh
# If certificate already exist it will update only cert and key not touching other parameter
# If certificate doesn't exist it will only upload cert and key and not set other parameter
# Note that we deploy full chain
# Written by Geoffroi Genot <ggenot@voxbone.com>
######## Public functions #####################
#domain keyfile certfile cafile fullchain
kong_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_info "Deploying certificate on Kong instance"
if [ -z "$KONG_URL" ]; then
_debug "KONG_URL Not set, using default http://localhost:8001"
KONG_URL="http://localhost:8001"
fi
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
#Get ssl_uuid linked to the domain
ssl_uuid=$(_get "$KONG_URL/certificates/$_cdomain" | _normalizeJson | _egrep_o '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
if [ -z "$ssl_uuid" ]; then
_debug "Unable to get Kong ssl_uuid for domain $_cdomain"
_debug "Make sure that KONG_URL is correctly configured"
_debug "Make sure that a Kong certificate match the sni"
_debug "Kong url: $KONG_URL"
_info "No existing certificate, creating..."
#return 1
fi
#Save kong url if it's succesful (First run case)
_saveaccountconf KONG_URL "$KONG_URL"
#Generate DEIM
delim="-----MultipartDelimiter$(date "+%s%N")"
nl="\015\012"
#Set Header
_H1="Content-Type: multipart/form-data; boundary=$delim"
#Generate data for request (Multipart/form-data with mixed content)
if [ -z "$ssl_uuid" ]; then
#set sni to domain
content="--$delim${nl}Content-Disposition: form-data; name=\"snis\"${nl}${nl}$_cdomain"
fi
#add key
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
#Add cert
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
#Close multipart
content="$content${nl}--$delim--${nl}"
#Convert CRLF
content=$(printf %b "$content")
#DEBUG
_debug header "$_H1"
_debug content "$content"
#Check if sslcreated (if not => POST else => PATCH)
if [ -z "$ssl_uuid" ]; then
#Post certificate to Kong
response=$(_post "$content" "$KONG_URL/certificates" "" "POST")
else
#patch
response=$(_post "$content" "$KONG_URL/certificates/$ssl_uuid" "" "PATCH")
fi
if ! [ "$(echo "$response" | _egrep_o "created_at")" = "created_at" ]; then
_err "An error occurred with cert upload. Check response:"
_err "$response"
return 1
fi
_debug response "$response"
_info "Certificate successfully deployed"
}
Regular → Executable
View File
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to mysqld server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
mysqld_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to mysqld server, Not implemented yet"
return 1
}
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to nginx server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
nginx_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to nginx server, Not implemented yet"
return 1
}
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to opensshd server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
opensshd_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to opensshd server, Not implemented yet"
return 1
}
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to pureftpd server.
#returns 0 means success, otherwise error.
######## Public functions #####################
#domain keyfile certfile cafile fullchain
pureftpd_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_err "deploy cert to pureftpd server, Not implemented yet"
return 1
}
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to vsftpd server.
#returns 0 means success, otherwise error.
#DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
#DEPLOY_VSFTPD_RELOAD="service vsftpd restart"
######## Public functions #####################
#domain keyfile certfile cafile fullchain
vsftpd_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"
_ssl_path="/etc/acme.sh/vsftpd"
if ! mkdir -p "$_ssl_path"; then
_err "Can not create folder:$_ssl_path"
return 1
fi
_info "Copying key and cert"
_real_key="$_ssl_path/vsftpd.key"
if ! cat "$_ckey" >"$_real_key"; then
_err "Error: write key file to: $_real_key"
return 1
fi
_real_fullchain="$_ssl_path/vsftpd.chain.pem"
if ! cat "$_cfullchain" >"$_real_fullchain"; then
_err "Error: write key file to: $_real_fullchain"
return 1
fi
DEFAULT_VSFTPD_RELOAD="service vsftpd restart"
_reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}"
if [ -z "$IS_RENEW" ]; then
DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf"
_vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}"
if [ ! -f "$_vsftpd_conf" ]; then
if [ -z "$DEPLOY_VSFTPD_CONF" ]; then
_err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF"
return 1
else
_err "It seems that the specified vsftpd conf is not valid, please check."
return 1
fi
fi
if [ ! -w "$_vsftpd_conf" ]; then
_err "The file $_vsftpd_conf is not writable, please change the permission."
return 1
fi
_backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak"
_info "Backup $_vsftpd_conf to $_backup_conf"
cp "$_vsftpd_conf" "$_backup_conf"
_info "Modify vsftpd conf: $_vsftpd_conf"
if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \
&& _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" \
&& _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then
_info "Set config success!"
else
_err "Config vsftpd server error, please report bug to us."
_info "Restoring vsftpd conf"
if cat "$_backup_conf" >"$_vsftpd_conf"; then
_info "Restore conf success"
eval "$_reload"
else
_err "Oops, error restore vsftpd conf, please report bug to us."
fi
return 1
fi
fi
_info "Run reload: $_reload"
if eval "$_reload"; then
_info "Reload success!"
if [ "$DEPLOY_VSFTPD_CONF" ]; then
_savedomainconf DEPLOY_VSFTPD_CONF "$DEPLOY_VSFTPD_CONF"
else
_cleardomainconf DEPLOY_VSFTPD_CONF
fi
if [ "$DEPLOY_VSFTPD_RELOAD" ]; then
_savedomainconf DEPLOY_VSFTPD_RELOAD "$DEPLOY_VSFTPD_RELOAD"
else
_cleardomainconf DEPLOY_VSFTPD_RELOAD
fi
return 0
else
_err "Reload error, restoring"
if cat "$_backup_conf" >"$_vsftpd_conf"; then
_info "Restore conf success"
eval "$_reload"
else
_err "Oops, error restore vsftpd conf, please report bug to us."
fi
return 1
fi
return 0
}
+249 -1
View File
@@ -140,7 +140,7 @@ Finally, make the DNS server and update Key available to `acme.sh`
``` ```
export NSUPDATE_SERVER="dns.example.com" export NSUPDATE_SERVER="dns.example.com"
export NSUPDATE_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa==" export NSUPDATE_KEY="/path/to/your/nsupdate.key"
``` ```
Ok, let's issue a cert now: Ok, let's issue a cert now:
@@ -257,6 +257,254 @@ 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 The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused
when needed. 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
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 acme_key algorithm 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/
```
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
```
## 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
```
# Use custom API # Use custom API
If your API is not supported yet, you can write your own DNS API. If your API is not supported yet, you can write your own DNS API.
Regular → Executable
View File
Regular → Executable
+1 -1
View File
@@ -67,7 +67,7 @@ _get_root() {
} }
_ali_rest() { _ali_rest() {
signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(_hex "$Ali_Secret&")" | _base64) signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(printf "%s" "$Ali_Secret&" | _hex_dump | tr -d " ")" | _base64)
signature=$(_ali_urlencode "$signature") signature=$(_ali_urlencode "$signature")
url="$Ali_API?$query&Signature=$signature" url="$Ali_API?$query&Signature=$signature"
Regular → Executable
+23 -7
View File
@@ -88,12 +88,25 @@ _get_root() {
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
_debug "IsTruncated"
_nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | cut -d '>' -f 2 | cut -d '<' -f 1)"
_debug "NextMarker" "$_nextMarker"
if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then
_debug "Truncated request OK"
i=2
p=1
continue
else
_err "Truncated request error."
fi
fi
#not valid #not valid
return 1 return 1
fi fi
if _contains "$response" "<Name>$h.</Name>"; then if _contains "$response" "<Name>$h.</Name>"; then
hostedzone="$(echo "$response" | sed 's/<HostedZone>/\n&/g' | _egrep_o "<HostedZone>.*?<Name>$h.<.Name>.*?<.HostedZone>")" hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
_debug hostedzone "$hostedzone" _debug hostedzone "$hostedzone"
if [ -z "$hostedzone" ]; then if [ -z "$hostedzone" ]; then
_err "Error, can not get hostedzone." _err "Error, can not get hostedzone."
@@ -143,7 +156,7 @@ aws_rest() {
CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n" CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
SignedHeaders="host;x-amz-date" SignedHeaders="host;x-amz-date"
if [ -n "$AWS_SESSION_TOKEN" ]; then if [ -n "$AWS_SESSION_TOKEN" ]; then
export _H2="x-amz-security-token: $AWS_SESSION_TOKEN" export _H3="x-amz-security-token: $AWS_SESSION_TOKEN"
CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n" CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n"
SignedHeaders="${SignedHeaders};x-amz-security-token" SignedHeaders="${SignedHeaders};x-amz-security-token"
fi fi
@@ -181,10 +194,10 @@ aws_rest() {
#kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################ #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
_debug2 kSecret "$kSecret" _secure_debug2 kSecret "$kSecret"
kSecretH="$(_hex "$kSecret")" kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
_debug2 kSecretH "$kSecretH" _secure_debug2 kSecretH "$kSecretH"
kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)" kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
_debug2 kDateH "$kDateH" _debug2 kDateH "$kDateH"
@@ -204,10 +217,13 @@ aws_rest() {
Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature" Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
_debug2 Authorization "$Authorization" _debug2 Authorization "$Authorization"
_H3="Authorization: $Authorization" _H2="Authorization: $Authorization"
_debug _H3 "$_H3" _debug _H2 "$_H2"
url="$AWS_URL/$ep" url="$AWS_URL/$ep"
if [ "$qsr" ]; then
url="$AWS_URL/$ep?$qsr"
fi
if [ "$mtd" = "GET" ]; then if [ "$mtd" = "GET" ]; then
response="$(_get "$url")" response="$(_get "$url")"
+15 -2
View File
@@ -14,6 +14,8 @@ dns_cf_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key="" CF_Key=""
CF_Email="" CF_Email=""
@@ -29,8 +31,8 @@ dns_cf_add() {
fi fi
#save the api key and email to the account conf file. #save the api key and email to the account conf file.
_saveaccountconf CF_Key "$CF_Key" _saveaccountconf_mutable CF_Key "$CF_Key"
_saveaccountconf CF_Email "$CF_Email" _saveaccountconf_mutable CF_Email "$CF_Email"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
@@ -83,6 +85,17 @@ dns_cf_add() {
dns_cf_rm() { dns_cf_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key=""
CF_Email=""
_err "You don't specify cloudflare api key and email yet."
_err "Please create you key and try again."
return 1
fi
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root "$fulldomain"; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env sh
# Author: Boyan Peychev <boyan at cloudns dot net>
# Repository: https://github.com/ClouDNS/acme.sh/
#CLOUDNS_AUTH_ID=XXXXX
#CLOUDNS_AUTH_PASSWORD="YYYYYYYYY"
CLOUDNS_API="https://api.cloudns.net"
######## Public functions #####################
#Usage: dns_cloudns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_cloudns_add() {
_info "Using cloudns"
if ! _dns_cloudns_init_check; then
return 1
fi
zone="$(_dns_cloudns_get_zone_name "$1")"
if [ -z "$zone" ]; then
_err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup."
return 1
fi
host="$(echo "$1" | sed "s/\.$zone\$//")"
record=$2
record_id=$(_dns_cloudns_get_record_id "$zone" "$host")
_debug zone "$zone"
_debug host "$host"
_debug record "$record"
_debug record_id "$record_id"
if [ -z "$record_id" ]; then
_info "Adding the TXT record for $1"
_dns_cloudns_http_api_call "dns/add-record.json" "domain-name=$zone&record-type=TXT&host=$host&record=$record&ttl=60"
if ! _contains "$response" "\"status\":\"Success\""; then
_err "Record cannot be added."
return 1
fi
_info "Added."
else
_info "Updating the TXT record for $1"
_dns_cloudns_http_api_call "dns/mod-record.json" "domain-name=$zone&record-id=$record_id&record-type=TXT&host=$host&record=$record&ttl=60"
if ! _contains "$response" "\"status\":\"Success\""; then
_err "The TXT record for $1 cannot be updated."
return 1
fi
_info "Updated."
fi
return 0
}
#Usage: dns_cloudns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_cloudns_rm() {
_info "Using cloudns"
if ! _dns_cloudns_init_check; then
return 1
fi
if [ -z "$zone" ]; then
zone="$(_dns_cloudns_get_zone_name "$1")"
if [ -z "$zone" ]; then
_err "Missing DNS zone at ClouDNS. Please log into your control panel and create the required DNS zone for the initial setup."
return 1
fi
fi
host="$(echo "$1" | sed "s/\.$zone\$//")"
record=$2
record_id=$(_dns_cloudns_get_record_id "$zone" "$host")
_debug zone "$zone"
_debug host "$host"
_debug record "$record"
_debug record_id "$record_id"
if [ ! -z "$record_id" ]; then
_info "Deleting the TXT record for $1"
_dns_cloudns_http_api_call "dns/delete-record.json" "domain-name=$zone&record-id=$record_id"
if ! _contains "$response" "\"status\":\"Success\""; then
_err "The TXT record for $1 cannot be deleted."
return 1
fi
_info "Deleted."
fi
return 0
}
#################### Private functions below ##################################
_dns_cloudns_init_check() {
if [ ! -z "$CLOUDNS_INIT_CHECK_COMPLETED" ]; then
return 0
fi
if [ -z "$CLOUDNS_AUTH_ID" ]; then
_err "CLOUDNS_AUTH_ID is not configured"
return 1
fi
if [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
_err "CLOUDNS_AUTH_PASSWORD is not configured"
return 1
fi
_dns_cloudns_http_api_call "dns/login.json" ""
if ! _contains "$response" "\"status\":\"Success\""; then
_err "Invalid CLOUDNS_AUTH_ID or CLOUDNS_AUTH_PASSWORD. Please check your login credentials."
return 1
fi
CLOUDNS_INIT_CHECK_COMPLETED=1
return 0
}
_dns_cloudns_get_zone_name() {
i=2
while true; do
zoneForCheck=$(printf "%s" "$1" | cut -d . -f $i-100)
if [ -z "$zoneForCheck" ]; then
return 1
fi
_debug zoneForCheck "$zoneForCheck"
_dns_cloudns_http_api_call "dns/get-zone-info.json" "domain-name=$zoneForCheck"
if ! _contains "$response" "\"status\":\"Failed\""; then
echo "$zoneForCheck"
return 0
fi
i=$(_math "$i" + 1)
done
return 1
}
_dns_cloudns_get_record_id() {
_dns_cloudns_http_api_call "dns/records.json" "domain-name=$1&host=$2&type=TXT"
if _contains "$response" "\"id\":"; then
echo "$response" | cut -d '"' -f 2
return 0
fi
return 1
}
_dns_cloudns_http_api_call() {
method=$1
_debug CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
_debug CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
if [ -z "$2" ]; then
data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD"
else
data="auth-id=$CLOUDNS_AUTH_ID&auth-password=$CLOUDNS_AUTH_PASSWORD&$2"
fi
response="$(_get "$CLOUDNS_API/$method?$data")"
_debug2 response "$response"
return 0
}
+5 -6
View File
@@ -82,7 +82,7 @@ existing_records() {
return 1 return 1
fi fi
seg=$(printf "%s\n" "$response" | _egrep_o '[^{]*host":"'"$_sub_domain"'"[^}]*\}') seg=$(printf "%s\n" "$response" | _egrep_o '"record_id":[^{]*host":"'"$_sub_domain"'"[^}]*\}')
_debug seg "$seg" _debug seg "$seg"
if [ -z "$seg" ]; then if [ -z "$seg" ]; then
return 0 return 0
@@ -155,7 +155,7 @@ _get_root() {
fi fi
if _contains "$response" "$h."; then if _contains "$response" "$h."; then
seg=$(printf "%s\n" "$response" | _egrep_o '[^{]*"'"$h"'."[^}]*}') seg=$(printf "%s\n" "$response" | _egrep_o '"id":[^{]*"'"$h"'."[^}]*}')
_debug seg "$seg" _debug seg "$seg"
_domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \") _domain_id=$(printf "%s\n" "$seg" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id" _debug _domain_id "$_domain_id"
@@ -209,8 +209,7 @@ _rest() {
return 1 return 1
fi fi
_debug2 response "$response" _debug2 response "$response"
if ! _contains "$response" '"message":"success"'; then
return 1 _contains "$response" '"code":1'
fi
return 0
} }
+328
View File
@@ -0,0 +1,328 @@
#!/usr/bin/env sh
########
# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/Neilpang/acme.sh)
#
# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
#
# Dependencies:
# -------------
# - oathtool (When using 2 Factor Authentication)
#
# Issues:
# -------
# Any issues / questions / suggestions can be posted here:
# https://github.com/noplanman/cyon-api/issues
#
# Author: Armando Lüscher <armando@noplanman.ch>
########
dns_cyon_add() {
_cyon_load_credentials \
&& _cyon_load_parameters "$@" \
&& _cyon_print_header "add" \
&& _cyon_login \
&& _cyon_change_domain_env \
&& _cyon_add_txt \
&& _cyon_logout
}
dns_cyon_rm() {
_cyon_load_credentials \
&& _cyon_load_parameters "$@" \
&& _cyon_print_header "delete" \
&& _cyon_login \
&& _cyon_change_domain_env \
&& _cyon_delete_txt \
&& _cyon_logout
}
#########################
### PRIVATE FUNCTIONS ###
#########################
_cyon_load_credentials() {
# Convert loaded password to/from base64 as needed.
if [ "${CY_Password_B64}" ]; then
CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")"
elif [ "${CY_Password}" ]; then
CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
fi
if [ -z "${CY_Username}" ] || [ -z "${CY_Password}" ]; then
# Dummy entries to satisfy script checker.
CY_Username=""
CY_Password=""
CY_OTP_Secret=""
_err ""
_err "You haven't set your cyon.ch login credentials yet."
_err "Please set the required cyon environment variables."
_err ""
return 1
fi
# Save the login credentials to the account.conf file.
_debug "Save credentials to account.conf"
_saveaccountconf CY_Username "${CY_Username}"
_saveaccountconf CY_Password_B64 "$CY_Password_B64"
if [ ! -z "${CY_OTP_Secret}" ]; then
_saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
else
_clearaccountconf CY_OTP_Secret
fi
}
_cyon_is_idn() {
_idn_temp="$(printf "%s" "${1}" | tr -d "0-9a-zA-Z.,-_")"
_idn_temp2="$(printf "%s" "${1}" | grep -o "xn--")"
[ "$_idn_temp" ] || [ "$_idn_temp2" ]
}
_cyon_load_parameters() {
# Read the required parameters to add the TXT entry.
# shellcheck disable=SC2018,SC2019
fulldomain="$(printf "%s" "${1}" | tr "A-Z" "a-z")"
fulldomain_idn="${fulldomain}"
# Special case for IDNs, as cyon needs a domain environment change,
# which uses the "pretty" instead of the punycode version.
if _cyon_is_idn "${fulldomain}"; then
if ! _exists idn; then
_err "Please install idn to process IDN names."
_err ""
return 1
fi
fulldomain="$(idn -u "${fulldomain}")"
fulldomain_idn="$(idn -a "${fulldomain}")"
fi
_debug fulldomain "${fulldomain}"
_debug fulldomain_idn "${fulldomain_idn}"
txtvalue="${2}"
_debug txtvalue "${txtvalue}"
# This header is required for curl calls.
_H1="X-Requested-With: XMLHttpRequest"
export _H1
}
_cyon_print_header() {
if [ "${1}" = "add" ]; then
_info ""
_info "+---------------------------------------------+"
_info "| Adding DNS TXT entry to your cyon.ch domain |"
_info "+---------------------------------------------+"
_info ""
_info " * Full Domain: ${fulldomain}"
_info " * TXT Value: ${txtvalue}"
_info ""
elif [ "${1}" = "delete" ]; then
_info ""
_info "+-------------------------------------------------+"
_info "| Deleting DNS TXT entry from your cyon.ch domain |"
_info "+-------------------------------------------------+"
_info ""
_info " * Full Domain: ${fulldomain}"
_info ""
fi
}
_cyon_get_cookie_header() {
printf "Cookie: %s" "$(grep "cyon=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'cyon=[^;]*;' | tr -d ';')"
}
_cyon_login() {
_info " - Logging in..."
username_encoded="$(printf "%s" "${CY_Username}" | _url_encode)"
password_encoded="$(printf "%s" "${CY_Password}" | _url_encode)"
login_url="https://my.cyon.ch/auth/index/dologin-async"
login_data="$(printf "%s" "username=${username_encoded}&password=${password_encoded}&pathname=%2F")"
login_response="$(_post "$login_data" "$login_url")"
_debug login_response "${login_response}"
# Bail if login fails.
if [ "$(printf "%s" "${login_response}" | _cyon_get_response_success)" != "success" ]; then
_err " $(printf "%s" "${login_response}" | _cyon_get_response_message)"
_err ""
return 1
fi
_info " success"
# NECESSARY!! Load the main page after login, to get the new cookie.
_H2="$(_cyon_get_cookie_header)"
export _H2
_get "https://my.cyon.ch/" >/dev/null
# todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
# 2FA authentication with OTP?
if [ ! -z "${CY_OTP_Secret}" ]; then
_info " - Authorising with OTP code..."
if ! _exists oathtool; then
_err "Please install oathtool to use 2 Factor Authentication."
_err ""
return 1
fi
# Get OTP code with the defined secret.
otp_code="$(oathtool --base32 --totp "${CY_OTP_Secret}" 2>/dev/null)"
login_otp_url="https://my.cyon.ch/auth/multi-factor/domultifactorauth-async"
login_otp_data="totpcode=${otp_code}&pathname=%2F&rememberme=0"
login_otp_response="$(_post "$login_otp_data" "$login_otp_url")"
_debug login_otp_response "${login_otp_response}"
# Bail if OTP authentication fails.
if [ "$(printf "%s" "${login_otp_response}" | _cyon_get_response_success)" != "success" ]; then
_err " $(printf "%s" "${login_otp_response}" | _cyon_get_response_message)"
_err ""
return 1
fi
_info " success"
fi
_info ""
}
_cyon_logout() {
_info " - Logging out..."
_get "https://my.cyon.ch/auth/index/dologout" >/dev/null
_info " success"
_info ""
}
_cyon_change_domain_env() {
_info " - Changing domain environment..."
# Get the "example.com" part of the full domain name.
domain_env="$(printf "%s" "${fulldomain}" | sed -E -e 's/.*\.(.*\..*)$/\1/')"
_debug "Changing domain environment to ${domain_env}"
gloo_item_key="$(_get "https://my.cyon.ch/domain/" | tr '\n' ' ' | sed -E -e "s/.*data-domain=\"${domain_env}\"[^<]*data-itemkey=\"([^\"]*).*/\1/")"
_debug gloo_item_key "${gloo_item_key}"
domain_env_url="https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/${gloo_item_key}"
domain_env_response="$(_get "${domain_env_url}")"
_debug domain_env_response "${domain_env_response}"
if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)"
# Bail if domain environment change fails.
if [ "${domain_env_success}" != "true" ]; then
_err " $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
_err ""
return 1
fi
_info " success"
_info ""
}
_cyon_add_txt() {
_info " - Adding DNS TXT entry..."
add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}"
add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
_debug add_txt_response "${add_txt_response}"
if ! _cyon_check_if_2fa_missed "${add_txt_response}"; then return 1; fi
add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)"
# Bail if adding TXT entry fails.
if [ "${add_txt_status}" != "true" ]; then
_err " ${add_txt_message}"
_err ""
return 1
fi
_info " success (TXT|${fulldomain_idn}.|${txtvalue})"
_info ""
}
_cyon_delete_txt() {
_info " - Deleting DNS TXT entry..."
list_txt_url="https://my.cyon.ch/domain/dnseditor/list-async"
list_txt_response="$(_get "${list_txt_url}" | sed -e 's/data-hash/\\ndata-hash/g')"
_debug list_txt_response "${list_txt_response}"
if ! _cyon_check_if_2fa_missed "${list_txt_response}"; then return 1; fi
# Find and delete all acme challenge entries for the $fulldomain.
_dns_entries="$(printf "%b\n" "${list_txt_response}" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p')"
printf "%s" "${_dns_entries}" | while read -r _hash _identifier; do
dns_type="$(printf "%s" "$_identifier" | cut -d'|' -f1)"
dns_domain="$(printf "%s" "$_identifier" | cut -d'|' -f2)"
if [ "${dns_type}" != "TXT" ] || [ "${dns_domain}" != "${fulldomain_idn}." ]; then
continue
fi
hash_encoded="$(printf "%s" "${_hash}" | _url_encode)"
identifier_encoded="$(printf "%s" "${_identifier}" | _url_encode)"
delete_txt_url="https://my.cyon.ch/domain/dnseditor/delete-record-async"
delete_txt_data="$(printf "%s" "hash=${hash_encoded}&identifier=${identifier_encoded}")"
delete_txt_response="$(_post "$delete_txt_data" "$delete_txt_url")"
_debug delete_txt_response "${delete_txt_response}"
if ! _cyon_check_if_2fa_missed "${delete_txt_response}"; then return 1; fi
delete_txt_message="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_message)"
delete_txt_status="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_status)"
# Skip if deleting TXT entry fails.
if [ "${delete_txt_status}" != "true" ]; then
_err " ${delete_txt_message} (${_identifier})"
else
_info " success (${_identifier})"
fi
done
_info " done"
_info ""
}
_cyon_get_response_message() {
_egrep_o '"message":"[^"]*"' | cut -d : -f 2 | tr -d '"'
}
_cyon_get_response_status() {
_egrep_o '"status":\w*' | cut -d : -f 2
}
_cyon_get_response_success() {
_egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
}
_cyon_check_if_2fa_missed() {
# Did we miss the 2FA?
if test "${1#*multi_factor_form}" != "${1}"; then
_err " Missed OTP authentication!"
_err ""
return 1
fi
}
+205
View File
@@ -0,0 +1,205 @@
#!/usr/bin/env sh
## Will be called by acme.sh to add the txt record to your api system.
## returns 0 means success, otherwise error.
## Author: thewer <github at thewer.com>
## GitHub: https://github.com/gitwer/acme.sh
##
## Environment Variables Required:
##
## DO_API_KEY="75310dc4ca779ac39a19f6355db573b49ce92ae126553ebd61ac3a3ae34834cc"
##
##################### Public functions #####################
## Create the text record for validation.
## Usage: fulldomain txtvalue
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
dns_dgon_add() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
_info "Using digitalocean dns validation - add record"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
## save the env vars (key and domain split location) for later automated use
_saveaccountconf DO_API_KEY "$DO_API_KEY"
## split the domain for DO API
if ! _get_base_domain "$fulldomain"; then
_err "domain not found in your account for addition"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
## Set the header with our post type and key auth key
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $DO_API_KEY"
PURL='https://api.digitalocean.com/v2/domains/'$_domain'/records'
PBODY='{"type":"TXT","name":"'$_sub_domain'","data":"'$txtvalue'"}'
_debug PURL "$PURL"
_debug PBODY "$PBODY"
## the create request - post
## args: BODY, URL, [need64, httpmethod]
response="$(_post "$PBODY" "$PURL")"
## check response
if [ "$?" != "0" ]; then
_err "error in response: $response"
return 1
fi
_debug2 response "$response"
## finished correctly
return 0
}
## Remove the txt record after validation.
## Usage: fulldomain txtvalue
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
dns_dgon_rm() {
fulldomain="$(echo "$1" | _lower_case)"
txtvalue=$2
_info "Using digitalocean dns validation - remove record"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
## split the domain for DO API
if ! _get_base_domain "$fulldomain"; then
_err "domain not found in your account for removal"
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
## Set the header with our post type and key auth key
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $DO_API_KEY"
## get URL for the list of domains
## 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"
## while we dont have a record ID we keep going
while [ -z "$record" ]; do
## 1) get the URL
## the create request - get
## args: URL, [onlyheader, timeout]
domain_list="$(_get "$GURL")"
## 2) find record
## check for what we are looing for: "type":"A","name":"$_sub_domain"
record="$(echo "$domain_list" | _egrep_o "\"id\"\s*\:\s*\"*\d+\"*[^}]*\"name\"\s*\:\s*\"$_sub_domain\"[^}]*\"data\"\s*\:\s*\"$txtvalue\"")"
## 3) check record and get next page
if [ -z "$record" ]; 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\=\d+")"
if [ -z "$nextpage" ]; then
_err "no record and no nextpage in digital ocean DNS removal"
return 1
fi
_debug2 nextpage "$nextpage"
GURL="$nextpage"
fi
## we break out of the loop when we have a record
done
## we found the record
rec_id="$(echo "$record" | _egrep_o "id\"\s*\:\s*\"*\d+" | _egrep_o "\d+")"
_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
return 0
}
##################### Private functions below #####################
## Split the domain provided into the "bade domain" and the "start prefix".
## This function searches for the longest subdomain in your account
## for the full domain given and splits it into the base domain (zone)
## and the prefix/record to be added/removed
## USAGE: fulldomain
## EG: "_acme-challenge.two.three.four.domain.com"
## returns
## _sub_domain="_acme-challenge.two"
## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists
## if only "domain.com" exists it will return
## _sub_domain="_acme-challenge.two.three.four"
## _domain="domain.com"
_get_base_domain() {
# args
fulldomain="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
_debug fulldomain "$fulldomain"
# domain max legal length = 253
MAX_DOM=255
## get a list of domains for the account to check thru
## Set the headers
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $DO_API_KEY"
_debug DO_API_KEY "$DO_API_KEY"
## get URL for the list of domains
## havent seen this request paginated, tested with 18 domains (more requires manual requests with DO)
DOMURL="https://api.digitalocean.com/v2/domains"
## get the domain list (DO gives basically a full XFER!)
domain_list="$(_get "$DOMURL")"
## check response
if [ "$?" != "0" ]; then
_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
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
## we went through the entire domain zone list and dint find one that matched
## doesnt look like we can add in the record
_err "domain not found in DigitalOcean account, but we should never get here"
return 1
}
+215
View File
@@ -0,0 +1,215 @@
#!/usr/bin/env sh
# DNSimple domain api
# https://github.com/pho3nixf1re/acme.sh/issues
#
# This is your oauth token which can be acquired on the account page. Please
# note that this must be an _account_ token and not a _user_ token.
# https://dnsimple.com/a/<your account id>/account/access_tokens
# DNSimple_OAUTH_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
DNSimple_API="https://api.dnsimple.com/v2"
######## Public functions #####################
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dnsimple_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$DNSimple_OAUTH_TOKEN" ]; then
DNSimple_OAUTH_TOKEN=""
_err "You have not set the dnsimple oauth token yet."
_err "Please visit https://dnsimple.com/user to generate it."
return 1
fi
# save the oauth token for later
_saveaccountconf DNSimple_OAUTH_TOKEN "$DNSimple_OAUTH_TOKEN"
if ! _get_account_id; then
_err "failed to retrive account id"
return 1
fi
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_get_records "$_account_id" "$_domain" "$_sub_domain"
if [ "$_records_count" = "0" ]; then
_info "Adding record"
if _dnsimple_rest POST "$_account_id/zones/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
if printf -- "%s" "$response" | grep "\"name\":\"$_sub_domain\"" >/dev/null; then
_info "Added"
return 0
else
_err "Unexpected response while adding text record."
return 1
fi
fi
_err "Add txt record error."
else
_info "Updating record"
_extract_record_id "$_records" "$_sub_domain"
if _dnsimple_rest \
PATCH \
"$_account_id/zones/$_domain/records/$_record_id" \
"{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
_info "Updated!"
return 0
fi
_err "Update error"
return 1
fi
}
# fulldomain
dns_dnsimple_rm() {
fulldomain=$1
if ! _get_account_id; then
_err "failed to retrive account id"
return 1
fi
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_get_records "$_account_id" "$_domain" "$_sub_domain"
_extract_record_id "$_records" "$_sub_domain"
if [ "$_record_id" ]; then
if _dnsimple_rest DELETE "$_account_id/zones/$_domain/records/$_record_id"; then
_info "removed record" "$_record_id"
return 0
fi
fi
_err "failed to remove record" "$_record_id"
return 1
}
#################### Private functions bellow ##################################
# _acme-challenge.www.domain.com
# returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
domain=$1
i=2
previous=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
# not valid
return 1
fi
if ! _dnsimple_rest GET "$_account_id/zones/$h"; then
return 1
fi
if _contains "$response" 'not found'; then
_debug "$h not found"
else
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$previous)
_domain="$h"
_debug _domain "$_domain"
_debug _sub_domain "$_sub_domain"
return 0
fi
previous="$i"
i=$(_math "$i" + 1)
done
return 1
}
# returns _account_id
_get_account_id() {
_debug "retrive account id"
if ! _dnsimple_rest GET "whoami"; then
return 1
fi
if _contains "$response" "\"account\":null"; then
_err "no account associated with this token"
return 1
fi
if _contains "$response" "timeout"; then
_err "timeout retrieving account id"
return 1
fi
_account_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"email\":" | cut -d: -f2 | cut -d, -f1)
_debug _account_id "$_account_id"
return 0
}
# returns
# _records
# _records_count
_get_records() {
account_id=$1
domain=$2
sub_domain=$3
_debug "fetching txt records"
_dnsimple_rest GET "$account_id/zones/$domain/records?per_page=100"
if ! _contains "$response" "\"id\":"; then
_err "failed to retrieve records"
return 1
fi
_records_count=$(printf "%s" "$response" | _egrep_o "\"name\":\"$sub_domain\"" | wc -l | _egrep_o "[0-9]+")
_records=$response
_debug _records_count "$_records_count"
}
# returns _record_id
_extract_record_id() {
_record_id=$(printf "%s" "$_records" | _egrep_o "\"id\":[^,]*,\"zone_id\":\"[^,]*\",\"parent_id\":null,\"name\":\"$_sub_domain\"" | cut -d: -f2 | cut -d, -f1)
_debug "_record_id" "$_record_id"
}
# returns response
_dnsimple_rest() {
method=$1
path="$2"
data="$3"
request_url="$DNSimple_API/$path"
_debug "$path"
export _H1="Accept: application/json"
export _H2="Authorization: Bearer $DNSimple_OAUTH_TOKEN"
if [ "$data" ] || [ "$method" = "DELETE" ]; then
_H1="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
}
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env sh
# DNS API for Domain-Offensive / Resellerinterface / Domainrobot
# Report bugs at https://github.com/seidler2547/acme.sh/issues
# set these environment variables to match your customer ID and password:
# DO_PID="KD-1234567"
# DO_PW="cdfkjl3n2"
DO_URL="https://soap.resellerinterface.de/"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_do_add() {
fulldomain=$1
txtvalue=$2
if _dns_do_authenticate; then
_info "Adding TXT record to ${_domain} as ${fulldomain}"
_dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300
if _contains "${response}" '>success<'; then
return 0
fi
_err "Could not create resource record, check logs"
fi
return 1
}
#fulldomain
dns_do_rm() {
fulldomain=$1
if _dns_do_authenticate; then
if _dns_do_list_rrs; then
_dns_do_had_error=0
for _rrid in ${_rr_list}; do
_info "Deleting resource record $_rrid for $_domain"
_dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}"
if ! _contains "${response}" '>success<'; then
_dns_do_had_error=1
_err "Could not delete resource record for ${_domain}, id ${_rrid}"
fi
done
return $_dns_do_had_error
fi
fi
return 1
}
#################### Private functions below ##################################
_dns_do_authenticate() {
_info "Authenticating as ${DO_PID}"
_dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}"
if _contains "${response}" '>success<'; then
_get_root "$fulldomain"
_debug "_domain $_domain"
return 0
else
_err "Authentication failed, are DO_PID and DO_PW set correctly?"
fi
return 1
}
_dns_do_list_rrs() {
_dns_do_soap getRRList origin "${_domain}"
if ! _contains "${response}" 'SOAP-ENC:Array'; then
_err "getRRList origin ${_domain} failed"
return 1
fi
_rr_list="$(echo "${response}" \
| tr -d "\n\r\t" \
| sed -e 's/<item xsi:type="ns2:Map">/\n/g' \
| grep ">$(_regexcape "$fulldomain")</value>" \
| sed -e 's/<\/item>/\n/g' \
| grep '>id</key><value' \
| _egrep_o '>[0-9]{1,16}<' \
| tr -d '><')"
[ "${_rr_list}" ]
}
_dns_do_soap() {
func="$1"
shift
# put the parameters to xml
body="<tns:${func} xmlns:tns=\"${DO_URL}\">"
while [ "$1" ]; do
_k="$1"
shift
_v="$1"
shift
body="$body<$_k>$_v</$_k>"
done
body="$body</tns:${func}>"
_debug2 "SOAP request ${body}"
# build SOAP XML
_xml='<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>'"$body"'</env:Body>
</env:Envelope>'
# set SOAP headers
export _H1="SOAPAction: ${DO_URL}#${func}"
if ! response="$(_post "${_xml}" "${DO_URL}")"; then
_err "Error <$1>"
return 1
fi
_debug2 "SOAP response $response"
# retrieve cookie header
_H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)"
export _H2
return 0
}
_get_root() {
domain=$1
i=1
_dns_do_soap getDomainList
_all_domains="$(echo "${response}" \
| tr -d "\n\r\t " \
| _egrep_o 'domain</key><value[^>]+>[^<]+' \
| sed -e 's/^domain<\/key><value[^>]*>//g')"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
return 1
fi
if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then
_domain="$h"
return 0
fi
i=$(_math $i + 1)
done
_debug "$domain not found"
return 1
}
_regexcape() {
echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g'
}
+228
View File
@@ -0,0 +1,228 @@
#!/usr/bin/env sh
#Client ID
#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d"
#
#Secret
#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9"
#
#Token
Dynu_Token=""
#
#Endpoint
Dynu_EndPoint="https://api.dynu.com/v1"
#
#Author: Dynu Systems, Inc.
#Report Bugs here: https://github.com/shar0119/acme.sh
#
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dynu_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
Dynu_ClientId=""
Dynu_Secret=""
_err "Dynu client id and secret is not specified."
_err "Please create you API client id and secret and try again."
return 1
fi
#save the client id and secret to the account conf file.
_saveaccountconf Dynu_ClientId "$Dynu_ClientId"
_saveaccountconf Dynu_Secret "$Dynu_Secret"
if [ -z "$Dynu_Token" ]; then
_info "Getting Dynu token."
if ! _dynu_authentication; then
_err "Can not get token."
fi
fi
_debug "Detect root zone"
if ! _get_root "$fulldomain"; then
_err "Invalid domain."
return 1
fi
_debug _node "$_node"
_debug _domain_name "$_domain_name"
_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
return 1
fi
if ! _contains "$response" "text_data"; then
_err "Could not add TXT record."
return 1
fi
return 0
}
#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dynu_rm() {
fulldomain=$1
txtvalue=$2
if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
Dynu_ClientId=""
Dynu_Secret=""
_err "Dynu client id and secret is not specified."
_err "Please create you API client id and secret and try again."
return 1
fi
#save the client id and secret to the account conf file.
_saveaccountconf Dynu_ClientId "$Dynu_ClientId"
_saveaccountconf Dynu_Secret "$Dynu_Secret"
if [ -z "$Dynu_Token" ]; then
_info "Getting Dynu token."
if ! _dynu_authentication; then
_err "Can not get token."
fi
fi
_debug "Detect root zone."
if ! _get_root "$fulldomain"; then
_err "Invalid domain."
return 1
fi
_debug _node "$_node"
_debug _domain_name "$_domain_name"
_info "Checking for TXT record."
if ! _get_recordid "$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 "$_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
# _node=_acme-challenge.www
# _domain_name=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 ! _dynu_rest GET "dns/get/$h"; then
return 1
fi
if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_name=$h
_node=$(printf "%s" "$domain" | cut -d . -f 1-$p)
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_get_recordid() {
fulldomain=$1
txtvalue=$2
if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then
return 1
fi
if ! _contains "$response" "$txtvalue"; then
_dns_record_id=0
return 0
fi
_dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | _egrep_o ",[^,]*," | grep ',"id":' | tr -d ",," | cut -d : -f 2)
return 0
}
_delete_txt_record() {
_dns_record_id=$1
if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then
return 1
fi
if ! _contains "$response" "true"; then
return 1
fi
return 0
}
_dynu_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Authorization: Bearer $Dynu_Token"
export _H2="Content-Type: application/json"
if [ "$data" ]; then
_debug data "$data"
response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")"
else
_info "Getting $Dynu_EndPoint/$ep"
response="$(_get "$Dynu_EndPoint/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_dynu_authentication() {
realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)"
export _H1="Authorization: Basic $realm"
export _H2="Content-Type: application/json"
response="$(_get "$Dynu_EndPoint/oauth2/token")"
if [ "$?" != "0" ]; then
_err "Authentication failed."
return 1
fi
if _contains "$response" "accessToken"; then
Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2)
fi
if _contains "$Dynu_Token" "null"; then
Dynu_Token=""
fi
_debug2 response "$response"
return 0
}
+375
View File
@@ -0,0 +1,375 @@
#!/usr/bin/env sh
#This file name is "dns_freedns.sh"
#So, here must be a method dns_freedns_add()
#Which will be called by acme.sh to add the txt record to your api system.
#returns 0 means success, otherwise error.
#
#Author: David Kerr
#Report Bugs here: https://github.com/dkerr64/acme.sh
#
######## Public functions #####################
# Export FreeDNS userid and password in following variables...
# FREEDNS_User=username
# FREEDNS_Password=password
# login cookie is saved in acme account config file so userid / pw
# need to be set only when changed.
#Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_freedns_add() {
fulldomain="$1"
txtvalue="$2"
_info "Add TXT record using FreeDNS"
_debug "fulldomain: $fulldomain"
_debug "txtvalue: $txtvalue"
if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then
FREEDNS_User=""
FREEDNS_Password=""
if [ -z "$FREEDNS_COOKIE" ]; then
_err "You did not specify the FreeDNS username and password yet."
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
return 1
fi
using_cached_cookies="true"
else
FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")"
if [ -z "$FREEDNS_COOKIE" ]; then
return 1
fi
using_cached_cookies="false"
fi
_debug "FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)"
_saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE"
# split our full domain name into two parts...
i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)"
i="$(_math "$i" - 1)"
top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)"
i="$(_math "$i" - 1)"
sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
# Sometimes FreeDNS does not return the subdomain page but rather
# returns a page regarding becoming a premium member. This usually
# happens after a period of inactivity. Immediately trying again
# returns the correct subdomain page. So, we will try twice to
# load the page and obtain our domain ID
attempts=2
while [ "$attempts" -gt "0" ]; do
attempts="$(_math "$attempts" - 1)"
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
if [ "$?" != "0" ]; then
if [ "$using_cached_cookies" = "true" ]; then
_err "Has your FreeDNS username and password changed? If so..."
_err "Please export as FREEDNS_User / FREEDNS_Password and try again."
fi
return 1
fi
# Now convert the tables in the HTML to CSV. This litte gem from
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
subdomain_csv="$(echo "$htmlpage" \
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
| sed 's/^[\ \t]*//g' \
| tr -d '\n' \
| sed 's/<\/TR[^>]*>/\n/Ig' \
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
| grep 'edit.php?' \
| grep "$top_domain")"
# The above beauty ends with striping out rows that do not have an
# href to edit.php and do not have the top domain we are looking for.
# So all we should be left with is CSV of table of subdomains we are
# interested in.
# Now we have to read through this table and extract the data we need
lines="$(echo "$subdomain_csv" | wc -l)"
nl='
'
i=0
found=0
while [ "$i" -lt "$lines" ]; do
i="$(_math "$i" + 1)"
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
tmp="$(echo "$line" | cut -d ',' -f 1)"
if [ $found = 0 ] && _startswith "$tmp" "<td>$top_domain"; then
# this line will contain DNSdomainid for the top_domain
DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')"
found=1
else
# lines contain DNS records for all subdomains
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
DNStype="$(echo "$line" | cut -d ',' -f 3)"
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
# Now get current value for the TXT record. This method may
# not produce accurate results as the value field is truncated
# on this webpage. To get full value we would need to load
# another page. However we don't really need this so long as
# there is only one TXT record for the acme challenge subdomain.
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^&quot;]*&quot;//;s/&quot;.*//;s/<\/td>.*//')"
if [ $found != 0 ]; then
break
# we are breaking out of the loop at the first match of DNS name
# and DNS type (if we are past finding the domainid). This assumes
# that there is only ever one TXT record for the LetsEncrypt/acme
# challenge subdomain. This seems to be a reasonable assumption
# as the acme client deletes the TXT record on successful validation.
fi
else
DNSname=""
DNStype=""
fi
fi
done
_debug "DNSname: $DNSname DNStype: $DNStype DNSdomainid: $DNSdomainid DNSdataid: $DNSdataid"
_debug "DNSvalue: $DNSvalue"
if [ -z "$DNSdomainid" ]; then
# If domain ID is empty then something went wrong (top level
# domain not found at FreeDNS).
if [ "$attempts" = "0" ]; then
# exhausted maximum retry attempts
_debug "$htmlpage"
_debug "$subdomain_csv"
_err "Domain $top_domain not found at FreeDNS"
return 1
fi
else
# break out of the 'retry' loop... we have found our domain ID
break
fi
_info "Domain $top_domain not found at FreeDNS"
_info "Retry loading subdomain page ($attempts attempts remaining)"
done
if [ -z "$DNSdataid" ]; then
# If data ID is empty then specific subdomain does not exist yet, need
# to create it this should always be the case as the acme client
# deletes the entry after domain is validated.
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
return $?
else
if [ "$txtvalue" = "$DNSvalue" ]; then
# if value in TXT record matches value requested then DNS record
# does not need to be updated. But...
# Testing value match fails. Website is truncating the value field.
# So for now we will always go down the else path. Though in theory
# should never come here anyway as the acme client deletes
# the TXT record on successful validation, so we should not even
# have found a TXT record !!
_info "No update necessary for $fulldomain at FreeDNS"
return 0
else
# Delete the old TXT record (with the wrong value)
_freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"
if [ "$?" = "0" ]; then
# And add in new TXT record with the value provided
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
fi
return $?
fi
fi
return 0
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_freedns_rm() {
fulldomain="$1"
txtvalue="$2"
_info "Delete TXT record using FreeDNS"
_debug "fulldomain: $fulldomain"
_debug "txtvalue: $txtvalue"
# Need to read cookie from conf file again in case new value set
# during login to FreeDNS when TXT record was created.
# acme.sh does not have a _readaccountconf() function
FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")"
_debug "FreeDNS login cookies: $FREEDNS_COOKIE"
# Sometimes FreeDNS does not return the subdomain page but rather
# returns a page regarding becoming a premium member. This usually
# happens after a period of inactivity. Immediately trying again
# returns the correct subdomain page. So, we will try twice to
# load the page and obtain our TXT record.
attempts=2
while [ "$attempts" -gt "0" ]; do
attempts="$(_math "$attempts" - 1)"
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
if [ "$?" != "0" ]; then
return 1
fi
# Now convert the tables in the HTML to CSV. This litte gem from
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
subdomain_csv="$(echo "$htmlpage" \
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
| sed 's/^[\ \t]*//g' \
| tr -d '\n' \
| sed 's/<\/TR[^>]*>/\n/Ig' \
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
| grep 'edit.php?' \
| grep "$fulldomain")"
# The above beauty ends with striping out rows that do not have an
# href to edit.php and do not have the domain name we are looking for.
# So all we should be left with is CSV of table of subdomains we are
# interested in.
# Now we have to read through this table and extract the data we need
lines="$(echo "$subdomain_csv" | wc -l)"
nl='
'
i=0
found=0
while [ "$i" -lt "$lines" ]; do
i="$(_math "$i" + 1)"
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
DNStype="$(echo "$line" | cut -d ',' -f 3)"
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^&quot;]*&quot;//;s/&quot;.*//;s/<\/td>.*//')"
_debug "DNSvalue: $DNSvalue"
# if [ "$DNSvalue" = "$txtvalue" ]; then
# Testing value match fails. Website is truncating the value
# field. So for now we will assume that there is only one TXT
# field for the sub domain and just delete it. Currently this
# is a safe assumption.
_freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"
return $?
# fi
fi
done
done
# If we get this far we did not find a match (after two attempts)
# Not necessarily an error, but log anyway.
_debug2 "$subdomain_csv"
_info "Cannot delete TXT record for $fulldomain/$txtvalue. Does not exist at FreeDNS"
return 0
}
#################### Private functions below ##################################
# usage: _freedns_login username password
# print string "cookie=value" etc.
# returns 0 success
_freedns_login() {
export _H1="Accept-Language:en-US"
username="$1"
password="$2"
url="https://freedns.afraid.org/zc.php?step=2"
_debug "Login to FreeDNS as user $username"
htmlpage="$(_post "username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" "$url")"
if [ "$?" != "0" ]; then
_err "FreeDNS login failed for user $username bad RC from _post"
return 1
fi
cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
# if cookies is not empty then logon successful
if [ -z "$cookies" ]; then
_debug "$htmlpage"
_err "FreeDNS login failed for user $username. Check $HTTP_HEADER file"
return 1
fi
printf "%s" "$cookies"
return 0
}
# usage _freedns_retrieve_subdomain_page login_cookies
# echo page retrieved (html)
# returns 0 success
_freedns_retrieve_subdomain_page() {
export _H1="Cookie:$1"
export _H2="Accept-Language:en-US"
url="https://freedns.afraid.org/subdomain/"
_debug "Retrieve subdomain page from FreeDNS"
htmlpage="$(_get "$url")"
if [ "$?" != "0" ]; then
_err "FreeDNS retrieve subdomains failed bad RC from _get"
return 1
elif [ -z "$htmlpage" ]; then
_err "FreeDNS returned empty subdomain page"
return 1
fi
_debug2 "$htmlpage"
printf "%s" "$htmlpage"
return 0
}
# usage _freedns_add_txt_record login_cookies domain_id subdomain value
# returns 0 success
_freedns_add_txt_record() {
export _H1="Cookie:$1"
export _H2="Accept-Language:en-US"
domain_id="$2"
subdomain="$3"
value="$(printf '%s' "$4" | _url_encode)"
url="http://freedns.afraid.org/subdomain/save.php?step=2"
htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")"
if [ "$?" != "0" ]; then
_err "FreeDNS failed to add TXT record for $subdomain bad RC from _post"
return 1
elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then
_debug "$htmlpage"
_err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
return 1
elif _contains "$htmlpage" "security code was incorrect"; then
_debug "$htmlpage"
_err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code"
_err "Note that you cannot use automatic DNS validation for FreeDNS public domains"
return 1
fi
_debug2 "$htmlpage"
_info "Added acme challenge TXT record for $fulldomain at FreeDNS"
return 0
}
# usage _freedns_delete_txt_record login_cookies data_id
# returns 0 success
_freedns_delete_txt_record() {
export _H1="Cookie:$1"
export _H2="Accept-Language:en-US"
data_id="$2"
url="https://freedns.afraid.org/subdomain/delete2.php"
htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")"
if [ "$?" != "0" ]; then
_err "FreeDNS failed to delete TXT record for $data_id bad RC from _get"
return 1
elif ! _contains "$htmlheader" "200 OK"; then
_debug "$htmlheader"
_err "FreeDNS failed to delete TXT record $data_id"
return 1
fi
_info "Deleted acme challenge TXT record for $fulldomain at FreeDNS"
return 0
}
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/env sh
# Gandi LiveDNS v5 API
# http://doc.livedns.gandi.net/
# currently under beta
#
# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
#
#Author: Frédéric Crozat <fcrozat@suse.com>
#Report Bugs here: https://github.com/fcrozat/acme.sh
#
######## Public functions #####################
GANDI_LIVEDNS_API="https://dns.beta.gandi.net/api/v5"
#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gandi_livedns_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$GANDI_LIVEDNS_KEY" ]; then
_err "No API key specified for Gandi LiveDNS."
_err "Create your key and export it as GANDI_LIVEDNS_KEY"
return 1
fi
_saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
_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"
_gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \
&& _contains "$response" '{"message": "DNS Record Created"}' \
&& _info "Add $(__green "success")"
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_gandi_livedns_rm() {
fulldomain=$1
txtvalue=$2
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug fulldomain "$fulldomain"
_debug domain "$_domain"
_debug sub_domain "$_sub_domain"
_gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" ""
}
#################### 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 ! _gandi_livedns_rest GET "domains/$h"; then
return 1
fi
if _contains "$response" '"code": 401'; then
_err "$response"
return 1
elif _contains "$response" '"code": 404'; 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
}
_gandi_livedns_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Content-Type: application/json"
export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
if [ "$m" = "GET" ]; then
response="$(_get "$GANDI_LIVEDNS_API/$ep")"
else
_debug data "$data"
response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
+1 -1
View File
@@ -40,7 +40,7 @@ dns_gd_add() {
if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then
if [ "$response" = "{}" ]; then if [ "$response" = "{}" ]; then
_info "Added, sleeping 10 seconds" _info "Added, sleeping 10 seconds"
sleep 10 _sleep 10
#todo: check if the record takes effect #todo: check if the record takes effect
return 0 return 0
else else
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env sh
## Infoblox API integration by Jason Keller and Elijah Tenai
##
## Report any bugs via https://github.com/jasonkeller/acme.sh
dns_infoblox_add() {
## Nothing to see here, just some housekeeping
fulldomain=$1
txtvalue=$2
baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View"
_info "Using Infoblox API"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
## Check for the credentials
if [ -z "$Infoblox_Creds" ] || [ -z "$Infoblox_Server" ]; then
Infoblox_Creds=""
Infoblox_Server=""
_err "You didn't specify the credentials, server or infoblox view yet (Infoblox_Creds, Infoblox_Server and Infoblox_View)."
_err "Please set them via EXPORT ([username:password], [ip or hostname]) and try again."
return 1
fi
if [ -z "$Infoblox_View" ]; then
Infoblox_View="default"
fi
## Save the credentials to the account file
_saveaccountconf Infoblox_Creds "$Infoblox_Creds"
_saveaccountconf Infoblox_Server "$Infoblox_Server"
_saveaccountconf Infoblox_View "$Infoblox_View"
## Base64 encode the credentials
Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64)
## Construct the HTTP Authorization header
export _H1="Accept-Language:en-US"
export _H2="Authorization: Basic $Infoblox_CredsEncoded"
## Add the challenge record to the Infoblox grid member
result=$(_post "" "$baseurlnObject" "" "POST")
## Let's see if we get something intelligible back from the unit
if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then
_info "Successfully created the txt record"
return 0
else
_err "Error encountered during record addition"
_err "$result"
return 1
fi
}
dns_infoblox_rm() {
## Nothing to see here, just some housekeeping
fulldomain=$1
txtvalue=$2
_info "Using Infoblox API"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
## Base64 encode the credentials
Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64)
## Construct the HTTP Authorization header
export _H1="Accept-Language:en-US"
export _H2="Authorization: Basic $Infoblox_CredsEncoded"
## Does the record exist? Let's check.
baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=$Infoblox_View&_return_type=xml-pretty"
result=$(_get "$baseurlnObject")
## Let's see if we get something intelligible back from the grid
if echo "$result" | egrep "record:txt/.*:.*/$Infoblox_View"; then
## Extract the object reference
objRef=$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/$Infoblox_View")
objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef"
## Delete them! All the stale records!
rmResult=$(_post "" "$objRmUrl" "" "DELETE")
## Let's see if that worked
if echo "$rmResult" | egrep "record:txt/.*:.*/$Infoblox_View"; then
_info "Successfully deleted $objRef"
return 0
else
_err "Error occurred during txt record delete"
_err "$rmResult"
return 1
fi
else
_err "Record to delete didn't match an existing record"
_err "$result"
return 1
fi
}
#################### Private functions below ##################################
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env sh
######## Public functions #####################
#Usage: dns_knot_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_knot_add() {
fulldomain=$1
txtvalue=$2
_checkKey || return 1
[ -n "${KNOT_SERVER}" ] || KNOT_SERVER="localhost"
# save the dns server and key to the account.conf file.
_saveaccountconf KNOT_SERVER "${KNOT_SERVER}"
_saveaccountconf KNOT_KEY "${KNOT_KEY}"
if ! _get_root "$fulldomain"; then
_err "Domain does not exist."
return 1
fi
_info "Adding ${fulldomain}. 60 TXT \"${txtvalue}\""
knsupdate -y "${KNOT_KEY}" <<EOF
server ${KNOT_SERVER}
zone ${_domain}.
update add ${fulldomain}. 60 TXT "${txtvalue}"
send
quit
EOF
if [ $? -ne 0 ]; then
_err "Error updating domain."
return 1
fi
_info "Domain TXT record successfully added."
return 0
}
#Usage: dns_knot_rm _acme-challenge.www.domain.com
dns_knot_rm() {
fulldomain=$1
_checkKey || return 1
[ -n "${KNOT_SERVER}" ] || KNOT_SERVER="localhost"
if ! _get_root "$fulldomain"; then
_err "Domain does not exist."
return 1
fi
_info "Removing ${fulldomain}. TXT"
knsupdate -y "${KNOT_KEY}" <<EOF
server ${KNOT_SERVER}
zone ${_domain}.
update del ${fulldomain}. TXT
send
quit
EOF
if [ $? -ne 0 ]; then
_err "error updating domain"
return 1
fi
_info "Domain TXT record successfully deleted."
return 0
}
#################### Private functions below ##################################
# _acme-challenge.www.domain.com
# returns
# _domain=domain.com
_get_root() {
domain=$1
i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)"
i=$(_math "$i" - 1)
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
return 1
fi
_domain="$h"
return 0
done
_debug "$domain not found"
return 1
}
_checkKey() {
if [ -z "${KNOT_KEY}" ]; then
_err "You must specify a TSIG key to authenticate the request."
return 1
fi
}
+4 -4
View File
@@ -34,7 +34,7 @@ dns_lexicon_add() {
# 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')
Lx_name_v=$(eval echo \$"$Lx_name") Lx_name_v=$(eval echo \$"$Lx_name")
_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 "$Lx_name" "$Lx_name_v"
eval export "$Lx_name" eval export "$Lx_name"
@@ -43,7 +43,7 @@ dns_lexicon_add() {
# 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')
Lx_token_v=$(eval echo \$"$Lx_token") Lx_token_v=$(eval echo \$"$Lx_token")
_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 "$Lx_token" "$Lx_token_v"
eval export "$Lx_token" eval export "$Lx_token"
@@ -52,7 +52,7 @@ dns_lexicon_add() {
# 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')
Lx_password_v=$(eval echo \$"$Lx_password") Lx_password_v=$(eval echo \$"$Lx_password")
_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 "$Lx_password" "$Lx_password_v"
eval export "$Lx_password" eval export "$Lx_password"
@@ -61,7 +61,7 @@ dns_lexicon_add() {
# 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')
Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken") Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken")
_debug "$Lx_domaintoken" "$Lx_domaintoken_v" _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v"
if [ "$Lx_domaintoken_v" ]; then if [ "$Lx_domaintoken_v" ]; then
eval export "$Lx_domaintoken" eval export "$Lx_domaintoken"
_saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v" _saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v"
+183
View File
@@ -0,0 +1,183 @@
#!/usr/bin/env sh
#Author: Philipp Grosswiler <philipp.grosswiler@swiss-design.net>
LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
######## Public functions #####################
#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_linode_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"
_parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue"
if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then
_resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\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_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"
_parameters="&DomainID=$_domain_id"
if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
if [ "$resource" ]; then
_resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
if [ "$_resource_id" ]; then
_debug _resource_id "$_resource_id"
_parameters="&DomainID=$_domain_id&ResourceID=$_resource_id"
if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then
_resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
_debug _resource_id "$_resource_id"
if [ -z "$_resource_id" ]; then
_err "Error deleting the domain resource."
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_API_KEY" ]; then
LINODE_API_KEY=""
_err "You didn't specify the Linode API key yet."
_err "Please create your key and try again."
return 1
fi
_saveaccountconf LINODE_API_KEY "$LINODE_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 "domain.list"; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
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 "\"DOMAINID\":\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"
if [ "$mtd" != "GET" ]; then
# both POST and DELETE.
_debug data "$data"
response="$(_post "$data" "$LINODE_API_URL$ep" "" "$mtd")"
else
response="$(_get "$LINODE_API_URL$ep$data")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
+37 -6
View File
@@ -46,12 +46,12 @@ dns_lua_add() {
return 1 return 1
fi fi
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain\"" | wc -l) count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
_debug count "$count" _debug count "$count"
if [ "$count" = "0" ]; then if [ "$count" = "0" ]; then
_info "Adding record" _info "Adding record"
if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then if _contains "$response" "$fulldomain"; then
_info "Added" _info "Added"
#todo: check if the record takes effect #todo: check if the record takes effect
return 0 return 0
@@ -63,11 +63,11 @@ dns_lua_add() {
_err "Add txt record error." _err "Add txt record error."
else else
_info "Updating record" _info "Updating record"
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | cut -d: -f2 | cut -d, -f1) record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
_debug "record_id" "$record_id" _debug "record_id" "$record_id"
_LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"ttl\":120}" _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}"
if [ "$?" = "0" ]; then if [ "$?" = "0" ] && _contains "$response" "updated_at"; then
_info "Updated!" _info "Updated!"
#todo: check if the record takes effect #todo: check if the record takes effect
return 0 return 0
@@ -81,7 +81,36 @@ dns_lua_add() {
#fulldomain #fulldomain
dns_lua_rm() { dns_lua_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
_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"
_LUA_rest GET "zones/${_domain_id}/records"
count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _LUA_rest DELETE "/zones/$_domain_id/records/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" "$record_id"
fi
} }
#################### Private functions below ################################## #################### Private functions below ##################################
@@ -99,6 +128,7 @@ _get_root() {
fi fi
while true; do while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then if [ -z "$h" ]; then
#not valid #not valid
return 1 return 1
@@ -106,6 +136,7 @@ _get_root() {
if _contains "$response" "\"name\":\"$h\""; then if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1) _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1)
_debug _domain_id "$_domain_id"
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"
@@ -127,7 +158,7 @@ _LUA_rest() {
export _H1="Accept: application/json" export _H1="Accept: application/json"
export _H2="Authorization: Basic $LUA_auth" export _H2="Authorization: Basic $LUA_auth"
if [ "$data" ]; then if [ "$m" != "GET" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$LUA_Api/$ep" "" "$m")" response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
else else
Executable → Regular
+31 -2
View File
@@ -78,7 +78,36 @@ dns_me_add() {
#fulldomain #fulldomain
dns_me_rm() { dns_me_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
_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"
_me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2)
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | 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 ! _me_rest DELETE "$_domain_id/records/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" ''
fi
} }
#################### Private functions below ################################## #################### Private functions below ##################################
@@ -124,13 +153,13 @@ _me_rest() {
_debug "$ep" _debug "$ep"
cdate=$(date -u +"%a, %d %b %Y %T %Z") cdate=$(date -u +"%a, %d %b %Y %T %Z")
hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(_hex "$ME_Secret")" hex) hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex)
export _H1="x-dnsme-apiKey: $ME_Key" export _H1="x-dnsme-apiKey: $ME_Key"
export _H2="x-dnsme-requestDate: $cdate" export _H2="x-dnsme-requestDate: $cdate"
export _H3="x-dnsme-hmac: $hmac" export _H3="x-dnsme-hmac: $hmac"
if [ "$data" ]; then if [ "$m" != "GET" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$ME_Api/$ep" "" "$m")" response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
else else
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env sh
# bug reports to dev@1e.ca
#
#NS1_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
NS1_Api="https://api.nsone.net/v1"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_nsone_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$NS1_Key" ]; then
NS1_Key=""
_err "You didn't specify nsone dns api key yet."
_err "Please create you key and try again."
return 1
fi
#save the api key and email to the account conf file.
_saveaccountconf NS1_Key "$NS1_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"
_debug "Getting txt records"
_nsone_rest GET "zones/${_domain}"
if ! _contains "$response" "\"records\":"; then
_err "Error"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Adding record"
if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\"}"; then
if _contains "$response" "$fulldomain"; then
_info "Added"
#todo: check if the record takes effect
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
else
_info "Updating record"
record_id=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain.\",[^{]*\"type\":\"TXT\",\"id\":\"[^,]*\"" | _head_n 1 | cut -d: -f7 | cut -d, -f1)
_debug "record_id" "$record_id"
_nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}"
if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then
_info "Updated!"
#todo: check if the record takes effect
return 0
fi
_err "Update error"
return 1
fi
}
#fulldomain
dns_nsone_rm() {
fulldomain=$1
txtvalue=$2
_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"
_nsone_rest GET "zones/${_domain}/$fulldomain/TXT"
count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",.*\"type\":\"TXT\"" | wc -l | tr -d " ")
_debug count "$count"
if [ "$count" = "0" ]; then
_info "Don't need to remove."
else
if ! _nsone_rest DELETE "zones/${_domain}/$fulldomain/TXT"; then
_err "Delete record error."
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
if ! _nsone_rest GET "zones"; then
return 1
fi
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" "\"zone\":\"$h\""; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h"
return 0
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_nsone_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
export _H1="Accept: application/json"
export _H2="X-NSONE-Key: $NS1_Key"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$NS1_Api/$ep" "" "$m")"
else
response="$(_get "$NS1_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
+7 -7
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#Applcation Key #Application Key
#OVH_AK="sdfsdfsdfljlbjkljlkjsdfoiwje" #OVH_AK="sdfsdfsdfljlbjkljlkjsdfoiwje"
# #
#Application Secret #Application Secret
@@ -14,7 +14,7 @@
#'ovh-eu' #'ovh-eu'
OVH_EU='https://eu.api.ovh.com/1.0' OVH_EU='https://eu.api.ovh.com/1.0'
#'ovh-ca': #'ovh-ca':
OVH_CA='https://ca.api.ovh.com/1.0' OVH_CA='https://ca.api.ovh.com/1.0'
#'kimsufi-eu' #'kimsufi-eu'
@@ -119,7 +119,7 @@ dns_ovh_add() {
_info "Checking authentication" _info "Checking authentication"
response="$(_ovh_rest GET "domain/")" response="$(_ovh_rest GET "domain")"
if _contains "$response" "INVALID_CREDENTIAL"; then if _contains "$response" "INVALID_CREDENTIAL"; then
_err "The consumer key is invalid: $OVH_CK" _err "The consumer key is invalid: $OVH_CK"
_err "Please retry to create a new one." _err "Please retry to create a new one."
@@ -191,7 +191,7 @@ _ovh_authentication() {
_H3="" _H3=""
_H4="" _H4=""
_ovhdata='{"accessRules": [{"method": "GET","path": "/*"},{"method": "POST","path": "/*"},{"method": "PUT","path": "/*"},{"method": "DELETE","path": "/*"}],"redirection":"'$ovh_success'"}' _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}'
response="$(_post "$_ovhdata" "$OVH_API/auth/credential")" response="$(_post "$_ovhdata" "$OVH_API/auth/credential")"
_debug3 response "$response" _debug3 response "$response"
@@ -207,7 +207,7 @@ _ovh_authentication() {
_err "Unable to get consumerKey" _err "Unable to get consumerKey"
return 1 return 1
fi fi
_debug consumerKey "$consumerKey" _secure_debug consumerKey "$consumerKey"
OVH_CK="$consumerKey" OVH_CK="$consumerKey"
_saveaccountconf OVH_CK "$OVH_CK" _saveaccountconf OVH_CK "$OVH_CK"
@@ -238,7 +238,7 @@ _get_root() {
return 1 return 1
fi fi
if ! _contains "$response" "This service does not exist" >/dev/null; then if ! _contains "$response" "This service does not exist" >/dev/null && ! _contains "$response" "NOT_GRANTED_CALL" >/dev/null; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain="$h" _domain="$h"
return 0 return 0
@@ -269,7 +269,7 @@ _ovh_rest() {
_ovh_t="$(_ovh_timestamp)" _ovh_t="$(_ovh_timestamp)"
_debug2 _ovh_t "$_ovh_t" _debug2 _ovh_t "$_ovh_t"
_ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t" _ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t"
_debug _ovh_p "$_ovh_p" _secure_debug _ovh_p "$_ovh_p"
_ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)" _ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)"
_debug2 _ovh_hex "$_ovh_hex" _debug2 _ovh_hex "$_ovh_hex"
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#PowerDNS Emdedded API #PowerDNS Embedded API
#https://doc.powerdns.com/md/httpapi/api_spec/ #https://doc.powerdns.com/md/httpapi/api_spec/
# #
#PDNS_Url="http://ns.example.com:8081" #PDNS_Url="http://ns.example.com:8081"
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env sh
#This is the vscale.io api wrapper for acme.sh
#
#Author: Alex Loban
#Report Bugs here: https://github.com/LAV45/acme.sh
#VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
VSCALE_API_URL="https://api.vscale.io/v1"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_vscale_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$VSCALE_API_KEY" ]; then
VSCALE_API_KEY=""
_err "You didn't specify the VSCALE api key yet."
_err "Please create you key and try again."
return 1
fi
_saveaccountconf VSCALE_API_KEY "$VSCALE_API_KEY"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_vscale_tmpl_json="{\"type\":\"TXT\",\"name\":\"$_sub_domain.$_domain\",\"content\":\"$txtvalue\"}"
if _vscale_rest POST "domains/$_domain_id/records/" "$_vscale_tmpl_json"; then
response=$(printf "%s\n" "$response" | _egrep_o "{\"error\": \".+\"" | cut -d : -f 2)
if [ -z "$response" ]; then
_info "txt record updated success."
return 0
fi
fi
return 1
}
#fulldomain txtvalue
dns_vscale_rm() {
fulldomain=$1
txtvalue=$2
_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"
_vscale_rest GET "domains/$_domain_id/records/"
if [ -n "$response" ]; then
record_id=$(printf "%s\n" "$response" | _egrep_o "\"TXT\", \"id\": [0-9]+, \"name\": \"$_sub_domain.$_domain\"" | cut -d : -f 2 | tr -d ", \"name\"")
_debug record_id "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if _vscale_rest DELETE "domains/$_domain_id/records/$record_id" && [ -z "$response" ]; then
_info "txt record deleted success."
return 0
fi
_debug response "$response"
return 1
fi
return 1
}
#################### 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 _vscale_rest GET "domains/"; then
response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
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 "{.*\"name\":\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 uri qstr data
_vscale_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="X-Token: ${VSCALE_API_KEY}"
if [ "$mtd" != "GET" ]; then
# both POST and DELETE.
_debug data "$data"
response="$(_post "$data" "$VSCALE_API_URL/$ep" "" "$mtd")"
else
response="$(_get "$VSCALE_API_URL/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}