tokenize | parse
}
+# Convert IP addresses to their reverse dns variants.
+# Used for ALPN certs as validation for IPs uses this in SNI since IPs aren't allowed there.
+ip_to_ptr() {
+ ip="$(cat)"
+ if [[ "${ip}" =~ : ]]; then
+ printf "%sip6.arpa" "$(printf "%s" "${ip}" | awk -F: 'BEGIN {OFS=""; }{addCount = 9 - NF; for(i=1; i<=NF;i++){if(length($i) == 0){ for(j=1;j<=addCount;j++){$i = ($i "0000");} } else { $i = substr(("0000" $i), length($i)+5-4);}}; print}' | rev | sed -e "s/./&./g")"
+ else
+ printf "%s.in-addr.arpa" "$(printf "%s" "${ip}" | awk -F. '{print $4"."$3"." $2"."$1}')"
+ fi
+}
+
# Create (identifiable) temporary files
_mktemp() {
mktemp "${TMPDIR:-/tmp}/dehydrated-XXXXXX"
# split to one per line:
# shellcheck disable=SC1003
altnames="$( <<<"${altnames}" _sed -e 's/^[[:space:]]*//; s/, /\'$'\n''/g' )"
- # we can only get DNS: ones signed
- if grep -qEv '^(DNS|othername):' <<<"${altnames}"; then
- _exiterr "Certificate signing request contains non-DNS Subject Alternative Names"
+ # we can only get DNS/IP: ones signed
+ if grep -qEv '^(DNS|IP( Address)*|othername):' <<<"${altnames}"; then
+ _exiterr "Certificate signing request contains non-DNS/IP Subject Alternative Names"
fi
- # strip away the DNS: prefix
- altnames="$( <<<"${altnames}" _sed -e 's/^(DNS:|othername:<unsupported>)//' )"
+ # strip away the DNS/IP: prefix
+ altnames="$( <<<"${altnames}" _sed -e 's/^(DNS:|IP( Address)*:|othername:<unsupported>)//' )"
printf "%s" "${altnames}" | tr '\n' ' '
else
# No SANs, extract CN
# Request new order and store authorization URIs
local challenge_identifiers=""
for altname in ${altnames}; do
- challenge_identifiers+="$(printf '{"type": "dns", "value": "%s"}, ' "${altname}")"
+ if [[ "${altname}" =~ ^ip: ]]; then
+ challenge_identifiers+="$(printf '{"type": "ip", "value": "%s"}, ' "${altname:3}")"
+ else
+ challenge_identifiers+="$(printf '{"type": "dns", "value": "%s"}, ' "${altname}")"
+ fi
done
challenge_identifiers="[${challenge_identifiers%, }]"
# Receive authorization ($authorization is authz uri)
response="$(signed_request "$(echo "${authorization}" | _sed -e 's/\"(.*)".*/\1/')" "" | jsonsh)"
identifier="$(echo "${response}" | get_json_string_value -p '"identifier","value"')"
+ identifier_type="$(echo "${response}" | get_json_string_value -p '"identifier","type"')"
echo " + Handling authorization for ${identifier}"
else
# Request new authorization ($authorization is altname)
challenge="$(echo "${response}" | get_json_dict_value -p '"challenges",'"${challengeindex}")"
# Gather challenge information
- challenge_names[${idx}]="${identifier}"
+ if [ "${identifier_type:-}" = "ip" ]; then
+ challenge_names[${idx}]="$(echo "${identifier}" | ip_to_ptr)"
+ else
+ challenge_names[${idx}]="${identifier}"
+ fi
challenge_tokens[${idx}]="$(echo "${challenge}" | get_json_string_value token)"
if [[ ${API} -eq 2 ]]; then
;;
"tls-alpn-01")
keyauth_hook="$(printf '%s' "${keyauth}" | "${OPENSSL}" dgst -sha256 -c -hex | awk '{print $NF}')"
- generate_alpn_certificate "${identifier}" "${keyauth_hook}"
+ generate_alpn_certificate "${identifier}" "${identifier_type}" "${keyauth_hook}"
;;
esac
keyauths[${idx}]="${keyauth}"
- deploy_args[${idx}]="${identifier} ${challenge_tokens[${idx}]} ${keyauth_hook}"
+ if [ "${identifier_type:-}" = "ip" ]; then
+ deploy_args[${idx}]="$(echo "${identifier}" | ip_to_ptr) ${challenge_tokens[${idx}]} ${keyauth_hook}"
+ else
+ deploy_args[${idx}]="${identifier} ${challenge_tokens[${idx}]} ${keyauth_hook}"
+ fi
idx=$((idx+1))
done
# Generate ALPN verification certificate
generate_alpn_certificate() {
local altname="${1}"
- local acmevalidation="${2}"
+ local identifier_type="${2}"
+ local acmevalidation="${3}"
local alpncertdir="${ALPNCERTDIR}"
if [[ ! -e "${alpncertdir}" ]]; then
echo " + Generating ALPN certificate and key for ${1}..."
tmp_openssl_cnf="$(_mktemp)"
cat "${OPENSSL_CNF}" > "${tmp_openssl_cnf}"
- printf "\n[SAN]\nsubjectAltName=DNS:%s\n" "${altname}" >> "${tmp_openssl_cnf}"
+ if [[ "${identifier_type}" = "ip" ]]; then
+ printf "\n[SAN]\nsubjectAltName=IP:%s\n" "${altname}" >> "${tmp_openssl_cnf}"
+ else
+ printf "\n[SAN]\nsubjectAltName=DNS:%s\n" "${altname}" >> "${tmp_openssl_cnf}"
+ fi
printf "1.3.6.1.5.5.7.1.31=critical,DER:04:20:%s\n" "${acmevalidation}" >> "${tmp_openssl_cnf}"
SUBJ="/CN=${altname}/"
[[ "${OSTYPE:0:5}" = "MINGW" ]] && SUBJ="/${SUBJ}"
+ if [[ "${identifier_type}" = "ip" ]]; then
+ altname="$(echo "${altname}" | ip_to_ptr)"
+ fi
_openssl req -x509 -new -sha256 -nodes -newkey rsa:2048 -keyout "${alpncertdir}/${altname}.key.pem" -out "${alpncertdir}/${altname}.crt.pem" -subj "${SUBJ}" -extensions SAN -config "${tmp_openssl_cnf}"
chmod g+r "${alpncertdir}/${altname}.key.pem" "${alpncertdir}/${altname}.crt.pem"
rm -f "${tmp_openssl_cnf}"
echo " + Generating signing request..."
SAN=""
for altname in ${altnames}; do
- SAN="${SAN}DNS:${altname}, "
+ if [[ "${altname}" =~ ^ip: ]]; then
+ SAN="${SAN}IP:${altname:3}, "
+ SUBJ="/CN=${domain:3}/"
+ else
+ SAN="${SAN}DNS:${altname}, "
+ SUBJ="/CN=${domain}/"
+ fi
done
SAN="${SAN%%, }"
local tmp_openssl_cnf
if [ "${OCSP_MUST_STAPLE}" = "yes" ]; then
printf "\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >> "${tmp_openssl_cnf}"
fi
- SUBJ="/CN=${domain}/"
if [[ "${OSTYPE:0:5}" = "MINGW" ]]; then
# The subject starts with a /, so MSYS will assume it's a path and convert
# it unless we escape it with another one:
if [[ -e "${cert}" && "${force_renew}" = "no" ]]; then
printf " + Checking domain name(s) of existing cert..."
- certnames="$("${OPENSSL}" x509 -in "${cert}" -text -noout | grep DNS: | _sed 's/DNS://g' | tr -d ' ' | tr ',' '\n' | sort -u | tr '\n' ' ' | _sed 's/ $//')"
- givennames="$(echo "${domain}" "${morenames}"| tr ' ' '\n' | sort -u | tr '\n' ' ' | _sed 's/ $//' | _sed 's/^ //')"
-
+ certnames="$("${OPENSSL}" x509 -in "${cert}" -text -noout | grep -E '(DNS|IP( Address*)):' | _sed 's/(DNS|IP( Address)*)://g' | tr -d ' ' | tr ',' '\n' | sort -u | tr '\n' ' ' | _sed 's/ $//')"
+ givennames="$(echo "${domain}" "${morenames}"| tr ' ' '\n' | sort -u | tr '\n' ' ' | _sed 's/ip://g' | _sed 's/ $//' | _sed 's/^ //')"
+
if [[ "${certnames}" = "${givennames}" ]]; then
echo " unchanged."
else