Merge pull request #4302 from acmesh-official/dev

sync
This commit is contained in:
neil 2022-09-08 22:12:10 +08:00 committed by GitHub
commit 8ded524236
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 710 additions and 93 deletions

View File

@ -81,9 +81,7 @@ jobs:
if [ "${{ secrets.TokenName5}}" ] ; then if [ "${{ secrets.TokenName5}}" ] ; then
echo "${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}" >> docker.env echo "${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}" >> docker.env
fi fi
echo "TEST_DNS_NO_WILDCARD" >> docker.env
echo "http_proxy" >> docker.env
echo "https_proxy" >> docker.env
- name: Run acmetest - name: Run acmetest
run: cd ../acmetest && ./rundocker.sh testall run: cd ../acmetest && ./rundocker.sh testall
@ -236,56 +234,9 @@ jobs:
Solaris:
runs-on: macos-12
needs: FreeBSD
env:
TEST_DNS : ${{ secrets.TEST_DNS }}
TestingDomain: ${{ secrets.TestingDomain }}
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
CASE: le_test_dnsapi
TEST_LOCAL: 1
DEBUG: ${{ secrets.DEBUG }}
http_proxy: ${{ secrets.http_proxy }}
https_proxy: ${{ secrets.https_proxy }}
steps:
- uses: actions/checkout@v2
- name: Clone acmetest
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/solaris-vm@v0
with:
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
copyback: false
prepare: pkgutil -y -i socat
run: |
pkg set-mediator -v -I default@1.1 openssl
export PATH=/usr/gnu/bin:$PATH
if [ "${{ secrets.TokenName1}}" ] ; then
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
fi
if [ "${{ secrets.TokenName2}}" ] ; then
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
fi
if [ "${{ secrets.TokenName3}}" ] ; then
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
fi
if [ "${{ secrets.TokenName4}}" ] ; then
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
fi
if [ "${{ secrets.TokenName5}}" ] ; then
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
fi
cd ../acmetest
./letest.sh
OpenBSD: OpenBSD:
runs-on: macos-12 runs-on: macos-12
needs: Solaris needs: FreeBSD
env: env:
TEST_DNS : ${{ secrets.TEST_DNS }} TEST_DNS : ${{ secrets.TEST_DNS }}
TestingDomain: ${{ secrets.TestingDomain }} TestingDomain: ${{ secrets.TestingDomain }}
@ -424,3 +375,51 @@ jobs:
Solaris:
runs-on: macos-12
needs: DragonFlyBSD
env:
TEST_DNS : ${{ secrets.TEST_DNS }}
TestingDomain: ${{ secrets.TestingDomain }}
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
CASE: le_test_dnsapi
TEST_LOCAL: 1
DEBUG: ${{ secrets.DEBUG }}
http_proxy: ${{ secrets.http_proxy }}
https_proxy: ${{ secrets.https_proxy }}
HTTPS_INSECURE: 1 # always set to 1 to ignore https error, sincc Solaris doesn't accept the expired ISRG X1 root
steps:
- uses: actions/checkout@v2
- name: Clone acmetest
run: cd .. && git clone https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/solaris-vm@v0
with:
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
copyback: false
prepare: pkgutil -y -i socat
run: |
pkg set-mediator -v -I default@1.1 openssl
export PATH=/usr/gnu/bin:$PATH
if [ "${{ secrets.TokenName1}}" ] ; then
export ${{ secrets.TokenName1}}=${{ secrets.TokenValue1}}
fi
if [ "${{ secrets.TokenName2}}" ] ; then
export ${{ secrets.TokenName2}}=${{ secrets.TokenValue2}}
fi
if [ "${{ secrets.TokenName3}}" ] ; then
export ${{ secrets.TokenName3}}=${{ secrets.TokenValue3}}
fi
if [ "${{ secrets.TokenName4}}" ] ; then
export ${{ secrets.TokenName4}}=${{ secrets.TokenValue4}}
fi
if [ "${{ secrets.TokenName5}}" ] ; then
export ${{ secrets.TokenName5}}=${{ secrets.TokenValue5}}
fi
cd ../acmetest
./letest.sh

12
acme.sh
View File

@ -2561,7 +2561,7 @@ __initHome() {
_script_home="$(dirname "$_script")" _script_home="$(dirname "$_script")"
_debug "_script_home" "$_script_home" _debug "_script_home" "$_script_home"
if [ -d "$_script_home" ]; then if [ -d "$_script_home" ]; then
_SCRIPT_HOME="$_script_home" export _SCRIPT_HOME="$_script_home"
else else
_err "It seems the script home is not correct:$_script_home" _err "It seems the script home is not correct:$_script_home"
fi fi
@ -6557,7 +6557,7 @@ install() {
if [ "$_accountemail" ]; then if [ "$_accountemail" ]; then
_saveaccountconf "ACCOUNT_EMAIL" "$_accountemail" _saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
fi fi
_saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
_info OK _info OK
} }
@ -6921,8 +6921,6 @@ installOnline() {
chmod +x $PROJECT_ENTRY chmod +x $PROJECT_ENTRY
if ./$PROJECT_ENTRY --install "$@"; then if ./$PROJECT_ENTRY --install "$@"; then
_info "Install success!" _info "Install success!"
_initpath
_saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
fi fi
cd .. cd ..
@ -7442,17 +7440,17 @@ _process() {
shift shift
;; ;;
--home) --home)
LE_WORKING_DIR="$2" export LE_WORKING_DIR="$2"
shift shift
;; ;;
--cert-home | --certhome) --cert-home | --certhome)
_certhome="$2" _certhome="$2"
CERT_HOME="$_certhome" export CERT_HOME="$_certhome"
shift shift
;; ;;
--config-home) --config-home)
_confighome="$2" _confighome="$2"
LE_CONFIG_HOME="$_confighome" export LE_CONFIG_HOME="$_confighome"
shift shift
;; ;;
--useragent) --useragent)

248
dnsapi/dns_bunny.sh Normal file
View File

@ -0,0 +1,248 @@
#!/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
}

View File

@ -13,6 +13,7 @@
# cPanel_Hostname=hostname # cPanel_Hostname=hostname
# #
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" # Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
# Used to add txt record # Used to add txt record
dns_cpanel_add() { dns_cpanel_add() {
fulldomain=$1 fulldomain=$1
@ -120,7 +121,7 @@ _myget() {
_get_root() { _get_root() {
_myget 'json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzones' _myget 'json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzones'
_domains=$(echo "$_result" | sed 's/.*\(zones.*\[\).*/\1/' | cut -d':' -f2 | sed 's/"//g' | sed 's/{//g') _domains=$(echo "$_result" | _egrep_o '"[a-z0-9\.\-]*":\["; cPanel first' | cut -d':' -f1 | sed 's/"//g' | sed 's/{//g')
_debug "_result is: $_result" _debug "_result is: $_result"
_debug "_domains is: $_domains" _debug "_domains is: $_domains"
if [ -z "$_domains" ]; then if [ -z "$_domains" ]; then
@ -146,7 +147,7 @@ _findentry() {
_debug "In _findentry" _debug "In _findentry"
#returns id of dns entry, if it exists #returns id of dns entry, if it exists
_myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&domain=$_domain" _myget "json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit&cpanel_jsonapi_func=fetchzone_records&domain=$_domain"
_id=$(echo "$_result" | sed "s/.*\(line.*$fulldomain.*$txtvalue\).*/\1/" | cut -d ':' -f 2 | cut -d ',' -f 1) _id=$(echo "$_result" | sed -e "s/},{/},\n{/g" | grep "$fulldomain" | grep "$txtvalue" | _egrep_o 'line":[0-9]+' | cut -d ':' -f 2)
_debug "_result is: $_result" _debug "_result is: $_result"
_debug "fulldomain. is $fulldomain." _debug "fulldomain. is $fulldomain."
_debug "txtvalue is $txtvalue" _debug "txtvalue is $txtvalue"

View File

@ -192,6 +192,7 @@ _get_base_domain() {
## get URL for the list of domains ## 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"}} ## may get: "links":{"pages":{"last":".../v2/domains/DOM/records?page=2","next":".../v2/domains/DOM/records?page=2"}}
DOMURL="https://api.digitalocean.com/v2/domains" DOMURL="https://api.digitalocean.com/v2/domains"
found=""
## while we dont have a matching domain we keep going ## while we dont have a matching domain we keep going
while [ -z "$found" ]; do while [ -z "$found" ]; do
@ -205,9 +206,7 @@ _get_base_domain() {
fi fi
_debug2 domain_list "$domain_list" _debug2 domain_list "$domain_list"
## for each shortening of our $fulldomain, check if it exists in the $domain_list i=1
## can never start on 1 (aka whole $fulldomain) as $fulldomain starts with "_acme-challenge"
i=2
while [ $i -gt 0 ]; do while [ $i -gt 0 ]; do
## get next longest domain ## get next longest domain
_domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM") _domain=$(printf "%s" "$fulldomain" | cut -d . -f "$i"-"$MAX_DOM")

View File

@ -13,8 +13,8 @@ DNSServices_API=https://dns.services/api
#Usage: dns_dnsservices_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" #Usage: dns_dnsservices_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_dnsservices_add() { dns_dnsservices_add() {
fulldomain=$1 fulldomain="$1"
txtvalue=$2 txtvalue="$2"
_info "Using dns.services to create ACME DNS challenge" _info "Using dns.services to create ACME DNS challenge"
_debug2 add_fulldomain "$fulldomain" _debug2 add_fulldomain "$fulldomain"
@ -61,8 +61,8 @@ dns_dnsservices_add() {
#Usage: fulldomain txtvalue #Usage: fulldomain txtvalue
#Description: Remove the txt record after validation. #Description: Remove the txt record after validation.
dns_dnsservices_rm() { dns_dnsservices_rm() {
fulldomain=$1 fulldomain="$1"
txtvalue=$2 txtvalue="$2"
_info "Using dns.services to remove DNS record $fulldomain TXT $txtvalue" _info "Using dns.services to remove DNS record $fulldomain TXT $txtvalue"
_debug rm_fulldomain "$fulldomain" _debug rm_fulldomain "$fulldomain"
@ -117,36 +117,40 @@ _setup_headers() {
} }
_get_root() { _get_root() {
domain=$1 domain="$1"
_debug2 _get_root "Get the root domain of ${domain} for DNS API" _debug2 _get_root "Get the root domain of ${domain} for DNS API"
# Setup _get() and _post() headers # Setup _get() and _post() headers
#_setup_headers #_setup_headers
result=$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/dns") result=$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/dns")
result2="$(printf "%s\n" "$result" | tr '[' '\n' | grep '"name"')"
result3="$(printf "%s\n" "$result2" | tr '}' '\n' | grep '"name"' | sed "s,^\,,,g" | sed "s,$,},g")"
useResult=""
_debug2 _get_root "Got the following root domain(s) $result" _debug2 _get_root "Got the following root domain(s) $result"
_debug2 _get_root "- JSON: $result" _debug2 _get_root "- JSON: $result"
if [ "$(echo "$result" | grep -c '"name"')" -gt "1" ]; then if [ "$(printf "%s\n" "$result" | tr '}' '\n' | grep -c '"name"')" -gt "1" ]; then
checkMultiZones="true" checkMultiZones="true"
_debug2 _get_root "- multiple zones found" _debug2 _get_root "- multiple zones found"
else else
checkMultiZones="false" checkMultiZones="false"
_debug2 _get_root "- single zone found"
fi fi
# Find/isolate the root zone to work with in createRecord() and deleteRecord() # Find/isolate the root zone to work with in createRecord() and deleteRecord()
rootZone="" rootZone=""
if [ "$checkMultiZones" = "true" ]; then if [ "$checkMultiZones" = "true" ]; then
rootZone=$(for zone in $(echo "$result" | tr -d '\n' ' '); do #rootZone=$(for x in $(printf "%s" "${result3}" | tr ',' '\n' | sed -n 's/.*"name":"\(.*\)",.*/\1/p'); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
if [ "$(echo "$domain" | grep "$zone")" != "" ]; then rootZone=$(for x in $(printf "%s\n" "${result3}" | tr ',' '\n' | grep name | cut -d'"' -f4); do if [ "$(echo "$domain" | grep "$x")" != "" ]; then echo "$x"; fi; done)
_debug2 _get_root "- trying to figure out if $zone is in $domain" if [ "$rootZone" != "" ]; then
echo "$zone" _debug2 _rootZone "- root zone for $domain is $rootZone"
break
fi
done)
else else
rootZone=$(echo "$result" | _egrep_o '"name":"[^"]*' | cut -d'"' -f4) _err "Could not find root zone for $domain, is it correctly typed?"
return 1
fi
else
rootZone=$(echo "$result" | tr '}' '\n' | _egrep_o '"name":"[^"]*' | cut -d'"' -f4)
_debug2 _get_root "- only found 1 domain in API: $rootZone" _debug2 _get_root "- only found 1 domain in API: $rootZone"
fi fi
@ -155,14 +159,18 @@ _get_root() {
return 1 return 1
fi fi
# Make sure we use the correct API zone data
useResult="$(printf "%s\n" "${result3}" tr ',' '\n' | grep "$rootZone")"
_debug2 _useResult "useResult=$useResult"
# Setup variables used by other functions to communicate with DNS.Services API # Setup variables used by other functions to communicate with DNS.Services API
#zoneInfo=$(echo "$result" | sed "s,\"zones,\n&,g" | grep zones | cut -d'[' -f2 | cut -d']' -f1 | tr '}' '\n' | grep "\"$rootZone\"") #zoneInfo=$(printf "%s\n" "$useResult" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g')
zoneInfo=$(echo "$result" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"name":")([^"]*)"(.*)$,\2,g' | grep "\"$rootZone\"") zoneInfo=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep '"name"' | cut -d'"' -f4)
rootZoneName="$rootZone" rootZoneName="$rootZone"
subDomainName="$(echo "$domain" | sed "s,\.$rootZone,,g")" subDomainName="$(printf "%s\n" "$domain" | sed "s,\.$rootZone,,g")"
subDomainNameClean="$(echo "$domain" | sed "s,_acme-challenge.,,g")" subDomainNameClean="$(printf "%s\n" "$domain" | sed "s,_acme-challenge.,,g")"
rootZoneDomainID=$(echo "$result" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"domain_id":")([^"]*)"(.*)$,\2,g') rootZoneDomainID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep domain_id | cut -d'"' -f4)
rootZoneServiceID=$(echo "$result" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"service_id":")([^"]*)"(.*)$,\2,g') rootZoneServiceID=$(printf "%s\n" "$useResult" | tr ',' '\n' | grep service_id | cut -d'"' -f4)
_debug2 _zoneInfo "Zone info from API : $zoneInfo" _debug2 _zoneInfo "Zone info from API : $zoneInfo"
_debug2 _get_root "Root zone name : $rootZoneName" _debug2 _get_root "Root zone name : $rootZoneName"
@ -175,13 +183,17 @@ _get_root() {
} }
createRecord() { createRecord() {
fulldomain=$1 fulldomain="$1"
txtvalue="$2" txtvalue="$2"
# Get root domain information - needed for DNS.Services API communication # Get root domain information - needed for DNS.Services API communication
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
_get_root "$fulldomain" _get_root "$fulldomain"
fi fi
if [ -z "$rootZoneName" ] || [ -z "$rootZoneDomainID" ] || [ -z "$rootZoneServiceID" ]; then
_err "Something happend - could not get the API zone information"
return 1
fi
_debug2 createRecord "CNAME TXT value is: $txtvalue" _debug2 createRecord "CNAME TXT value is: $txtvalue"
@ -203,8 +215,8 @@ createRecord() {
} }
deleteRecord() { deleteRecord() {
fulldomain=$1 fulldomain="$1"
txtvalue=$2 txtvalue="$2"
_log deleteRecord "Deleting $fulldomain TXT $txtvalue record" _log deleteRecord "Deleting $fulldomain TXT $txtvalue record"
@ -213,8 +225,10 @@ deleteRecord() {
fi fi
result="$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID")" result="$(_H1="$_H1" _H2="$_H2" _get "$DNSServices_API/service/$rootZoneServiceID/dns/$rootZoneDomainID")"
recordInfo="$(echo "$result" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}")" #recordInfo="$(echo "$result" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}")"
recordID="$(echo "$recordInfo" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"id":")([^"]*)"(.*)$,\2,g')" #recordID="$(echo "$recordInfo" | sed -e 's/:{/:{\n/g' -e 's/},/\n},\n/g' | grep "${txtvalue}" | sed -E 's,.*(zones)(.*),\1\2,g' | sed -E 's,^(.*"id":")([^"]*)"(.*)$,\2,g')"
recordID="$(printf "%s\n" "$result" | tr '}' '\n' | grep -- "$txtvalue" | tr ',' '\n' | grep '"id"' | cut -d'"' -f4)"
_debug2 _recordID "recordID used for deletion of record: $recordID"
if [ -z "$recordID" ]; then if [ -z "$recordID" ]; then
_info "Record $fulldomain TXT $txtvalue not found or already deleted" _info "Record $fulldomain TXT $txtvalue not found or already deleted"

View File

@ -137,7 +137,7 @@ _get_root() {
domain=$1 domain=$1
i=2 i=2
p=1 p=1
if _opns_rest "GET" "/domain/searchDomain"; then if _opns_rest "GET" "/domain/searchMasterDomain"; then
_domain_response="$response" _domain_response="$response"
else else
return 1 return 1
@ -150,7 +150,7 @@ _get_root() {
return 1 return 1
fi fi
_debug h "$h" _debug h "$h"
id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"master\",[^.]*,\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2) id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"master\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$id" ]; then if [ -n "$id" ]; then
_debug id "$id" _debug id "$id"
_host=$(printf "%s" "$domain" | cut -d . -f 1-$p) _host=$(printf "%s" "$domain" | cut -d . -f 1-$p)

94
dnsapi/dns_selfhost.sh Normal file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env sh
#
# Author: Marvin Edeler
# Report Bugs here: https://github.com/Marvo2011/acme.sh/issues/1
# Last Edit: 17.02.2022
dns_selfhost_add() {
fulldomain=$1
txt=$2
_info "Calling acme-dns on selfhost"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txt"
SELFHOSTDNS_UPDATE_URL="https://selfhost.de/cgi-bin/api.pl"
# Get values, but don't save until we successfully validated
SELFHOSTDNS_USERNAME="${SELFHOSTDNS_USERNAME:-$(_readaccountconf_mutable SELFHOSTDNS_USERNAME)}"
SELFHOSTDNS_PASSWORD="${SELFHOSTDNS_PASSWORD:-$(_readaccountconf_mutable SELFHOSTDNS_PASSWORD)}"
# These values are domain dependent, so read them from there
SELFHOSTDNS_MAP="${SELFHOSTDNS_MAP:-$(_readdomainconf SELFHOSTDNS_MAP)}"
# Selfhost api can't dynamically add TXT record,
# so we have to store the last used RID of the domain to support a second RID for wildcard domains
# (format: 'fulldomainA:lastRid fulldomainB:lastRid ...')
SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(_readdomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL)
if [ -z "${SELFHOSTDNS_USERNAME:-}" ] || [ -z "${SELFHOSTDNS_PASSWORD:-}" ]; then
_err "SELFHOSTDNS_USERNAME and SELFHOSTDNS_PASSWORD must be set"
return 1
fi
# get the domain entry from SELFHOSTDNS_MAP
# only match full domains (at the beginning of the string or with a leading whitespace),
# e.g. don't match mytest.example.com or sub.test.example.com for test.example.com
# if the domain is defined multiple times only the last occurance will be matched
mapEntry=$(echo "$SELFHOSTDNS_MAP" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain)(:[[:digit:]]+)([:]?[[:digit:]]*)(.*)/\2\3\4/p")
_debug2 mapEntry "$mapEntry"
if test -z "$mapEntry"; then
_err "SELFHOSTDNS_MAP must contain the fulldomain incl. prefix and at least one RID"
return 1
fi
# get the RIDs from the map entry
rid1=$(echo "$mapEntry" | cut -d: -f2)
rid2=$(echo "$mapEntry" | cut -d: -f3)
# read last used rid domain
lastUsedRidForDomainEntry=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain:[[:digit:]]+)(.*)/\2/p")
_debug2 lastUsedRidForDomainEntry "$lastUsedRidForDomainEntry"
lastUsedRidForDomain=$(echo "$lastUsedRidForDomainEntry" | cut -d: -f2)
rid="$rid1"
if [ "$lastUsedRidForDomain" = "$rid" ] && ! test -z "$rid2"; then
rid="$rid2"
fi
_info "Trying to add $txt on selfhost for rid: $rid"
data="?username=$SELFHOSTDNS_USERNAME&password=$SELFHOSTDNS_PASSWORD&rid=$rid&content=$txt"
response="$(_get "$SELFHOSTDNS_UPDATE_URL$data")"
if ! echo "$response" | grep "200 OK" >/dev/null; then
_err "Invalid response of acme-dns for selfhost"
return 1
fi
# write last used rid domain
newLastUsedRidForDomainEntry="$fulldomain:$rid"
if ! test -z "$lastUsedRidForDomainEntry"; then
# replace last used rid entry for domain
SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/$lastUsedRidForDomainEntry/$newLastUsedRidForDomainEntry/p")
else
# add last used rid entry for domain
if test -z "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"; then
SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$newLastUsedRidForDomainEntry"
else
SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$SELFHOSTDNS_MAP_LAST_USED_INTERNAL $newLastUsedRidForDomainEntry"
fi
fi
# Now that we know the values are good, save them
_saveaccountconf_mutable SELFHOSTDNS_USERNAME "$SELFHOSTDNS_USERNAME"
_saveaccountconf_mutable SELFHOSTDNS_PASSWORD "$SELFHOSTDNS_PASSWORD"
# These values are domain dependent, so store them there
_savedomainconf SELFHOSTDNS_MAP "$SELFHOSTDNS_MAP"
_savedomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"
}
dns_selfhost_rm() {
fulldomain=$1
txt=$2
_debug fulldomain "$fulldomain"
_debug txtvalue "$txt"
_info "Creating and removing of records is not supported by selfhost API, will not delete anything."
}

View File

@ -195,7 +195,7 @@ _get_paketnr() {
fqdn="$1" fqdn="$1"
form="$2" form="$2"
domains=$(echo "$form" | grep 'header-paket-domain' | sed 's/<[^>]*>//g' | sed 's/^.*>\([^>]*\)$/\1/') domains=$(echo "$form" | grep '<ul class="nav header-paket-list">' | sed 's/<li/\n<li/g' | sed 's/<[^>]*>/ /g' | sed 's/^.*>\([^>]*\)$/\1/')
domain='' domain=''
for domain in $domains; do for domain in $domains; do
if _contains "$fqdn" "$domain\$"; then if _contains "$fqdn" "$domain\$"; then
@ -210,7 +210,7 @@ _get_paketnr() {
TLD="$domain" TLD="$domain"
_debug domain "$domain" _debug domain "$domain"
RECORD=$(echo "$fqdn" | cut -c"1-$((${#fqdn} - ${#TLD} - 1))") RECORD=$(echo "$fqdn" | cut -c"1-$((${#fqdn} - ${#TLD} - 1))")
PAKETNR=$(echo "$form" | grep "data-textfilter=\".* $domain " | _tail_n 1 | sed "s|.*$WORLD4YOU_API/\\([0-9]*\\)/.*|\\1|") PAKETNR=$(echo "$domains" | grep "$domain" | sed 's/^[^,]*, *\([0-9]*\).*$/\1/')
return 0 return 0
} }

264
dnsapi/dns_yc.sh Normal file
View File

@ -0,0 +1,264 @@
#!/usr/bin/env sh
#YC_Zone_ID="" # DNS Zone ID
#YC_Folder_ID="" # YC Folder ID
#YC_SA_ID="" # Service Account ID
#YC_SA_Key_ID="" # Service Account IAM Key ID
#YC_SA_Key_File_Path="/path/to/private.key" # Path to private.key use instead of YC_SA_Key_File_PEM_b64
#YC_SA_Key_File_PEM_b64="" # Base64 content of private.key use instead of YC_SA_Key_File_Path
YC_Api="https://dns.api.cloud.yandex.net/dns/v1"
######## Public functions #####################
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_yc_add() {
fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
txtvalue=$2
YC_SA_Key_File_PEM_b64="${YC_SA_Key_File_PEM_b64:-$(_readaccountconf_mutable YC_SA_Key_File_PEM_b64)}"
YC_SA_Key_File_Path="${YC_SA_Key_File_Path:-$(_readaccountconf_mutable YC_SA_Key_File_Path)}"
if [ "$YC_SA_Key_File_PEM_b64" ]; then
echo "$YC_SA_Key_File_PEM_b64" | _dbase64 >private.key
YC_SA_Key_File="private.key"
_savedomainconf YC_SA_Key_File_PEM_b64 "$YC_SA_Key_File_PEM_b64"
else
YC_SA_Key_File="$YC_SA_Key_File_Path"
_savedomainconf YC_SA_Key_File_Path "$YC_SA_Key_File_Path"
fi
YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
if [ "$YC_SA_ID" ] && [ "$YC_SA_Key_ID" ] && [ "$YC_SA_Key_File" ]; then
if [ -f "$YC_SA_Key_File" ]; then
if _isRSA "$YC_SA_Key_File" >/dev/null 2>&1; then
if [ "$YC_Zone_ID" ]; then
_savedomainconf YC_Zone_ID "$YC_Zone_ID"
_savedomainconf YC_SA_ID "$YC_SA_ID"
_savedomainconf YC_SA_Key_ID "$YC_SA_Key_ID"
elif [ "$YC_Folder_ID" ]; then
_savedomainconf YC_Folder_ID "$YC_Folder_ID"
_saveaccountconf_mutable YC_SA_ID "$YC_SA_ID"
_saveaccountconf_mutable YC_SA_Key_ID "$YC_SA_Key_ID"
_clearaccountconf_mutable YC_Zone_ID
_clearaccountconf YC_Zone_ID
else
_err "You didn't specify a Yandex Cloud Zone ID or Folder ID yet."
return 1
fi
else
_err "YC_SA_Key_File not a RSA file(_isRSA function return false)."
return 1
fi
else
_err "YC_SA_Key_File not found in path $YC_SA_Key_File."
return 1
fi
else
_clearaccountconf YC_Zone_ID
_clearaccountconf YC_Folder_ID
_clearaccountconf YC_SA_ID
_clearaccountconf YC_SA_Key_ID
_clearaccountconf YC_SA_Key_File_PEM_b64
_clearaccountconf YC_SA_Key_File_Path
_err "You didn't specify a YC_SA_ID or YC_SA_Key_ID or YC_SA_Key_File."
return 1
fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
if ! _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
_err "Error: $response"
return 1
fi
_info "Adding record"
if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"merges\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
if _contains "$response" "\"done\": true"; then
_info "Added, OK"
return 0
else
_err "Add txt record error."
return 1
fi
fi
_err "Add txt record error."
return 1
}
#fulldomain txtvalue
dns_yc_rm() {
fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
txtvalue=$2
YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
return 1
fi
_debug _domain_id "$_domain_id"
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
_debug "Getting txt records"
if _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
exists_txtvalue=$(echo "$response" | _normalizeJson | _egrep_o "\"data\".*\][^,]*" | _egrep_o "[^:]*$")
_debug exists_txtvalue "$exists_txtvalue"
else
_err "Error: $response"
return 1
fi
if _yc_rest POST "zones/$_domain_id:updateRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":$exists_txtvalue}]}"; then
if _contains "$response" "\"done\": true"; then
_info "Delete, OK"
return 0
else
_err "Delete record error."
return 1
fi
fi
_err "Delete record error."
return 1
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root() {
domain=$1
i=1
p=1
# Use Zone ID directly if provided
if [ "$YC_Zone_ID" ]; then
if ! _yc_rest GET "zones/$YC_Zone_ID"; then
return 1
else
if echo "$response" | tr -d " " | _egrep_o "\"id\":\"$YC_Zone_ID\"" >/dev/null; then
_domain=$(echo "$response" | _egrep_o "\"zone\": *\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
if [ "$_domain" ]; then
_cutlength=$((${#domain} - ${#_domain}))
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cutlength")
_domain_id=$YC_Zone_ID
return 0
else
return 1
fi
else
return 1
fi
fi
fi
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
_debug h "$h"
if [ -z "$h" ]; then
#not valid
return 1
fi
if [ "$YC_Folder_ID" ]; then
if ! _yc_rest GET "zones?folderId=$YC_Folder_ID"; then
return 1
fi
else
echo "You didn't specify a Yandex Cloud Folder ID."
return 1
fi
if _contains "$response" "\"zone\": \"$h\""; then
_domain_id=$(echo "$response" | _normalizeJson | _egrep_o "[^{]*\"zone\":\"$h\"[^}]*" | _egrep_o "\"id\"[^,]*" | _egrep_o "[^:]*$" | tr -d '"')
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
_domain=$h
return 0
fi
return 1
fi
p=$i
i=$(_math "$i" + 1)
done
return 1
}
_yc_rest() {
m=$1
ep="$2"
data="$3"
_debug "$ep"
if [ ! "$YC_Token" ]; then
_debug "Login"
_yc_login
else
_debug "Token already exists. Skip Login."
fi
token_trimmed=$(echo "$YC_Token" | tr -d '"')
export _H1="Content-Type: application/json"
export _H2="Authorization: Bearer $token_trimmed"
if [ "$m" != "GET" ]; then
_debug data "$data"
response="$(_post "$data" "$YC_Api/$ep" "" "$m")"
else
response="$(_get "$YC_Api/$ep")"
fi
if [ "$?" != "0" ]; then
_err "error $ep"
return 1
fi
_debug2 response "$response"
return 0
}
_yc_login() {
header=$(echo "{\"typ\":\"JWT\",\"alg\":\"PS256\",\"kid\":\"$YC_SA_Key_ID\"}" | _normalizeJson | _base64 | _url_replace)
_debug header "$header"
_current_timestamp=$(_time)
_expire_timestamp=$(_math "$_current_timestamp" + 1200) # 20 minutes
payload=$(echo "{\"iss\":\"$YC_SA_ID\",\"aud\":\"https://iam.api.cloud.yandex.net/iam/v1/tokens\",\"iat\":$_current_timestamp,\"exp\":$_expire_timestamp}" | _normalizeJson | _base64 | _url_replace)
_debug payload "$payload"
#signature=$(printf "%s.%s" "$header" "$payload" | ${ACME_OPENSSL_BIN:-openssl} dgst -sign "$YC_SA_Key_File -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _base64 | _url_replace )
_signature=$(printf "%s.%s" "$header" "$payload" | _sign "$YC_SA_Key_File" "sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _url_replace)
_debug2 _signature "$_signature"
rm -rf "$YC_SA_Key_File"
_jwt=$(printf "{\"jwt\": \"%s.%s.%s\"}" "$header" "$payload" "$_signature")
_debug2 _jwt "$_jwt"
export _H1="Content-Type: application/json"
_iam_response="$(_post "$_jwt" "https://iam.api.cloud.yandex.net/iam/v1/tokens" "" "POST")"
_debug3 _iam_response "$(echo "$_iam_response" | _normalizeJson)"
YC_Token="$(echo "$_iam_response" | _normalizeJson | _egrep_o "\"iamToken\"[^,]*" | _egrep_o "[^:]*$" | tr -d '"')"
_debug3 YC_Token
return 0
}