123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- #!/usr/bin/env sh
- # shellcheck disable=SC2034
- dns_edgedns_info='Akamai.com Edge DNS
- Site: techdocs.Akamai.com/edge-dns/reference/edge-dns-api
- Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_edgedns
- Options: Specify individual credentials
- AKAMAI_HOST Host
- AKAMAI_ACCESS_TOKEN Access token
- AKAMAI_CLIENT_TOKEN Client token
- AKAMAI_CLIENT_SECRET Client secret
- Issues: github.com/acmesh-official/acme.sh/issues/3157
- '
- # Akamai Edge DNS v2 API
- # User must provide Open Edgegrid API credentials to the EdgeDNS installation. The remote user in EdgeDNS must have CRUD access to
- # Edge DNS Zones and Recordsets, e.g. DNS—Zone Record Management authorization
- # Report bugs to https://control.akamai.com/apps/support-ui/#/contact-support
- # *** TBD. NOT IMPLEMENTED YET ***
- # Specify Edgegrid credentials file and section.
- # AKAMAI_EDGERC Edge RC. Full file path
- # AKAMAI_EDGERC_SECTION Edge RC Section. E.g. "default"
- ACME_EDGEDNS_VERSION="0.1.0"
- ######## Public functions #####################
- # Usage: dns_edgedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
- # Used to add txt record
- #
- dns_edgedns_add() {
- fulldomain=$1
- txtvalue=$2
- _debug "ENTERING DNS_EDGEDNS_ADD"
- _debug2 "fulldomain" "$fulldomain"
- _debug2 "txtvalue" "$txtvalue"
- if ! _EDGEDNS_credentials; then
- _err "$@"
- return 1
- fi
- if ! _EDGEDNS_getZoneInfo "$fulldomain"; then
- _err "Invalid domain"
- return 1
- fi
- _debug2 "Add: zone" "$zone"
- acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "$edge_endpoint" "$zone" "$fulldomain")
- _debug3 "Add URL" "$acmeRecordURI"
- # Get existing TXT record
- _edge_result=$(_edgedns_rest GET "$acmeRecordURI")
- _api_status="$?"
- _debug3 "_edge_result" "$_edge_result"
- if [ "$_api_status" -ne 0 ]; then
- if [ "$curResult" = "FATAL" ]; then
- _err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
- fi
- if [ "$_edge_result" != "404" ]; then
- _err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")"
- return 1
- fi
- fi
- rdata="\"${txtvalue}\""
- record_op="POST"
- if [ "$_api_status" -eq 0 ]; then
- # record already exists. Get existing record data and update
- record_op="PUT"
- rdlist="${_edge_result#*\"rdata\":[}"
- rdlist="${rdlist%%]*}"
- rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\")
- _debug3 "existing TXT found"
- _debug3 "record data" "$rdlist"
- # value already there?
- if _contains "$rdlist" "$txtvalue"; then
- return 0
- fi
- _txt_val=""
- while [ "$_txt_val" != "$rdlist" ] && [ "${rdlist}" ]; do
- _txt_val="${rdlist%%,*}"
- rdlist="${rdlist#*,}"
- rdata="${rdata},\"${_txt_val}\""
- done
- fi
- # Add the txtvalue TXT Record
- body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}"
- _debug3 "Add body '${body}'"
- _edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body")
- _api_status="$?"
- if [ "$_api_status" -eq 0 ]; then
- _log "$(printf "Text value %s added to recordset %s" "$txtvalue" "$fulldomain")"
- return 0
- else
- _err "$(printf "error adding TXT record for validation. Error: %s" "$_edge_result")"
- return 1
- fi
- }
- # Usage: dns_edgedns_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
- # Used to delete txt record
- #
- dns_edgedns_rm() {
- fulldomain=$1
- txtvalue=$2
- _debug "ENTERING DNS_EDGEDNS_RM"
- _debug2 "fulldomain" "$fulldomain"
- _debug2 "txtvalue" "$txtvalue"
- if ! _EDGEDNS_credentials; then
- _err "$@"
- return 1
- fi
- if ! _EDGEDNS_getZoneInfo "$fulldomain"; then
- _err "Invalid domain"
- return 1
- fi
- _debug2 "RM: zone" "${zone}"
- acmeRecordURI=$(printf "%s/%s/names/%s/types/TXT" "${edge_endpoint}" "$zone" "$fulldomain")
- _debug3 "RM URL" "$acmeRecordURI"
- # Get existing TXT record
- _edge_result=$(_edgedns_rest GET "$acmeRecordURI")
- _api_status="$?"
- if [ "$_api_status" -ne 0 ]; then
- if [ "$curResult" = "FATAL" ]; then
- _err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
- fi
- if [ "$_edge_result" != "404" ]; then
- _err "$(printf "Failure accessing Akamai Edge DNS API Server. Error: %s" "$_edge_result")"
- return 1
- fi
- fi
- _debug3 "_edge_result" "$_edge_result"
- record_op="DELETE"
- body=""
- if [ "$_api_status" -eq 0 ]; then
- # record already exists. Get existing record data and update
- rdlist="${_edge_result#*\"rdata\":[}"
- rdlist="${rdlist%%]*}"
- rdlist=$(echo "$rdlist" | tr -d '"' | tr -d "\\\\")
- _debug3 "rdlist" "$rdlist"
- if [ -n "$rdlist" ]; then
- record_op="PUT"
- comma=""
- rdata=""
- _txt_val=""
- while [ "$_txt_val" != "$rdlist" ] && [ "$rdlist" ]; do
- _txt_val="${rdlist%%,*}"
- rdlist="${rdlist#*,}"
- _debug3 "_txt_val" "$_txt_val"
- _debug3 "txtvalue" "$txtvalue"
- if ! _contains "$_txt_val" "$txtvalue"; then
- rdata="${rdata}${comma}\"${_txt_val}\""
- comma=","
- fi
- done
- if [ -z "$rdata" ]; then
- record_op="DELETE"
- else
- # Recreate the txtvalue TXT Record
- body="{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"ttl\":600, \"rdata\":"[${rdata}]"}"
- _debug3 "body" "$body"
- fi
- fi
- fi
- _edge_result=$(_edgedns_rest "$record_op" "$acmeRecordURI" "$body")
- _api_status="$?"
- if [ "$_api_status" -eq 0 ]; then
- _log "$(printf "Text value %s removed from recordset %s" "$txtvalue" "$fulldomain")"
- return 0
- else
- _err "$(printf "error removing TXT record for validation. Error: %s" "$_edge_result")"
- return 1
- fi
- }
- #################### Private functions below ##################################
- _EDGEDNS_credentials() {
- _debug "GettingEdge DNS credentials"
- _log "$(printf "ACME DNSAPI Edge DNS version %s" ${ACME_EDGEDNS_VERSION})"
- args_missing=0
- AKAMAI_ACCESS_TOKEN="${AKAMAI_ACCESS_TOKEN:-$(_readaccountconf_mutable AKAMAI_ACCESS_TOKEN)}"
- if [ -z "$AKAMAI_ACCESS_TOKEN" ]; then
- AKAMAI_ACCESS_TOKEN=""
- AKAMAI_CLIENT_TOKEN=""
- AKAMAI_HOST=""
- AKAMAI_CLIENT_SECRET=""
- _err "AKAMAI_ACCESS_TOKEN is missing"
- args_missing=1
- fi
- AKAMAI_CLIENT_TOKEN="${AKAMAI_CLIENT_TOKEN:-$(_readaccountconf_mutable AKAMAI_CLIENT_TOKEN)}"
- if [ -z "$AKAMAI_CLIENT_TOKEN" ]; then
- AKAMAI_ACCESS_TOKEN=""
- AKAMAI_CLIENT_TOKEN=""
- AKAMAI_HOST=""
- AKAMAI_CLIENT_SECRET=""
- _err "AKAMAI_CLIENT_TOKEN is missing"
- args_missing=1
- fi
- AKAMAI_HOST="${AKAMAI_HOST:-$(_readaccountconf_mutable AKAMAI_HOST)}"
- if [ -z "$AKAMAI_HOST" ]; then
- AKAMAI_ACCESS_TOKEN=""
- AKAMAI_CLIENT_TOKEN=""
- AKAMAI_HOST=""
- AKAMAI_CLIENT_SECRET=""
- _err "AKAMAI_HOST is missing"
- args_missing=1
- fi
- AKAMAI_CLIENT_SECRET="${AKAMAI_CLIENT_SECRET:-$(_readaccountconf_mutable AKAMAI_CLIENT_SECRET)}"
- if [ -z "$AKAMAI_CLIENT_SECRET" ]; then
- AKAMAI_ACCESS_TOKEN=""
- AKAMAI_CLIENT_TOKEN=""
- AKAMAI_HOST=""
- AKAMAI_CLIENT_SECRET=""
- _err "AKAMAI_CLIENT_SECRET is missing"
- args_missing=1
- fi
- if [ "$args_missing" = 1 ]; then
- _err "You have not properly specified the EdgeDNS Open Edgegrid API credentials. Please try again."
- return 1
- else
- _saveaccountconf_mutable AKAMAI_ACCESS_TOKEN "$AKAMAI_ACCESS_TOKEN"
- _saveaccountconf_mutable AKAMAI_CLIENT_TOKEN "$AKAMAI_CLIENT_TOKEN"
- _saveaccountconf_mutable AKAMAI_HOST "$AKAMAI_HOST"
- _saveaccountconf_mutable AKAMAI_CLIENT_SECRET "$AKAMAI_CLIENT_SECRET"
- # Set whether curl should use secure or insecure mode
- fi
- export HTTPS_INSECURE=0 # All Edgegrid API calls are secure
- edge_endpoint=$(printf "https://%s/config-dns/v2/zones" "$AKAMAI_HOST")
- _debug3 "Edge API Endpoint:" "$edge_endpoint"
- }
- _EDGEDNS_getZoneInfo() {
- _debug "Getting Zoneinfo"
- zoneEnd=false
- curZone=$1
- while [ -n "$zoneEnd" ]; do
- # we can strip the first part of the fulldomain, since its just the _acme-challenge string
- curZone="${curZone#*.}"
- # suffix . needed for zone -> domain.tld.
- # create zone get url
- get_zone_url=$(printf "%s/%s" "$edge_endpoint" "$curZone")
- _debug3 "Zone Get: " "${get_zone_url}"
- curResult=$(_edgedns_rest GET "$get_zone_url")
- retVal=$?
- if [ "$retVal" -ne 0 ]; then
- if [ "$curResult" = "FATAL" ]; then
- _err "$(printf "Fatal error: acme API function call : %s" "$retVal")"
- fi
- if [ "$curResult" != "404" ]; then
- _err "$(printf "Managed zone validation failed. Error response: %s" "$retVal")"
- return 1
- fi
- fi
- if _contains "$curResult" "\"zone\":"; then
- _debug2 "Zone data" "${curResult}"
- zone=$(echo "${curResult}" | _egrep_o "\"zone\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")
- _debug3 "Zone" "${zone}"
- zoneEnd=""
- return 0
- fi
- if [ "${curZone#*.}" != "$curZone" ]; then
- _debug3 "$(printf "%s still contains a '.' - so we can check next higher level" "$curZone")"
- else
- zoneEnd=true
- _err "Couldn't retrieve zone data."
- return 1
- fi
- done
- _err "Failed to retrieve zone data."
- return 2
- }
- _edgedns_headers=""
- _edgedns_rest() {
- _debug "Handling API Request"
- m=$1
- # Assume endpoint is complete path, including query args if applicable
- ep=$2
- body_data=$3
- _edgedns_content_type=""
- _request_url_path="$ep"
- _request_body="$body_data"
- _request_method="$m"
- _edgedns_headers=""
- tab=""
- _edgedns_headers="${_edgedns_headers}${tab}Host: ${AKAMAI_HOST}"
- tab="\t"
- # Set in acme.sh _post/_get
- #_edgedns_headers="${_edgedns_headers}${tab}User-Agent:ACME DNSAPI Edge DNS version ${ACME_EDGEDNS_VERSION}"
- _edgedns_headers="${_edgedns_headers}${tab}Accept: application/json,*/*"
- if [ "$m" != "GET" ] && [ "$m" != "DELETE" ]; then
- _edgedns_content_type="application/json"
- _debug3 "_request_body" "$_request_body"
- _body_len=$(echo "$_request_body" | tr -d "\n\r" | awk '{print length}')
- _edgedns_headers="${_edgedns_headers}${tab}Content-Length: ${_body_len}"
- fi
- _edgedns_make_auth_header
- _edgedns_headers="${_edgedns_headers}${tab}Authorization: ${_signed_auth_header}"
- _secure_debug2 "Made Auth Header" "$_signed_auth_header"
- hdr_indx=1
- work_header="${_edgedns_headers}${tab}"
- _debug3 "work_header" "$work_header"
- while [ "$work_header" ]; do
- entry="${work_header%%\\t*}"
- work_header="${work_header#*\\t}"
- export "$(printf "_H%s=%s" "$hdr_indx" "$entry")"
- _debug2 "Request Header " "$entry"
- hdr_indx=$((hdr_indx + 1))
- done
- # clear headers from previous request to avoid getting wrong http code on timeouts
- : >"$HTTP_HEADER"
- _debug2 "$ep"
- if [ "$m" != "GET" ]; then
- _debug3 "Method data" "$data"
- # body url [needbase64] [POST|PUT|DELETE] [ContentType]
- response=$(_post "$_request_body" "$ep" false "$m" "$_edgedns_content_type")
- else
- response=$(_get "$ep")
- fi
- _ret="$?"
- if [ "$_ret" -ne 0 ]; then
- _err "$(printf "acme.sh API function call failed. Error: %s" "$_ret")"
- echo "FATAL"
- return "$_ret"
- fi
- _debug2 "response" "${response}"
- _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
- _debug2 "http response code" "$_code"
- if [ "$_code" = "200" ] || [ "$_code" = "201" ]; then
- # All good
- response="$(echo "${response}" | _normalizeJson)"
- echo "$response"
- return 0
- fi
- if [ "$_code" = "204" ]; then
- # Success, no body
- echo "$_code"
- return 0
- fi
- if [ "$_code" = "400" ]; then
- _err "Bad request presented"
- _log "$(printf "Headers: %s" "$_edgedns_headers")"
- _log "$(printf "Method: %s" "$_request_method")"
- _log "$(printf "URL: %s" "$ep")"
- _log "$(printf "Data: %s" "$data")"
- fi
- if [ "$_code" = "403" ]; then
- _err "access denied make sure your Edgegrid cedentials are correct."
- fi
- echo "$_code"
- return 1
- }
- _edgedns_eg_timestamp() {
- _debug "Generating signature Timestamp"
- _debug3 "Retriving ntp time"
- _timeheaders="$(_get "https://www.ntp.org" "onlyheader")"
- _debug3 "_timeheaders" "$_timeheaders"
- _ntpdate="$(echo "$_timeheaders" | grep -i "Date:" | _head_n 1 | cut -d ':' -f 2- | tr -d "\r\n")"
- _debug3 "_ntpdate" "$_ntpdate"
- _ntpdate="$(echo "${_ntpdate}" | sed -e 's/^[[:space:]]*//')"
- _debug3 "_NTPDATE" "$_ntpdate"
- _ntptime="$(echo "${_ntpdate}" | _head_n 1 | cut -d " " -f 5 | tr -d "\r\n")"
- _debug3 "_ntptime" "$_ntptime"
- _eg_timestamp=$(date -u "+%Y%m%dT")
- _eg_timestamp="$(printf "%s%s+0000" "$_eg_timestamp" "$_ntptime")"
- _debug "_eg_timestamp" "$_eg_timestamp"
- }
- _edgedns_new_nonce() {
- _debug "Generating Nonce"
- _nonce=$(echo "EDGEDNS$(_time)" | _digest sha1 hex | cut -c 1-32)
- _debug3 "_nonce" "$_nonce"
- }
- _edgedns_make_auth_header() {
- _debug "Constructing Auth Header"
- _edgedns_new_nonce
- _edgedns_eg_timestamp
- # "Unsigned authorization header: 'EG1-HMAC-SHA256 client_token=block;access_token=block;timestamp=20200806T14:16:33+0000;nonce=72cde72c-82d9-4721-9854-2ba057929d67;'"
- _auth_header="$(printf "EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;" "$AKAMAI_CLIENT_TOKEN" "$AKAMAI_ACCESS_TOKEN" "$_eg_timestamp" "$_nonce")"
- _secure_debug2 "Unsigned Auth Header: " "$_auth_header"
- _edgedns_sign_request
- _signed_auth_header="$(printf "%ssignature=%s" "$_auth_header" "$_signed_req")"
- _secure_debug2 "Signed Auth Header: " "${_signed_auth_header}"
- }
- _edgedns_sign_request() {
- _debug2 "Signing http request"
- _edgedns_make_data_to_sign "$_auth_header"
- _secure_debug2 "Returned signed data" "$_mdata"
- _edgedns_make_signing_key "$_eg_timestamp"
- _edgedns_base64_hmac_sha256 "$_mdata" "$_signing_key"
- _signed_req="$_hmac_out"
- _secure_debug2 "Signed Request" "$_signed_req"
- }
- _edgedns_make_signing_key() {
- _debug2 "Creating sigining key"
- ts=$1
- _edgedns_base64_hmac_sha256 "$ts" "$AKAMAI_CLIENT_SECRET"
- _signing_key="$_hmac_out"
- _secure_debug2 "Signing Key" "$_signing_key"
- }
- _edgedns_make_data_to_sign() {
- _debug2 "Processing data to sign"
- hdr=$1
- _secure_debug2 "hdr" "$hdr"
- _edgedns_make_content_hash
- path="$(echo "$_request_url_path" | tr -d "\n\r" | sed 's/https\?:\/\///')"
- path=${path#*"$AKAMAI_HOST"}
- _debug "hier path" "$path"
- # dont expose headers to sign so use MT string
- _mdata="$(printf "%s\thttps\t%s\t%s\t%s\t%s\t%s" "$_request_method" "$AKAMAI_HOST" "$path" "" "$_hash" "$hdr")"
- _secure_debug2 "Data to Sign" "$_mdata"
- }
- _edgedns_make_content_hash() {
- _debug2 "Generating content hash"
- _hash=""
- _debug2 "Request method" "${_request_method}"
- if [ "$_request_method" != "POST" ] || [ -z "$_request_body" ]; then
- return 0
- fi
- _debug2 "Req body" "$_request_body"
- _edgedns_base64_sha256 "$_request_body"
- _hash="$_sha256_out"
- _debug2 "Content hash" "$_hash"
- }
- _edgedns_base64_hmac_sha256() {
- _debug2 "Generating hmac"
- data=$1
- key=$2
- encoded_data="$(echo "$data" | iconv -t utf-8)"
- encoded_key="$(echo "$key" | iconv -t utf-8)"
- _secure_debug2 "encoded data" "$encoded_data"
- _secure_debug2 "encoded key" "$encoded_key"
- encoded_key_hex=$(printf "%s" "$encoded_key" | _hex_dump | tr -d ' ')
- data_sig="$(echo "$encoded_data" | tr -d "\n\r" | _hmac sha256 "$encoded_key_hex" | _base64)"
- _secure_debug2 "data_sig:" "$data_sig"
- _hmac_out="$(echo "$data_sig" | tr -d "\n\r" | iconv -f utf-8)"
- _secure_debug2 "hmac" "$_hmac_out"
- }
- _edgedns_base64_sha256() {
- _debug2 "Creating sha256 digest"
- trg=$1
- _secure_debug2 "digest data" "$trg"
- digest="$(echo "$trg" | tr -d "\n\r" | _digest "sha256")"
- _sha256_out="$(echo "$digest" | tr -d "\n\r" | iconv -f utf-8)"
- _secure_debug2 "digest decode" "$_sha256_out"
- }
- #_edgedns_parse_edgerc() {
- # filepath=$1
- # section=$2
- #}
|