Compare commits

...
255 Commits
20 changed files with 1736 additions and 623 deletions
+9
View File
@@ -0,0 +1,9 @@
<!--
Do NOT send pull request to `master` branch.
Please send to `dev` branch instead.
Any PR to `master` branch will NOT be merged.
-->
+54 -11
View File
@@ -1,11 +1,54 @@
language: bash language: shell
sudo: required
env:
global: os:
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64 - linux
- osx
script:
- curl -sSL $SHFMT_URL -o ~/shfmt env:
- chmod +x ~/shfmt global:
- ~/shfmt -l -w -i 2 . - SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64
- git diff --exit-code || (echo "Run shfmt to fix the formatting issues" && false)
addons:
apt:
sources:
- debian-sid # Grab shellcheck from the Debian repo (o_O)
packages:
- shellcheck
install:
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
brew update && brew install openssl;
brew info openssl;
ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/;
ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/;
ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl;
_old_path="$PATH";
echo "PATH=$PATH";
export PATH="";
export OPENSSL_BIN="/usr/local/openssl";
openssl version 2>&1 || true;
$OPENSSL_BIN version 2>&1 || true;
export PATH="$_old_path";
fi
script:
- echo "TEST_LOCAL=$TEST_LOCAL"
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
- command -V openssl && openssl version
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then chmod +x ~/shfmt ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ~/shfmt -l -w -i 2 . ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then git diff --exit-code && echo "shfmt OK" ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi
- cd ..
- 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" = "osx" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi
matrix:
fast_finish: true
+119 -97
View File
@@ -1,21 +1,23 @@
# 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)
- An ACME protocol client written purely in Shell (Unix shell) language. - An ACME protocol client written purely in Shell (Unix shell) language.
- Fully ACME protocol implementation. - Full ACME protocol implementation.
- Simple, powerful and very easy to use. You only need 3 minutes to learn. - Simple, powerful and very easy to use. You only need 3 minutes to learn it.
- Bash, dash and sh compatible. - Bash, dash and sh compatible.
- Simplest shell script for Let's Encrypt free certificate client. - Simplest shell script for Let's Encrypt free certificate client.
- Purely written in Shell with no dependencies on python or Let's Encrypt official 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.
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&smallest&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
# [中文说明](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)
#Tested OS
# Tested OS
| NO | Status| Platform| | NO | Status| Platform|
|----|-------|---------| |----|-------|---------|
|1|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu |1|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu
@@ -37,42 +39,41 @@ Wiki: https://github.com/Neilpang/acme.sh/wiki
|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT) |17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT)
|18|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris |18|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris
|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
For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest): For all build statuses, check our [daily build project](https://github.com/Neilpang/acmetest):
https://github.com/Neilpang/acmetest https://github.com/Neilpang/acmetest
# Supported Mode
1. Webroot mode # Supported modes
2. Standalone mode
3. Apache mode
4. Dns mode
- Webroot mode
- Standalone mode
- Apache mode
- DNS mode
# 1. How to install # 1. How to install
### 1. Install online: ### 1. Install online
Check this project: https://github.com/Neilpang/get.acme.sh Check this project: https://github.com/Neilpang/get.acme.sh
```bash ```bash
curl https://get.acme.sh | sh curl https://get.acme.sh | sh
``` ```
Or: Or:
```bash ```bash
wget -O - https://get.acme.sh | sh wget -O - https://get.acme.sh | sh
``` ```
### 2. Or, Install from git: ### 2. Or, Install from git
Clone this project: Clone this project and launch installation:
```bash ```bash
git clone https://github.com/Neilpang/acme.sh.git git clone https://github.com/Neilpang/acme.sh.git
@@ -82,14 +83,14 @@ cd ./acme.sh
You `don't have to be root` then, although `it is recommended`. You `don't have to be root` then, although `it is recommended`.
Advanced Installation: https://github.com/Neilpang/acme.sh/wiki/How-to-install Advanced Installation: https://github.com/Neilpang/acme.sh/wiki/How-to-install
The installer will perform 3 actions: The installer will perform 3 actions:
1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`. 1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`.
All certs will be placed in this folder. All certs will be placed in this folder too.
2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`. 2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`.
3. Create everyday cron job to check and renew the cert if needed. 3. Create daily cron job to check and renew the certs if needed.
Cron entry example: Cron entry example:
@@ -97,18 +98,17 @@ Cron entry example:
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null 0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
``` ```
After the installation, you must close current terminal and reopen again to make the alias take effect. After the installation, you must close the current terminal and reopen it to make the alias take effect.
Ok, you are ready to issue certs now.
Ok, you are ready to issue cert now.
Show help message: Show help message:
``` ```
root@v1:~# acme.sh -h root@v1:~# acme.sh -h
``` ```
# 2. Just issue a cert: # 2. Just issue a cert
**Example 1:** Single domain. **Example 1:** Single domain.
@@ -119,56 +119,59 @@ acme.sh --issue -d example.com -w /home/wwwroot/example.com
**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` is the web root folder. You **MUST** have `write access` to this folder.
Second argument **"example.com"** is the main domain you want to issue cert for. Second argument **"example.com"** is the main domain you want to issue the cert for.
You must have at least a domain there. You must have at least one domain there.
You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`. You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`.
Generate/issued certs will be placed in `~/.acme.sh/example.com/` Generated/issued certs will be placed in `~/.acme.sh/example.com/`
The issued cert will be renewed every **60** days automatically. The issued cert will be renewed automatically every **60** days.
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 3. Install the issued cert to apache/nginx etc. # 3. Install the issued cert to Apache/Nginx etc.
After you issue a cert, you probably want to install/copy the cert to your nginx/apache or other servers. After you issue a cert, you probably want to install/copy the cert to your Apache/Nginx or other servers.
You **MUST** use this command to copy the certs to the target files, **Do NOT** use the certs files in **.acme.sh/** folder, they are for internal use only, the folder structure may change in future. You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future.
**nginx** example **Apache** example:
```bash ```bash
acme.sh --installcert -d example.com \ acme.sh --installcert -d example.com \
--keypath /path/to/keyfile/in/nginx/key.pem \ --certpath /path/to/certfile/in/apache/cert.pem \
--fullchainpath path/to/fullchain/nginx/cert.pem \ --keypath /path/to/keyfile/in/apache/key.pem \
--reloadcmd "service nginx restart" --fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd "service apache2 force-reload"
``` ```
**apache** example **Nginx** example:
```bash ```bash
acme.sh --installcert -d example.com \ acme.sh --installcert -d example.com \
--certpath /path/to/certfile/in/apache/cert.pem \ --keypath /path/to/keyfile/in/nginx/key.pem \
--keypath /path/to/keyfile/in/apache/key.pem \ --fullchainpath /path/to/fullchain/nginx/cert.pem \
--fullchainpath path/to/fullchain/certfile/apache/fullchain.pem \ --reloadcmd "service nginx force-reload"
--reloadcmd "service apache2 restart"
``` ```
Only the domain is required, all the other parameters are optional. Only the domain is required, all the other parameters are optional.
Install/copy the issued cert/key to the production apache or nginx path. The ownership and permission info of existing files are preserved. You may want to precreate the files to have defined ownership and permission.
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 will be automatically reloaded by the command: `service apache2 reload` or `service nginx reload`.
# 4. Use Standalone server to issue cert # 4. Use Standalone server to issue cert
**(requires you be root/sudoer, or you have permission to listen tcp 80 port)** **(requires you to be root/sudoer or have permission to listen on port 80 (TCP))**
The tcp `80` port **MUST** be free to listen, otherwise you will be prompted to free the `80` port and try again. Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
```bash ```bash
acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
@@ -176,13 +179,14 @@ acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# 5. Use Standalone tls server to issue cert
**(requires you be root/sudoer, or you have permission to listen tcp 443 port)** # 5. Use Standalone TLS server to issue cert
**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))**
acme.sh supports `tls-sni-01` validation. acme.sh supports `tls-sni-01` validation.
The tcp `443` port **MUST** be free to listen, otherwise you will be prompted to free the `443` port and try again. Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again.
```bash ```bash
acme.sh --issue --tls -d example.com -d www.example.com -d cp.example.com acme.sh --issue --tls -d example.com -d www.example.com -d cp.example.com
@@ -190,31 +194,33 @@ acme.sh --issue --tls -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
# 6. Use Apache mode # 6. Use Apache mode
**(requires you be root/sudoer, since it is required to interact with apache server)** **(requires you to be root/sudoer, since it is required to interact with Apache server)**
If you are running a web server, apache or nginx, it is recommended to use the `Webroot mode`. If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
Particularly, if you are running an apache server, you should use apache mode instead. This mode doesn't write any files to your web root folder. Particularly, if you are running an Apache server, you should use Apache mode instead. This mode doesn't write any files to your web root folder.
Just set string "apache" as the second argument, it will force use of apache plugin automatically. Just set string "apache" as the second argument and it will force use of apache plugin automatically.
``` ```
acme.sh --issue --apache -d example.com -d www.example.com -d user.example.com 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 DNS mode: # 7. Use DNS mode:
Support the `dns-01` challenge. Support the `dns-01` challenge.
```bash ```bash
acme.sh --issue --dns -d example.com -d www.example.com -d user.example.com acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
``` ```
You should get the output like below: You should get an output like below:
``` ```
Add the following txt record: Add the following txt record:
@@ -226,7 +232,6 @@ Domain:_acme-challenge.www.example.com
Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Please add those txt records to the domains. Waiting for the dns to take effect. Please add those txt records to the domains. Waiting for the dns to take effect.
``` ```
Then just rerun with `renew` argument: Then just rerun with `renew` argument:
@@ -237,52 +242,58 @@ acme.sh --renew -d example.com
Ok, it's finished. Ok, it's finished.
# 8. Automatic DNS API integration # 8. Automatic DNS API integration
If your DNS provider supports API access, we can use API to automatically issue the certs. If your DNS provider supports API access, we can use that API to automatically issue the certs.
You don't have do anything manually! You don't have to do anything manually!
### Currently acme.sh supports: ### Currently acme.sh supports:
1. Cloudflare.com API 1. CloudFlare.com API
2. Dnspod.cn API 1. DNSPod.cn API
3. Cloudxns.com API 1. CloudXNS.com API
4. Godaddy.com API 1. GoDaddy.com API
5. OVH, kimsufi, soyoustart and runabove API 1. OVH, kimsufi, soyoustart and runabove API
6. AWS Route 53, see: https://github.com/Neilpang/acme.sh/issues/65 1. AWS Route 53
7. PowerDNS API 1. PowerDNS.com API
8. lexicon dns api: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api 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.) (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
9. LuaDNS.com API 1. LuaDNS.com API
10. DNSMadeEasy.com API 1. DNSMadeEasy.com API
1. nsupdate API
1. aliyun.com(阿里云) API
1. ISPConfig 3.1 API
1. Alwaysdata.com API
##### More APIs are coming soon... **More APIs coming soon...**
If your DNS provider is not on the supported list above, you can write your own script API easily. If you do please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute to the project. If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
For more details: [How to use dns api](dnsapi) For more details: [How to use DNS API](dnsapi)
# 9. Issue ECC certificate:
`Let's Encrypt` now can issue **ECDSA** certificates. # 9. Issue ECC certificates
And we also support it. `Let's Encrypt` can now issue **ECDSA** certificates.
And we support them too!
Just set the `length` parameter with a prefix `ec-`. Just set the `length` parameter with a prefix `ec-`.
For example: For example:
### Single domain ECC cerfiticate: ### Single domain ECC cerfiticate
```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
``` ```
SAN multi domain ECC certificate: ### SAN multi domain ECC certificate
```bash ```bash
acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
``` ```
Please look at the last parameter above. Please look at the last parameter above.
@@ -294,40 +305,48 @@ 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 cert # 10. 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.
However, you can also force to renew any cert: However, you can also force to renew any cert:
``` ```
acme.sh --renew -d example.com --force acme.sh --renew -d example.com --force
``` ```
or, for ECC cert: or, for ECC cert:
``` ```
acme.sh --renew -d example.com --force --ecc acme.sh --renew -d example.com --force --ecc
``` ```
# 11. How to upgrade `acme.sh` # 11. How to upgrade `acme.sh`
acme.sh is in developing, it's strongly recommended to use the latest code.
acme.sh is in constant development, so it's strongly recommended to use the latest code.
You can update acme.sh to the latest code: You can update acme.sh to the latest code:
``` ```
acme.sh --upgrade acme.sh --upgrade
``` ```
You can enable auto upgrade: You can also enable auto upgrade:
``` ```
acme.sh --upgrade --auto-upgrade acme.sh --upgrade --auto-upgrade
``` ```
Then **acme.sh** will keep up to date automatically.
Then **acme.sh** will be kept up to date automatically.
Disable auto upgrade: Disable auto upgrade:
``` ```
acme.sh --upgrade --auto-upgrade 0 acme.sh --upgrade --auto-upgrade 0
``` ```
# 12. Issue a cert from an existing CSR # 12. 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
@@ -339,22 +358,25 @@ Speak ACME language using shell, directly to "Let's Encrypt".
TODO: TODO:
# Acknowledgment
# 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 # License & Others
License is GPLv3 License is GPLv3
Please Star and Fork me. Please Star and Fork me.
[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcomed. [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
# Donate # Donate
1. PayPal: donate@acme.sh
1. PayPal: 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)
+235 -215
View File
File diff suppressed because it is too large Load Diff
+173 -70
View File
@@ -1,99 +1,80 @@
# How to use dns api # How to use DNS API
## Use CloudFlare domain api to automatically issue cert ## 1. Use CloudFlare domain API to automatically issue cert
For now, we support clourflare integeration. First you need to login to your CloudFlare account to get your API key.
First you need to login to your clourflare account to get your api key.
``` ```
export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export CF_Email="xxxx@sss.com" export CF_Email="xxxx@sss.com"
``` ```
Ok, let's issue cert now: Ok, let's issue a cert now:
``` ```
acme.sh --issue --dns dns_cf -d example.com -d www.example.com acme.sh --issue --dns dns_cf -d example.com -d www.example.com
``` ```
The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key. The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 2. Use DNSPod.cn domain API to automatically issue cert
## Use Dnspod.cn domain api to automatically issue cert First you need to login to your DNSPod account to get your API Key and ID.
For now, we support dnspod.cn integeration.
First you need to login to your dnspod.cn account to get your api key and key id.
``` ```
export DP_Id="1234" export DP_Id="1234"
export DP_Key="sADDsdasdgdsf" export DP_Key="sADDsdasdgdsf"
``` ```
Ok, let's issue cert now: Ok, let's issue a cert now:
``` ```
acme.sh --issue --dns dns_dp -d example.com -d www.example.com acme.sh --issue --dns dns_dp -d example.com -d www.example.com
``` ```
The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf`, when next time you use dnspod.cn api, it will reuse this key. The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## Use Cloudxns.com domain api to automatically issue cert ## 3. Use CloudXNS.com domain API to automatically issue cert
For now, we support Cloudxns.com integeration. First you need to login to your CloudXNS account to get your API Key and Secret.
First you need to login to your Cloudxns.com account to get your api key and key secret.
``` ```
export CX_Key="1234" export CX_Key="1234"
export CX_Secret="sADDsdasdgdsf" export CX_Secret="sADDsdasdgdsf"
``` ```
Ok, let's issue cert now: Ok, let's issue a cert now:
``` ```
acme.sh --issue --dns dns_cx -d example.com -d www.example.com acme.sh --issue --dns dns_cx -d example.com -d www.example.com
``` ```
The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf`, when next time you use Cloudxns.com api, it will reuse this key. The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## Use Godaddy.com domain api to automatically issue cert ## 4. Use GoDaddy.com domain API to automatically issue cert
We support Godaddy integration. First you need to login to your GoDaddy account to get your API Key and Secret.
First you need to login to your Godaddy account to get your api key and api secret.
https://developer.godaddy.com/keys/ https://developer.godaddy.com/keys/
Please Create a Production key, instead of a Test key. Please create a Production key, instead of a Test key.
``` ```
export GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" export GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd" export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd"
``` ```
Ok, let's issue cert now: Ok, let's issue a cert now:
``` ```
acme.sh --issue --dns dns_gd -d example.com -d www.example.com acme.sh --issue --dns dns_gd -d example.com -d www.example.com
``` ```
The `GD_Key` and `GD_Secret` will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key. The `GD_Key` and `GD_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## Use PowerDNS embedded api to automatically issue cert
We support PowerDNS embedded API integration. ## 5. Use PowerDNS embedded API to automatically issue cert
First you need to enable api and set your api-token in PowerDNS configuration. First you need to login to your PowerDNS account to enable the API and set your API-Token in the configuration.
https://doc.powerdns.com/md/httpapi/README/ https://doc.powerdns.com/md/httpapi/README/
@@ -102,75 +83,197 @@ export PDNS_Url="http://ns.example.com:8081"
export PDNS_ServerId="localhost" export PDNS_ServerId="localhost"
export PDNS_Token="0123456789ABCDEF" export PDNS_Token="0123456789ABCDEF"
export PDNS_Ttl=60 export PDNS_Ttl=60
``` ```
Ok, let's issue cert now: Ok, let's issue a cert now:
``` ```
acme.sh --issue --dns dns_pdns -d example.com -d www.example.com acme.sh --issue --dns dns_pdns -d example.com -d www.example.com
``` ```
The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf`. The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## Use OVH/kimsufi/soyoustart/runabove API
## 6. Use OVH/kimsufi/soyoustart/runabove API to automatically issue cert
https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api https://github.com/Neilpang/acme.sh/wiki/How-to-use-OVH-domain-api
# Use custom api
If your api is not supported yet, you can write your own dns api. ## 7. Use nsupdate to automatically issue cert
Let's assume you want to name it 'myapi',
1. Create a bash script named `~/.acme.sh/dns_myapi.sh`,
2. In the script, you must have a function named `dns_myapi_add()`. Which will be called by acme.sh to add dns records.
3. Then you can use your api to issue cert like:
First, generate a key for updating the zone
``` ```
acme.sh --issue --dns dns_myapi -d example.com -d www.example.com b=$(dnssec-keygen -a hmac-sha512 -b 512 -n USER -K /tmp foo)
cat > /etc/named/keys/update.key <<EOF
key "update" {
algorithm hmac-sha512;
secret "$(awk '/^Key/{print $2}' /tmp/$b.private)";
};
EOF
rm -f /tmp/$b.{private,key}
``` ```
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh) Include this key in your named configuration
```
include "/etc/named/keys/update.key";
```
# Use lexicon dns api Next, configure your zone to allow dynamic updates.
https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api Depending on your named version, use either
```
zone "example.com" {
type master;
allow-update { key "update"; };
};
```
or
```
zone "example.com" {
type master;
update-policy {
grant update subdomain example.com.;
};
}
```
## Use LuaDNS domain API Finally, make the DNS server and update Key available to `acme.sh`
```
export NSUPDATE_SERVER="dns.example.com"
export NSUPDATE_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=="
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_nsupdate -d example.com -d www.example.com
```
The `NSUPDATE_SERVER` and `NSUPDATE_KEY` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 8. Use LuaDNS domain API
Get your API token at https://api.luadns.com/settings Get your API token at https://api.luadns.com/settings
``` ```
export LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" export LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export LUA_Email="xxxx@sss.com" export LUA_Email="xxxx@sss.com"
``` ```
To issue a cert: To issue a cert:
``` ```
acme.sh --issue --dns dns_lua --dnssleep 3 -d example.com -d www.example.com acme.sh --issue --dns dns_lua -d example.com -d www.example.com
``` ```
The `LUA_Key` and `LUA_Email` will be saved in `~/.acme.sh/account.conf`, and will be reused when needed. The `LUA_Key` and `LUA_Email` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## Use DNSMadeEasy domain API
## 9. Use DNSMadeEasy domain API
Get your API credentials at https://cp.dnsmadeeasy.com/account/info Get your API credentials at https://cp.dnsmadeeasy.com/account/info
``` ```
export ME_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" export ME_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export ME_Secret="qdfqsdfkjdskfj" export ME_Secret="qdfqsdfkjdskfj"
``` ```
To issue a cert: To issue a cert:
``` ```
acme.sh --issue --dns dns_me --dnssleep 3 -d example.com -d www.example.com acme.sh --issue --dns dns_me -d example.com -d www.example.com
``` ```
The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf`, and will be reused when needed. The `ME_Key` and `ME_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 10. Use Amazon Route53 domain API
https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API
```
export AWS_ACCESS_KEY_ID=XXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX
```
To issue a cert:
```
acme.sh --issue --dns dns_aws -d example.com -d www.example.com
```
The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 11. Use Aliyun domain API to automatically issue cert
First you need to login to your Aliyun account to get your API key.
[https://ak-console.aliyun.com/#/accesskey](https://ak-console.aliyun.com/#/accesskey)
```
export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export Ali_Secret="jlsdflanljkljlfdsaklkjflsa"
```
Ok, let's issue a cert now:
```
acme.sh --issue --dns dns_ali -d example.com -d www.example.com
```
The `Ali_Key` and `Ali_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 12. Use ISPConfig 3.1 API
This only works for ISPConfig 3.1 (and newer).
Create a Remote User in the ISPConfig Control Panel. The Remote User must have access to at least `DNS zone functions` and `DNS txt functions`.
```
export ISPC_User="xxx"
export ISPC_Password="xxx"
export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
export ISPC_Api_Insecure=1
```
If you have installed ISPConfig on a different port, then alter the 8080 accordingly.
Leaver ISPC_Api_Insecure set to 1 if you have not a valid ssl cert for your installation. Change it to 0 if you have a valid ssl cert.
To issue a cert:
```
acme.sh --issue --dns dns_ispconfig -d example.com -d www.example.com
```
The `ISPC_User`, `ISPC_Password`, `ISPC_Api`and `ISPC_Api_Insecure` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 13. Use Alwaysdata domain API
First you need to login to your Alwaysdata account to get your API Key.
```sh
export AD_API_KEY="myalwaysdataapikey"
```
Ok, let's issue a cert now:
```sh
acme.sh --issue --dns dns_ad -d example.com -d www.example.com
```
The `AD_API_KEY` will be saved in `~/.acme.sh/account.conf` and will be reused
when needed.
# Use custom API
If your API is not supported yet, you can write your own DNS API.
Let's assume you want to name it 'myapi':
1. Create a bash script named `~/.acme.sh/dns_myapi.sh`,
2. In the script you must have a function named `dns_myapi_add()` which will be called by acme.sh to add the DNS records.
3. Then you can use your API to issue cert like this:
```
acme.sh --issue --dns dns_myapi -d example.com -d www.example.com
```
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
# Use lexicon DNS API
https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env sh
#
#AD_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
#This is the Alwaysdata api wrapper for acme.sh
#
#Author: Paul Koppen
#Report Bugs here: https://github.com/wpk-/acme.sh
AD_API_URL="https://$AD_API_KEY:@api.alwaysdata.com/v1"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ad_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$AD_API_KEY" ]; then
AD_API_KEY=""
_err "You didn't specify the AD api key yet."
_err "Please create you key and try again."
return 1
fi
_saveaccountconf AD_API_KEY "$AD_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"
_ad_tmpl_json="{\"domain\":$_domain_id,\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"
if _ad_rest POST "record/" "$_ad_tmpl_json" && [ -z "$response" ]; then
_info "txt record updated success."
return 0
fi
return 1
}
#fulldomain txtvalue
dns_ad_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"
_ad_rest GET "record/?domain=$_domain_id&name=$_sub_domain"
if [ -n "$response" ]; then
record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
_debug record_id "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if _ad_rest DELETE "record/$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 _ad_rest GET "domain/"; 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
_ad_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" "$AD_API_URL/$ep" "" "$mtd")"
else
response="$(_get "$AD_API_URL/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
+187
View File
@@ -0,0 +1,187 @@
#!/usr/bin/env sh
Ali_API="https://alidns.aliyuncs.com/"
#Ali_Key="LTqIA87hOKdjevsf5"
#Ali_Secret="0p5EYueFNq501xnCPzKNbx6K51qPH2"
#Usage: dns_ali_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ali_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
Ali_Key=""
Ali_Secret=""
_err "You don't specify aliyun api key and secret yet."
return 1
fi
#save the api key and secret to the account conf file.
_saveaccountconf Ali_Key "$Ali_Key"
_saveaccountconf Ali_Secret "$Ali_Secret"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
return 1
fi
_debug "Add record"
_add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _ali_rest "Add record"
}
dns_ali_rm() {
fulldomain=$1
_clean
}
#################### Private functions below ##################################
_get_root() {
domain=$1
i=2
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
_describe_records_query "$h"
if ! _ali_rest "Get root" "ignore"; then
return 1
fi
if _contains "$response" "PageNumber"; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_debug _sub_domain "$_sub_domain"
_domain="$h"
_debug _domain "$_domain"
return 0
fi
p="$i"
i=$(_math "$i" + 1)
done
return 1
}
_ali_rest() {
signature=$(printf "%s" "GET&%2F&$(_ali_urlencode "$query")" | _hmac "sha1" "$(_hex "$Ali_Secret&")" | _base64)
signature=$(_ali_urlencode "$signature")
url="$Ali_API?$query&Signature=$signature"
if ! response="$(_get "$url")"; then
_err "Error <$1>"
return 1
fi
if [ -z "$2" ]; then
message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
if [ -n "$message" ]; then
_err "$message"
return 1
fi
fi
_debug2 response "$response"
return 0
}
_ali_urlencode() {
_str="$1"
_str_len=${#_str}
_u_i=1
while [ "$_u_i" -le "$_str_len" ]; do
_str_c="$(printf "%s" "$_str" | cut -c "$_u_i")"
case $_str_c in [a-zA-Z0-9.~_-])
printf "%s" "$_str_c"
;;
*)
printf "%%%02X" "'$_str_c"
;;
esac
_u_i="$(_math "$_u_i" + 1)"
done
}
_ali_nonce() {
#_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
#Not so good...
date +"%s%N"
}
_check_exist_query() {
query=''
query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=DescribeDomainRecords'
query=$query'&DomainName='$1
query=$query'&Format=json'
query=$query'&RRKeyWord=_acme-challenge'
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
query=$query'&Timestamp='$(_timestamp)
query=$query'&TypeKeyWord=TXT'
query=$query'&Version=2015-01-09'
}
_add_record_query() {
query=''
query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=AddDomainRecord'
query=$query'&DomainName='$1
query=$query'&Format=json'
query=$query'&RR='$2
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
query=$query'&Timestamp='$(_timestamp)
query=$query'&Type=TXT'
query=$query'&Value='$3
query=$query'&Version=2015-01-09'
}
_delete_record_query() {
query=''
query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=DeleteDomainRecord'
query=$query'&Format=json'
query=$query'&RecordId='$1
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
query=$query'&Timestamp='$(_timestamp)
query=$query'&Version=2015-01-09'
}
_describe_records_query() {
query=''
query=$query'AccessKeyId='$Ali_Key
query=$query'&Action=DescribeDomainRecords'
query=$query'&DomainName='$1
query=$query'&Format=json'
query=$query'&SignatureMethod=HMAC-SHA1'
query=$query"&SignatureNonce=$(_ali_nonce)"
query=$query'&SignatureVersion=1.0'
query=$query'&Timestamp='$(_timestamp)
query=$query'&Version=2015-01-09'
}
_clean() {
_check_exist_query "$_domain"
if ! _ali_rest "Check exist records" "ignore"; then
return 1
fi
records="$(echo "$response" -n | _egrep_o "\"RecordId\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
printf "%s" "$records" \
| while read -r record_id; do
_delete_record_query "$record_id"
_ali_rest "Delete record $record_id" "ignore"
done
}
_timestamp() {
date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ"
}
+227
View File
@@ -0,0 +1,227 @@
#!/usr/bin/env sh
#
#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
#
#AWS_SECRET_ACCESS_KEY="xxxxxxx"
#This is the Amazon Route53 api wrapper for acme.sh
AWS_HOST="route53.amazonaws.com"
AWS_URL="https://$AWS_HOST"
AWS_WIKI="https://github.com/Neilpang/acme.sh/wiki/How-to-use-Amazon-Route53-API"
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_aws_add() {
fulldomain=$1
txtvalue=$2
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
_err "You don't specify aws route53 api key id and and api key secret yet."
_err "Please create you key and try again. see $(__green $AWS_WIKI)"
return 1
fi
if [ -z "$AWS_SESSION_TOKEN" ]; then
_saveaccountconf AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
_saveaccountconf AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
fi
_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"
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>UPSERT</Action><ResourceRecordSet><Name>$fulldomain</Name><Type>TXT</Type><TTL>300</TTL><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "txt record updated success."
return 0
fi
return 1
}
#fulldomain txtvalue
dns_aws_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"
_aws_tmpl_xml="<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2013-04-01/\"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><ResourceRecords><ResourceRecord><Value>\"$txtvalue\"</Value></ResourceRecord></ResourceRecords><Name>$fulldomain.</Name><Type>TXT</Type><TTL>300</TTL></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>"
if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then
_info "txt record deleted success."
return 0
fi
return 1
}
#################### Private functions below ##################################
_get_root() {
domain=$1
i=2
p=1
if aws_rest GET "2013-04-01/hostedzone"; then
_debug "response" "$response"
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
#not valid
return 1
fi
if _contains "$response" "<Name>$h.</Name>"; then
hostedzone="$(echo "$response" | sed 's/<HostedZone>/\n&/g' | _egrep_o "<HostedZone>.*?<Name>$h.<.Name>.*?<.HostedZone>")"
_debug hostedzone "$hostedzone"
if [ -z "$hostedzone" ]; then
_err "Error, can not get hostedzone."
return 1
fi
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
fi
return 1
}
#method uri qstr data
aws_rest() {
mtd="$1"
ep="$2"
qsr="$3"
data="$4"
_debug mtd "$mtd"
_debug ep "$ep"
_debug qsr "$qsr"
_debug data "$data"
CanonicalURI="/$ep"
_debug2 CanonicalURI "$CanonicalURI"
CanonicalQueryString="$qsr"
_debug2 CanonicalQueryString "$CanonicalQueryString"
RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
_debug2 RequestDate "$RequestDate"
#RequestDate="20161120T141056Z" ##############
export _H1="x-amz-date: $RequestDate"
aws_host="$AWS_HOST"
CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
SignedHeaders="host;x-amz-date"
if [ -n "$AWS_SESSION_TOKEN" ]; then
export _H2="x-amz-security-token: $AWS_SESSION_TOKEN"
CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n"
SignedHeaders="${SignedHeaders};x-amz-security-token"
fi
_debug2 CanonicalHeaders "$CanonicalHeaders"
_debug2 SignedHeaders "$SignedHeaders"
RequestPayload="$data"
_debug2 RequestPayload "$RequestPayload"
Hash="sha256"
CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
_debug2 CanonicalRequest "$CanonicalRequest"
HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)"
_debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
Algorithm="AWS4-HMAC-SHA256"
_debug2 Algorithm "$Algorithm"
RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
_debug2 RequestDateOnly "$RequestDateOnly"
Region="us-east-1"
Service="route53"
CredentialScope="$RequestDateOnly/$Region/$Service/aws4_request"
_debug2 CredentialScope "$CredentialScope"
StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
_debug2 StringToSign "$StringToSign"
kSecret="AWS4$AWS_SECRET_ACCESS_KEY"
#kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
_debug2 kSecret "$kSecret"
kSecretH="$(_hex "$kSecret")"
_debug2 kSecretH "$kSecretH"
kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
_debug2 kDateH "$kDateH"
kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)"
_debug2 kRegionH "$kRegionH"
kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
_debug2 kServiceH "$kServiceH"
kSigningH="$(printf "aws4_request%s" | _hmac "$Hash" "$kServiceH" hex)"
_debug2 kSigningH "$kSigningH"
signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
_debug2 signature "$signature"
Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
_debug2 Authorization "$Authorization"
_H3="Authorization: $Authorization"
_debug _H3 "$_H3"
url="$AWS_URL/$ep"
if [ "$mtd" = "GET" ]; then
response="$(_get "$url")"
else
response="$(_post "$data" "$url")"
fi
_ret="$?"
if [ "$_ret" = "0" ]; then
if _contains "$response" "<ErrorResponse"; then
_err "Response error:$response"
return 1
fi
fi
return "$_ret"
}
+66 -26
View File
@@ -15,17 +15,25 @@ dns_cf_add() {
txtvalue=$2 txtvalue=$2
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key=""
CF_Email=""
_err "You don't specify cloudflare api key and email yet." _err "You don't specify cloudflare api key and email yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
fi fi
if ! _contains "$CF_Email" "@"; then
_err "It seems that the CF_Email=$CF_Email is not a valid email address."
_err "Please check and retry."
return 1
fi
#save the api key and email to the account conf file. #save the api key and email to the account conf file.
_saveaccountconf CF_Key "$CF_Key" _saveaccountconf CF_Key "$CF_Key"
_saveaccountconf CF_Email "$CF_Email" _saveaccountconf 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
_err "invalid domain" _err "invalid domain"
return 1 return 1
fi fi
@@ -36,20 +44,18 @@ dns_cf_add() {
_debug "Getting txt records" _debug "Getting txt records"
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain" _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain"
if ! printf "$response" | grep \"success\":true >/dev/null; then if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
_err "Error" _err "Error"
return 1 return 1
fi fi
count=$(printf "%s\n" "$response" | _egrep_o \"count\":[^,]* | cut -d : -f 2) count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2)
_debug count "$count" _debug count "$count"
if [ "$count" = "0" ]; then if [ "$count" = "0" ]; then
_info "Adding record" _info "Adding record"
if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
if printf -- "%s" "$response" | grep $fulldomain >/dev/null; then if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
_info "Added, sleeping 10 seconds" _info "Added, OK"
sleep 10
#todo: check if the record takes effect
return 0 return 0
else else
_err "Add txt record error." _err "Add txt record error."
@@ -59,14 +65,12 @@ dns_cf_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\":\"[^\"]*\" | cut -d : -f 2 | tr -d \" | head -n 1) record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1)
_debug "record_id" $record_id _debug "record_id" "$record_id"
_cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
_info "Updated, sleeping 10 seconds" _info "Updated, OK"
sleep 10
#todo: check if the record takes effect
return 0 return 0
fi fi
_err "Update error" _err "Update error"
@@ -75,13 +79,48 @@ dns_cf_add() {
} }
#fulldomain #fulldomain txtvalue
dns_cf_rm() { dns_cf_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"
_cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue"
if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then
_err "Error"
return 1
fi
count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | 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 | tr -d \" | head -n 1)
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id to remove."
return 1
fi
if ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then
_err "Delete record error."
return 1
fi
_contains "$response" '"success":true'
fi
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
@@ -91,8 +130,9 @@ _get_root() {
domain=$1 domain=$1
i=2 i=2
p=1 p=1
while [ '1' ]; do while true; do
h=$(printf $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
@@ -102,17 +142,17 @@ _get_root() {
return 1 return 1
fi fi
if printf $response | grep \"name\":\"$h\" >/dev/null; then if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o \"id\":\"[^\"]*\" | head -n 1 | cut -d : -f 2 | tr -d \") _domain_id=$(printf "%s\n" "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | head -n 1 | cut -d : -f 2 | tr -d \")
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf $domain | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain=$h
return 0 return 0
fi fi
return 1 return 1
fi fi
p=$i p=$i
i=$(expr $i + 1) i=$(_math "$i" + 1)
done done
return 1 return 1
} }
@@ -121,15 +161,15 @@ _cf_rest() {
m=$1 m=$1
ep="$2" ep="$2"
data="$3" data="$3"
_debug $ep _debug "$ep"
_H1="X-Auth-Email: $CF_Email" export _H1="X-Auth-Email: $CF_Email"
_H2="X-Auth-Key: $CF_Key" export _H2="X-Auth-Key: $CF_Key"
_H3="Content-Type: application/json" export _H3="Content-Type: application/json"
if [ "$data" ]; then if [ "$m" != "GET" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$CF_Api/$ep" "" $m)" response="$(_post "$data" "$CF_Api/$ep" "" "$m")"
else else
response="$(_get "$CF_Api/$ep")" response="$(_get "$CF_Api/$ep")"
fi fi
+43 -33
View File
@@ -17,24 +17,26 @@ dns_cx_add() {
txtvalue=$2 txtvalue=$2
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ]; then
CX_Key=""
CX_Secret=""
_err "You don't specify cloudxns.com api key or secret yet." _err "You don't specify cloudxns.com api key or secret yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
fi fi
REST_API=$CX_Api REST_API="$CX_Api"
#save the api key and email to the account conf file. #save the api key and email to the account conf file.
_saveaccountconf CX_Key "$CX_Key" _saveaccountconf CX_Key "$CX_Key"
_saveaccountconf CX_Secret "$CX_Secret" _saveaccountconf CX_Secret "$CX_Secret"
_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"
return 1 return 1
fi fi
existing_records $_domain $_sub_domain existing_records "$_domain" "$_sub_domain"
_debug count "$count" _debug count "$count"
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_err "Error get existing records." _err "Error get existing records."
@@ -42,9 +44,9 @@ dns_cx_add() {
fi fi
if [ "$count" = "0" ]; then if [ "$count" = "0" ]; then
add_record $_domain $_sub_domain $txtvalue add_record "$_domain" "$_sub_domain" "$txtvalue"
else else
update_record $_domain $_sub_domain $txtvalue update_record "$_domain" "$_sub_domain" "$txtvalue"
fi fi
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
@@ -56,7 +58,15 @@ dns_cx_add() {
#fulldomain #fulldomain
dns_cx_rm() { dns_cx_rm() {
fulldomain=$1 fulldomain=$1
REST_API="$CX_Api"
if _get_root "$fulldomain"; then
record_id=""
existing_records "$_domain" "$_sub_domain"
if ! [ "$record_id" = "" ]; then
_rest DELETE "record/$record_id/$_domain_id" "{}"
_info "Deleted record ${fulldomain}"
fi
fi
} }
#usage: root sub #usage: root sub
@@ -67,20 +77,20 @@ existing_records() {
_debug "Getting txt records" _debug "Getting txt records"
root=$1 root=$1
sub=$2 sub=$2
count=0
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
return 1 return 1
fi fi
count=0
seg=$(printf "%s\n" "$response" | _egrep_o "{[^\{]*host\":\"$_sub_domain\"[^\}]*\}") seg=$(printf "%s\n" "$response" | _egrep_o '[^{]*host":"'"$_sub_domain"'"[^}]*\}')
_debug seg "$seg" _debug seg "$seg"
if [ -z "$seg" ]; then if [ -z "$seg" ]; then
return 0 return 0
fi fi
if printf "$response" | grep '"type":"TXT"' >/dev/null; then if printf "%s" "$response" | grep '"type":"TXT"' >/dev/null; then
count=1 count=1
record_id=$(printf "%s\n" "$seg" | _egrep_o \"record_id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") record_id=$(printf "%s\n" "$seg" | _egrep_o '"record_id":"[^"]*"' | cut -d : -f 2 | tr -d \" | _head_n 1)
_debug record_id "$record_id" _debug record_id "$record_id"
return 0 return 0
fi fi
@@ -93,7 +103,7 @@ add_record() {
root=$1 root=$1
sub=$2 sub=$2
txtvalue=$3 txtvalue=$3
fulldomain=$sub.$root fulldomain="$sub.$root"
_info "Adding record" _info "Adding record"
@@ -110,7 +120,7 @@ update_record() {
root=$1 root=$1
sub=$2 sub=$2
txtvalue=$3 txtvalue=$3
fulldomain=$sub.$root fulldomain="$sub.$root"
_info "Updating record" _info "Updating record"
@@ -121,7 +131,7 @@ update_record() {
return 1 return 1
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
@@ -136,30 +146,30 @@ _get_root() {
return 1 return 1
fi fi
while [ '1' ]; do while true; do
h=$(printf $domain | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h" _debug h "$h"
if [ -z "$h" ]; then if [ -z "$h" ]; then
#not valid #not valid
return 1 return 1
fi fi
if printf "$response" | grep "$h." >/dev/null; then if _contains "$response" "$h."; then
seg=$(printf "%s" "$response" | _egrep_o "\{[^\{]*\"$h\.\"[^\}]*\}") seg=$(printf "%s\n" "$response" | _egrep_o '[^{]*"'"$h"'."[^}]*}')
_debug seg "$seg" _debug seg "$seg"
_domain_id=$(printf "%s" "$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"
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf $domain | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_debug _sub_domain $_sub_domain _debug _sub_domain "$_sub_domain"
_domain=$h _domain="$h"
_debug _domain $_domain _debug _domain "$_domain"
return 0 return 0
fi fi
return 1 return 1
fi fi
p=$i p="$i"
i=$(expr $i + 1) i=$(_math "$i" + 1)
done done
return 1 return 1
} }
@@ -168,7 +178,7 @@ _get_root() {
_rest() { _rest() {
m=$1 m=$1
ep="$2" ep="$2"
_debug $ep _debug ep "$ep"
url="$REST_API/$ep" url="$REST_API/$ep"
_debug url "$url" _debug url "$url"
@@ -180,16 +190,16 @@ _rest() {
sec="$CX_Key$url$data$cdate$CX_Secret" sec="$CX_Key$url$data$cdate$CX_Secret"
_debug sec "$sec" _debug sec "$sec"
hmac=$(printf "$sec" | openssl md5 | cut -d " " -f 2) hmac=$(printf "%s" "$sec" | _digest md5 hex)
_debug hmac "$hmac" _debug hmac "$hmac"
_H1="API-KEY: $CX_Key" export _H1="API-KEY: $CX_Key"
_H2="API-REQUEST-DATE: $cdate" export _H2="API-REQUEST-DATE: $cdate"
_H3="API-HMAC: $hmac" export _H3="API-HMAC: $hmac"
_H4="Content-Type: application/json" export _H4="Content-Type: application/json"
if [ "$data" ]; then if [ "$data" ]; then
response="$(_post "$data" "$url" "" $m)" response="$(_post "$data" "$url" "" "$m")"
else else
response="$(_get "$url")" response="$(_get "$url")"
fi fi
@@ -199,7 +209,7 @@ _rest() {
return 1 return 1
fi fi
_debug2 response "$response" _debug2 response "$response"
if ! printf "$response" | grep '"message":"success"' >/dev/null; then if ! _contains "$response" '"message":"success"'; then
return 1 return 1
fi fi
return 0 return 0
+64 -34
View File
@@ -6,9 +6,8 @@
# #
#DP_Key="sADDsdasdgdsf" #DP_Key="sADDsdasdgdsf"
DP_Api="https://dnsapi.cn" REST_API="https://dnsapi.cn"
#REST_API
######## Public functions ##################### ######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
@@ -17,24 +16,24 @@ dns_dp_add() {
txtvalue=$2 txtvalue=$2
if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then
DP_Id=""
DP_Key=""
_err "You don't specify dnspod api key and key id yet." _err "You don't specify dnspod api key and key id yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
fi fi
REST_API=$DP_Api
#save the api key and email to the account conf file. #save the api key and email to the account conf file.
_saveaccountconf DP_Id "$DP_Id" _saveaccountconf DP_Id "$DP_Id"
_saveaccountconf DP_Key "$DP_Key" _saveaccountconf DP_Key "$DP_Key"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root $fulldomain; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
return 1 return 1
fi fi
existing_records $_domain $_sub_domain existing_records "$_domain" "$_sub_domain"
_debug count "$count" _debug count "$count"
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
_err "Error get existing records." _err "Error get existing records."
@@ -42,15 +41,45 @@ dns_dp_add() {
fi fi
if [ "$count" = "0" ]; then if [ "$count" = "0" ]; then
add_record $_domain $_sub_domain $txtvalue add_record "$_domain" "$_sub_domain" "$txtvalue"
else else
update_record $_domain $_sub_domain $txtvalue update_record "$_domain" "$_sub_domain" "$txtvalue"
fi fi
} }
#fulldomain #fulldomain txtvalue
dns_dp_rm() { dns_dp_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
_err "Record.Lis error."
return 1
fi
if _contains "$response" 'No records'; then
_info "Don't need to remove."
return 0
fi
record_id=$(echo "$response" | _egrep_o '{[^{]*"value":"'"$txtvalue"'"' | cut -d , -f 1 | cut -d : -f 2 | tr -d \")
_debug record_id "$record_id"
if [ -z "$record_id" ]; then
_err "Can not get record id."
return 1
fi
if ! _rest POST "Record.Remove" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&record_id=$record_id"; then
_err "Record.Remove error."
return 1
fi
_contains "$response" "Action completed successful"
} }
@@ -67,14 +96,15 @@ existing_records() {
return 1 return 1
fi fi
if printf "$response" | grep 'No records'; then if _contains "$response" 'No records'; then
count=0 count=0
return 0 return 0
fi fi
if printf "$response" | grep "Action completed successful" >/dev/null; then if _contains "$response" "Action completed successful"; then
count=$(printf "$response" | grep '<type>TXT</type>' | wc -l) count=$(printf "%s" "$response" | grep -c '<type>TXT</type>' | tr -d ' ')
record_id=$(printf "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1) record_id=$(printf "%s" "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1)
_debug record_id "$record_id"
return 0 return 0
else else
_err "get existing records error." _err "get existing records error."
@@ -90,7 +120,7 @@ add_record() {
root=$1 root=$1
sub=$2 sub=$2
txtvalue=$3 txtvalue=$3
fulldomain=$sub.$root fulldomain="$sub.$root"
_info "Adding record" _info "Adding record"
@@ -98,7 +128,7 @@ add_record() {
return 1 return 1
fi fi
if printf "$response" | grep "Action completed successful"; then if _contains "$response" "Action completed successful"; then
return 0 return 0
fi fi
@@ -112,7 +142,7 @@ update_record() {
root=$1 root=$1
sub=$2 sub=$2
txtvalue=$3 txtvalue=$3
fulldomain=$sub.$root fulldomain="$sub.$root"
_info "Updating record" _info "Updating record"
@@ -120,7 +150,7 @@ update_record() {
return 1 return 1
fi fi
if printf "$response" | grep "Action completed successful"; then if _contains "$response" "Action completed successful"; then
return 0 return 0
fi fi
@@ -128,7 +158,7 @@ update_record() {
return 1 #error return 1 #error
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
@@ -138,8 +168,8 @@ _get_root() {
domain=$1 domain=$1
i=2 i=2
p=1 p=1
while [ '1' ]; do while true; do
h=$(printf $domain | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
#not valid #not valid
return 1 return 1
@@ -149,39 +179,39 @@ _get_root() {
return 1 return 1
fi fi
if printf "$response" | grep "Action completed successful" >/dev/null; then if _contains "$response" "Action completed successful"; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \") _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")
_debug _domain_id "$_domain_id" _debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf $domain | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_debug _sub_domain $_sub_domain _debug _sub_domain "$_sub_domain"
_domain=$h _domain="$h"
_debug _domain $_domain _debug _domain "$_domain"
return 0 return 0
fi fi
return 1 return 1
fi fi
p=$i p="$i"
i=$(expr $i + 1) i=$(_math "$i" + 1)
done done
return 1 return 1
} }
#Usage: method URI data #Usage: method URI data
_rest() { _rest() {
m=$1 m="$1"
ep="$2" ep="$2"
data="$3" data="$3"
_debug $ep _debug "$ep"
url="$REST_API/$ep" url="$REST_API/$ep"
_debug url "$url" _debug url "$url"
if [ "$data" ]; then if [ "$m" = "GET" ]; then
_debug2 data "$data" response="$(_get "$url" | tr -d '\r')"
response="$(_post $data "$url")"
else else
response="$(_get "$url")" _debug2 data "$data"
response="$(_post "$data" "$url" | tr -d '\r')"
fi fi
if [ "$?" != "0" ]; then if [ "$?" != "0" ]; then
+16 -15
View File
@@ -16,6 +16,8 @@ dns_gd_add() {
txtvalue=$2 txtvalue=$2
if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
GD_Key=""
GD_Secret=""
_err "You don't specify godaddy api key and secret yet." _err "You don't specify godaddy api key and secret yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
@@ -26,11 +28,11 @@ dns_gd_add() {
_saveaccountconf GD_Secret "$GD_Secret" _saveaccountconf GD_Secret "$GD_Secret"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root $fulldomain; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
return 1 return 1
fi fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
@@ -57,18 +59,17 @@ dns_gd_rm() {
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
# _domain=domain.com # _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() { _get_root() {
domain=$1 domain=$1
i=2 i=2
p=1 p=1
while [ '1' ]; do while true; do
h=$(printf $domain | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
#not valid #not valid
return 1 return 1
@@ -78,15 +79,15 @@ _get_root() {
return 1 return 1
fi fi
if printf "$response" | grep '"code":"NOT_FOUND"' >/dev/null; then if _contains "$response" '"code":"NOT_FOUND"'; then
_debug "$h not found" _debug "$h not found"
else else
_sub_domain=$(printf $domain | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain="$h"
return 0 return 0
fi fi
p=$i p="$i"
i=$(expr $i + 1) i=$(_math "$i" + 1)
done done
return 1 return 1
} }
@@ -95,14 +96,14 @@ _gd_rest() {
m=$1 m=$1
ep="$2" ep="$2"
data="$3" data="$3"
_debug $ep _debug "$ep"
_H1="Authorization: sso-key $GD_Key:$GD_Secret" export _H1="Authorization: sso-key $GD_Key:$GD_Secret"
_H2="Content-Type: application/json" export _H2="Content-Type: application/json"
if [ "$data" ]; then if [ "$data" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$GD_Api/$ep" "" $m)" response="$(_post "$data" "$GD_Api/$ep" "" "$m")"
else else
response="$(_get "$GD_Api/$ep")" response="$(_get "$GD_Api/$ep")"
fi fi
+177
View File
@@ -0,0 +1,177 @@
#!/usr/bin/env sh
# ISPConfig 3.1 API
# User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to:
# - DNS zone Functions
# - DNS txt Functions
# Report bugs to https://github.com/sjau/acme.sh
# Values to export:
# export ISPC_User="remoteUser"
# export ISPC_Password="remotePassword"
# export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
# export ISPC_Api_Insecure=1 # Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_ispconfig_add() {
fulldomain="${1}"
txtvalue="${2}"
_debug "Calling: dns_ispconfig_add() '${fulldomain}' '${txtvalue}'"
_ISPC_credentials && _ISPC_login && _ISPC_getZoneInfo && _ISPC_addTxt
}
#Usage: dns_myapi_rm _acme-challenge.www.domain.com
dns_ispconfig_rm() {
fulldomain="${1}"
_debug "Calling: dns_ispconfig_rm() '${fulldomain}'"
_ISPC_credentials && _ISPC_login && _ISPC_rmTxt
}
#################### Private functions below ##################################
_ISPC_credentials() {
if [ -z "${ISPC_User}" ] || [ -z "$ISPC_Password" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then
ISPC_User=""
ISPC_Password=""
ISPC_Api=""
ISPC_Api_Insecure=""
_err "You haven't specified the ISPConfig Login data, URL and whether you want check the ISPC SSL cert. Please try again."
return 1
else
_saveaccountconf ISPC_User "${ISPC_User}"
_saveaccountconf ISPC_Password "${ISPC_Password}"
_saveaccountconf ISPC_Api "${ISPC_Api}"
_saveaccountconf ISPC_Api_Insecure "${ISPC_Api_Insecure}"
# Set whether curl should use secure or insecure mode
export HTTPS_INSECURE="${ISPC_Api_Insecure}"
fi
}
_ISPC_login() {
_info "Getting Session ID"
curData="{\"username\":\"${ISPC_User}\",\"password\":\"${ISPC_Password}\",\"client_login\":false}"
curResult="$(_post "${curData}" "${ISPC_Api}?login")"
_debug "Calling _ISPC_login: '${curData}' '${ISPC_Api}?login'"
_debug "Result of _ISPC_login: '$curResult'"
if _contains "${curResult}" '"code":"ok"'; then
sessionID=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_info "Retrieved Session ID."
_debug "Session ID: '${sessionID}'"
else
_err "Couldn't retrieve the Session ID."
return 1
fi
}
_ISPC_getZoneInfo() {
_info "Getting Zoneinfo"
zoneEnd=false
curZone="${fulldomain}"
while [ "${zoneEnd}" = false ]; do
# we can strip the first part of the fulldomain, since it's just the _acme-challenge string
curZone="${curZone#*.}"
# suffix . needed for zone -> domain.tld.
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"origin\":\"${curZone}.\"}}"
curResult="$(_post "${curData}" "${ISPC_Api}?dns_zone_get")"
_debug "Calling _ISPC_getZoneInfo: '${curData}' '${ISPC_Api}?login'"
_debug "Result of _ISPC_getZoneInfo: '$curResult'"
if _contains "${curResult}" '"id":"'; then
zoneFound=true
zoneEnd=true
_info "Retrieved zone data."
_debug "Zone data: '${curResult}'"
fi
if [ "${curZone#*.}" != "$curZone" ]; then
_debug2 "$curZone still contains a '.' - so we can check next higher level"
else
zoneEnd=true
_err "Couldn't retrieve zone data."
return 1
fi
done
if [ "${zoneFound}" ]; then
server_id=$(echo "${curResult}" | _egrep_o "server_id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "Server ID: '${server_id}'"
case "${server_id}" in
'' | *[!0-9]*)
_err "Server ID is not numeric."
return 1
;;
*) _info "Retrieved Server ID" ;;
esac
zone=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "Zone: '${zone}'"
case "${zone}" in
'' | *[!0-9]*)
_err "Zone ID is not numeric."
return 1
;;
*) _info "Retrieved Zone ID" ;;
esac
client_id=$(echo "${curResult}" | _egrep_o "sys_userid.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "Client ID: '${client_id}'"
case "${client_id}" in
'' | *[!0-9]*)
_err "Client ID is not numeric."
return 1
;;
*) _info "Retrieved Client ID." ;;
esac
zoneFound=""
zoneEnd=""
fi
}
_ISPC_addTxt() {
curSerial="$(date +%s)"
curStamp="$(date +'%F %T')"
params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\""
curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}}}"
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")"
_debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'"
_debug "Result of _ISPC_addTxt: '$curResult'"
record_id=$(echo "${curResult}" | _egrep_o "\"response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "Record ID: '${record_id}'"
case "${record_id}" in
'' | *[!0-9]*)
_err "Couldn't add ACME Challenge TXT record to zone."
return 1
;;
*) _info "Added ACME Challenge TXT record to zone." ;;
esac
}
_ISPC_rmTxt() {
# Need to get the record ID.
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"name\":\"${fulldomain}.\",\"type\":\"TXT\"}}"
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_get")"
_debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_get'"
_debug "Result of _ISPC_rmTxt: '$curResult'"
if _contains "${curResult}" '"code":"ok"'; then
record_id=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
_debug "Record ID: '${record_id}'"
case "${record_id}" in
'' | *[!0-9]*)
_err "Record ID is not numeric."
return 1
;;
*)
unset IFS
_info "Retrieved Record ID."
curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\"}"
curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")"
_debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'"
_debug "Result of _ISPC_rmTxt: '$curResult'"
if _contains "${curResult}" '"code":"ok"'; then
_info "Removed ACME Challenge TXT record from zone."
else
_err "Couldn't remove ACME Challenge TXT record from zone."
return 1
fi
;;
esac
fi
}
+26 -20
View File
@@ -2,7 +2,7 @@
# dns api wrapper of lexicon for acme.sh # dns api wrapper of lexicon for acme.sh
lexicon_url="https://github.com/AnalogJ/lexicon" # https://github.com/AnalogJ/lexicon
lexicon_cmd="lexicon" lexicon_cmd="lexicon"
wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api" wiki="https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api"
@@ -14,14 +14,15 @@ dns_lexicon_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
domain=$(printf "$fulldomain" | cut -d . -f 2-999) domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
if ! _exists $lexicon_cmd; then if ! _exists "$lexicon_cmd"; then
_err "Please install $lexicon_cmd first: $wiki" _err "Please install $lexicon_cmd first: $wiki"
return 1 return 1
fi fi
if [ -z "$PROVIDER" ]; then if [ -z "$PROVIDER" ]; then
PROVIDER=""
_err "Please define env PROVIDER first: $wiki" _err "Please define env PROVIDER first: $wiki"
return 1 return 1
fi fi
@@ -29,39 +30,44 @@ dns_lexicon_add() {
_savedomainconf PROVIDER "$PROVIDER" _savedomainconf PROVIDER "$PROVIDER"
export PROVIDER export PROVIDER
Lx_name=$(echo LEXICON_${PROVIDER}_USERNAME | tr [a-z] [A-Z]) # e.g. busybox-ash does not know [:upper:]
eval Lx_name_v="\$$Lx_name" # shellcheck disable=SC2018,SC2019
Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z')
Lx_name_v=$(eval echo \$"$Lx_name")
_debug "$Lx_name" "$Lx_name_v" _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"
export "$Lx_name" eval export "$Lx_name"
fi fi
Lx_token=$(echo LEXICON_${PROVIDER}_TOKEN | tr [a-z] [A-Z]) # shellcheck disable=SC2018,SC2019
eval Lx_token_v="\$$Lx_token" Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z')
Lx_token_v=$(eval echo \$"$Lx_token")
_debug "$Lx_token" "$Lx_token_v" _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"
export "$Lx_token" eval export "$Lx_token"
fi fi
Lx_password=$(echo LEXICON_${PROVIDER}_PASSWORD | tr [a-z] [A-Z]) # shellcheck disable=SC2018,SC2019
eval Lx_password_v="\$$Lx_password" Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z')
Lx_password_v=$(eval echo \$"$Lx_password")
_debug "$Lx_password" "$Lx_password_v" _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"
export "$Lx_password" eval export "$Lx_password"
fi fi
Lx_domaintoken=$(echo LEXICON_${PROVIDER}_DOMAINTOKEN | tr [a-z] [A-Z]) # shellcheck disable=SC2018,SC2019
eval Lx_domaintoken_v="\$$Lx_domaintoken" Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z')
Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken")
_debug "$Lx_domaintoken" "$Lx_domaintoken_v" _debug "$Lx_domaintoken" "$Lx_domaintoken_v"
if [ "$Lx_domaintoken_v" ]; then if [ "$Lx_domaintoken_v" ]; then
export "$Lx_domaintoken" eval export "$Lx_domaintoken"
_saveaccountconf $Lx_domaintoken "$Lx_domaintoken_v" _saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v"
fi fi
$lexicon_cmd "$PROVIDER" create ${domain} TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" $lexicon_cmd "$PROVIDER" create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}"
} }
+21 -19
View File
@@ -8,7 +8,7 @@
#LUA_Email="user@luadns.net" #LUA_Email="user@luadns.net"
LUA_Api="https://api.luadns.com/v1" LUA_Api="https://api.luadns.com/v1"
LUA_auth=$(printf $LUA_Email:$LUA_Key | _base64) LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
######## Public functions ##################### ######## Public functions #####################
@@ -18,6 +18,8 @@ dns_lua_add() {
txtvalue=$2 txtvalue=$2
if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then
LUA_Key=""
LUA_Email=""
_err "You don't specify luadns api key and email yet." _err "You don't specify luadns api key and email yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
@@ -28,7 +30,7 @@ dns_lua_add() {
_saveaccountconf LUA_Email "$LUA_Email" _saveaccountconf LUA_Email "$LUA_Email"
_debug "First detect the root zone" _debug "First detect the root zone"
if ! _get_root $fulldomain; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
return 1 return 1
fi fi
@@ -39,17 +41,17 @@ dns_lua_add() {
_debug "Getting txt records" _debug "Getting txt records"
_LUA_rest GET "zones/${_domain_id}/records" _LUA_rest GET "zones/${_domain_id}/records"
if ! printf "$response" | grep \"id\": >/dev/null; then if ! _contains "$response" "\"id\":"; then
_err "Error" _err "Error"
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\"" | wc -l)
_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 printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
_info "Added" _info "Added"
#todo: check if the record takes effect #todo: check if the record takes effect
return 0 return 0
@@ -61,8 +63,8 @@ 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\"" | 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" ]; then
@@ -82,7 +84,7 @@ dns_lua_rm() {
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
@@ -95,24 +97,24 @@ _get_root() {
if ! _LUA_rest GET "zones"; then if ! _LUA_rest GET "zones"; then
return 1 return 1
fi fi
while [ '1' ]; do while true; do
h=$(printf $domain | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
#not valid #not valid
return 1 return 1
fi fi
if printf $response | grep \"name\":\"$h\" >/dev/null; 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)
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf $domain | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain="$h"
return 0 return 0
fi fi
return 1 return 1
fi fi
p=$i p=$i
i=$(expr $i + 1) i=$(_math "$i" + 1)
done done
return 1 return 1
} }
@@ -121,13 +123,13 @@ _LUA_rest() {
m=$1 m=$1
ep="$2" ep="$2"
data="$3" data="$3"
_debug $ep _debug "$ep"
_H1="Accept: application/json" export _H1="Accept: application/json"
_H2="Authorization: Basic $LUA_auth" export _H2="Authorization: Basic $LUA_auth"
if [ "$data" ]; then if [ "$data" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$LUA_Api/$ep" "" $m)" response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
else else
response="$(_get "$LUA_Api/$ep")" response="$(_get "$LUA_Api/$ep")"
fi fi
+21 -19
View File
@@ -15,6 +15,8 @@ dns_me_add() {
txtvalue=$2 txtvalue=$2
if [ -z "$ME_Key" ] || [ -z "$ME_Secret" ]; then if [ -z "$ME_Key" ] || [ -z "$ME_Secret" ]; then
ME_Key=""
ME_Secret=""
_err "You didn't specify DNSMadeEasy api key and secret yet." _err "You didn't specify DNSMadeEasy api key and secret yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
@@ -25,7 +27,7 @@ dns_me_add() {
_saveaccountconf ME_Secret "$ME_Secret" _saveaccountconf ME_Secret "$ME_Secret"
_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"
return 1 return 1
fi fi
@@ -36,12 +38,12 @@ dns_me_add() {
_debug "Getting txt records" _debug "Getting txt records"
_me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT" _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
if ! printf "$response" | grep \"totalRecords\": >/dev/null; then if ! _contains "$response" "\"totalRecords\":"; then
_err "Error" _err "Error"
return 1 return 1
fi fi
count=$(printf "%s\n" "$response" | _egrep_o \"totalRecords\":[^,]* | cut -d : -f 2) count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2)
_debug count "$count" _debug count "$count"
if [ "$count" = "0" ]; then if [ "$count" = "0" ]; then
_info "Adding record" _info "Adding record"
@@ -58,8 +60,8 @@ dns_me_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\":[^,]* | cut -d : -f 2 | head -n 1) record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | head -n 1)
_debug "record_id" $record_id _debug "record_id" "$record_id"
_me_rest PUT "$_domain_id/records/$record_id/" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}" _me_rest PUT "$_domain_id/records/$record_id/" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
@@ -79,7 +81,7 @@ dns_me_rm() {
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
@@ -89,8 +91,8 @@ _get_root() {
domain=$1 domain=$1
i=2 i=2
p=1 p=1
while [ '1' ]; do while true; do
h=$(printf $domain | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
#not valid #not valid
return 1 return 1
@@ -100,17 +102,17 @@ _get_root() {
return 1 return 1
fi fi
if printf $response | grep \"name\":\"$h\" >/dev/null; then if _contains "$response" "\"name\":\"$h\""; then
_domain_id=$(printf "%s\n" "$response" | _egrep_o \"id\":[^,]* | head -n 1 | cut -d : -f 2) _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | head -n 1 | cut -d : -f 2 | tr -d '}')
if [ "$_domain_id" ]; then if [ "$_domain_id" ]; then
_sub_domain=$(printf $domain | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain="$h"
return 0 return 0
fi fi
return 1 return 1
fi fi
p=$i p=$i
i=$(expr $i + 1) i=$(_math "$i" + 1)
done done
return 1 return 1
} }
@@ -119,18 +121,18 @@ _me_rest() {
m=$1 m=$1
ep="$2" ep="$2"
data="$3" data="$3"
_debug $ep _debug "$ep"
cdate=$(date -u +"%a, %d %b %Y %T %Z") cdate=$(date -u +"%a, %d %b %Y %T %Z")
hmac=$(printf "$cdate" | _hmac sha1 "$ME_Secret" 1) hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(_hex "$ME_Secret")" hex)
_H1="x-dnsme-apiKey: $ME_Key" export _H1="x-dnsme-apiKey: $ME_Key"
_H2="x-dnsme-requestDate: $cdate" export _H2="x-dnsme-requestDate: $cdate"
_H3="x-dnsme-hmac: $hmac" export _H3="x-dnsme-hmac: $hmac"
if [ "$data" ]; then if [ "$data" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$ME_Api/$ep" "" $m)" response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
else else
response="$(_get "$ME_Api/$ep")" response="$(_get "$ME_Api/$ep")"
fi fi
+14 -31
View File
@@ -5,48 +5,31 @@
#So, here must be a method dns_myapi_add() #So, here must be a method dns_myapi_add()
#Which will be called by acme.sh to add the txt record to your api system. #Which will be called by acme.sh to add the txt record to your api system.
#returns 0 means success, otherwise error. #returns 0 means success, otherwise error.
#
#Author: Neilpang
#Report Bugs here: https://github.com/Neilpang/acme.sh
#
######## Public functions ##################### ######## Public functions #####################
#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_myapi_add() { dns_myapi_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
_info "Using myapi"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
_err "Not implemented!" _err "Not implemented!"
return 1 return 1
} }
#fulldomain #Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_myapi_rm() { dns_myapi_rm() {
fulldomain=$1 fulldomain=$1
txtvalue=$2
_info "Using myapi"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
_info() {
if [ -z "$2" ]; then
echo "[$(date)] $1"
else
echo "[$(date)] $1='$2'"
fi
}
_err() {
_info "$@" >&2
return 1
}
_debug() {
if [ -z "$DEBUG" ]; then
return
fi
_err "$@"
return 0
}
_debug2() {
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
_debug "$@"
fi
return
}
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env sh
######## Public functions #####################
#Usage: dns_nsupdate_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_nsupdate_add() {
fulldomain=$1
txtvalue=$2
_checkKeyFile || return 1
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
# save the dns server and key to the account conf file.
_saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}"
_saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}"
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
nsupdate -k "${NSUPDATE_KEY}" <<EOF
server ${NSUPDATE_SERVER}
update add ${fulldomain}. 60 in txt "${txtvalue}"
send
EOF
if [ $? -ne 0 ]; then
_err "error updating domain"
return 1
fi
return 0
}
#Usage: dns_nsupdate_rm _acme-challenge.www.domain.com
dns_nsupdate_rm() {
fulldomain=$1
_checkKeyFile || return 1
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
_info "removing ${fulldomain}. txt"
nsupdate -k "${NSUPDATE_KEY}" <<EOF
server ${NSUPDATE_SERVER}
update delete ${fulldomain}. txt
send
EOF
if [ $? -ne 0 ]; then
_err "error updating domain"
return 1
fi
return 0
}
#################### Private functions below ##################################
_checkKeyFile() {
if [ -z "${NSUPDATE_KEY}" ]; then
_err "you must specify a path to the nsupdate key file"
return 1
fi
if [ ! -r "${NSUPDATE_KEY}" ]; then
_err "key ${NSUPDATE_KEY} is unreadable"
return 1
fi
}
+18 -17
View File
@@ -86,6 +86,8 @@ dns_ovh_add() {
txtvalue=$2 txtvalue=$2
if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then
OVH_AK=""
OVH_AS=""
_err "You don't specify OVH application key and application secret yet." _err "You don't specify OVH application key and application secret yet."
_err "Please create you key and try again." _err "Please create you key and try again."
return 1 return 1
@@ -127,11 +129,11 @@ dns_ovh_add() {
_info "Consumer key is ok." _info "Consumer key is ok."
_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"
return 1 return 1
fi fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain" _debug _sub_domain "$_sub_domain"
_debug _domain "$_domain" _debug _domain "$_domain"
@@ -157,7 +159,7 @@ dns_ovh_add() {
_err "Can not get record id." _err "Can not get record id."
return 1 return 1
fi fi
_debug "record_id" $record_id _debug "record_id" "$record_id"
if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then if _ovh_rest PUT "domain/zone/$_domain/record/$record_id" "{\"target\":\"$txtvalue\",\"subDomain\":\"$_sub_domain\",\"ttl\":60}"; then
if _contains "$response" "null"; then if _contains "$response" "null"; then
@@ -180,7 +182,7 @@ dns_ovh_rm() {
} }
#################### Private functions bellow ################################## #################### Private functions below ##################################
_ovh_authentication() { _ovh_authentication() {
@@ -221,13 +223,12 @@ _ovh_authentication() {
#returns #returns
# _sub_domain=_acme-challenge.www # _sub_domain=_acme-challenge.www
# _domain=domain.com # _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() { _get_root() {
domain=$1 domain=$1
i=2 i=2
p=1 p=1
while [ '1' ]; do while true; do
h=$(printf $domain | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
#not valid #not valid
return 1 return 1
@@ -238,12 +239,12 @@ _get_root() {
fi fi
if ! _contains "$response" "This service does not exist" >/dev/null; then if ! _contains "$response" "This service does not exist" >/dev/null; then
_sub_domain=$(printf $domain | cut -d . -f 1-$p) _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h _domain="$h"
return 0 return 0
fi fi
p=$i p=$i
i=$(expr $i + 1) i=$(_math "$i" + 1)
done done
return 1 return 1
} }
@@ -261,7 +262,7 @@ _ovh_rest() {
m=$1 m=$1
ep="$2" ep="$2"
data="$3" data="$3"
_debug $ep _debug "$ep"
_ovh_url="$OVH_API/$ep" _ovh_url="$OVH_API/$ep"
_debug2 _ovh_url "$_ovh_url" _debug2 _ovh_url "$_ovh_url"
@@ -272,15 +273,15 @@ _ovh_rest() {
_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"
_H1="X-Ovh-Application: $OVH_AK" export _H1="X-Ovh-Application: $OVH_AK"
_H2="X-Ovh-Signature: \$1\$$_ovh_hex" export _H2="X-Ovh-Signature: \$1\$$_ovh_hex"
_debug2 _H2 "$_H2" _debug2 _H2 "$_H2"
_H3="X-Ovh-Timestamp: $_ovh_t" export _H3="X-Ovh-Timestamp: $_ovh_t"
_H4="X-Ovh-Consumer: $OVH_CK" export _H4="X-Ovh-Consumer: $OVH_CK"
_H5="Content-Type: application/json;charset=utf-8" export _H5="Content-Type: application/json;charset=utf-8"
if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ]; then
_debug data "$data" _debug data "$data"
response="$(_post "$data" "$_ovh_url" "" $m)" response="$(_post "$data" "$_ovh_url" "" "$m")"
else else
response="$(_get "$_ovh_url")" response="$(_get "$_ovh_url")"
fi fi
+61 -16
View File
@@ -12,30 +12,35 @@ DEFAULT_PDNS_TTL=60
######## Public functions ##################### ######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000" #Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
#fulldomain
#txtvalue
dns_pdns_add() { dns_pdns_add() {
fulldomain=$1 fulldomain=$1
txtvalue=$2 txtvalue=$2
if [ -z "$PDNS_Url" ]; then if [ -z "$PDNS_Url" ]; then
PDNS_Url=""
_err "You don't specify PowerDNS address." _err "You don't specify PowerDNS address."
_err "Please set PDNS_Url and try again." _err "Please set PDNS_Url and try again."
return 1 return 1
fi fi
if [ -z "$PDNS_ServerId" ]; then if [ -z "$PDNS_ServerId" ]; then
PDNS_ServerId=""
_err "You don't specify PowerDNS server id." _err "You don't specify PowerDNS server id."
_err "Please set you PDNS_ServerId and try again." _err "Please set you PDNS_ServerId and try again."
return 1 return 1
fi fi
if [ -z "$PDNS_Token" ]; then if [ -z "$PDNS_Token" ]; then
PDNS_Token=""
_err "You don't specify PowerDNS token." _err "You don't specify PowerDNS token."
_err "Please create you PDNS_Token and try again." _err "Please create you PDNS_Token and try again."
return 1 return 1
fi fi
if [ -z "$PDNS_Ttl" ]; then if [ -z "$PDNS_Ttl" ]; then
PDNS_Ttl=$DEFAULT_PDNS_TTL PDNS_Ttl="$DEFAULT_PDNS_TTL"
fi fi
#save the api addr and key to the account conf file. #save the api addr and key to the account conf file.
@@ -47,8 +52,8 @@ dns_pdns_add() {
_saveaccountconf PDNS_Ttl "$PDNS_Ttl" _saveaccountconf PDNS_Ttl "$PDNS_Ttl"
fi fi
_debug "First detect the root zone" _debug "Detect root zone"
if ! _get_root $fulldomain; then if ! _get_root "$fulldomain"; then
_err "invalid domain" _err "invalid domain"
return 1 return 1
fi fi
@@ -65,6 +70,18 @@ dns_pdns_add() {
dns_pdns_rm() { dns_pdns_rm() {
fulldomain=$1 fulldomain=$1
_debug "Detect root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain "$_domain"
if ! rm_record "$_domain" "$fulldomain"; then
return 1
fi
return 0
} }
set_record() { set_record() {
@@ -73,45 +90,73 @@ set_record() {
full=$2 full=$2
txtvalue=$3 txtvalue=$3
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"name\": \"$full.\", \"changetype\": \"REPLACE\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then
_err "Set txt record error." _err "Set txt record error."
return 1 return 1
fi fi
if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root./notify"; then
_err "Notify servers error." if ! notify_slaves "$root"; then
return 1 return 1
fi fi
return 0 return 0
} }
#################### Private functions bellow ################################## rm_record() {
_info "Remove record"
root=$1
full=$2
if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then
_err "Delete txt record error."
return 1
fi
if ! notify_slaves "$root"; then
return 1
fi
return 0
}
notify_slaves() {
root=$1
if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root./notify"; then
_err "Notify slaves error."
return 1
fi
return 0
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com #_acme-challenge.www.domain.com
#returns #returns
# _domain=domain.com # _domain=domain.com
_get_root() { _get_root() {
domain=$1 domain=$1
i=1 i=1
p=1
if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then
_zones_response=$response _zones_response="$response"
fi fi
while [ '1' ]; do while true; do
h=$(printf $domain | cut -d . -f $i-100) h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then if [ -z "$h" ]; then
return 1 return 1
fi fi
if printf "$_zones_response" | grep "\"name\": \"$h.\"" >/dev/null; then if _contains "$_zones_response" "\"name\": \"$h.\""; then
_domain=$h _domain="$h"
return 0 return 0
fi fi
p=$i i=$(_math $i + 1)
i=$(expr $i + 1)
done done
_debug "$domain not found" _debug "$domain not found"
return 1 return 1
} }
@@ -120,7 +165,7 @@ _pdns_rest() {
ep=$2 ep=$2
data=$3 data=$3
_H1="X-API-Key: $PDNS_Token" export _H1="X-API-Key: $PDNS_Token"
if [ ! "$method" = "GET" ]; then if [ ! "$method" = "GET" ]; then
_debug data "$data" _debug data "$data"