Skip to content

Instantly share code, notes, and snippets.

@gnanet
Created February 23, 2025 23:21
Show Gist options
  • Save gnanet/56d42eb9f430048c71ec927cd794637f to your computer and use it in GitHub Desktop.
Save gnanet/56d42eb9f430048c71ec927cd794637f to your computer and use it in GitHub Desktop.
acme.sh deploy-hook-script to deploy certificate to Hetzner Cloud API using hcloud command
#!/usr/bin/bash
# Here is a script to deploy cert to Hetzner Cloud API using hcloud command
#
# it requires the hcloud binary from
# https://github.com/hetznercloud/cli
#
# it requires following environment variables:
#
# HCLOUD_API_TOKEN - this contains the token to the hcloud API
#
# Usage:
#
# export HCLOUD_API_TOKEN="your-token-here"; acme.sh --deploy -d www.mydomain.com --deploy-hook hcloud_api
#
# returns 0 means success, otherwise error
######## Public functions #####################
#domain keyfile certfile cafile fullchain
hcloud_api_deploy() {
_cdomain="$1"
_ckey="$2"
_ckey_file="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_cfullchain_file="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
# validate required env vars
_getdeployconf HCLOUD_API_TOKEN
if [ -z "$HCLOUD_API_TOKEN" ]; then
_err "HCLOUD_API_TOKEN needs to be defined (contains token for Hetzner Cloud API)"
return 1
fi
_savedeployconf HCLOUD_API_TOKEN "$HCLOUD_API_TOKEN"
HCLOUD_CMD=$(command -v hcloud)
if [ -z "$HCLOUD_CMD" ]; then
_err "Install hcloud command, or set HCLOUD_CMD, if you installed hcloud to an unusual path"
return 1
fi
# JSON does not allow multiline strings.
# So replacing new-lines with "\n" here
_ckey=$(sed -z 's/\n/\\n/g' <"$2")
_ccert=$(sed -z 's/\n/\\n/g' <"$3")
_cca=$(sed -z 's/\n/\\n/g' <"$4")
_cfullchain=$(sed -z 's/\n/\\n/g' <"$5")
Le_CertCreateTimeStr=$(_readdomainconf Le_CertCreateTimeStr)
_debug Le_CertCreateTimeStr "$Le_CertCreateTimeStr"
if [ -n "$Le_CertCreateTimeStr" ]; then
_cert_date=$(echo "${Le_CertCreateTimeStr}" | cut -d 'T' -f1 | tr -d '-')
else
_cert_date=$(date +%Y%m%d)
fi
_cert_name="$_cdomain $_cert_date"
_same_cert_id=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate list | grep $_cdomain | grep "$_cert_name"| grep -oE "^[0-9]+")
_info "export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate list | grep $_cdomain | grep \"$_cert_name\"| grep -oE \"^[0-9]+\""
if [ -n "$_same_cert_id" ]; then
_err "Cert already exists: $_cert_name! $_cdomain ssl not renewed?"
exit 1
fi
_old_cert_id=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate list | grep $_cdomain | grep -v "$_cert_name"| grep -oE "^[0-9]+")
if [ -n "$_old_cert_id" ]; then
_lb_list=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate describe $_old_cert_id --output json | grep -oE "used_by.:\[[^]]*\]" | grep -oE "id.:[0-9]+" | cut -d ':' -f2)
fi
_debug _cdomain "$_cdomain"
_debug _cert_name "$_cert_name"
_debug _json_ckey "$_ckey"
_debug _json_cfullchain "$_cfullchain"
_debug _old_cert_id "$_old_cert_id"
if [ -z "$_old_cert_id" ]; then
_info "$(__green "$_cdomain is new, creating it")"
_cnt_lb_resp=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer list | grep -v "NETWORK ZONE" | wc -l)
if [[ $_cnt_lb_resp -eq 1 ]]; then
_info "$(__green "Found one single LB, collected lb_id")"
_lb_list=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer list | grep -v "NETWORK ZONE" | cut -d ' ' -f1 )
fi
if [ -f ${_cfullchain_file} ] && [ -f ${_ckey_file} ]; then
_create_resp=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate create --cert-file ${_cfullchain_file} --key-file ${_ckey_file} --domain ${_cdomain} --type upload --name "${_cert_name}" 2>&1 )
_new_cert_id=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate list | grep "$_cert_name" | grep -oE "^[0-9]+")
_debug _create_resp "$_create_resp"
_info "$(__green "Appending ${_new_cert_id} to the cert_list of the LB")"
for _lb_id in $_lb_list; do
_old_certlist=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer describe $_lb_id --output json | grep -oE ".certificates.:\[[0-9,]+\]" | grep -oE "[0-9,]+")
_new_certlist="${_old_certlist},${_new_cert_id}"
_response_lb=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer update-service $_lb_id --listen-port 443 --http-certificates $_new_certlist 2>&1)
_debug _response_lb "$_response_lb"
done
else
_err "Missing PEM files for _cdomain"
exit 1
fi
else
_info "$(__green "$_cdomain exists, updating it")"
#URL="https://api.hetzner.cloud/v1/certificates"
#export _H1="Authorization: Bearer $HCLOUD_API_TOKEN"
#export _H2="Content-Type: application/json"
#_result=$( _post "{\"name\":\"$_cert_name\",\"type\":\"uploaded\",\"certificate\":\"$_cfullchain\",\"private_key\":\"$_ckey\"}" "$URL" )
#_new_cert_id=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate list | grep "$_cert_name" | grep -oE "^[0-9]+")
_cnt_lb_resp=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer list | grep -v "NETWORK ZONE" | wc -l)
if [[ $_cnt_lb_resp -eq 1 ]]; then
_info "$(__green "Found one single LB, collected lb_id")"
_lb_list=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer list | grep -v "NETWORK ZONE" | cut -d ' ' -f1 )
fi
if [ -f ${_cfullchain_file} ] && [ -f ${_ckey_file} ]; then
_create_resp=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate create --cert-file ${_cfullchain_file} --key-file ${_ckey_file} --domain ${_cdomain} --type upload --name "${_cert_name}" 2>&1 )
_new_cert_id=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate list | grep "$_cert_name" | grep -oE "^[0-9]+")
_debug _create_resp "$_create_resp"
else
_err "Missing PEM files for _cdomain"
exit 1
fi
if [ -z "$_new_cert_id" ]; then
_err "Upload of $_cdomain failed, got no new_cert_id"
exit 1
else
_info "$(__green "New cert id ${_new_cert_id}")"
fi
_debug _old_cert_id "$_old_cert_id"
for _lb_id in $_lb_list; do
_info "$(__green "Replacing old cert ${_old_cert_id} with new cert ${_new_cert_id} in the cert_list of the LB ${_lb_id}")"
_old_certlist=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer describe $_lb_id --output json | grep -oE ".certificates.:\[[0-9,]+\]" | grep -oE "[0-9,]+")
_new_certlist=$(echo "$_old_certlist" | sed -e "s/$_old_cert_id/$_new_cert_id/g")
_debug _old_certlist ${_old_certlist}
_debug _new_certlist ${_new_certlist}
_response_lb=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} load-balancer update-service $_lb_id --listen-port 443 --http-certificates $_new_certlist 2>&1)
_debug _response_lb "$_response_lb"
done
_oldlb_list=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate describe $_old_cert_id --output json | grep -oE "used_by.:\[[^]]*\]" | grep -oE "id.:[0-9]+" | cut -d ':' -f2)
if [[ "x${_oldlb_list}" == "x" ]]; then
_del_resp=$(export HCLOUD_TOKEN=$HCLOUD_API_TOKEN; ${HCLOUD_CMD} certificate delete $_old_cert_id 2>&1)
_debug _del_resp "$_del_resp"
fi
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment