X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2Fproxmox-acme;h=a00d23a2a120e39e05c93837780e490eb2c5e916;hb=51b0ba75c14785c6de881560561d273b77ce43b5;hp=566588f64386369ee2c6685c23420263ca95f2ca;hpb=54fd0088c11e2f2fcbcf8bdd83ac581000a9acd7;p=proxmox-acme.git diff --git a/src/proxmox-acme b/src/proxmox-acme index 566588f..a00d23a 100644 --- a/src/proxmox-acme +++ b/src/proxmox-acme @@ -1,6 +1,6 @@ -#!/usr/bin/env sh +#!/bin/bash -VER=0.9 +VER=1.0 PROJECT_NAME="ProxmoxACME" @@ -33,6 +33,11 @@ _digest() { fi } +_usage() { + __red "$@" >&2 + printf "\n" >&2 +} + _upper_case() { # shellcheck disable=SC2018,SC2019 tr 'a-z' 'A-Z' @@ -108,6 +113,117 @@ _egrep_o() { fi } +_h2b() { + if _exists xxd; then + if _contains "$(xxd --help 2>&1)" "assumes -c30"; then + if xxd -r -p -c 9999 2>/dev/null; then + return + fi + else + if xxd -r -p 2>/dev/null; then + return + fi + fi + fi + + hex=$(cat) + ic="" + jc="" + _debug2 _URGLY_PRINTF "$_URGLY_PRINTF" + if [ -z "$_URGLY_PRINTF" ]; then + if [ "$_ESCAPE_XARGS" ] && _exists xargs; then + _debug2 "xargs" + echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' | xargs printf + else + for h in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/ \1/g'); do + if [ -z "$h" ]; then + break + fi + printf "\x$h%s" + done + fi + else + for c in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\)/ \1/g'); do + if [ -z "$ic" ]; then + ic=$c + continue + fi + jc=$c + ic="$(_h_char_2_dec "$ic")" + jc="$(_h_char_2_dec "$jc")" + printf '\'"$(printf "%o" "$(_math "$ic" \* 16 + $jc)")""%s" + ic="" + jc="" + done + fi + +} + +#Usage: keyfile hashalg +#Output: Base64-encoded signature value +_sign() { + keyfile="$1" + alg="$2" + if [ -z "$alg" ]; then + _usage "Usage: _sign keyfile hashalg" + return 1 + fi + + _sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile " + + if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1 || grep "BEGIN PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then + $_sign_openssl -$alg | _base64 + elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then + if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then + _err "Sign failed: $_sign_openssl" + _err "Key file: $keyfile" + _err "Key content:$(wc -l <"$keyfile") lines" + return 1 + fi + _debug3 "_signedECText" "$_signedECText" + _ec_r="$(echo "$_signedECText" | _head_n 2 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")" + _ec_s="$(echo "$_signedECText" | _head_n 3 | _tail_n 1 | cut -d : -f 4 | tr -d "\r\n")" + if [ "$__ECC_KEY_LEN" -eq "256" ]; then + while [ "${#_ec_r}" -lt "64" ]; do + _ec_r="0${_ec_r}" + done + while [ "${#_ec_s}" -lt "64" ]; do + _ec_s="0${_ec_s}" + done + fi + if [ "$__ECC_KEY_LEN" -eq "384" ]; then + while [ "${#_ec_r}" -lt "96" ]; do + _ec_r="0${_ec_r}" + done + while [ "${#_ec_s}" -lt "96" ]; do + _ec_s="0${_ec_s}" + done + fi + if [ "$__ECC_KEY_LEN" -eq "512" ]; then + while [ "${#_ec_r}" -lt "132" ]; do + _ec_r="0${_ec_r}" + done + while [ "${#_ec_s}" -lt "132" ]; do + _ec_s="0${_ec_s}" + done + fi + _debug3 "_ec_r" "$_ec_r" + _debug3 "_ec_s" "$_ec_s" + printf "%s" "$_ec_r$_ec_s" | _h2b | _base64 + else + _err "Unknown key file format." + return 1 + fi + +} + +#dummy function because proxmox-acme does not call inithttp +_resethttp() { + : +} + +_HTTP_MAX_RETRY=8 + # body url [needbase64] [POST|PUT|DELETE] [ContentType] _post() { body="$1" @@ -115,6 +231,33 @@ _post() { needbase64="$3" httpmethod="$4" _postContentType="$5" + _sleep_retry_sec=1 + _http_retry_times=0 + _hcode=0 + while [ "${_http_retry_times}" -le "$_HTTP_MAX_RETRY" ]; do + [ "$_http_retry_times" = "$_HTTP_MAX_RETRY" ] + _lastHCode="$?" + _debug "Retrying post" + _post_impl "$body" "$_post_url" "$needbase64" "$httpmethod" "$_postContentType" "$_lastHCode" + _hcode="$?" + _debug _hcode "$_hcode" + if [ "$_hcode" = "0" ]; then + break + fi + _http_retry_times=$(_math $_http_retry_times + 1) + _sleep $_sleep_retry_sec + done + return $_hcode +} + +# body url [needbase64] [POST|PUT|DELETE] [ContentType] [displayError] +_post_impl() { + body="$1" + _post_url="$2" + needbase64="$3" + httpmethod="$4" + _postContentType="$5" + displayError="$6" if [ -z "$httpmethod" ]; then httpmethod="POST" @@ -158,7 +301,9 @@ _post() { fi _ret="$?" if [ "$_ret" != "0" ]; then - _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret" + if [ -z "$displayError" ] || [ "$displayError" = "0" ]; then + _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret" + fi fi printf "%s" "$response" return $_ret @@ -169,6 +314,31 @@ _get() { url="$1" onlyheader="$2" t="$3" + _sleep_retry_sec=1 + _http_retry_times=0 + _hcode=0 + while [ "${_http_retry_times}" -le "$_HTTP_MAX_RETRY" ]; do + [ "$_http_retry_times" = "$_HTTP_MAX_RETRY" ] + _lastHCode="$?" + _debug "Retrying GET" + _get_impl "$url" "$onlyheader" "$t" "$_lastHCode" + _hcode="$?" + _debug _hcode "$_hcode" + if [ "$_hcode" = "0" ]; then + break + fi + _http_retry_times=$(_math $_http_retry_times + 1) + _sleep $_sleep_retry_sec + done + return $_hcode +} + +# url getheader timeout displayError +_get_impl() { + url="$1" + onlyheader="$2" + t="$3" + displayError="$4" _CURL="curl -L --silent --dump-header $HTTP_HEADER -g " if [ "$HTTPS_INSECURE" ]; then @@ -184,7 +354,9 @@ _get() { fi ret=$? if [ "$ret" != "0" ]; then - _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $ret" + if [ -z "$displayError" ] || [ "$displayError" = "0" ]; then + _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $ret" + fi fi return $ret } @@ -495,7 +667,7 @@ __red() { } _log() { - return + return 0 } _info() { @@ -516,7 +688,7 @@ _err() { # key _readaccountconf() { - echo "$1" + echo "${!1}" } # key @@ -526,11 +698,11 @@ _readaccountconf_mutable() { # no-ops: _clearaccountconf() { - return + return 0 } _cleardomainconf() { - return + return 0 } _debug() { @@ -562,42 +734,36 @@ _secure_debug3() { } _saveaccountconf() { - return + return 0 } _saveaccountconf_mutable() { - return + return 0 } _save_conf() { - return + return 0 } _savedomainconf() { - return + return 0 } _source_plugin_config() { - return + return 0 } # Proxmox implementation to inject the DNSAPI variables _load_plugin_config() { - tmp_str="${plugin_conf_string//[^,]}" - index="$(_math ${#tmp_str} + 1)" - while [ "$index" -gt "0" ] - do - field=$(_getfield $plugin_conf_string "$index" ",") - ADDR=(${field/=/ }) + while IFS= read -r line; do + ADDR=(${line/=/ }) key="${ADDR[0]}" value="${ADDR[1]}" - # decode base64 encoded values - value=$(echo $value | /usr/bin/openssl base64 -d -A) - # acme.sh uses eval insted of export - export "$key"="$value" - index="$(_math "$index" - 1)" + if [ -n "$key" ]; then + export "$key"="$value" + fi done } @@ -613,9 +779,9 @@ setup() { dns_plugin="dns_$1" dns_plugin_path="${DNS_PLUGIN_PATH}/${dns_plugin}.sh" fqdn="_acme-challenge.$2" - txtvalue=$3 + DEBUG=$3 + IFS= read -r txtvalue plugin_conf_string=$4 - DEBUG=$5 _load_plugin_config @@ -640,9 +806,8 @@ teardown() { dns_plugin="dns_$1" dns_plugin_path="${DNS_PLUGIN_PATH}/${dns_plugin}.sh" fqdn="_acme-challenge.$2" - txtvalue=$3 - plugin_conf_string=$4 - DEBUG=$5 + DEBUG=$3 + IFS= read -r txtvalue _load_plugin_config