acme.sh 70 KB

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