From 550fb93ddd2629ada05a0389a88515e1992ee45f Mon Sep 17 00:00:00 2001 From: Ciprian Ciubotariu Date: Sun, 11 Mar 2018 18:31:24 +0000 Subject: [PATCH] Rancher deploy hook both for deploy and renew Add a rancher deploy-hook script that functions both for initial certificate deployment and for updating them when renewed For this purpose we store the rancher configuration from certificate deployments within the acme.sh domain configuration file --- deploy/rancher.sh | 270 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 deploy/rancher.sh diff --git a/deploy/rancher.sh b/deploy/rancher.sh new file mode 100644 index 00000000..c34586b3 --- /dev/null +++ b/deploy/rancher.sh @@ -0,0 +1,270 @@ +#!/bin/bash + +# Deploy and maintain certificates within Rancher environments + +# here are the defaults, overridable via env vars + +#export RANCHER_CONFIG=${HOME}/.rancher/cli.json +#export RANCHER_ENV= + +# usage: + +# - download rancher-cli from your rancher server and use it to create cli.json +# the format of the file is quite simple, so you can just create your own +# ! also run chmod 600 ~/.rancher/cli.json, since rancher-cli doesn't +# - for multiple servers override RANCHER_CONFIG +# - for multiple environments on a server set RANCHER_ENV appropriately; +# otherwise the one within cli.json is used +# - each run with --deploy saves the rancher configuration into the domain conf file in acme.sh +# the list will be used when running acme.sh --renew[-all] to push the certificate onto rancher environments +# you have to keep the rancher config files for renewal to work +# - to remove a rancher server from a certificate you have to edit its conf file + +# deploy example: +# acme.sh --deploy -d my.website.com --deploy-hook rancher +# RANCHER_ENV=1a6 acme.sh --deploy -d my.website.com --deploy-hook rancher + +# renew example: +# acme.sh --renew -d my.website.com + + +######## Private functions ##################### + +# save rancher environment configuration in the domain config file +function _rancher_savedomainconf () { + local _rancherEnvId="$1" + local _rancherConfigFile="$2" + + # use this hash to store the config pair + local _configId=$(echo "${_rancherEnvId}@${_rancherConfigFile}" | md5sum | head -c 8) + + _info "Saving rancher information in domain file under id $_configId: environment $_rancherEnvId, config file $_rancherConfigFile" + _savedomainconf "Rancher_ConfigFile_$_configId" "$_rancherConfig" + _savedomainconf "Rancher_EnvId_$_configId" "$_rancherEnvId" + + local _rancherEnvs=$(_readdomainconf Rancher_Configs) + if [[ ! "$_rancherConfigs" = *"$_configId"* ]] ; then + _savedomainconf "Rancher_Configs" "$_rancherConfigs $_configId" + fi +} + +# read rancher's cli.json file +function _rancher_read_configfile () { + local _rancherConfigFile="$1" + _info "Reading rancher configuration $_rancherConfig" + + if [ ! -r "${_rancherConfig}" ] ; then + _err "cannot read rancher configuration" + return 1 + fi + eval $(jq --monochrome-output < "${_rancherConfig}" \ + '@sh "_rancherUrl=\(.url)","_accessKey=\(.accessKey)","_secretKey=\(.secretKey)","_envId=\(.environment)"' | xargs) + _debug _rancherUrl "$_rancherUrl" + _debug _accessKey "$_accessKey" + _secure_debug _secretKey "$_secretKey" + _debug _envId "$_envId" + + # when set by rancher-cli rancerUrl has an unwanted trailing "/schemas" + _rancherUrl=${_rancherUrl%/schemas} + + return 0 +} + +# deploy a new certificate into a rancher environment +function _rancher_deploy_cert () { + local _cert=$(<"$_ccert") + local _chain=$(<"$_cca") + local _privkey=$(<"$_ckey") + + local _curlUrl="$_rancherUrl/projects/$_envId/certificates" + local _curlMethod="POST" + local _curlAuth="$_accessKey:$_secretKey" + local _certJson=$(jq --null-input --compact-output \ + --arg cert "$_cert" \ + --arg chain "$_chain" \ + --arg privkey "$_privkey" \ + --arg name "$_cdomain" \ + '{type:"certificate",cert:$cert,certChain:$chain,key:$privkey,name:$name}') + + _debug _curlUrl "$_curlUrl" + _debug _curlMethod "$_curlMethod" + _secure_debug _curlAuth "$_curlAuth" + _secure_debug _certJson "$_certJson" + + local _curlResult=$(curl -s \ + -u "${_curlAuth}" \ + -X "${_curlMethod}" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d "${_certJson}" \ + "${_curlUrl}" | + jq -r 'if (.type == "error") then "error: status="+(.status|tostring)+", code="+(.code|tostring)+", detail="+(.detail|tostring) else "success" end') + _debug _curlResult "$_curlResult" + if [ "$_curlResult" == "success" ] ; then + _info "Certificate successfully deployed" + return 0 + else + _err "Deployment failed: $_curlResult" + return 1 + fi +} + +# get the id of an existing certificate by domain from a rancher environment +function _get_rancher_certId () { + local _curlUrl="$_rancherUrl/projects/$_envId/certificates" + local _curlMethod="GET" + local _curlAuth="$_accessKey:$_secretKey" + local _filter=".data[] | select(.CN==\"$_cdomain\") | .id" + + _debug _curlUrl "$_curlUrl" + _debug _curlMethod "$_curlMethod" + _secure_debug _curlAuth "$_curlAuth" + _debug _filter "$_filter" + + local _curlResult=$(curl -s \ + -u "${_curlAuth}" \ + -X "${_curlMethod}" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + "${_curlUrl}" | + jq -r "$_filter") + echo $_curlResult +} + +# update an existing certificate on a rancher environment +function _rancher_update_cert () { + local _certId=$(_get_rancher_certId) + + if [ "$_certId" == "" ] ; then + _err "Cannot find certificate for domain $_cdomain on rancher environment $_envId at url $_rancherUrl" + return 1 + fi + + _info "Found that certificate $_cdomain has id $_certId on rancher environment $_envId at url $_rancherUrl" + + local _cert=$(<"$_ccert") + local _chain=$(<"$_cca") + local _privkey=$(<"$_ckey") + + local _curlUrl="$_rancherUrl/projects/$_envId/certificates/$_certId" + local _curlMethod="PUT" + local _curlAuth="$_accessKey:$_secretKey" + local _certJson=$(jq --null-input --compact-output \ + --arg cert "$_cert" \ + --arg chain "$_chain" \ + --arg privkey "$_privkey" \ + --arg name "$_cdomain" \ + '{type:"certificate",cert:$cert,certChain:$chain,key:$privkey,name:$name}') + + _debug _curlUrl "$_curlUrl" + _debug _curlMethod "$_curlMethod" + _secure_debug _curlAuth "$_curlAuth" + _secure_debug _certJson "$_certJson" + + local _curlResult=$(curl -s \ + -u "${_curlAuth}" \ + -X "${_curlMethod}" \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d "${_certJson}" \ + "${_curlUrl}" | + jq -r 'if (.type == "error") then "error: status="+(.status|tostring)+", code="+(.code|tostring)+", detail="+(.detail|tostring) else "success" end') + _debug _curlResult "$_curlResult" + + if [ "$_curlResult" == "success" ] ; then + _info "Certificate successfully updated" + return 0 + else + _err "Certificate update failed: $_curlResult" + return 1 + fi +} + +# deploy a new certificate into a rancher environment +function _rancher_deploy () { + local _defaultRancherConfig=${HOME}/.rancher/cli.json + local _rancherConfig=${RANCHER_CONFIG:-${_defaultRancherConfig}} + + _rancher_read_configfile "${_rancherConfig}" + local _success=$? + if [ "$_success" != "0" ] ; then + return 1 + fi + + if [ -n "${RANCHER_ENV}" ] ; then + _envId="${RANCHER_ENV}" + fi + + if [ -z "$_envId" ] ; then + _err "Empty rancher env ID" + return 1 + fi + + _info "Deploying certificate $_cdomain into rancher environment $_envId at $_rancherUrl" + _rancher_deploy_cert + local _success=$? + if [ "$_success" == "0" ] ; then + _rancher_savedomainconf "$_envId" "$_rancherConfig" + return 0 + else + return 1 + fi +} + +# renew an existing certificate in a rancher environment +function _rancher_renew () { + local _rancherConfigs=$(_readdomainconf Rancher_Configs) + _info "Found rancher env configs: $_rancherConfigs" + for _configId in $_rancherConfigs ; do + _info "Processing rancher config $_configId" + local _rancherConfig=$(_readdomainconf "Rancher_ConfigFile_$_configId") + _rancher_read_configfile "${_rancherConfig}" + _envId=$(_readdomainconf "Rancher_EnvId_$_configId") + local _success=$? + if [ "$_success" != "0" ] ; then + return 1 + fi + + _info "Updating certificate $_cdomain from rancher environment $_envId at $_rancherUrl" + _rancher_update_cert + local _success=$? + if [ "$_success" != "0" ] ; then + return 1 + fi + done + + return 0 +} + + + +######## Public functions ##################### + + +# domain keyfile certfile cafile fullchain +rancher_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + _debug _cdomain "$_cdomain" + _debug _ckey "$_ckey" + _debug _ccert "$_ccert" + _debug _cca "$_cca" + _debug _cfullchain "$_cfullchain" + + if ! _exists jq; then + _err "The command jq is not found." + return 1 + fi + + _debug IS_RENEW "$IS_RENEW" + if [ "$IS_RENEW" == "1" ] ; then + _rancher_renew + else + _rancher_deploy + fi + return $? +}