mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2025-06-16 15:02:54 +00:00
Merge pull request #1 from Neilpang/master
Merge Neilpang/le into raunsbaekdk/le
This commit is contained in:
commit
0ac62ff701
49
README.md
49
README.md
@ -11,6 +11,7 @@ Do NOT require to be `root/sudoer`.
|
|||||||
#Tested OS
|
#Tested OS
|
||||||
1. Ubuntu/Debian.
|
1. Ubuntu/Debian.
|
||||||
2. CentOS
|
2. CentOS
|
||||||
|
3. Windows (cygwin with curl, openssl and crontab included)
|
||||||
|
|
||||||
|
|
||||||
#Supported Mode
|
#Supported Mode
|
||||||
@ -27,14 +28,15 @@ Do NOT require to be `root/sudoer`.
|
|||||||
```
|
```
|
||||||
./le.sh install
|
./le.sh install
|
||||||
```
|
```
|
||||||
You don't have to be root then, altough it is recommended.
|
You don't have to be root then, although it is recommended.
|
||||||
|
|
||||||
Which does 3 jobs:
|
Which does 3 jobs:
|
||||||
* create and copy `le.sh` to your home dir: `~/.le`
|
* create and copy `le.sh` to your home dir: `~/.le`
|
||||||
All the certs will be placed in this folder.
|
All the certs will be placed in this folder.
|
||||||
* create symbol link: `/usr/local/bin/le -> ~/.le/le.sh` . (You must be root to do so.)
|
* create alias : `le.sh=~/.le/le.sh` and `le=~/.le/le.sh`.
|
||||||
* create everyday cron job to check and renew the cert if needed.
|
* 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.
|
Ok, you are ready to issue cert now.
|
||||||
Show help message:
|
Show help message:
|
||||||
@ -43,7 +45,7 @@ root@v1:~# le.sh
|
|||||||
https://github.com/Neilpang/le
|
https://github.com/Neilpang/le
|
||||||
v1.1.1
|
v1.1.1
|
||||||
Usage: le.sh [command] ...[args]....
|
Usage: le.sh [command] ...[args]....
|
||||||
Avalible commands:
|
Available commands:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
Install le.sh to your system.
|
Install le.sh to your system.
|
||||||
@ -104,7 +106,7 @@ The issued cert will be renewed every 80 days automatically.
|
|||||||
|
|
||||||
# Install issued cert to apache/nginx etc.
|
# Install issued cert to apache/nginx etc.
|
||||||
```
|
```
|
||||||
le installcert aa.com /path/to/certfile/in/apache/nginx /path/to/keyfile/in/apache/nginx /path/to/ca/certfile/apahce/nginx "service apache2|nginx 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"
|
||||||
```
|
```
|
||||||
|
|
||||||
Install the issued cert/key to the production apache or nginx path.
|
Install the issued cert/key to the production apache or nginx path.
|
||||||
@ -139,9 +141,6 @@ Support the latest dns-01 challenge.
|
|||||||
le issue dns aa.com www.aa.com,user.aa.com
|
le issue dns aa.com www.aa.com,user.aa.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Use domain api to automatically add dns record is not finished yet.
|
|
||||||
So, you must manually add the txt record to finish verifying.
|
|
||||||
|
|
||||||
You will get the output like bellow:
|
You will get the output like bellow:
|
||||||
```
|
```
|
||||||
Add the following txt record:
|
Add the following txt record:
|
||||||
@ -164,6 +163,42 @@ le renew aa.com
|
|||||||
Ok, it's finished.
|
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 key length to the `length` paramiter 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", not supported by letsencrypt yet.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#Under the Hood
|
#Under the Hood
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
168
dnsapi/dns-cf.sh
Executable file
168
dnsapi/dns-cf.sh
Executable file
@ -0,0 +1,168 @@
|
|||||||
|
#!/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 "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\":\"[^\"]*\" | 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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
354
le.sh
354
le.sh
@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
VER=1.1.1
|
VER=1.1.6
|
||||||
PROJECT="https://github.com/Neilpang/le"
|
PROJECT="https://github.com/Neilpang/le"
|
||||||
|
|
||||||
DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
|
DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
|
||||||
@ -41,6 +41,7 @@ _err() {
|
|||||||
else
|
else
|
||||||
echo "$1"="$2" >&2
|
echo "$1"="$2" >&2
|
||||||
fi
|
fi
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_h2b() {
|
_h2b() {
|
||||||
@ -64,13 +65,19 @@ _base64() {
|
|||||||
|
|
||||||
#domain [2048]
|
#domain [2048]
|
||||||
createAccountKey() {
|
createAccountKey() {
|
||||||
|
_info "Creating account key"
|
||||||
if [ -z "$1" ] ; then
|
if [ -z "$1" ] ; then
|
||||||
echo Usage: $0 account-domain [2048]
|
echo Usage: createAccountKey account-domain [2048]
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
account=$1
|
account=$1
|
||||||
length=$2
|
length=$2
|
||||||
|
|
||||||
|
if [[ "$length" == "ec-"* ]] ; then
|
||||||
|
length=2048
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$2" ] ; then
|
if [ -z "$2" ] ; then
|
||||||
_info "Use default length 2048"
|
_info "Use default length 2048"
|
||||||
length=2048
|
length=2048
|
||||||
@ -89,20 +96,53 @@ createAccountKey() {
|
|||||||
|
|
||||||
#domain length
|
#domain length
|
||||||
createDomainKey() {
|
createDomainKey() {
|
||||||
|
_info "Creating domain key"
|
||||||
if [ -z "$1" ] ; then
|
if [ -z "$1" ] ; then
|
||||||
echo Usage: $0 domain [2048]
|
echo Usage: createDomainKey domain [2048]
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
domain=$1
|
domain=$1
|
||||||
length=$2
|
length=$2
|
||||||
if [ -z "$2" ] ; then
|
isec=""
|
||||||
_info "Use default length 2048"
|
if [[ "$length" == "ec-"* ]] ; then
|
||||||
|
isec="1"
|
||||||
|
length=$(printf $length | cut -d '-' -f 2-100)
|
||||||
|
eccname="$length"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$length" ] ; then
|
||||||
|
if [ "$isec" ] ; then
|
||||||
|
length=256
|
||||||
|
else
|
||||||
length=2048
|
length=2048
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
_info "Use length $length"
|
||||||
|
|
||||||
|
if [ "$isec" ] ; then
|
||||||
|
if [ "$length" == "256" ] ; then
|
||||||
|
eccname="prime256v1"
|
||||||
|
fi
|
||||||
|
if [ "$length" == "384" ] ; then
|
||||||
|
eccname="secp384r1"
|
||||||
|
fi
|
||||||
|
if [ "$length" == "521" ] ; then
|
||||||
|
eccname="secp521r1"
|
||||||
|
fi
|
||||||
|
_info "Using ec name: $eccname"
|
||||||
|
fi
|
||||||
|
|
||||||
_initpath $domain
|
_initpath $domain
|
||||||
|
|
||||||
if [ -f "$CERT_KEY_PATH" ] && ! [ "$FORCE" ] ; then
|
if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
|
||||||
|
#generate account key
|
||||||
|
if [ "$isec" ] ; then
|
||||||
|
openssl ecparam -name $eccname -genkey 2>/dev/null > "$CERT_KEY_PATH"
|
||||||
|
else
|
||||||
|
openssl genrsa $length 2>/dev/null > "$CERT_KEY_PATH"
|
||||||
|
fi
|
||||||
|
else
|
||||||
if [ "$IS_RENEW" ] ; then
|
if [ "$IS_RENEW" ] ; then
|
||||||
_info "Domain key exists, skip"
|
_info "Domain key exists, skip"
|
||||||
return 0
|
return 0
|
||||||
@ -111,15 +151,13 @@ createDomainKey() {
|
|||||||
_err "Set FORCE=1, and try again."
|
_err "Set FORCE=1, and try again."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
#generate account key
|
|
||||||
openssl genrsa $length > "$CERT_KEY_PATH"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# domain domainlist
|
# domain domainlist
|
||||||
createCSR() {
|
createCSR() {
|
||||||
|
_info "Creating csr"
|
||||||
if [ -z "$1" ] ; then
|
if [ -z "$1" ] ; then
|
||||||
echo Usage: $0 domain [domainlist]
|
echo Usage: $0 domain [domainlist]
|
||||||
return
|
return
|
||||||
@ -160,8 +198,8 @@ _send_signed_request() {
|
|||||||
_debug url $url
|
_debug url $url
|
||||||
_debug payload "$payload"
|
_debug payload "$payload"
|
||||||
|
|
||||||
CURL_HEADER="$WORKING_DIR/curl.header"
|
CURL_HEADER="$LE_WORKING_DIR/curl.header"
|
||||||
dp="$WORKING_DIR/curl.dump"
|
dp="$LE_WORKING_DIR/curl.dump"
|
||||||
CURL="curl --silent --dump-header $CURL_HEADER "
|
CURL="curl --silent --dump-header $CURL_HEADER "
|
||||||
if [ "$DEBUG" ] ; then
|
if [ "$DEBUG" ] ; then
|
||||||
CURL="$CURL --trace-ascii $dp "
|
CURL="$CURL --trace-ascii $dp "
|
||||||
@ -239,6 +277,29 @@ _setopt() {
|
|||||||
_debug "$(grep -H -n "^$__opt$__sep" $__conf)"
|
_debug "$(grep -H -n "^$__opt$__sep" $__conf)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#_savedomainconf key value
|
||||||
|
#save to domain.conf
|
||||||
|
_savedomainconf() {
|
||||||
|
key="$1"
|
||||||
|
value="$2"
|
||||||
|
if [ "$DOMAIN_CONF" ] ; then
|
||||||
|
_setopt $DOMAIN_CONF "$key" "=" "$value"
|
||||||
|
else
|
||||||
|
_err "DOMAIN_CONF is empty, can not save $key=$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#_saveaccountconf key value
|
||||||
|
_saveaccountconf() {
|
||||||
|
key="$1"
|
||||||
|
value="$2"
|
||||||
|
if [ "$ACCOUNT_CONF_PATH" ] ; then
|
||||||
|
_setopt $ACCOUNT_CONF_PATH "$key" "=" "$value"
|
||||||
|
else
|
||||||
|
_err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_startserver() {
|
_startserver() {
|
||||||
content="$1"
|
content="$1"
|
||||||
_NC="nc -q 1"
|
_NC="nc -q 1"
|
||||||
@ -247,9 +308,9 @@ _startserver() {
|
|||||||
fi
|
fi
|
||||||
# while true ; do
|
# while true ; do
|
||||||
if [ "$DEBUG" ] ; then
|
if [ "$DEBUG" ] ; then
|
||||||
echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p 80 -vv
|
echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort -vv
|
||||||
else
|
else
|
||||||
echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p 80 > /dev/null
|
echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort > /dev/null
|
||||||
fi
|
fi
|
||||||
# done
|
# done
|
||||||
}
|
}
|
||||||
@ -268,6 +329,18 @@ _initpath() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -z "$LE_WORKING_DIR" ]; then
|
||||||
|
LE_WORKING_DIR=$HOME/.le
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ACCOUNT_CONF_PATH" ] ; then
|
||||||
|
ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$ACCOUNT_CONF_PATH" ] ; then
|
||||||
|
source "$ACCOUNT_CONF_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$API" ] ; then
|
if [ -z "$API" ] ; then
|
||||||
if [ -z "$STAGE" ] ; then
|
if [ -z "$STAGE" ] ; then
|
||||||
API="$DEFAULT_CA"
|
API="$DEFAULT_CA"
|
||||||
@ -277,47 +350,44 @@ _initpath() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$WORKING_DIR" ]; then
|
|
||||||
WORKING_DIR=$HOME/.le
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$ACME_DIR" ] ; then
|
if [ -z "$ACME_DIR" ] ; then
|
||||||
ACME_DIR="/home/.acme"
|
ACME_DIR="/home/.acme"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
|
if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
|
||||||
APACHE_CONF_BACKUP_DIR="$WORKING_DIR/"
|
APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
domain="$1"
|
domain="$1"
|
||||||
mkdir -p "$WORKING_DIR"
|
mkdir -p "$LE_WORKING_DIR"
|
||||||
|
|
||||||
if [ -z "$ACCOUNT_KEY_PATH" ] ; then
|
if [ -z "$ACCOUNT_KEY_PATH" ] ; then
|
||||||
ACCOUNT_KEY_PATH="$WORKING_DIR/account.acc"
|
ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$domain" ] ; then
|
if [ -z "$domain" ] ; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$WORKING_DIR/$domain"
|
domainhome="$LE_WORKING_DIR/$domain"
|
||||||
|
mkdir -p "$domainhome"
|
||||||
|
|
||||||
if [ -z "$DOMAIN_CONF" ] ; then
|
if [ -z "$DOMAIN_CONF" ] ; then
|
||||||
DOMAIN_CONF="$WORKING_DIR/$domain/$Le_Domain.conf"
|
DOMAIN_CONF="$domainhome/$Le_Domain.conf"
|
||||||
fi
|
|
||||||
if [ -z "$CSR_PATH" ] ; then
|
|
||||||
CSR_PATH="$WORKING_DIR/$domain/$domain.csr"
|
|
||||||
fi
|
|
||||||
if [ -z "$CERT_KEY_PATH" ] ; then
|
|
||||||
CERT_KEY_PATH="$WORKING_DIR/$domain/$domain.key"
|
|
||||||
fi
|
|
||||||
if [ -z "$CERT_PATH" ] ; then
|
|
||||||
CERT_PATH="$WORKING_DIR/$domain/$domain.cer"
|
|
||||||
fi
|
|
||||||
if [ -z "$CA_CERT_PATH" ] ; then
|
|
||||||
CA_CERT_PATH="$WORKING_DIR/$domain/ca.cer"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CSR_PATH" ] ; then
|
||||||
|
CSR_PATH="$domainhome/$domain.csr"
|
||||||
|
fi
|
||||||
|
if [ -z "$CERT_KEY_PATH" ] ; then
|
||||||
|
CERT_KEY_PATH="$domainhome/$domain.key"
|
||||||
|
fi
|
||||||
|
if [ -z "$CERT_PATH" ] ; then
|
||||||
|
CERT_PATH="$domainhome/$domain.cer"
|
||||||
|
fi
|
||||||
|
if [ -z "$CA_CERT_PATH" ] ; then
|
||||||
|
CA_CERT_PATH="$domainhome/ca.cer"
|
||||||
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,7 +492,7 @@ _clearupwebbroot() {
|
|||||||
_debug "remove $__webroot/.well-known/acme-challenge/$3"
|
_debug "remove $__webroot/.well-known/acme-challenge/$3"
|
||||||
rm -rf "$__webroot/.well-known/acme-challenge/$3"
|
rm -rf "$__webroot/.well-known/acme-challenge/$3"
|
||||||
else
|
else
|
||||||
_info "skip for removelevel:$2"
|
_info "Skip for removelevel:$2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@ -489,10 +559,15 @@ issue() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
netprc="$(ss -ntpl | grep ':80 ')"
|
if [ -z "$Le_HTTPPort" ] ; then
|
||||||
|
Le_HTTPPort=80
|
||||||
|
fi
|
||||||
|
_setopt "$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort"
|
||||||
|
|
||||||
|
netprc="$(ss -ntpl | grep :$Le_HTTPPort" ")"
|
||||||
if [ "$netprc" ] ; then
|
if [ "$netprc" ] ; then
|
||||||
_err "$netprc"
|
_err "$netprc"
|
||||||
_err "tcp port 80 is already used by $(echo "$netprc" | cut -d : -f 4)"
|
_err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
|
||||||
_err "Please stop it first"
|
_err "Please stop it first"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -539,7 +614,7 @@ issue() {
|
|||||||
_debug HEADER "$HEADER"
|
_debug HEADER "$HEADER"
|
||||||
|
|
||||||
accountkey_json=$(echo -n "$jwk" | sed "s/ //g")
|
accountkey_json=$(echo -n "$jwk" | sed "s/ //g")
|
||||||
thumbprint=$(echo -n "$accountkey_json" | openssl sha -sha256 -binary | _base64 | _b64)
|
thumbprint=$(echo -n "$accountkey_json" | openssl dgst -sha256 -binary | _base64 | _b64)
|
||||||
|
|
||||||
|
|
||||||
_info "Registering account"
|
_info "Registering account"
|
||||||
@ -551,7 +626,7 @@ issue() {
|
|||||||
|
|
||||||
if [ "$code" == "" ] || [ "$code" == '201' ] ; then
|
if [ "$code" == "" ] || [ "$code" == '201' ] ; then
|
||||||
_info "Registered"
|
_info "Registered"
|
||||||
echo $response > $WORKING_DIR/account.json
|
echo $response > $LE_WORKING_DIR/account.json
|
||||||
elif [ "$code" == '409' ] ; then
|
elif [ "$code" == '409' ] ; then
|
||||||
_info "Already registered"
|
_info "Already registered"
|
||||||
else
|
else
|
||||||
@ -616,12 +691,49 @@ issue() {
|
|||||||
_debug txt "$txt"
|
_debug txt "$txt"
|
||||||
#dns
|
#dns
|
||||||
#1. check use api
|
#1. check use api
|
||||||
|
d_api=""
|
||||||
|
if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
|
||||||
|
d_api="$LE_WORKING_DIR/$d/$Le_Webroot"
|
||||||
|
elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
|
||||||
|
d_api="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
|
||||||
|
elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
|
||||||
|
d_api="$LE_WORKING_DIR/$Le_Webroot"
|
||||||
|
elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
|
||||||
|
d_api="$LE_WORKING_DIR/$Le_Webroot.sh"
|
||||||
|
elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
|
||||||
|
d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
|
||||||
|
elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
|
||||||
|
d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
|
||||||
|
fi
|
||||||
|
_debug d_api "$d_api"
|
||||||
|
|
||||||
|
if [ "$d_api" ]; then
|
||||||
|
_info "Found domain api file: $d_api"
|
||||||
|
else
|
||||||
_err "Add the following TXT record:"
|
_err "Add the following TXT record:"
|
||||||
_err "Domain: $txtdomain"
|
_err "Domain: $txtdomain"
|
||||||
_err "TXT value: $txt"
|
_err "TXT value: $txt"
|
||||||
_err "Please be aware that you prepend _acme-challenge. before your domain"
|
_err "Please be aware that you prepend _acme-challenge. before your domain"
|
||||||
_err "so the resulting subdomain will be: $txtdomain"
|
_err "so the resulting subdomain will be: $txtdomain"
|
||||||
#dnsadded='1'
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! source $d_api ; then
|
||||||
|
_err "Load file $d_api error. Please check your api file and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
addcommand="$Le_Webroot-add"
|
||||||
|
if ! command -v $addcommand ; then
|
||||||
|
_err "It seems that your api file is not correct, it must have a function named: $Le_Webroot"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! $addcommand $txtdomain $txt ; then
|
||||||
|
_err "Error add txt for domain:$txtdomain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
dnsadded='1'
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -634,6 +746,10 @@ issue() {
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$dnsadded" == '1' ] ; then
|
||||||
|
_info "Sleep 60 seconds for the txt records to take effect"
|
||||||
|
sleep 60
|
||||||
|
fi
|
||||||
|
|
||||||
_debug "ok, let's start to verify"
|
_debug "ok, let's start to verify"
|
||||||
ventries=$(echo "$vlist" | sed "s/,/ /g")
|
ventries=$(echo "$vlist" | sed "s/,/ /g")
|
||||||
@ -754,7 +870,7 @@ issue() {
|
|||||||
|
|
||||||
|
|
||||||
if [ -z "$Le_LinkCert" ] ; then
|
if [ -z "$Le_LinkCert" ] ; then
|
||||||
response="$(echo $response | openssl base64 -d)"
|
response="$(echo $response | openssl base64 -d -A)"
|
||||||
_err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
|
_err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -803,23 +919,30 @@ renew() {
|
|||||||
|
|
||||||
_initpath $Le_Domain
|
_initpath $Le_Domain
|
||||||
|
|
||||||
if [ -f "$DOMAIN_CONF" ] ; then
|
if [ ! -f "$DOMAIN_CONF" ] ; then
|
||||||
|
_info "$Le_Domain is not a issued domain, skip."
|
||||||
|
return 0;
|
||||||
|
fi
|
||||||
|
|
||||||
source "$DOMAIN_CONF"
|
source "$DOMAIN_CONF"
|
||||||
if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
|
if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
|
||||||
_info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
|
_info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
|
||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
IS_RENEW="1"
|
IS_RENEW="1"
|
||||||
issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
|
issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
|
||||||
|
local res=$?
|
||||||
IS_RENEW=""
|
IS_RENEW=""
|
||||||
|
|
||||||
|
return $res
|
||||||
}
|
}
|
||||||
|
|
||||||
renewAll() {
|
renewAll() {
|
||||||
_initpath
|
_initpath
|
||||||
_info "renewAll"
|
_info "renewAll"
|
||||||
|
|
||||||
for d in $(ls -F $WORKING_DIR | grep '/$') ; do
|
for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do
|
||||||
d=$(echo $d | cut -d '/' -f 1)
|
d=$(echo $d | cut -d '/' -f 1)
|
||||||
_info "renew $d"
|
_info "renew $d"
|
||||||
|
|
||||||
@ -914,13 +1037,13 @@ installcronjob() {
|
|||||||
_initpath
|
_initpath
|
||||||
_info "Installing cron job"
|
_info "Installing cron job"
|
||||||
if ! crontab -l | grep 'le.sh cron' ; then
|
if ! crontab -l | grep 'le.sh cron' ; then
|
||||||
if [ -f "$WORKING_DIR/le.sh" ] ; then
|
if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
|
||||||
lesh="\"$WORKING_DIR\"/le.sh"
|
lesh="\"$LE_WORKING_DIR\"/le.sh"
|
||||||
else
|
else
|
||||||
_err "Can not install cronjob, le.sh not found."
|
_err "Can not install cronjob, le.sh not found."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
crontab -l | { cat; echo "0 0 * * * $SUDO WORKING_DIR=\"$WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
|
crontab -l | { cat; echo "0 0 * * * $SUDO LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -930,13 +1053,89 @@ uninstallcronjob() {
|
|||||||
cr="$(crontab -l | grep 'le.sh cron')"
|
cr="$(crontab -l | grep 'le.sh cron')"
|
||||||
if [ "$cr" ] ; then
|
if [ "$cr" ] ; then
|
||||||
crontab -l | sed "/le.sh cron/d" | crontab -
|
crontab -l | sed "/le.sh cron/d" | crontab -
|
||||||
WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 7 | cut -d '=' -f 2 | tr -d '"')"
|
LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 7 | cut -d '=' -f 2 | tr -d '"')"
|
||||||
_info WORKING_DIR "$WORKING_DIR"
|
_info LE_WORKING_DIR "$LE_WORKING_DIR"
|
||||||
fi
|
fi
|
||||||
_initpath
|
_initpath
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Detect profile file if not specified as environment variable
|
||||||
|
_detect_profile() {
|
||||||
|
if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
|
||||||
|
echo "$PROFILE"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local DETECTED_PROFILE
|
||||||
|
DETECTED_PROFILE=''
|
||||||
|
local SHELLTYPE
|
||||||
|
SHELLTYPE="$(basename "/$SHELL")"
|
||||||
|
|
||||||
|
if [ "$SHELLTYPE" = "bash" ]; then
|
||||||
|
if [ -f "$HOME/.bashrc" ]; then
|
||||||
|
DETECTED_PROFILE="$HOME/.bashrc"
|
||||||
|
elif [ -f "$HOME/.bash_profile" ]; then
|
||||||
|
DETECTED_PROFILE="$HOME/.bash_profile"
|
||||||
|
fi
|
||||||
|
elif [ "$SHELLTYPE" = "zsh" ]; then
|
||||||
|
DETECTED_PROFILE="$HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$DETECTED_PROFILE" ]; then
|
||||||
|
if [ -f "$HOME/.profile" ]; then
|
||||||
|
DETECTED_PROFILE="$HOME/.profile"
|
||||||
|
elif [ -f "$HOME/.bashrc" ]; then
|
||||||
|
DETECTED_PROFILE="$HOME/.bashrc"
|
||||||
|
elif [ -f "$HOME/.bash_profile" ]; then
|
||||||
|
DETECTED_PROFILE="$HOME/.bash_profile"
|
||||||
|
elif [ -f "$HOME/.zshrc" ]; then
|
||||||
|
DETECTED_PROFILE="$HOME/.zshrc"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$DETECTED_PROFILE" ]; then
|
||||||
|
echo "$DETECTED_PROFILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_initconf() {
|
||||||
|
_initpath
|
||||||
|
if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
|
||||||
|
echo "#Account configurations:
|
||||||
|
#Here are the supported macros, uncomment them to make them take effect.
|
||||||
|
#ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
|
||||||
|
|
||||||
|
#STAGE=1 # Use the staging api
|
||||||
|
#FORCE=1 # Force to issue cert
|
||||||
|
#DEBUG=1 # Debug mode
|
||||||
|
|
||||||
|
#dns api
|
||||||
|
#######################
|
||||||
|
#Cloudflare:
|
||||||
|
#api key
|
||||||
|
#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
#account email
|
||||||
|
#CF_Email="xxxx@sss.com"
|
||||||
|
|
||||||
|
#######################
|
||||||
|
#Dnspod.cn:
|
||||||
|
#api key id
|
||||||
|
#DP_Id="1234"
|
||||||
|
#api key
|
||||||
|
#DP_Key="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
#######################
|
||||||
|
#Cloudxns.com:
|
||||||
|
#CX_Key="1234"
|
||||||
|
#
|
||||||
|
#CX_Secret="sADDsdasdgdsf"
|
||||||
|
|
||||||
|
" > $ACCOUNT_CONF_PATH
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
install() {
|
install() {
|
||||||
_initpath
|
_initpath
|
||||||
|
|
||||||
@ -969,29 +1168,40 @@ install() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_info "Installing to $WORKING_DIR"
|
_info "Installing to $LE_WORKING_DIR"
|
||||||
|
|
||||||
#try install to /bin if is root
|
_info "Installed to $LE_WORKING_DIR/le.sh"
|
||||||
if [ ! -f /usr/local/bin/le.sh ] ; then
|
cp le.sh $LE_WORKING_DIR/
|
||||||
#if root
|
chmod +x $LE_WORKING_DIR/le.sh
|
||||||
if $SUDO cp le.sh /usr/local/bin/le.sh > /dev/null 2>&1; then
|
|
||||||
$SUDO chmod 755 /usr/local/bin/le.sh
|
_profile="$(_detect_profile)"
|
||||||
$SUDO ln -s "/usr/local/bin/le.sh" /usr/local/bin/le
|
if [ "$_profile" ] ; then
|
||||||
rm -f $WORKING_DIR/le.sh
|
_debug "Found profile: $_profile"
|
||||||
$SUDO ln -s /usr/local/bin/le.sh $WORKING_DIR/le.sh
|
|
||||||
_info "Installed to /usr/local/bin/le"
|
echo "LE_WORKING_DIR=$LE_WORKING_DIR
|
||||||
|
alias le=\"$LE_WORKING_DIR/le.sh\"
|
||||||
|
alias le.sh=\"$LE_WORKING_DIR/le.sh\"
|
||||||
|
" > "$LE_WORKING_DIR/le.env"
|
||||||
|
|
||||||
|
_setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\""
|
||||||
|
_info "OK, Close and reopen your terminal to start using le"
|
||||||
else
|
else
|
||||||
#install to home, for non root user
|
_info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
|
||||||
cp le.sh $WORKING_DIR/
|
|
||||||
chmod +x $WORKING_DIR/le.sh
|
|
||||||
_info "Installed to $WORKING_DIR/le.sh"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mkdir -p $LE_WORKING_DIR/dnsapi
|
||||||
|
cp dnsapi/* $LE_WORKING_DIR/dnsapi/
|
||||||
|
|
||||||
|
#to keep compatible mv the .acc file to .key file
|
||||||
|
if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
|
||||||
|
mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
|
||||||
fi
|
fi
|
||||||
rm -f $WORKING_DIR/le
|
|
||||||
ln -s $WORKING_DIR/le.sh $WORKING_DIR/le
|
|
||||||
|
|
||||||
installcronjob
|
installcronjob
|
||||||
|
|
||||||
|
if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
|
||||||
|
_initconf
|
||||||
|
fi
|
||||||
_info OK
|
_info OK
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -999,15 +1209,13 @@ uninstall() {
|
|||||||
uninstallcronjob
|
uninstallcronjob
|
||||||
_initpath
|
_initpath
|
||||||
|
|
||||||
if [ -f "/usr/local/bin/le.sh" ] ; then
|
_profile="$(_detect_profile)"
|
||||||
_info "Removing /usr/local/bin/le.sh"
|
if [ "$_profile" ] ; then
|
||||||
if $SUDO rm -f /usr/local/bin/le.sh ; then
|
sed -i /le.env/d "$_profile"
|
||||||
$SUDO rm -f /usr/local/bin/le
|
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
rm -f $WORKING_DIR/le
|
rm -f $LE_WORKING_DIR/le.sh
|
||||||
rm -f $WORKING_DIR/le.sh
|
_info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
|
||||||
_info "The keys and certs are in $WORKING_DIR, you can remove them by yourself."
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user