]>
Commit | Line | Data |
---|---|---|
4c3b3608 | 1 | #!/usr/bin/env bash |
635695ec | 2 | VER=2.1.1 |
a7b7355d | 3 | |
6cc11ffb | 4 | PROJECT_NAME="acme.sh" |
a7b7355d | 5 | |
6cc11ffb | 6 | PROJECT_ENTRY="acme.sh" |
7 | ||
8 | PROJECT="https://github.com/Neilpang/$PROJECT_NAME" | |
4c3b3608 | 9 | |
10 | DEFAULT_CA="https://acme-v01.api.letsencrypt.org" | |
11 | DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf" | |
12 | ||
a7b7355d | 13 | DEFAULT_USER_AGENT="$PROJECT_ENTRY client: $PROJECT" |
bbbdcb09 | 14 | |
4c3b3608 | 15 | STAGE_CA="https://acme-staging.api.letsencrypt.org" |
16 | ||
17 | VTYPE_HTTP="http-01" | |
18 | VTYPE_DNS="dns-01" | |
19 | ||
88fab7d6 | 20 | BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----" |
21 | END_CSR="-----END CERTIFICATE REQUEST-----" | |
22 | ||
23 | BEGIN_CERT="-----BEGIN CERTIFICATE-----" | |
24 | END_CERT="-----END CERTIFICATE-----" | |
25 | ||
a63b05a9 | 26 | if [[ -z "$AGREEMENT" ]] ; then |
4c3b3608 | 27 | AGREEMENT="$DEFAULT_AGREEMENT" |
28 | fi | |
29 | ||
4c3b3608 | 30 | |
31 | _info() { | |
a63b05a9 | 32 | if [[ -z "$2" ]] ; then |
33 | echo "[$(date)] $1" | |
4c3b3608 | 34 | else |
a63b05a9 | 35 | echo "[$(date)] $1"="'$2'" |
4c3b3608 | 36 | fi |
37 | } | |
38 | ||
39 | _err() { | |
a63b05a9 | 40 | _info "$@" >&2 |
4c3b3608 | 41 | return 1 |
42 | } | |
43 | ||
c60883ef | 44 | _debug() { |
a63b05a9 | 45 | if [[ -z "$DEBUG" ]] ; then |
c60883ef | 46 | return |
47 | fi | |
a63b05a9 | 48 | _err "$@" |
c60883ef | 49 | return 0 |
50 | } | |
51 | ||
a63b05a9 | 52 | _debug2() { |
8f7ad693 | 53 | if [[ "$DEBUG" ]] && [[ "$DEBUG" -ge "2" ]] ; then |
a63b05a9 | 54 | _debug "$@" |
55 | fi | |
56 | return | |
57 | } | |
58 | ||
c60883ef | 59 | _exists() { |
60 | cmd="$1" | |
a63b05a9 | 61 | if [[ -z "$cmd" ]] ; then |
c60883ef | 62 | _err "Usage: _exists cmd" |
63 | return 1 | |
64 | fi | |
eac18b1c | 65 | if type command >/dev/null 2>&1 ; then |
66 | command -v $cmd >/dev/null 2>&1 | |
67 | else | |
68 | type $cmd >/dev/null 2>&1 | |
69 | fi | |
c60883ef | 70 | ret="$?" |
a63b05a9 | 71 | _debug2 "$cmd exists=$ret" |
c60883ef | 72 | return $ret |
73 | } | |
74 | ||
4c3b3608 | 75 | _h2b() { |
76 | hex=$(cat) | |
77 | i=1 | |
78 | j=2 | |
79 | while [ '1' ] ; do | |
80 | h=$(printf $hex | cut -c $i-$j) | |
a63b05a9 | 81 | if [[ -z "$h" ]] ; then |
4c3b3608 | 82 | break; |
83 | fi | |
84 | printf "\x$h" | |
85 | let "i+=2" | |
86 | let "j+=2" | |
87 | done | |
88 | } | |
89 | ||
c60883ef | 90 | #options file |
91 | _sed_i() { | |
92 | options="$1" | |
93 | filename="$2" | |
a63b05a9 | 94 | if [[ -z "$filename" ]] ; then |
c60883ef | 95 | _err "Usage:_sed_i options filename" |
96 | return 1 | |
97 | fi | |
98 | ||
99 | if sed -h 2>&1 | grep "\-i[SUFFIX]" ; then | |
100 | _debug "Using sed -i" | |
101 | sed -i "" | |
102 | else | |
103 | _debug "No -i support in sed" | |
104 | text="$(cat $filename)" | |
105 | echo "$text" | sed "$options" > "$filename" | |
106 | fi | |
107 | } | |
108 | ||
88fab7d6 | 109 | #Usage: file startline endline |
110 | _getfile() { | |
111 | filename="$1" | |
112 | startline="$2" | |
113 | endline="$3" | |
a63b05a9 | 114 | if [[ -z "$endline" ]] ; then |
88fab7d6 | 115 | _err "Usage: file startline endline" |
116 | return 1 | |
117 | fi | |
118 | ||
119 | i="$(grep -n -- "$startline" $filename | cut -d : -f 1)" | |
a63b05a9 | 120 | if [[ -z "$i" ]] ; then |
88fab7d6 | 121 | _err "Can not find start line: $startline" |
122 | return 1 | |
123 | fi | |
124 | let "i+=1" | |
125 | _debug i $i | |
126 | ||
127 | j="$(grep -n -- "$endline" $filename | cut -d : -f 1)" | |
a63b05a9 | 128 | if [[ -z "$j" ]] ; then |
88fab7d6 | 129 | _err "Can not find end line: $endline" |
130 | return 1 | |
131 | fi | |
132 | let "j-=1" | |
133 | _debug j $j | |
134 | ||
135 | sed -n $i,${j}p "$filename" | |
136 | ||
137 | } | |
138 | ||
139 | #Usage: multiline | |
4c3b3608 | 140 | _base64() { |
a63b05a9 | 141 | if [[ "$1" ]] ; then |
88fab7d6 | 142 | openssl base64 -e |
143 | else | |
144 | openssl base64 -e | tr -d '\r\n' | |
145 | fi | |
146 | } | |
147 | ||
148 | #Usage: multiline | |
149 | _dbase64() { | |
a63b05a9 | 150 | if [[ "$1" ]] ; then |
88fab7d6 | 151 | openssl base64 -d -A |
152 | else | |
153 | openssl base64 -d | |
154 | fi | |
155 | } | |
156 | ||
157 | #Usage: hashalg | |
158 | #Output Base64-encoded digest | |
159 | _digest() { | |
160 | alg="$1" | |
a63b05a9 | 161 | if [[ -z "$alg" ]] ; then |
88fab7d6 | 162 | _err "Usage: _digest hashalg" |
163 | return 1 | |
164 | fi | |
165 | ||
a63b05a9 | 166 | if [[ "$alg" == "sha256" ]] ; then |
88fab7d6 | 167 | openssl dgst -sha256 -binary | _base64 |
168 | else | |
169 | _err "$alg is not supported yet" | |
170 | return 1 | |
171 | fi | |
172 | ||
173 | } | |
174 | ||
175 | #Usage: keyfile hashalg | |
176 | #Output: Base64-encoded signature value | |
177 | _sign() { | |
178 | keyfile="$1" | |
179 | alg="$2" | |
a63b05a9 | 180 | if [[ -z "$alg" ]] ; then |
88fab7d6 | 181 | _err "Usage: _sign keyfile hashalg" |
182 | return 1 | |
183 | fi | |
184 | ||
a63b05a9 | 185 | if [[ "$alg" == "sha256" ]] ; then |
88fab7d6 | 186 | openssl dgst -sha256 -sign "$keyfile" | _base64 |
187 | else | |
188 | _err "$alg is not supported yet" | |
189 | return 1 | |
190 | fi | |
191 | ||
4c3b3608 | 192 | } |
193 | ||
34c27e09 | 194 | _ss() { |
195 | _port="$1" | |
edf08da6 | 196 | |
197 | if _exists "ss" ; then | |
198 | _debug "Using: ss" | |
199 | ss -ntpl | grep :$_port" " | |
200 | return 0 | |
201 | fi | |
202 | ||
203 | if _exists "netstat" ; then | |
251fc37c | 204 | _debug "Using: netstat" |
ccb96535 | 205 | if netstat -h 2>&1 | grep "\-p proto" >/dev/null ; then |
206 | #for windows version netstat tool | |
50a4ef9c | 207 | netstat -anb -p tcp | grep "LISTENING" | grep :$_port" " |
ccb96535 | 208 | else |
14165e5a | 209 | if netstat -help 2>&1 | grep "\-p protocol" >/dev/null ; then |
edf08da6 | 210 | netstat -an -p tcp | grep LISTEN | grep :$_port" " |
211 | else | |
212 | netstat -ntpl | grep :$_port" " | |
213 | fi | |
ccb96535 | 214 | fi |
34c27e09 | 215 | return 0 |
216 | fi | |
edf08da6 | 217 | |
34c27e09 | 218 | return 1 |
219 | } | |
220 | ||
ac2d5123 | 221 | toPkcs() { |
222 | domain="$1" | |
223 | pfxPassword="$2" | |
224 | if [[ -z "$domain" ]] ; then | |
a7b7355d | 225 | echo "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]" |
ac2d5123 | 226 | return 1 |
227 | fi | |
228 | ||
229 | _initpath "$domain" | |
230 | ||
231 | if [[ "$pfxPassword" ]] ; then | |
232 | openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword" | |
233 | else | |
234 | openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" | |
235 | fi | |
236 | ||
237 | if [[ "$?" == "0" ]] ; then | |
238 | _info "Success, Pfx is exported to: $CERT_PFX_PATH" | |
239 | fi | |
240 | ||
241 | } | |
242 | ||
4c3b3608 | 243 | #domain [2048] |
244 | createAccountKey() { | |
245 | _info "Creating account key" | |
a63b05a9 | 246 | if [[ -z "$1" ]] ; then |
a7b7355d | 247 | echo Usage: $PROJECT_ENTRY --createAccountKey -d domain.com [--accountkeylength 2048] |
4c3b3608 | 248 | return |
249 | fi | |
250 | ||
251 | account=$1 | |
252 | length=$2 | |
b5eb4b90 | 253 | _debug account "$account" |
254 | _debug length "$length" | |
4c3b3608 | 255 | if [[ "$length" == "ec-"* ]] ; then |
256 | length=2048 | |
257 | fi | |
258 | ||
b5eb4b90 | 259 | if [[ -z "$2" ]] || [[ "$2" == "no" ]] ; then |
4c3b3608 | 260 | _info "Use default length 2048" |
261 | length=2048 | |
262 | fi | |
263 | _initpath | |
264 | ||
a63b05a9 | 265 | if [[ -f "$ACCOUNT_KEY_PATH" ]] ; then |
4c3b3608 | 266 | _info "Account key exists, skip" |
267 | return | |
268 | else | |
269 | #generate account key | |
f89d991d | 270 | openssl genrsa $length 2>/dev/null > "$ACCOUNT_KEY_PATH" |
4c3b3608 | 271 | fi |
272 | ||
273 | } | |
274 | ||
275 | #domain length | |
276 | createDomainKey() { | |
277 | _info "Creating domain key" | |
a63b05a9 | 278 | if [[ -z "$1" ]] ; then |
a7b7355d | 279 | echo Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ] |
4c3b3608 | 280 | return |
281 | fi | |
282 | ||
283 | domain=$1 | |
284 | length=$2 | |
285 | isec="" | |
286 | if [[ "$length" == "ec-"* ]] ; then | |
287 | isec="1" | |
288 | length=$(printf $length | cut -d '-' -f 2-100) | |
289 | eccname="$length" | |
290 | fi | |
291 | ||
a63b05a9 | 292 | if [[ -z "$length" ]] ; then |
293 | if [[ "$isec" ]] ; then | |
4c3b3608 | 294 | length=256 |
295 | else | |
296 | length=2048 | |
297 | fi | |
298 | fi | |
299 | _info "Use length $length" | |
300 | ||
a63b05a9 | 301 | if [[ "$isec" ]] ; then |
302 | if [[ "$length" == "256" ]] ; then | |
4c3b3608 | 303 | eccname="prime256v1" |
304 | fi | |
a63b05a9 | 305 | if [[ "$length" == "384" ]] ; then |
4c3b3608 | 306 | eccname="secp384r1" |
307 | fi | |
a63b05a9 | 308 | if [[ "$length" == "521" ]] ; then |
4c3b3608 | 309 | eccname="secp521r1" |
310 | fi | |
311 | _info "Using ec name: $eccname" | |
312 | fi | |
313 | ||
314 | _initpath $domain | |
315 | ||
a63b05a9 | 316 | if [[ ! -f "$CERT_KEY_PATH" ]] || ( [[ "$FORCE" ]] && ! [[ "$IS_RENEW" ]] ); then |
4c3b3608 | 317 | #generate account key |
a63b05a9 | 318 | if [[ "$isec" ]] ; then |
4c3b3608 | 319 | openssl ecparam -name $eccname -genkey 2>/dev/null > "$CERT_KEY_PATH" |
320 | else | |
321 | openssl genrsa $length 2>/dev/null > "$CERT_KEY_PATH" | |
322 | fi | |
323 | else | |
a63b05a9 | 324 | if [[ "$IS_RENEW" ]] ; then |
4c3b3608 | 325 | _info "Domain key exists, skip" |
326 | return 0 | |
327 | else | |
328 | _err "Domain key exists, do you want to overwrite the key?" | |
41e3eafa | 329 | _err "Add '--force', and try again." |
4c3b3608 | 330 | return 1 |
331 | fi | |
332 | fi | |
333 | ||
334 | } | |
335 | ||
336 | # domain domainlist | |
337 | createCSR() { | |
338 | _info "Creating csr" | |
a63b05a9 | 339 | if [[ -z "$1" ]] ; then |
a7b7355d | 340 | echo Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ] |
4c3b3608 | 341 | return |
342 | fi | |
343 | domain=$1 | |
344 | _initpath $domain | |
345 | ||
346 | domainlist=$2 | |
347 | ||
a63b05a9 | 348 | if [[ -f "$CSR_PATH" ]] && [[ "$IS_RENEW" ]] && [[ -z "$FORCE" ]]; then |
4c3b3608 | 349 | _info "CSR exists, skip" |
350 | return | |
351 | fi | |
352 | ||
a63b05a9 | 353 | if [[ -z "$domainlist" ]] || [[ "$domainlist" == "no" ]]; then |
4c3b3608 | 354 | #single domain |
355 | _info "Single domain" $domain | |
1ad65f7d | 356 | printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n" > "$DOMAIN_SSL_CONF" |
357 | openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH" | |
4c3b3608 | 358 | else |
359 | alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")" | |
360 | #multi | |
361 | _info "Multi domain" "$alt" | |
362 | printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n[SAN]\nsubjectAltName=$alt" > "$DOMAIN_SSL_CONF" | |
363 | openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -reqexts SAN -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH" | |
364 | fi | |
365 | ||
366 | } | |
367 | ||
166096dc | 368 | _urlencode() { |
4c3b3608 | 369 | __n=$(cat) |
370 | echo $__n | tr '/+' '_-' | tr -d '= ' | |
371 | } | |
372 | ||
373 | _time2str() { | |
374 | #BSD | |
375 | if date -u -d@$1 2>/dev/null ; then | |
376 | return | |
377 | fi | |
378 | ||
379 | #Linux | |
380 | if date -u -r $1 2>/dev/null ; then | |
381 | return | |
382 | fi | |
383 | ||
384 | } | |
385 | ||
44df2967 | 386 | _stat() { |
387 | #Linux | |
388 | if stat -c '%U:%G' "$1" 2>/dev/null ; then | |
389 | return | |
390 | fi | |
391 | ||
392 | #BSD | |
393 | if stat -f '%Su:%Sg' "$1" 2>/dev/null ; then | |
394 | return | |
395 | fi | |
396 | } | |
397 | ||
166096dc | 398 | #keyfile |
399 | _calcjwk() { | |
400 | keyfile="$1" | |
a63b05a9 | 401 | if [[ -z "$keyfile" ]] ; then |
166096dc | 402 | _err "Usage: _calcjwk keyfile" |
403 | return 1 | |
404 | fi | |
405 | EC_SIGN="" | |
406 | if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then | |
407 | _debug "RSA key" | |
408 | pub_exp=$(openssl rsa -in $keyfile -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1) | |
a63b05a9 | 409 | if [[ "${#pub_exp}" == "5" ]] ; then |
166096dc | 410 | pub_exp=0$pub_exp |
411 | fi | |
a63b05a9 | 412 | _debug2 pub_exp "$pub_exp" |
166096dc | 413 | |
414 | e=$(echo $pub_exp | _h2b | _base64) | |
a63b05a9 | 415 | _debug2 e "$e" |
166096dc | 416 | |
417 | modulus=$(openssl rsa -in $keyfile -modulus -noout | cut -d '=' -f 2 ) | |
418 | n=$(echo $modulus| _h2b | _base64 | _urlencode ) | |
419 | jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}' | |
a63b05a9 | 420 | _debug2 jwk "$jwk" |
166096dc | 421 | |
422 | HEADER='{"alg": "RS256", "jwk": '$jwk'}' | |
423 | HEADERPLACE='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}' | |
424 | elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then | |
425 | _debug "EC key" | |
426 | EC_SIGN="1" | |
427 | crv="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")" | |
a63b05a9 | 428 | _debug2 crv $crv |
166096dc | 429 | |
430 | pubi="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)" | |
a63b05a9 | 431 | _debug2 pubi $pubi |
166096dc | 432 | let "pubi=pubi+1" |
433 | ||
434 | pubj="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)" | |
a63b05a9 | 435 | _debug2 pubj $pubj |
166096dc | 436 | let "pubj=pubj-1" |
437 | ||
438 | pubtext="$(openssl ec -in $keyfile -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")" | |
a63b05a9 | 439 | _debug2 pubtext "$pubtext" |
166096dc | 440 | |
441 | xlen="$(printf "$pubtext" | tr -d ':' | wc -c)" | |
442 | let "xlen=xlen/4" | |
a63b05a9 | 443 | _debug2 xlen $xlen |
166096dc | 444 | |
445 | let "xend=xlen+1" | |
446 | x="$(printf $pubtext | cut -d : -f 2-$xend)" | |
a63b05a9 | 447 | _debug2 x $x |
166096dc | 448 | |
449 | x64="$(printf $x | tr -d : | _h2b | _base64 | _urlencode)" | |
a63b05a9 | 450 | _debug2 x64 $x64 |
166096dc | 451 | |
452 | let "xend+=1" | |
453 | y="$(printf $pubtext | cut -d : -f $xend-10000)" | |
a63b05a9 | 454 | _debug2 y $y |
166096dc | 455 | |
456 | y64="$(printf $y | tr -d : | _h2b | _base64 | _urlencode)" | |
a63b05a9 | 457 | _debug2 y64 $y64 |
166096dc | 458 | |
459 | jwk='{"kty": "EC", "crv": "'$crv'", "x": "'$x64'", "y": "'$y64'"}' | |
a63b05a9 | 460 | _debug2 jwk "$jwk" |
166096dc | 461 | |
462 | HEADER='{"alg": "ES256", "jwk": '$jwk'}' | |
463 | HEADERPLACE='{"nonce": "NONCE", "alg": "ES256", "jwk": '$jwk'}' | |
464 | ||
465 | else | |
466 | _err "Only RSA or EC key is supported." | |
467 | return 1 | |
468 | fi | |
469 | ||
a63b05a9 | 470 | _debug2 HEADER "$HEADER" |
166096dc | 471 | } |
c60883ef | 472 | # body url [needbase64] |
473 | _post() { | |
474 | body="$1" | |
475 | url="$2" | |
476 | needbase64="$3" | |
477 | ||
478 | if _exists "curl" ; then | |
bbbdcb09 | 479 | CURL="$CURL --dump-header $HTTP_HEADER " |
a63b05a9 | 480 | if [[ "$needbase64" ]] ; then |
bbbdcb09 | 481 | response="$($CURL -A "User-Agent: $USER_AGENT" -X POST --data "$body" $url | _base64)" |
c60883ef | 482 | else |
bbbdcb09 | 483 | response="$($CURL -A "User-Agent: $USER_AGENT" -X POST --data "$body" $url)" |
c60883ef | 484 | fi |
485 | else | |
a63b05a9 | 486 | if [[ "$needbase64" ]] ; then |
bbbdcb09 | 487 | response="$($WGET -S -O - --user-agent="$USER_AGENT" --post-data="$body" $url 2>"$HTTP_HEADER" | _base64)" |
c60883ef | 488 | else |
bbbdcb09 | 489 | response="$($WGET -S -O - --user-agent="$USER_AGENT" --post-data="$body" $url 2>"$HTTP_HEADER")" |
c60883ef | 490 | fi |
491 | _sed_i "s/^ *//g" "$HTTP_HEADER" | |
492 | fi | |
493 | echo -n "$response" | |
494 | ||
495 | } | |
496 | ||
497 | # url getheader | |
498 | _get() { | |
499 | url="$1" | |
500 | onlyheader="$2" | |
501 | _debug url $url | |
502 | if _exists "curl" ; then | |
a63b05a9 | 503 | if [[ "$onlyheader" ]] ; then |
bbbdcb09 | 504 | $CURL -I -A "User-Agent: $USER_AGENT" $url |
c60883ef | 505 | else |
bbbdcb09 | 506 | $CURL -A "User-Agent: $USER_AGENT" $url |
c60883ef | 507 | fi |
508 | else | |
bbbdcb09 | 509 | _debug "WGET" "$WGET" |
a63b05a9 | 510 | if [[ "$onlyheader" ]] ; then |
bbbdcb09 | 511 | eval $WGET --user-agent=\"$USER_AGENT\" -S -O /dev/null $url 2>&1 | sed 's/^[ ]*//g' |
c60883ef | 512 | else |
bbbdcb09 | 513 | eval $WGET --user-agent=\"$USER_AGENT\" -O - $url |
c60883ef | 514 | fi |
515 | fi | |
516 | ret=$? | |
517 | return $ret | |
518 | } | |
166096dc | 519 | |
520 | # url payload needbase64 keyfile | |
4c3b3608 | 521 | _send_signed_request() { |
522 | url=$1 | |
523 | payload=$2 | |
524 | needbase64=$3 | |
166096dc | 525 | keyfile=$4 |
a63b05a9 | 526 | if [[ -z "$keyfile" ]] ; then |
166096dc | 527 | keyfile="$ACCOUNT_KEY_PATH" |
528 | fi | |
4c3b3608 | 529 | _debug url $url |
530 | _debug payload "$payload" | |
531 | ||
166096dc | 532 | if ! _calcjwk "$keyfile" ; then |
533 | return 1 | |
534 | fi | |
c60883ef | 535 | |
166096dc | 536 | payload64=$(echo -n $payload | _base64 | _urlencode) |
a63b05a9 | 537 | _debug2 payload64 $payload64 |
4c3b3608 | 538 | |
539 | nonceurl="$API/directory" | |
bbbdcb09 | 540 | nonce="$(_get $nonceurl "onlyheader" | grep -o "Replay-Nonce:.*$" | head -1 | tr -d "\r\n" | cut -d ' ' -f 2)" |
4c3b3608 | 541 | |
542 | _debug nonce "$nonce" | |
543 | ||
544 | protected="$(printf "$HEADERPLACE" | sed "s/NONCE/$nonce/" )" | |
a63b05a9 | 545 | _debug2 protected "$protected" |
4c3b3608 | 546 | |
166096dc | 547 | protected64="$(printf "$protected" | _base64 | _urlencode)" |
a63b05a9 | 548 | _debug2 protected64 "$protected64" |
166096dc | 549 | |
88fab7d6 | 550 | sig=$(echo -n "$protected64.$payload64" | _sign "$keyfile" "sha256" | _urlencode) |
a63b05a9 | 551 | _debug2 sig "$sig" |
4c3b3608 | 552 | |
553 | body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}" | |
a63b05a9 | 554 | _debug2 body "$body" |
4c3b3608 | 555 | |
bbbdcb09 | 556 | |
c60883ef | 557 | response="$(_post "$body" $url "$needbase64" )" |
4c3b3608 | 558 | |
c60883ef | 559 | responseHeaders="$(cat $HTTP_HEADER)" |
4c3b3608 | 560 | |
a63b05a9 | 561 | _debug2 responseHeaders "$responseHeaders" |
562 | _debug2 response "$response" | |
c60883ef | 563 | code="$(grep "^HTTP" $HTTP_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )" |
4c3b3608 | 564 | _debug code $code |
565 | ||
566 | } | |
567 | ||
4c3b3608 | 568 | |
569 | #setopt "file" "opt" "=" "value" [";"] | |
570 | _setopt() { | |
571 | __conf="$1" | |
572 | __opt="$2" | |
573 | __sep="$3" | |
574 | __val="$4" | |
575 | __end="$5" | |
a63b05a9 | 576 | if [[ -z "$__opt" ]] ; then |
4c3b3608 | 577 | echo usage: _setopt '"file" "opt" "=" "value" [";"]' |
578 | return | |
579 | fi | |
a63b05a9 | 580 | if [[ ! -f "$__conf" ]] ; then |
4c3b3608 | 581 | touch "$__conf" |
582 | fi | |
583 | ||
584 | if grep -H -n "^$__opt$__sep" "$__conf" > /dev/null ; then | |
a63b05a9 | 585 | _debug2 OK |
4c3b3608 | 586 | if [[ "$__val" == *"&"* ]] ; then |
587 | __val="$(echo $__val | sed 's/&/\\&/g')" | |
588 | fi | |
589 | text="$(cat $__conf)" | |
6dfaaa70 | 590 | echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf" |
4c3b3608 | 591 | |
592 | elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev/null ; then | |
593 | if [[ "$__val" == *"&"* ]] ; then | |
594 | __val="$(echo $__val | sed 's/&/\\&/g')" | |
595 | fi | |
596 | text="$(cat $__conf)" | |
6dfaaa70 | 597 | echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf" |
4c3b3608 | 598 | |
599 | else | |
a63b05a9 | 600 | _debug2 APP |
4c3b3608 | 601 | echo "$__opt$__sep$__val$__end" >> "$__conf" |
602 | fi | |
603 | _debug "$(grep -H -n "^$__opt$__sep" $__conf)" | |
604 | } | |
605 | ||
606 | #_savedomainconf key value | |
607 | #save to domain.conf | |
608 | _savedomainconf() { | |
609 | key="$1" | |
610 | value="$2" | |
a63b05a9 | 611 | if [[ "$DOMAIN_CONF" ]] ; then |
4c3b3608 | 612 | _setopt $DOMAIN_CONF "$key" "=" "$value" |
613 | else | |
614 | _err "DOMAIN_CONF is empty, can not save $key=$value" | |
615 | fi | |
616 | } | |
617 | ||
618 | #_saveaccountconf key value | |
619 | _saveaccountconf() { | |
620 | key="$1" | |
621 | value="$2" | |
a63b05a9 | 622 | if [[ "$ACCOUNT_CONF_PATH" ]] ; then |
281aa349 | 623 | _setopt $ACCOUNT_CONF_PATH "$key" "=" "\"$value\"" |
4c3b3608 | 624 | else |
625 | _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value" | |
626 | fi | |
627 | } | |
628 | ||
629 | _startserver() { | |
630 | content="$1" | |
6fc1447f | 631 | _debug "startserver: $$" |
1b2e940d | 632 | nchelp="$(nc -h 2>&1)" |
850c1128 | 633 | |
399306a1 | 634 | if echo "$nchelp" | grep "\-q[ ,]" >/dev/null ; then |
850c1128 | 635 | _NC="nc -q 1 -l" |
636 | else | |
f76eb452 | 637 | if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null ; then |
638 | _NC="nc -c -l" | |
6d60f288 | 639 | elif echo "$nchelp" | grep "\-N" |grep "Shutdown the network socket after EOF on stdin" >/dev/null ; then |
640 | _NC="nc -N -l" | |
f76eb452 | 641 | else |
642 | _NC="nc -l" | |
643 | fi | |
4c3b3608 | 644 | fi |
1b2e940d | 645 | |
f76eb452 | 646 | _debug "_NC" "$_NC" |
4c3b3608 | 647 | # while true ; do |
a63b05a9 | 648 | if [[ "$DEBUG" ]] ; then |
3aff11f6 | 649 | if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort -vv ; then |
650 | echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort -vv ; | |
651 | fi | |
4c3b3608 | 652 | else |
bac3b6b0 | 653 | if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1; then |
654 | echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1 | |
3aff11f6 | 655 | fi |
4c3b3608 | 656 | fi |
a63b05a9 | 657 | if [[ "$?" != "0" ]] ; then |
051c706d | 658 | _err "nc listen error." |
6fc1447f | 659 | exit 1 |
051c706d | 660 | fi |
4c3b3608 | 661 | # done |
662 | } | |
663 | ||
6fc1447f | 664 | _stopserver(){ |
4c3b3608 | 665 | pid="$1" |
6fc1447f | 666 | _debug "pid" "$pid" |
667 | if [[ -z "$pid" ]] ; then | |
668 | return | |
669 | fi | |
670 | ||
dca09ded | 671 | if [[ "$(ps | grep "$pid")" ]] ; then |
672 | _debug "Found proc process, kill it." | |
770dc4b2 | 673 | kill -s 9 $pid > /dev/null |
6fc1447f | 674 | fi |
675 | ||
233e8a20 | 676 | for ncid in $(echo $(ps | grep nc) | cut -d " " -f 1) ; do |
dca09ded | 677 | _debug "kill $ncid" |
770dc4b2 | 678 | kill -s 9 $ncid > /dev/null |
dca09ded | 679 | done |
680 | ||
3d434e43 | 681 | _get "http://localhost:$Le_HTTPPort" >/dev/null 2>&1 |
4c3b3608 | 682 | |
683 | } | |
684 | ||
685 | _initpath() { | |
686 | ||
a63b05a9 | 687 | if [[ -z "$LE_WORKING_DIR" ]] ; then |
6cc11ffb | 688 | LE_WORKING_DIR=$HOME/.$PROJECT_NAME |
4c3b3608 | 689 | fi |
690 | ||
d53289d7 | 691 | _DEFAULT_ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf" |
692 | ||
635695ec | 693 | if [[ -z "$ACCOUNT_CONF_PATH" ]] ; then |
694 | if [[ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ]] ; then | |
695 | source "$_DEFAULT_ACCOUNT_CONF_PATH" | |
696 | fi | |
d53289d7 | 697 | fi |
698 | ||
a63b05a9 | 699 | if [[ -z "$ACCOUNT_CONF_PATH" ]] ; then |
d53289d7 | 700 | ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH" |
4c3b3608 | 701 | fi |
702 | ||
a63b05a9 | 703 | if [[ -f "$ACCOUNT_CONF_PATH" ]] ; then |
4c3b3608 | 704 | source "$ACCOUNT_CONF_PATH" |
705 | fi | |
706 | ||
281aa349 | 707 | if [[ "$IN_CRON" ]] ; then |
708 | if [[ ! "$_USER_PATH_EXPORTED" ]] ; then | |
709 | _USER_PATH_EXPORTED=1 | |
710 | export PATH="$USER_PATH:$PATH" | |
711 | fi | |
712 | fi | |
713 | ||
a63b05a9 | 714 | if [[ -z "$API" ]] ; then |
715 | if [[ -z "$STAGE" ]] ; then | |
4c3b3608 | 716 | API="$DEFAULT_CA" |
717 | else | |
718 | API="$STAGE_CA" | |
719 | _info "Using stage api:$API" | |
720 | fi | |
721 | fi | |
722 | ||
a63b05a9 | 723 | if [[ -z "$ACME_DIR" ]] ; then |
4c3b3608 | 724 | ACME_DIR="/home/.acme" |
725 | fi | |
726 | ||
a63b05a9 | 727 | if [[ -z "$APACHE_CONF_BACKUP_DIR" ]] ; then |
8a144f4d | 728 | APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR" |
4c3b3608 | 729 | fi |
730 | ||
a63b05a9 | 731 | if [[ -z "$USER_AGENT" ]] ; then |
bbbdcb09 | 732 | USER_AGENT="$DEFAULT_USER_AGENT" |
733 | fi | |
734 | ||
735 | HTTP_HEADER="$LE_WORKING_DIR/http.header" | |
736 | ||
737 | WGET="wget -q" | |
8f7ad693 | 738 | if [[ "$DEBUG" ]] && [[ "$DEBUG" -ge "2" ]] ; then |
bbbdcb09 | 739 | WGET="$WGET -d " |
740 | fi | |
741 | ||
742 | dp="$LE_WORKING_DIR/curl.dump" | |
4a0f23e2 | 743 | CURL="curl -L --silent" |
8f7ad693 | 744 | if [[ "$DEBUG" ]] && [[ "$DEBUG" -ge "2" ]] ; then |
4a0f23e2 | 745 | CURL="$CURL -L --trace-ascii $dp " |
bbbdcb09 | 746 | fi |
b2817897 | 747 | |
748 | _DEFAULT_ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key" | |
a63b05a9 | 749 | if [[ -z "$ACCOUNT_KEY_PATH" ]] ; then |
b2817897 | 750 | ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH" |
4c3b3608 | 751 | fi |
b2817897 | 752 | |
753 | domain="$1" | |
4c3b3608 | 754 | |
a63b05a9 | 755 | if [[ -z "$domain" ]] ; then |
4c3b3608 | 756 | return 0 |
757 | fi | |
758 | ||
b2817897 | 759 | _DEFAULT_CERT_HOME="$LE_WORKING_DIR" |
760 | if [[ -z "$CERT_HOME" ]] ; then | |
761 | CERT_HOME="$_DEFAULT_CERT_HOME" | |
762 | fi | |
763 | ||
764 | domainhome="$CERT_HOME/$domain" | |
4c3b3608 | 765 | mkdir -p "$domainhome" |
766 | ||
a63b05a9 | 767 | if [[ -z "$DOMAIN_PATH" ]] ; then |
4c3b3608 | 768 | DOMAIN_PATH="$domainhome" |
769 | fi | |
a63b05a9 | 770 | if [[ -z "$DOMAIN_CONF" ]] ; then |
1ad65f7d | 771 | DOMAIN_CONF="$domainhome/$domain.conf" |
4c3b3608 | 772 | fi |
773 | ||
a63b05a9 | 774 | if [[ -z "$DOMAIN_SSL_CONF" ]] ; then |
1ad65f7d | 775 | DOMAIN_SSL_CONF="$domainhome/$domain.ssl.conf" |
4c3b3608 | 776 | fi |
777 | ||
a63b05a9 | 778 | if [[ -z "$CSR_PATH" ]] ; then |
4c3b3608 | 779 | CSR_PATH="$domainhome/$domain.csr" |
780 | fi | |
a63b05a9 | 781 | if [[ -z "$CERT_KEY_PATH" ]] ; then |
4c3b3608 | 782 | CERT_KEY_PATH="$domainhome/$domain.key" |
783 | fi | |
a63b05a9 | 784 | if [[ -z "$CERT_PATH" ]] ; then |
4c3b3608 | 785 | CERT_PATH="$domainhome/$domain.cer" |
786 | fi | |
a63b05a9 | 787 | if [[ -z "$CA_CERT_PATH" ]] ; then |
4c3b3608 | 788 | CA_CERT_PATH="$domainhome/ca.cer" |
789 | fi | |
a63b05a9 | 790 | if [[ -z "$CERT_FULLCHAIN_PATH" ]] ; then |
850db6d4 | 791 | CERT_FULLCHAIN_PATH="$domainhome/fullchain.cer" |
caf1fc10 | 792 | fi |
a63b05a9 | 793 | if [[ -z "$CERT_PFX_PATH" ]] ; then |
ac2d5123 | 794 | CERT_PFX_PATH="$domainhome/$domain.pfx" |
795 | fi | |
4c3b3608 | 796 | } |
797 | ||
798 | ||
799 | _apachePath() { | |
4c3b3608 | 800 | httpdconfname="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )" |
d62ee940 | 801 | if [[ "$httpdconfname" == '/'* ]] ; then |
802 | httpdconf="$httpdconfname" | |
c456d954 | 803 | httpdconfname="$(basename $httpdconfname)" |
d62ee940 | 804 | else |
805 | httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )" | |
806 | httpdconf="$httpdroot/$httpdconfname" | |
807 | fi | |
808 | ||
a63b05a9 | 809 | if [[ ! -f $httpdconf ]] ; then |
4c3b3608 | 810 | _err "Apache Config file not found" $httpdconf |
811 | return 1 | |
812 | fi | |
813 | return 0 | |
814 | } | |
815 | ||
816 | _restoreApache() { | |
a63b05a9 | 817 | if [[ -z "$usingApache" ]] ; then |
4c3b3608 | 818 | return 0 |
819 | fi | |
820 | _initpath | |
821 | if ! _apachePath ; then | |
822 | return 1 | |
823 | fi | |
824 | ||
a63b05a9 | 825 | if [[ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ]] ; then |
4c3b3608 | 826 | _debug "No config file to restore." |
827 | return 0 | |
828 | fi | |
829 | ||
830 | cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf" | |
5ef501c5 | 831 | _debug "Restored: $httpdconf." |
4c3b3608 | 832 | if ! apachectl -t ; then |
833 | _err "Sorry, restore apache config error, please contact me." | |
834 | return 1; | |
835 | fi | |
5ef501c5 | 836 | _debug "Restored successfully." |
4c3b3608 | 837 | rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" |
838 | return 0 | |
839 | } | |
840 | ||
841 | _setApache() { | |
842 | _initpath | |
843 | if ! _apachePath ; then | |
844 | return 1 | |
845 | fi | |
846 | ||
847 | #backup the conf | |
848 | _debug "Backup apache config file" $httpdconf | |
849 | cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/ | |
850 | _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname" | |
851 | _info "In case there is an error that can not be restored automatically, you may try restore it yourself." | |
852 | _info "The backup file will be deleted on sucess, just forget it." | |
853 | ||
854 | #add alias | |
b09d597c | 855 | |
856 | apacheVer="$(apachectl -V | grep "Server version:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2 )" | |
857 | _debug "apacheVer" "$apacheVer" | |
858 | apacheMajer="$(echo "$apacheVer" | cut -d . -f 1)" | |
859 | apacheMinor="$(echo "$apacheVer" | cut -d . -f 2)" | |
860 | ||
861 | if [[ "$apacheVer" ]] && [[ "$apacheMajer" -ge "2" ]] && [[ "$apacheMinor" -ge "4" ]] ; then | |
862 | echo " | |
4c3b3608 | 863 | Alias /.well-known/acme-challenge $ACME_DIR |
864 | ||
865 | <Directory $ACME_DIR > | |
866 | Require all granted | |
b09d597c | 867 | </Directory> |
868 | " >> $httpdconf | |
869 | else | |
870 | echo " | |
871 | Alias /.well-known/acme-challenge $ACME_DIR | |
872 | ||
873 | <Directory $ACME_DIR > | |
874 | Order allow,deny | |
875 | Allow from all | |
4c3b3608 | 876 | </Directory> |
877 | " >> $httpdconf | |
b09d597c | 878 | fi |
879 | ||
4c3b3608 | 880 | |
881 | if ! apachectl -t ; then | |
882 | _err "Sorry, apache config error, please contact me." | |
883 | _restoreApache | |
884 | return 1; | |
885 | fi | |
886 | ||
a63b05a9 | 887 | if [[ ! -d "$ACME_DIR" ]] ; then |
4c3b3608 | 888 | mkdir -p "$ACME_DIR" |
889 | chmod 755 "$ACME_DIR" | |
890 | fi | |
891 | ||
892 | if ! apachectl graceful ; then | |
893 | _err "Sorry, apachectl graceful error, please contact me." | |
894 | _restoreApache | |
895 | return 1; | |
896 | fi | |
897 | usingApache="1" | |
898 | return 0 | |
899 | } | |
900 | ||
5ef501c5 | 901 | _clearup() { |
4c3b3608 | 902 | _stopserver $serverproc |
903 | serverproc="" | |
904 | _restoreApache | |
905 | } | |
906 | ||
907 | # webroot removelevel tokenfile | |
908 | _clearupwebbroot() { | |
909 | __webroot="$1" | |
a63b05a9 | 910 | if [[ -z "$__webroot" ]] ; then |
4c3b3608 | 911 | _debug "no webroot specified, skip" |
912 | return 0 | |
913 | fi | |
914 | ||
a63b05a9 | 915 | if [[ "$2" == '1' ]] ; then |
4c3b3608 | 916 | _debug "remove $__webroot/.well-known" |
917 | rm -rf "$__webroot/.well-known" | |
a63b05a9 | 918 | elif [[ "$2" == '2' ]] ; then |
4c3b3608 | 919 | _debug "remove $__webroot/.well-known/acme-challenge" |
920 | rm -rf "$__webroot/.well-known/acme-challenge" | |
a63b05a9 | 921 | elif [[ "$2" == '3' ]] ; then |
4c3b3608 | 922 | _debug "remove $__webroot/.well-known/acme-challenge/$3" |
923 | rm -rf "$__webroot/.well-known/acme-challenge/$3" | |
924 | else | |
925 | _info "Skip for removelevel:$2" | |
926 | fi | |
927 | ||
928 | return 0 | |
929 | ||
930 | } | |
931 | ||
932 | issue() { | |
a63b05a9 | 933 | if [[ -z "$2" ]] ; then |
a7b7355d | 934 | echo "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ " |
4c3b3608 | 935 | return 1 |
936 | fi | |
937 | Le_Webroot="$1" | |
938 | Le_Domain="$2" | |
939 | Le_Alt="$3" | |
940 | Le_Keylength="$4" | |
941 | Le_RealCertPath="$5" | |
942 | Le_RealKeyPath="$6" | |
943 | Le_RealCACertPath="$7" | |
944 | Le_ReloadCmd="$8" | |
a63b05a9 | 945 | Le_RealFullChainPath="$9" |
4c3b3608 | 946 | |
947 | _initpath $Le_Domain | |
948 | ||
a63b05a9 | 949 | if [[ -f "$DOMAIN_CONF" ]] ; then |
4c3b3608 | 950 | Le_NextRenewTime=$(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut -d '=' -f 2) |
a63b05a9 | 951 | if [[ -z "$FORCE" ]] && [[ "$Le_NextRenewTime" ]] && [[ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ]] ; then |
4c3b3608 | 952 | _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)" |
953 | return 2 | |
954 | fi | |
955 | fi | |
96a46cfc | 956 | |
957 | _setopt "$DOMAIN_CONF" "Le_Domain" "=" "$Le_Domain" | |
958 | _setopt "$DOMAIN_CONF" "Le_Alt" "=" "$Le_Alt" | |
959 | _setopt "$DOMAIN_CONF" "Le_Webroot" "=" "$Le_Webroot" | |
960 | _setopt "$DOMAIN_CONF" "Le_Keylength" "=" "$Le_Keylength" | |
961 | _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\"" | |
962 | _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\"" | |
963 | _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\"" | |
964 | _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\"" | |
a63b05a9 | 965 | _setopt "$DOMAIN_CONF" "Le_RealFullChainPath" "=" "\"$Le_RealFullChainPath\"" |
4c3b3608 | 966 | |
a63b05a9 | 967 | if [[ "$Le_Alt" == "no" ]] ; then |
4c3b3608 | 968 | Le_Alt="" |
969 | fi | |
a63b05a9 | 970 | if [[ "$Le_Keylength" == "no" ]] ; then |
4c3b3608 | 971 | Le_Keylength="" |
972 | fi | |
a63b05a9 | 973 | if [[ "$Le_RealCertPath" == "no" ]] ; then |
4c3b3608 | 974 | Le_RealCertPath="" |
975 | fi | |
a63b05a9 | 976 | if [[ "$Le_RealKeyPath" == "no" ]] ; then |
4c3b3608 | 977 | Le_RealKeyPath="" |
978 | fi | |
a63b05a9 | 979 | if [[ "$Le_RealCACertPath" == "no" ]] ; then |
4c3b3608 | 980 | Le_RealCACertPath="" |
981 | fi | |
a63b05a9 | 982 | if [[ "$Le_ReloadCmd" == "no" ]] ; then |
4c3b3608 | 983 | Le_ReloadCmd="" |
984 | fi | |
a63b05a9 | 985 | if [[ "$Le_RealFullChainPath" == "no" ]] ; then |
986 | Le_RealFullChainPath="" | |
987 | fi | |
96a46cfc | 988 | |
4c3b3608 | 989 | |
a63b05a9 | 990 | if [[ "$Le_Webroot" == *"no"* ]] ; then |
4c3b3608 | 991 | _info "Standalone mode." |
5ef501c5 | 992 | if ! _exists "nc" ; then |
4c3b3608 | 993 | _err "Please install netcat(nc) tools first." |
994 | return 1 | |
995 | fi | |
996 | ||
a63b05a9 | 997 | if [[ -z "$Le_HTTPPort" ]] ; then |
4c3b3608 | 998 | Le_HTTPPort=80 |
999 | fi | |
1000 | _setopt "$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort" | |
1001 | ||
251fc37c | 1002 | netprc="$(_ss "$Le_HTTPPort" | grep "$Le_HTTPPort")" |
a63b05a9 | 1003 | if [[ "$netprc" ]] ; then |
4c3b3608 | 1004 | _err "$netprc" |
1005 | _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)" | |
1006 | _err "Please stop it first" | |
1007 | return 1 | |
1008 | fi | |
1009 | fi | |
1010 | ||
a63b05a9 | 1011 | if [[ "$Le_Webroot" == *"apache"* ]] ; then |
4c3b3608 | 1012 | if ! _setApache ; then |
1013 | _err "set up apache error. Report error to me." | |
1014 | return 1 | |
1015 | fi | |
4c3b3608 | 1016 | else |
1017 | usingApache="" | |
1018 | fi | |
1019 | ||
41e3eafa | 1020 | if [[ ! -f "$ACCOUNT_KEY_PATH" ]] ; then |
1021 | if ! createAccountKey $Le_Domain $Le_Keylength ; then | |
1022 | _err "Create account key error." | |
5ef501c5 | 1023 | if [[ "$usingApache" ]] ; then |
1024 | _restoreApache | |
1025 | fi | |
41e3eafa | 1026 | return 1 |
1027 | fi | |
1028 | fi | |
1029 | ||
166096dc | 1030 | if ! _calcjwk "$ACCOUNT_KEY_PATH" ; then |
5ef501c5 | 1031 | if [[ "$usingApache" ]] ; then |
1032 | _restoreApache | |
1033 | fi | |
166096dc | 1034 | return 1 |
1035 | fi | |
1036 | ||
1037 | accountkey_json=$(echo -n "$jwk" | tr -d ' ' ) | |
88fab7d6 | 1038 | thumbprint=$(echo -n "$accountkey_json" | _digest "sha256" | _urlencode) |
4c3b3608 | 1039 | |
88fab7d6 | 1040 | accountkeyhash="$(cat "$ACCOUNT_KEY_PATH" | _digest "sha256" )" |
a63b05a9 | 1041 | accountkeyhash="$(echo $accountkeyhash$API | _digest "sha256" )" |
1042 | if [[ "$accountkeyhash" != "$ACCOUNT_KEY_HASH" ]] ; then | |
166096dc | 1043 | _info "Registering account" |
1044 | regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}' | |
a63b05a9 | 1045 | if [[ "$ACCOUNT_EMAIL" ]] ; then |
166096dc | 1046 | regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' |
1047 | fi | |
1048 | _send_signed_request "$API/acme/new-reg" "$regjson" | |
1049 | ||
a63b05a9 | 1050 | if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then |
166096dc | 1051 | _info "Registered" |
1052 | echo $response > $LE_WORKING_DIR/account.json | |
a63b05a9 | 1053 | elif [[ "$code" == '409' ]] ; then |
166096dc | 1054 | _info "Already registered" |
1055 | else | |
1056 | _err "Register account Error: $response" | |
1057 | _clearup | |
1058 | return 1 | |
1059 | fi | |
1060 | ACCOUNT_KEY_HASH="$accountkeyhash" | |
1061 | _saveaccountconf "ACCOUNT_KEY_HASH" "$ACCOUNT_KEY_HASH" | |
1062 | else | |
1063 | _info "Skip register account key" | |
1064 | fi | |
1065 | ||
41e3eafa | 1066 | if [[ ! -f "$CERT_KEY_PATH" ]] ; then |
1067 | if ! createDomainKey $Le_Domain $Le_Keylength ; then | |
1068 | _err "Create domain key error." | |
5ef501c5 | 1069 | _clearup |
41e3eafa | 1070 | return 1 |
1071 | fi | |
4c3b3608 | 1072 | fi |
1073 | ||
1074 | if ! createCSR $Le_Domain $Le_Alt ; then | |
1075 | _err "Create CSR error." | |
5ef501c5 | 1076 | _clearup |
4c3b3608 | 1077 | return 1 |
1078 | fi | |
a63b05a9 | 1079 | |
4c3b3608 | 1080 | vlist="$Le_Vlist" |
1081 | # verify each domain | |
1082 | _info "Verify each domain" | |
1083 | sep='#' | |
a63b05a9 | 1084 | if [[ -z "$vlist" ]] ; then |
4c3b3608 | 1085 | alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' ) |
a63b05a9 | 1086 | _index=1 |
1087 | _currentRoot="" | |
4c3b3608 | 1088 | for d in $alldomains |
a63b05a9 | 1089 | do |
1090 | _info "Getting webroot for domain" $d | |
1091 | _w="$(echo $Le_Webroot | cut -d , -f $_index)" | |
1092 | _debug _w "$_w" | |
1093 | if [[ "$_w" ]] ; then | |
1094 | _currentRoot="$_w" | |
1095 | fi | |
1096 | _debug "_currentRoot" "$_currentRoot" | |
1097 | let "_index+=1" | |
1098 | ||
1099 | vtype="$VTYPE_HTTP" | |
1100 | if [[ "$_currentRoot" == "dns"* ]] ; then | |
1101 | vtype="$VTYPE_DNS" | |
1102 | fi | |
4c3b3608 | 1103 | _info "Getting token for domain" $d |
1104 | _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}" | |
a63b05a9 | 1105 | if [[ ! -z "$code" ]] && [[ ! "$code" == '201' ]] ; then |
4c3b3608 | 1106 | _err "new-authz error: $response" |
1107 | _clearup | |
1108 | return 1 | |
1109 | fi | |
1110 | ||
348cddd7 | 1111 | entry="$(printf $response | egrep -o '\{[^{]*"type":"'$vtype'"[^}]*')" |
4c3b3608 | 1112 | _debug entry "$entry" |
1113 | ||
1114 | token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')" | |
1115 | _debug token $token | |
1116 | ||
1117 | uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )" | |
1118 | _debug uri $uri | |
1119 | ||
1120 | keyauthorization="$token.$thumbprint" | |
1121 | _debug keyauthorization "$keyauthorization" | |
1122 | ||
a63b05a9 | 1123 | dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot" |
4c3b3608 | 1124 | _debug dvlist "$dvlist" |
1125 | ||
1126 | vlist="$vlist$dvlist," | |
1127 | ||
1128 | done | |
1129 | ||
1130 | #add entry | |
1131 | dnsadded="" | |
1132 | ventries=$(echo "$vlist" | tr ',' ' ' ) | |
1133 | for ventry in $ventries | |
1134 | do | |
1135 | d=$(echo $ventry | cut -d $sep -f 1) | |
1136 | keyauthorization=$(echo $ventry | cut -d $sep -f 2) | |
a63b05a9 | 1137 | vtype=$(echo $ventry | cut -d $sep -f 4) |
1138 | _currentRoot=$(echo $ventry | cut -d $sep -f 5) | |
1139 | if [[ "$vtype" == "$VTYPE_DNS" ]] ; then | |
4c3b3608 | 1140 | dnsadded='0' |
1141 | txtdomain="_acme-challenge.$d" | |
1142 | _debug txtdomain "$txtdomain" | |
88fab7d6 | 1143 | txt="$(echo -e -n $keyauthorization | _digest "sha256" | _urlencode)" |
4c3b3608 | 1144 | _debug txt "$txt" |
1145 | #dns | |
1146 | #1. check use api | |
1147 | d_api="" | |
a63b05a9 | 1148 | if [[ -f "$LE_WORKING_DIR/$d/$_currentRoot" ]] ; then |
1149 | d_api="$LE_WORKING_DIR/$d/$_currentRoot" | |
1150 | elif [[ -f "$LE_WORKING_DIR/$d/$_currentRoot.sh" ]] ; then | |
1151 | d_api="$LE_WORKING_DIR/$d/$_currentRoot.sh" | |
1152 | elif [[ -f "$LE_WORKING_DIR/$_currentRoot" ]] ; then | |
1153 | d_api="$LE_WORKING_DIR/$_currentRoot" | |
1154 | elif [[ -f "$LE_WORKING_DIR/$_currentRoot.sh" ]] ; then | |
1155 | d_api="$LE_WORKING_DIR/$_currentRoot.sh" | |
1156 | elif [[ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot" ]] ; then | |
1157 | d_api="$LE_WORKING_DIR/dnsapi/$_currentRoot" | |
1158 | elif [[ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot.sh" ]] ; then | |
1159 | d_api="$LE_WORKING_DIR/dnsapi/$_currentRoot.sh" | |
4c3b3608 | 1160 | fi |
1161 | _debug d_api "$d_api" | |
1162 | ||
a63b05a9 | 1163 | if [[ "$d_api" ]] ; then |
4c3b3608 | 1164 | _info "Found domain api file: $d_api" |
1165 | else | |
1166 | _err "Add the following TXT record:" | |
1167 | _err "Domain: $txtdomain" | |
1168 | _err "TXT value: $txt" | |
1169 | _err "Please be aware that you prepend _acme-challenge. before your domain" | |
1170 | _err "so the resulting subdomain will be: $txtdomain" | |
1171 | continue | |
1172 | fi | |
4c3b3608 | 1173 | |
73b8b120 | 1174 | ( |
1175 | if ! source $d_api ; then | |
1176 | _err "Load file $d_api error. Please check your api file and try again." | |
1177 | return 1 | |
1178 | fi | |
1179 | ||
a63b05a9 | 1180 | addcommand="$_currentRoot-add" |
d53289d7 | 1181 | if ! _exists $addcommand ; then |
73b8b120 | 1182 | _err "It seems that your api file is not correct, it must have a function named: $addcommand" |
1183 | return 1 | |
1184 | fi | |
1185 | ||
1186 | if ! $addcommand $txtdomain $txt ; then | |
1187 | _err "Error add txt for domain:$txtdomain" | |
1188 | return 1 | |
1189 | fi | |
1190 | ) | |
4c3b3608 | 1191 | |
73b8b120 | 1192 | if [[ "$?" != "0" ]] ; then |
5ef501c5 | 1193 | _clearup |
4c3b3608 | 1194 | return 1 |
1195 | fi | |
1196 | dnsadded='1' | |
1197 | fi | |
1198 | done | |
1199 | ||
a63b05a9 | 1200 | if [[ "$dnsadded" == '0' ]] ; then |
4c3b3608 | 1201 | _setopt "$DOMAIN_CONF" "Le_Vlist" "=" "\"$vlist\"" |
1202 | _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit." | |
1203 | _err "Please add the TXT records to the domains, and retry again." | |
5ef501c5 | 1204 | _clearup |
4c3b3608 | 1205 | return 1 |
1206 | fi | |
1207 | ||
1208 | fi | |
1209 | ||
a63b05a9 | 1210 | if [[ "$dnsadded" == '1' ]] ; then |
4c3b3608 | 1211 | _info "Sleep 60 seconds for the txt records to take effect" |
1212 | sleep 60 | |
1213 | fi | |
1214 | ||
1215 | _debug "ok, let's start to verify" | |
a63b05a9 | 1216 | |
4c3b3608 | 1217 | ventries=$(echo "$vlist" | tr ',' ' ' ) |
1218 | for ventry in $ventries | |
1219 | do | |
1220 | d=$(echo $ventry | cut -d $sep -f 1) | |
1221 | keyauthorization=$(echo $ventry | cut -d $sep -f 2) | |
1222 | uri=$(echo $ventry | cut -d $sep -f 3) | |
a63b05a9 | 1223 | vtype=$(echo $ventry | cut -d $sep -f 4) |
1224 | _currentRoot=$(echo $ventry | cut -d $sep -f 5) | |
4c3b3608 | 1225 | _info "Verifying:$d" |
1226 | _debug "d" "$d" | |
1227 | _debug "keyauthorization" "$keyauthorization" | |
1228 | _debug "uri" "$uri" | |
1229 | removelevel="" | |
1230 | token="" | |
a63b05a9 | 1231 | |
1232 | _debug "_currentRoot" "$_currentRoot" | |
1233 | ||
1234 | ||
1235 | if [[ "$vtype" == "$VTYPE_HTTP" ]] ; then | |
1236 | if [[ "$_currentRoot" == "no" ]] ; then | |
4c3b3608 | 1237 | _info "Standalone mode server" |
1238 | _startserver "$keyauthorization" & | |
6fc1447f | 1239 | if [[ "$?" != "0" ]] ; then |
5ef501c5 | 1240 | _clearup |
6fc1447f | 1241 | return 1 |
1242 | fi | |
4c3b3608 | 1243 | serverproc="$!" |
1244 | sleep 2 | |
1245 | _debug serverproc $serverproc | |
6fc1447f | 1246 | |
4c3b3608 | 1247 | else |
6f930641 | 1248 | if [[ "$_currentRoot" == "apache" ]] ; then |
1249 | wellknown_path="$ACME_DIR" | |
1250 | else | |
a63b05a9 | 1251 | wellknown_path="$_currentRoot/.well-known/acme-challenge" |
6f930641 | 1252 | if [[ ! -d "$_currentRoot/.well-known" ]] ; then |
1253 | removelevel='1' | |
1254 | elif [[ ! -d "$_currentRoot/.well-known/acme-challenge" ]] ; then | |
1255 | removelevel='2' | |
1256 | else | |
1257 | removelevel='3' | |
1258 | fi | |
4c3b3608 | 1259 | fi |
6f930641 | 1260 | |
4c3b3608 | 1261 | _debug wellknown_path "$wellknown_path" |
6f930641 | 1262 | |
4c3b3608 | 1263 | token="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)" |
1264 | _debug "writing token:$token to $wellknown_path/$token" | |
1265 | ||
1266 | mkdir -p "$wellknown_path" | |
1267 | echo -n "$keyauthorization" > "$wellknown_path/$token" | |
df886ffa | 1268 | if [[ ! "$usingApache" ]] ; then |
a63b05a9 | 1269 | webroot_owner=$(_stat $_currentRoot) |
df886ffa | 1270 | _debug "Changing owner/group of .well-known to $webroot_owner" |
a63b05a9 | 1271 | chown -R $webroot_owner "$_currentRoot/.well-known" |
df886ffa | 1272 | fi |
4c3b3608 | 1273 | |
1274 | fi | |
1275 | fi | |
1276 | ||
1277 | _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" | |
1278 | ||
a63b05a9 | 1279 | if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then |
c60883ef | 1280 | _err "$d:Challenge error: $response" |
a63b05a9 | 1281 | _clearupwebbroot "$_currentRoot" "$removelevel" "$token" |
4c3b3608 | 1282 | _clearup |
1283 | return 1 | |
1284 | fi | |
1285 | ||
6fc1447f | 1286 | waittimes=0 |
1287 | if [[ -z "$MAX_RETRY_TIMES" ]] ; then | |
1288 | MAX_RETRY_TIMES=30 | |
1289 | fi | |
1290 | ||
a63b05a9 | 1291 | while [[ "1" ]] ; do |
6fc1447f | 1292 | let "waittimes+=1" |
1293 | if [[ "$waittimes" -ge "$MAX_RETRY_TIMES" ]] ; then | |
1294 | _err "$d:Timeout" | |
1295 | _clearupwebbroot "$_currentRoot" "$removelevel" "$token" | |
1296 | _clearup | |
1297 | return 1 | |
1298 | fi | |
1299 | ||
4c3b3608 | 1300 | _debug "sleep 5 secs to verify" |
1301 | sleep 5 | |
1302 | _debug "checking" | |
c60883ef | 1303 | response="$(_get $uri)" |
a63b05a9 | 1304 | if [[ "$?" != "0" ]] ; then |
c60883ef | 1305 | _err "$d:Verify error:$response" |
a63b05a9 | 1306 | _clearupwebbroot "$_currentRoot" "$removelevel" "$token" |
4c3b3608 | 1307 | _clearup |
1308 | return 1 | |
1309 | fi | |
1310 | ||
7d91e35f | 1311 | status=$(echo $response | egrep -o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"') |
a63b05a9 | 1312 | if [[ "$status" == "valid" ]] ; then |
4c3b3608 | 1313 | _info "Success" |
1314 | _stopserver $serverproc | |
1315 | serverproc="" | |
a63b05a9 | 1316 | _clearupwebbroot "$_currentRoot" "$removelevel" "$token" |
4c3b3608 | 1317 | break; |
1318 | fi | |
1319 | ||
a63b05a9 | 1320 | if [[ "$status" == "invalid" ]] ; then |
4c3b3608 | 1321 | error=$(echo $response | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4) |
1322 | _err "$d:Verify error:$error" | |
a63b05a9 | 1323 | _clearupwebbroot "$_currentRoot" "$removelevel" "$token" |
4c3b3608 | 1324 | _clearup |
1325 | return 1; | |
1326 | fi | |
1327 | ||
a63b05a9 | 1328 | if [[ "$status" == "pending" ]] ; then |
4c3b3608 | 1329 | _info "Pending" |
1330 | else | |
1331 | _err "$d:Verify error:$response" | |
a63b05a9 | 1332 | _clearupwebbroot "$_currentRoot" "$removelevel" "$token" |
4c3b3608 | 1333 | _clearup |
1334 | return 1 | |
1335 | fi | |
1336 | ||
1337 | done | |
1338 | ||
1339 | done | |
1340 | ||
1341 | _clearup | |
1342 | _info "Verify finished, start to sign." | |
fa8311dc | 1343 | der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)" |
4c3b3608 | 1344 | _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" |
1345 | ||
1346 | ||
bbbdcb09 | 1347 | Le_LinkCert="$(grep -i -o '^Location.*$' $HTTP_HEADER | head -1 | tr -d "\r\n" | cut -d " " -f 2)" |
4c3b3608 | 1348 | _setopt "$DOMAIN_CONF" "Le_LinkCert" "=" "$Le_LinkCert" |
1349 | ||
a63b05a9 | 1350 | if [[ "$Le_LinkCert" ]] ; then |
88fab7d6 | 1351 | echo "$BEGIN_CERT" > "$CERT_PATH" |
c60883ef | 1352 | _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" |
88fab7d6 | 1353 | echo "$END_CERT" >> "$CERT_PATH" |
4c3b3608 | 1354 | _info "Cert success." |
1355 | cat "$CERT_PATH" | |
1356 | ||
1357 | _info "Your cert is in $CERT_PATH" | |
caf1fc10 | 1358 | cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH" |
281aa349 | 1359 | |
1360 | if [[ ! "$USER_PATH" ]] || [[ ! "$IN_CRON" ]] ; then | |
1361 | USER_PATH="$PATH" | |
1362 | _saveaccountconf "USER_PATH" "$USER_PATH" | |
1363 | fi | |
4c3b3608 | 1364 | fi |
1365 | ||
1366 | ||
a63b05a9 | 1367 | if [[ -z "$Le_LinkCert" ]] ; then |
88fab7d6 | 1368 | response="$(echo $response | _dbase64 "multiline" )" |
4c3b3608 | 1369 | _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')" |
1370 | return 1 | |
1371 | fi | |
1372 | ||
1373 | _setopt "$DOMAIN_CONF" 'Le_Vlist' '=' "\"\"" | |
1374 | ||
bbbdcb09 | 1375 | Le_LinkIssuer=$(grep -i '^Link' $HTTP_HEADER | head -1 | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' ) |
4c3b3608 | 1376 | _setopt "$DOMAIN_CONF" "Le_LinkIssuer" "=" "$Le_LinkIssuer" |
1377 | ||
a63b05a9 | 1378 | if [[ "$Le_LinkIssuer" ]] ; then |
88fab7d6 | 1379 | echo "$BEGIN_CERT" > "$CA_CERT_PATH" |
c60883ef | 1380 | _get "$Le_LinkIssuer" | _base64 "multiline" >> "$CA_CERT_PATH" |
88fab7d6 | 1381 | echo "$END_CERT" >> "$CA_CERT_PATH" |
4c3b3608 | 1382 | _info "The intermediate CA cert is in $CA_CERT_PATH" |
caf1fc10 | 1383 | cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH" |
1384 | _info "And the full chain certs is there: $CERT_FULLCHAIN_PATH" | |
4c3b3608 | 1385 | fi |
1386 | ||
1387 | Le_CertCreateTime=$(date -u "+%s") | |
1388 | _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime" | |
1389 | ||
1390 | Le_CertCreateTimeStr=$(date -u ) | |
1391 | _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\"" | |
1392 | ||
06625071 | 1393 | if [[ -z "$Le_RenewalDays" ]] || [[ "$Le_RenewalDays" -lt "0" ]] || [[ "$Le_RenewalDays" -gt "80" ]] ; then |
4c3b3608 | 1394 | Le_RenewalDays=80 |
1395 | fi | |
1396 | ||
1397 | _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays" | |
1398 | ||
1399 | let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60" | |
1400 | _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime" | |
1401 | ||
1402 | Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime ) | |
1403 | _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\"" | |
1404 | ||
1405 | ||
a63b05a9 | 1406 | installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" |
4c3b3608 | 1407 | |
1408 | } | |
1409 | ||
1410 | renew() { | |
1411 | Le_Domain="$1" | |
a63b05a9 | 1412 | if [[ -z "$Le_Domain" ]] ; then |
a7b7355d | 1413 | _err "Usage: $PROJECT_ENTRY --renew -d domain.com" |
4c3b3608 | 1414 | return 1 |
1415 | fi | |
1416 | ||
1417 | _initpath $Le_Domain | |
1418 | ||
a63b05a9 | 1419 | if [[ ! -f "$DOMAIN_CONF" ]] ; then |
4c3b3608 | 1420 | _info "$Le_Domain is not a issued domain, skip." |
1421 | return 0; | |
1422 | fi | |
1423 | ||
1424 | source "$DOMAIN_CONF" | |
a63b05a9 | 1425 | if [[ -z "$FORCE" ]] && [[ "$Le_NextRenewTime" ]] && [[ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ]] ; then |
4c3b3608 | 1426 | _info "Skip, Next renewal time is: $Le_NextRenewTimeStr" |
1427 | return 2 | |
1428 | fi | |
1429 | ||
1430 | IS_RENEW="1" | |
a63b05a9 | 1431 | issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" |
4c3b3608 | 1432 | local res=$? |
1433 | IS_RENEW="" | |
1434 | ||
1435 | return $res | |
1436 | } | |
1437 | ||
1438 | renewAll() { | |
1439 | _initpath | |
1440 | _info "renewAll" | |
1441 | ||
b2817897 | 1442 | for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do |
4c3b3608 | 1443 | d=$(echo $d | cut -d '/' -f 1) |
1444 | _info "renew $d" | |
1445 | ||
1446 | Le_LinkCert="" | |
1447 | Le_Domain="" | |
ed373617 | 1448 | Le_Alt="no" |
4c3b3608 | 1449 | Le_Webroot="" |
1450 | Le_Keylength="" | |
1451 | Le_LinkIssuer="" | |
1452 | ||
1453 | Le_CertCreateTime="" | |
1454 | Le_CertCreateTimeStr="" | |
1455 | Le_RenewalDays="" | |
1456 | Le_NextRenewTime="" | |
1457 | Le_NextRenewTimeStr="" | |
1458 | ||
1459 | Le_RealCertPath="" | |
1460 | Le_RealKeyPath="" | |
1461 | ||
1462 | Le_RealCACertPath="" | |
1463 | ||
1464 | Le_ReloadCmd="" | |
a63b05a9 | 1465 | Le_RealFullChainPath="" |
4c3b3608 | 1466 | |
1467 | DOMAIN_PATH="" | |
1468 | DOMAIN_CONF="" | |
1469 | DOMAIN_SSL_CONF="" | |
1470 | CSR_PATH="" | |
1471 | CERT_KEY_PATH="" | |
1472 | CERT_PATH="" | |
1473 | CA_CERT_PATH="" | |
ac2d5123 | 1474 | CERT_PFX_PATH="" |
caf1fc10 | 1475 | CERT_FULLCHAIN_PATH="" |
4c3b3608 | 1476 | ACCOUNT_KEY_PATH="" |
1477 | ||
1478 | wellknown_path="" | |
1479 | ||
1480 | renew "$d" | |
1481 | done | |
1482 | ||
1483 | } | |
1484 | ||
1485 | installcert() { | |
1486 | Le_Domain="$1" | |
a63b05a9 | 1487 | if [[ -z "$Le_Domain" ]] ; then |
a7b7355d | 1488 | echo "Usage: $PROJECT_ENTRY --installcert -d domain.com [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]" |
4c3b3608 | 1489 | return 1 |
1490 | fi | |
1491 | ||
1492 | Le_RealCertPath="$2" | |
1493 | Le_RealKeyPath="$3" | |
1494 | Le_RealCACertPath="$4" | |
1495 | Le_ReloadCmd="$5" | |
a63b05a9 | 1496 | Le_RealFullChainPath="$6" |
4c3b3608 | 1497 | |
1498 | _initpath $Le_Domain | |
1499 | ||
1500 | _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\"" | |
1501 | _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\"" | |
1502 | _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\"" | |
1503 | _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\"" | |
a63b05a9 | 1504 | _setopt "$DOMAIN_CONF" "Le_RealFullChainPath" "=" "\"$Le_RealFullChainPath\"" |
4c3b3608 | 1505 | |
a63b05a9 | 1506 | if [[ "$Le_RealCertPath" ]] ; then |
1507 | if [[ -f "$Le_RealCertPath" ]] ; then | |
4c3b3608 | 1508 | cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak |
1509 | fi | |
1510 | cat "$CERT_PATH" > "$Le_RealCertPath" | |
1511 | fi | |
1512 | ||
a63b05a9 | 1513 | if [[ "$Le_RealCACertPath" ]] ; then |
1514 | if [[ "$Le_RealCACertPath" == "$Le_RealCertPath" ]] ; then | |
4c3b3608 | 1515 | echo "" >> "$Le_RealCACertPath" |
1516 | cat "$CA_CERT_PATH" >> "$Le_RealCACertPath" | |
1517 | else | |
a63b05a9 | 1518 | if [[ -f "$Le_RealCACertPath" ]] ; then |
78552b18 B |
1519 | cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak |
1520 | fi | |
4c3b3608 | 1521 | cat "$CA_CERT_PATH" > "$Le_RealCACertPath" |
1522 | fi | |
1523 | fi | |
1524 | ||
1525 | ||
a63b05a9 | 1526 | if [[ "$Le_RealKeyPath" ]] ; then |
1527 | if [[ -f "$Le_RealKeyPath" ]] ; then | |
4c3b3608 | 1528 | cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak |
1529 | fi | |
1530 | cat "$CERT_KEY_PATH" > "$Le_RealKeyPath" | |
1531 | fi | |
a63b05a9 | 1532 | |
1533 | if [[ "$Le_RealFullChainPath" ]] ; then | |
1534 | if [[ -f "$Le_RealFullChainPath" ]] ; then | |
1535 | cp -p "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak | |
1536 | fi | |
1537 | cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath" | |
1538 | fi | |
4c3b3608 | 1539 | |
a63b05a9 | 1540 | if [[ "$Le_ReloadCmd" ]] ; then |
4c3b3608 | 1541 | _info "Run Le_ReloadCmd: $Le_ReloadCmd" |
1542 | (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd") | |
1543 | fi | |
1544 | ||
1545 | } | |
1546 | ||
1547 | installcronjob() { | |
1548 | _initpath | |
77546ea5 | 1549 | if ! _exists "crontab" ; then |
1550 | _err "crontab doesn't exist, so, we can not install cron jobs." | |
1551 | _err "All your certs will not be renewed automatically." | |
a7b7355d | 1552 | _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday." |
77546ea5 | 1553 | return 1 |
1554 | fi | |
1555 | ||
4c3b3608 | 1556 | _info "Installing cron job" |
a7b7355d | 1557 | if ! crontab -l | grep "$PROJECT_ENTRY --cron" ; then |
1558 | if [[ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ]] ; then | |
1559 | lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY" | |
4c3b3608 | 1560 | else |
a7b7355d | 1561 | _err "Can not install cronjob, $PROJECT_ENTRY not found." |
4c3b3608 | 1562 | return 1 |
1563 | fi | |
a7b7355d | 1564 | crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab - |
4c3b3608 | 1565 | fi |
a63b05a9 | 1566 | if [[ "$?" != "0" ]] ; then |
4c3b3608 | 1567 | _err "Install cron job failed. You need to manually renew your certs." |
1568 | _err "Or you can add cronjob by yourself:" | |
a7b7355d | 1569 | _err "$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null" |
4c3b3608 | 1570 | return 1 |
1571 | fi | |
1572 | } | |
1573 | ||
1574 | uninstallcronjob() { | |
37db5b81 | 1575 | if ! _exists "crontab" ; then |
1576 | return | |
1577 | fi | |
4c3b3608 | 1578 | _info "Removing cron job" |
a7b7355d | 1579 | cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")" |
a63b05a9 | 1580 | if [[ "$cr" ]] ; then |
a7b7355d | 1581 | crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab - |
1582 | LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')" | |
4c3b3608 | 1583 | _info LE_WORKING_DIR "$LE_WORKING_DIR" |
1584 | fi | |
1585 | _initpath | |
a7b7355d | 1586 | |
4c3b3608 | 1587 | } |
1588 | ||
6cb415f5 | 1589 | revoke() { |
1590 | Le_Domain="$1" | |
a63b05a9 | 1591 | if [[ -z "$Le_Domain" ]] ; then |
a7b7355d | 1592 | echo "Usage: $PROJECT_ENTRY --revoke -d domain.com" |
6cb415f5 | 1593 | return 1 |
1594 | fi | |
1595 | ||
1596 | _initpath $Le_Domain | |
a63b05a9 | 1597 | if [[ ! -f "$DOMAIN_CONF" ]] ; then |
6cb415f5 | 1598 | _err "$Le_Domain is not a issued domain, skip." |
1599 | return 1; | |
1600 | fi | |
1601 | ||
a63b05a9 | 1602 | if [[ ! -f "$CERT_PATH" ]] ; then |
6cb415f5 | 1603 | _err "Cert for $Le_Domain $CERT_PATH is not found, skip." |
1604 | return 1 | |
1605 | fi | |
1606 | ||
1607 | cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}"| tr -d "\r\n" | _urlencode)" | |
1608 | ||
a63b05a9 | 1609 | if [[ -z "$cert" ]] ; then |
6cb415f5 | 1610 | _err "Cert for $Le_Domain is empty found, skip." |
1611 | return 1 | |
1612 | fi | |
1613 | ||
1614 | data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}" | |
1615 | uri="$API/acme/revoke-cert" | |
1616 | ||
1617 | _info "Try domain key first." | |
1618 | if _send_signed_request $uri "$data" "" "$CERT_KEY_PATH"; then | |
a63b05a9 | 1619 | if [[ -z "$response" ]] ; then |
6cb415f5 | 1620 | _info "Revoke success." |
1621 | rm -f $CERT_PATH | |
1622 | return 0 | |
1623 | else | |
1624 | _err "Revoke error by domain key." | |
1625 | _err "$resource" | |
1626 | fi | |
1627 | fi | |
1628 | ||
1629 | _info "Then try account key." | |
1630 | ||
1631 | if _send_signed_request $uri "$data" "" "$ACCOUNT_KEY_PATH" ; then | |
a63b05a9 | 1632 | if [[ -z "$response" ]] ; then |
6cb415f5 | 1633 | _info "Revoke success." |
1634 | rm -f $CERT_PATH | |
1635 | return 0 | |
1636 | else | |
1637 | _err "Revoke error." | |
1638 | _debug "$resource" | |
1639 | fi | |
1640 | fi | |
1641 | return 1 | |
1642 | } | |
4c3b3608 | 1643 | |
1644 | # Detect profile file if not specified as environment variable | |
1645 | _detect_profile() { | |
a63b05a9 | 1646 | if [ -n "$PROFILE" -a -f "$PROFILE" ] ; then |
4c3b3608 | 1647 | echo "$PROFILE" |
1648 | return | |
1649 | fi | |
1650 | ||
1651 | local DETECTED_PROFILE | |
1652 | DETECTED_PROFILE='' | |
1653 | local SHELLTYPE | |
1654 | SHELLTYPE="$(basename "/$SHELL")" | |
1655 | ||
a63b05a9 | 1656 | if [[ "$SHELLTYPE" = "bash" ]] ; then |
1657 | if [[ -f "$HOME/.bashrc" ]] ; then | |
4c3b3608 | 1658 | DETECTED_PROFILE="$HOME/.bashrc" |
a63b05a9 | 1659 | elif [[ -f "$HOME/.bash_profile" ]] ; then |
4c3b3608 | 1660 | DETECTED_PROFILE="$HOME/.bash_profile" |
1661 | fi | |
a63b05a9 | 1662 | elif [[ "$SHELLTYPE" = "zsh" ]] ; then |
4c3b3608 | 1663 | DETECTED_PROFILE="$HOME/.zshrc" |
1664 | fi | |
1665 | ||
a63b05a9 | 1666 | if [[ -z "$DETECTED_PROFILE" ]] ; then |
1667 | if [[ -f "$HOME/.profile" ]] ; then | |
4c3b3608 | 1668 | DETECTED_PROFILE="$HOME/.profile" |
a63b05a9 | 1669 | elif [[ -f "$HOME/.bashrc" ]] ; then |
4c3b3608 | 1670 | DETECTED_PROFILE="$HOME/.bashrc" |
a63b05a9 | 1671 | elif [[ -f "$HOME/.bash_profile" ]] ; then |
4c3b3608 | 1672 | DETECTED_PROFILE="$HOME/.bash_profile" |
a63b05a9 | 1673 | elif [[ -f "$HOME/.zshrc" ]] ; then |
4c3b3608 | 1674 | DETECTED_PROFILE="$HOME/.zshrc" |
1675 | fi | |
1676 | fi | |
1677 | ||
a63b05a9 | 1678 | if [[ ! -z "$DETECTED_PROFILE" ]] ; then |
4c3b3608 | 1679 | echo "$DETECTED_PROFILE" |
1680 | fi | |
1681 | } | |
1682 | ||
1683 | _initconf() { | |
1684 | _initpath | |
a63b05a9 | 1685 | if [[ ! -f "$ACCOUNT_CONF_PATH" ]] ; then |
d53289d7 | 1686 | echo "#ACCOUNT_CONF_PATH=xxxx |
1687 | ||
1688 | #Account configurations: | |
4c3b3608 | 1689 | #Here are the supported macros, uncomment them to make them take effect. |
d53289d7 | 1690 | |
4c3b3608 | 1691 | #ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account. |
5fd3f21b | 1692 | #ACCOUNT_KEY_PATH=\"/path/to/account.key\" |
b2817897 | 1693 | #CERT_HOME=\"/path/to/cert/home\" |
4c3b3608 | 1694 | |
1695 | #STAGE=1 # Use the staging api | |
1696 | #FORCE=1 # Force to issue cert | |
1697 | #DEBUG=1 # Debug mode | |
1698 | ||
166096dc | 1699 | #ACCOUNT_KEY_HASH=account key hash |
1700 | ||
635695ec | 1701 | USER_AGENT=\"$USER_AGENT\" |
281aa349 | 1702 | |
1703 | #USER_PATH="" | |
1704 | ||
4c3b3608 | 1705 | #dns api |
1706 | ####################### | |
1707 | #Cloudflare: | |
1708 | #api key | |
3d49985a | 1709 | #CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\" |
4c3b3608 | 1710 | #account email |
3d49985a | 1711 | #CF_Email=\"xxxx@sss.com\" |
4c3b3608 | 1712 | |
1713 | ####################### | |
1714 | #Dnspod.cn: | |
1715 | #api key id | |
3d49985a | 1716 | #DP_Id=\"1234\" |
4c3b3608 | 1717 | #api key |
3d49985a | 1718 | #DP_Key=\"sADDsdasdgdsf\" |
4c3b3608 | 1719 | |
1720 | ####################### | |
1721 | #Cloudxns.com: | |
3d49985a | 1722 | #CX_Key=\"1234\" |
4c3b3608 | 1723 | # |
3d49985a | 1724 | #CX_Secret=\"sADDsdasdgdsf\" |
4c3b3608 | 1725 | |
1726 | " > $ACCOUNT_CONF_PATH | |
1727 | fi | |
1728 | } | |
1729 | ||
c60883ef | 1730 | _precheck() { |
1731 | if ! _exists "curl" && ! _exists "wget"; then | |
1732 | _err "Please install curl or wget first, we need to access http resources." | |
4c3b3608 | 1733 | return 1 |
1734 | fi | |
1735 | ||
c60883ef | 1736 | if ! _exists "crontab" ; then |
77546ea5 | 1737 | _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'." |
c60883ef | 1738 | _err "We need to set cron job to renew the certs automatically." |
77546ea5 | 1739 | _err "Otherwise, your certs will not be able to be renewed automatically." |
a63b05a9 | 1740 | if [[ -z "$FORCE" ]] ; then |
a7b7355d | 1741 | _err "Please add '--force' and try install again to go without crontab." |
1742 | _err "./$PROJECT_ENTRY --install --force" | |
77546ea5 | 1743 | return 1 |
1744 | fi | |
4c3b3608 | 1745 | fi |
1746 | ||
c60883ef | 1747 | if ! _exists "openssl" ; then |
1748 | _err "Please install openssl first." | |
1749 | _err "We need openssl to generate keys." | |
4c3b3608 | 1750 | return 1 |
1751 | fi | |
1752 | ||
c60883ef | 1753 | if ! _exists "nc" ; then |
1754 | _err "It is recommended to install nc first, try to install 'nc' or 'netcat'." | |
1755 | _err "We use nc for standalone server if you use standalone mode." | |
1756 | _err "If you don't use standalone mode, just ignore this warning." | |
1757 | fi | |
1758 | ||
1759 | return 0 | |
1760 | } | |
1761 | ||
1762 | install() { | |
635695ec | 1763 | |
c60883ef | 1764 | if ! _initpath ; then |
1765 | _err "Install failed." | |
4c3b3608 | 1766 | return 1 |
1767 | fi | |
635695ec | 1768 | |
c60883ef | 1769 | if ! _precheck ; then |
1770 | _err "Pre-check failed, can not install." | |
4c3b3608 | 1771 | return 1 |
1772 | fi | |
c60883ef | 1773 | |
6cc11ffb | 1774 | #convert from le |
1775 | if [[ -d "$HOME/.le" ]] ; then | |
1776 | for envfile in "le.env" "le.sh.env" | |
1777 | do | |
1778 | if [[ -f "$HOME/.le/$envfile" ]] ; then | |
1779 | if grep "le.sh" "$HOME/.le/$envfile" >/dev/null ; then | |
1780 | _upgrading="1" | |
1781 | _info "You are upgrading from le.sh" | |
1782 | _info "Renaming \"$HOME/.le\" to $LE_WORKING_DIR" | |
1783 | mv "$HOME/.le" "$LE_WORKING_DIR" | |
1784 | mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env" | |
1785 | break; | |
1786 | fi | |
1787 | fi | |
1788 | done | |
1789 | fi | |
1790 | ||
4c3b3608 | 1791 | _info "Installing to $LE_WORKING_DIR" |
635695ec | 1792 | |
4a0f23e2 | 1793 | if ! mkdir -p "$LE_WORKING_DIR" ; then |
1794 | _err "Can not craete working dir: $LE_WORKING_DIR" | |
1795 | return 1 | |
1796 | fi | |
1797 | ||
a7b7355d | 1798 | cp $PROJECT_ENTRY "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY" |
4c3b3608 | 1799 | |
a63b05a9 | 1800 | if [[ "$?" != "0" ]] ; then |
a7b7355d | 1801 | _err "Install failed, can not copy $PROJECT_ENTRY" |
4c3b3608 | 1802 | return 1 |
1803 | fi | |
1804 | ||
a7b7355d | 1805 | _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" |
4c3b3608 | 1806 | |
1807 | _profile="$(_detect_profile)" | |
a63b05a9 | 1808 | if [[ "$_profile" ]] ; then |
4c3b3608 | 1809 | _debug "Found profile: $_profile" |
1810 | ||
6cc11ffb | 1811 | _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env" |
1812 | if [[ "$_upgrading" == "1" ]] ; then | |
1813 | echo "$(cat $_envfile)" | sed "s|^LE_WORKING_DIR.*$||" > "$_envfile" | |
1814 | echo "$(cat $_envfile)" | sed "s|^alias le.*$||" > "$_envfile" | |
1815 | echo "$(cat $_envfile)" | sed "s|^alias le.sh.*$||" > "$_envfile" | |
1816 | fi | |
1817 | ||
1818 | _setopt "$_envfile" "LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\"" | |
1819 | _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY\"" | |
1820 | ||
b86869a0 | 1821 | echo "" >> "$_profile" |
a7b7355d | 1822 | _setopt "$_profile" "source \"$LE_WORKING_DIR/$PROJECT_NAME.env\"" |
1823 | _info "OK, Close and reopen your terminal to start using $PROJECT_NAME" | |
4c3b3608 | 1824 | else |
a7b7355d | 1825 | _info "No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME" |
4c3b3608 | 1826 | fi |
1827 | ||
6ed1c718 | 1828 | if [[ -d "dnsapi" ]] ; then |
1829 | mkdir -p $LE_WORKING_DIR/dnsapi | |
1830 | cp dnsapi/* $LE_WORKING_DIR/dnsapi/ | |
1831 | fi | |
4c3b3608 | 1832 | |
1833 | #to keep compatible mv the .acc file to .key file | |
a63b05a9 | 1834 | if [[ -f "$LE_WORKING_DIR/account.acc" ]] ; then |
4c3b3608 | 1835 | mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key" |
1836 | fi | |
d53289d7 | 1837 | |
a63b05a9 | 1838 | if [[ ! -f "$ACCOUNT_CONF_PATH" ]] ; then |
4c3b3608 | 1839 | _initconf |
1840 | fi | |
6cc11ffb | 1841 | |
1842 | if [[ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ]] ; then | |
635695ec | 1843 | _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\"" |
6cc11ffb | 1844 | fi |
1845 | ||
b2817897 | 1846 | if [[ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ]] ; then |
1847 | _saveaccountconf "CERT_HOME" "$CERT_HOME" | |
1848 | fi | |
1849 | ||
1850 | if [[ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ]] ; then | |
1851 | _saveaccountconf "ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH" | |
1852 | fi | |
1853 | ||
d53289d7 | 1854 | installcronjob |
1855 | ||
4c3b3608 | 1856 | _info OK |
1857 | } | |
1858 | ||
1859 | uninstall() { | |
1860 | uninstallcronjob | |
1861 | _initpath | |
1862 | ||
1863 | _profile="$(_detect_profile)" | |
a63b05a9 | 1864 | if [[ "$_profile" ]] ; then |
7203a1c1 | 1865 | text="$(cat $_profile)" |
a7b7355d | 1866 | echo "$text" | sed "s|^source.*$PROJECT_NAME.env.*$||" > "$_profile" |
4c3b3608 | 1867 | fi |
1868 | ||
a7b7355d | 1869 | rm -f $LE_WORKING_DIR/$PROJECT_ENTRY |
4c3b3608 | 1870 | _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself." |
1871 | ||
1872 | } | |
1873 | ||
1874 | cron() { | |
281aa349 | 1875 | IN_CRON=1 |
4c3b3608 | 1876 | renewAll |
281aa349 | 1877 | IN_CRON="" |
4c3b3608 | 1878 | } |
1879 | ||
1880 | version() { | |
a63b05a9 | 1881 | echo "$PROJECT" |
1882 | echo "v$VER" | |
4c3b3608 | 1883 | } |
1884 | ||
1885 | showhelp() { | |
1886 | version | |
a7b7355d | 1887 | echo "Usage: $PROJECT_ENTRY command ...[parameters].... |
a63b05a9 | 1888 | Commands: |
1889 | --help, -h Show this help message. | |
1890 | --version, -v Show version info. | |
a7b7355d | 1891 | --install Install $PROJECT_NAME to your system. |
1892 | --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job. | |
a63b05a9 | 1893 | --issue Issue a cert. |
1894 | --installcert Install the issued cert to apache/nginx or any other server. | |
1895 | --renew, -r Renew a cert. | |
1896 | --renewAll Renew all the certs | |
1897 | --revoke Revoke a cert. | |
1898 | --installcronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job. | |
1899 | --uninstallcronjob Uninstall the cron job. The 'uninstall' command can do this automatically. | |
1900 | --cron Run cron job to renew all the certs. | |
1901 | --toPkcs Export the certificate and key to a pfx file. | |
1902 | --createAccountKey, -cak Create an account private key, professional use. | |
1903 | --createDomainKey, -cdk Create an domain private key, professional use. | |
1904 | --createCSR, -ccsr Create CSR , professional use. | |
1905 | ||
1906 | Parameters: | |
1907 | --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc. | |
1908 | --force, -f Used to force to install or force to renew a cert immediately. | |
1909 | --staging, --test Use staging server, just for test. | |
1910 | --debug Output debug info. | |
1911 | ||
1912 | --webroot, -w /path/to/webroot Specifies the web root folder for web root mode. | |
1913 | --standalone Use standalone mode. | |
1914 | --apache Use apache mode. | |
1915 | --dns [dns-cf|dns-dp|dns-cx|/path/to/api/file] Use dns mode or dns api. | |
1916 | ||
1917 | --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384. | |
1918 | --accountkeylength, -ak [2048] Specifies the account key length. | |
1919 | ||
1920 | These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert: | |
1921 | ||
1922 | --certpath /path/to/real/cert/file After issue/renew, the cert will be copied to this path. | |
1923 | --keypath /path/to/real/key/file After issue/renew, the key will be copied to this path. | |
1924 | --capath /path/to/real/ca/file After issue/renew, the intermediate cert will be copied to this path. | |
1925 | --fullchainpath /path/to/fullchain/file After issue/renew, the fullchain cert will be copied to this path. | |
1926 | ||
1927 | --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server. | |
1928 | ||
1929 | --accountconf Specifies a customized account config file. | |
635695ec | 1930 | --home Specifies the home dir for $PROJECT_NAME . |
b2817897 | 1931 | --certhome Specifies the home dir to save all the certs. |
635695ec | 1932 | --useragent Specifies the user agent string. it will be saved for future use too. |
b5eb4b90 | 1933 | --accountemail Specifies the account email for registering, Only valid for the '--install' command. |
06625071 | 1934 | --accountkey Specifies the account key path, Only valid for the '--install' command. |
1935 | --days Specifies the days to renew the cert when using '--issue' command. The max value is 80 days. | |
a63b05a9 | 1936 | |
4c3b3608 | 1937 | " |
1938 | } | |
1939 | ||
4a0f23e2 | 1940 | _installOnline() { |
1941 | _info "Installing from online archive." | |
a63b05a9 | 1942 | if [[ ! "$BRANCH" ]] ; then |
4a0f23e2 | 1943 | BRANCH="master" |
1944 | fi | |
1945 | _initpath | |
1946 | target="$PROJECT/archive/$BRANCH.tar.gz" | |
1947 | _info "Downloading $target" | |
1948 | localname="$BRANCH.tar.gz" | |
1949 | if ! _get "$target" > $localname ; then | |
1950 | _debug "Download error." | |
1951 | return 1 | |
1952 | fi | |
1953 | _info "Extracting $localname" | |
1954 | tar xzf $localname | |
6cc11ffb | 1955 | cd "$PROJECT_NAME-$BRANCH" |
a7b7355d | 1956 | chmod +x $PROJECT_ENTRY |
1957 | if ./$PROJECT_ENTRY install ; then | |
4a0f23e2 | 1958 | _info "Install success!" |
1959 | fi | |
1960 | ||
1961 | cd .. | |
6cc11ffb | 1962 | rm -rf "$PROJECT_NAME-$BRANCH" |
4a0f23e2 | 1963 | rm -f "$localname" |
1964 | } | |
1965 | ||
a63b05a9 | 1966 | |
1967 | _process() { | |
1968 | _CMD="" | |
1969 | _domain="" | |
1970 | _altdomains="no" | |
1971 | _webroot="" | |
1972 | _keylength="no" | |
1973 | _accountkeylength="no" | |
1974 | _certpath="no" | |
1975 | _keypath="no" | |
1976 | _capath="no" | |
1977 | _fullchainpath="no" | |
1978 | _reloadcmd="no" | |
1979 | _password="" | |
635695ec | 1980 | _accountconf="" |
1981 | _useragent="" | |
b5eb4b90 | 1982 | _accountemail="" |
1983 | _accountkey="" | |
b2817897 | 1984 | _certhome="" |
a63b05a9 | 1985 | while (( ${#} )); do |
1986 | case "${1}" in | |
1987 | ||
1988 | --help|-h) | |
1989 | showhelp | |
1990 | return | |
1991 | ;; | |
1992 | --version|-v) | |
1993 | version | |
1994 | return | |
1995 | ;; | |
1996 | --install) | |
1997 | _CMD="install" | |
1998 | ;; | |
1999 | --uninstall) | |
2000 | _CMD="uninstall" | |
2001 | ;; | |
2002 | --issue) | |
2003 | _CMD="issue" | |
2004 | ;; | |
2005 | --installcert|-i) | |
2006 | _CMD="installcert" | |
2007 | ;; | |
2008 | --renew|-r) | |
2009 | _CMD="renew" | |
2010 | ;; | |
2011 | --renewAll|-renewall) | |
2012 | _CMD="renewAll" | |
2013 | ;; | |
2014 | --revoke) | |
2015 | _CMD="revoke" | |
2016 | ;; | |
2017 | --installcronjob) | |
2018 | _CMD="installcronjob" | |
2019 | ;; | |
2020 | --uninstallcronjob) | |
2021 | _CMD="uninstallcronjob" | |
2022 | ;; | |
2023 | --cron) | |
2024 | _CMD="cron" | |
2025 | ;; | |
2026 | --toPkcs) | |
2027 | _CMD="toPkcs" | |
2028 | ;; | |
2029 | --createAccountKey|--createaccountkey|-cak) | |
2030 | _CMD="createAccountKey" | |
2031 | ;; | |
2032 | --createDomainKey|--createdomainkey|-cdk) | |
2033 | _CMD="createDomainKey" | |
2034 | ;; | |
2035 | --createCSR|--createcsr|-ccr) | |
2036 | _CMD="createCSR" | |
2037 | ;; | |
2038 | ||
2039 | ||
2040 | --domain|-d) | |
2041 | _dvalue="$2" | |
2042 | ||
2043 | if [[ -z "$_dvalue" ]] || [[ "$_dvalue" == "-"* ]] ; then | |
2044 | _err "'$_dvalue' is not a valid domain for parameter '$1'" | |
2045 | return 1 | |
2046 | fi | |
2047 | ||
2048 | if [[ -z "$_domain" ]] ; then | |
2049 | _domain="$_dvalue" | |
2050 | else | |
2051 | if [[ "$_altdomains" == "no" ]] ; then | |
2052 | _altdomains="$_dvalue" | |
2053 | else | |
2054 | _altdomains="$_altdomains,$_dvalue" | |
2055 | fi | |
2056 | fi | |
2057 | shift | |
2058 | ;; | |
2059 | ||
2060 | --force|-f) | |
2061 | FORCE="1" | |
2062 | ;; | |
2063 | --staging|--test) | |
2064 | STAGE="1" | |
2065 | ;; | |
2066 | --debug) | |
6fc1447f | 2067 | if [[ "$2" == "-"* ]] || [[ -z "$2" ]]; then |
a63b05a9 | 2068 | DEBUG="1" |
2069 | else | |
2070 | DEBUG="$2" | |
2071 | shift | |
6fc1447f | 2072 | fi |
a63b05a9 | 2073 | ;; |
a63b05a9 | 2074 | --webroot|-w) |
2075 | wvalue="$2" | |
2076 | if [[ -z "$_webroot" ]] ; then | |
2077 | _webroot="$wvalue" | |
2078 | else | |
2079 | _webroot="$_webroot,$wvalue" | |
2080 | fi | |
2081 | shift | |
2082 | ;; | |
2083 | --standalone) | |
2084 | wvalue="no" | |
2085 | if [[ -z "$_webroot" ]] ; then | |
2086 | _webroot="$wvalue" | |
2087 | else | |
2088 | _webroot="$_webroot,$wvalue" | |
2089 | fi | |
2090 | ;; | |
2091 | --apache) | |
2092 | wvalue="apache" | |
2093 | if [[ -z "$_webroot" ]] ; then | |
2094 | _webroot="$wvalue" | |
2095 | else | |
2096 | _webroot="$_webroot,$wvalue" | |
2097 | fi | |
2098 | ;; | |
2099 | --dns) | |
2100 | wvalue="dns" | |
2101 | if [[ "$2" != "-"* ]] ; then | |
2102 | wvalue="$2" | |
2103 | shift | |
2104 | fi | |
2105 | if [[ -z "$_webroot" ]] ; then | |
2106 | _webroot="$wvalue" | |
2107 | else | |
2108 | _webroot="$_webroot,$wvalue" | |
2109 | fi | |
2110 | ;; | |
2111 | --keylength|-k) | |
2112 | _keylength="$2" | |
2113 | accountkeylength="$2" | |
2114 | shift | |
2115 | ;; | |
2116 | --accountkeylength|-ak) | |
2117 | accountkeylength="$2" | |
2118 | shift | |
2119 | ;; | |
2120 | ||
2121 | --certpath) | |
2122 | _certpath="$2" | |
2123 | shift | |
2124 | ;; | |
2125 | --keypath) | |
2126 | _keypath="$2" | |
2127 | shift | |
2128 | ;; | |
2129 | --capath) | |
2130 | _capath="$2" | |
2131 | shift | |
2132 | ;; | |
2133 | --fullchainpath) | |
2134 | _fullchainpath="$2" | |
2135 | shift | |
2136 | ;; | |
635695ec | 2137 | --reloadcmd|--reloadCmd) |
a63b05a9 | 2138 | _reloadcmd="$2" |
2139 | shift | |
2140 | ;; | |
2141 | --password) | |
2142 | _password="$2" | |
2143 | shift | |
2144 | ;; | |
2145 | --accountconf) | |
635695ec | 2146 | _accountconf="$2" |
2147 | ACCOUNT_CONF_PATH="$_accountconf" | |
a7b7355d | 2148 | shift |
a63b05a9 | 2149 | ;; |
a7b7355d | 2150 | --home) |
a63b05a9 | 2151 | LE_WORKING_DIR="$2" |
a7b7355d | 2152 | shift |
a63b05a9 | 2153 | ;; |
b2817897 | 2154 | --certhome) |
2155 | _certhome="$2" | |
2156 | CERT_HOME="$_certhome" | |
2157 | shift | |
2158 | ;; | |
635695ec | 2159 | --useragent) |
2160 | _useragent="$2" | |
2161 | USER_AGENT="$_useragent" | |
2162 | shift | |
2163 | ;; | |
b5eb4b90 | 2164 | --accountemail ) |
2165 | _accountemail="$2" | |
2166 | ACCOUNT_EMAIL="$_accountemail" | |
2167 | shift | |
2168 | ;; | |
2169 | --accountkey ) | |
2170 | _accountkey="$2" | |
2171 | ACCOUNT_KEY_PATH="$_accountkey" | |
2172 | shift | |
2173 | ;; | |
06625071 | 2174 | --days ) |
2175 | _days="$2" | |
2176 | Le_RenewalDays="$_days" | |
2177 | shift | |
2178 | ;; | |
a63b05a9 | 2179 | *) |
2180 | _err "Unknown parameter : $1" | |
2181 | return 1 | |
2182 | ;; | |
2183 | esac | |
2184 | ||
2185 | shift 1 | |
2186 | done | |
2187 | ||
2188 | ||
2189 | case "${_CMD}" in | |
2190 | install) install ;; | |
2191 | uninstall) uninstall ;; | |
2192 | issue) | |
70a55875 | 2193 | issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" |
a63b05a9 | 2194 | ;; |
2195 | installcert) | |
3ed4102a | 2196 | installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" |
a63b05a9 | 2197 | ;; |
2198 | renew) | |
2199 | renew "$_domain" | |
2200 | ;; | |
2201 | renewAll) | |
2202 | renewAll | |
2203 | ;; | |
2204 | revoke) | |
2205 | revoke "$_domain" | |
2206 | ;; | |
2207 | installcronjob) installcronjob ;; | |
2208 | uninstallcronjob) uninstallcronjob ;; | |
2209 | cron) cron ;; | |
2210 | toPkcs) | |
2211 | toPkcs "$_domain" "$_password" | |
2212 | ;; | |
2213 | createAccountKey) | |
2214 | createAccountKey "$_domain" "$_accountkeylength" | |
2215 | ;; | |
2216 | createDomainKey) | |
2217 | createDomainKey "$_domain" "$_keylength" | |
2218 | ;; | |
2219 | createCSR) | |
2220 | createCSR "$_domain" "$_altdomains" | |
2221 | ;; | |
2222 | ||
2223 | *) | |
2224 | _err "Invalid command: $_CMD" | |
2225 | showhelp; | |
2226 | return 1 | |
2227 | ;; | |
2228 | esac | |
2229 | ||
635695ec | 2230 | if [[ "$_useragent" ]] ; then |
2231 | _saveaccountconf "USER_AGENT" "$_useragent" | |
2232 | fi | |
b5eb4b90 | 2233 | if [[ "$_accountemail" ]] ; then |
2234 | _saveaccountconf "ACCOUNT_EMAIL" "$_accountemail" | |
2235 | fi | |
b2817897 | 2236 | |
635695ec | 2237 | |
a63b05a9 | 2238 | } |
2239 | ||
2240 | ||
2241 | if [[ "$INSTALLONLINE" ]] ; then | |
d1f97fc8 | 2242 | INSTALLONLINE="" |
4a0f23e2 | 2243 | _installOnline $BRANCH |
2244 | exit | |
2245 | fi | |
4c3b3608 | 2246 | |
a63b05a9 | 2247 | if [[ -z "$1" ]] ; then |
4c3b3608 | 2248 | showhelp |
2249 | else | |
a63b05a9 | 2250 | if [[ "$1" == "-"* ]] ; then |
2251 | _process "$@" | |
2252 | else | |
2253 | "$@" | |
2254 | fi | |
4c3b3608 | 2255 | fi |
a63b05a9 | 2256 | |
2257 |