249 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Bash
		
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Bash
		
	
	
#!/usr/bin/env sh
 | 
						|
 | 
						|
## Will be called by acme.sh to add the TXT record via the Bunny DNS API.
 | 
						|
## returns 0 means success, otherwise error.
 | 
						|
 | 
						|
## Author: nosilver4u <nosilver4u at ewww.io>
 | 
						|
## GitHub: https://github.com/nosilver4u/acme.sh
 | 
						|
 | 
						|
##
 | 
						|
## Environment Variables Required:
 | 
						|
##
 | 
						|
## BUNNY_API_KEY="75310dc4-ca77-9ac3-9a19-f6355db573b49ce92ae1-2655-3ebd-61ac-3a3ae34834cc"
 | 
						|
##
 | 
						|
 | 
						|
#####################  Public functions  #####################
 | 
						|
 | 
						|
## Create the text record for validation.
 | 
						|
## Usage: fulldomain txtvalue
 | 
						|
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
 | 
						|
dns_bunny_add() {
 | 
						|
  fulldomain="$(echo "$1" | _lower_case)"
 | 
						|
  txtvalue=$2
 | 
						|
 | 
						|
  BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
 | 
						|
  # Check if API Key is set
 | 
						|
  if [ -z "$BUNNY_API_KEY" ]; then
 | 
						|
    BUNNY_API_KEY=""
 | 
						|
    _err "You did not specify Bunny.net API key."
 | 
						|
    _err "Please export BUNNY_API_KEY and try again."
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
 | 
						|
  _info "Using Bunny.net dns validation - add record"
 | 
						|
  _debug fulldomain "$fulldomain"
 | 
						|
  _debug txtvalue "$txtvalue"
 | 
						|
 | 
						|
  ## save the env vars (key and domain split location) for later automated use
 | 
						|
  _saveaccountconf_mutable BUNNY_API_KEY "$BUNNY_API_KEY"
 | 
						|
 | 
						|
  ## split the domain for Bunny API
 | 
						|
  if ! _get_base_domain "$fulldomain"; then
 | 
						|
    _err "domain not found in your account for addition"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
  _debug _sub_domain "$_sub_domain"
 | 
						|
  _debug _domain "$_domain"
 | 
						|
  _debug _domain_id "$_domain_id"
 | 
						|
 | 
						|
  ## Set the header with our post type and auth key
 | 
						|
  export _H1="Accept: application/json"
 | 
						|
  export _H2="AccessKey: $BUNNY_API_KEY"
 | 
						|
  export _H3="Content-Type: application/json"
 | 
						|
  PURL="https://api.bunny.net/dnszone/$_domain_id/records"
 | 
						|
  PBODY='{"Id":'$_domain_id',"Type":3,"Name":"'$_sub_domain'","Value":"'$txtvalue'","ttl":120}'
 | 
						|
 | 
						|
  _debug PURL "$PURL"
 | 
						|
  _debug PBODY "$PBODY"
 | 
						|
 | 
						|
  ## the create request - POST
 | 
						|
  ## args: BODY, URL, [need64, httpmethod]
 | 
						|
  response="$(_post "$PBODY" "$PURL" "" "PUT")"
 | 
						|
 | 
						|
  ## check response
 | 
						|
  if [ "$?" != "0" ]; then
 | 
						|
    _err "error in response: $response"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
  _debug2 response "$response"
 | 
						|
 | 
						|
  ## finished correctly
 | 
						|
  return 0
 | 
						|
}
 | 
						|
 | 
						|
## Remove the txt record after validation.
 | 
						|
## Usage: fulldomain txtvalue
 | 
						|
## EG: "_acme-challenge.www.other.domain.com" "XKrxpRBosdq0HG9i01zxXp5CPBs"
 | 
						|
dns_bunny_rm() {
 | 
						|
  fulldomain="$(echo "$1" | _lower_case)"
 | 
						|
  txtvalue=$2
 | 
						|
 | 
						|
  BUNNY_API_KEY="${BUNNY_API_KEY:-$(_readaccountconf_mutable BUNNY_API_KEY)}"
 | 
						|
  # Check if API Key Exists
 | 
						|
  if [ -z "$BUNNY_API_KEY" ]; then
 | 
						|
    BUNNY_API_KEY=""
 | 
						|
    _err "You did not specify Bunny.net API key."
 | 
						|
    _err "Please export BUNNY_API_KEY and try again."
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
 | 
						|
  _info "Using Bunny.net dns validation - remove record"
 | 
						|
  _debug fulldomain "$fulldomain"
 | 
						|
  _debug txtvalue "$txtvalue"
 | 
						|
 | 
						|
  ## split the domain for Bunny API
 | 
						|
  if ! _get_base_domain "$fulldomain"; then
 | 
						|
    _err "Domain not found in your account for TXT record removal"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
  _debug _sub_domain "$_sub_domain"
 | 
						|
  _debug _domain "$_domain"
 | 
						|
  _debug _domain_id "$_domain_id"
 | 
						|
 | 
						|
  ## Set the header with our post type and key auth key
 | 
						|
  export _H1="Accept: application/json"
 | 
						|
  export _H2="AccessKey: $BUNNY_API_KEY"
 | 
						|
  ## get URL for the list of DNS records
 | 
						|
  GURL="https://api.bunny.net/dnszone/$_domain_id"
 | 
						|
 | 
						|
  ## 1) Get the domain/zone records
 | 
						|
  ## the fetch request - GET
 | 
						|
  ## args: URL, [onlyheader, timeout]
 | 
						|
  domain_list="$(_get "$GURL")"
 | 
						|
 | 
						|
  ## check response
 | 
						|
  if [ "$?" != "0" ]; then
 | 
						|
    _err "error in domain_list response: $domain_list"
 | 
						|
    return 1
 | 
						|
  fi
 | 
						|
  _debug2 domain_list "$domain_list"
 | 
						|
 | 
						|
  ## 2) search through records
 | 
						|
  ## check for what we are looking for: "Type":3,"Value":"$txtvalue","Name":"$_sub_domain"
 | 
						|
  record="$(echo "$domain_list" | _egrep_o "\"Id\"\s*\:\s*\"*[0-9]+\"*,\s*\"Type\"[^}]*\"Value\"\s*\:\s*\"$txtvalue\"[^}]*\"Name\"\s*\:\s*\"$_sub_domain\"")"
 | 
						|
 | 
						|
  if [ -n "$record" ]; then
 | 
						|
 | 
						|
    ## We found records
 | 
						|
    rec_ids="$(echo "$record" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
 | 
						|
    _debug rec_ids "$rec_ids"
 | 
						|
    if [ -n "$rec_ids" ]; then
 | 
						|
      echo "$rec_ids" | while IFS= read -r rec_id; do
 | 
						|
        ## delete the record
 | 
						|
        ## delete URL for removing the one we dont want
 | 
						|
        DURL="https://api.bunny.net/dnszone/$_domain_id/records/$rec_id"
 | 
						|
 | 
						|
        ## the removal request - DELETE
 | 
						|
        ## args: BODY, URL, [need64, httpmethod]
 | 
						|
        response="$(_post "" "$DURL" "" "DELETE")"
 | 
						|
 | 
						|
        ## check response (sort of)
 | 
						|
        if [ "$?" != "0" ]; then
 | 
						|
          _err "error in remove response: $response"
 | 
						|
          return 1
 | 
						|
        fi
 | 
						|
        _debug2 response "$response"
 | 
						|
 | 
						|
      done
 | 
						|
    fi
 | 
						|
  fi
 | 
						|
 | 
						|
  ## finished correctly
 | 
						|
  return 0
 | 
						|
}
 | 
						|
 | 
						|
#####################  Private functions below  #####################
 | 
						|
 | 
						|
## Split the domain provided into the "base domain" and the "start prefix".
 | 
						|
## This function searches for the longest subdomain in your account
 | 
						|
## for the full domain given and splits it into the base domain (zone)
 | 
						|
## and the prefix/record to be added/removed
 | 
						|
## USAGE: fulldomain
 | 
						|
## EG: "_acme-challenge.two.three.four.domain.com"
 | 
						|
## returns
 | 
						|
## _sub_domain="_acme-challenge.two"
 | 
						|
## _domain="three.four.domain.com" *IF* zone "three.four.domain.com" exists
 | 
						|
## _domain_id=234
 | 
						|
## if only "domain.com" exists it will return
 | 
						|
## _sub_domain="_acme-challenge.two.three.four"
 | 
						|
## _domain="domain.com"
 | 
						|
## _domain_id=234
 | 
						|
_get_base_domain() {
 | 
						|
  # args
 | 
						|
  fulldomain="$(echo "$1" | _lower_case)"
 | 
						|
  _debug fulldomain "$fulldomain"
 | 
						|
 | 
						|
  # domain max legal length = 253
 | 
						|
  MAX_DOM=255
 | 
						|
  page=1
 | 
						|
 | 
						|
  ## get a list of domains for the account to check thru
 | 
						|
  ## Set the headers
 | 
						|
  export _H1="Accept: application/json"
 | 
						|
  export _H2="AccessKey: $BUNNY_API_KEY"
 | 
						|
  _debug BUNNY_API_KEY "$BUNNY_API_KEY"
 | 
						|
  ## get URL for the list of domains
 | 
						|
  ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
 | 
						|
  DOMURL="https://api.bunny.net/dnszone"
 | 
						|
 | 
						|
  ## while we dont have a matching domain we keep going
 | 
						|
  while [ -z "$found" ]; do
 | 
						|
    ## get the domain list (current page)
 | 
						|
    domain_list="$(_get "$DOMURL")"
 | 
						|
 | 
						|
    ## check response
 | 
						|
    if [ "$?" != "0" ]; then
 | 
						|
      _err "error in domain_list response: $domain_list"
 | 
						|
      return 1
 | 
						|
    fi
 | 
						|
    _debug2 domain_list "$domain_list"
 | 
						|
 | 
						|
    i=1
 | 
						|
    while [ $i -gt 0 ]; do
 | 
						|
      ## get next longest domain
 | 
						|
      _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")
 | 
						|
      ## check we got something back from our cut (or are we at the end)
 | 
						|
      if [ -z "$_domain" ]; then
 | 
						|
        break
 | 
						|
      fi
 | 
						|
      ## we got part of a domain back - grep it out
 | 
						|
      found="$(echo "$domain_list" | _egrep_o "\"Id\"\s*:\s*\"*[0-9]+\"*,\s*\"Domain\"\s*\:\s*\"$_domain\"")"
 | 
						|
      ## check if it exists
 | 
						|
      if [ -n "$found" ]; then
 | 
						|
        ## exists - exit loop returning the parts
 | 
						|
        sub_point=$(_math $i - 1)
 | 
						|
        _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-"$sub_point")
 | 
						|
        _domain_id="$(echo "$found" | _egrep_o "Id\"\s*\:\s*\"*[0-9]+" | _egrep_o "[0-9]+")"
 | 
						|
        _debug _domain_id "$_domain_id"
 | 
						|
        _debug _domain "$_domain"
 | 
						|
        _debug _sub_domain "$_sub_domain"
 | 
						|
        found=""
 | 
						|
        return 0
 | 
						|
      fi
 | 
						|
      ## increment cut point $i
 | 
						|
      i=$(_math $i + 1)
 | 
						|
    done
 | 
						|
 | 
						|
    if [ -z "$found" ]; then
 | 
						|
      page=$(_math $page + 1)
 | 
						|
      nextpage="https://api.bunny.net/dnszone?page=$page"
 | 
						|
      ## Find the next page if we don't have a match.
 | 
						|
      hasnextpage="$(echo "$domain_list" | _egrep_o "\"HasMoreItems\"\s*:\s*true")"
 | 
						|
      if [ -z "$hasnextpage" ]; then
 | 
						|
        _err "No record and no nextpage in Bunny.net domain search."
 | 
						|
        found=""
 | 
						|
        return 1
 | 
						|
      fi
 | 
						|
      _debug2 nextpage "$nextpage"
 | 
						|
      DOMURL="$nextpage"
 | 
						|
    fi
 | 
						|
 | 
						|
  done
 | 
						|
 | 
						|
  ## We went through the entire domain zone list and didn't find one that matched.
 | 
						|
  ## If we ever get here, something is broken in the code...
 | 
						|
  _err "Domain not found in Bunny.net account, but we should never get here!"
 | 
						|
  found=""
 | 
						|
  return 1
 | 
						|
}
 |