You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1450 lines
36 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. #!/usr/bin/env bash
  2. VER=1.1.9
  3. PROJECT="https://github.com/Neilpang/le"
  4. DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
  5. DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
  6. STAGE_CA="https://acme-staging.api.letsencrypt.org"
  7. VTYPE_HTTP="http-01"
  8. VTYPE_DNS="dns-01"
  9. if [ -z "$AGREEMENT" ] ; then
  10. AGREEMENT="$DEFAULT_AGREEMENT"
  11. fi
  12. _debug() {
  13. if [ -z "$DEBUG" ] ; then
  14. return
  15. fi
  16. if [ -z "$2" ] ; then
  17. echo $1
  18. else
  19. echo "$1"="$2"
  20. fi
  21. }
  22. _info() {
  23. if [ -z "$2" ] ; then
  24. echo "$1"
  25. else
  26. echo "$1"="$2"
  27. fi
  28. }
  29. _err() {
  30. if [ -z "$2" ] ; then
  31. echo "$1" >&2
  32. else
  33. echo "$1"="$2" >&2
  34. fi
  35. return 1
  36. }
  37. _h2b() {
  38. hex=$(cat)
  39. i=1
  40. j=2
  41. while [ '1' ] ; do
  42. h=$(printf $hex | cut -c $i-$j)
  43. if [ -z "$h" ] ; then
  44. break;
  45. fi
  46. printf "\x$h"
  47. let "i+=2"
  48. let "j+=2"
  49. done
  50. }
  51. _base64() {
  52. openssl base64 -e | tr -d '\n'
  53. }
  54. _ss() {
  55. _port="$1"
  56. if command -v "netstat" >/dev/null 2>&1 ; then
  57. _debug "Using: netstat"
  58. netstat -ntpl | grep :$_port" "
  59. return 0
  60. fi
  61. if command -v "ss" >/dev/null 2>&1 ; then
  62. _debug "Using: ss"
  63. ss -ntpl | grep :$_port" "
  64. return 0
  65. fi
  66. return 1
  67. }
  68. #domain [2048]
  69. createAccountKey() {
  70. _info "Creating account key"
  71. if [ -z "$1" ] ; then
  72. echo Usage: createAccountKey account-domain [2048]
  73. return
  74. fi
  75. account=$1
  76. length=$2
  77. if [[ "$length" == "ec-"* ]] ; then
  78. length=2048
  79. fi
  80. if [ -z "$2" ] ; then
  81. _info "Use default length 2048"
  82. length=2048
  83. fi
  84. _initpath
  85. if [ -f "$ACCOUNT_KEY_PATH" ] ; then
  86. _info "Account key exists, skip"
  87. return
  88. else
  89. #generate account key
  90. openssl genrsa $length 2>/dev/null > "$ACCOUNT_KEY_PATH"
  91. fi
  92. }
  93. #domain length
  94. createDomainKey() {
  95. _info "Creating domain key"
  96. if [ -z "$1" ] ; then
  97. echo Usage: createDomainKey domain [2048]
  98. return
  99. fi
  100. domain=$1
  101. length=$2
  102. isec=""
  103. if [[ "$length" == "ec-"* ]] ; then
  104. isec="1"
  105. length=$(printf $length | cut -d '-' -f 2-100)
  106. eccname="$length"
  107. fi
  108. if [ -z "$length" ] ; then
  109. if [ "$isec" ] ; then
  110. length=256
  111. else
  112. length=2048
  113. fi
  114. fi
  115. _info "Use length $length"
  116. if [ "$isec" ] ; then
  117. if [ "$length" == "256" ] ; then
  118. eccname="prime256v1"
  119. fi
  120. if [ "$length" == "384" ] ; then
  121. eccname="secp384r1"
  122. fi
  123. if [ "$length" == "521" ] ; then
  124. eccname="secp521r1"
  125. fi
  126. _info "Using ec name: $eccname"
  127. fi
  128. _initpath $domain
  129. if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
  130. #generate account key
  131. if [ "$isec" ] ; then
  132. openssl ecparam -name $eccname -genkey 2>/dev/null > "$CERT_KEY_PATH"
  133. else
  134. openssl genrsa $length 2>/dev/null > "$CERT_KEY_PATH"
  135. fi
  136. else
  137. if [ "$IS_RENEW" ] ; then
  138. _info "Domain key exists, skip"
  139. return 0
  140. else
  141. _err "Domain key exists, do you want to overwrite the key?"
  142. _err "Set FORCE=1, and try again."
  143. return 1
  144. fi
  145. fi
  146. }
  147. # domain domainlist
  148. createCSR() {
  149. _info "Creating csr"
  150. if [ -z "$1" ] ; then
  151. echo Usage: $0 domain [domainlist]
  152. return
  153. fi
  154. domain=$1
  155. _initpath $domain
  156. domainlist=$2
  157. if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && ! [ "$FORCE" ]; then
  158. _info "CSR exists, skip"
  159. return
  160. fi
  161. if [ -z "$domainlist" ] ; then
  162. #single domain
  163. _info "Single domain" $domain
  164. printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n" > "$DOMAIN_SSL_CONF"
  165. openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
  166. else
  167. alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
  168. #multi
  169. _info "Multi domain" "$alt"
  170. printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n[SAN]\nsubjectAltName=$alt" > "$DOMAIN_SSL_CONF"
  171. openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -reqexts SAN -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
  172. fi
  173. }
  174. _urlencode() {
  175. __n=$(cat)
  176. echo $__n | tr '/+' '_-' | tr -d '= '
  177. }
  178. _time2str() {
  179. #BSD
  180. if date -u -d@$1 2>/dev/null ; then
  181. return
  182. fi
  183. #Linux
  184. if date -u -r $1 2>/dev/null ; then
  185. return
  186. fi
  187. }
  188. _stat() {
  189. #Linux
  190. if stat -c '%U:%G' "$1" 2>/dev/null ; then
  191. return
  192. fi
  193. #BSD
  194. if stat -f '%Su:%Sg' "$1" 2>/dev/null ; then
  195. return
  196. fi
  197. }
  198. #keyfile
  199. _calcjwk() {
  200. keyfile="$1"
  201. if [ -z "$keyfile" ] ; then
  202. _err "Usage: _calcjwk keyfile"
  203. return 1
  204. fi
  205. EC_SIGN=""
  206. if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
  207. _debug "RSA key"
  208. pub_exp=$(openssl rsa -in $keyfile -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
  209. if [ "${#pub_exp}" == "5" ] ; then
  210. pub_exp=0$pub_exp
  211. fi
  212. _debug pub_exp "$pub_exp"
  213. e=$(echo $pub_exp | _h2b | _base64)
  214. _debug e "$e"
  215. modulus=$(openssl rsa -in $keyfile -modulus -noout | cut -d '=' -f 2 )
  216. n=$(echo $modulus| _h2b | _base64 | _urlencode )
  217. jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
  218. _debug jwk "$jwk"
  219. HEADER='{"alg": "RS256", "jwk": '$jwk'}'
  220. HEADERPLACE='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
  221. elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
  222. _debug "EC key"
  223. EC_SIGN="1"
  224. crv="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
  225. _debug crv $crv
  226. pubi="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
  227. _debug pubi $pubi
  228. let "pubi=pubi+1"
  229. pubj="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
  230. _debug pubj $pubj
  231. let "pubj=pubj-1"
  232. pubtext="$(openssl ec -in $keyfile -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
  233. _debug pubtext "$pubtext"
  234. xlen="$(printf "$pubtext" | tr -d ':' | wc -c)"
  235. let "xlen=xlen/4"
  236. _debug xlen $xlen
  237. let "xend=xlen+1"
  238. x="$(printf $pubtext | cut -d : -f 2-$xend)"
  239. _debug x $x
  240. x64="$(printf $x | tr -d : | _h2b | _base64 | _urlencode)"
  241. _debug x64 $x64
  242. let "xend+=1"
  243. y="$(printf $pubtext | cut -d : -f $xend-10000)"
  244. _debug y $y
  245. y64="$(printf $y | tr -d : | _h2b | _base64 | _urlencode)"
  246. _debug y64 $y64
  247. jwk='{"kty": "EC", "crv": "'$crv'", "x": "'$x64'", "y": "'$y64'"}'
  248. _debug jwk "$jwk"
  249. HEADER='{"alg": "ES256", "jwk": '$jwk'}'
  250. HEADERPLACE='{"nonce": "NONCE", "alg": "ES256", "jwk": '$jwk'}'
  251. else
  252. _err "Only RSA or EC key is supported."
  253. return 1
  254. fi
  255. _debug HEADER "$HEADER"
  256. }
  257. # url payload needbase64 keyfile
  258. _send_signed_request() {
  259. url=$1
  260. payload=$2
  261. needbase64=$3
  262. keyfile=$4
  263. if [ -z "$keyfile" ] ; then
  264. keyfile="$ACCOUNT_KEY_PATH"
  265. fi
  266. _debug url $url
  267. _debug payload "$payload"
  268. if ! _calcjwk "$keyfile" ; then
  269. return 1
  270. fi
  271. CURL_HEADER="$LE_WORKING_DIR/curl.header"
  272. dp="$LE_WORKING_DIR/curl.dump"
  273. CURL="curl --silent --dump-header $CURL_HEADER "
  274. if [ "$DEBUG" ] ; then
  275. CURL="$CURL --trace-ascii $dp "
  276. fi
  277. payload64=$(echo -n $payload | _base64 | _urlencode)
  278. _debug payload64 $payload64
  279. nonceurl="$API/directory"
  280. nonce="$($CURL -I $nonceurl | grep -o "^Replay-Nonce:.*$" | tr -d "\r\n" | cut -d ' ' -f 2)"
  281. _debug nonce "$nonce"
  282. protected="$(printf "$HEADERPLACE" | sed "s/NONCE/$nonce/" )"
  283. _debug protected "$protected"
  284. protected64="$(printf "$protected" | _base64 | _urlencode)"
  285. _debug protected64 "$protected64"
  286. sig=$(echo -n "$protected64.$payload64" | openssl dgst -sha256 -sign "$keyfile" | _base64 | _urlencode)
  287. _debug sig "$sig"
  288. body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
  289. _debug body "$body"
  290. if [ "$needbase64" ] ; then
  291. response="$($CURL -X POST --data "$body" $url | _base64)"
  292. else
  293. response="$($CURL -X POST --data "$body" $url)"
  294. fi
  295. responseHeaders="$(cat $CURL_HEADER)"
  296. _debug responseHeaders "$responseHeaders"
  297. _debug response "$response"
  298. code="$(grep ^HTTP $CURL_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
  299. _debug code $code
  300. }
  301. _get() {
  302. url="$1"
  303. _debug url $url
  304. response="$(curl --silent $url)"
  305. ret=$?
  306. _debug response "$response"
  307. code="$(echo $response | grep -o '"status":[0-9]\+' | cut -d : -f 2)"
  308. _debug code $code
  309. return $ret
  310. }
  311. #setopt "file" "opt" "=" "value" [";"]
  312. _setopt() {
  313. __conf="$1"
  314. __opt="$2"
  315. __sep="$3"
  316. __val="$4"
  317. __end="$5"
  318. if [ -z "$__opt" ] ; then
  319. echo usage: _setopt '"file" "opt" "=" "value" [";"]'
  320. return
  321. fi
  322. if [ ! -f "$__conf" ] ; then
  323. touch "$__conf"
  324. fi
  325. if grep -H -n "^$__opt$__sep" "$__conf" > /dev/null ; then
  326. _debug OK
  327. if [[ "$__val" == *"&"* ]] ; then
  328. __val="$(echo $__val | sed 's/&/\\&/g')"
  329. fi
  330. text="$(cat $__conf)"
  331. echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
  332. elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev/null ; then
  333. if [[ "$__val" == *"&"* ]] ; then
  334. __val="$(echo $__val | sed 's/&/\\&/g')"
  335. fi
  336. text="$(cat $__conf)"
  337. echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
  338. else
  339. _debug APP
  340. echo "$__opt$__sep$__val$__end" >> "$__conf"
  341. fi
  342. _debug "$(grep -H -n "^$__opt$__sep" $__conf)"
  343. }
  344. #_savedomainconf key value
  345. #save to domain.conf
  346. _savedomainconf() {
  347. key="$1"
  348. value="$2"
  349. if [ "$DOMAIN_CONF" ] ; then
  350. _setopt $DOMAIN_CONF "$key" "=" "$value"
  351. else
  352. _err "DOMAIN_CONF is empty, can not save $key=$value"
  353. fi
  354. }
  355. #_saveaccountconf key value
  356. _saveaccountconf() {
  357. key="$1"
  358. value="$2"
  359. if [ "$ACCOUNT_CONF_PATH" ] ; then
  360. _setopt $ACCOUNT_CONF_PATH "$key" "=" "$value"
  361. else
  362. _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
  363. fi
  364. }
  365. _startserver() {
  366. content="$1"
  367. nchelp="$(nc -h 2>&1)"
  368. if echo "$nchelp" | grep "\-q " >/dev/null ; then
  369. _NC="nc -q 1 -l"
  370. else
  371. if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null ; then
  372. _NC="nc -c -l"
  373. else
  374. _NC="nc -l"
  375. fi
  376. fi
  377. _debug "_NC" "$_NC"
  378. # while true ; do
  379. if [ "$DEBUG" ] ; then
  380. if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort -vv ; then
  381. echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort -vv ;
  382. fi
  383. else
  384. if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1; then
  385. echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1
  386. fi
  387. fi
  388. if [ "$?" != "0" ] ; then
  389. _err "nc listen error."
  390. return 1
  391. fi
  392. # done
  393. }
  394. _stopserver() {
  395. pid="$1"
  396. }
  397. _initpath() {
  398. if [ -z "$LE_WORKING_DIR" ]; then
  399. LE_WORKING_DIR=$HOME/.le
  400. fi
  401. if [ -z "$ACCOUNT_CONF_PATH" ] ; then
  402. ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
  403. fi
  404. if [ -f "$ACCOUNT_CONF_PATH" ] ; then
  405. source "$ACCOUNT_CONF_PATH"
  406. fi
  407. if [ -z "$API" ] ; then
  408. if [ -z "$STAGE" ] ; then
  409. API="$DEFAULT_CA"
  410. else
  411. API="$STAGE_CA"
  412. _info "Using stage api:$API"
  413. fi
  414. fi
  415. if [ -z "$ACME_DIR" ] ; then
  416. ACME_DIR="/home/.acme"
  417. fi
  418. if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
  419. APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR/"
  420. fi
  421. domain="$1"
  422. if ! mkdir -p "$LE_WORKING_DIR" ; then
  423. _err "Can not craete working dir: $LE_WORKING_DIR"
  424. return 1
  425. fi
  426. if [ -z "$ACCOUNT_KEY_PATH" ] ; then
  427. ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
  428. fi
  429. if [ -z "$domain" ] ; then
  430. return 0
  431. fi
  432. domainhome="$LE_WORKING_DIR/$domain"
  433. mkdir -p "$domainhome"
  434. if [ -z "$DOMAIN_PATH" ] ; then
  435. DOMAIN_PATH="$domainhome"
  436. fi
  437. if [ -z "$DOMAIN_CONF" ] ; then
  438. DOMAIN_CONF="$domainhome/$domain.conf"
  439. fi
  440. if [ -z "$DOMAIN_SSL_CONF" ] ; then
  441. DOMAIN_SSL_CONF="$domainhome/$domain.ssl.conf"
  442. fi
  443. if [ -z "$CSR_PATH" ] ; then
  444. CSR_PATH="$domainhome/$domain.csr"
  445. fi
  446. if [ -z "$CERT_KEY_PATH" ] ; then
  447. CERT_KEY_PATH="$domainhome/$domain.key"
  448. fi
  449. if [ -z "$CERT_PATH" ] ; then
  450. CERT_PATH="$domainhome/$domain.cer"
  451. fi
  452. if [ -z "$CA_CERT_PATH" ] ; then
  453. CA_CERT_PATH="$domainhome/ca.cer"
  454. fi
  455. if [ -z "$CERT_FULLCHAIN_PATH" ] ; then
  456. CERT_FULLCHAIN_PATH="$domainhome/fullchain.cer"
  457. fi
  458. }
  459. _apachePath() {
  460. httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
  461. httpdconfname="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
  462. httpdconf="$httpdroot/$httpdconfname"
  463. if [ ! -f $httpdconf ] ; then
  464. _err "Apache Config file not found" $httpdconf
  465. return 1
  466. fi
  467. return 0
  468. }
  469. _restoreApache() {
  470. if [ -z "$usingApache" ] ; then
  471. return 0
  472. fi
  473. _initpath
  474. if ! _apachePath ; then
  475. return 1
  476. fi
  477. if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
  478. _debug "No config file to restore."
  479. return 0
  480. fi
  481. cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
  482. if ! apachectl -t ; then
  483. _err "Sorry, restore apache config error, please contact me."
  484. return 1;
  485. fi
  486. rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
  487. return 0
  488. }
  489. _setApache() {
  490. _initpath
  491. if ! _apachePath ; then
  492. return 1
  493. fi
  494. #backup the conf
  495. _debug "Backup apache config file" $httpdconf
  496. cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
  497. _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
  498. _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
  499. _info "The backup file will be deleted on sucess, just forget it."
  500. #add alias
  501. echo "
  502. Alias /.well-known/acme-challenge $ACME_DIR
  503. <Directory $ACME_DIR >
  504. Require all granted
  505. </Directory>
  506. " >> $httpdconf
  507. if ! apachectl -t ; then
  508. _err "Sorry, apache config error, please contact me."
  509. _restoreApache
  510. return 1;
  511. fi
  512. if [ ! -d "$ACME_DIR" ] ; then
  513. mkdir -p "$ACME_DIR"
  514. chmod 755 "$ACME_DIR"
  515. fi
  516. if ! apachectl graceful ; then
  517. _err "Sorry, apachectl graceful error, please contact me."
  518. _restoreApache
  519. return 1;
  520. fi
  521. usingApache="1"
  522. return 0
  523. }
  524. _clearup () {
  525. _stopserver $serverproc
  526. serverproc=""
  527. _restoreApache
  528. }
  529. # webroot removelevel tokenfile
  530. _clearupwebbroot() {
  531. __webroot="$1"
  532. if [ -z "$__webroot" ] ; then
  533. _debug "no webroot specified, skip"
  534. return 0
  535. fi
  536. if [ "$2" == '1' ] ; then
  537. _debug "remove $__webroot/.well-known"
  538. rm -rf "$__webroot/.well-known"
  539. elif [ "$2" == '2' ] ; then
  540. _debug "remove $__webroot/.well-known/acme-challenge"
  541. rm -rf "$__webroot/.well-known/acme-challenge"
  542. elif [ "$2" == '3' ] ; then
  543. _debug "remove $__webroot/.well-known/acme-challenge/$3"
  544. rm -rf "$__webroot/.well-known/acme-challenge/$3"
  545. else
  546. _info "Skip for removelevel:$2"
  547. fi
  548. return 0
  549. }
  550. issue() {
  551. if [ -z "$2" ] ; then
  552. _err "Usage: le issue webroot|no|apache|dns a.com [www.a.com,b.com,c.com]|no [key-length]|no"
  553. return 1
  554. fi
  555. Le_Webroot="$1"
  556. Le_Domain="$2"
  557. Le_Alt="$3"
  558. Le_Keylength="$4"
  559. Le_RealCertPath="$5"
  560. Le_RealKeyPath="$6"
  561. Le_RealCACertPath="$7"
  562. Le_ReloadCmd="$8"
  563. _initpath $Le_Domain
  564. if [ -f "$DOMAIN_CONF" ] ; then
  565. Le_NextRenewTime=$(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut -d '=' -f 2)
  566. if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
  567. _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)"
  568. return 2
  569. fi
  570. fi
  571. if [ "$Le_Alt" == "no" ] ; then
  572. Le_Alt=""
  573. fi
  574. if [ "$Le_Keylength" == "no" ] ; then
  575. Le_Keylength=""
  576. fi
  577. if [ "$Le_RealCertPath" == "no" ] ; then
  578. Le_RealCertPath=""
  579. fi
  580. if [ "$Le_RealKeyPath" == "no" ] ; then
  581. Le_RealKeyPath=""
  582. fi
  583. if [ "$Le_RealCACertPath" == "no" ] ; then
  584. Le_RealCACertPath=""
  585. fi
  586. if [ "$Le_ReloadCmd" == "no" ] ; then
  587. Le_ReloadCmd=""
  588. fi
  589. _setopt "$DOMAIN_CONF" "Le_Domain" "=" "$Le_Domain"
  590. _setopt "$DOMAIN_CONF" "Le_Alt" "=" "$Le_Alt"
  591. _setopt "$DOMAIN_CONF" "Le_Webroot" "=" "$Le_Webroot"
  592. _setopt "$DOMAIN_CONF" "Le_Keylength" "=" "$Le_Keylength"
  593. _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
  594. _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
  595. _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
  596. _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
  597. if [ "$Le_Webroot" == "no" ] ; then
  598. _info "Standalone mode."
  599. if ! command -v "nc" > /dev/null ; then
  600. _err "Please install netcat(nc) tools first."
  601. return 1
  602. fi
  603. if [ -z "$Le_HTTPPort" ] ; then
  604. Le_HTTPPort=80
  605. fi
  606. _setopt "$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort"
  607. netprc="$(_ss "$Le_HTTPPort" | grep "$Le_HTTPPort")"
  608. if [ "$netprc" ] ; then
  609. _err "$netprc"
  610. _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
  611. _err "Please stop it first"
  612. return 1
  613. fi
  614. fi
  615. if [ "$Le_Webroot" == "apache" ] ; then
  616. if ! _setApache ; then
  617. _err "set up apache error. Report error to me."
  618. return 1
  619. fi
  620. wellknown_path="$ACME_DIR"
  621. else
  622. usingApache=""
  623. fi
  624. createAccountKey $Le_Domain $Le_Keylength
  625. if ! _calcjwk "$ACCOUNT_KEY_PATH" ; then
  626. return 1
  627. fi
  628. accountkey_json=$(echo -n "$jwk" | tr -d ' ' )
  629. thumbprint=$(echo -n "$accountkey_json" | openssl dgst -sha256 -binary | _base64 | _urlencode)
  630. accountkeyhash="$(cat "$ACCOUNT_KEY_PATH" | openssl dgst -sha256 -binary | _base64)"
  631. if [ "$accountkeyhash" != "$ACCOUNT_KEY_HASH" ] ; then
  632. _info "Registering account"
  633. regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
  634. if [ "$ACCOUNT_EMAIL" ] ; then
  635. regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
  636. fi
  637. _send_signed_request "$API/acme/new-reg" "$regjson"
  638. if [ "$code" == "" ] || [ "$code" == '201' ] ; then
  639. _info "Registered"
  640. echo $response > $LE_WORKING_DIR/account.json
  641. elif [ "$code" == '409' ] ; then
  642. _info "Already registered"
  643. else
  644. _err "Register account Error: $response"
  645. _clearup
  646. return 1
  647. fi
  648. ACCOUNT_KEY_HASH="$accountkeyhash"
  649. _saveaccountconf "ACCOUNT_KEY_HASH" "$ACCOUNT_KEY_HASH"
  650. else
  651. _info "Skip register account key"
  652. fi
  653. if ! createDomainKey $Le_Domain $Le_Keylength ; then
  654. _err "Create domain key error."
  655. return 1
  656. fi
  657. if ! createCSR $Le_Domain $Le_Alt ; then
  658. _err "Create CSR error."
  659. return 1
  660. fi
  661. vtype="$VTYPE_HTTP"
  662. if [[ "$Le_Webroot" == "dns"* ]] ; then
  663. vtype="$VTYPE_DNS"
  664. fi
  665. vlist="$Le_Vlist"
  666. # verify each domain
  667. _info "Verify each domain"
  668. sep='#'
  669. if [ -z "$vlist" ] ; then
  670. alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
  671. for d in $alldomains
  672. do
  673. _info "Getting token for domain" $d
  674. _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
  675. if [ ! -z "$code" ] && [ ! "$code" == '201' ] ; then
  676. _err "new-authz error: $response"
  677. _clearup
  678. return 1
  679. fi
  680. entry="$(printf $response | egrep -o '\{[^{]*"type":"'$vtype'"[^}]*')"
  681. _debug entry "$entry"
  682. token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
  683. _debug token $token
  684. uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
  685. _debug uri $uri
  686. keyauthorization="$token.$thumbprint"
  687. _debug keyauthorization "$keyauthorization"
  688. dvlist="$d$sep$keyauthorization$sep$uri"
  689. _debug dvlist "$dvlist"
  690. vlist="$vlist$dvlist,"
  691. done
  692. #add entry
  693. dnsadded=""
  694. ventries=$(echo "$vlist" | tr ',' ' ' )
  695. for ventry in $ventries
  696. do
  697. d=$(echo $ventry | cut -d $sep -f 1)
  698. keyauthorization=$(echo $ventry | cut -d $sep -f 2)
  699. if [ "$vtype" == "$VTYPE_DNS" ] ; then
  700. dnsadded='0'
  701. txtdomain="_acme-challenge.$d"
  702. _debug txtdomain "$txtdomain"
  703. txt="$(echo -e -n $keyauthorization | openssl dgst -sha256 -binary | _base64 | _urlencode)"
  704. _debug txt "$txt"
  705. #dns
  706. #1. check use api
  707. d_api=""
  708. if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
  709. d_api="$LE_WORKING_DIR/$d/$Le_Webroot"
  710. elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
  711. d_api="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
  712. elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
  713. d_api="$LE_WORKING_DIR/$Le_Webroot"
  714. elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
  715. d_api="$LE_WORKING_DIR/$Le_Webroot.sh"
  716. elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
  717. d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
  718. elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
  719. d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
  720. fi
  721. _debug d_api "$d_api"
  722. if [ "$d_api" ]; then
  723. _info "Found domain api file: $d_api"
  724. else
  725. _err "Add the following TXT record:"
  726. _err "Domain: $txtdomain"
  727. _err "TXT value: $txt"
  728. _err "Please be aware that you prepend _acme-challenge. before your domain"
  729. _err "so the resulting subdomain will be: $txtdomain"
  730. continue
  731. fi
  732. if ! source $d_api ; then
  733. _err "Load file $d_api error. Please check your api file and try again."
  734. return 1
  735. fi
  736. addcommand="$Le_Webroot-add"
  737. if ! command -v $addcommand ; then
  738. _err "It seems that your api file is not correct, it must have a function named: $addcommand"
  739. return 1
  740. fi
  741. if ! $addcommand $txtdomain $txt ; then
  742. _err "Error add txt for domain:$txtdomain"
  743. return 1
  744. fi
  745. dnsadded='1'
  746. fi
  747. done
  748. if [ "$dnsadded" == '0' ] ; then
  749. _setopt "$DOMAIN_CONF" "Le_Vlist" "=" "\"$vlist\""
  750. _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
  751. _err "Please add the TXT records to the domains, and retry again."
  752. return 1
  753. fi
  754. fi
  755. if [ "$dnsadded" == '1' ] ; then
  756. _info "Sleep 60 seconds for the txt records to take effect"
  757. sleep 60
  758. fi
  759. _debug "ok, let's start to verify"
  760. ventries=$(echo "$vlist" | tr ',' ' ' )
  761. for ventry in $ventries
  762. do
  763. d=$(echo $ventry | cut -d $sep -f 1)
  764. keyauthorization=$(echo $ventry | cut -d $sep -f 2)
  765. uri=$(echo $ventry | cut -d $sep -f 3)
  766. _info "Verifying:$d"
  767. _debug "d" "$d"
  768. _debug "keyauthorization" "$keyauthorization"
  769. _debug "uri" "$uri"
  770. removelevel=""
  771. token=""
  772. if [ "$vtype" == "$VTYPE_HTTP" ] ; then
  773. if [ "$Le_Webroot" == "no" ] ; then
  774. _info "Standalone mode server"
  775. _startserver "$keyauthorization" &
  776. serverproc="$!"
  777. sleep 2
  778. _debug serverproc $serverproc
  779. else
  780. if [ -z "$wellknown_path" ] ; then
  781. wellknown_path="$Le_Webroot/.well-known/acme-challenge"
  782. fi
  783. _debug wellknown_path "$wellknown_path"
  784. if [ ! -d "$Le_Webroot/.well-known" ] ; then
  785. removelevel='1'
  786. elif [ ! -d "$Le_Webroot/.well-known/acme-challenge" ] ; then
  787. removelevel='2'
  788. else
  789. removelevel='3'
  790. fi
  791. token="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
  792. _debug "writing token:$token to $wellknown_path/$token"
  793. mkdir -p "$wellknown_path"
  794. echo -n "$keyauthorization" > "$wellknown_path/$token"
  795. webroot_owner=$(_stat $Le_Webroot)
  796. _debug "Changing owner/group of .well-known to $webroot_owner"
  797. chown -R $webroot_owner "$Le_Webroot/.well-known"
  798. fi
  799. fi
  800. _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
  801. if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
  802. _err "$d:Challenge error: $resource"
  803. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  804. _clearup
  805. return 1
  806. fi
  807. while [ "1" ] ; do
  808. _debug "sleep 5 secs to verify"
  809. sleep 5
  810. _debug "checking"
  811. if ! _get $uri ; then
  812. _err "$d:Verify error:$resource"
  813. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  814. _clearup
  815. return 1
  816. fi
  817. status=$(echo $response | egrep -o '"status":"[^"]+"' | cut -d : -f 2 | tr -d '"')
  818. if [ "$status" == "valid" ] ; then
  819. _info "Success"
  820. _stopserver $serverproc
  821. serverproc=""
  822. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  823. break;
  824. fi
  825. if [ "$status" == "invalid" ] ; then
  826. error=$(echo $response | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4)
  827. _err "$d:Verify error:$error"
  828. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  829. _clearup
  830. return 1;
  831. fi
  832. if [ "$status" == "pending" ] ; then
  833. _info "Pending"
  834. else
  835. _err "$d:Verify error:$response"
  836. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  837. _clearup
  838. return 1
  839. fi
  840. done
  841. done
  842. _clearup
  843. _info "Verify finished, start to sign."
  844. der="$(openssl req -in $CSR_PATH -outform DER | _base64 | _urlencode)"
  845. _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
  846. Le_LinkCert="$(grep -i -o '^Location.*$' $CURL_HEADER | tr -d "\r\n" | cut -d " " -f 2)"
  847. _setopt "$DOMAIN_CONF" "Le_LinkCert" "=" "$Le_LinkCert"
  848. if [ "$Le_LinkCert" ] ; then
  849. echo -----BEGIN CERTIFICATE----- > "$CERT_PATH"
  850. curl --silent "$Le_LinkCert" | openssl base64 -e >> "$CERT_PATH"
  851. echo -----END CERTIFICATE----- >> "$CERT_PATH"
  852. _info "Cert success."
  853. cat "$CERT_PATH"
  854. _info "Your cert is in $CERT_PATH"
  855. cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
  856. fi
  857. if [ -z "$Le_LinkCert" ] ; then
  858. response="$(echo $response | openssl base64 -d -A)"
  859. _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
  860. return 1
  861. fi
  862. _setopt "$DOMAIN_CONF" 'Le_Vlist' '=' "\"\""
  863. Le_LinkIssuer=$(grep -i '^Link' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
  864. _setopt "$DOMAIN_CONF" "Le_LinkIssuer" "=" "$Le_LinkIssuer"
  865. if [ "$Le_LinkIssuer" ] ; then
  866. echo -----BEGIN CERTIFICATE----- > "$CA_CERT_PATH"
  867. curl --silent "$Le_LinkIssuer" | openssl base64 -e >> "$CA_CERT_PATH"
  868. echo -----END CERTIFICATE----- >> "$CA_CERT_PATH"
  869. _info "The intermediate CA cert is in $CA_CERT_PATH"
  870. cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH"
  871. _info "And the full chain certs is there: $CERT_FULLCHAIN_PATH"
  872. fi
  873. Le_CertCreateTime=$(date -u "+%s")
  874. _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime"
  875. Le_CertCreateTimeStr=$(date -u )
  876. _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\""
  877. if [ ! "$Le_RenewalDays" ] ; then
  878. Le_RenewalDays=80
  879. fi
  880. _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays"
  881. let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
  882. _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime"
  883. Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
  884. _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\""
  885. installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
  886. }
  887. renew() {
  888. Le_Domain="$1"
  889. if [ -z "$Le_Domain" ] ; then
  890. _err "Usage: $0 domain.com"
  891. return 1
  892. fi
  893. _initpath $Le_Domain
  894. if [ ! -f "$DOMAIN_CONF" ] ; then
  895. _info "$Le_Domain is not a issued domain, skip."
  896. return 0;
  897. fi
  898. source "$DOMAIN_CONF"
  899. if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
  900. _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
  901. return 2
  902. fi
  903. IS_RENEW="1"
  904. issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
  905. local res=$?
  906. IS_RENEW=""
  907. return $res
  908. }
  909. renewAll() {
  910. _initpath
  911. _info "renewAll"
  912. for d in $(ls -F ${LE_WORKING_DIR}/ | grep [^.].*[.].*/$ ) ; do
  913. d=$(echo $d | cut -d '/' -f 1)
  914. _info "renew $d"
  915. Le_LinkCert=""
  916. Le_Domain=""
  917. Le_Alt=""
  918. Le_Webroot=""
  919. Le_Keylength=""
  920. Le_LinkIssuer=""
  921. Le_CertCreateTime=""
  922. Le_CertCreateTimeStr=""
  923. Le_RenewalDays=""
  924. Le_NextRenewTime=""
  925. Le_NextRenewTimeStr=""
  926. Le_RealCertPath=""
  927. Le_RealKeyPath=""
  928. Le_RealCACertPath=""
  929. Le_ReloadCmd=""
  930. DOMAIN_PATH=""
  931. DOMAIN_CONF=""
  932. DOMAIN_SSL_CONF=""
  933. CSR_PATH=""
  934. CERT_KEY_PATH=""
  935. CERT_PATH=""
  936. CA_CERT_PATH=""
  937. CERT_FULLCHAIN_PATH=""
  938. ACCOUNT_KEY_PATH=""
  939. wellknown_path=""
  940. renew "$d"
  941. done
  942. }
  943. installcert() {
  944. Le_Domain="$1"
  945. if [ -z "$Le_Domain" ] ; then
  946. _err "Usage: $0 domain.com [cert-file-path]|no [key-file-path]|no [ca-cert-file-path]|no [reloadCmd]|no"
  947. return 1
  948. fi
  949. Le_RealCertPath="$2"
  950. Le_RealKeyPath="$3"
  951. Le_RealCACertPath="$4"
  952. Le_ReloadCmd="$5"
  953. _initpath $Le_Domain
  954. _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
  955. _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
  956. _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
  957. _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
  958. if [ "$Le_RealCertPath" ] ; then
  959. if [ -f "$Le_RealCertPath" ] ; then
  960. cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
  961. fi
  962. cat "$CERT_PATH" > "$Le_RealCertPath"
  963. fi
  964. if [ "$Le_RealCACertPath" ] ; then
  965. if [ -f "$Le_RealCACertPath" ] ; then
  966. cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
  967. fi
  968. if [ "$Le_RealCACertPath" == "$Le_RealCertPath" ] ; then
  969. echo "" >> "$Le_RealCACertPath"
  970. cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
  971. else
  972. cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
  973. fi
  974. fi
  975. if [ "$Le_RealKeyPath" ] ; then
  976. if [ -f "$Le_RealKeyPath" ] ; then
  977. cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
  978. fi
  979. cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
  980. fi
  981. if [ "$Le_ReloadCmd" ] ; then
  982. _info "Run Le_ReloadCmd: $Le_ReloadCmd"
  983. (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd")
  984. fi
  985. }
  986. installcronjob() {
  987. _initpath
  988. _info "Installing cron job"
  989. if ! crontab -l | grep 'le.sh cron' ; then
  990. if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
  991. lesh="\"$LE_WORKING_DIR\"/le.sh"
  992. else
  993. _err "Can not install cronjob, le.sh not found."
  994. return 1
  995. fi
  996. crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
  997. fi
  998. if [ "$?" != "0" ] ; then
  999. _err "Install cron job failed. You need to manually renew your certs."
  1000. _err "Or you can add cronjob by yourself:"
  1001. _err "LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"
  1002. return 1
  1003. fi
  1004. }
  1005. uninstallcronjob() {
  1006. _info "Removing cron job"
  1007. cr="$(crontab -l | grep 'le.sh cron')"
  1008. if [ "$cr" ] ; then
  1009. crontab -l | sed "/le.sh cron/d" | crontab -
  1010. LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 6 | cut -d '=' -f 2 | tr -d '"')"
  1011. _info LE_WORKING_DIR "$LE_WORKING_DIR"
  1012. fi
  1013. _initpath
  1014. }
  1015. # Detect profile file if not specified as environment variable
  1016. _detect_profile() {
  1017. if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
  1018. echo "$PROFILE"
  1019. return
  1020. fi
  1021. local DETECTED_PROFILE
  1022. DETECTED_PROFILE=''
  1023. local SHELLTYPE
  1024. SHELLTYPE="$(basename "/$SHELL")"
  1025. if [ "$SHELLTYPE" = "bash" ]; then
  1026. if [ -f "$HOME/.bashrc" ]; then
  1027. DETECTED_PROFILE="$HOME/.bashrc"
  1028. elif [ -f "$HOME/.bash_profile" ]; then
  1029. DETECTED_PROFILE="$HOME/.bash_profile"
  1030. fi
  1031. elif [ "$SHELLTYPE" = "zsh" ]; then
  1032. DETECTED_PROFILE="$HOME/.zshrc"
  1033. fi
  1034. if [ -z "$DETECTED_PROFILE" ]; then
  1035. if [ -f "$HOME/.profile" ]; then
  1036. DETECTED_PROFILE="$HOME/.profile"
  1037. elif [ -f "$HOME/.bashrc" ]; then
  1038. DETECTED_PROFILE="$HOME/.bashrc"
  1039. elif [ -f "$HOME/.bash_profile" ]; then
  1040. DETECTED_PROFILE="$HOME/.bash_profile"
  1041. elif [ -f "$HOME/.zshrc" ]; then
  1042. DETECTED_PROFILE="$HOME/.zshrc"
  1043. fi
  1044. fi
  1045. if [ ! -z "$DETECTED_PROFILE" ]; then
  1046. echo "$DETECTED_PROFILE"
  1047. fi
  1048. }
  1049. _initconf() {
  1050. _initpath
  1051. if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
  1052. echo "#Account configurations:
  1053. #Here are the supported macros, uncomment them to make them take effect.
  1054. #ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
  1055. #ACCOUNT_KEY_PATH=\"/path/to/account.key\"
  1056. #STAGE=1 # Use the staging api
  1057. #FORCE=1 # Force to issue cert
  1058. #DEBUG=1 # Debug mode
  1059. #ACCOUNT_KEY_HASH=account key hash
  1060. #dns api
  1061. #######################
  1062. #Cloudflare:
  1063. #api key
  1064. #CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
  1065. #account email
  1066. #CF_Email=\"xxxx@sss.com\"
  1067. #######################
  1068. #Dnspod.cn:
  1069. #api key id
  1070. #DP_Id=\"1234\"
  1071. #api key
  1072. #DP_Key=\"sADDsdasdgdsf\"
  1073. #######################
  1074. #Cloudxns.com:
  1075. #CX_Key=\"1234\"
  1076. #
  1077. #CX_Secret=\"sADDsdasdgdsf\"
  1078. " > $ACCOUNT_CONF_PATH
  1079. fi
  1080. }
  1081. install() {
  1082. if ! _initpath ; then
  1083. _err "Install failed."
  1084. return 1
  1085. fi
  1086. #check if there is sudo installed, AND if the current user is a sudoer.
  1087. if command -v sudo > /dev/null ; then
  1088. if [ "$(sudo -n uptime 2>&1|grep "load"|wc -l)" != "0" ] ; then
  1089. SUDO=sudo
  1090. fi
  1091. fi
  1092. if command -v yum > /dev/null ; then
  1093. YUM="1"
  1094. INSTALL="$SUDO yum install -y "
  1095. elif command -v apt-get > /dev/null ; then
  1096. INSTALL="$SUDO apt-get install -y "
  1097. fi
  1098. if ! command -v "curl" > /dev/null ; then
  1099. _err "Please install curl first."
  1100. _err "$INSTALL curl"
  1101. return 1
  1102. fi
  1103. if ! command -v "crontab" > /dev/null ; then
  1104. _err "Please install crontab first."
  1105. if [ "$YUM" ] ; then
  1106. _err "$INSTALL crontabs"
  1107. else
  1108. _err "$INSTALL crontab"
  1109. fi
  1110. return 1
  1111. fi
  1112. if ! command -v "openssl" > /dev/null ; then
  1113. _err "Please install openssl first."
  1114. _err "$INSTALL openssl"
  1115. return 1
  1116. fi
  1117. _info "Installing to $LE_WORKING_DIR"
  1118. cp le.sh "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/le.sh"
  1119. if [ "$?" != "0" ] ; then
  1120. _err "Install failed, can not copy le.sh"
  1121. return 1
  1122. fi
  1123. _info "Installed to $LE_WORKING_DIR/le.sh"
  1124. _profile="$(_detect_profile)"
  1125. if [ "$_profile" ] ; then
  1126. _debug "Found profile: $_profile"
  1127. echo "LE_WORKING_DIR=$LE_WORKING_DIR
  1128. alias le=\"$LE_WORKING_DIR/le.sh\"
  1129. alias le.sh=\"$LE_WORKING_DIR/le.sh\"
  1130. " > "$LE_WORKING_DIR/le.env"
  1131. echo "" >> "$_profile"
  1132. _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\""
  1133. _info "OK, Close and reopen your terminal to start using le"
  1134. else
  1135. _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
  1136. fi
  1137. mkdir -p $LE_WORKING_DIR/dnsapi
  1138. cp dnsapi/* $LE_WORKING_DIR/dnsapi/
  1139. #to keep compatible mv the .acc file to .key file
  1140. if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
  1141. mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
  1142. fi
  1143. installcronjob
  1144. if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
  1145. _initconf
  1146. fi
  1147. _info OK
  1148. }
  1149. uninstall() {
  1150. uninstallcronjob
  1151. _initpath
  1152. _profile="$(_detect_profile)"
  1153. if [ "$_profile" ] ; then
  1154. text="$(cat $_profile)"
  1155. echo "$text" | sed "s|^source.*le.env.*$||" > "$_profile"
  1156. fi
  1157. rm -f $LE_WORKING_DIR/le.sh
  1158. _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
  1159. }
  1160. cron() {
  1161. renewAll
  1162. }
  1163. version() {
  1164. _info "$PROJECT"
  1165. _info "v$VER"
  1166. }
  1167. showhelp() {
  1168. version
  1169. echo "Usage: le.sh [command] ...[args]....
  1170. Avalible commands:
  1171. install:
  1172. Install le.sh to your system.
  1173. issue:
  1174. Issue a cert.
  1175. installcert:
  1176. Install the issued cert to apache/nginx or any other server.
  1177. renew:
  1178. Renew a cert.
  1179. renewAll:
  1180. Renew all the certs.
  1181. uninstall:
  1182. Uninstall le.sh, and uninstall the cron job.
  1183. version:
  1184. Show version info.
  1185. installcronjob:
  1186. Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
  1187. uninstallcronjob:
  1188. Uninstall the cron job. The 'uninstall' command can do this automatically.
  1189. createAccountKey:
  1190. Create an account private key, professional use.
  1191. createDomainKey:
  1192. Create an domain private key, professional use.
  1193. createCSR:
  1194. Create CSR , professional use.
  1195. "
  1196. }
  1197. if [ -z "$1" ] ; then
  1198. showhelp
  1199. else
  1200. "$@"
  1201. fi