3 # Script for acme.sh to deploy certificates to haproxy
5 # The following variables can be exported:
7 # export DEPLOY_HAPROXY_PEM_NAME="${domain}.pem"
9 # Defines the name of the PEM file.
10 # Defaults to "<domain>.pem"
12 # export DEPLOY_HAPROXY_PEM_PATH="/etc/haproxy"
14 # Defines location of PEM file for HAProxy.
15 # Defaults to /etc/haproxy
17 # export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy"
19 # OPTIONAL: Reload command used post deploy
20 # This defaults to be a no-op (ie "true").
21 # It is strongly recommended to set this something that makes sense
24 # export DEPLOY_HAPROXY_ISSUER="no"
26 # OPTIONAL: Places CA file as "${DEPLOY_HAPROXY_PEM}.issuer"
27 # Note: Required for OCSP stapling to work
29 # export DEPLOY_HAPROXY_BUNDLE="no"
31 # OPTIONAL: Deploy this certificate as part of a multi-cert bundle
32 # This adds a suffix to the certificate based on the certificate type
33 # eg RSA certificates will have .rsa as a suffix to the file name
34 # HAProxy will load all certificates and provide one or the other
35 # depending on client capabilities
36 # Note: This functionality requires HAProxy was compiled against
37 # a version of OpenSSL that supports this.
40 ######## Public functions #####################
42 #domain keyfile certfile cafile fullchain
51 DEPLOY_HAPROXY_PEM_PATH_DEFAULT
="/etc/haproxy"
52 DEPLOY_HAPROXY_PEM_NAME_DEFAULT
="${_cdomain}.pem"
53 DEPLOY_HAPROXY_BUNDLE_DEFAULT
="no"
54 DEPLOY_HAPROXY_ISSUER_DEFAULT
="no"
55 DEPLOY_HAPROXY_RELOAD_DEFAULT
="true"
57 if [ -f "${DOMAIN_CONF}" ]; then
58 # shellcheck disable=SC1090
62 _debug _cdomain
"${_cdomain}"
63 _debug _ckey
"${_ckey}"
64 _debug _ccert
"${_ccert}"
66 _debug _cfullchain
"${_cfullchain}"
68 # PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
69 if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then
70 Le_Deploy_haproxy_pem_path
="${DEPLOY_HAPROXY_PEM_PATH}"
71 _savedomainconf Le_Deploy_haproxy_pem_path
"${Le_Deploy_haproxy_pem_path}"
72 elif [ -z "${Le_Deploy_haproxy_pem_path}" ]; then
73 Le_Deploy_haproxy_pem_path
="${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}"
76 # Ensure PEM_PATH exists
77 if [ -d "${Le_Deploy_haproxy_pem_path}" ]; then
78 _debug
"PEM_PATH ${Le_Deploy_haproxy_pem_path} exists"
80 _err
"PEM_PATH ${Le_Deploy_haproxy_pem_path} does not exist"
84 # PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
85 if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then
86 Le_Deploy_haproxy_pem_name
="${DEPLOY_HAPROXY_PEM_NAME}"
87 _savedomainconf Le_Deploy_haproxy_pem_name
"${Le_Deploy_haproxy_pem_name}"
88 elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
89 Le_Deploy_haproxy_pem_name
="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
92 # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
93 if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then
94 Le_Deploy_haproxy_bundle
="${DEPLOY_HAPROXY_BUNDLE}"
95 _savedomainconf Le_Deploy_haproxy_bundle
"${Le_Deploy_haproxy_bundle}"
96 elif [ -z "${Le_Deploy_haproxy_bundle}" ]; then
97 Le_Deploy_haproxy_bundle
="${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
100 # ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
101 if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then
102 Le_Deploy_haproxy_issuer
="${DEPLOY_HAPROXY_ISSUER}"
103 _savedomainconf Le_Deploy_haproxy_issuer
"${Le_Deploy_haproxy_issuer}"
104 elif [ -z "${Le_Deploy_haproxy_issuer}" ]; then
105 Le_Deploy_haproxy_issuer
="${DEPLOY_HAPROXY_ISSUER_DEFAULT}"
108 # RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
109 if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then
110 Le_Deploy_haproxy_reload
="${DEPLOY_HAPROXY_RELOAD}"
111 _savedomainconf Le_Deploy_haproxy_reload
"${Le_Deploy_haproxy_reload}"
112 elif [ -z "${Le_Deploy_haproxy_reload}" ]; then
113 Le_Deploy_haproxy_reload
="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
116 # Set the suffix depending if we are creating a bundle or not
117 if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
118 _info
"Bundle creation requested"
119 # Initialise $Le_Keylength if its not already set
120 if [ -z "${Le_Keylength}" ]; then
123 if _isEccKey
"${Le_Keylength}"; then
124 _info
"ECC key type detected"
127 _info
"RSA key type detected"
133 _debug _suffix
"${_suffix}"
135 # Set variables for later
136 _pem
="${Le_Deploy_haproxy_pem_path}/${Le_Deploy_haproxy_pem_name}${_suffix}"
137 _issuer
="${_pem}.issuer"
139 _reload
="${Le_Deploy_haproxy_reload}"
141 _info
"Deploying PEM file"
142 # Create a temporary PEM file
143 _temppem
="$(_mktemp)"
144 _debug _temppem
"${_temppem}"
145 cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
148 # Check that we could create the temporary file
149 if [ "${_ret}" != "0" ]; then
150 _err
"Error code ${_ret} returned during PEM file creation"
151 [ -f "${_temppem}" ] && rm -f "${_temppem}"
155 # Move PEM file into place
156 _info
"Moving new certificate into place"
157 _debug _pem
"${_pem}"
158 cat "${_temppem}" >"${_pem}"
162 [ -f "${_temppem}" ] && rm -f "${_temppem}"
164 # Deal with any failure of moving PEM file into place
165 if [ "${_ret}" != "0" ]; then
166 _err
"Error code ${_ret} returned while moving new certificate into place"
170 # Update .issuer file if requested
171 if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then
172 _info
"Updating .issuer file"
173 _debug _issuer
"${_issuer}"
174 cat "${_cca}" >"${_issuer}"
177 if [ "${_ret}" != "0" ]; then
178 _err
"Error code ${_ret} returned while copying issuer/CA certificate into place"
182 [ -f "${_issuer}" ] && _err
"Issuer file update not requested but .issuer file exists"
185 # Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option
186 if [ -z "${Le_OCSP_Staple}" ]; then
189 if [ "${Le_OCSP_Staple}" = "1" ]; then
190 _info
"Updating OCSP stapling info"
191 _debug _ocsp
"${_ocsp}"
192 _info
"Extracting OCSP URL"
193 _ocsp_url
=$
(openssl x509
-noout -ocsp_uri -in "${_pem}")
194 _debug _ocsp_url
"${_ocsp_url}"
196 # Only process OCSP if URL was present
197 if [ "${_ocsp_url}" != "" ]; then
198 # Extract the hostname from the OCSP URL
199 _info
"Extracting OCSP URL"
200 _ocsp_host
=$
(echo "${_ocsp_url}" | cut
-d/ -f3)
201 _debug _ocsp_host
"${_ocsp_host}"
203 # Only process the certificate if we have a .issuer file
204 if [ -r "${_issuer}" ]; then
205 # Check if issuer cert is also a root CA cert
206 _subjectdn
=$
(openssl x509
-in "${_issuer}" -subject -noout | cut
-d'/' -f2,3,4,5,6,7,8,9,10)
207 _debug _subjectdn
"${_subjectdn}"
208 _issuerdn
=$
(openssl x509
-in "${_issuer}" -issuer -noout | cut
-d'/' -f2,3,4,5,6,7,8,9,10)
209 _debug _issuerdn
"${_issuerdn}"
210 _info
"Requesting OCSP response"
211 # If the issuer is a CA cert then our command line has "-CAfile" added
212 if [ "${_subjectdn}" = "${_issuerdn}" ]; then
213 _cafile_argument
="-CAfile \"${_issuer}\""
217 _debug _cafile_argument
"${_cafile_argument}"
218 # if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed
219 _openssl_version
=$
(openssl version | cut
-d' ' -f2)
220 _debug _openssl_version
"${_openssl_version}"
221 _openssl_major
=$
(echo "${_openssl_version}" | cut
-d '.' -f1)
222 _openssl_minor
=$
(echo "${_openssl_version}" | cut
-d '.' -f2)
223 if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then
228 # Request the OCSP response from the issuer and store it
229 _openssl_ocsp_cmd
="openssl ocsp \
230 -issuer \"${_issuer}\" \
232 -url \"${_ocsp_url}\" \
233 -header Host${_header_sep}\"${_ocsp_host}\" \
234 -respout \"${_ocsp}\" \
235 -verify_other \"${_issuer}\" \
237 ${_cafile_argument} \
238 | grep -q \"${_pem}: good\""
239 _debug _openssl_ocsp_cmd
"${_openssl_ocsp_cmd}"
240 eval "${_openssl_ocsp_cmd}"
243 # Non fatal: No issuer file was present so no OCSP stapling file created
244 _err
"OCSP stapling in use but no .issuer file was present"
247 # Non fatal: No OCSP url was found int the certificate
248 _err
"OCSP update requested but no OCSP URL was found in certificate"
251 # Non fatal: Check return code of openssl command
252 if [ "${_ret}" != "0" ]; then
253 _err
"Updating OCSP stapling failed with return code ${_ret}"
256 # An OCSP file was already present but certificate did not have OCSP extension
257 if [ -f "${_ocsp}" ]; then
258 _err
"OCSP was not requested but .ocsp file exists."
259 # Could remove the file at this step, although HAProxy just ignores it in this case
260 # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file"
265 _debug _reload
"${_reload}"
268 if [ "${_ret}" != "0" ]; then
269 _err
"Error code ${_ret} during reload"
272 _info
"Reload successful"