From 877cbe04c915bdb00c48612e0f6f3e51082c89d4 Mon Sep 17 00:00:00 2001 From: Amin Sharifi Date: Sun, 4 Jan 2026 16:18:18 +0330 Subject: [PATCH 1/3] Add VirakCloud DNS API support with add and remove TXT record functions --- dnsapi/dns_virakcloud.sh | 229 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100755 dnsapi/dns_virakcloud.sh diff --git a/dnsapi/dns_virakcloud.sh b/dnsapi/dns_virakcloud.sh new file mode 100755 index 00000000..ade4796b --- /dev/null +++ b/dnsapi/dns_virakcloud.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env sh +# shellcheck disable=SC2034 +dns_virakcloud_info='VirakCloud DNS API +Site: VirakCloud.com +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_virakcloud +Options: + VIRAKCLOUD_API_TOKEN VirakCloud API Bearer Token +' + +VIRAKCLOUD_API_URL="https://public-api.virakcloud.com/dns" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +#Used to add txt record +dns_virakcloud_add() { + fulldomain=$1 + txtvalue=$2 + + VIRAKCLOUD_API_TOKEN="${VIRAKCLOUD_API_TOKEN:-$(_readaccountconf_mutable VIRAKCLOUD_API_TOKEN)}" + + if [ -z "$VIRAKCLOUD_API_TOKEN" ]; then + _err "You haven't configured your VirakCloud API token yet." + _err "Please set VIRAKCLOUD_API_TOKEN environment variable or run:" + _err " export VIRAKCLOUD_API_TOKEN=\"your-api-token\"" + return 1 + fi + + _saveaccountconf_mutable VIRAKCLOUD_API_TOKEN "$VIRAKCLOUD_API_TOKEN" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + if [ "$http_code" = "401" ]; then + return 1 + fi + _err "Invalid domain" + return 1 + fi + + _debug _domain "$_domain" + _debug fulldomain "$fulldomain" + + _info "Adding TXT record" + + if _virakcloud_rest POST "domains/${_domain}/records" "{\"record\":\"${fulldomain}\",\"type\":\"TXT\",\"ttl\":3600,\"content\":\"${txtvalue}\"}"; then + if echo "$response" | grep -q "success" || echo "$response" | grep -q "\"data\""; then + _info "Added, OK" + return 0 + elif echo "$response" | grep -q "already exists" || echo "$response" | grep -q "duplicate"; then + _info "Record already exists, OK" + return 0 + else + _err "Add TXT record error." + _err "Response: $response" + return 1 + fi + fi + + _err "Add TXT record error." + return 1 +} + +#Usage: fulldomain txtvalue +#Used to remove the txt record after validation +dns_virakcloud_rm() { + fulldomain=$1 + txtvalue=$2 + + VIRAKCLOUD_API_TOKEN="${VIRAKCLOUD_API_TOKEN:-$(_readaccountconf_mutable VIRAKCLOUD_API_TOKEN)}" + + if [ -z "$VIRAKCLOUD_API_TOKEN" ]; then + _err "You haven't configured your VirakCloud API token yet." + return 1 + fi + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + if [ "$http_code" = "401" ]; then + return 1 + fi + _err "Invalid domain" + return 1 + fi + + _debug _domain "$_domain" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + _info "Removing TXT record" + + _debug "Getting list of records to find content ID" + if ! _virakcloud_rest GET "domains/${_domain}/records" ""; then + return 1 + fi + + _debug2 "Records response" "$response" + + contentid="" + # Extract innermost objects (content objects) which look like {"id":"...","content_raw":"..."} + # We filter for the one containing txtvalue + + target_obj=$(echo "$response" | grep -o '{[^}]*}' | grep "$txtvalue" | _head_n 1) + + if [ -n "$target_obj" ]; then + contentid=$(echo "$target_obj" | _egrep_o '"id":"[^"]*"' | cut -d '"' -f 4) + fi + + if [ -z "$contentid" ]; then + _debug "Could not find matching record ID in response" + _info "Record not found, may have been already removed" + return 0 + fi + + _debug contentid "$contentid" + + if _virakcloud_rest DELETE "domains/${_domain}/records/${fulldomain}/TXT/${contentid}" ""; then + if echo "$response" | grep -q "success" || [ -z "$response" ]; then + _info "Removed, OK" + return 0 + elif echo "$response" | grep -q "not found" || echo "$response" | grep -q "404"; then + _info "Record not found, OK" + return 0 + else + _err "Remove TXT record error." + _err "Response: $response" + return 1 + fi + fi + + _err "Remove TXT record error." + return 1 +} + +#################### Private functions below ################################## + +#_acme-challenge.www.domain.com +#returns +# _domain=domain.com +_get_root() { + domain=$1 + i=1 + p=1 + + # Optimization: skip _acme-challenge subdomain to avoid 422 errors + if echo "$domain" | grep -q "^_acme-challenge."; then + i=2 + fi + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) + _debug h "$h" + + if [ -z "$h" ]; then + return 1 + fi + + if ! _virakcloud_rest GET "domains/$h" ""; then + http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + if [ "$http_code" = "401" ]; then + return 1 + fi + p=$i + i=$(_math "$i" + 1) + continue + fi + + if echo "$response" | grep -q "\"name\""; then + _domain="$h" + return 0 + fi + + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + +_virakcloud_rest() { + m=$1 + ep="$2" + data="$3" + + _debug "$ep" + + export _H1="Content-Type: application/json" + export _H2="Authorization: Bearer $VIRAKCLOUD_API_TOKEN" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$VIRAKCLOUD_API_URL/$ep" "" "$m")" + else + response="$(_get "$VIRAKCLOUD_API_URL/$ep")" + fi + + _ret="$?" + + if [ "$_ret" != "0" ]; then + _err "error on $m $ep" + return 1 + fi + + http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" + _debug "http response code" "$http_code" + + if [ "$http_code" = "401" ]; then + _err "VirakCloud API returned 401 Unauthorized." + _err "Your VIRAKCLOUD_API_TOKEN is invalid or expired." + _err "Please check your API token and try again." + return 1 + fi + + if [ "$http_code" = "403" ]; then + _err "VirakCloud API returned 403 Forbidden." + _err "Your API token does not have permission to access this resource." + return 1 + fi + + if [ -n "$http_code" ] && [ "$http_code" -ge 400 ]; then + _err "VirakCloud API error. HTTP code: $http_code" + _err "Response: $response" + return 1 + fi + + _debug2 response "$response" + return 0 +} From ef035248c35976ff849eda4011e67f08bac41831 Mon Sep 17 00:00:00 2001 From: Amin Sharifi Date: Sun, 4 Jan 2026 16:29:30 +0330 Subject: [PATCH 2/3] Add VirakCloud DNS API support --- dnsapi/dns_virakcloud.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_virakcloud.sh b/dnsapi/dns_virakcloud.sh index ade4796b..84da5773 100755 --- a/dnsapi/dns_virakcloud.sh +++ b/dnsapi/dns_virakcloud.sh @@ -43,6 +43,7 @@ dns_virakcloud_add() { _info "Adding TXT record" + if _virakcloud_rest POST "domains/${_domain}/records" "{\"record\":\"${fulldomain}\",\"type\":\"TXT\",\"ttl\":3600,\"content\":\"${txtvalue}\"}"; then if echo "$response" | grep -q "success" || echo "$response" | grep -q "\"data\""; then _info "Added, OK" From 70462b5ac3f257d8814002427b0693f67b437b3c Mon Sep 17 00:00:00 2001 From: Amin Sharifi Date: Sun, 4 Jan 2026 16:33:37 +0330 Subject: [PATCH 3/3] run `~/shfmt -l -w -i 2 dnsapi/dns_virakcloud.sh` and Remove unnecessary blank lines in dns_virakcloud.sh --- dnsapi/dns_virakcloud.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_virakcloud.sh b/dnsapi/dns_virakcloud.sh index 84da5773..7ae665d2 100755 --- a/dnsapi/dns_virakcloud.sh +++ b/dnsapi/dns_virakcloud.sh @@ -43,7 +43,6 @@ dns_virakcloud_add() { _info "Adding TXT record" - if _virakcloud_rest POST "domains/${_domain}/records" "{\"record\":\"${fulldomain}\",\"type\":\"TXT\",\"ttl\":3600,\"content\":\"${txtvalue}\"}"; then if echo "$response" | grep -q "success" || echo "$response" | grep -q "\"data\""; then _info "Added, OK" @@ -101,11 +100,11 @@ dns_virakcloud_rm() { contentid="" # Extract innermost objects (content objects) which look like {"id":"...","content_raw":"..."} # We filter for the one containing txtvalue - + target_obj=$(echo "$response" | grep -o '{[^}]*}' | grep "$txtvalue" | _head_n 1) - + if [ -n "$target_obj" ]; then - contentid=$(echo "$target_obj" | _egrep_o '"id":"[^"]*"' | cut -d '"' -f 4) + contentid=$(echo "$target_obj" | _egrep_o '"id":"[^"]*"' | cut -d '"' -f 4) fi if [ -z "$contentid" ]; then @@ -197,7 +196,7 @@ _virakcloud_rest() { fi _ret="$?" - + if [ "$_ret" != "0" ]; then _err "error on $m $ep" return 1