Update haproxy deploy hook

Add functionality to add OCSP stapling info (.ocsp file), issuer (.issuer file) and multi-cert bundles (suffix on pem file based on key type).

This also corrects the order of key, certificate and intermediate in the PEM file, which although HAProxy does not seem to care, was incorrect in the prior version.
This commit is contained in:
andrewheberle 2018-05-10 11:51:59 +08:00 committed by GitHub
parent 39ba697e19
commit 6567bb4c12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,8 +1,32 @@
#!/usr/bin/env sh
#Here is a script to deploy cert to haproxy server.
#returns 0 means success, otherwise error.
# Script for acme.sh to deploy certificates to haproxy
#
# The following variables can be exported:
#
# export DEPLOY_HAPROXY_PEM=""
#
# REQUIRED: Defines location of PEM file for HAProxy
#
# export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy"
#
# OPTIONAL: Reload command used post deploy
#
# export DEPLOY_HAPROXY_ISSUER="no"
#
# OPTIONAL: Places CA file as "${DEPLOY_HAPROXY_PEM}.issuer"
# Note: Required for OCSP stapling to work
#
# export DEPLOY_HAPROXY_BUNDLE="no"
#
# OPTIONAL: Deploy this certificate as part of a multi-cert bundle
# This adds a suffix to the certificate based on the certificate type
# eg RSA certificates will have .rsa as a suffix to the file name
# HAProxy will load all certificates and provide one or the other
# depending on client capabilities
# Note: This functionality requires HAProxy was compiled against
# a version of OpenSSL that supports this.
#
######## Public functions #####################
@ -14,45 +38,209 @@ haproxy_deploy() {
_cca="$4"
_cfullchain="$5"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
# Some defaults
DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
DEPLOY_HAPROXY_RELOAD_DEFAULT="systemctl reload haproxy"
# handle reload preference
DEFAULT_HAPROXY_RELOAD="/usr/sbin/service haproxy restart"
if [ -z "${DEPLOY_HAPROXY_RELOAD}" ]; then
_reload="${DEFAULT_HAPROXY_RELOAD}"
_cleardomainconf DEPLOY_HAPROXY_RELOAD
if [ -f "${DOMAIN_CONF}" ]; then
# shellcheck disable=SC1090
. "${DOMAIN_CONF}"
fi
_debug _cdomain "${_cdomain}"
_debug _ckey "${_ckey}"
_debug _ccert "${_ccert}"
_debug _cca "${_cca}"
_debug _cfullchain "${_cfullchain}"
# CERT is required
if [ -z "${DEPLOY_HAPROXY_PEM}" ]; then
if [ -z "${Le_Deploy_haproxy_pem}" ]; then
_err "{DEPLOY_HAPROXY_PEM} not defined."
return 1
fi
else
_reload="${DEPLOY_HAPROXY_RELOAD}"
_savedomainconf DEPLOY_HAPROXY_RELOAD "$DEPLOY_HAPROXY_RELOAD"
Le_Deploy_haproxy_cert="${DEPLOY_HAPROXY_PEM}"
_savedomainconf Le_Deploy_haproxy_cert "${Le_Deploy_haproxy_pem}"
fi
_savedomainconf DEPLOY_HAPROXY_PEM_PATH "$DEPLOY_HAPROXY_PEM_PATH"
# work out the path where the PEM file should go
_pem_path="${DEPLOY_HAPROXY_PEM_PATH}"
if [ -z "$_pem_path" ]; then
_err "Path to save PEM file not found. Please define DEPLOY_HAPROXY_PEM_PATH."
return 1
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then
Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}"
_savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}"
elif [ -z "${Le_Deploy_haproxy_bundle}" ]; then
Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
fi
_pem_full_path="$_pem_path/$_cdomain.pem"
_info "Full path to PEM $_pem_full_path"
# combine the key and fullchain into a single pem and install
cat "$_cfullchain" "$_ckey" >"$_pem_full_path"
chmod 600 "$_pem_full_path"
_info "Certificate successfully deployed"
# ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then
Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}"
_savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}"
elif [ -z "${Le_Deploy_haproxy_issuer}" ]; then
Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
fi
# restart HAProxy
_info "Run reload: $_reload"
if eval "$_reload"; then
_info "Reload success!"
return 0
# RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}"
_savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}"
elif [ -z "${Le_Deploy_haproxy_reload}" ]; then
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
fi
# Set the suffix depending if we are creating a bundle or not
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
_info "Bundle creation requested"
# Initialise $Le_KeyLength if its not already set
if [ -z "${Le_KeyLength}" ]; then
Le_KeyLength=""
fi
if _isEccKey "${Le_KeyLength}"; then
_info "ECC key type so set suffix to .ecc"
_suffix=".ecc"
else
_info "RSA key type so set suffix to .rsa"
_suffix=".rsa"
fi
else
_err "Reload error"
return 1
_suffix=""
fi
# Set variables for later
_pem="${Le_Deploy_haproxy_pem}${_suffix}"
_issuer="${_pem}.issuer"
_ocsp="${_pem}.ocsp"
_reload="${Le_Deploy_haproxy_reload}"
_info "Deploying PEM file"
# Create a temporary PEM file
_temppem="$(_mktemp)"
_debug _temppem "${_temppem}"
cat "${_ckey}" "${_ccert}" "${_cca}" > "${_temppem}"
_ret="$?"
# Check that we could create the temporary file
if [ "${_ret}" != "0" ]; then
_err "Error code ${_ret} returned during PEM file creation"
[ -f "${_temppem}" ] && rm -f "${_temppem}"
return ${_ret}
fi
# Move PEM file into place
_info "Moving new certificate into place"
_debug _pem "${_pem}"
cat "${_temppem}" > "${_pem}"
_ret=$?
# Clean up temp file
[ -f "${_temppem}" ] && rm -f "${_temppem}"
# Deal with any failure of moving PEM file into place
if [ "${_ret}" != "0" ]; then
_err "Error code ${_ret} returned while moving new certificate into place"
return ${_ret}
fi
# Update .issuer file if requested
if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then
_info "Updating .issuer file"
_debug _issuer "${_issuer}"
cat "${_cca}" > "${_issuer}"
_ret="$?"
if [ "${_ret}" != "0" ]; then
_err "Error code ${_ret} returned while copying issuer/CA certificate into place"
return ${_ret}
fi
else
[ -f "${_issuer}" ] _err "Issuer file update not requested but .issuer file exists"
fi
# Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
if [ -z "${Le_OCSP_Staple}" ]; then
Le_OCSP_Staple="0"
fi
if [ "${Le_OCSP_Staple}" = "1" ]; then
_info "Updating OCSP stapling info"
_debug _ocsp "${_ocsp}"
_info "Extracting OCSP URL"
_ocsp_url=$(openssl x509 -noout -ocsp_uri -in "${_pem}")
_debug _ocsp_url "${_ocsp_url}"
# Only process OCSP if URL was present
if [ "${_ocsp_url}" != "" ]; then
# Extract the hostname from the OCSP URL
_info "Extracting OCSP URL"
_ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3)
_debug _ocsp_host "${_ocsp_host}"
# Only process the certificate if we have a .issuer file
if [ -r "${_issuer}" ]; then
# Check if issuer cert is also a root CA cert
_subjectdn=$(openssl x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
_debug _subjectdn "${_subjectdn}"
_issuerdn=$(openssl x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10)
_debug _issuerdn "${_issuerdn}"
_info "Requesting OCSP response"
# Request the OCSP response from the issuer and store it
if [ "${_subjectdn}" = "${_issuerdn}" ]; then
# If the issuer is a CA cert then our command line has "-CAfile" added
openssl ocsp \
-issuer "${_issuer}" \
-cert "${_pem}" \
-url "${_ocsp_url}" \
-header Host "${_ocsp_host}" \
-respout "${_ocsp}" \
-verify_other "${_issuer}" \
-no_nonce \
-CAfile "${_issuer}"
_ret=$?
else
# Issuer is not a root CA so no "-CAfile" option
openssl ocsp \
-issuer "${_issuer}" \
-cert "${_pem}" \
-url "${_ocsp_url}" \
-header Host "${_ocsp_host}" \
-respout "${_ocsp}" \
-verify_other "${_issuer}" \
-no_nonce
_ret=$?
fi
else
# Non fatal: No issuer file was present so no OCSP stapling file created
_err "OCSP stapling in use but no .issuer file was present"
fi
else
# Non fatal: No OCSP url was found int the certificate
_err "OCSP update requested but no OCSP URL was found in certificate"
fi
# Check return code of openssl command
if [ "${_ret}" != "0" ]; then
_err "Updating OCSP stapling failed with return code ${_ret}"
return ${_ret}
fi
else
# An OCSP file was already present but certificate did not have OCSP extension
if [ -f "${_ocsp}" ]; then
_err "OCSP was not requested but .ocsp file exists."
# Should remove the file at this step, although HAProxy just ignores it in this case
# rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
fi
fi
# Reload HAProxy
_debug _reload "${_reload}"
eval "${_reload}"
_ret=$?
if [ "${_ret}" != "0" ]; then
_info "Reload successful"
else
_err "Error code ${_ret} during reload"
return ${_ret}
fi
return 0
}