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

270 lines
9.1 KiB

  1. #!/bin/bash
  2. # Deploy and maintain certificates within Rancher environments
  3. # here are the defaults, overridable via env vars
  4. #export RANCHER_CONFIG=${HOME}/.rancher/cli.json
  5. #export RANCHER_ENV=
  6. # usage:
  7. # - download rancher-cli from your rancher server and use it to create cli.json
  8. # the format of the file is quite simple, so you can just create your own
  9. # ! also run chmod 600 ~/.rancher/cli.json, since rancher-cli doesn't
  10. # - for multiple servers override RANCHER_CONFIG
  11. # - for multiple environments on a server set RANCHER_ENV appropriately;
  12. # otherwise the one within cli.json is used
  13. # - each run with --deploy saves the rancher configuration into the domain conf file in acme.sh
  14. # the list will be used when running acme.sh --renew[-all] to push the certificate onto rancher environments
  15. # you have to keep the rancher config files for renewal to work
  16. # - to remove a rancher server from a certificate you have to edit its conf file
  17. # deploy example:
  18. # acme.sh --deploy -d my.website.com --deploy-hook rancher
  19. # RANCHER_ENV=1a6 acme.sh --deploy -d my.website.com --deploy-hook rancher
  20. # renew example:
  21. # acme.sh --renew -d my.website.com
  22. ######## Private functions #####################
  23. # save rancher environment configuration in the domain config file
  24. function _rancher_savedomainconf () {
  25. local _rancherEnvId="$1"
  26. local _rancherConfigFile="$2"
  27. # use this hash to store the config pair
  28. local _configId=$(echo "${_rancherEnvId}@${_rancherConfigFile}" | md5sum | head -c 8)
  29. _info "Saving rancher information in domain file under id $_configId: environment $_rancherEnvId, config file $_rancherConfigFile"
  30. _savedomainconf "Rancher_ConfigFile_$_configId" "$_rancherConfig"
  31. _savedomainconf "Rancher_EnvId_$_configId" "$_rancherEnvId"
  32. local _rancherEnvs=$(_readdomainconf Rancher_Configs)
  33. if [[ ! "$_rancherConfigs" = *"$_configId"* ]] ; then
  34. _savedomainconf "Rancher_Configs" "$_rancherConfigs $_configId"
  35. fi
  36. }
  37. # read rancher's cli.json file
  38. function _rancher_read_configfile () {
  39. local _rancherConfigFile="$1"
  40. _info "Reading rancher configuration $_rancherConfig"
  41. if [ ! -r "${_rancherConfig}" ] ; then
  42. _err "cannot read rancher configuration"
  43. return 1
  44. fi
  45. eval $(jq --monochrome-output < "${_rancherConfig}" \
  46. '@sh "_rancherUrl=\(.url)","_accessKey=\(.accessKey)","_secretKey=\(.secretKey)","_envId=\(.environment)"' | xargs)
  47. _debug _rancherUrl "$_rancherUrl"
  48. _debug _accessKey "$_accessKey"
  49. _secure_debug _secretKey "$_secretKey"
  50. _debug _envId "$_envId"
  51. # when set by rancher-cli rancerUrl has an unwanted trailing "/schemas"
  52. _rancherUrl=${_rancherUrl%/schemas}
  53. return 0
  54. }
  55. # deploy a new certificate into a rancher environment
  56. function _rancher_deploy_cert () {
  57. local _cert=$(<"$_ccert")
  58. local _chain=$(<"$_cca")
  59. local _privkey=$(<"$_ckey")
  60. local _curlUrl="$_rancherUrl/projects/$_envId/certificates"
  61. local _curlMethod="POST"
  62. local _curlAuth="$_accessKey:$_secretKey"
  63. local _certJson=$(jq --null-input --compact-output \
  64. --arg cert "$_cert" \
  65. --arg chain "$_chain" \
  66. --arg privkey "$_privkey" \
  67. --arg name "$_cdomain" \
  68. '{type:"certificate",cert:$cert,certChain:$chain,key:$privkey,name:$name}')
  69. _debug _curlUrl "$_curlUrl"
  70. _debug _curlMethod "$_curlMethod"
  71. _secure_debug _curlAuth "$_curlAuth"
  72. _secure_debug _certJson "$_certJson"
  73. local _curlResult=$(curl -s \
  74. -u "${_curlAuth}" \
  75. -X "${_curlMethod}" \
  76. -H 'Content-Type: application/json' \
  77. -H 'Accept: application/json' \
  78. -d "${_certJson}" \
  79. "${_curlUrl}" |
  80. jq -r 'if (.type == "error") then "error: status="+(.status|tostring)+", code="+(.code|tostring)+", detail="+(.detail|tostring) else "success" end')
  81. _debug _curlResult "$_curlResult"
  82. if [ "$_curlResult" == "success" ] ; then
  83. _info "Certificate successfully deployed"
  84. return 0
  85. else
  86. _err "Deployment failed: $_curlResult"
  87. return 1
  88. fi
  89. }
  90. # get the id of an existing certificate by domain from a rancher environment
  91. function _get_rancher_certId () {
  92. local _curlUrl="$_rancherUrl/projects/$_envId/certificates"
  93. local _curlMethod="GET"
  94. local _curlAuth="$_accessKey:$_secretKey"
  95. local _filter=".data[] | select(.CN==\"$_cdomain\") | .id"
  96. _debug _curlUrl "$_curlUrl"
  97. _debug _curlMethod "$_curlMethod"
  98. _secure_debug _curlAuth "$_curlAuth"
  99. _debug _filter "$_filter"
  100. local _curlResult=$(curl -s \
  101. -u "${_curlAuth}" \
  102. -X "${_curlMethod}" \
  103. -H 'Content-Type: application/json' \
  104. -H 'Accept: application/json' \
  105. "${_curlUrl}" |
  106. jq -r "$_filter")
  107. echo $_curlResult
  108. }
  109. # update an existing certificate on a rancher environment
  110. function _rancher_update_cert () {
  111. local _certId=$(_get_rancher_certId)
  112. if [ "$_certId" == "" ] ; then
  113. _err "Cannot find certificate for domain $_cdomain on rancher environment $_envId at url $_rancherUrl"
  114. return 1
  115. fi
  116. _info "Found that certificate $_cdomain has id $_certId on rancher environment $_envId at url $_rancherUrl"
  117. local _cert=$(<"$_ccert")
  118. local _chain=$(<"$_cca")
  119. local _privkey=$(<"$_ckey")
  120. local _curlUrl="$_rancherUrl/projects/$_envId/certificates/$_certId"
  121. local _curlMethod="PUT"
  122. local _curlAuth="$_accessKey:$_secretKey"
  123. local _certJson=$(jq --null-input --compact-output \
  124. --arg cert "$_cert" \
  125. --arg chain "$_chain" \
  126. --arg privkey "$_privkey" \
  127. --arg name "$_cdomain" \
  128. '{type:"certificate",cert:$cert,certChain:$chain,key:$privkey,name:$name}')
  129. _debug _curlUrl "$_curlUrl"
  130. _debug _curlMethod "$_curlMethod"
  131. _secure_debug _curlAuth "$_curlAuth"
  132. _secure_debug _certJson "$_certJson"
  133. local _curlResult=$(curl -s \
  134. -u "${_curlAuth}" \
  135. -X "${_curlMethod}" \
  136. -H 'Content-Type: application/json' \
  137. -H 'Accept: application/json' \
  138. -d "${_certJson}" \
  139. "${_curlUrl}" |
  140. jq -r 'if (.type == "error") then "error: status="+(.status|tostring)+", code="+(.code|tostring)+", detail="+(.detail|tostring) else "success" end')
  141. _debug _curlResult "$_curlResult"
  142. if [ "$_curlResult" == "success" ] ; then
  143. _info "Certificate successfully updated"
  144. return 0
  145. else
  146. _err "Certificate update failed: $_curlResult"
  147. return 1
  148. fi
  149. }
  150. # deploy a new certificate into a rancher environment
  151. function _rancher_deploy () {
  152. local _defaultRancherConfig=${HOME}/.rancher/cli.json
  153. local _rancherConfig=${RANCHER_CONFIG:-${_defaultRancherConfig}}
  154. _rancher_read_configfile "${_rancherConfig}"
  155. local _success=$?
  156. if [ "$_success" != "0" ] ; then
  157. return 1
  158. fi
  159. if [ -n "${RANCHER_ENV}" ] ; then
  160. _envId="${RANCHER_ENV}"
  161. fi
  162. if [ -z "$_envId" ] ; then
  163. _err "Empty rancher env ID"
  164. return 1
  165. fi
  166. _info "Deploying certificate $_cdomain into rancher environment $_envId at $_rancherUrl"
  167. _rancher_deploy_cert
  168. local _success=$?
  169. if [ "$_success" == "0" ] ; then
  170. _rancher_savedomainconf "$_envId" "$_rancherConfig"
  171. return 0
  172. else
  173. return 1
  174. fi
  175. }
  176. # renew an existing certificate in a rancher environment
  177. function _rancher_renew () {
  178. local _rancherConfigs=$(_readdomainconf Rancher_Configs)
  179. _info "Found rancher env configs: $_rancherConfigs"
  180. for _configId in $_rancherConfigs ; do
  181. _info "Processing rancher config $_configId"
  182. local _rancherConfig=$(_readdomainconf "Rancher_ConfigFile_$_configId")
  183. _rancher_read_configfile "${_rancherConfig}"
  184. _envId=$(_readdomainconf "Rancher_EnvId_$_configId")
  185. local _success=$?
  186. if [ "$_success" != "0" ] ; then
  187. return 1
  188. fi
  189. _info "Updating certificate $_cdomain from rancher environment $_envId at $_rancherUrl"
  190. _rancher_update_cert
  191. local _success=$?
  192. if [ "$_success" != "0" ] ; then
  193. return 1
  194. fi
  195. done
  196. return 0
  197. }
  198. ######## Public functions #####################
  199. # domain keyfile certfile cafile fullchain
  200. rancher_deploy() {
  201. _cdomain="$1"
  202. _ckey="$2"
  203. _ccert="$3"
  204. _cca="$4"
  205. _cfullchain="$5"
  206. _debug _cdomain "$_cdomain"
  207. _debug _ckey "$_ckey"
  208. _debug _ccert "$_ccert"
  209. _debug _cca "$_cca"
  210. _debug _cfullchain "$_cfullchain"
  211. if ! _exists jq; then
  212. _err "The command jq is not found."
  213. return 1
  214. fi
  215. _debug IS_RENEW "$IS_RENEW"
  216. if [ "$IS_RENEW" == "1" ] ; then
  217. _rancher_renew
  218. else
  219. _rancher_deploy
  220. fi
  221. return $?
  222. }