1#!/usr/bin/env sh 2 3# Script for acme.sh to deploy certificates to haproxy 4# 5# The following variables can be exported: 6# 7# export DEPLOY_HAPROXY_PEM_NAME="${domain}.pem" 8# 9# Defines the name of the PEM file. 10# Defaults to "<domain>.pem" 11# 12# export DEPLOY_HAPROXY_PEM_PATH="/etc/haproxy" 13# 14# Defines location of PEM file for HAProxy. 15# Defaults to /etc/haproxy 16# 17# export DEPLOY_HAPROXY_RELOAD="systemctl reload haproxy" 18# 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 22# for your distro. 23# 24# export DEPLOY_HAPROXY_ISSUER="no" 25# 26# OPTIONAL: Places CA file as "${DEPLOY_HAPROXY_PEM}.issuer" 27# Note: Required for OCSP stapling to work 28# 29# export DEPLOY_HAPROXY_BUNDLE="no" 30# 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. 38# 39 40######## Public functions ##################### 41 42#domain keyfile certfile cafile fullchain 43haproxy_deploy() { 44 _cdomain="$1" 45 _ckey="$2" 46 _ccert="$3" 47 _cca="$4" 48 _cfullchain="$5" 49 50 # Some defaults 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" 56 57 _debug _cdomain "${_cdomain}" 58 _debug _ckey "${_ckey}" 59 _debug _ccert "${_ccert}" 60 _debug _cca "${_cca}" 61 _debug _cfullchain "${_cfullchain}" 62 63 # PEM_PATH is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}" 64 _getdeployconf DEPLOY_HAPROXY_PEM_PATH 65 _debug2 DEPLOY_HAPROXY_PEM_PATH "${DEPLOY_HAPROXY_PEM_PATH}" 66 if [ -n "${DEPLOY_HAPROXY_PEM_PATH}" ]; then 67 Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH}" 68 _savedomainconf Le_Deploy_haproxy_pem_path "${Le_Deploy_haproxy_pem_path}" 69 elif [ -z "${Le_Deploy_haproxy_pem_path}" ]; then 70 Le_Deploy_haproxy_pem_path="${DEPLOY_HAPROXY_PEM_PATH_DEFAULT}" 71 fi 72 73 # Ensure PEM_PATH exists 74 if [ -d "${Le_Deploy_haproxy_pem_path}" ]; then 75 _debug "PEM_PATH ${Le_Deploy_haproxy_pem_path} exists" 76 else 77 _err "PEM_PATH ${Le_Deploy_haproxy_pem_path} does not exist" 78 return 1 79 fi 80 81 # PEM_NAME is optional. If not provided then assume "${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" 82 _getdeployconf DEPLOY_HAPROXY_PEM_NAME 83 _debug2 DEPLOY_HAPROXY_PEM_NAME "${DEPLOY_HAPROXY_PEM_NAME}" 84 if [ -n "${DEPLOY_HAPROXY_PEM_NAME}" ]; then 85 Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME}" 86 _savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}" 87 elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then 88 Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}" 89 fi 90 91 # BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" 92 _getdeployconf DEPLOY_HAPROXY_BUNDLE 93 _debug2 DEPLOY_HAPROXY_BUNDLE "${DEPLOY_HAPROXY_BUNDLE}" 94 if [ -n "${DEPLOY_HAPROXY_BUNDLE}" ]; then 95 Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE}" 96 _savedomainconf Le_Deploy_haproxy_bundle "${Le_Deploy_haproxy_bundle}" 97 elif [ -z "${Le_Deploy_haproxy_bundle}" ]; then 98 Le_Deploy_haproxy_bundle="${DEPLOY_HAPROXY_BUNDLE_DEFAULT}" 99 fi 100 101 # ISSUER is optional. If not provided then assume "${DEPLOY_HAPROXY_ISSUER_DEFAULT}" 102 _getdeployconf DEPLOY_HAPROXY_ISSUER 103 _debug2 DEPLOY_HAPROXY_ISSUER "${DEPLOY_HAPROXY_ISSUER}" 104 if [ -n "${DEPLOY_HAPROXY_ISSUER}" ]; then 105 Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER}" 106 _savedomainconf Le_Deploy_haproxy_issuer "${Le_Deploy_haproxy_issuer}" 107 elif [ -z "${Le_Deploy_haproxy_issuer}" ]; then 108 Le_Deploy_haproxy_issuer="${DEPLOY_HAPROXY_ISSUER_DEFAULT}" 109 fi 110 111 # RELOAD is optional. If not provided then assume "${DEPLOY_HAPROXY_RELOAD_DEFAULT}" 112 _getdeployconf DEPLOY_HAPROXY_RELOAD 113 _debug2 DEPLOY_HAPROXY_RELOAD "${DEPLOY_HAPROXY_RELOAD}" 114 if [ -n "${DEPLOY_HAPROXY_RELOAD}" ]; then 115 Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD}" 116 _savedomainconf Le_Deploy_haproxy_reload "${Le_Deploy_haproxy_reload}" 117 elif [ -z "${Le_Deploy_haproxy_reload}" ]; then 118 Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}" 119 fi 120 121 # Set the suffix depending if we are creating a bundle or not 122 if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then 123 _info "Bundle creation requested" 124 # Initialise $Le_Keylength if its not already set 125 if [ -z "${Le_Keylength}" ]; then 126 Le_Keylength="" 127 fi 128 if _isEccKey "${Le_Keylength}"; then 129 _info "ECC key type detected" 130 _suffix=".ecdsa" 131 else 132 _info "RSA key type detected" 133 _suffix=".rsa" 134 fi 135 else 136 _suffix="" 137 fi 138 _debug _suffix "${_suffix}" 139 140 # Set variables for later 141 _pem="${Le_Deploy_haproxy_pem_path}/${Le_Deploy_haproxy_pem_name}${_suffix}" 142 _issuer="${_pem}.issuer" 143 _ocsp="${_pem}.ocsp" 144 _reload="${Le_Deploy_haproxy_reload}" 145 146 _info "Deploying PEM file" 147 # Create a temporary PEM file 148 _temppem="$(_mktemp)" 149 _debug _temppem "${_temppem}" 150 cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}" 151 _ret="$?" 152 153 # Check that we could create the temporary file 154 if [ "${_ret}" != "0" ]; then 155 _err "Error code ${_ret} returned during PEM file creation" 156 [ -f "${_temppem}" ] && rm -f "${_temppem}" 157 return ${_ret} 158 fi 159 160 # Move PEM file into place 161 _info "Moving new certificate into place" 162 _debug _pem "${_pem}" 163 cat "${_temppem}" >"${_pem}" 164 _ret=$? 165 166 # Clean up temp file 167 [ -f "${_temppem}" ] && rm -f "${_temppem}" 168 169 # Deal with any failure of moving PEM file into place 170 if [ "${_ret}" != "0" ]; then 171 _err "Error code ${_ret} returned while moving new certificate into place" 172 return ${_ret} 173 fi 174 175 # Update .issuer file if requested 176 if [ "${Le_Deploy_haproxy_issuer}" = "yes" ]; then 177 _info "Updating .issuer file" 178 _debug _issuer "${_issuer}" 179 cat "${_cca}" >"${_issuer}" 180 _ret="$?" 181 182 if [ "${_ret}" != "0" ]; then 183 _err "Error code ${_ret} returned while copying issuer/CA certificate into place" 184 return ${_ret} 185 fi 186 else 187 [ -f "${_issuer}" ] && _err "Issuer file update not requested but .issuer file exists" 188 fi 189 190 # Update .ocsp file if certificate was requested with --ocsp/--ocsp-must-staple option 191 if [ -z "${Le_OCSP_Staple}" ]; then 192 Le_OCSP_Staple="0" 193 fi 194 if [ "${Le_OCSP_Staple}" = "1" ]; then 195 _info "Updating OCSP stapling info" 196 _debug _ocsp "${_ocsp}" 197 _info "Extracting OCSP URL" 198 _ocsp_url=$(${ACME_OPENSSL_BIN:-openssl} x509 -noout -ocsp_uri -in "${_pem}") 199 _debug _ocsp_url "${_ocsp_url}" 200 201 # Only process OCSP if URL was present 202 if [ "${_ocsp_url}" != "" ]; then 203 # Extract the hostname from the OCSP URL 204 _info "Extracting OCSP URL" 205 _ocsp_host=$(echo "${_ocsp_url}" | cut -d/ -f3) 206 _debug _ocsp_host "${_ocsp_host}" 207 208 # Only process the certificate if we have a .issuer file 209 if [ -r "${_issuer}" ]; then 210 # Check if issuer cert is also a root CA cert 211 _subjectdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -subject -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) 212 _debug _subjectdn "${_subjectdn}" 213 _issuerdn=$(${ACME_OPENSSL_BIN:-openssl} x509 -in "${_issuer}" -issuer -noout | cut -d'/' -f2,3,4,5,6,7,8,9,10) 214 _debug _issuerdn "${_issuerdn}" 215 _info "Requesting OCSP response" 216 # If the issuer is a CA cert then our command line has "-CAfile" added 217 if [ "${_subjectdn}" = "${_issuerdn}" ]; then 218 _cafile_argument="-CAfile \"${_issuer}\"" 219 else 220 _cafile_argument="" 221 fi 222 _debug _cafile_argument "${_cafile_argument}" 223 # if OpenSSL/LibreSSL is v1.1 or above, the format for the -header option has changed 224 _openssl_version=$(${ACME_OPENSSL_BIN:-openssl} version | cut -d' ' -f2) 225 _debug _openssl_version "${_openssl_version}" 226 _openssl_major=$(echo "${_openssl_version}" | cut -d '.' -f1) 227 _openssl_minor=$(echo "${_openssl_version}" | cut -d '.' -f2) 228 if [ "${_openssl_major}" -eq "1" ] && [ "${_openssl_minor}" -ge "1" ] || [ "${_openssl_major}" -ge "2" ]; then 229 _header_sep="=" 230 else 231 _header_sep=" " 232 fi 233 # Request the OCSP response from the issuer and store it 234 _openssl_ocsp_cmd="${ACME_OPENSSL_BIN:-openssl} ocsp \ 235 -issuer \"${_issuer}\" \ 236 -cert \"${_pem}\" \ 237 -url \"${_ocsp_url}\" \ 238 -header Host${_header_sep}\"${_ocsp_host}\" \ 239 -respout \"${_ocsp}\" \ 240 -verify_other \"${_issuer}\" \ 241 ${_cafile_argument} \ 242 | grep -q \"${_pem}: good\"" 243 _debug _openssl_ocsp_cmd "${_openssl_ocsp_cmd}" 244 eval "${_openssl_ocsp_cmd}" 245 _ret=$? 246 else 247 # Non fatal: No issuer file was present so no OCSP stapling file created 248 _err "OCSP stapling in use but no .issuer file was present" 249 fi 250 else 251 # Non fatal: No OCSP url was found int the certificate 252 _err "OCSP update requested but no OCSP URL was found in certificate" 253 fi 254 255 # Non fatal: Check return code of openssl command 256 if [ "${_ret}" != "0" ]; then 257 _err "Updating OCSP stapling failed with return code ${_ret}" 258 fi 259 else 260 # An OCSP file was already present but certificate did not have OCSP extension 261 if [ -f "${_ocsp}" ]; then 262 _err "OCSP was not requested but .ocsp file exists." 263 # Could remove the file at this step, although HAProxy just ignores it in this case 264 # rm -f "${_ocsp}" || _err "Problem removing stale .ocsp file" 265 fi 266 fi 267 268 # Reload HAProxy 269 _debug _reload "${_reload}" 270 eval "${_reload}" 271 _ret=$? 272 if [ "${_ret}" != "0" ]; then 273 _err "Error code ${_ret} during reload" 274 return ${_ret} 275 else 276 _info "Reload successful" 277 fi 278 279 return 0 280} 281