mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2025-04-30 13:12:49 +00:00
Merge remote-tracking branch 'refs/remotes/Neilpang/master'
This commit is contained in:
commit
6a7b084f97
203
README.md
203
README.md
@ -1,20 +1,28 @@
|
|||||||
# le
|
# le: means simp`Le`
|
||||||
Simplest shell script for LetsEncrypt free Certificate client
|
Simplest shell script for LetsEncrypt free Certificate client
|
||||||
|
|
||||||
This is a shell version from https://github.com/diafygi/acme-tiny
|
Simple and Powerful, you only need 3 minutes to learn.
|
||||||
|
|
||||||
Pure written in bash, no dependencies to python , acme-tiny or LetsEncrypt official client (https://github.com/letsencrypt/letsencrypt)
|
Pure written in bash, no dependencies to python , acme-tiny or LetsEncrypt official client.
|
||||||
|
Just one script, to issue, renew your certificates automatically.
|
||||||
Just one script, to issue, renew your certiricates automatically.
|
|
||||||
|
|
||||||
Probably it's the smallest&easiest&smartest shell script to automatically issue&renew the free certificates from LetsEncrypt.
|
Probably it's the smallest&easiest&smartest shell script to automatically issue&renew the free certificates from LetsEncrypt.
|
||||||
|
|
||||||
|
Do NOT require to be `root/sudoer`.
|
||||||
|
|
||||||
#Supported OS
|
#Tested OS
|
||||||
1. Tested on Ubuntu/Debian.
|
1. Ubuntu/Debian.
|
||||||
2. CentOS is Not tested yet, It should work.
|
2. CentOS
|
||||||
|
3. Windows (cygwin with curl, openssl and crontab included)
|
||||||
|
4. FreeBSD with bash
|
||||||
|
|
||||||
|
|
||||||
|
#Supported Mode
|
||||||
|
1. Webroot mode
|
||||||
|
2. Standalone mode
|
||||||
|
3. Apache mode
|
||||||
|
4. Dns mode
|
||||||
|
|
||||||
#How to use
|
#How to use
|
||||||
|
|
||||||
1. Clone this project: https://github.com/Neilpang/le.git
|
1. Clone this project: https://github.com/Neilpang/le.git
|
||||||
@ -23,64 +31,199 @@ Probably it's the smallest&easiest&smartest shell script to automatically issue
|
|||||||
```
|
```
|
||||||
./le.sh install
|
./le.sh install
|
||||||
```
|
```
|
||||||
Which does 2 things:
|
You don't have to be root then, although it is recommended.
|
||||||
* create and copy le.sh to your home dir: `~/.le`
|
|
||||||
All the certs will be placed in this folder.
|
|
||||||
* create symbol link: `/bin/le -> ~/.le/le.sh`
|
|
||||||
|
|
||||||
3. Ok, you are ready to issue cert now.
|
Which does 3 jobs:
|
||||||
|
* create and copy `le.sh` to your home dir: `~/.le`
|
||||||
|
All the certs will be placed in this folder.
|
||||||
|
* create alias : `le.sh=~/.le/le.sh` and `le=~/.le/le.sh`.
|
||||||
|
* create everyday cron job to check and renew the cert if needed.
|
||||||
|
|
||||||
|
After install, you must close current terminal and reopen again to make the alias take effect.
|
||||||
|
|
||||||
|
Ok, you are ready to issue cert now.
|
||||||
Show help message:
|
Show help message:
|
||||||
```
|
```
|
||||||
root@xvm:~# le
|
root@v1:~# le.sh
|
||||||
Usage: issue|renew|renewAll|createAccountKey|createDomainKey|createCSR|install|uninstall
|
https://github.com/Neilpang/le
|
||||||
|
v1.1.1
|
||||||
|
Usage: le.sh [command] ...[args]....
|
||||||
|
Available commands:
|
||||||
|
|
||||||
|
install:
|
||||||
|
Install le.sh to your system.
|
||||||
|
issue:
|
||||||
|
Issue a cert.
|
||||||
|
installcert:
|
||||||
|
Install the issued cert to apache/nginx or any other server.
|
||||||
|
renew:
|
||||||
|
Renew a cert.
|
||||||
|
renewAll:
|
||||||
|
Renew all the certs.
|
||||||
|
uninstall:
|
||||||
|
Uninstall le.sh, and uninstall the cron job.
|
||||||
|
version:
|
||||||
|
Show version info.
|
||||||
|
installcronjob:
|
||||||
|
Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
|
||||||
|
uninstallcronjob:
|
||||||
|
Uninstall the cron job. The 'uninstall' command can do this automatically.
|
||||||
|
createAccountKey:
|
||||||
|
Create an account private key, professional use.
|
||||||
|
createDomainKey:
|
||||||
|
Create an domain private key, professional use.
|
||||||
|
createCSR:
|
||||||
|
Create CSR , professional use.
|
||||||
|
|
||||||
|
|
||||||
|
root@v1:~/le# le issue
|
||||||
|
Usage: le issue webroot|no|apache|dns a.com [www.a.com,b.com,c.com]|no [key-length]|no
|
||||||
|
|
||||||
root@xvm:~# le issue
|
|
||||||
Usage: /bin/le webroot a.com [www.a.com,b.com,c.com] [key-length] [cert-file-path] [key-file-path] [reloadCmd]
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Set the param value to "no" means you want to ignore it.
|
||||||
|
|
||||||
|
For example, if you give "no" to "key-length", it will use default length 2048.
|
||||||
|
|
||||||
|
And if you give 'no' to 'cert-file-path', it will not copy the issued cert to the "cert-file-path".
|
||||||
|
|
||||||
|
In all the cases, the issued cert will be placed in "~/.le/domain.com/"
|
||||||
|
|
||||||
|
|
||||||
# Just issue a cert:
|
# Just issue a cert:
|
||||||
```
|
```
|
||||||
le issue /home/wwwroot/aa.com aa.com www.aa.com,cp.aa.com
|
le issue /home/wwwroot/aa.com aa.com www.aa.com,cp.aa.com
|
||||||
```
|
```
|
||||||
First argument " /home/wwwroot/aa.com" is the web root folder
|
First argument `/home/wwwroot/aa.com` is the web root folder, You must have `write` access to this folder.
|
||||||
|
|
||||||
Second argument "aa.com" is the domain you want to issue cert for.
|
Second argument "aa.com" is the main domain you want to issue cert for.
|
||||||
|
|
||||||
Third argument is the additional domain list you want to use. Comma sperated list, Optional.
|
Third argument is the additional domain list you want to use. Comma separated list, which is Optional.
|
||||||
|
|
||||||
'You must point and bind all the domains to the same webroot dir:/home/wwwroot/aa.com'
|
You must point and bind all the domains to the same webroot dir:`/home/wwwroot/aa.com`
|
||||||
|
|
||||||
The cert will be placed in `~/.le/aa.com/`
|
The cert will be placed in `~/.le/aa.com/`
|
||||||
|
|
||||||
|
The issued cert will be renewed every 80 days automatically.
|
||||||
|
|
||||||
The issued cert will be renewed every 50 days automatically.
|
# Install issued cert to apache/nginx etc.
|
||||||
|
|
||||||
|
|
||||||
# Issue a cert, and install to apache
|
|
||||||
```
|
```
|
||||||
le issue /home/wwwroot/aa.com aa.com www.aa.com,cp.aa.com 2048 /path/to/certfile/in/apache/nginx /path/to/keyfile/in/apache/nginx "service apache2 reload"
|
le installcert aa.com /path/to/certfile/in/apache/nginx /path/to/keyfile/in/apache/nginx /path/to/ca/certfile/apache/nginx "service apache2|nginx reload"
|
||||||
```
|
```
|
||||||
This can link the issued cert to the production apache or nginx path.
|
|
||||||
Once the cert is renewed, the apache/nginx will be automatically reloaded by the command: ` service apache2 reload`
|
|
||||||
|
|
||||||
|
Install the issued cert/key to the production apache or nginx path.
|
||||||
|
|
||||||
|
The cert will be renewed every 80 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`
|
||||||
|
|
||||||
|
|
||||||
|
# Use Standalone server to issue cert( requires you be root/sudoer, or you have permission to listen tcp 80 port):
|
||||||
|
Same usage as all above, just give `no` as the webroot.
|
||||||
|
The tcp `80` port must be free to listen, otherwise you will be prompted to free the `80` port and try again.
|
||||||
|
|
||||||
|
```
|
||||||
|
le issue no aa.com www.aa.com,cp.aa.com
|
||||||
|
```
|
||||||
|
|
||||||
|
# Use Apache mode(requires you 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.
|
||||||
|
Particularly, if you are running an apache server, you can use apache mode instead. Which doesn't write any file to your web root folder.
|
||||||
|
|
||||||
|
Just set string "apache" to the first argument, it will use apache plugin automatically.
|
||||||
|
|
||||||
|
```
|
||||||
|
le issue apache aa.com www.aa.com,user.aa.com
|
||||||
|
```
|
||||||
|
All the other arguments are the same with previous.
|
||||||
|
|
||||||
|
|
||||||
|
# Use DNS mode:
|
||||||
|
Support the latest dns-01 challenge.
|
||||||
|
|
||||||
|
```
|
||||||
|
le issue dns aa.com www.aa.com,user.aa.com
|
||||||
|
```
|
||||||
|
|
||||||
|
You will get the output like bellow:
|
||||||
|
```
|
||||||
|
Add the following txt record:
|
||||||
|
Domain:_acme-challenge.aa.com
|
||||||
|
Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c
|
||||||
|
|
||||||
|
Add the following txt record:
|
||||||
|
Domain:_acme-challenge.www.aa.com
|
||||||
|
Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
```
|
||||||
|
|
||||||
|
Please add those txt records to the domains. Waiting for the dns to take effect.
|
||||||
|
|
||||||
|
Then just retry with 'renew' command:
|
||||||
|
|
||||||
|
```
|
||||||
|
le renew aa.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, it's finished.
|
||||||
|
|
||||||
|
|
||||||
|
#Automatic dns api integeration
|
||||||
|
|
||||||
|
If your dns provider supports api access, we can use api to automatically issue certs.
|
||||||
|
You don't have do anything manually.
|
||||||
|
|
||||||
|
###Currently we support:
|
||||||
|
|
||||||
|
1. Cloudflare.com api
|
||||||
|
2. Dnspod.cn api
|
||||||
|
3. Cloudxns.com api
|
||||||
|
|
||||||
|
More apis are comming soon....
|
||||||
|
|
||||||
|
If your dns provider is not in the supported list above, you can write your own script api easily.
|
||||||
|
|
||||||
|
For more details: [How to use dns api](dnsapi)
|
||||||
|
|
||||||
|
|
||||||
|
# Issue ECC certificate:
|
||||||
|
LetsEncrypt now can issue ECDSA certificate.
|
||||||
|
And we also support it.
|
||||||
|
|
||||||
|
Just set the `length` parameter with a prefix `ec-`.
|
||||||
|
For example:
|
||||||
|
```
|
||||||
|
le issue /home/wwwroot/aa.com aa.com www.aa.com ec-256
|
||||||
|
```
|
||||||
|
Please look at the last parameter above.
|
||||||
|
|
||||||
|
Valid values are:
|
||||||
|
|
||||||
|
1. ec-256 (prime256v1, "ECDSA P-256")
|
||||||
|
2. ec-384 (secp384r1, "ECDSA P-384")
|
||||||
|
3. ec-521 (secp521r1, "ECDSA P-521", which is not supported by letsencrypt yet.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#Under the Hood
|
#Under the Hood
|
||||||
|
|
||||||
Use bash to say ACME language directly to Let's encrypt.
|
Speak ACME language with bash directly to Let's encrypt.
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
|
|
||||||
|
#Acknowledgment
|
||||||
|
1. Acme-tiny: https://github.com/diafygi/acme-tiny
|
||||||
|
2. ACME protocol: https://github.com/ietf-wg-acme/acme
|
||||||
|
3. letsencrypt: https://github.com/letsencrypt/letsencrypt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#License & Other
|
#License & Other
|
||||||
|
|
||||||
License is GPLv3
|
License is GPLv3
|
||||||
|
|
||||||
Please Star and Fork me.
|
Please Star and Fork me.
|
||||||
|
|
||||||
Issues and pullrequests are welcomed.
|
Issues and pull requests are welcomed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
86
dnsapi/README.md
Normal file
86
dnsapi/README.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# How to use dns api
|
||||||
|
|
||||||
|
## Use CloudFlare domain api to automatically issue cert
|
||||||
|
|
||||||
|
For now, we support clourflare integeration.
|
||||||
|
|
||||||
|
First you need to login to your clourflare account to get your api key.
|
||||||
|
|
||||||
|
```
|
||||||
|
export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
|
||||||
|
export CF_Email="xxxx@sss.com"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, let's issue cert now:
|
||||||
|
```
|
||||||
|
le.sh issue dns-cf aa.com www.aa.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The `CF_Key` and `CF_Email` will be saved in `~/.le/account.conf`, when next time you use cloudflare api, it will reuse this key.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Use Dnspod.cn domain api to automatically issue cert
|
||||||
|
|
||||||
|
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_Key="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, let's issue cert now:
|
||||||
|
```
|
||||||
|
le.sh issue dns-dp aa.com www.aa.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The `DP_Id` and `DP_Key` will be saved in `~/.le/account.conf`, when next time you use dnspod.cn api, it will reuse this key.
|
||||||
|
|
||||||
|
|
||||||
|
## Use Cloudxns.com domain api to automatically issue cert
|
||||||
|
|
||||||
|
For now, we support Cloudxns.com integeration.
|
||||||
|
|
||||||
|
First you need to login to your Cloudxns.com account to get your api key and key secret.
|
||||||
|
|
||||||
|
```
|
||||||
|
export CX_Key="1234"
|
||||||
|
|
||||||
|
export CX_Secret="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, let's issue cert now:
|
||||||
|
```
|
||||||
|
le.sh issue dns-cx aa.com www.aa.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The `CX_Key` and `CX_Secret` will be saved in `~/.le/account.conf`, when next time you use Cloudxns.com api, it will reuse this key.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 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 `~/.le/dns-myapi.sh`,
|
||||||
|
2. In the scrypt, you must have a function named `dns-myapi-add()`. Which will be called by le.sh to add dns records.
|
||||||
|
3. Then you can use your api to issue cert like:
|
||||||
|
|
||||||
|
```
|
||||||
|
le.sh issue dns-myapi aa.com www.aa.com
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details, please check our sample script: [dns-myapi.sh](dns-myapi.sh)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
171
dnsapi/dns-cf.sh
Executable file
171
dnsapi/dns-cf.sh
Executable file
@ -0,0 +1,171 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
#
|
||||||
|
#CF_Email="xxxx@sss.com"
|
||||||
|
|
||||||
|
|
||||||
|
CF_Api="https://api.cloudflare.com/client/v4/"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns-cf-add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ] ; then
|
||||||
|
_err "You don't specify cloudflare api key and email yet."
|
||||||
|
_err "Please create you key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf CF_Key "$CF_Key"
|
||||||
|
_saveaccountconf CF_Email "$CF_Email"
|
||||||
|
|
||||||
|
_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"
|
||||||
|
|
||||||
|
if [ "$?" != "0" ] || ! printf $response | grep \"success\":true > /dev/null ; then
|
||||||
|
_err "Error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=$(printf $response | grep -o \"count\":[^,]* | cut -d : -f 2)
|
||||||
|
|
||||||
|
if [ "$count" == "0" ] ; then
|
||||||
|
_info "Adding record"
|
||||||
|
if _cf_rest POST "/zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
|
||||||
|
if printf $response | grep $fulldomain > /dev/null ; then
|
||||||
|
_info "Added, sleeping 10 seconds"
|
||||||
|
sleep 10
|
||||||
|
#todo: check if the record takes effect
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "Add txt record error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_err "Add txt record error."
|
||||||
|
else
|
||||||
|
_info "Updating record"
|
||||||
|
record_id=$(printf $response | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||||
|
_debug "record_id" $record_id
|
||||||
|
|
||||||
|
_cf_rest PUT "/zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}"
|
||||||
|
if [ "$?" == "0" ]; then
|
||||||
|
_info "Updated, sleeping 10 seconds"
|
||||||
|
sleep 10
|
||||||
|
#todo: check if the record takes effect
|
||||||
|
return 0;
|
||||||
|
fi
|
||||||
|
_err "Update error"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#################### Private functions bellow ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while [ '1' ] ; do
|
||||||
|
h=$(printf $domain | cut -d . -f $i-100)
|
||||||
|
if [ -z "$h" ] ; then
|
||||||
|
#not valid
|
||||||
|
return 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _cf_rest GET "zones?name=$h" ; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf $response | grep \"name\":\"$h\" ; then
|
||||||
|
_domain_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | head -1 | cut -d : -f 2 | tr -d \")
|
||||||
|
if [ "$_domain_id" ] ; then
|
||||||
|
_sub_domain=$(printf $domain | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
let "i+=1"
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_cf_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
_debug $ep
|
||||||
|
if [ "$3" ] ; then
|
||||||
|
data="$3"
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json" --data $data)"
|
||||||
|
else
|
||||||
|
response="$(curl --silent -X $m "$CF_Api/$ep" -H "X-Auth-Email: $CF_Email" -H "X-Auth-Key: $CF_Key" -H "Content-Type: application/json")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ] ; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_debug() {
|
||||||
|
|
||||||
|
if [ -z "$DEBUG" ] ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo $1
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_info() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1"
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_err() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1" >&2
|
||||||
|
else
|
||||||
|
echo "$1"="$2" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
234
dnsapi/dns-cx.sh
Normal file
234
dnsapi/dns-cx.sh
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Cloudxns.com Domain api
|
||||||
|
#
|
||||||
|
#CX_Key="1234"
|
||||||
|
#
|
||||||
|
#CX_Secret="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
|
||||||
|
CX_Api="https://www.cloudxns.net/api2"
|
||||||
|
|
||||||
|
|
||||||
|
#REST_API
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns-cx-add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$CX_Key" ] || [ -z "$CX_Secret" ] ; then
|
||||||
|
_err "You don't specify cloudxns.com api key or secret yet."
|
||||||
|
_err "Please create you key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
REST_API=$CX_Api
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf CX_Key "$CX_Key"
|
||||||
|
_saveaccountconf CX_Secret "$CX_Secret"
|
||||||
|
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root $fulldomain ; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
existing_records $_domain $_sub_domain
|
||||||
|
_debug count "$count"
|
||||||
|
if [ "$?" != "0" ] ; then
|
||||||
|
_err "Error get existing records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$count" == "0" ] ; then
|
||||||
|
add_record $_domain $_sub_domain $txtvalue
|
||||||
|
else
|
||||||
|
update_record $_domain $_sub_domain $txtvalue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" == "0" ] ; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#usage: root sub
|
||||||
|
#return if the sub record already exists.
|
||||||
|
#echos the existing records count.
|
||||||
|
# '0' means doesn't exist
|
||||||
|
existing_records() {
|
||||||
|
_debug "Getting txt records"
|
||||||
|
root=$1
|
||||||
|
sub=$2
|
||||||
|
|
||||||
|
if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100" ; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
count=0
|
||||||
|
seg=$(printf "$response" | grep -o "{[^{]*host\":\"$_sub_domain[^}]*}")
|
||||||
|
_debug seg "$seg"
|
||||||
|
if [ -z "$seg" ] ; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf "$response" | grep '"type":"TXT"' > /dev/null ; then
|
||||||
|
count=1
|
||||||
|
record_id=$(printf "$seg" | grep -o \"record_id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||||
|
_debug record_id "$record_id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#add the txt record.
|
||||||
|
#usage: root sub txtvalue
|
||||||
|
add_record() {
|
||||||
|
root=$1
|
||||||
|
sub=$2
|
||||||
|
txtvalue=$3
|
||||||
|
fulldomain=$sub.$root
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
|
||||||
|
if ! _rest POST "record" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#update the txt record
|
||||||
|
#Usage: root sub txtvalue
|
||||||
|
update_record() {
|
||||||
|
root=$1
|
||||||
|
sub=$2
|
||||||
|
txtvalue=$3
|
||||||
|
fulldomain=$sub.$root
|
||||||
|
|
||||||
|
_info "Updating record"
|
||||||
|
|
||||||
|
if _rest PUT "record/$record_id" "{\"domain_id\": $_domain_id, \"host\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"type\":\"TXT\",\"ttl\":600, \"line_id\":1}" ; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#################### Private functions bellow ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
|
||||||
|
if ! _rest GET "domain" ; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [ '1' ] ; do
|
||||||
|
h=$(printf $domain | cut -d . -f $i-100)
|
||||||
|
_debug h "$h"
|
||||||
|
if [ -z "$h" ] ; then
|
||||||
|
#not valid
|
||||||
|
return 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf "$response" | grep "$h." ; then
|
||||||
|
seg=$(printf "$response" | grep -o "{[^{]*$h\.[^}]*\}" )
|
||||||
|
_debug seg "$seg"
|
||||||
|
_domain_id=$(printf "$seg" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
if [ "$_domain_id" ] ; then
|
||||||
|
_sub_domain=$(printf $domain | cut -d . -f 1-$p)
|
||||||
|
_debug _sub_domain $_sub_domain
|
||||||
|
_domain=$h
|
||||||
|
_debug _domain $_domain
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
let "i+=1"
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#Usage: method URI data
|
||||||
|
_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
_debug $ep
|
||||||
|
url="$REST_API/$ep"
|
||||||
|
_debug url "$url"
|
||||||
|
|
||||||
|
cdate=$(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
_debug cdate "$cdate"
|
||||||
|
|
||||||
|
data="$3"
|
||||||
|
_debug data "$data"
|
||||||
|
|
||||||
|
sec="$CX_Key$url$data$cdate$CX_Secret"
|
||||||
|
_debug sec "$sec"
|
||||||
|
hmac=$(printf "$sec"| openssl md5 |cut -d " " -f 2)
|
||||||
|
_debug hmac "$hmac"
|
||||||
|
|
||||||
|
if [ "$3" ] ; then
|
||||||
|
response="$(curl --silent -X $m "$url" -H "API-KEY: $CX_Key" -H "API-REQUEST-DATE: $cdate" -H "API-HMAC: $hmac" -H 'Content-Type: application/json' -d "$data")"
|
||||||
|
else
|
||||||
|
response="$(curl --silent -X $m "$url" -H "API-KEY: $CX_Key" -H "API-REQUEST-DATE: $cdate" -H "API-HMAC: $hmac" -H 'Content-Type: application/json')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ] ; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
if ! printf "$response" | grep '"message":"success"' > /dev/null ; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_debug() {
|
||||||
|
|
||||||
|
if [ -z "$DEBUG" ] ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo $1
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_info() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1"
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_err() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1" >&2
|
||||||
|
else
|
||||||
|
echo "$1"="$2" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
229
dnsapi/dns-dp.sh
Normal file
229
dnsapi/dns-dp.sh
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Dnspod.cn Domain api
|
||||||
|
#
|
||||||
|
#DP_Id="1234"
|
||||||
|
#
|
||||||
|
#DP_Key="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
|
||||||
|
DP_Api="https://dnsapi.cn"
|
||||||
|
|
||||||
|
|
||||||
|
#REST_API
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns-dp-add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if [ -z "$DP_Id" ] || [ -z "$DP_Key" ] ; then
|
||||||
|
_err "You don't specify dnspod api key and key id yet."
|
||||||
|
_err "Please create you key and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
REST_API=$DP_Api
|
||||||
|
|
||||||
|
#save the api key and email to the account conf file.
|
||||||
|
_saveaccountconf DP_Id "$DP_Id"
|
||||||
|
_saveaccountconf DP_Key "$DP_Key"
|
||||||
|
|
||||||
|
|
||||||
|
_debug "First detect the root zone"
|
||||||
|
if ! _get_root $fulldomain ; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
existing_records $_domain $_sub_domain
|
||||||
|
_debug count "$count"
|
||||||
|
if [ "$?" != "0" ] ; then
|
||||||
|
_err "Error get existing records."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$count" == "0" ] ; then
|
||||||
|
add_record $_domain $_sub_domain $txtvalue
|
||||||
|
else
|
||||||
|
update_record $_domain $_sub_domain $txtvalue
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#usage: root sub
|
||||||
|
#return if the sub record already exists.
|
||||||
|
#echos the existing records count.
|
||||||
|
# '0' means doesn't exist
|
||||||
|
existing_records() {
|
||||||
|
_debug "Getting txt records"
|
||||||
|
root=$1
|
||||||
|
sub=$2
|
||||||
|
|
||||||
|
if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf "$response" | grep 'No records' ; then
|
||||||
|
count=0;
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf "$response" | grep "Action completed successful" >/dev/null ; then
|
||||||
|
count=$(printf "$response" | grep '<type>TXT</type>' | wc -l)
|
||||||
|
|
||||||
|
record_id=$(printf "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1)
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_err "get existing records error."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
count=0
|
||||||
|
}
|
||||||
|
|
||||||
|
#add the txt record.
|
||||||
|
#usage: root sub txtvalue
|
||||||
|
add_record() {
|
||||||
|
root=$1
|
||||||
|
sub=$2
|
||||||
|
txtvalue=$3
|
||||||
|
fulldomain=$sub.$root
|
||||||
|
|
||||||
|
_info "Adding record"
|
||||||
|
|
||||||
|
if ! _rest POST "Record.Create" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf "$response" | grep "Action completed successful" ; then
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
return 1 #error
|
||||||
|
}
|
||||||
|
|
||||||
|
#update the txt record
|
||||||
|
#Usage: root sub txtvalue
|
||||||
|
update_record() {
|
||||||
|
root=$1
|
||||||
|
sub=$2
|
||||||
|
txtvalue=$3
|
||||||
|
fulldomain=$sub.$root
|
||||||
|
|
||||||
|
_info "Updating record"
|
||||||
|
|
||||||
|
if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf "$response" | grep "Action completed successful" ; then
|
||||||
|
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1 #error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#################### Private functions bellow ##################################
|
||||||
|
#_acme-challenge.www.domain.com
|
||||||
|
#returns
|
||||||
|
# _sub_domain=_acme-challenge.www
|
||||||
|
# _domain=domain.com
|
||||||
|
# _domain_id=sdjkglgdfewsdfg
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=2
|
||||||
|
p=1
|
||||||
|
while [ '1' ] ; do
|
||||||
|
h=$(printf $domain | cut -d . -f $i-100)
|
||||||
|
if [ -z "$h" ] ; then
|
||||||
|
#not valid
|
||||||
|
return 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _rest POST "Domain.Info" "login_token=$DP_Id,$DP_Key&format=json&domain=$h"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf "$response" | grep "Action completed successful" ; then
|
||||||
|
_domain_id=$(printf "$response" | grep -o \"id\":\"[^\"]*\" | cut -d : -f 2 | tr -d \")
|
||||||
|
_debug _domain_id "$_domain_id"
|
||||||
|
if [ "$_domain_id" ] ; then
|
||||||
|
_sub_domain=$(printf $domain | cut -d . -f 1-$p)
|
||||||
|
_debug _sub_domain $_sub_domain
|
||||||
|
_domain=$h
|
||||||
|
_debug _domain $_domain
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
p=$i
|
||||||
|
let "i+=1"
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#Usage: method URI data
|
||||||
|
_rest() {
|
||||||
|
m=$1
|
||||||
|
ep="$2"
|
||||||
|
_debug $ep
|
||||||
|
url="$REST_API/$ep"
|
||||||
|
|
||||||
|
_debug url "$url"
|
||||||
|
|
||||||
|
if [ "$3" ] ; then
|
||||||
|
data="$3"
|
||||||
|
_debug data "$data"
|
||||||
|
response="$(curl --silent -X $m "$url" -d $data)"
|
||||||
|
else
|
||||||
|
response="$(curl --silent -X $m "$url" )"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$?" != "0" ] ; then
|
||||||
|
_err "error $ep"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_debug response "$response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_debug() {
|
||||||
|
|
||||||
|
if [ -z "$DEBUG" ] ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo $1
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_info() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1"
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_err() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1" >&2
|
||||||
|
else
|
||||||
|
echo "$1"="$2" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
61
dnsapi/dns-myapi.sh
Normal file
61
dnsapi/dns-myapi.sh
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#Here is a sample custom api script.
|
||||||
|
#This file name is "dns-myapi.sh"
|
||||||
|
#So, here must be a method dns-myapi-add()
|
||||||
|
#Which will be called by le.sh to add the txt record to your api system.
|
||||||
|
#returns 0 meanst success, otherwise error.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns-myapi-add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
_err "Not implemented!"
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#################### Private functions bellow ##################################
|
||||||
|
|
||||||
|
|
||||||
|
_debug() {
|
||||||
|
|
||||||
|
if [ -z "$DEBUG" ] ; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo $1
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_info() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1"
|
||||||
|
else
|
||||||
|
echo "$1"="$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_err() {
|
||||||
|
if [ -z "$2" ] ; then
|
||||||
|
echo "$1" >&2
|
||||||
|
else
|
||||||
|
echo "$1"="$2" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user