acme.sh 72 KB

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