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