Skip to content

Commit

Permalink
EAB + ZeroSSL support
Browse files Browse the repository at this point in the history
  • Loading branch information
lukas2511 committed Sep 14, 2020
1 parent 4b91fcf commit 5fc1175
Showing 1 changed file with 86 additions and 7 deletions.
93 changes: 86 additions & 7 deletions dehydrated
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ _mktemp() {
# Check for script dependencies
check_dependencies() {
# look for required binaries
for binary in grep mktemp diff sed awk curl cut; do
for binary in grep mktemp diff sed awk curl cut; do
bin_path="$(command -v "${binary}" 2>/dev/null)" || _exiterr "This script requires ${binary}."
[[ -x "${bin_path}" ]] || _exiterr "${binary} found in PATH but it's not executable"
done
Expand Down Expand Up @@ -318,7 +318,7 @@ load_config() {
fi

# Preset
CA_ZEROSSL=""
CA_ZEROSSL="https://acme.zerossl.com/v2/DV90"
CA_LETSENCRYPT="https://acme-v02.api.letsencrypt.org/directory"

# Default values
Expand Down Expand Up @@ -424,6 +424,11 @@ load_config() {
# Check BASEDIR and set default variables
[[ -d "${BASEDIR}" ]] || _exiterr "BASEDIR does not exist: ${BASEDIR}"

# Check for ca cli parameter
if [ -n "${PARAM_CA:-}" ]; then
CA="${PARAM_CA}"
fi

# Preset CAs
if [ "${CA}" = "letsencrypt" ]; then
CA="{$CA_LETSENCRYPT}"
Expand Down Expand Up @@ -527,7 +532,8 @@ init_system() {
CA_NEW_ORDER="$(printf "%s" "${CA_DIRECTORY}" | get_json_string_value newOrder)" &&
CA_NEW_NONCE="$(printf "%s" "${CA_DIRECTORY}" | get_json_string_value newNonce)" &&
CA_NEW_ACCOUNT="$(printf "%s" "${CA_DIRECTORY}" | get_json_string_value newAccount)" &&
CA_TERMS="$(printf "%s" "${CA_DIRECTORY}" | get_json_string_value termsOfService)" &&
CA_TERMS="$(printf "%s" "${CA_DIRECTORY}" | get_json_string_value -p '"meta","termsOfService"')" &&
CA_REQUIRES_EAB="$(printf "%s" "${CA_DIRECTORY}" | get_json_bool_value -p '"meta","externalAccountRequired"' || echo false)" &&
CA_REVOKE_CERT="$(printf "%s" "${CA_DIRECTORY}" | get_json_string_value revokeCert)" ||
_exiterr "Problem retrieving ACME/CA-URLs, check if your configured CA points to the directory entrypoint."
# Since acct URI is missing from directory we will assume it is the same as CA_NEW_ACCOUNT without the new part
Expand All @@ -539,6 +545,7 @@ init_system() {

# Checking for private key ...
register_new_key="no"
generated="false"
if [[ -n "${PARAM_ACCOUNT_KEY:-}" ]]; then
# a private key was specified from the command line so use it for this run
echo "Using private key ${PARAM_ACCOUNT_KEY} instead of account key"
Expand All @@ -557,6 +564,7 @@ init_system() {
fi

echo "+ Generating account key..."
generated="true"
local tmp_account_key="$(_mktemp)"
_openssl genrsa -out "${tmp_account_key}" "${KEYSIZE}"
cat "${tmp_account_key}" > "${ACCOUNT_KEY}"
Expand All @@ -582,6 +590,35 @@ init_system() {
FAILED=true
fi

# ZeroSSL special sauce
if [[ "${CA}" = "${CA_ZEROSSL}" ]]; then
if [[ -z "${EAB_KID:-}" ]] || [[ -z "${EAB_HMAC_KEY:-}" ]]; then
if [[ -z "${CONTACT_EMAIL}" ]]; then
echo "ZeroSSL requires contact email to be set or EAB_KID/EAB_HMAC_KEY to be manually configured"
FAILED=true
else
zeroapi="$(curl -s "https://api.zerossl.com/acme/eab-credentials-email" -d "email=${CONTACT_EMAIL}" | jsonsh)"
EAB_KID="$(printf "%s" "${zeroapi}" | get_json_string_value eab_kid)"
EAB_HMAC_KEY="$(printf "%s" "${zeroapi}" | get_json_string_value eab_hmac_key)"
if [[ -z "${EAB_KID:-}" ]] || [[ -z "${EAB_HMAC_KEY:-}" ]]; then
echo "Unknown error retrieving ZeroSSL API credentials"
echo "${zeroapi}"
FAILED=true
fi
fi
fi
fi

# Check if external account is required
if [[ "${FAILED}" = "false" ]]; then
if [[ "${CA_REQUIRES_EAB}" = "true" ]]; then
if [[ -z "${EAB_KID:-}" ]] || [[ -z "${EAB_HMAC_KEY:-}" ]]; then
FAILED=true
echo "This CA requires an external account but no EAB_KID/EAB_HMAC_KEY has been configured"
fi
fi
fi

# If an email for the contact has been provided then adding it to the registration request
if [[ "${FAILED}" = "false" ]]; then
if [[ ${API} -eq 1 ]]; then
Expand All @@ -591,19 +628,37 @@ init_system() {
(signed_request "${CA_NEW_REG}" '{"resource": "new-reg", "agreement": "'"${CA_TERMS}"'"}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
fi
else
if [[ -n "${CONTACT_EMAIL}" ]]; then
(signed_request "${CA_NEW_ACCOUNT}" '{"contact":["mailto:'"${CONTACT_EMAIL}"'"], "termsOfServiceAgreed": true}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
if [[ -n "${EAB_KID:-}" ]] && [[ -n "${EAB_HMAC_KEY:-}" ]]; then
eab_url="${CA_NEW_ACCOUNT}"
eab_protected64="$(printf '{"alg":"HS256","kid":"%s","url":"%s"}' "${EAB_KID}" "${eab_url}" | urlbase64)"
eab_payload64="$(printf "%s" '{"e": "'"${pubExponent64}"'", "kty": "RSA", "n": "'"${pubMod64}"'"}' | urlbase64)"
eab_key="$(printf "%s" "${EAB_HMAC_KEY}" | deurlbase64)"
eab_signed64="$(printf '%s' "${eab_protected64}.${eab_payload64}" | "${OPENSSL}" dgst -binary -sha256 -hmac "${eab_key}" | urlbase64)"

if [[ -n "${CONTACT_EMAIL}" ]]; then
regjson='{"contact":["mailto:'"${CONTACT_EMAIL}"'"], "termsOfServiceAgreed": true, "externalAccountBinding": {"protected": "'"${eab_protected64}"'", "payload": "'"${eab_payload64}"'", "signature": "'"${eab_signed64}"'"}}'
else
regjson='{"termsOfServiceAgreed": true, "externalAccountBinding": {"protected": "'"${eab_protected64}"'", "payload": "'"${eab_payload64}"'", "signature": "'"${eab_signed64}"'"}}'
fi
else
(signed_request "${CA_NEW_ACCOUNT}" '{"termsOfServiceAgreed": true}' > "${ACCOUNT_KEY_JSON}") || FAILED=true
if [[ -n "${CONTACT_EMAIL}" ]]; then
regjson='{"contact":["mailto:'"${CONTACT_EMAIL}"'"], "termsOfServiceAgreed": true}'
else
regjson='{"termsOfServiceAgreed": true}'
fi
fi
(signed_request "${CA_NEW_ACCOUNT}" "${regjson}" > "${ACCOUNT_KEY_JSON}") || FAILED=true
fi
fi

if [[ "${FAILED}" = "true" ]]; then
echo >&2
echo >&2
echo "Error registering account key. See message above for more information." >&2
rm "${ACCOUNT_KEY}" "${ACCOUNT_KEY_JSON}"
if [[ "${generated}" = "true" ]]; then
rm "${ACCOUNT_KEY}"
fi
rm -f "${ACCOUNT_KEY_JSON}"
exit 1
fi
elif [[ "${COMMAND:-}" = "register" ]]; then
Expand Down Expand Up @@ -669,12 +724,27 @@ urlbase64() {
"${OPENSSL}" base64 -e | tr -d '\n\r' | _sed -e 's:=*$::g' -e 'y:+/:-_:'
}

# Decode data from url-safe formatted base64
deurlbase64() {
data="$(cat | tr -d ' \n\r')"
modlen=$((${#data} % 4))
padding=""
if [[ "${modlen}" = "2" ]]; then padding="==";
elif [[ "${modlen}" = "3" ]]; then padding="="; fi
printf "%s%s" "${data}" "${padding}" | tr -d '\n\r' | _sed -e 'y:-_:+/:' | "${OPENSSL}" base64 -d -A
}

# Convert hex string to binary data
hex2bin() {
# Remove spaces, add leading zero, escape as hex string and parse with printf
printf -- "$(cat | _sed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')"
}

# Convert binary data to hex string
bin2hex() {
hexdump | _sed 's/^[^ ]*//' | tr -d ' \n\r'
}

# OpenSSL writes to stderr/stdout even when there are no errors. So just
# display the output if the exit code was != 0 to simplify debugging.
_openssl() {
Expand Down Expand Up @@ -1873,6 +1943,15 @@ main() {
fi
;;

# PARAM_Usage: --ca url/preset
# PARAM_Description: Use specified CA URL or preset
--ca)
shift 1
check_parameters "${1:-}"
[[ -n "${PARAM_CA:-}" ]] && _exiterr "CA can only be specified once!"
PARAM_CA="${1}"
;;

# PARAM_Usage: --alias certalias
# PARAM_Description: Use specified name for certificate directory (and per-certificate config) instead of the primary domain (only used if --domain is specified)
--alias)
Expand Down

0 comments on commit 5fc1175

Please sign in to comment.