Guest User

Untitled

a guest
Dec 6th, 2017
774
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 74.12 KB | None | 0 0
  1. #!/usr/bin/env bash
  2. # ---------------------------------------------------------------------------
  3. # getssl - Obtain SSL certificates from the letsencrypt.org ACME server
  4.  
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9.  
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License at <http://www.gnu.org/licenses/> for
  14. # more details.
  15.  
  16. # For usage, run "getssl -h" or see https://github.com/srvrco/getssl
  17.  
  18. # Revision history:
  19. # 2016-01-08 Created (v0.1)
  20. # 2016-01-11 type correction and upload to github (v0.2)
  21. # 2016-01-11 added import of any existing cert on -c option (v0.3)
  22. # 2016-01-12 corrected formatting of imported certificate (v0.4)
  23. # 2016-01-12 corrected error on removal of token in some instances (v0.5)
  24. # 2016-01-18 corrected issue with removing tmp if run as root with the -c option (v0.6)
  25. # 2016-01-18 added option to upload a single PEN file ( used by cpanel) (v0.7)
  26. # 2016-01-23 added dns challenge option (v0.8)
  27. # 2016-01-24 create the ACL directory if it does not exist. (v0.9) - dstosberg
  28. # 2016-01-26 correcting a couple of small bugs and allow curl to follow redirects (v0.10)
  29. # 2016-01-27 add a very basic openssl.cnf file if it doesn't exist and tidy code slightly (v0.11)
  30. # 2016-01-28 Typo corrections, quoted file variables and fix bug on DNS_DEL_COMMAND (v0.12)
  31. # 2016-01-28 changed DNS checks to use nslookup and allow hyphen in domain names (v0.13)
  32. # 2016-01-29 Fix ssh-reload-command, extra waiting for DNS-challenge,
  33. # 2016-01-29 add error_exit and cleanup help message (v0.14)
  34. # 2016-01-29 added -a|--all option to renew all configured certificates (v0.15)
  35. # 2016-01-29 added option for elliptic curve keys (v0.16)
  36. # 2016-01-29 added server-type option to use and check cert validity from website (v0.17)
  37. # 2016-01-30 added --quiet option for running in cron (v0.18)
  38. # 2016-01-31 removed usage of xxd to make script more compatible across versions (v0.19)
  39. # 2016-01-31 removed usage of base64 to make script more compatible across platforms (v0.20)
  40. # 2016-01-31 added option to safe a full chain certificate (v0.21)
  41. # 2016-02-01 commented code and added option for copying concatenated certs to file (v0.22)
  42. # 2016-02-01 re-arrange flow for DNS-challenge, to reduce time taken (v0.23)
  43. # 2016-02-04 added options for other server types (ldaps, or any port) and check_remote (v0.24)
  44. # 2016-02-04 added short sleep following service restart before checking certs (v0.25)
  45. # 2016-02-12 fix challenge token location when directory doesn't exist (v0.26)
  46. # 2016-02-17 fix sed -E issue, and reduce length of renew check to 365 days for older systems (v0.27)
  47. # 2016-04-05 Ensure DNS cleanup on error exit. (0.28) - pecigonzalo
  48. # 2016-04-15 Remove NS Lookup of A record when using dns validation (0.29) - pecigonzalo
  49. # 2016-04-17 Improving the wording in a couple of comments and info statements. (0.30)
  50. # 2016-05-04 Improve check for if DNS_DEL_COMMAND is blank. (0.31)
  51. # 2016-05-06 Setting umask to 077 for security of private keys etc. (0.32)
  52. # 2016-05-20 update to reflect changes in staging ACME server json (0.33)
  53. # 2016-05-20 tidying up checking of json following AMCE changes. (0.34)
  54. # 2016-05-21 added AUTH_DNS_SERVER to getssl.cfg as optional definition of authoritative DNS server (0.35)
  55. # 2016-05-21 added DNS_WAIT to getssl.cfg as (default = 10 seconds as before) (0.36)
  56. # 2016-05-21 added PUBLIC_DNS_SERVER option, for forcing use of an external DNS server (0.37)
  57. # 2016-05-28 added FTP method of uploading tokens to remote server (blocked for certs as not secure) (0.38)
  58. # 2016-05-28 added FTP method into the default config notes. (0.39)
  59. # 2016-05-30 Add sftp with password to copy files (0.40)
  60. # 2016-05-30 Add version check to see if there is a more recent version of getssl (0.41)
  61. # 2016-05-30 Add [-u|--upgrade] option to automatically upgrade getssl (0.42)
  62. # 2016-05-30 Added backup when auto-upgrading (0.43)
  63. # 2016-05-30 Improvements to auto-upgrade (0.44)
  64. # 2016-05-31 Improved comments - no structural changes
  65. # 2016-05-31 After running for nearly 6 months, final testing prior to a 1.00 stable version. (0.90)
  66. # 2016-06-01 Reorder functions alphabetically as part of code tidy. (0.91)
  67. # 2016-06-03 Version 1.0 of code for release (1.00)
  68. # 2016-06-09 bugfix of issue 44, and add success statement (ignoring quiet flag) (1.01)
  69. # 2016-06-13 test return status of DNS_ADD_COMMAND and error_exit if a problem (hadleyrich) (1.02)
  70. # 2016-06-13 bugfix of issue 45, problem with SERVER_TYPE when it's just a port number (1.03)
  71. # 2016-06-13 bugfix issue 47 - DNS_DEL_COMMAND cleanup was run when not required. (1.04)
  72. # 2016-06-15 add error checking on RELOAD_CMD (1.05)
  73. # 2016-06-20 updated sed and date functions to run on MAC OS X (1.06)
  74. # 2016-06-20 added CHALLENGE_CHECK_TYPE variable to allow checks direct on https rather than http (1.07)
  75. # 2016-06-21 updated grep functions to run on MAC OS X (1.08)
  76. # 2016-06-11 updated to enable running on windows with cygwin (1.09)
  77. # 2016-07-02 Corrections to work with older slackware issue #56 (1.10)
  78. # 2016-07-02 Updating help info re ACL in config file (1.11)
  79. # 2016-07-04 adding DOMAIN_STORAGE as a variable to solve for issue #59 (1.12)
  80. # 2016-07-05 updated order to better handle non-standard DOMAIN_STORAGE location (1.13)
  81. # 2016-07-06 added additional comments about SANS in example template (1.14)
  82. # 2016-07-07 check for duplicate domains in domain / SANS (1.15)
  83. # 2016-07-08 modified to be used on older bash for issue #64 (1.16)
  84. # 2016-07-11 added -w to -a option and comments in domain template (1.17)
  85. # 2016-07-18 remove / regenerate csr when generating new private domain key (1.18)
  86. # 2016-07-21 add output of combined private key and domain cert (1.19)
  87. # 2016-07-21 updated typo (1.20)
  88. # 2016-07-22 corrected issue in nslookup debug option - issue #74 (1.21)
  89. # 2016-07-26 add more server-types based on openssl s_client (1.22)
  90. # 2016-08-01 updated agreement for letsencrypt (1.23)
  91. # 2016-08-02 updated agreement for letsencrypt to update automatically (1.24)
  92. # 2016-08-03 improve messages on test of certificate installation (1.25)
  93. # 2016-08-04 remove carriage return from agreement - issue #80 (1.26)
  94. # 2016-08-04 set permissions for token folders - issue #81 (1.27)
  95. # 2016-08-07 allow default chained file creation - issue #85 (1.28)
  96. # 2016-08-07 use copy rather than move when archiving certs - issue #86 (1.29)
  97. # 2016-08-07 enable use of a single ACL for all checks (if USE_SINGLE_ACL="true" (1.30)
  98. # 2016-08-23 check for already validated domains (issue #93) - (1.31)
  99. # 2016-08-23 updated already validated domains (1.32)
  100. # 2016-08-23 included better force_renew and template for USE_SINGLE_ACL (1.33)
  101. # 2016-08-23 enable insecure certificate on https token check #94 (1.34)
  102. # 2016-08-23 export OPENSSL_CONF so it's used by all openssl commands (1.35)
  103. # 2016-08-25 updated defaults for ACME agreement (1.36)
  104. # 2016-09-04 correct issue #101 when some domains already validated (1.37)
  105. # 2016-09-12 Checks if which is installed (1.38)
  106. # 2016-09-13 Don't check for updates, if -U parameter has been given (1.39)
  107. # 2016-09-17 Improved error messages from invalid certs (1.40)
  108. # 2016-09-19 remove update check on recursive calls when using -a (1.41)
  109. # 2016-09-21 changed shebang for portability (1.42)
  110. # 2016-09-21 Included option to Deactivate an Authorization (1.43)
  111. # 2016-09-22 retry on 500 error from ACME server (1.44)
  112. # 2016-09-22 added additional checks and retry on 500 error from ACME server (1.45)
  113. # 2016-09-24 merged in IPv6 support (1.46)
  114. # 2016-09-27 added additional debug info issue #119 (1.47)
  115. # 2016-09-27 removed IPv6 switch in favour of checking both IPv4 and IPv6 (1.48)
  116. # 2016-09-28 Add -Q, or --mute, switch to mute notifications about successfully upgrading getssl (1.49)
  117. # 2016-09-30 improved portability to work natively on FreeBSD, Slackware and OSX (1.50)
  118. # 2016-09-30 comment out PRIVATE_KEY_ALG from the domain template Issue #125 (1.51)
  119. # 2016-10-03 check remote certificate for right domain before saving to local (1.52)
  120. # 2016-10-04 allow existing CSR with domain name in subject (1.53)
  121. # 2016-10-05 improved the check for CSR with domain in subject (1.54)
  122. # 2016-10-06 prints update info on what was included in latest updates (1.55)
  123. # 2016-10-06 when using -a flag, ignore folders in working directory which aren't domains (1.56)
  124. # 2016-10-12 alllow multiple tokens in DNS challenge (1.57)
  125. # 2016-10-14 added CHECK_ALL_AUTH_DNS option to check all DNS servres, not just one primary server (1.58)
  126. # 2016-10-14 added archive of chain and private key for each cert, and purge old archives (1.59)
  127. # 2016-10-17 updated info comment on failed cert due to rate limits. (1.60)
  128. # 2016-10-17 fix error messages when using 1.0.1e-fips (1.61)
  129. # 2016-10-20 set secure permissions when generating account key (1.62)
  130. # 2016-10-20 set permsissions to 700 for getssl script during upgrade (1.63)
  131. # 2016-10-20 add option to revoke a certificate (1.64)
  132. # 2016-10-21 set revocation server default to acme-v01.api.letsencrypt.org (1.65)
  133. # 2016-10-21 bug fix for revocation on different servers. (1.66)
  134. # 2016-10-22 Tidy up archive code for certificates and reduce permissions for security
  135. # 2016-10-22 Add EC signing for secp384r1 and secp521r1 (the latter not yet supported by Let's Encrypt
  136. # 2016-10-22 Add option to create a new private key for every cert (REUSE_PRIVATE_KEY="true" by default)
  137. # 2016-10-22 Combine EC signing, Private key reuse and archive permissions (1.67)
  138. # 2016-10-25 added CHECK_REMOTE_WAIT option ( to pause before final remote check)
  139. # 2016-10-25 Added EC account key support ( prime256v1, secp384r1 ) (1.68)
  140. # 2016-10-25 Ignore DNS_EXTRA_WAIT if all domains already validated (issue #146) (1.69)
  141. # 2016-10-25 Add option for dual ESA / EDSA certs (1.70)
  142. # 2016-10-25 bug fix Issue #141 challenge error 400 (1.71)
  143. # 2016-10-26 check content of key files, not just recreate if missing.
  144. # 2016-10-26 Improvements on portability (1.72)
  145. # 2016-10-26 Date formatting for busybox (1.73)
  146. # 2016-10-27 bug fix - issue #157 not recognising EC keys on some versions of openssl (1.74)
  147. # 2016-10-31 generate EC account keys and tidy code.
  148. # 2016-10-31 fix warning message if cert doesn't exist (1.75)
  149. # 2016-10-31 remove only specified DNS token #161 (1.76)
  150. # 2016-11-03 Reduce long lines, and remove echo from update (1.77)
  151. # 2016-11-05 added TOKEN_USER_ID (to set ownership of token files )
  152. # 2016-11-05 updated style to work with latest shellcheck (1.78)
  153. # 2016-11-07 style updates
  154. # 2016-11-07 bug fix DOMAIN_PEM_LOCATION starting with ./ #167
  155. # 2016-11-08 Fix for openssl 1.1.0 #166 (1.79)
  156. # 2016-11-08 Add and comment optional sshuserid for ssh ACL (1.80)
  157. # 2016-11-09 Add SKIP_HTTP_TOKEN_CHECK option (Issue #170) (1.81)
  158. # 2016-11-13 bug fix DOMAIN_KEY_CERT generation (1.82)
  159. # 2016-11-17 add PREVENT_NON_INTERACTIVE_RENEWAL option (1.83)
  160. # 2016-12-03 add HTTP_TOKEN_CHECK_WAIT option (1.84)
  161. # ----------------------------------------------------------------------------------------
  162.  
  163. PROGNAME=${0##*/}
  164. VERSION="1.84"
  165.  
  166. # defaults
  167. CODE_LOCATION="https://raw.githubusercontent.com/srvrco/getssl/master/getssl"
  168. CA="https://acme-staging.api.letsencrypt.org"
  169. DEFAULT_REVOKE_CA="https://acme-v01.api.letsencrypt.org"
  170. ACCOUNT_KEY_TYPE="rsa"
  171. ACCOUNT_KEY_LENGTH=4096
  172. WORKING_DIR=~/.getssl
  173. DOMAIN_KEY_LENGTH=4096
  174. SSLCONF="$(openssl version -d 2>/dev/null| cut -d\" -f2)/openssl.cnf"
  175. VALIDATE_VIA_DNS=""
  176. RELOAD_CMD=""
  177. RENEW_ALLOW="30"
  178. REUSE_PRIVATE_KEY="true"
  179. PRIVATE_KEY_ALG="rsa"
  180. SERVER_TYPE="https"
  181. CHECK_REMOTE="true"
  182. USE_SINGLE_ACL="false"
  183. CHECK_ALL_AUTH_DNS="false"
  184. DNS_WAIT=10
  185. DNS_EXTRA_WAIT=""
  186. CHECK_REMOTE_WAIT=0
  187. PUBLIC_DNS_SERVER=""
  188. CHALLENGE_CHECK_TYPE="http"
  189. DEACTIVATE_AUTH="false"
  190. PREVIOUSLY_VALIDATED="true"
  191. DUAL_RSA_ECDSA="false"
  192. SKIP_HTTP_TOKEN_CHECK="false"
  193. HTTP_TOKEN_CHECK_WAIT=0
  194. ORIG_UMASK=$(umask)
  195. _USE_DEBUG=0
  196. _CREATE_CONFIG=0
  197. _CHECK_ALL=0
  198. _FORCE_RENEW=0
  199. _QUIET=0
  200. _MUTE=0
  201. _UPGRADE=0
  202. _UPGRADE_CHECK=1
  203. _RECREATE_CSR=0
  204. _REVOKE=0
  205.  
  206. # store copy of original command in case of upgrading script and re-running
  207. ORIGCMD="$0 $*"
  208.  
  209. # Define all functions (in alphabetical order)
  210.  
  211. cert_archive() { # Archive certificate file by copying with dates at end.
  212. debug "creating an achive copy of current new certs"
  213. date_time=$(date +%Y_%m_%d_%H_%M)
  214. mkdir -p "${DOMAIN_DIR}/archive/${date_time}"
  215. umask 077
  216. cp "$CERT_FILE" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.crt"
  217. cp "$CERT_FILE" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.csr"
  218. cp "$DOMAIN_DIR/${DOMAIN}.key" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.key"
  219. cp "$CA_CERT" "${DOMAIN_DIR}/archive/${date_time}/chain.crt"
  220. if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
  221. cp "$CERT_FILE" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.crt"
  222. cp "$CERT_FILE" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.csr"
  223. cp "$DOMAIN_DIR/${DOMAIN}.key" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.key"
  224. cp "$CA_CERT" "${DOMAIN_DIR}/archive/${date_time}/chain.ec.crt"
  225. fi
  226. umask "$ORIG_UMASK"
  227. debug "purging old GetSSL archives"
  228. purge_archive "$DOMAIN_DIR"
  229. }
  230.  
  231. check_challenge_completion() { # checks with the ACME server if our challenge is OK
  232. uri=$1
  233. domain=$2
  234. keyauthorization=$3
  235.  
  236. debug "sending request to ACME server saying we're ready for challenge"
  237. send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
  238.  
  239. # check response from our request to perform challenge
  240. if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then
  241. error_exit "$domain:Challenge error: $code"
  242. fi
  243.  
  244. # loop "forever" to keep checking for a response from the ACME server.
  245. while true ; do
  246. debug "checking"
  247. if ! get_cr "$uri" ; then
  248. error_exit "$domain:Verify error:$code"
  249. fi
  250.  
  251. status=$(json_get "$response" status)
  252.  
  253. # If ACME response is valid, then break out of loop
  254. if [[ "$status" == "valid" ]] ; then
  255. info "Verified $domain"
  256. break;
  257. fi
  258.  
  259. # if ACME response is that their check gave an invalid response, error exit
  260. if [[ "$status" == "invalid" ]] ; then
  261. err_detail=$(json_get "$response" detail)
  262. error_exit "$domain:Verify error:$err_detail"
  263. fi
  264.  
  265. # if ACME response is pending ( they haven't completed checks yet) then wait and try again.
  266. if [[ "$status" == "pending" ]] ; then
  267. info "Pending"
  268. else
  269. error_exit "$domain:Verify error:$response"
  270. fi
  271. debug "sleep 5 secs before testing verify again"
  272. sleep 5
  273. done
  274.  
  275. if [[ "$DEACTIVATE_AUTH" == "true" ]]; then
  276. deactivate_url=$(echo "$responseHeaders" | grep "^Link" | awk -F"[<>]" '{print $2}')
  277. deactivate_url_list="$deactivate_url_list $deactivate_url"
  278. debug "adding url to deactivate list - $deactivate_url"
  279. fi
  280. }
  281.  
  282. check_getssl_upgrade() { # check if a more recent version of code is available available
  283. temp_upgrade="$(mktemp)"
  284. curl --silent "$CODE_LOCATION" --output "$temp_upgrade"
  285. errcode=$?
  286. if [[ $errcode -eq 60 ]]; then
  287. error_exit "curl needs updating, your version does not support SNI (multiple SSL domains on a single IP)"
  288. elif [[ $errcode -gt 0 ]]; then
  289. error_exit "curl error : $errcode"
  290. fi
  291. latestversion=$(awk -F '"' '$1 == "VERSION=" {print $2}' "$temp_upgrade")
  292. latestvdec=$(echo "$latestversion"| tr -d '.')
  293. localvdec=$(echo "$VERSION"| tr -d '.' )
  294. debug "current code is version ${VERSION}"
  295. debug "Most recent version is ${latestversion}"
  296. # use a default of 0 for cases where the latest code has not been obtained.
  297. if [[ "${latestvdec:-0}" -gt "$localvdec" ]]; then
  298. if [[ ${_UPGRADE} -eq 1 ]]; then
  299. install "$0" "${0}.v${VERSION}"
  300. install -m 700 "$temp_upgrade" "$0"
  301. if [[ ${_MUTE} -eq 0 ]]; then
  302. echo "Updated getssl from v${VERSION} to v${latestversion}"
  303. echo "these update notification can be turned off using the -Q option"
  304. echo ""
  305. echo "Updates are;"
  306. awk "/\(${VERSION}\)$/ {s=1} s; /\(${latestversion}\)$/ {s=0}" "$temp_upgrade" | awk '{if(NR>1)print}'
  307. echo ""
  308. fi
  309. eval "$ORIGCMD"
  310. graceful_exit
  311. else
  312. info ""
  313. info "A more recent version (v${latestversion}) of getssl is available, please update"
  314. info "the easiest way is to use the -u or --upgrade flag"
  315. info ""
  316. fi
  317. fi
  318. rm -f "$temp_upgrade"
  319. }
  320.  
  321. clean_up() { # Perform pre-exit housekeeping
  322. umask "$ORIG_UMASK"
  323. if [[ $VALIDATE_VIA_DNS == "true" ]]; then
  324. # Tidy up DNS entries if things failed part way though.
  325. shopt -s nullglob
  326. for dnsfile in $TEMP_DIR/dns_verify/*; do
  327. # shellcheck source=/dev/null
  328. . "$dnsfile"
  329. debug "attempting to clean up DNS entry for $d"
  330. eval "$DNS_DEL_COMMAND" "$d" "$auth_key"
  331. done
  332. shopt -u nullglob
  333. fi
  334. if [[ ! -z "$DOMAIN_DIR" ]]; then
  335. rm -rf "${TEMP_DIR:?}"
  336. fi
  337. }
  338.  
  339. copy_file_to_location() { # copies a file, using scp if required.
  340. cert=$1 # descriptive name, just used for display
  341. from=$2 # current file location
  342. to=$3 # location to move file to.
  343. if [[ ! -z "$to" ]]; then
  344. info "copying $cert to $to"
  345. debug "copying from $from to $to"
  346. if [[ "${to:0:4}" == "ssh:" ]] ; then
  347. debug "using scp scp -q $from ${to:4}"
  348. if ! scp -q "$from" "${to:4}" >/dev/null 2>&1 ; then
  349. error_exit "problem copying file to the server using scp.
  350. scp $from ${to:4}"
  351. fi
  352. debug "userid $TOKEN_USER_ID"
  353. if [[ ! -z "$TOKEN_USER_ID" ]]; then
  354. servername=$(echo "$to" | awk -F":" '{print $2}')
  355. tofile=$(echo "$to" | awk -F":" '{print $3}')
  356. debug "servername $servername"
  357. debug "file $tofile"
  358. # shellcheck disable=SC2029
  359. ssh "$servername" "chown $TOKEN_USER_ID $tofile"
  360. fi
  361. elif [[ "${to:0:4}" == "ftp:" ]] ; then
  362. if [[ "$cert" != "challenge token" ]] ; then
  363. error_exit "ftp is not a sercure method for copying certificates or keys"
  364. fi
  365. debug "using ftp to copy the file from $from"
  366. ftpuser=$(echo "$to"| awk -F: '{print $2}')
  367. ftppass=$(echo "$to"| awk -F: '{print $3}')
  368. ftphost=$(echo "$to"| awk -F: '{print $4}')
  369. ftplocn=$(echo "$to"| awk -F: '{print $5}')
  370. ftpdirn=$(dirname "$ftplocn")
  371. ftpfile=$(basename "$ftplocn")
  372. fromdir=$(dirname "$from")
  373. fromfile=$(basename "$from")
  374. debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile"
  375. debug "from dir=$fromdir file=$fromfile"
  376. ftp -n <<- _EOF
  377. open $ftphost
  378. user $ftpuser $ftppass
  379. cd $ftpdirn
  380. lcd $fromdir
  381. put $fromfile
  382. _EOF
  383. elif [[ "${to:0:5}" == "sftp:" ]] ; then
  384. debug "using sftp to copy the file from $from"
  385. ftpuser=$(echo "$to"| awk -F: '{print $2}')
  386. ftppass=$(echo "$to"| awk -F: '{print $3}')
  387. ftphost=$(echo "$to"| awk -F: '{print $4}')
  388. ftplocn=$(echo "$to"| awk -F: '{print $5}')
  389. ftpdirn=$(dirname "$ftplocn")
  390. ftpfile=$(basename "$ftplocn")
  391. fromdir=$(dirname "$from")
  392. fromfile=$(basename "$from")
  393. debug "sftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile"
  394. debug "from dir=$fromdir file=$fromfile"
  395. sshpass -p "$ftppass" sftp "$ftpuser@$ftphost" <<- _EOF
  396. cd $ftpdirn
  397. lcd $fromdir
  398. put $fromfile
  399. _EOF
  400. else
  401. if ! mkdir -p "$(dirname "$to")" ; then
  402. error_exit "cannot create ACL directory $(basename "$to")"
  403. fi
  404. if ! cp -p "$from" "$to" ; then
  405. error_exit "cannot copy $from to $to"
  406. fi
  407. if [[ ! -z "$TOKEN_USER_ID" ]]; then
  408. chown "$TOKEN_USER_ID" "$to"
  409. fi
  410. fi
  411. debug "copied $from to $to"
  412. fi
  413. }
  414.  
  415. create_csr() { # create a csr using a given key (if it doesn't already exist)
  416. csr_file=$1
  417. csr_key=$2
  418. # check if domain csr exists - if not then create it
  419. if [[ -s "$csr_file" ]]; then
  420. debug "domain csr exists at - $csr_file"
  421. # check all domains in config are in csr
  422. alldomains=$(echo "$DOMAIN,$SANS" | sed -e 's/ //g; y/,/\n/' | sort -u)
  423. domains_in_csr=$(openssl req -text -noout -in "$csr_file" \
  424. | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \
  425. | sort -u)
  426. for d in $alldomains; do
  427. if [[ "$(echo "${domains_in_csr}"| grep "^${d}$")" != "${d}" ]]; then
  428. info "existing csr at $csr_file does not contain ${d} - re-create-csr"\
  429. ".... $(echo "${domains_in_csr}"| grep "^${d}$")"
  430. _RECREATE_CSR=1
  431. fi
  432. done
  433. # check all domains in csr are in config
  434. if [[ "$alldomains" != "$domains_in_csr" ]]; then
  435. info "existing csr at $csr_file does not have the same domains as the config - re-create-csr"
  436. _RECREATE_CSR=1
  437. fi
  438. fi
  439. # end of ... check if domain csr exists - if not then create it
  440.  
  441. # if CSR does not exist, or flag set to recreate, then create csr
  442. if [[ ! -s "$csr_file" ]] || [[ "$_RECREATE_CSR" == "1" ]]; then
  443. info "creating domain csr - $csr_file"
  444. # create a temporary config file, for portability.
  445. tmp_conf=$(mktemp)
  446. cat "$SSLCONF" > "$tmp_conf"
  447. printf "[SAN]\n%s" "$SANLIST" >> "$tmp_conf"
  448. openssl req -new -sha256 -key "$csr_key" -subj "/" -reqexts SAN -config "$tmp_conf" > "$csr_file"
  449. rm -f "$tmp_conf"
  450. fi
  451. }
  452.  
  453. create_key() { # create a domain key (if it doesn't already exist)
  454. key_type=$1 # domain key type
  455. key_loc=$2 # domain key location
  456. key_len=$3 # domain key length - for rsa keys.
  457. # check if domain key exists, if not then create it.
  458. if [[ -s "$key_loc" ]]; then
  459. debug "domain key exists at $key_loc - skipping generation"
  460. # ideally need to check validity of domain key
  461. else
  462. umask 077
  463. info "creating domain key - $key_loc"
  464. case "$key_type" in
  465. rsa)
  466. openssl genrsa "$key_len" > "$key_loc";;
  467. prime256v1|secp384r1|secp521r1)
  468. openssl ecparam -genkey -name "$key_type" > "$key_loc";;
  469. *)
  470. error_exit "unknown private key algorithm type $key_loc";;
  471. esac
  472. umask "$ORIG_UMASK"
  473. # remove csr on generation of new domain key
  474. rm -f "${key_loc::-4}.csr"
  475. fi
  476. }
  477.  
  478. date_epoc() { # convert the date into epoch time
  479. if [[ "$os" == "bsd" ]]; then
  480. date -j -f "%b %d %T %Y %Z" "$1" +%s
  481. elif [[ "$os" == "mac" ]]; then
  482. date -j -f "%b %d %T %Y %Z" "$1" +%s
  483. elif [[ "$os" == "busybox" ]]; then
  484. de_ld=$(echo "$1" | awk '{print $1 $2 $3 $4}')
  485. date -D "%b %d %T %Y" -d "$de_ld" +%s
  486. else
  487. date -d "$1" +%s
  488. fi
  489.  
  490. }
  491.  
  492. date_fmt() { # format date from epoc time to YYYY-MM-DD
  493. if [[ "$os" == "bsd" ]]; then #uses older style date function.
  494. date -j -f "%s" "$1" +%F
  495. elif [[ "$os" == "mac" ]]; then # MAC OSX uses older BSD style date.
  496. date -j -f "%s" "$1" +%F
  497. else
  498. date -d "@$1" +%F
  499. fi
  500. }
  501.  
  502. date_renew() { # calculates the renewal time in epoch
  503. date_now_s=$( date +%s )
  504. echo "$((date_now_s + RENEW_ALLOW*24*60*60))"
  505. }
  506.  
  507. debug() { # write out debug info if the debug flag has been set
  508. if [[ ${_USE_DEBUG} -eq 1 ]]; then
  509. echo " "
  510. echo "$@"
  511. fi
  512. }
  513.  
  514. error_exit() { # give error message on error exit
  515. echo -e "${PROGNAME}: ${1:-"Unknown Error"}" >&2
  516. clean_up
  517. exit 1
  518. }
  519.  
  520. get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns )
  521. gad_d="$1" # domain name
  522. gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER
  523.  
  524. if [[ "$os" == "cygwin" ]]; then
  525. all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \
  526. | grep "primary name server" \
  527. | awk '{print $NF}')
  528. if [[ -z "$all_auth_dns_servers" ]]; then
  529. error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config"
  530. fi
  531. primary_ns="$all_auth_dns_servers"
  532. return
  533. fi
  534.  
  535. res=$(nslookup -debug=1 -type=soa -type=ns "$1" ${gad_s})
  536.  
  537. if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then
  538. # this is a Non-authoritative server, need to check for an authoritative one.
  539. gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g')
  540. if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
  541. # if domain name doesn't exist, then find auth servers for next level up
  542. gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }')
  543. gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}')
  544. fi
  545. fi
  546.  
  547. if [[ -z "$gad_s" ]]; then
  548. res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d")
  549. else
  550. res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d" "${gad_s}")
  551. fi
  552.  
  553. if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then
  554. gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g')
  555. elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
  556. gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }')
  557. gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}')
  558. fi
  559.  
  560. all_auth_dns_servers=$(nslookup -type=soa -type=ns "$gad_d" "$gad_s" \
  561. | awk ' $2 ~ "nameserver" {print $4}' \
  562. | sed 's/\.$//g'| tr '\n' ' ')
  563. if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
  564. primary_ns="$all_auth_dns_servers"
  565. else
  566. primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
  567. fi
  568. }
  569.  
  570. get_certificate() { # get certificate for csr, if all domains validated.
  571. gc_csr=$1 # the csr file
  572. gc_certfile=$2 # The filename for the certificate
  573. gc_cafile=$3 # The filename for the CA certificate
  574.  
  575. der=$(openssl req -in "$gc_csr" -outform DER | urlbase64)
  576. debug "der $der"
  577. send_signed_request "$CA/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
  578.  
  579. # convert certificate information into correct format and save to file.
  580. CertData=$(awk ' $1 ~ "^Location" {print $2}' "$CURL_HEADER" |tr -d '\r')
  581. debug "certdata location = $CertData"
  582. if [[ "$CertData" ]] ; then
  583. echo -----BEGIN CERTIFICATE----- > "$gc_certfile"
  584. curl --silent "$CertData" | openssl base64 -e >> "$gc_certfile"
  585. echo -----END CERTIFICATE----- >> "$gc_certfile"
  586. info "Certificate saved in $CERT_FILE"
  587. fi
  588.  
  589. # If certificate wasn't a valid certificate, error exit.
  590. if [[ -z "$CertData" ]] ; then
  591. response2=$(echo "$response" | fold -w64 |openssl base64 -d)
  592. debug "response was $response"
  593. error_exit "Sign failed: $(echo "$response2" | grep "detail")"
  594. fi
  595.  
  596. # get a copy of the CA certificate.
  597. IssuerData=$(grep -i '^Link' "$CURL_HEADER" \
  598. | cut -d " " -f 2\
  599. | cut -d ';' -f 1 \
  600. | sed 's/<//g' \
  601. | sed 's/>//g')
  602. if [[ "$IssuerData" ]] ; then
  603. echo -----BEGIN CERTIFICATE----- > "$gc_cafile"
  604. curl --silent "$IssuerData" | openssl base64 -e >> "$gc_cafile"
  605. echo -----END CERTIFICATE----- >> "$gc_cafile"
  606. info "The intermediate CA cert is in $gc_cafile"
  607. fi
  608. }
  609.  
  610. get_cr() { # get curl response
  611. url="$1"
  612. debug url "$url"
  613. response=$(curl --silent "$url")
  614. ret=$?
  615. debug response "$response"
  616. code=$(json_get "$response" status)
  617. debug code "$code"
  618. debug "get_cr return code $ret"
  619. return $ret
  620. }
  621.  
  622. get_os() { # function to get the current Operating System
  623. uname_res=$(uname -s)
  624. if [[ $(date -h 2>&1 | grep -ic busybox) -gt 0 ]]; then
  625. os="busybox"
  626. elif [[ ${uname_res} == "Linux" ]]; then
  627. os="linux"
  628. elif [[ ${uname_res} == "FreeBSD" ]]; then
  629. os="bsd"
  630. elif [[ ${uname_res} == "Darwin" ]]; then
  631. os="mac"
  632. elif [[ ${uname_res:0:6} == "CYGWIN" ]]; then
  633. os="cygwin"
  634. else
  635. os="unknown"
  636. fi
  637. debug "detected os type = $os"
  638. }
  639.  
  640. get_signing_params() { # get signing parameters from key
  641. skey=$1
  642. if [[ "$(grep -c "RSA PRIVATE KEY" "$skey")" -gt 0 ]]; then # RSA key
  643. pub_exp64=$(openssl rsa -in "${skey}" -noout -text \
  644. | grep publicExponent \
  645. | grep -oE "0x[a-f0-9]+" \
  646. | cut -d'x' -f2 \
  647. | hex2bin \
  648. | urlbase64)
  649. pub_mod64=$(openssl rsa -in "${skey}" -noout -modulus \
  650. | cut -d'=' -f2 \
  651. | hex2bin \
  652. | urlbase64)
  653.  
  654. jwk='{"e":"'"${pub_exp64}"'","kty":"RSA","n":"'"${pub_mod64}"'"}'
  655. jwkalg="RS256"
  656. signalg="sha256"
  657. elif [[ "$(grep -c "EC PRIVATE KEY" "$skey")" -gt 0 ]]; then # Elliptic curve key.
  658. crv="$(openssl ec -in "$skey" -noout -text 2>/dev/null | awk '$2 ~ "CURVE:" {print $3}')"
  659. if [[ -z "$crv" ]]; then
  660. gsp_keytype="$(openssl ec -in "$skey" -noout -text 2>/dev/null \
  661. | grep "^ASN1 OID:" \
  662. | awk '{print $3}')"
  663. case "$gsp_keytype" in
  664. prime256v1) crv="P-256" ;;
  665. secp384r1) crv="P-384" ;;
  666. secp521r1) crv="P-521" ;;
  667. *) error_exit "invalid curve algorithm type $gsp_keytype";;
  668. esac
  669. fi
  670. case "$crv" in
  671. P-256) jwkalg="ES256" ; signalg="sha256" ;;
  672. P-384) jwkalg="ES384" ; signalg="sha384" ;;
  673. P-521) jwkalg="ES512" ; signalg="sha512" ;;
  674. *) error_exit "invalid curve algorithm type $crv";;
  675. esac
  676. pubtext="$(openssl ec -in "$skey" -noout -text 2>/dev/null \
  677. | awk '/^pub:/{p=1;next}/^ASN1 OID:/{p=0}p' \
  678. | tr -d ": \n\r")"
  679. mid=$(( (${#pubtext} -2) / 2 + 2 ))
  680. debug "pubtext = $pubtext"
  681. x64=$(echo "$pubtext" | cut -b 3-$mid | hex2bin | urlbase64)
  682. y64=$(echo "$pubtext" | cut -b $((mid+1))-${#pubtext} | hex2bin | urlbase64)
  683. jwk='{"crv":"'"$crv"'","kty":"EC","x":"'"$x64"'","y":"'"$y64"'"}'
  684. debug "jwk $jwk"
  685. else
  686. error_exit "Invlid key file"
  687. fi
  688. thumbprint="$(printf "%s" "$jwk" | openssl dgst -sha256 -binary | urlbase64)"
  689. debug "jwk alg = $jwkalg"
  690. debug "jwk = $jwk"
  691. debug "thumbprint $thumbprint"
  692. }
  693.  
  694. graceful_exit() { # normal exit function.
  695. clean_up
  696. exit
  697. }
  698.  
  699. help_message() { # print out the help message
  700. cat <<- _EOF_
  701. $PROGNAME ver. $VERSION
  702. Obtain SSL certificates from the letsencrypt.org ACME server
  703.  
  704. $(usage)
  705.  
  706. Options:
  707. -a, --all Check all certificates
  708. -d, --debug Outputs debug information
  709. -c, --create Create default config files
  710. -f, --force Force renewal of cert (overrides expiry checks)
  711. -h, --help Display this help message and exit
  712. -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded)
  713. -Q, --mute Like -q, but mutes notification about successful upgrade
  714. -r, --revoke cert key [CA_server] Revoke a certificate (the cert and key are required)
  715. -u, --upgrade Upgrade getssl if a more recent version is available
  716. -U, --nocheck Do not check if a more recent version is available
  717. -w working_dir Working directory
  718.  
  719. _EOF_
  720. }
  721.  
  722. hex2bin() { # Remove spaces, add leading zero, escape as hex string and parse with printf
  723. # printf -- "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')"
  724. echo -e -n "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')"
  725. }
  726.  
  727. info() { # write out info as long as the quiet flag has not been set.
  728. if [[ ${_QUIET} -eq 0 ]]; then
  729. echo "$@"
  730. fi
  731. }
  732.  
  733. json_get() { # get the value corresponding to $2 in the JSON passed as $1.
  734. # remove newlines, so it's a single chunk of JSON
  735. json_data=$( echo "$1" | tr '\n' ' ')
  736. # if $3 is defined, this is the section which the item is in.
  737. if [[ ! -z "$3" ]]; then
  738. jg_section=$(echo "$json_data" | awk -F"[}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${3}"'\"/){print $i}}}')
  739. if [[ "$2" == "uri" ]]; then
  740. jg_subsect=$(echo "$jg_section" | awk -F"[,]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i)}}}')
  741. jg_result=$(echo "$jg_subsect" | awk -F'"' '{print $4}')
  742. else
  743. jg_result=$(echo "$jg_section" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}')
  744. fi
  745. else
  746. jg_result=$(echo "$json_data" |awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}')
  747. fi
  748. # check number of quotes
  749. jg_q=${jg_result//[^\"]/}
  750. # if 2 quotes, assume it's a quoted variable and just return the data within the quotes.
  751. if [[ ${#jg_q} -eq 2 ]]; then
  752. echo "$jg_result" | awk -F'"' '{print $2}'
  753. else
  754. echo "$jg_result"
  755. fi
  756. }
  757.  
  758. os_esed() { # Use different sed version for different os types (extended regex)
  759. if [[ "$os" == "bsd" ]]; then # BSD requires -E flag for extended regex
  760. sed -E "${@}"
  761. elif [[ "$os" == "mac" ]]; then # MAC uses older BSD style sed.
  762. sed -E "${@}"
  763. else
  764. sed -r "${@}"
  765. fi
  766. }
  767.  
  768. purge_archive() { # purge archive of old, invalid, certificates
  769. arcdir="$1/archive"
  770. debug "purging archives in ${arcdir}/"
  771. for padir in $arcdir/????_??_??_??_??; do
  772. # check each directory
  773. if [[ -d "$padir" ]]; then
  774. tstamp=$(basename "$padir"| awk -F"_" '{print $1"-"$2"-"$3" "$4":"$5}')
  775. if [[ "$os" == "bsd" ]]; then
  776. direpoc=$(date -j -f "%F %H:%M" "$tstamp" +%s)
  777. elif [[ "$os" == "mac" ]]; then
  778. direpoc=$(date -j -f "%F %H:%M" "$tstamp" +%s)
  779. else
  780. direpoc=$(date -d "$tstamp" +%s)
  781. fi
  782. current_epoc=$(date "+%s")
  783. # as certs currently valid for 90 days, purge anything older than 100
  784. purgedate=$((current_epoc - 60*60*24*100))
  785. if [[ "$direpoc" -lt "$purgedate" ]]; then
  786. echo "purge $padir"
  787. rm -rf "${padir:?}"
  788. fi
  789. fi
  790. done
  791. }
  792.  
  793. reload_service() { # Runs a command to reload services ( via ssh if needed)
  794. if [[ ! -z "$RELOAD_CMD" ]]; then
  795. info "reloading SSL services"
  796. if [[ "${RELOAD_CMD:0:4}" == "ssh:" ]] ; then
  797. sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}')
  798. command=${RELOAD_CMD:(( ${#sshhost} + 5))}
  799. debug "running following command to reload cert"
  800. debug "ssh $sshhost ${command}"
  801. # shellcheck disable=SC2029
  802. ssh "$sshhost" "${command}" 1>/dev/null 2>&1
  803. # allow 2 seconds for services to restart
  804. sleep 2
  805. else
  806. debug "running reload command $RELOAD_CMD"
  807. if ! eval "$RELOAD_CMD" ; then
  808. error_exit "error running $RELOAD_CMD"
  809. fi
  810. fi
  811. fi
  812. }
  813.  
  814. revoke_certificate() { #revoke a certificate
  815. debug "revoking cert $REVOKE_CERT"
  816. debug "using key $REVOKE_KEY"
  817. ACCOUNT_KEY="$REVOKE_KEY"
  818. # need to set the revoke key as "account_key" since it's used in send_signed_request.
  819. get_signing_params "$REVOKE_KEY"
  820. TEMP_DIR=$(mktemp -d)
  821. debug "revoking from $CA"
  822. rcertdata=$(openssl x509 -in "$REVOKE_CERT" -inform PEM -outform DER | urlbase64)
  823. send_signed_request "$CA/acme/revoke-cert" "{\"resource\": \"revoke-cert\", \"certificate\": \"$rcertdata\"}"
  824. if [[ $code -eq "200" ]]; then
  825. info "certificate revoked"
  826. else
  827. error_exit "Revocation failed: $(echo "$response" | grep "detail")"
  828. fi
  829. }
  830.  
  831. requires() { # check if required function is available
  832. result=$(which "$1" 2>/dev/null)
  833. debug "checking for required $1 ... $result"
  834. if [[ -z "$result" ]]; then
  835. error_exit "This script requires $1 installed"
  836. fi
  837. }
  838.  
  839. send_signed_request() { # Sends a request to the ACME server, signed with your private key.
  840. url=$1
  841. payload=$2
  842. needbase64=$3
  843.  
  844. debug url "$url"
  845. debug payload "$payload"
  846.  
  847. CURL_HEADER="$TEMP_DIR/curl.header"
  848. dp="$TEMP_DIR/curl.dump"
  849. CURL="curl --silent --dump-header $CURL_HEADER "
  850. if [[ ${_USE_DEBUG} -eq 1 ]]; then
  851. CURL="$CURL --trace-ascii $dp "
  852. fi
  853.  
  854. # convert payload to url base 64
  855. payload64="$(printf '%s' "${payload}" | urlbase64)"
  856. debug payload64 "$payload64"
  857.  
  858. # get nonce from ACME server
  859. nonceurl="$CA/directory"
  860. nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ')
  861.  
  862. debug nonce "$nonce"
  863.  
  864. # Build header with just our public key and algorithm information
  865. header='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"'}'
  866.  
  867. # Build another header which also contains the previously received nonce and encode it as urlbase64
  868. protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}'
  869. protected64="$(printf '%s' "${protected}" | urlbase64)"
  870. debug protected "$protected"
  871.  
  872. # Sign header with nonce and our payload with our private key and encode signature as urlbase64
  873. sign_string "$(printf '%s' "${protected64}.${payload64}")" "${ACCOUNT_KEY}" "$signalg"
  874.  
  875. # Send header + extended header + payload + signature to the acme-server
  876. body="{\"header\": ${header},"
  877. body+="\"protected\": \"${protected64}\","
  878. body+="\"payload\": \"${payload64}\","
  879. body+="\"signature\": \"${signed64}\"}"
  880. debug "header, payload and signature = $body"
  881.  
  882. code="500"
  883. loop_limit=5
  884. while [[ "$code" -eq 500 ]]; do
  885. if [[ "$needbase64" ]] ; then
  886. response=$($CURL -X POST --data "$body" "$url" | urlbase64)
  887. else
  888. response=$($CURL -X POST --data "$body" "$url")
  889. fi
  890.  
  891. responseHeaders=$(cat "$CURL_HEADER")
  892. debug responseHeaders "$responseHeaders"
  893. debug response "$response"
  894. code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1)
  895. debug code "$code"
  896. response_status=$(json_get "$response" status \
  897. | head -1| awk -F'"' '{print $2}')
  898. debug "response status = $response_status"
  899.  
  900. if [[ "$code" -eq 500 ]]; then
  901. info "error on acme server - trying again ...."
  902. sleep 2
  903. loop_limit=$((loop_limit - 1))
  904. if [[ $loop_limit -lt 1 ]]; then
  905. error_exit "500 error from ACME server: $response"
  906. fi
  907. fi
  908. done
  909. }
  910.  
  911. sign_string() { #sign a string with a given key and algorithm and return urlbase64
  912. # sets the result in variable signed64
  913. str=$1
  914. key=$2
  915. signalg=$3
  916.  
  917. if [[ "$(grep -c "RSA PRIVATE KEY" "$key")" -gt 0 ]]; then # RSA key
  918. signed64="$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" | urlbase64)"
  919. elif [[ "$(grep -c "EC PRIVATE KEY" "$key")" -gt 0 ]]; then # Elliptic curve key.
  920. signed=$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" -hex | awk '{print $2}')
  921. debug "EC signature $signed"
  922. if [[ "${signed:4:4}" == "0220" ]]; then #sha256
  923. R=$(echo "$signed" | cut -c 9-72)
  924. part2=$(echo "$signed" | cut -c 73-)
  925. elif [[ "${signed:4:4}" == "0221" ]]; then #sha256
  926. R=$(echo "$signed" | cut -c 11-74)
  927. part2=$(echo "$signed" | cut -c 75-)
  928. elif [[ "${signed:4:4}" == "0230" ]]; then #sha384
  929. R=$(echo "$signed" | cut -c 9-104)
  930. part2=$(echo "$signed" | cut -c 105-)
  931. elif [[ "${signed:4:4}" == "0231" ]]; then #sha384
  932. R=$(echo "$signed" | cut -c 11-106)
  933. part2=$(echo "$signed" | cut -c 107-)
  934. elif [[ "${signed:6:4}" == "0241" ]]; then #sha512
  935. R=$(echo "$signed" | cut -c 11-140)
  936. part2=$(echo "$signed" | cut -c 141-)
  937. elif [[ "${signed:6:4}" == "0242" ]]; then #sha512
  938. R=$(echo "$signed" | cut -c 11-142)
  939. part2=$(echo "$signed" | cut -c 143-)
  940. else
  941. error_exit "error in EC signing couldn't get R from $signed"
  942. fi
  943. debug "R $R"
  944.  
  945. if [[ "${part2:0:4}" == "0220" ]]; then #sha256
  946. S=$(echo "$part2" | cut -c 5-68)
  947. elif [[ "${part2:0:4}" == "0221" ]]; then #sha256
  948. S=$(echo "$part2" | cut -c 7-70)
  949. elif [[ "${part2:0:4}" == "0230" ]]; then #sha384
  950. S=$(echo "$part2" | cut -c 5-100)
  951. elif [[ "${part2:0:4}" == "0231" ]]; then #sha384
  952. S=$(echo "$part2" | cut -c 7-102)
  953. elif [[ "${part2:0:4}" == "0241" ]]; then #sha512
  954. S=$(echo "$part2" | cut -c 5-136)
  955. elif [[ "${part2:0:4}" == "0242" ]]; then #sha512
  956. S=$(echo "$part2" | cut -c 5-136)
  957. else
  958. error_exit "error in EC signing couldn't get S from $signed"
  959. fi
  960.  
  961. debug "S $S"
  962. signed64=$(printf '%s' "${R}${S}" | hex2bin | urlbase64 )
  963. debug "encoded RS $signed64"
  964. fi
  965. }
  966.  
  967. signal_exit() { # Handle trapped signals
  968. case $1 in
  969. INT)
  970. error_exit "Program interrupted by user" ;;
  971. TERM)
  972. echo -e "\n$PROGNAME: Program terminated" >&2
  973. graceful_exit ;;
  974. *)
  975. error_exit "$PROGNAME: Terminating on unknown signal" ;;
  976. esac
  977. }
  978.  
  979. urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and '/' replaced with '_'
  980. openssl base64 -e | tr -d '\n\r' | os_esed -e 's:=*$::g' -e 'y:+/:-_:'
  981. }
  982.  
  983. usage() { # program usage
  984. echo "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet]"\
  985. "[-Q|--mute] [-u|--upgrade] [-U|--nocheck] [-r|--revoke cert key] [-w working_dir] domain"
  986. }
  987.  
  988. write_domain_template() { # write out a template file for a domain.
  989. cat > "$1" <<- _EOF_domain_
  990. # Uncomment and modify any variables you need
  991. # see https://github.com/srvrco/getssl/wiki/Config-variables for details
  992. #
  993. # The staging server is best for testing
  994. #CA="https://acme-staging.api.letsencrypt.org"
  995. # This server issues full certificates, however has rate limits
  996. #CA="https://acme-v01.api.letsencrypt.org"
  997.  
  998. #AGREEMENT="$AGREEMENT"
  999.  
  1000. # Set an email address associated with your account - generally set at account level rather than domain.
  1001. #ACCOUNT_EMAIL="me@example.com"
  1002. #ACCOUNT_KEY_LENGTH=4096
  1003. #ACCOUNT_KEY="$WORKING_DIR/account.key"
  1004. #PRIVATE_KEY_ALG="rsa"
  1005.  
  1006. # Additional domains - this could be multiple domains / subdomains in a comma separated list
  1007. # Note: this is Additional domains - so should not include the primary domain.
  1008. SANS=${EX_SANS}
  1009.  
  1010. # Acme Challenge Location. The first line for the domain, the following ones for each additional domain.
  1011. # If these start with ssh: then the next variable is assumed to be the hostname and the rest the location.
  1012. # An ssh key will be needed to provide you with access to the remote server.
  1013. # Optionally, you can specify a different userid for ssh/scp to use on the remote server before the @ sign.
  1014. # If left blank, the username on the local server will be used to authenticate against the remote server.
  1015. # If these start with ftp: then the next variables are ftpuserid:ftppassword:servername:ACL_location
  1016. # These should be of the form "/path/to/your/website/folder/.well-known/acme-challenge"
  1017. # where "/path/to/your/website/folder/" is the path, on your web server, to the web root for your domain.
  1018. #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge'
  1019. # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge'
  1020. # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge'
  1021. # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge')
  1022.  
  1023. #Enable use of a single ACL for all checks
  1024. #USE_SINGLE_ACL="true"
  1025.  
  1026. # Location for all your certs, these can either be on the server (full path name)
  1027. # or using ssh /sftp as for the ACL
  1028. #DOMAIN_CERT_LOCATION="ssh:server5:/etc/ssl/domain.crt"
  1029. #DOMAIN_KEY_LOCATION="ssh:server5:/etc/ssl/domain.key"
  1030. #CA_CERT_LOCATION="/etc/ssl/chain.crt"
  1031. #DOMAIN_CHAIN_LOCATION="" # this is the domain cert and CA cert
  1032. #DOMAIN_KEY_CERT_LOCATION="" # this is the domain_key and domain cert
  1033. #DOMAIN_PEM_LOCATION="" # this is the domain_key. domain cert and CA cert
  1034.  
  1035. # The command needed to reload apache / nginx or whatever you use
  1036. #RELOAD_CMD=""
  1037. # The time period within which you want to allow renewal of a certificate
  1038. # this prevents hitting some of the rate limits.
  1039. RENEW_ALLOW="30"
  1040.  
  1041. # Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp,
  1042. # smtps_deprecated, smtps, smtp_submission, xmpp, xmpps, ldaps or a port number which
  1043. # will be checked for certificate expiry and also will be checked after
  1044. # an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true
  1045. #SERVER_TYPE="https"
  1046. #CHECK_REMOTE="true"
  1047.  
  1048. # Use the following 3 variables if you want to validate via DNS
  1049. #VALIDATE_VIA_DNS="true"
  1050. #DNS_ADD_COMMAND=
  1051. #DNS_DEL_COMMAND=
  1052. #AUTH_DNS_SERVER=""
  1053. #DNS_WAIT=10
  1054. #DNS_EXTRA_WAIT=60
  1055. _EOF_domain_
  1056. }
  1057.  
  1058. write_getssl_template() { # write out the main template file
  1059. cat > "$1" <<- _EOF_getssl_
  1060. # Uncomment and modify any variables you need
  1061. # see https://github.com/srvrco/getssl/wiki/Config-variables for details
  1062. #
  1063. # The staging server is best for testing (hence set as default)
  1064. CA="https://acme-staging.api.letsencrypt.org"
  1065. # This server issues full certificates, however has rate limits
  1066. #CA="https://acme-v01.api.letsencrypt.org"
  1067.  
  1068. #AGREEMENT="$AGREEMENT"
  1069.  
  1070. # Set an email address associated with your account - generally set at account level rather than domain.
  1071. #ACCOUNT_EMAIL="me@example.com"
  1072. ACCOUNT_KEY_LENGTH=4096
  1073. ACCOUNT_KEY="$WORKING_DIR/account.key"
  1074. PRIVATE_KEY_ALG="rsa"
  1075. #REUSE_PRIVATE_KEY="true"
  1076.  
  1077. # The command needed to reload apache / nginx or whatever you use
  1078. #RELOAD_CMD=""
  1079. # The time period within which you want to allow renewal of a certificate
  1080. # this prevents hitting some of the rate limits.
  1081. RENEW_ALLOW="30"
  1082.  
  1083. # Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp,
  1084. # smtps_deprecated, smtps, smtp_submission, xmpp, xmpps, ldaps or a port number which
  1085. # will be checked for certificate expiry and also will be checked after
  1086. # an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true
  1087. SERVER_TYPE="https"
  1088. CHECK_REMOTE="true"
  1089.  
  1090. # openssl config file. The default should work in most cases.
  1091. SSLCONF="$SSLCONF"
  1092.  
  1093. # Use the following 3 variables if you want to validate via DNS
  1094. #VALIDATE_VIA_DNS="true"
  1095. #DNS_ADD_COMMAND=
  1096. #DNS_DEL_COMMAND=
  1097. #AUTH_DNS_SERVER=""
  1098. #DNS_WAIT=10
  1099. #DNS_EXTRA_WAIT=60
  1100. _EOF_getssl_
  1101. }
  1102.  
  1103. write_openssl_conf() { # write out a minimal openssl conf
  1104. cat > "$1" <<- _EOF_openssl_conf_
  1105. # minimal openssl.cnf file
  1106. distinguished_name = req_distinguished_name
  1107. [ req_distinguished_name ]
  1108. [v3_req]
  1109. [v3_ca]
  1110. _EOF_openssl_conf_
  1111. }
  1112.  
  1113. # Trap signals
  1114. trap "signal_exit TERM" TERM HUP
  1115. trap "signal_exit INT" INT
  1116.  
  1117. # Parse command-line
  1118. while [[ -n $1 ]]; do
  1119. case $1 in
  1120. -h | --help)
  1121. help_message; graceful_exit ;;
  1122. -d | --debug)
  1123. _USE_DEBUG=1 ;;
  1124. -c | --create)
  1125. _CREATE_CONFIG=1 ;;
  1126. -f | --force)
  1127. _FORCE_RENEW=1 ;;
  1128. -a | --all)
  1129. _CHECK_ALL=1 ;;
  1130. -q | --quiet)
  1131. _QUIET=1 ;;
  1132. -Q | --mute)
  1133. _QUIET=1
  1134. _MUTE=1 ;;
  1135. -r | --revoke)
  1136. _REVOKE=1
  1137. shift
  1138. REVOKE_CERT="$1"
  1139. shift
  1140. REVOKE_KEY="$1"
  1141. shift
  1142. REVOKE_CA="$1" ;;
  1143. -u | --upgrade)
  1144. _UPGRADE=1 ;;
  1145. -U | --nocheck)
  1146. _UPGRADE_CHECK=0 ;;
  1147. -w)
  1148. shift; WORKING_DIR="$1" ;;
  1149. -* | --*)
  1150. usage
  1151. error_exit "Unknown option $1" ;;
  1152. *)
  1153. DOMAIN="$1" ;;
  1154. esac
  1155. shift
  1156. done
  1157.  
  1158. # Main logic
  1159. ############
  1160.  
  1161. # Get the current OS, so the correct functions can be used for that OS. (sets the variable os)
  1162. get_os
  1163.  
  1164. #check if required applications are included
  1165.  
  1166. requires which
  1167. requires openssl
  1168. requires curl
  1169. requires nslookup
  1170. requires awk
  1171. requires tr
  1172. requires date
  1173. requires grep
  1174. requires sed
  1175. requires sort
  1176.  
  1177. # Check if upgrades are available (unless they have specified -U to ignore Upgrade checks)
  1178. if [[ $_UPGRADE_CHECK -eq 1 ]]; then
  1179. check_getssl_upgrade
  1180. fi
  1181.  
  1182. # Revoke a certificate
  1183. if [[ $_REVOKE -eq 1 ]]; then
  1184. if [[ -z $REVOKE_CA ]]; then
  1185. CA=$DEFAULT_REVOKE_CA
  1186. elif [[ "$REVOKE_CA" == "-d" ]]; then
  1187. _USE_DEBUG=1
  1188. CA=$DEFAULT_REVOKE_CA
  1189. else
  1190. CA=$REVOKE_CA
  1191. fi
  1192. revoke_certificate
  1193. graceful_exit
  1194. fi
  1195.  
  1196. # get latest agreement from CA (as default)
  1197. AGREEMENT=$(curl -I "${CA}/terms" 2>/dev/null | awk '$1 ~ "Location:" {print $2}'|tr -d '\r')
  1198.  
  1199. # if nothing in command line, print help and exit.
  1200. if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then
  1201. help_message
  1202. graceful_exit
  1203. fi
  1204.  
  1205. # if the "working directory" doesn't exist, then create it.
  1206. if [[ ! -d "$WORKING_DIR" ]]; then
  1207. debug "Making working directory - $WORKING_DIR"
  1208. mkdir -p "$WORKING_DIR"
  1209. fi
  1210.  
  1211. # read any variables from config in working directory
  1212. if [[ -s "$WORKING_DIR/getssl.cfg" ]]; then
  1213. debug "reading config from $WORKING_DIR/getssl.cfg"
  1214. # shellcheck source=/dev/null
  1215. . "$WORKING_DIR/getssl.cfg"
  1216. fi
  1217.  
  1218. # Define defaults for variables unset in the main config.
  1219. ACCOUNT_KEY="${ACCOUNT_KEY:=$WORKING_DIR/account.key}"
  1220. DOMAIN_STORAGE="${DOMAIN_STORAGE:=$WORKING_DIR}"
  1221. DOMAIN_DIR="$DOMAIN_STORAGE/$DOMAIN"
  1222. CERT_FILE="$DOMAIN_DIR/${DOMAIN}.crt"
  1223. CA_CERT="$DOMAIN_DIR/chain.crt"
  1224. TEMP_DIR="$DOMAIN_DIR/tmp"
  1225.  
  1226. # Set the OPENSSL_CONF environment variable so openssl knows which config to use
  1227. export OPENSSL_CONF=$SSLCONF
  1228.  
  1229. # if "-a" option then check other parameters and create run for each domain.
  1230. if [[ ${_CHECK_ALL} -eq 1 ]]; then
  1231. info "Check all certificates"
  1232.  
  1233. if [[ ${_CREATE_CONFIG} -eq 1 ]]; then
  1234. error_exit "cannot combine -c|--create with -a|--all"
  1235. fi
  1236.  
  1237. if [[ ${_FORCE_RENEW} -eq 1 ]]; then
  1238. error_exit "cannot combine -f|--force with -a|--all because of rate limits"
  1239. fi
  1240.  
  1241. if [[ ! -d "$DOMAIN_STORAGE" ]]; then
  1242. error_exit "DOMAIN_STORAGE not found - $DOMAIN_STORAGE"
  1243. fi
  1244.  
  1245. for dir in ${DOMAIN_STORAGE}/*; do
  1246. if [[ -d "$dir" ]]; then
  1247. debug "Checking $dir"
  1248. cmd="$0 -U" # No update checks when calling recursively
  1249. if [[ ${_USE_DEBUG} -eq 1 ]]; then
  1250. cmd="$cmd -d"
  1251. fi
  1252. if [[ ${_QUIET} -eq 1 ]]; then
  1253. cmd="$cmd -q"
  1254. fi
  1255. # check if $dir looks like a domain name (contains a period)
  1256. if [[ $(basename "$dir") == *.* ]]; then
  1257. cmd="$cmd -w $WORKING_DIR $(basename "$dir")"
  1258. debug "CMD: $cmd"
  1259. eval "$cmd"
  1260. fi
  1261. fi
  1262. done
  1263.  
  1264. graceful_exit
  1265. fi
  1266. # end of "-a" option (looping through all domains)
  1267.  
  1268. # if "-c|--create" option used, then create config files.
  1269. if [[ ${_CREATE_CONFIG} -eq 1 ]]; then
  1270. # If main config file does not exists then create it.
  1271. if [[ ! -s "$WORKING_DIR/getssl.cfg" ]]; then
  1272. info "creating main config file $WORKING_DIR/getssl.cfg"
  1273. if [[ ! -s "$SSLCONF" ]]; then
  1274. SSLCONF="$WORKING_DIR/openssl.cnf"
  1275. write_openssl_conf "$SSLCONF"
  1276. fi
  1277. write_getssl_template "$WORKING_DIR/getssl.cfg"
  1278. fi
  1279. # If domain and domain config don't exist then create them.
  1280. if [[ ! -d "$DOMAIN_DIR" ]]; then
  1281. info "Making domain directory - $DOMAIN_DIR"
  1282. mkdir -p "$DOMAIN_DIR"
  1283. fi
  1284. if [[ -s "$DOMAIN_DIR/getssl.cfg" ]]; then
  1285. info "domain config already exists $DOMAIN_DIR/getssl.cfg"
  1286. else
  1287. info "creating domain config file in $DOMAIN_DIR/getssl.cfg"
  1288. # if domain has an existing cert, copy from domain and use to create defaults.
  1289. EX_CERT=$(echo \
  1290. | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null \
  1291. | openssl x509 2>/dev/null)
  1292. EX_SANS="www.${DOMAIN}"
  1293. if [[ ! -z "${EX_CERT}" ]]; then
  1294. EX_SANS=$(echo "$EX_CERT" \
  1295. | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \
  1296. | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-)
  1297. EX_SANS=${EX_SANS//$'\n'/','}
  1298. fi
  1299. write_domain_template "$DOMAIN_DIR/getssl.cfg"
  1300. fi
  1301. TEMP_DIR="$DOMAIN_DIR/tmp"
  1302. # end of "-c|--create" option, so exit
  1303. graceful_exit
  1304. fi
  1305. # end of "-c|--create" option to create config file.
  1306.  
  1307. # if domain directory doesn't exist, then create it.
  1308. if [[ ! -d "$DOMAIN_DIR" ]]; then
  1309. debug "Making working directory - $DOMAIN_DIR"
  1310. mkdir -p "$DOMAIN_DIR"
  1311. fi
  1312.  
  1313. # define a temporary directory, and if it doesn't exist, create it.
  1314. TEMP_DIR="$DOMAIN_DIR/tmp"
  1315. if [[ ! -d "${TEMP_DIR}" ]]; then
  1316. debug "Making temp directory - ${TEMP_DIR}"
  1317. mkdir -p "${TEMP_DIR}"
  1318. fi
  1319.  
  1320. # read any variables from config in domain directory
  1321. if [[ -s "$DOMAIN_DIR/getssl.cfg" ]]; then
  1322. debug "reading config from $DOMAIN_DIR/getssl.cfg"
  1323. # shellcheck source=/dev/null
  1324. . "$DOMAIN_DIR/getssl.cfg"
  1325. fi
  1326.  
  1327. # from SERVER_TYPE convert names to port numbers and additional data.
  1328. if [[ ${SERVER_TYPE} == "https" ]] || [[ ${SERVER_TYPE} == "webserver" ]]; then
  1329. REMOTE_PORT=443
  1330. elif [[ ${SERVER_TYPE} == "ftp" ]]; then
  1331. REMOTE_PORT=21
  1332. REMOTE_EXTRA="-starttls ftp"
  1333. elif [[ ${SERVER_TYPE} == "ftpi" ]]; then
  1334. REMOTE_PORT=990
  1335. elif [[ ${SERVER_TYPE} == "imap" ]]; then
  1336. REMOTE_PORT=143
  1337. REMOTE_EXTRA="-starttls imap"
  1338. elif [[ ${SERVER_TYPE} == "imaps" ]]; then
  1339. REMOTE_PORT=993
  1340. elif [[ ${SERVER_TYPE} == "pop3" ]]; then
  1341. REMOTE_PORT=110
  1342. REMOTE_EXTRA="-starttls pop3"
  1343. elif [[ ${SERVER_TYPE} == "pop3s" ]]; then
  1344. REMOTE_PORT=995
  1345. elif [[ ${SERVER_TYPE} == "smtp" ]]; then
  1346. REMOTE_PORT=25
  1347. REMOTE_EXTRA="-starttls smtp"
  1348. elif [[ ${SERVER_TYPE} == "smtps_deprecated" ]]; then
  1349. REMOTE_PORT=465
  1350. elif [[ ${SERVER_TYPE} == "smtps" ]] || [[ ${SERVER_TYPE} == "smtp_submission" ]]; then
  1351. REMOTE_PORT=587
  1352. REMOTE_EXTRA="-starttls smtp"
  1353. elif [[ ${SERVER_TYPE} == "xmpp" ]]; then
  1354. REMOTE_PORT=5222
  1355. REMOTE_EXTRA="-starttls xmpp"
  1356. elif [[ ${SERVER_TYPE} == "xmpps" ]]; then
  1357. REMOTE_PORT=5269
  1358. elif [[ ${SERVER_TYPE} == "ldaps" ]]; then
  1359. REMOTE_PORT=636
  1360. elif [[ ${SERVER_TYPE} =~ ^[0-9]+$ ]]; then
  1361. REMOTE_PORT=${SERVER_TYPE}
  1362. else
  1363. error_exit "unknown server type"
  1364. fi
  1365. # end of converting SERVER_TYPE names to port numbers and additional data.
  1366.  
  1367.  
  1368.  
  1369. # if check_remote is true then connect and obtain the current certificate (if not forcing renewal)
  1370. if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then
  1371. debug "getting certificate for $DOMAIN from remote server"
  1372. # shellcheck disable=SC2086
  1373. EX_CERT=$(echo \
  1374. | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \
  1375. | openssl x509 2>/dev/null)
  1376. if [[ ! -z "$EX_CERT" ]]; then # if obtained a cert
  1377. if [[ -s "$CERT_FILE" ]]; then # if local exists
  1378. CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null)
  1379. else # since local doesn't exist leave empty so that the domain validation will happen
  1380. CERT_LOCAL=""
  1381. fi
  1382. CERT_REMOTE=$(echo "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null)
  1383. if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then
  1384. debug "certificate on server is same as the local cert"
  1385. else
  1386. # check if the certificate is for the right domain
  1387. EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -text \
  1388. | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \
  1389. | sort -u | grep "^$DOMAIN\$")
  1390. if [[ "$EX_CERT_DOMAIN" == "$DOMAIN" ]]; then
  1391. # check renew-date on ex_cert and compare to local ( if local exists)
  1392. enddate_ex=$(echo "$EX_CERT" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-)
  1393. enddate_ex_s=$(date_epoc "$enddate_ex")
  1394. debug "external cert has enddate $enddate_ex ( $enddate_ex_s ) "
  1395. if [[ -s "$CERT_FILE" ]]; then # if local exists
  1396. enddate_lc=$(openssl x509 -noout -enddate < "$CERT_FILE" 2>/dev/null| cut -d= -f 2-)
  1397. enddate_lc_s=$(date_epoc "$enddate_lc")
  1398. debug "local cert has enddate $enddate_lc ( $enddate_lc_s ) "
  1399. else
  1400. enddate_lc_s=0
  1401. debug "local cert doesn't exist"
  1402. fi
  1403. if [[ "$enddate_ex_s" -eq "$enddate_lc_s" ]]; then
  1404. debug "certificates expire at the same time"
  1405. elif [[ "$enddate_ex_s" -gt "$enddate_lc_s" ]]; then
  1406. # remote has longer to expiry date than local copy.
  1407. debug "remote cert has longer to run than local cert - ignoring"
  1408. else
  1409. info "remote expires sooner than local for $DOMAIN, attempting to upload from local"
  1410. copy_file_to_location "domain certificate" \
  1411. "$CERT_FILE" \
  1412. "$DOMAIN_CERT_LOCATION"
  1413. copy_file_to_location "private key" \
  1414. "$DOMAIN_DIR/${DOMAIN}.key" \
  1415. "$DOMAIN_KEY_LOCATION"
  1416. copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION"
  1417. cat "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}_chain.pem"
  1418. copy_file_to_location "full pem" \
  1419. "$TEMP_DIR/${DOMAIN}_chain.pem" \
  1420. "$DOMAIN_CHAIN_LOCATION"
  1421. cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem"
  1422. copy_file_to_location "private key and domain cert pem" \
  1423. "$TEMP_DIR/${DOMAIN}_K_C.pem" \
  1424. "$DOMAIN_KEY_CERT_LOCATION"
  1425. cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem"
  1426. copy_file_to_location "full pem" \
  1427. "$TEMP_DIR/${DOMAIN}.pem" \
  1428. "$DOMAIN_PEM_LOCATION"
  1429. reload_service
  1430. fi
  1431. else
  1432. info "Certificate on remote domain does not match domain, ignoring remote certificate"
  1433. fi
  1434. fi
  1435. else
  1436. info "no certificate obtained from host"
  1437. fi
  1438. # end of .... if obtained a cert
  1439. fi
  1440. # end of .... check_remote is true then connect and obtain the current certificate
  1441.  
  1442.  
  1443.  
  1444. # if there is an existing certificate file, check details.
  1445. if [[ -s "$CERT_FILE" ]]; then
  1446. debug "certificate $CERT_FILE exists"
  1447. enddate=$(openssl x509 -in "$CERT_FILE" -noout -enddate 2>/dev/null| cut -d= -f 2-)
  1448. debug "local cert is valid until $enddate"
  1449. if [[ "$enddate" != "-" ]]; then
  1450. enddate_s=$(date_epoc "$enddate")
  1451. if [[ $(date_renew) -lt "$enddate_s" ]] && [[ $_FORCE_RENEW -ne 1 ]]; then
  1452. info "certificate for $DOMAIN is still valid for more than $RENEW_ALLOW days (until $enddate)"
  1453. # everything is OK, so exit.
  1454. graceful_exit
  1455. else
  1456. debug "certificate for $DOMAIN needs renewal"
  1457. fi
  1458. fi
  1459. fi
  1460. # end of .... if there is an existing certificate file, check details.
  1461.  
  1462. if [[ ! -t 0 ]] && [[ "$PREVENT_NON_INTERACTIVE_RENEWAL" = "true" ]]; then
  1463. errmsg="$DOMAIN due for renewal, "
  1464. errmsg+="Did not not completed due to PREVENT_NON_INTERACTIVE_RENEWAL=true in config"
  1465. error_exit "$errmsg"
  1466. fi
  1467.  
  1468. # create account key if it doesn't exist.
  1469. if [[ -s "$ACCOUNT_KEY" ]]; then
  1470. debug "Account key exists at $ACCOUNT_KEY skipping generation"
  1471. else
  1472. info "creating account key $ACCOUNT_KEY"
  1473. create_key "$ACCOUNT_KEY_TYPE" "$ACCOUNT_KEY" "$ACCOUNT_KEY_LENGTH"
  1474. fi
  1475.  
  1476.  
  1477. # if not reusing priavte key, then remove the old keys
  1478. if [[ "$REUSE_PRIVATE_KEY" != "true" ]]; then
  1479. if [[ -s "$DOMAIN_DIR/${DOMAIN}.key" ]]; then
  1480. rm -f "$DOMAIN_DIR/${DOMAIN}.key"
  1481. fi
  1482. if [[ -s "$DOMAIN_DIR/${DOMAIN}.ec.key" ]]; then
  1483. rm -f "$DOMAIN_DIR/${DOMAIN}.ecs.key"
  1484. fi
  1485. fi
  1486. # create new domain keys if they don't already exist
  1487. if [[ "$DUAL_RSA_ECDSA" == "false" ]]; then
  1488. create_key "${PRIVATE_KEY_ALG}" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LENGTH"
  1489. else
  1490. create_key "rsa" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LENGTH"
  1491. create_key "${PRIVATE_KEY_ALG}" "$DOMAIN_DIR/${DOMAIN}.ec.key" "$DOMAIN_KEY_LENGTH"
  1492. fi
  1493. # End of creating domain keys.
  1494.  
  1495.  
  1496.  
  1497. #create SAN
  1498. if [[ -z "$SANS" ]]; then
  1499. SANLIST="subjectAltName=DNS:${DOMAIN}"
  1500. else
  1501. SANLIST="subjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}"
  1502. fi
  1503. debug "created SAN list = $SANLIST"
  1504.  
  1505. # list of main domain and all domains in SAN
  1506. alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g")
  1507.  
  1508. # check domain and san list for duplicates
  1509. echo "" > "$TEMP_DIR/sanlist"
  1510. for d in $alldomains; do
  1511. if [[ "$(grep "^${d}$" "$TEMP_DIR/sanlist")" = "$d" ]]; then
  1512. error_exit "$d appears to be duplicated in domain, SAN list"
  1513. else
  1514. echo "$d" >> "$TEMP_DIR/sanlist"
  1515. fi
  1516. # check nslookup for domains (ignore if using DNS check, as site may not be published yet)
  1517. if [[ $VALIDATE_VIA_DNS != "true" ]]; then
  1518. debug "checking nslookup for ${d}"
  1519. if [[ "$(nslookup -query=AAAA "${d}"|grep -c "^${d}.*has AAAA address")" -ge 1 ]]; then
  1520. debug "found IPv6 record for ${d}"
  1521. elif [[ "$(nslookup "${d}"| grep -c ^Name)" -ge 1 ]]; then
  1522. debug "found IPv4 record for ${d}"
  1523. else
  1524. echo "DNS lookup failed for $d"
  1525. fi
  1526. fi
  1527. done
  1528. # End of setting up SANS.
  1529.  
  1530.  
  1531.  
  1532.  
  1533. #create CSR's
  1534. if [[ "$DUAL_RSA_ECDSA" == "false" ]]; then
  1535. create_csr "$DOMAIN_DIR/${DOMAIN}.csr" "$DOMAIN_DIR/${DOMAIN}.key"
  1536. else
  1537. create_csr "$DOMAIN_DIR/${DOMAIN}.csr" "$DOMAIN_DIR/${DOMAIN}.key"
  1538. create_csr "$DOMAIN_DIR/${DOMAIN}.ec.csr" "$DOMAIN_DIR/${DOMAIN}.ec.key"
  1539. fi
  1540.  
  1541.  
  1542. # use account key to register with CA
  1543. # currently the code registers every time, and gets an "already registered" back if it has been.
  1544. get_signing_params "$ACCOUNT_KEY"
  1545.  
  1546. if [[ "$ACCOUNT_EMAIL" ]] ; then
  1547. regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
  1548. else
  1549. regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
  1550. fi
  1551.  
  1552. info "Registering account"
  1553. # send the request to the ACME server.
  1554. send_signed_request "$CA/acme/new-reg" "$regjson"
  1555.  
  1556. if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then
  1557. info "Registered"
  1558. echo "$response" > "$TEMP_DIR/account.json"
  1559. elif [[ "$code" == '409' ]] ; then
  1560. debug "Already registered"
  1561. else
  1562. error_exit "Error registering account ... $(json_get "$response" detail)"
  1563. fi
  1564. # end of registering account with CA
  1565.  
  1566.  
  1567.  
  1568.  
  1569. # verify each domain
  1570. info "Verify each domain"
  1571.  
  1572. # loop through domains for cert ( from SANS list)
  1573. alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g")
  1574. dn=0
  1575. for d in $alldomains; do
  1576. # $d is domain in current loop, which is number $dn for ACL
  1577. info "Verifying $d"
  1578. if [[ "$USE_SINGLE_ACL" == "true" ]]; then
  1579. DOMAIN_ACL="${ACL[0]}"
  1580. else
  1581. DOMAIN_ACL="${ACL[$dn]}"
  1582. fi
  1583.  
  1584. # check if we have the information needed to place the challenge
  1585. if [[ $VALIDATE_VIA_DNS == "true" ]]; then
  1586. if [[ -z "$DNS_ADD_COMMAND" ]]; then
  1587. error_exit "DNS_ADD_COMMAND not defined for domain $d"
  1588. fi
  1589. if [[ -z "$DNS_DEL_COMMAND" ]]; then
  1590. error_exit "DNS_DEL_COMMAND not defined for domain $d"
  1591. fi
  1592. else
  1593. if [[ -z "${DOMAIN_ACL}" ]]; then
  1594. error_exit "ACL location not specified for domain $d in $DOMAIN_DIR/getssl.cfg"
  1595. else
  1596. debug "domain $d has ACL = ${DOMAIN_ACL}"
  1597. fi
  1598. fi
  1599.  
  1600. # request a challenge token from ACME server
  1601. request="{\"resource\":\"new-authz\",\"identifier\":{\"type\":\"dns\",\"value\":\"$d\"}}"
  1602. send_signed_request "$CA/acme/new-authz" "$request"
  1603.  
  1604. debug "completed send_signed_request"
  1605. # check if we got a valid response and token, if not then error exit
  1606. if [[ ! -z "$code" ]] && [[ ! "$code" == '201' ]] ; then
  1607. error_exit "new-authz error: $response"
  1608. fi
  1609.  
  1610. if [[ $response_status == "valid" ]]; then
  1611. info "$d is already validated"
  1612. if [[ "$DEACTIVATE_AUTH" == "true" ]]; then
  1613. deactivate_url="$(echo "$responseHeaders" | awk ' $1 ~ "^Location" {print $2}' | tr -d "\r")"
  1614. deactivate_url_list+=" $deactivate_url "
  1615. debug "url added to deactivate list ${deactivate_url}"
  1616. debug "deactivate list is now $deactivate_url_list"
  1617. fi
  1618. # increment domain-counter
  1619. ((dn++))
  1620. else
  1621. PREVIOUSLY_VALIDATED="false"
  1622. if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification
  1623. # get the dns component of the ACME response
  1624. # get the token from the dns component
  1625. token=$(json_get "$response" "token" "dns-01")
  1626. debug token "$token"
  1627. # get the uri from the dns component
  1628. uri=$(json_get "$response" "uri" "dns-01")
  1629. debug uri "$uri"
  1630.  
  1631. keyauthorization="$token.$thumbprint"
  1632. debug keyauthorization "$keyauthorization"
  1633.  
  1634. #create signed authorization key from token.
  1635. auth_key=$(printf '%s' "$keyauthorization" | openssl dgst -sha256 -binary \
  1636. | openssl base64 -e \
  1637. | tr -d '\n\r' \
  1638. | sed -e 's:=*$::g' -e 'y:+/:-_:')
  1639. debug auth_key "$auth_key"
  1640.  
  1641. debug "adding dns via command: $DNS_ADD_COMMAND $d $auth_key"
  1642. if ! eval "$DNS_ADD_COMMAND" "$d" "$auth_key" ; then
  1643. error_exit "DNS_ADD_COMMAND failed for domain $d"
  1644. fi
  1645.  
  1646. # find a primary / authoritative DNS server for the domain
  1647. if [[ -z "$AUTH_DNS_SERVER" ]]; then
  1648. get_auth_dns "$d"
  1649. else
  1650. primary_ns="$AUTH_DNS_SERVER"
  1651. fi
  1652. debug primary_ns "$primary_ns"
  1653.  
  1654. # make a directory to hold pending dns-challenges
  1655. if [[ ! -d "$TEMP_DIR/dns_verify" ]]; then
  1656. mkdir "$TEMP_DIR/dns_verify"
  1657. fi
  1658.  
  1659. # generate a file with the current variables for the dns-challenge
  1660. cat > "$TEMP_DIR/dns_verify/$d" <<- _EOF_
  1661. token="${token}"
  1662. uri="${uri}"
  1663. keyauthorization="${keyauthorization}"
  1664. d="${d}"
  1665. primary_ns="${primary_ns}"
  1666. auth_key="${auth_key}"
  1667. _EOF_
  1668.  
  1669. else # set up the correct http token for verification
  1670. # get the token from the http component
  1671. token=$(json_get "$response" "token" "http-01")
  1672. debug token "$token"
  1673. # get the uri from the http component
  1674. uri=$(json_get "$response" "uri" "http-01")
  1675. debug uri "$uri"
  1676.  
  1677. #create signed authorization key from token.
  1678. keyauthorization="$token.$thumbprint"
  1679. debug keyauthorization "$keyauthorization"
  1680.  
  1681. # save variable into temporary file
  1682. echo -n "$keyauthorization" > "$TEMP_DIR/$token"
  1683. chmod 644 "$TEMP_DIR/$token"
  1684.  
  1685. # copy to token to acme challenge location
  1686. umask 0022
  1687. debug "copying file from $TEMP_DIR/$token to ${DOMAIN_ACL}"
  1688. copy_file_to_location "challenge token" \
  1689. "$TEMP_DIR/$token" \
  1690. "${DOMAIN_ACL}/$token"
  1691. umask "$ORIG_UMASK"
  1692.  
  1693. wellknown_url="${CHALLENGE_CHECK_TYPE}://$d/.well-known/acme-challenge/$token"
  1694. debug wellknown_url "$wellknown_url"
  1695.  
  1696. if [[ "$SKIP_HTTP_TOKEN_CHECK" == "true" ]]; then
  1697. info "SKIP_HTTP_TOKEN_CHECK=true so not checking that token is working correctly"
  1698. else
  1699. sleep "$HTTP_TOKEN_CHECK_WAIT"
  1700. # check that we can reach the challenge ourselves, if not, then error
  1701. if [[ ! "$(curl -k --silent --location "$wellknown_url")" == "$keyauthorization" ]]; then
  1702. error_exit "for some reason could not reach $wellknown_url - please check it manually"
  1703. fi
  1704. fi
  1705.  
  1706. check_challenge_completion "$uri" "$d" "$keyauthorization"
  1707.  
  1708. debug "remove token from ${DOMAIN_ACL}"
  1709. if [[ "${DOMAIN_ACL:0:4}" == "ssh:" ]] ; then
  1710. sshhost=$(echo "${DOMAIN_ACL}"| awk -F: '{print $2}')
  1711. command="rm -f ${DOMAIN_ACL:(( ${#sshhost} + 5))}/${token:?}"
  1712. debug "running following command to remove token"
  1713. debug "ssh $sshhost ${command}"
  1714. # shellcheck disable=SC2029
  1715. ssh "$sshhost" "${command}" 1>/dev/null 2>&1
  1716. rm -f "${TEMP_DIR:?}/${token:?}"
  1717. elif [[ "${DOMAIN_ACL:0:4}" == "ftp:" ]] ; then
  1718. debug "using ftp to remove token file"
  1719. ftpuser=$(echo "${DOMAIN_ACL}"| awk -F: '{print $2}')
  1720. ftppass=$(echo "${DOMAIN_ACL}"| awk -F: '{print $3}')
  1721. ftphost=$(echo "${DOMAIN_ACL}"| awk -F: '{print $4}')
  1722. ftplocn=$(echo "${DOMAIN_ACL}"| awk -F: '{print $5}')
  1723. debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost location=$ftplocn"
  1724. ftp -n <<- EOF
  1725. open $ftphost
  1726. user $ftpuser $ftppass
  1727. cd $ftplocn
  1728. delete ${token:?}
  1729. EOF
  1730. else
  1731. rm -f "${DOMAIN_ACL:?}/${token:?}"
  1732. fi
  1733. fi
  1734. # increment domain-counter
  1735. ((dn++))
  1736. fi
  1737. done # end of ... loop through domains for cert ( from SANS list)
  1738.  
  1739. # perform validation if via DNS challenge
  1740. if [[ $VALIDATE_VIA_DNS == "true" ]]; then
  1741. # loop through dns-variable files to check if dns has been changed
  1742. for dnsfile in $TEMP_DIR/dns_verify/*; do
  1743. if [[ -e "$dnsfile" ]]; then
  1744. debug "loading DNSfile: $dnsfile"
  1745. # shellcheck source=/dev/null
  1746. . "$dnsfile"
  1747.  
  1748. # check for token at public dns server, waiting for a valid response.
  1749. for ns in $primary_ns; do
  1750. debug "checking dns at $ns"
  1751. ntries=0
  1752. check_dns="fail"
  1753. while [[ "$check_dns" == "fail" ]]; do
  1754. if [[ "$os" == "cygwin" ]]; then
  1755. check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \
  1756. | grep ^_acme -A2\
  1757. | grep '"'|awk -F'"' '{ print $2}')
  1758. else
  1759. check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \
  1760. | grep ^_acme|awk -F'"' '{ print $2}')
  1761. fi
  1762. debug "expecting $auth_key"
  1763. debug "${ns} gave ... $check_result"
  1764.  
  1765. if [[ "$check_result" == *"$auth_key"* ]]; then
  1766. check_dns="success"
  1767. else
  1768. if [[ $ntries -lt 100 ]]; then
  1769. ntries=$(( ntries + 1 ))
  1770. info "checking DNS at ${ns} for ${d}. Attempt $ntries/100 gave wrong result, "\
  1771. "waiting $DNS_WAIT secs before checking again"
  1772. sleep $DNS_WAIT
  1773. else
  1774. debug "dns check failed - removing existing value"
  1775. error_exit "checking _acme-challenge.$DOMAIN gave $check_result not $auth_key"
  1776. fi
  1777. fi
  1778. done
  1779. done
  1780. fi
  1781. done
  1782.  
  1783. if [[ "$DNS_EXTRA_WAIT" -gt 0 && "$PREVIOUSLY_VALIDATED" != "true" ]]; then
  1784. info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME-server to check the dns"
  1785. sleep "$DNS_EXTRA_WAIT"
  1786. fi
  1787.  
  1788. # loop through dns-variable files to let the ACME server check the challenges
  1789. for dnsfile in $TEMP_DIR/dns_verify/*; do
  1790. if [[ -e "$dnsfile" ]]; then
  1791. debug "loading DNSfile: $dnsfile"
  1792. # shellcheck source=/dev/null
  1793. . "$dnsfile"
  1794.  
  1795. check_challenge_completion "$uri" "$d" "$keyauthorization"
  1796.  
  1797. debug "remove DNS entry"
  1798. eval "$DNS_DEL_COMMAND" "$d" "$auth_key"
  1799. # remove $dnsfile after each loop.
  1800. rm -f "$dnsfile"
  1801. fi
  1802. done
  1803. fi
  1804. # end of ... perform validation if via DNS challenge
  1805. #end of varify each domain.
  1806.  
  1807.  
  1808.  
  1809.  
  1810. # Verification has been completed for all SANS, so request certificate.
  1811. info "Verification completed, obtaining certificate."
  1812.  
  1813. #obtain the certificate.
  1814. get_certificate "$DOMAIN_DIR/${DOMAIN}.csr" \
  1815. "$CERT_FILE" \
  1816. "$CA_CERT"
  1817. if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
  1818. get_certificate "$DOMAIN_DIR/${DOMAIN}.ec.csr" \
  1819. "${CERT_FILE::-4}.ec.crt" \
  1820. "${CA_CERT::-4}.ec.crt"
  1821. fi
  1822.  
  1823. # create Archive of new certs and keys.
  1824. cert_archive
  1825.  
  1826. debug "Certificates obtained and archived locally, will now copy to specified locations"
  1827.  
  1828.  
  1829.  
  1830.  
  1831. # copy certs to the correct location (creating concatenated files as required)
  1832.  
  1833. copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION"
  1834. copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION"
  1835. copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION"
  1836. if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then
  1837. if [[ ! -z "$DOMAIN_CERT_LOCATION" ]]; then
  1838. copy_file_to_location "ec domain certificate" \
  1839. "${CERT_FILE::-4}.ec.crt" \
  1840. "${DOMAIN_CERT_LOCATION::-4}.ec.crt"
  1841. fi
  1842. if [[ ! -z "$DOMAIN_KEY_LOCATION" ]]; then
  1843. copy_file_to_location "ec private key" \
  1844. "$DOMAIN_DIR/${DOMAIN}.ec.key" \
  1845. "${DOMAIN_KEY_LOCATION::-4}.ec.key"
  1846. fi
  1847. if [[ ! -z "$CA_CERT_LOCATION" ]]; then
  1848. copy_file_to_location "ec CA certificate" \
  1849. "${CA_CERT::-4}.ec.crt" \
  1850. "${CA_CERT_LOCATION::-4}.ec.crt"
  1851. fi
  1852. fi
  1853.  
  1854. # if DOMAIN_CHAIN_LOCATION is not blank, then create and copy file.
  1855. if [[ ! -z "$DOMAIN_CHAIN_LOCATION" ]]; then
  1856. if [[ "$(dirname "$DOMAIN_CHAIN_LOCATION")" == "." ]]; then
  1857. to_location="${DOMAIN_DIR}/${DOMAIN_CHAIN_LOCATION}"
  1858. else
  1859. to_location="${DOMAIN_CHAIN_LOCATION}"
  1860. fi
  1861. cat "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}_chain.pem"
  1862. copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location"
  1863. fi
  1864. # if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file.
  1865. if [[ ! -z "$DOMAIN_KEY_CERT_LOCATION" ]]; then
  1866. if [[ "$(dirname "$DOMAIN_KEY_CERT_LOCATION")" == "." ]]; then
  1867. to_location="${DOMAIN_DIR}/${DOMAIN_KEY_CERT_LOCATION}"
  1868. else
  1869. to_location="${DOMAIN_KEY_CERT_LOCATION}"
  1870. fi
  1871. cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem"
  1872. copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location"
  1873. fi
  1874. # if DOMAIN_PEM_LOCATION is not blank, then create and copy file.
  1875. if [[ ! -z "$DOMAIN_PEM_LOCATION" ]]; then
  1876. if [[ "$(dirname "$DOMAIN_PEM_LOCATION")" == "." ]]; then
  1877. to_location="${DOMAIN_DIR}/${DOMAIN_PEM_LOCATION}"
  1878. else
  1879. to_location="${DOMAIN_PEM_LOCATION}"
  1880. fi
  1881. cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem"
  1882. copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location"
  1883. fi
  1884. # end of copying certs.
  1885.  
  1886.  
  1887.  
  1888.  
  1889. # Run reload command to restart apache / nginx or whatever system
  1890. reload_service
  1891.  
  1892.  
  1893.  
  1894.  
  1895. # deactivate authorizations
  1896. if [[ "$DEACTIVATE_AUTH" == "true" ]]; then
  1897. debug "in deactivate list is $deactivate_url_list"
  1898. for deactivate_url in $deactivate_url_list; do
  1899. resp=$(curl "$deactivate_url" 2>/dev/null)
  1900. d=$(json_get "$resp" "hostname")
  1901. info "deactivating domain $d"
  1902. debug "deactivating $deactivate_url"
  1903. send_signed_request "$deactivate_url" "{\"resource\": \"authz\", \"status\": \"deactivated\"}"
  1904. # check response
  1905. if [[ "$code" == "200" ]]; then
  1906. debug "Authorization deactivated"
  1907. else
  1908. error_exit "$domain: Deactivation error: $code"
  1909. fi
  1910. done
  1911. fi
  1912. # end of deactivating authorizations
  1913.  
  1914.  
  1915. # Check if the certificate is installed correctly
  1916. if [[ ${CHECK_REMOTE} == "true" ]]; then
  1917. sleep "$CHECK_REMOTE_WAIT"
  1918. # shellcheck disable=SC2086
  1919. CERT_REMOTE=$(echo \
  1920. | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \
  1921. | openssl x509 -noout -fingerprint 2>/dev/null)
  1922. CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null)
  1923. if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then
  1924. info "${DOMAIN} - certificate installed OK on server"
  1925. else
  1926. error_exit "${DOMAIN} - certificate obtained but certificate on server is different from the new certificate"
  1927. fi
  1928. fi
  1929. # end of Check if the certificate is installed correctly
  1930.  
  1931.  
  1932.  
  1933. # To have reached here, a certificate should have been successfully obtained.
  1934. # Use echo rather than info so that 'quiet' is ignored.
  1935. echo "certificate obtained for ${DOMAIN}"
  1936.  
  1937.  
  1938. # gracefully exit ( tidying up temporary files etc).
  1939. graceful_exit
Add Comment
Please, Sign In to add comment