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.

1446 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. _NC="nc -l"
  372. fi
  373. _debug "$_NC $Le_HTTPPort"
  374. # while true ; do
  375. if [ "$DEBUG" ] ; then
  376. if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort -vv ; then
  377. echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort -vv ;
  378. fi
  379. else
  380. if ! echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1; then
  381. echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1
  382. fi
  383. fi
  384. if [ "$?" != "0" ] ; then
  385. _err "nc listen error."
  386. return 1
  387. fi
  388. # done
  389. }
  390. _stopserver() {
  391. pid="$1"
  392. }
  393. _initpath() {
  394. if [ -z "$LE_WORKING_DIR" ]; then
  395. LE_WORKING_DIR=$HOME/.le
  396. fi
  397. if [ -z "$ACCOUNT_CONF_PATH" ] ; then
  398. ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
  399. fi
  400. if [ -f "$ACCOUNT_CONF_PATH" ] ; then
  401. source "$ACCOUNT_CONF_PATH"
  402. fi
  403. if [ -z "$API" ] ; then
  404. if [ -z "$STAGE" ] ; then
  405. API="$DEFAULT_CA"
  406. else
  407. API="$STAGE_CA"
  408. _info "Using stage api:$API"
  409. fi
  410. fi
  411. if [ -z "$ACME_DIR" ] ; then
  412. ACME_DIR="/home/.acme"
  413. fi
  414. if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
  415. APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR/"
  416. fi
  417. domain="$1"
  418. if ! mkdir -p "$LE_WORKING_DIR" ; then
  419. _err "Can not craete working dir: $LE_WORKING_DIR"
  420. return 1
  421. fi
  422. if [ -z "$ACCOUNT_KEY_PATH" ] ; then
  423. ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
  424. fi
  425. if [ -z "$domain" ] ; then
  426. return 0
  427. fi
  428. domainhome="$LE_WORKING_DIR/$domain"
  429. mkdir -p "$domainhome"
  430. if [ -z "$DOMAIN_PATH" ] ; then
  431. DOMAIN_PATH="$domainhome"
  432. fi
  433. if [ -z "$DOMAIN_CONF" ] ; then
  434. DOMAIN_CONF="$domainhome/$domain.conf"
  435. fi
  436. if [ -z "$DOMAIN_SSL_CONF" ] ; then
  437. DOMAIN_SSL_CONF="$domainhome/$domain.ssl.conf"
  438. fi
  439. if [ -z "$CSR_PATH" ] ; then
  440. CSR_PATH="$domainhome/$domain.csr"
  441. fi
  442. if [ -z "$CERT_KEY_PATH" ] ; then
  443. CERT_KEY_PATH="$domainhome/$domain.key"
  444. fi
  445. if [ -z "$CERT_PATH" ] ; then
  446. CERT_PATH="$domainhome/$domain.cer"
  447. fi
  448. if [ -z "$CA_CERT_PATH" ] ; then
  449. CA_CERT_PATH="$domainhome/ca.cer"
  450. fi
  451. if [ -z "$CERT_FULLCHAIN_PATH" ] ; then
  452. CERT_FULLCHAIN_PATH="$domainhome/fullchain.cer"
  453. fi
  454. }
  455. _apachePath() {
  456. httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
  457. httpdconfname="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
  458. httpdconf="$httpdroot/$httpdconfname"
  459. if [ ! -f $httpdconf ] ; then
  460. _err "Apache Config file not found" $httpdconf
  461. return 1
  462. fi
  463. return 0
  464. }
  465. _restoreApache() {
  466. if [ -z "$usingApache" ] ; then
  467. return 0
  468. fi
  469. _initpath
  470. if ! _apachePath ; then
  471. return 1
  472. fi
  473. if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
  474. _debug "No config file to restore."
  475. return 0
  476. fi
  477. cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
  478. if ! apachectl -t ; then
  479. _err "Sorry, restore apache config error, please contact me."
  480. return 1;
  481. fi
  482. rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
  483. return 0
  484. }
  485. _setApache() {
  486. _initpath
  487. if ! _apachePath ; then
  488. return 1
  489. fi
  490. #backup the conf
  491. _debug "Backup apache config file" $httpdconf
  492. cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
  493. _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
  494. _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
  495. _info "The backup file will be deleted on sucess, just forget it."
  496. #add alias
  497. echo "
  498. Alias /.well-known/acme-challenge $ACME_DIR
  499. <Directory $ACME_DIR >
  500. Require all granted
  501. </Directory>
  502. " >> $httpdconf
  503. if ! apachectl -t ; then
  504. _err "Sorry, apache config error, please contact me."
  505. _restoreApache
  506. return 1;
  507. fi
  508. if [ ! -d "$ACME_DIR" ] ; then
  509. mkdir -p "$ACME_DIR"
  510. chmod 755 "$ACME_DIR"
  511. fi
  512. if ! apachectl graceful ; then
  513. _err "Sorry, apachectl graceful error, please contact me."
  514. _restoreApache
  515. return 1;
  516. fi
  517. usingApache="1"
  518. return 0
  519. }
  520. _clearup () {
  521. _stopserver $serverproc
  522. serverproc=""
  523. _restoreApache
  524. }
  525. # webroot removelevel tokenfile
  526. _clearupwebbroot() {
  527. __webroot="$1"
  528. if [ -z "$__webroot" ] ; then
  529. _debug "no webroot specified, skip"
  530. return 0
  531. fi
  532. if [ "$2" == '1' ] ; then
  533. _debug "remove $__webroot/.well-known"
  534. rm -rf "$__webroot/.well-known"
  535. elif [ "$2" == '2' ] ; then
  536. _debug "remove $__webroot/.well-known/acme-challenge"
  537. rm -rf "$__webroot/.well-known/acme-challenge"
  538. elif [ "$2" == '3' ] ; then
  539. _debug "remove $__webroot/.well-known/acme-challenge/$3"
  540. rm -rf "$__webroot/.well-known/acme-challenge/$3"
  541. else
  542. _info "Skip for removelevel:$2"
  543. fi
  544. return 0
  545. }
  546. issue() {
  547. if [ -z "$2" ] ; then
  548. _err "Usage: le issue webroot|no|apache|dns a.com [www.a.com,b.com,c.com]|no [key-length]|no"
  549. return 1
  550. fi
  551. Le_Webroot="$1"
  552. Le_Domain="$2"
  553. Le_Alt="$3"
  554. Le_Keylength="$4"
  555. Le_RealCertPath="$5"
  556. Le_RealKeyPath="$6"
  557. Le_RealCACertPath="$7"
  558. Le_ReloadCmd="$8"
  559. _initpath $Le_Domain
  560. if [ -f "$DOMAIN_CONF" ] ; then
  561. Le_NextRenewTime=$(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut -d '=' -f 2)
  562. if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
  563. _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)"
  564. return 2
  565. fi
  566. fi
  567. if [ "$Le_Alt" == "no" ] ; then
  568. Le_Alt=""
  569. fi
  570. if [ "$Le_Keylength" == "no" ] ; then
  571. Le_Keylength=""
  572. fi
  573. if [ "$Le_RealCertPath" == "no" ] ; then
  574. Le_RealCertPath=""
  575. fi
  576. if [ "$Le_RealKeyPath" == "no" ] ; then
  577. Le_RealKeyPath=""
  578. fi
  579. if [ "$Le_RealCACertPath" == "no" ] ; then
  580. Le_RealCACertPath=""
  581. fi
  582. if [ "$Le_ReloadCmd" == "no" ] ; then
  583. Le_ReloadCmd=""
  584. fi
  585. _setopt "$DOMAIN_CONF" "Le_Domain" "=" "$Le_Domain"
  586. _setopt "$DOMAIN_CONF" "Le_Alt" "=" "$Le_Alt"
  587. _setopt "$DOMAIN_CONF" "Le_Webroot" "=" "$Le_Webroot"
  588. _setopt "$DOMAIN_CONF" "Le_Keylength" "=" "$Le_Keylength"
  589. _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
  590. _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
  591. _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
  592. _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
  593. if [ "$Le_Webroot" == "no" ] ; then
  594. _info "Standalone mode."
  595. if ! command -v "nc" > /dev/null ; then
  596. _err "Please install netcat(nc) tools first."
  597. return 1
  598. fi
  599. if [ -z "$Le_HTTPPort" ] ; then
  600. Le_HTTPPort=80
  601. fi
  602. _setopt "$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort"
  603. netprc="$(_ss "$Le_HTTPPort" | grep "$Le_HTTPPort")"
  604. if [ "$netprc" ] ; then
  605. _err "$netprc"
  606. _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
  607. _err "Please stop it first"
  608. return 1
  609. fi
  610. fi
  611. if [ "$Le_Webroot" == "apache" ] ; then
  612. if ! _setApache ; then
  613. _err "set up apache error. Report error to me."
  614. return 1
  615. fi
  616. wellknown_path="$ACME_DIR"
  617. else
  618. usingApache=""
  619. fi
  620. createAccountKey $Le_Domain $Le_Keylength
  621. if ! _calcjwk "$ACCOUNT_KEY_PATH" ; then
  622. return 1
  623. fi
  624. accountkey_json=$(echo -n "$jwk" | tr -d ' ' )
  625. thumbprint=$(echo -n "$accountkey_json" | openssl dgst -sha256 -binary | _base64 | _urlencode)
  626. accountkeyhash="$(cat "$ACCOUNT_KEY_PATH" | openssl dgst -sha256 -binary | _base64)"
  627. if [ "$accountkeyhash" != "$ACCOUNT_KEY_HASH" ] ; then
  628. _info "Registering account"
  629. regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
  630. if [ "$ACCOUNT_EMAIL" ] ; then
  631. regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
  632. fi
  633. _send_signed_request "$API/acme/new-reg" "$regjson"
  634. if [ "$code" == "" ] || [ "$code" == '201' ] ; then
  635. _info "Registered"
  636. echo $response > $LE_WORKING_DIR/account.json
  637. elif [ "$code" == '409' ] ; then
  638. _info "Already registered"
  639. else
  640. _err "Register account Error: $response"
  641. _clearup
  642. return 1
  643. fi
  644. ACCOUNT_KEY_HASH="$accountkeyhash"
  645. _saveaccountconf "ACCOUNT_KEY_HASH" "$ACCOUNT_KEY_HASH"
  646. else
  647. _info "Skip register account key"
  648. fi
  649. if ! createDomainKey $Le_Domain $Le_Keylength ; then
  650. _err "Create domain key error."
  651. return 1
  652. fi
  653. if ! createCSR $Le_Domain $Le_Alt ; then
  654. _err "Create CSR error."
  655. return 1
  656. fi
  657. vtype="$VTYPE_HTTP"
  658. if [[ "$Le_Webroot" == "dns"* ]] ; then
  659. vtype="$VTYPE_DNS"
  660. fi
  661. vlist="$Le_Vlist"
  662. # verify each domain
  663. _info "Verify each domain"
  664. sep='#'
  665. if [ -z "$vlist" ] ; then
  666. alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
  667. for d in $alldomains
  668. do
  669. _info "Getting token for domain" $d
  670. _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
  671. if [ ! -z "$code" ] && [ ! "$code" == '201' ] ; then
  672. _err "new-authz error: $response"
  673. _clearup
  674. return 1
  675. fi
  676. entry="$(printf $response | egrep -o '\{[^{]*"type":"'$vtype'"[^}]*')"
  677. _debug entry "$entry"
  678. token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
  679. _debug token $token
  680. uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
  681. _debug uri $uri
  682. keyauthorization="$token.$thumbprint"
  683. _debug keyauthorization "$keyauthorization"
  684. dvlist="$d$sep$keyauthorization$sep$uri"
  685. _debug dvlist "$dvlist"
  686. vlist="$vlist$dvlist,"
  687. done
  688. #add entry
  689. dnsadded=""
  690. ventries=$(echo "$vlist" | tr ',' ' ' )
  691. for ventry in $ventries
  692. do
  693. d=$(echo $ventry | cut -d $sep -f 1)
  694. keyauthorization=$(echo $ventry | cut -d $sep -f 2)
  695. if [ "$vtype" == "$VTYPE_DNS" ] ; then
  696. dnsadded='0'
  697. txtdomain="_acme-challenge.$d"
  698. _debug txtdomain "$txtdomain"
  699. txt="$(echo -e -n $keyauthorization | openssl dgst -sha256 -binary | _base64 | _urlencode)"
  700. _debug txt "$txt"
  701. #dns
  702. #1. check use api
  703. d_api=""
  704. if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
  705. d_api="$LE_WORKING_DIR/$d/$Le_Webroot"
  706. elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
  707. d_api="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
  708. elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
  709. d_api="$LE_WORKING_DIR/$Le_Webroot"
  710. elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
  711. d_api="$LE_WORKING_DIR/$Le_Webroot.sh"
  712. elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
  713. d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
  714. elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
  715. d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
  716. fi
  717. _debug d_api "$d_api"
  718. if [ "$d_api" ]; then
  719. _info "Found domain api file: $d_api"
  720. else
  721. _err "Add the following TXT record:"
  722. _err "Domain: $txtdomain"
  723. _err "TXT value: $txt"
  724. _err "Please be aware that you prepend _acme-challenge. before your domain"
  725. _err "so the resulting subdomain will be: $txtdomain"
  726. continue
  727. fi
  728. if ! source $d_api ; then
  729. _err "Load file $d_api error. Please check your api file and try again."
  730. return 1
  731. fi
  732. addcommand="$Le_Webroot-add"
  733. if ! command -v $addcommand ; then
  734. _err "It seems that your api file is not correct, it must have a function named: $addcommand"
  735. return 1
  736. fi
  737. if ! $addcommand $txtdomain $txt ; then
  738. _err "Error add txt for domain:$txtdomain"
  739. return 1
  740. fi
  741. dnsadded='1'
  742. fi
  743. done
  744. if [ "$dnsadded" == '0' ] ; then
  745. _setopt "$DOMAIN_CONF" "Le_Vlist" "=" "\"$vlist\""
  746. _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
  747. _err "Please add the TXT records to the domains, and retry again."
  748. return 1
  749. fi
  750. fi
  751. if [ "$dnsadded" == '1' ] ; then
  752. _info "Sleep 60 seconds for the txt records to take effect"
  753. sleep 60
  754. fi
  755. _debug "ok, let's start to verify"
  756. ventries=$(echo "$vlist" | tr ',' ' ' )
  757. for ventry in $ventries
  758. do
  759. d=$(echo $ventry | cut -d $sep -f 1)
  760. keyauthorization=$(echo $ventry | cut -d $sep -f 2)
  761. uri=$(echo $ventry | cut -d $sep -f 3)
  762. _info "Verifying:$d"
  763. _debug "d" "$d"
  764. _debug "keyauthorization" "$keyauthorization"
  765. _debug "uri" "$uri"
  766. removelevel=""
  767. token=""
  768. if [ "$vtype" == "$VTYPE_HTTP" ] ; then
  769. if [ "$Le_Webroot" == "no" ] ; then
  770. _info "Standalone mode server"
  771. _startserver "$keyauthorization" &
  772. serverproc="$!"
  773. sleep 2
  774. _debug serverproc $serverproc
  775. else
  776. if [ -z "$wellknown_path" ] ; then
  777. wellknown_path="$Le_Webroot/.well-known/acme-challenge"
  778. fi
  779. _debug wellknown_path "$wellknown_path"
  780. if [ ! -d "$Le_Webroot/.well-known" ] ; then
  781. removelevel='1'
  782. elif [ ! -d "$Le_Webroot/.well-known/acme-challenge" ] ; then
  783. removelevel='2'
  784. else
  785. removelevel='3'
  786. fi
  787. token="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
  788. _debug "writing token:$token to $wellknown_path/$token"
  789. mkdir -p "$wellknown_path"
  790. echo -n "$keyauthorization" > "$wellknown_path/$token"
  791. webroot_owner=$(_stat $Le_Webroot)
  792. _debug "Changing owner/group of .well-known to $webroot_owner"
  793. chown -R $webroot_owner "$Le_Webroot/.well-known"
  794. fi
  795. fi
  796. _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
  797. if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
  798. _err "$d:Challenge error: $resource"
  799. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  800. _clearup
  801. return 1
  802. fi
  803. while [ "1" ] ; do
  804. _debug "sleep 5 secs to verify"
  805. sleep 5
  806. _debug "checking"
  807. if ! _get $uri ; then
  808. _err "$d:Verify error:$resource"
  809. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  810. _clearup
  811. return 1
  812. fi
  813. status=$(echo $response | egrep -o '"status":"[^"]+"' | cut -d : -f 2 | tr -d '"')
  814. if [ "$status" == "valid" ] ; then
  815. _info "Success"
  816. _stopserver $serverproc
  817. serverproc=""
  818. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  819. break;
  820. fi
  821. if [ "$status" == "invalid" ] ; then
  822. error=$(echo $response | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4)
  823. _err "$d:Verify error:$error"
  824. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  825. _clearup
  826. return 1;
  827. fi
  828. if [ "$status" == "pending" ] ; then
  829. _info "Pending"
  830. else
  831. _err "$d:Verify error:$response"
  832. _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
  833. _clearup
  834. return 1
  835. fi
  836. done
  837. done
  838. _clearup
  839. _info "Verify finished, start to sign."
  840. der="$(openssl req -in $CSR_PATH -outform DER | _base64 | _urlencode)"
  841. _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
  842. Le_LinkCert="$(grep -i -o '^Location.*$' $CURL_HEADER | tr -d "\r\n" | cut -d " " -f 2)"
  843. _setopt "$DOMAIN_CONF" "Le_LinkCert" "=" "$Le_LinkCert"
  844. if [ "$Le_LinkCert" ] ; then
  845. echo -----BEGIN CERTIFICATE----- > "$CERT_PATH"
  846. curl --silent "$Le_LinkCert" | openssl base64 -e >> "$CERT_PATH"
  847. echo -----END CERTIFICATE----- >> "$CERT_PATH"
  848. _info "Cert success."
  849. cat "$CERT_PATH"
  850. _info "Your cert is in $CERT_PATH"
  851. cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
  852. fi
  853. if [ -z "$Le_LinkCert" ] ; then
  854. response="$(echo $response | openssl base64 -d -A)"
  855. _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
  856. return 1
  857. fi
  858. _setopt "$DOMAIN_CONF" 'Le_Vlist' '=' "\"\""
  859. Le_LinkIssuer=$(grep -i '^Link' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
  860. _setopt "$DOMAIN_CONF" "Le_LinkIssuer" "=" "$Le_LinkIssuer"
  861. if [ "$Le_LinkIssuer" ] ; then
  862. echo -----BEGIN CERTIFICATE----- > "$CA_CERT_PATH"
  863. curl --silent "$Le_LinkIssuer" | openssl base64 -e >> "$CA_CERT_PATH"
  864. echo -----END CERTIFICATE----- >> "$CA_CERT_PATH"
  865. _info "The intermediate CA cert is in $CA_CERT_PATH"
  866. cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH"
  867. _info "And the full chain certs is there: $CERT_FULLCHAIN_PATH"
  868. fi
  869. Le_CertCreateTime=$(date -u "+%s")
  870. _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime"
  871. Le_CertCreateTimeStr=$(date -u )
  872. _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\""
  873. if [ ! "$Le_RenewalDays" ] ; then
  874. Le_RenewalDays=80
  875. fi
  876. _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays"
  877. let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
  878. _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime"
  879. Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
  880. _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\""
  881. installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
  882. }
  883. renew() {
  884. Le_Domain="$1"
  885. if [ -z "$Le_Domain" ] ; then
  886. _err "Usage: $0 domain.com"
  887. return 1
  888. fi
  889. _initpath $Le_Domain
  890. if [ ! -f "$DOMAIN_CONF" ] ; then
  891. _info "$Le_Domain is not a issued domain, skip."
  892. return 0;
  893. fi
  894. source "$DOMAIN_CONF"
  895. if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
  896. _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
  897. return 2
  898. fi
  899. IS_RENEW="1"
  900. issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
  901. local res=$?
  902. IS_RENEW=""
  903. return $res
  904. }
  905. renewAll() {
  906. _initpath
  907. _info "renewAll"
  908. for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do
  909. d=$(echo $d | cut -d '/' -f 1)
  910. _info "renew $d"
  911. Le_LinkCert=""
  912. Le_Domain=""
  913. Le_Alt=""
  914. Le_Webroot=""
  915. Le_Keylength=""
  916. Le_LinkIssuer=""
  917. Le_CertCreateTime=""
  918. Le_CertCreateTimeStr=""
  919. Le_RenewalDays=""
  920. Le_NextRenewTime=""
  921. Le_NextRenewTimeStr=""
  922. Le_RealCertPath=""
  923. Le_RealKeyPath=""
  924. Le_RealCACertPath=""
  925. Le_ReloadCmd=""
  926. DOMAIN_PATH=""
  927. DOMAIN_CONF=""
  928. DOMAIN_SSL_CONF=""
  929. CSR_PATH=""
  930. CERT_KEY_PATH=""
  931. CERT_PATH=""
  932. CA_CERT_PATH=""
  933. CERT_FULLCHAIN_PATH=""
  934. ACCOUNT_KEY_PATH=""
  935. wellknown_path=""
  936. renew "$d"
  937. done
  938. }
  939. installcert() {
  940. Le_Domain="$1"
  941. if [ -z "$Le_Domain" ] ; then
  942. _err "Usage: $0 domain.com [cert-file-path]|no [key-file-path]|no [ca-cert-file-path]|no [reloadCmd]|no"
  943. return 1
  944. fi
  945. Le_RealCertPath="$2"
  946. Le_RealKeyPath="$3"
  947. Le_RealCACertPath="$4"
  948. Le_ReloadCmd="$5"
  949. _initpath $Le_Domain
  950. _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
  951. _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
  952. _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
  953. _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
  954. if [ "$Le_RealCertPath" ] ; then
  955. if [ -f "$Le_RealCertPath" ] ; then
  956. cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
  957. fi
  958. cat "$CERT_PATH" > "$Le_RealCertPath"
  959. fi
  960. if [ "$Le_RealCACertPath" ] ; then
  961. if [ -f "$Le_RealCACertPath" ] ; then
  962. cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
  963. fi
  964. if [ "$Le_RealCACertPath" == "$Le_RealCertPath" ] ; then
  965. echo "" >> "$Le_RealCACertPath"
  966. cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
  967. else
  968. cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
  969. fi
  970. fi
  971. if [ "$Le_RealKeyPath" ] ; then
  972. if [ -f "$Le_RealKeyPath" ] ; then
  973. cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
  974. fi
  975. cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
  976. fi
  977. if [ "$Le_ReloadCmd" ] ; then
  978. _info "Run Le_ReloadCmd: $Le_ReloadCmd"
  979. (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd")
  980. fi
  981. }
  982. installcronjob() {
  983. _initpath
  984. _info "Installing cron job"
  985. if ! crontab -l | grep 'le.sh cron' ; then
  986. if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
  987. lesh="\"$LE_WORKING_DIR\"/le.sh"
  988. else
  989. _err "Can not install cronjob, le.sh not found."
  990. return 1
  991. fi
  992. crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
  993. fi
  994. if [ "$?" != "0" ] ; then
  995. _err "Install cron job failed. You need to manually renew your certs."
  996. _err "Or you can add cronjob by yourself:"
  997. _err "LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"
  998. return 1
  999. fi
  1000. }
  1001. uninstallcronjob() {
  1002. _info "Removing cron job"
  1003. cr="$(crontab -l | grep 'le.sh cron')"
  1004. if [ "$cr" ] ; then
  1005. crontab -l | sed "/le.sh cron/d" | crontab -
  1006. LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 6 | cut -d '=' -f 2 | tr -d '"')"
  1007. _info LE_WORKING_DIR "$LE_WORKING_DIR"
  1008. fi
  1009. _initpath
  1010. }
  1011. # Detect profile file if not specified as environment variable
  1012. _detect_profile() {
  1013. if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
  1014. echo "$PROFILE"
  1015. return
  1016. fi
  1017. local DETECTED_PROFILE
  1018. DETECTED_PROFILE=''
  1019. local SHELLTYPE
  1020. SHELLTYPE="$(basename "/$SHELL")"
  1021. if [ "$SHELLTYPE" = "bash" ]; then
  1022. if [ -f "$HOME/.bashrc" ]; then
  1023. DETECTED_PROFILE="$HOME/.bashrc"
  1024. elif [ -f "$HOME/.bash_profile" ]; then
  1025. DETECTED_PROFILE="$HOME/.bash_profile"
  1026. fi
  1027. elif [ "$SHELLTYPE" = "zsh" ]; then
  1028. DETECTED_PROFILE="$HOME/.zshrc"
  1029. fi
  1030. if [ -z "$DETECTED_PROFILE" ]; then
  1031. if [ -f "$HOME/.profile" ]; then
  1032. DETECTED_PROFILE="$HOME/.profile"
  1033. elif [ -f "$HOME/.bashrc" ]; then
  1034. DETECTED_PROFILE="$HOME/.bashrc"
  1035. elif [ -f "$HOME/.bash_profile" ]; then
  1036. DETECTED_PROFILE="$HOME/.bash_profile"
  1037. elif [ -f "$HOME/.zshrc" ]; then
  1038. DETECTED_PROFILE="$HOME/.zshrc"
  1039. fi
  1040. fi
  1041. if [ ! -z "$DETECTED_PROFILE" ]; then
  1042. echo "$DETECTED_PROFILE"
  1043. fi
  1044. }
  1045. _initconf() {
  1046. _initpath
  1047. if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
  1048. echo "#Account configurations:
  1049. #Here are the supported macros, uncomment them to make them take effect.
  1050. #ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
  1051. #ACCOUNT_KEY_PATH=\"/path/to/account.key\"
  1052. #STAGE=1 # Use the staging api
  1053. #FORCE=1 # Force to issue cert
  1054. #DEBUG=1 # Debug mode
  1055. #ACCOUNT_KEY_HASH=account key hash
  1056. #dns api
  1057. #######################
  1058. #Cloudflare:
  1059. #api key
  1060. #CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
  1061. #account email
  1062. #CF_Email=\"xxxx@sss.com\"
  1063. #######################
  1064. #Dnspod.cn:
  1065. #api key id
  1066. #DP_Id=\"1234\"
  1067. #api key
  1068. #DP_Key=\"sADDsdasdgdsf\"
  1069. #######################
  1070. #Cloudxns.com:
  1071. #CX_Key=\"1234\"
  1072. #
  1073. #CX_Secret=\"sADDsdasdgdsf\"
  1074. " > $ACCOUNT_CONF_PATH
  1075. fi
  1076. }
  1077. install() {
  1078. if ! _initpath ; then
  1079. _err "Install failed."
  1080. return 1
  1081. fi
  1082. #check if there is sudo installed, AND if the current user is a sudoer.
  1083. if command -v sudo > /dev/null ; then
  1084. if [ "$(sudo -n uptime 2>&1|grep "load"|wc -l)" != "0" ] ; then
  1085. SUDO=sudo
  1086. fi
  1087. fi
  1088. if command -v yum > /dev/null ; then
  1089. YUM="1"
  1090. INSTALL="$SUDO yum install -y "
  1091. elif command -v apt-get > /dev/null ; then
  1092. INSTALL="$SUDO apt-get install -y "
  1093. fi
  1094. if ! command -v "curl" > /dev/null ; then
  1095. _err "Please install curl first."
  1096. _err "$INSTALL curl"
  1097. return 1
  1098. fi
  1099. if ! command -v "crontab" > /dev/null ; then
  1100. _err "Please install crontab first."
  1101. if [ "$YUM" ] ; then
  1102. _err "$INSTALL crontabs"
  1103. else
  1104. _err "$INSTALL crontab"
  1105. fi
  1106. return 1
  1107. fi
  1108. if ! command -v "openssl" > /dev/null ; then
  1109. _err "Please install openssl first."
  1110. _err "$INSTALL openssl"
  1111. return 1
  1112. fi
  1113. _info "Installing to $LE_WORKING_DIR"
  1114. cp le.sh "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/le.sh"
  1115. if [ "$?" != "0" ] ; then
  1116. _err "Install failed, can not copy le.sh"
  1117. return 1
  1118. fi
  1119. _info "Installed to $LE_WORKING_DIR/le.sh"
  1120. _profile="$(_detect_profile)"
  1121. if [ "$_profile" ] ; then
  1122. _debug "Found profile: $_profile"
  1123. echo "LE_WORKING_DIR=$LE_WORKING_DIR
  1124. alias le=\"$LE_WORKING_DIR/le.sh\"
  1125. alias le.sh=\"$LE_WORKING_DIR/le.sh\"
  1126. " > "$LE_WORKING_DIR/le.env"
  1127. echo "" >> "$_profile"
  1128. _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\""
  1129. _info "OK, Close and reopen your terminal to start using le"
  1130. else
  1131. _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
  1132. fi
  1133. mkdir -p $LE_WORKING_DIR/dnsapi
  1134. cp dnsapi/* $LE_WORKING_DIR/dnsapi/
  1135. #to keep compatible mv the .acc file to .key file
  1136. if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
  1137. mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
  1138. fi
  1139. installcronjob
  1140. if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
  1141. _initconf
  1142. fi
  1143. _info OK
  1144. }
  1145. uninstall() {
  1146. uninstallcronjob
  1147. _initpath
  1148. _profile="$(_detect_profile)"
  1149. if [ "$_profile" ] ; then
  1150. text="$(cat $_profile)"
  1151. echo "$text" | sed "s|^source.*le.env.*$||" > "$_profile"
  1152. fi
  1153. rm -f $LE_WORKING_DIR/le.sh
  1154. _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
  1155. }
  1156. cron() {
  1157. renewAll
  1158. }
  1159. version() {
  1160. _info "$PROJECT"
  1161. _info "v$VER"
  1162. }
  1163. showhelp() {
  1164. version
  1165. echo "Usage: le.sh [command] ...[args]....
  1166. Avalible commands:
  1167. install:
  1168. Install le.sh to your system.
  1169. issue:
  1170. Issue a cert.
  1171. installcert:
  1172. Install the issued cert to apache/nginx or any other server.
  1173. renew:
  1174. Renew a cert.
  1175. renewAll:
  1176. Renew all the certs.
  1177. uninstall:
  1178. Uninstall le.sh, and uninstall the cron job.
  1179. version:
  1180. Show version info.
  1181. installcronjob:
  1182. Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
  1183. uninstallcronjob:
  1184. Uninstall the cron job. The 'uninstall' command can do this automatically.
  1185. createAccountKey:
  1186. Create an account private key, professional use.
  1187. createDomainKey:
  1188. Create an domain private key, professional use.
  1189. createCSR:
  1190. Create CSR , professional use.
  1191. "
  1192. }
  1193. if [ -z "$1" ] ; then
  1194. showhelp
  1195. else
  1196. "$@"
  1197. fi