Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
Acme.Sh
Commits
6de7ef7c
Commit
6de7ef7c
authored
Dec 26, 2015
by
neil
Browse files
first public version
parents
Changes
1
Hide whitespace changes
Inline
Side-by-side
le.sh
0 → 100644
View file @
6de7ef7c
#!/bin/bash
WORKING_DIR
=
~/.le
ACCOUNT_KEY_PATH
=
$WORKING_DIR
/account.acc
CERT_KEY_PATH
=
$WORKING_DIR
/domain.key
CSR_PATH
=
$WORKING_DIR
/domain.csr
CERT_PATH
=
$WORKING_DIR
/domain.cer
DOMAIN_CONF
=
$WORKING_DIR
/domain.conf
CURL_HEADER
=
""
HEADER
=
""
HEADERPLACE
=
""
ACCOUNT_EMAIL
=
""
DEFAULT_CA
=
"https://acme-v01.api.letsencrypt.org"
API
=
$DEFAULT_CA
DEBUG
=
_debug
()
{
if
!
[
"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
}
#domain [2048]
createAccountKey
()
{
if
[
-z
"
$1
"
]
;
then
echo
Usage:
$0
account-domain
[
2048]
return
fi
account
=
$1
length
=
$2
if
[
-z
"
$2
"
]
;
then
echo
Use default length 2048
length
=
2048
fi
mkdir
-p
$WORKING_DIR
ACCOUNT_KEY_PATH
=
$WORKING_DIR
/account.acc
if
[
-f
"
$ACCOUNT_KEY_PATH
"
]
;
then
echo
account key exists, skip
return
else
#generate account key
openssl genrsa
$length
>
$ACCOUNT_KEY_PATH
fi
}
#domain length
createDomainKey
()
{
if
[
-z
"
$1
"
]
;
then
echo
Usage:
$0
domain
[
2048]
return
fi
domain
=
$1
length
=
$2
if
[
-z
"
$2
"
]
;
then
echo
Use default length 2048
length
=
2048
fi
mkdir
-p
$WORKING_DIR
/
$domain
CERT_KEY_PATH
=
$WORKING_DIR
/
$domain
/
$domain
.key
if
[
-f
"
$CERT_KEY_PATH
"
]
;
then
echo
domain key exists, skip
else
#generate account key
openssl genrsa
$length
>
$CERT_KEY_PATH
fi
}
# domain domainlist
createCSR
()
{
if
[
-z
"
$1
"
]
;
then
echo
Usage:
$0
domain
[
domainlist]
return
fi
domain
=
$1
_initpath
$domain
domainlist
=
$2
if
[
-f
$CSR_PATH
]
;
then
echo
CSR exists, skip
return
fi
if
[
-z
"
$domainlist
"
]
;
then
#single domain
echo
single domain
openssl req
-new
-sha256
-key
$CERT_KEY_PATH
-subj
"/CN=
$domain
"
>
$CSR_PATH
else
alt
=
DNS:
$(
echo
$domainlist
|
sed
"s/,/,DNS:/g"
)
#multi
echo
multi domain
$alt
openssl req
-new
-sha256
-key
$CERT_KEY_PATH
-subj
"/CN=
$domain
"
-reqexts
SAN
-config
<
(
cat
/etc/ssl/openssl.cnf <
(
printf
"[SAN]
\n
subjectAltName=
$alt
"
))
-out
$CSR_PATH
fi
}
_b64
()
{
while
read
__line
;
do
__n
=
$__n$__line
done
;
__n
=
$(
echo
$__n
|
sed
"s|/|_|g"
)
__n
=
$(
echo
$__n
|
sed
"s| ||g"
)
__n
=
$(
echo
$__n
|
sed
"s|+|-|g"
)
__n
=
$(
echo
$__n
|
sed
"s|=||g"
)
echo
$__n
}
_send_signed_request
()
{
url
=
$1
payload
=
$2
needbas64
=
"
$3
"
_info url
$url
_info payload
"
$payload
"
CURL_HEADER
=
"
$WORKING_DIR
/curl.header"
dp
=
"
$WORKING_DIR
/curl.dump"
CURL
=
"curl --silent --dump-header
$CURL_HEADER
"
if
[
"DEBUG"
]
;
then
CURL
=
"
$CURL
--trace-ascii
$dp
"
fi
payload64
=
$(
echo
-n
$payload
|
base64
| _b64
)
_debug payload64
$payload64
nonceurl
=
"
$API
/directory"
nonce
=
$(
$CURL
-I
$nonceurl
|
grep
"^Replay-Nonce:"
|
sed
s/
\\
r//|sed s/
\\
n//|
cut
-d
' '
-f
2
)
_debug nonce
$nonce
protected
=
$(
echo
-n
"
$HEADERPLACE
"
|
sed
"s/NONCE/
$nonce
/"
)
_debug protected
"
$protected
"
protected64
=
$(
echo
-n
$protected
|
base64
| _b64
)
_debug protected64
"
$protected64
"
sig
=
$(
echo
-n
"
$protected64
.
$payload64
"
| openssl dgst
-sha256
-sign
$ACCOUNT_KEY_PATH
|
base64
| _b64
)
_debug sig
"
$sig
"
body
=
"{
\"
header
\"
:
$HEADER
,
\"
protected
\"
:
\"
$protected64
\"
,
\"
payload
\"
:
\"
$payload64
\"
,
\"
signature
\"
:
\"
$sig
\"
}"
_debug body
"
$body
"
if
[
"
$needbas64
"
]
;
then
response
=
$(
$CURL
-X
POST
--data
"
$body
"
$url
|
base64
)
else
response
=
$(
$CURL
-X
POST
--data
"
$body
"
$url
)
fi
responseHeaders
=
"
$(
cat
$CURL_HEADER
)
"
_debug responseHeaders
"
$responseHeaders
"
_debug response
"
$response
"
code
=
$(
grep
^HTTP
$CURL_HEADER
|
tail
-1
|
cut
-d
" "
-f
2
)
_debug code
$code
}
_get
()
{
url
=
"
$1
"
_info url
$url
response
=
$(
curl
--silent
$url
)
ret
=
$?
_debug response
"
$response
"
code
=
$(
echo
$response
|
grep
-o
'"status":[0-9]\+'
|
cut
-d
:
-f
2
)
_debug code
$code
return
$ret
}
#setopt "file" "opt" "=" "value" [";"]
_setopt
()
{
__conf
=
"
$1
"
__opt
=
"
$2
"
__sep
=
"
$3
"
__val
=
"
$4
"
__end
=
"
$5
"
if
[
-z
"
$__opt
"
]
;
then
echo
usage:
$0
'"file" "opt" "=" "value" [";"]'
return
fi
if
grep
-H
-n
"^
$__opt$__sep
"
$__conf
;
then
echo
OK
sed
-i
"s|^
$__opt$__sep
.*
$|$__opt$__sep$__val$__end
|"
$__conf
else
echo
APP
echo
"
$__opt$__sep$__val$__end
"
>>
$__conf
fi
grep
-H
-n
"^
$__opt$__sep
"
$__conf
}
_initpath
()
{
WORKING_DIR
=
~/.le
domain
=
$1
mkdir
-p
$WORKING_DIR
ACCOUNT_KEY_PATH
=
$WORKING_DIR
/account.acc
if
[
-z
"
$domain
"
]
;
then
return
0
fi
mkdir
-p
$WORKING_DIR
/
$domain
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
}
#issue webroot a.com [www.a.com,b.com,c.com] [key-length] [cert-file-path] [key-file-path] [reloadCmd]
issue
()
{
if
[
-z
"
$1
"
]
;
then
echo
"Usage:
$0
webroot a.com [www.a.com,b.com,c.com] [key-length] [cert-file-path] [key-file-path] [reloadCmd]"
return
1
fi
Le_Webroot
=
$1
Le_Domain
=
$2
Le_Alt
=
$3
Le_Keylength
=
$4
if
[
-z
"
$Le_Domain
"
]
;
then
Le_Domain
=
"
$1
"
fi
_initpath
$Le_Domain
DOMAIN_CONF
=
$WORKING_DIR
/
$Le_Domain
/
$Le_Domain
.conf
if
[
-f
"
$DOMAIN_CONF
"
]
;
then
source
"
$DOMAIN_CONF
"
if
[
"
$(
date
-u
"+%s"
)
"
-lt
"
$Le_NextRenewTime
"
]
;
then
_info
"Skip, Next renwal time is:
$Le_NextRenewTimeStr
"
return
2
fi
fi
if
[
-z
"
$Le_Webroot
"
]
;
then
echo
Usage:
$0
webroot a.com
[
b.com,c.com]
[
key-length]
return
1
fi
createAccountKey
$Le_Domain
$Le_Keylength
createDomainKey
$Le_Domain
$Le_Keylength
createCSR
$Le_Domain
$Le_Alt
pub_exp
=
$(
openssl rsa
-in
$ACCOUNT_KEY_PATH
-noout
-text
|
grep
"^publicExponent:"
|
cut
-d
'('
-f
2 |
cut
-d
'x'
-f
2 |
cut
-d
')'
-f
1
)
if
[
"
${#
pub_exp
}
"
==
"5"
]
;
then
pub_exp
=
0
$pub_exp
fi
_debug pub_exp
"
$pub_exp
"
e
=
$(
echo
$pub_exp
| xxd
-r
-p
|
base64
)
_debug e
"
$e
"
modulus
=
$(
openssl rsa
-in
$ACCOUNT_KEY_PATH
-modulus
-noout
|
cut
-d
'='
-f
2
)
n
=
$(
echo
$modulus
| xxd
-r
-p
|
base64
| _b64
)
jwk
=
'{"e": "'
$e
'", "kty": "RSA", "n": "'
$n
'"}'
HEADER
=
'{"alg": "RS256", "jwk": '
$jwk
'}'
HEADERPLACE
=
'{"nonce": "NONCE", "alg": "RS256", "jwk": '
$jwk
'}'
_debug HEADER
"
$HEADER
"
accountkey_json
=
$(
echo
-n
"
$jwk
"
|
sed
"s/ //g"
)
thumbprint
=
$(
echo
-n
"
$accountkey_json
"
|
sha256sum
| xxd
-r
-p
|
base64
| _b64
)
_info
"Registering account"
regjson
=
'{"resource": "new-reg", "agreement": "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"}'
if
[
"
$ACCOUNT_EMAIL
"
]
;
then
regjson
=
'{"resource": "new-reg", "contact": ["mailto: '
$ACCOUNT_EMAIL
'"], "agreement": "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"}'
fi
_send_signed_request
"
$API
/acme/new-reg"
"
$regjson
"
if
[
"
$code
"
==
""
]
||
[
"
$code
"
==
'201'
]
;
then
_info
"Registered"
echo
$response
>
$WORKING_DIR
/account.json
elif
[
"
$code
"
==
'409'
]
;
then
_info
"Already registered"
else
_info
"Register account Error."
return
1
fi
# verify each domain
_info
"verify each domain"
alldomains
=
$(
echo
"
$Le_Domain
,
$Le_Alt
"
|
sed
"s/,/ /g"
)
for
d
in
$alldomains
do
_info
"Verifing domain
$d
"
_send_signed_request
"
$API
/acme/new-authz"
"{
\"
resource
\"
:
\"
new-authz
\"
,
\"
identifier
\"
: {
\"
type
\"
:
\"
dns
\"
,
\"
value
\"
:
\"
$d
\"
}}"
if
[
!
-z
"
$code
"
]
&&
[
!
"
$code
"
==
'201'
]
;
then
_info
"new-authz error:
$d
"
return
1
fi
http01
=
$(
echo
$response
| egrep
-o
'{[^{]*"type":"http-01"[^}]*'
)
_debug http01
"
$http01
"
token
=
$(
echo
"
$http01
"
|
sed
's/,/\n'
/g|
grep
'"token":'
|
cut
-d
:
-f
2|sed
's/"//g'
)
_info token
$token
uri
=
$(
echo
"
$http01
"
|
sed
's/,/\n'
/g|
grep
'"uri":'
|
cut
-d
:
-f
2,3|sed
's/"//g'
)
_info uri
$uri
keyauthorization
=
"
$token
.
$thumbprint
"
_debug keyauthorization
"
$keyauthorization
"
wellknown_path
=
"
$Le_Webroot
/.well-known/acme-challenge"
_debug wellknown_path
"
$wellknown_path
"
mkdir
-p
"
$wellknown_path
"
wellknown_path
=
"
$wellknown_path
/
$token
"
echo
-n
"
$keyauthorization
"
>
$wellknown_path
wellknown_url
=
"http://
$d
/.well-known/acme-challenge/
$token
"
_debug wellknown_url
"
$wellknown_url
"
_debug challenge
"
$challenge
"
_send_signed_request
$uri
"{
\"
resource
\"
:
\"
challenge
\"
,
\"
keyAuthorization
\"
:
\"
$keyauthorization
\"
}"
if
[
!
-z
"
$code
"
]
&&
[
!
"
$code
"
==
'202'
]
;
then
_info
"challenge error:
$d
"
return
1
fi
while
[
"1"
]
;
do
_debug
"sleep 5 secs to verify"
sleep
5
_debug
"checking"
if
!
_get
$uri
;
then
_info
"verify error:
$d
"
return
1
fi
status
=
$(
echo
$response
| egrep
-o
'"status":"[^"]+"'
|
cut
-d
:
-f
2 |
sed
's/"//g'
)
if
[
"
$status
"
==
"valid"
]
;
then
_info
"verify success:
$d
"
break
;
fi
if
[
"
$status
"
==
"invalid"
]
;
then
error
=
$(
echo
$response
| egrep
-o
'"error":{[^}]*}'
|
grep
-o
'"detail":"[^"]*"'
|
cut
-d
'"'
-f
4
)
_info
"verify error:
$d
"
_debug
$error
return
1
;
fi
if
[
"
$status
"
==
"pending"
]
;
then
_info
"verify pending:
$d
"
else
_info
"verify error:
$d
"
return
1
fi
done
done
_info
"verify finished, start to sign."
der
=
$(
openssl req
-in
$CSR_PATH
-outform
DER |
base64
| _b64
)
_send_signed_request
"
$API
/acme/new-cert"
"{
\"
resource
\"
:
\"
new-cert
\"
,
\"
csr
\"
:
\"
$der
\"
}"
"needbas64"
echo
-----BEGIN
CERTIFICATE-----
>
$CERT_PATH
echo
$response
|
sed
"s/ /
\n
/g"
>>
$CERT_PATH
echo
-----END
CERTIFICATE-----
>>
$CERT_PATH
_info
"Cert success."
cat
$CERT_PATH
_setopt
$DOMAIN_CONF
"Le_Domain"
"="
"
$Le_Domain
"
_setopt
$DOMAIN_CONF
"Le_Alt"
"="
"
$Le_Alt
"
_setopt
$DOMAIN_CONF
"Le_Webroot"
"="
"
$Le_Webroot
"
_setopt
$DOMAIN_CONF
"Le_Keylength"
"="
"
$Le_Keylength
"
Le_LinkIssuer
=
$(
grep
-i
'^Link'
$CURL_HEADER
|
cut
-d
" "
-f
2|
cut
-d
';'
-f
1 |
sed
's/<//g'
|
sed
's/>//g'
)
_setopt
$DOMAIN_CONF
"Le_LinkIssuer"
"="
"
$Le_LinkIssuer
"
Le_LinkCert
=
$(
grep
-i
'^Location'
$CURL_HEADER
|
cut
-d
" "
-f
2
)
_setopt
$DOMAIN_CONF
"Le_LinkCert"
"="
"
$Le_LinkCert
"
Le_CertCreateTime
=
$(
date
-u
"+%s"
)
_setopt
$DOMAIN_CONF
"Le_CertCreateTime"
"="
"
$Le_CertCreateTime
"
Le_CertCreateTimeStr
=
$(
date
-u
"+%Y-%m-%d %H:%M:%S UTC"
)
_setopt
$DOMAIN_CONF
"Le_CertCreateTimeStr"
"="
"
\"
$Le_CertCreateTimeStr
\"
"
if
[
!
"
$Le_RenewalDays
"
]
;
then
Le_RenewalDays
=
50
fi
_setopt
$DOMAIN_CONF
"Le_RenewalDays"
"="
"
$Le_RenewalDays
"
Le_NextRenewTime
=
$(
date
-u
-d
"+
$Le_RenewalDays
day"
"+%s"
)
_setopt
$DOMAIN_CONF
"Le_NextRenewTime"
"="
"
$Le_NextRenewTime
"
Le_NextRenewTimeStr
=
$(
date
-u
-d
"+
$Le_RenewalDays
day"
"+%Y-%m-%d %H:%M:%S UTC"
)
_setopt
$DOMAIN_CONF
"Le_NextRenewTimeStr"
"="
"
\"
$Le_NextRenewTimeStr
\"
"
_setopt
$DOMAIN_CONF
"Le_RealCertPath"
"="
"
\"
$Le_RealCertPath
\"
"
if
[
"
$Le_RealCertPath
"
]
;
then
if
[
-f
"
$Le_RealCertPath
"
]
;
then
rm
-f
$Le_RealCertPath
fi
ln
-s
$CERT_PATH
$Le_RealCertPath
fi
_setopt
$DOMAIN_CONF
"Le_RealKeyPath"
"="
"
\"
$Le_RealKeyPath
\"
"
if
[
"
$Le_RealKeyPath
"
]
;
then
if
[
-f
"
$Le_RealKeyPath
"
]
;
then
rm
-f
$Le_RealKeyPath
fi
ln
-s
$CERT_KEY_PATH
$Le_RealKeyPath
fi
_setopt
$DOMAIN_CONF
"Le_ReloadCmd"
"="
"
\"
$Le_ReloadCmd
\"
"
if
[
"Le_ReloadCmd"
]
;
then
_info
"Run Le_ReloadCmd:
$Le_ReloadCmd
"
$Le_ReloadCmd
fi
}
renew
()
{
Le_Domain
=
"
$1
"
if
[
-z
"
$Le_Domain
"
]
;
then
echo
Usage:
$0
domain.com
return
1
fi
DOMAIN_CONF
=
$WORKING_DIR
/
$Le_Domain
/
$Le_Domain
.conf
if
[
-f
"
$DOMAIN_CONF
"
]
;
then
source
"
$DOMAIN_CONF
"
if
[
"
$(
date
-u
"+%s"
)
"
-lt
"
$Le_NextRenewTime
"
]
;
then
_info
"Skip, Next renwal time is:
$Le_NextRenewTimeStr
"
return
2
fi
fi
if
[
-z
"
$Le_Webroot
"
]
;
then
echo
Le_Webroot can not found, please remove the conf file and issue a new cert
return
1
fi
issue
$Le_Domain
}
renewAll
()
{
_info
"renewAll"
for
d
in
$(
ls
-F
$WORKING_DIR
|
grep
'/$'
)
;
do
d
=
$(
echo
$d
|
cut
-d
'/'
-f
1
)
_info
"renew
$d
"
renew
"
$d
"
done
}
install
()
{
_initpath
_info
"Installing to
$WORKING_DIR
"
mkdir
-p
$WORKING_DIR
/
cp
le.sh
$WORKING_DIR
/
chmod
+x
$WORKING_DIR
/le.sh
if
[
!
-f
/bin/le.sh
]
;
then
ln
-s
$WORKING_DIR
/le.sh /bin/le.sh
ln
-s
$WORKING_DIR
/le.sh /bin/le
fi
_info
"Installing cron job"
if
!
crontab
-l
|
grep
'le.sh renewAll'
;
then
crontab
-l
|
{
cat
;
echo
"0 0 * * * le.sh renewAll"
;
}
| crontab -
service cron restart
fi
_info OK
}
uninstall
()
{
_initpath
_info
"Removing cron job"
crontab
-l
|
sed
"/le.sh renewAll/d"
| crontab -
_info
"Removing /bin/le.sh"
rm
-f
/bin/le
rm
-f
/bin/le.sh
_info
"The keys and certs are in
$WORKING_DIR
, you can remove them by yourself."
}
showhelp
()
{
echo
"Usage: issue|renew|renewAll|createAccountKey|createDomainKey|createCSR|install|uninstall"
}
if
[
-z
"
$1
"
]
;
then
showhelp
fi
$1
$2
$3
$4
$5
$6
$7
$8
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment