Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
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