mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2026-08-13 12:33:30 +02:00
Merge pull request #7078 from acmesh-official/dev
Some checks failed
DNS / CheckToken (push) Has been cancelled
DragonFlyBSD / DragonFlyBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
FreeBSD / FreeBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
FreeBSD / FreeBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
GhostBSD / GhostBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
GhostBSD / GhostBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Haiku / Haiku (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Haiku / Haiku (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Linux / Linux (almalinux:latest) (push) Has been cancelled
Linux / Linux (alpine:latest) (push) Has been cancelled
Linux / Linux (archlinux:latest) (push) Has been cancelled
Linux / Linux (debian:latest) (push) Has been cancelled
Linux / Linux (fedora:latest) (push) Has been cancelled
Linux / Linux (gentoo/stage3) (push) Has been cancelled
Linux / Linux (kalilinux/kali) (push) Has been cancelled
Linux / Linux (opensuse/leap:latest) (push) Has been cancelled
Linux / Linux (oraclelinux:8) (push) Has been cancelled
Linux / Linux (ubuntu:latest) (push) Has been cancelled
MacOS / MacOS (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
MidnightBSD / MidnightBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
NetBSD / NetBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Omnios / Omnios (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Omnios / Omnios (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenBSD / OpenBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenBSD / OpenBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenIndiana / OpenIndiana (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenIndiana / OpenIndiana (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
PebbleStrict / PebbleStrict (push) Has been cancelled
PebbleStrict / PebbleStrict_IPCert (push) Has been cancelled
Solaris / Solaris (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Solaris / Solaris (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Tribblix / Tribblix (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Tribblix / Tribblix (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (Smallstep Intermediate CA, Smallstep Intermediate CA, , 1, https://localhost:9000/acme/acme/directory, ) (push) Has been cancelled
Ubuntu / Ubuntu (Smallstep Intermediate CA, Smallstep Intermediate CA, , 1, https://localhost:9000/acme/acme/directory, 1, , 172.17.0.1) (push) Has been cancelled
Ubuntu / Ubuntu (ZeroSSL RSA DV SSL CA 2, ZeroSSL ECC DV SSL CA 2, githubtest@acme.sh, ZeroSSL.com, ) (push) Has been cancelled
Windows / Windows (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Build DockerHub / CheckToken (push) Has been cancelled
Shellcheck / ShellCheck (push) Has been cancelled
Shellcheck / shfmt (push) Has been cancelled
DNS / Fail (push) Has been cancelled
DNS / Docker (push) Has been cancelled
DNS / MacOS (push) Has been cancelled
DNS / Windows (push) Has been cancelled
DNS / FreeBSD (push) Has been cancelled
DNS / GhostBSD (push) Has been cancelled
DNS / OpenBSD (push) Has been cancelled
DNS / NetBSD (push) Has been cancelled
DNS / DragonFlyBSD (push) Has been cancelled
DNS / MidnightBSD (push) Has been cancelled
DNS / Solaris (push) Has been cancelled
DNS / Omnios (push) Has been cancelled
DNS / OpenIndiana (push) Has been cancelled
DNS / Tribblix (push) Has been cancelled
DNS / Haiku (push) Has been cancelled
Build DockerHub / build (push) Has been cancelled
Some checks failed
DNS / CheckToken (push) Has been cancelled
DragonFlyBSD / DragonFlyBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
FreeBSD / FreeBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
FreeBSD / FreeBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
GhostBSD / GhostBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
GhostBSD / GhostBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Haiku / Haiku (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Haiku / Haiku (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Linux / Linux (almalinux:latest) (push) Has been cancelled
Linux / Linux (alpine:latest) (push) Has been cancelled
Linux / Linux (archlinux:latest) (push) Has been cancelled
Linux / Linux (debian:latest) (push) Has been cancelled
Linux / Linux (fedora:latest) (push) Has been cancelled
Linux / Linux (gentoo/stage3) (push) Has been cancelled
Linux / Linux (kalilinux/kali) (push) Has been cancelled
Linux / Linux (opensuse/leap:latest) (push) Has been cancelled
Linux / Linux (oraclelinux:8) (push) Has been cancelled
Linux / Linux (ubuntu:latest) (push) Has been cancelled
MacOS / MacOS (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
MidnightBSD / MidnightBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
NetBSD / NetBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Omnios / Omnios (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Omnios / Omnios (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenBSD / OpenBSD (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenBSD / OpenBSD (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenIndiana / OpenIndiana (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
OpenIndiana / OpenIndiana (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
PebbleStrict / PebbleStrict (push) Has been cancelled
PebbleStrict / PebbleStrict_IPCert (push) Has been cancelled
Solaris / Solaris (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Solaris / Solaris (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Tribblix / Tribblix (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Tribblix / Tribblix (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (1, , , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Ubuntu / Ubuntu (Smallstep Intermediate CA, Smallstep Intermediate CA, , 1, https://localhost:9000/acme/acme/directory, ) (push) Has been cancelled
Ubuntu / Ubuntu (Smallstep Intermediate CA, Smallstep Intermediate CA, , 1, https://localhost:9000/acme/acme/directory, 1, , 172.17.0.1) (push) Has been cancelled
Ubuntu / Ubuntu (ZeroSSL RSA DV SSL CA 2, ZeroSSL ECC DV SSL CA 2, githubtest@acme.sh, ZeroSSL.com, ) (push) Has been cancelled
Windows / Windows (, , , LetsEncrypt.org_test, (STAGING)) (push) Has been cancelled
Build DockerHub / CheckToken (push) Has been cancelled
Shellcheck / ShellCheck (push) Has been cancelled
Shellcheck / shfmt (push) Has been cancelled
DNS / Fail (push) Has been cancelled
DNS / Docker (push) Has been cancelled
DNS / MacOS (push) Has been cancelled
DNS / Windows (push) Has been cancelled
DNS / FreeBSD (push) Has been cancelled
DNS / GhostBSD (push) Has been cancelled
DNS / OpenBSD (push) Has been cancelled
DNS / NetBSD (push) Has been cancelled
DNS / DragonFlyBSD (push) Has been cancelled
DNS / MidnightBSD (push) Has been cancelled
DNS / Solaris (push) Has been cancelled
DNS / Omnios (push) Has been cancelled
DNS / OpenIndiana (push) Has been cancelled
DNS / Tribblix (push) Has been cancelled
DNS / Haiku (push) Has been cancelled
Build DockerHub / build (push) Has been cancelled
sync
This commit is contained in:
commit
992886c4eb
30 changed files with 2238 additions and 234 deletions
60
.github/workflows/DNS.yml
vendored
60
.github/workflows/DNS.yml
vendored
|
|
@ -260,9 +260,67 @@ jobs:
|
|||
|
||||
|
||||
|
||||
OpenBSD:
|
||||
GhostBSD:
|
||||
runs-on: ubuntu-latest
|
||||
needs: FreeBSD
|
||||
# GhostBSD VM frequently flakes on boot/ssh; don't let it fail the whole run
|
||||
continue-on-error: true
|
||||
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 }}
|
||||
TokenName1: ${{ secrets.TokenName1}}
|
||||
TokenName2: ${{ secrets.TokenName2}}
|
||||
TokenName3: ${{ secrets.TokenName3}}
|
||||
TokenName4: ${{ secrets.TokenName4}}
|
||||
TokenName5: ${{ secrets.TokenName5}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Clone acmetest
|
||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||
- uses: vmactions/ghostbsd-vm@v1
|
||||
with:
|
||||
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
|
||||
envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||
prepare: pkg install -y socat curl
|
||||
usesh: true
|
||||
sync: nfs
|
||||
run: |
|
||||
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
|
||||
- name: DebugOnError
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
echo "See how to debug in VM:"
|
||||
echo "https://github.com/acmesh-official/acme.sh/wiki/debug-in-VM"
|
||||
|
||||
|
||||
|
||||
OpenBSD:
|
||||
runs-on: ubuntu-latest
|
||||
needs: GhostBSD
|
||||
env:
|
||||
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||
TestingDomain: ${{ secrets.TestingDomain }}
|
||||
|
|
|
|||
82
.github/workflows/GhostBSD.yml
vendored
Normal file
82
.github/workflows/GhostBSD.yml
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
name: GhostBSD
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- '*.sh'
|
||||
- '.github/workflows/GhostBSD.yml'
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
paths:
|
||||
- '*.sh'
|
||||
- '.github/workflows/GhostBSD.yml'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
|
||||
jobs:
|
||||
GhostBSD:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||
CA_ECDSA: ""
|
||||
CA: ""
|
||||
CA_EMAIL: ""
|
||||
TEST_PREFERRED_CHAIN: (STAGING)
|
||||
- TEST_ACME_Server: "LetsEncrypt.org_test"
|
||||
CA_ECDSA: ""
|
||||
CA: ""
|
||||
CA_EMAIL: ""
|
||||
TEST_PREFERRED_CHAIN: (STAGING)
|
||||
ACME_USE_WGET: 1
|
||||
#- TEST_ACME_Server: "ZeroSSL.com"
|
||||
# CA_ECDSA: "ZeroSSL ECC DV SSL CA 2"
|
||||
# CA: "ZeroSSL RSA DV SSL CA 2"
|
||||
# CA_EMAIL: "githubtest@acme.sh"
|
||||
# TEST_PREFERRED_CHAIN: ""
|
||||
runs-on: ubuntu-latest
|
||||
# GhostBSD VM frequently flakes on boot/ssh; don't let it fail the whole run
|
||||
continue-on-error: true
|
||||
env:
|
||||
TEST_LOCAL: 1
|
||||
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
|
||||
CA_ECDSA: ${{ matrix.CA_ECDSA }}
|
||||
CA: ${{ matrix.CA }}
|
||||
CA_EMAIL: ${{ matrix.CA_EMAIL }}
|
||||
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
|
||||
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: anyvm-org/cf-tunnel@v0
|
||||
id: tunnel
|
||||
with:
|
||||
protocol: http
|
||||
port: 8080
|
||||
- name: Set envs
|
||||
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
|
||||
- name: Clone acmetest
|
||||
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
|
||||
- uses: vmactions/ghostbsd-vm@v1
|
||||
with:
|
||||
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
|
||||
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
|
||||
nat: |
|
||||
"8080": "80"
|
||||
prepare: pkg install -y socat curl wget
|
||||
usesh: true
|
||||
sync: nfs
|
||||
run: |
|
||||
cd ../acmetest \
|
||||
&& ./letest.sh
|
||||
- name: DebugOnError
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
echo "See how to debug in VM:"
|
||||
echo "https://github.com/acmesh-official/acme.sh/wiki/debug-in-VM"
|
||||
4
.github/workflows/dockerhub.yml
vendored
4
.github/workflows/dockerhub.yml
vendored
|
|
@ -50,14 +50,14 @@ jobs:
|
|||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v4
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: ${DOCKER_IMAGE}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v4
|
||||
- name: login to docker hub
|
||||
run: |
|
||||
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||
|
|
|
|||
2
.github/workflows/issue.yml
vendored
2
.github/workflows/issue.yml
vendored
|
|
@ -7,7 +7,7 @@ jobs:
|
|||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
- uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
|
|
|
|||
2
.github/workflows/pr_dns.yml
vendored
2
.github/workflows/pr_dns.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.actor != 'neilpang'
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
- uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
|
|
|
|||
2
.github/workflows/pr_notify.yml
vendored
2
.github/workflows/pr_notify.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.actor != 'neilpang'
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
- uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
|
|
|
|||
2
.github/workflows/wiki-monitor.yml
vendored
2
.github/workflows/wiki-monitor.yml
vendored
|
|
@ -51,7 +51,7 @@ jobs:
|
|||
} > wiki-change-msg.txt
|
||||
|
||||
- name: Create issue to notify Neilpang
|
||||
uses: peter-evans/create-issue-from-file@v5
|
||||
uses: peter-evans/create-issue-from-file@v6
|
||||
with:
|
||||
title: "Wiki edited"
|
||||
content-filepath: ./wiki-change-msg.txt
|
||||
|
|
|
|||
8
CONTRIBUTING.md
Normal file
8
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Contributing
|
||||
|
||||
1. Do NOT send pull request to `master` branch.
|
||||
Please send to `dev` branch instead.
|
||||
Any PR to `master` branch will NOT be merged.
|
||||
|
||||
2. For dns api support, read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
|
||||
You will NOT get any review without passing this guide. You also need to fix the CI errors.
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg" alt="Solaris"></a>
|
||||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg" alt="DragonFlyBSD"></a>
|
||||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/MidnightBSD.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/MidnightBSD.yml/badge.svg" alt="MidnightBSD"></a>
|
||||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/GhostBSD.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/GhostBSD.yml/badge.svg" alt="GhostBSD"></a>
|
||||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg" alt="Omnios"></a>
|
||||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/OpenIndiana.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/OpenIndiana.yml/badge.svg" alt="OpenIndiana"></a>
|
||||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Tribblix.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Tribblix.yml/badge.svg" alt="Tribblix"></a>
|
||||
|
|
@ -128,6 +129,7 @@
|
|||
|24|[](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
|
||||
|25|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Haiku.yml)|Haiku OS
|
||||
|26|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Tribblix.yml)|Tribblix
|
||||
|27|[](https://github.com/acmesh-official/acme.sh/actions/workflows/GhostBSD.yml)|GhostBSD
|
||||
|
||||
|
||||
> 🧪 Check our [testing project](https://github.com/acmesh-official/acmetest)
|
||||
|
|
@ -627,6 +629,8 @@ This project exists thanks to all the people who contribute.
|
|||
|
||||
<a href="https://github.com/acmesh-official/acme.sh/graphs/contributors"><img src="https://opencollective.com/acmesh/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
If you want to become a contributor make sure to read [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
### 💰 Financial Contributors
|
||||
|
||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)]
|
||||
|
|
|
|||
59
acme.sh
59
acme.sh
|
|
@ -4707,6 +4707,13 @@ issue() {
|
|||
else
|
||||
_cleardomainconf "Le_ChallengeAlias"
|
||||
fi
|
||||
# Save Le_DNSSleep unconditionally here: the save inside the dns_entries
|
||||
# branch is skipped when all authorizations are already valid (e.g. issuing
|
||||
# the ECC twin of a just-issued RSA cert), which left the setting out of
|
||||
# that cert's conf. https://github.com/acmesh-official/acme.sh/issues/6986
|
||||
if [ "$Le_DNSSleep" ]; then
|
||||
_savedomainconf "Le_DNSSleep" "$Le_DNSSleep"
|
||||
fi
|
||||
if [ "$_preferred_chain" ]; then
|
||||
_savedomainconf "Le_Preferred_Chain" "$_preferred_chain" "base64"
|
||||
else
|
||||
|
|
@ -5312,6 +5319,8 @@ $_authorizations_map"
|
|||
fi
|
||||
fi
|
||||
elif [ "$vtype" = "$VTYPE_ALPN" ]; then
|
||||
_ncaddr="$(_getfield "$_local_addr" "$_ncIndex")"
|
||||
_ncIndex="$(_math $_ncIndex + 1)"
|
||||
acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
|
||||
_debug acmevalidationv1 "$acmevalidationv1"
|
||||
if ! _starttlsserver "$d" "" "$Le_TLSPort" "$keyauthorization" "$_ncaddr" "$acmevalidationv1"; then
|
||||
|
|
@ -5542,6 +5551,13 @@ $_authorizations_map"
|
|||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$response" "$BEGIN_CERT"; then
|
||||
response="$(echo "$response" | _dbase64 "multiline" | tr -d '\0' | _normalizeJson)"
|
||||
_err "Signing failed: $(echo "$response" | _egrep_o '"detail":"[^"]*"')"
|
||||
_on_issue_err "$_post_hook"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$response" >"$CERT_PATH"
|
||||
_split_cert_chain "$CERT_PATH" "$CERT_FULLCHAIN_PATH" "$CA_CERT_PATH"
|
||||
if [ -z "$_preferred_chain" ]; then
|
||||
|
|
@ -5561,6 +5577,11 @@ $_authorizations_map"
|
|||
_err "$response"
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! _contains "$response" "$BEGIN_CERT"; then
|
||||
_debug2 "Skipping alternate cert link due to unexpected response format."
|
||||
continue
|
||||
fi
|
||||
_relcert="$CERT_PATH.alt"
|
||||
_relfullchain="$CERT_FULLCHAIN_PATH.alt"
|
||||
_relca="$CA_CERT_PATH.alt"
|
||||
|
|
@ -5817,6 +5838,14 @@ renew() {
|
|||
fi
|
||||
_info "Renewing using Le_API=$Le_API"
|
||||
|
||||
# Honor --local-address given on the renew/renewAll command line: it overrides
|
||||
# the value saved at issue time (and gets re-saved by issue() below), so certs
|
||||
# issued before the machine gained multiple addresses can still be renewed.
|
||||
# https://github.com/acmesh-official/acme.sh/issues/7009
|
||||
if [ "$_local_address" ]; then
|
||||
Le_LocalAddress="$_local_address"
|
||||
fi
|
||||
|
||||
_clearAPI
|
||||
_clearCA
|
||||
export ACME_DIRECTORY="$Le_API"
|
||||
|
|
@ -6449,6 +6478,7 @@ _install_win_taskscheduler() {
|
|||
_lesh="$1"
|
||||
_centry="$2"
|
||||
_randomminute="$3"
|
||||
_randomhour="$4"
|
||||
if ! _exists cygpath; then
|
||||
_err "cygpath not found"
|
||||
return 1
|
||||
|
|
@ -6518,7 +6548,7 @@ installcronjob() {
|
|||
fi
|
||||
_t=$(_time)
|
||||
random_minute=$(_math $_t % 60)
|
||||
random_hour=$(_math $_t / 60 % 24)
|
||||
random_hour=$(_math $_t / 60 % 6)
|
||||
|
||||
if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
|
||||
_CRONTAB="fcrontab"
|
||||
|
|
@ -6527,7 +6557,7 @@ installcronjob() {
|
|||
if ! _exists "$_CRONTAB"; then
|
||||
if _exists cygpath && _exists schtasks.exe; then
|
||||
_info "It seems you are on Windows, let's install the Windows scheduler task."
|
||||
if _install_win_taskscheduler "$lesh" "$_c_entry" "$random_minute"; then
|
||||
if _install_win_taskscheduler "$lesh" "$_c_entry" "$random_minute" "$random_hour"; then
|
||||
_info "Successfully installed Windows scheduler task."
|
||||
return 0
|
||||
else
|
||||
|
|
@ -6549,7 +6579,7 @@ installcronjob() {
|
|||
fi
|
||||
$_CRONTAB -l 2>/dev/null | {
|
||||
cat
|
||||
echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
|
||||
echo "$random_minute $random_hour,$(_math $random_hour + 6),$(_math $random_hour + 12),$(_math $random_hour + 18) * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
|
||||
} | $_CRONTAB_STDIN
|
||||
fi
|
||||
if [ "$?" != "0" ]; then
|
||||
|
|
@ -7177,6 +7207,12 @@ install() {
|
|||
|
||||
if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ]; then
|
||||
_saveaccountconf "CERT_HOME" "$CERT_HOME"
|
||||
# Create the custom cert home now instead of on first issuance, so the
|
||||
# user can see --install honored it.
|
||||
# https://github.com/acmesh-official/acme.sh/issues/4756
|
||||
if [ ! -d "$CERT_HOME" ]; then
|
||||
mkdir -p "$CERT_HOME"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ]; then
|
||||
|
|
@ -7498,8 +7534,8 @@ Parameters:
|
|||
|
||||
--dnssleep <seconds> The time in seconds to wait for all the txt records to propagate in dns api mode.
|
||||
It's not necessary to use this by default, $PROJECT_NAME polls dns status by DOH automatically.
|
||||
-k, --keylength <bits> Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
|
||||
-ak, --accountkeylength <bits> Specifies the account key length: 2048, 3072, 4096
|
||||
-k, --keylength <bits> Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256 (default), ec-384, ec-521.
|
||||
-ak, --accountkeylength <bits> Specifies the account key length: 2048, 3072, 4096, 8192 or ec-256 (default), ec-384, ec-521.
|
||||
--log [file] Specifies the log file. Defaults to \"$DEFAULT_LOG_FILE\" if argument is omitted.
|
||||
--log-level <1|2> Specifies the log level, default is $DEFAULT_LOG_LEVEL.
|
||||
--syslog <0|3|6|7> Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
|
||||
|
|
@ -7717,9 +7753,16 @@ _checkSudo() {
|
|||
return 0
|
||||
fi
|
||||
if [ -n "$SUDO_COMMAND" ]; then
|
||||
#it's a normal user doing "sudo su", or `sudo -i` or `sudo -s`, or `sudo su acmeuser1`
|
||||
_endswith "$SUDO_COMMAND" /bin/su || _contains "$SUDO_COMMAND" "/bin/su " || grep "^$SUDO_COMMAND\$" /etc/shells >/dev/null 2>&1
|
||||
return $?
|
||||
#The SUDO_* env vars are often inherited into shells that were not
|
||||
#started as `sudo acme.sh` at all (e.g. `sudo su - user`, or
|
||||
#`sudo pct enter <VID>` on Proxmox, which copies them into the
|
||||
#container). Only warn when sudo was used to run acme.sh itself;
|
||||
#anything else means the sudo happened further up and is fine.
|
||||
#https://github.com/acmesh-official/acme.sh/issues/6400
|
||||
if _contains "$SUDO_COMMAND" "$PROJECT_ENTRY"; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
#otherwise
|
||||
return 1
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ routeros_deploy() {
|
|||
_savedeployconf ROUTER_OS_PORT "$ROUTER_OS_PORT"
|
||||
_savedeployconf ROUTER_OS_SSH_CMD "$ROUTER_OS_SSH_CMD"
|
||||
_savedeployconf ROUTER_OS_SCP_CMD "$ROUTER_OS_SCP_CMD"
|
||||
_savedeployconf ROUTER_OS_ADDITIONAL_SERVICES "$ROUTER_OS_ADDITIONAL_SERVICES"
|
||||
_savedeployconf ROUTER_OS_ADDITIONAL_SERVICES "$ROUTER_OS_ADDITIONAL_SERVICES" "base64"
|
||||
|
||||
# push key to routeros
|
||||
if ! _scp_certificate "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key"; then
|
||||
|
|
@ -143,6 +143,7 @@ comment=\"generated by routeros deploy script in acme.sh\" \
|
|||
source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
|
||||
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
|
||||
\n/certificate remove [ find name=$_cdomain.cer_2 ];\
|
||||
\n/certificate remove [ find name=$_cdomain.cer_3 ];\
|
||||
\ndelay 1;\
|
||||
\n/certificate import file-name=\\\"$_cdomain.cer\\\" passphrase=\\\"\\\";\
|
||||
\n/certificate import file-name=\\\"$_cdomain.key\\\" passphrase=\\\"\\\";\
|
||||
|
|
|
|||
|
|
@ -16,7 +16,12 @@
|
|||
#
|
||||
# # API KEY
|
||||
# # Use the folowing URL to create a new API token: <TRUENAS_HOSTNAME OR IP>/ui/apikeys
|
||||
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI"
|
||||
# export DEPLOY_TRUENAS_APIKEY="<API_KEY_GENERATED_IN_THE_WEB_UI>"
|
||||
# Optional:
|
||||
# export DEPLOY_TRUENAS_HOSTNAME="<TRUENAS_HOSTNAME_OR_IP>"
|
||||
# export DEPLOY_TRUENAS_PROTOCOL="wss" # ws or wss
|
||||
# export DEPLOY_TRUENAS_PORT="443" # optional, e.g. 80, 443, 8443
|
||||
|
||||
#
|
||||
|
||||
### Private functions
|
||||
|
|
@ -56,7 +61,6 @@ _ws_call() {
|
|||
_ws_upload_cert() {
|
||||
|
||||
/usr/bin/env python - <<EOF
|
||||
|
||||
import sys
|
||||
|
||||
from truenas_api_client import Client
|
||||
|
|
@ -78,7 +82,6 @@ with Client(uri="$_ws_uri") as c:
|
|||
print("R:0")
|
||||
print("E:_ws_upload_cert error!")
|
||||
sys.exit(7)
|
||||
|
||||
EOF
|
||||
|
||||
return $?
|
||||
|
|
@ -181,6 +184,8 @@ truenas_ws_deploy() {
|
|||
_getdeployconf DEPLOY_TRUENAS_APIKEY
|
||||
_getdeployconf DEPLOY_TRUENAS_HOSTNAME
|
||||
_getdeployconf DEPLOY_TRUENAS_PROTOCOL
|
||||
_getdeployconf DEPLOY_TRUENAS_PORT
|
||||
|
||||
# Check API Key
|
||||
if [ -z "$DEPLOY_TRUENAS_APIKEY" ]; then
|
||||
_err "TrueNAS API key not found, please set the DEPLOY_TRUENAS_APIKEY environment variable."
|
||||
|
|
@ -196,7 +201,21 @@ truenas_ws_deploy() {
|
|||
_info "TrueNAS protocol not set. Using 'ws'."
|
||||
DEPLOY_TRUENAS_PROTOCOL="ws"
|
||||
fi
|
||||
_ws_uri="$DEPLOY_TRUENAS_PROTOCOL://$DEPLOY_TRUENAS_HOSTNAME/websocket"
|
||||
|
||||
# Check port, optional
|
||||
if [ -n "$DEPLOY_TRUENAS_PORT" ]; then
|
||||
case "$DEPLOY_TRUENAS_PORT" in
|
||||
'' | *[!0-9]*)
|
||||
_err "Invalid TrueNAS port '$DEPLOY_TRUENAS_PORT'. DEPLOY_TRUENAS_PORT must be numeric."
|
||||
return 8
|
||||
;;
|
||||
esac
|
||||
|
||||
_ws_uri="$DEPLOY_TRUENAS_PROTOCOL://$DEPLOY_TRUENAS_HOSTNAME:$DEPLOY_TRUENAS_PORT/websocket"
|
||||
else
|
||||
_ws_uri="$DEPLOY_TRUENAS_PROTOCOL://$DEPLOY_TRUENAS_HOSTNAME/websocket"
|
||||
fi
|
||||
|
||||
_debug2 DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||
_debug2 DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
|
||||
_debug _ws_uri "$_ws_uri"
|
||||
|
|
@ -216,13 +235,14 @@ truenas_ws_deploy() {
|
|||
|
||||
if [ "$_ws_response" != "TRUE" ]; then
|
||||
_err "TrueNAS is not ready."
|
||||
_err "Please check environment variables DEPLOY_TRUENAS_APIKEY, DEPLOY_TRUENAS_HOSTNAME and DEPLOY_TRUENAS_PROTOCOL."
|
||||
_err "Please check environment variables DEPLOY_TRUENAS_APIKEY, DEPLOY_TRUENAS_HOSTNAME, DEPLOY_TRUENAS_PROTOCOL and DEPLOY_TRUENAS_PORT."
|
||||
_err "Verify API key."
|
||||
return 2
|
||||
fi
|
||||
_savedeployconf DEPLOY_TRUENAS_APIKEY "$DEPLOY_TRUENAS_APIKEY"
|
||||
_savedeployconf DEPLOY_TRUENAS_HOSTNAME "$DEPLOY_TRUENAS_HOSTNAME"
|
||||
_savedeployconf DEPLOY_TRUENAS_PROTOCOL "$DEPLOY_TRUENAS_PROTOCOL"
|
||||
_savedeployconf DEPLOY_TRUENAS_PORT "$DEPLOY_TRUENAS_PORT"
|
||||
_info "TrueNAS health: OK"
|
||||
|
||||
########## System info
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_1984hosting
|
|||
Options:
|
||||
One984HOSTING_Username Username
|
||||
One984HOSTING_Password Password
|
||||
One984HOSTING_TOTP_Secret Base32 TOTP shared secret. Required only if the account has 2FA enabled. Requires oathtool. Used to mint the OTP code automatically at login so cron renewals keep working.
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/2851
|
||||
Author: Adrian Fedoreanu
|
||||
'
|
||||
|
|
@ -124,11 +125,28 @@ _1984hosting_login() {
|
|||
_debug "Login to 1984Hosting as user $One984HOSTING_Username."
|
||||
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
|
||||
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
|
||||
url="https://1984.hosting/api/auth/"
|
||||
|
||||
_get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
|
||||
csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
|
||||
sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')"
|
||||
# When 2FA is enabled, mint a fresh TOTP code from the stored shared secret.
|
||||
# Empty otpkey is accepted by the server when 2FA is off.
|
||||
otpkey=""
|
||||
if [ -n "$One984HOSTING_TOTP_Secret" ]; then
|
||||
if ! _exists oathtool; then
|
||||
_err "oathtool is required to use One984HOSTING_TOTP_Secret for 2FA. Please install it."
|
||||
return 1
|
||||
fi
|
||||
otpcode="$(oathtool --base32 --totp "$One984HOSTING_TOTP_Secret" 2>/dev/null)"
|
||||
if [ -z "$otpcode" ]; then
|
||||
_err "Failed to generate TOTP code from One984HOSTING_TOTP_Secret."
|
||||
return 1
|
||||
fi
|
||||
otpkey="$(printf '%s' "$otpcode" | _url_encode)"
|
||||
fi
|
||||
|
||||
# Fetch the login page to obtain CSRF and session cookies.
|
||||
# Note: _get sets the global 'url', so assign the auth URL afterwards.
|
||||
_get "https://1984.hosting/accounts/login/" >/dev/null
|
||||
csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | _head_n 1 | tr -d ';')"
|
||||
sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | _head_n 1 | tr -d ';')"
|
||||
|
||||
if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then
|
||||
_err "One or more cookies are empty: '$csrftoken', '$sessionid'."
|
||||
|
|
@ -140,17 +158,23 @@ _1984hosting_login() {
|
|||
csrf_header=$(echo "$csrftoken" | sed 's/csrftoken=//' | _head_n 1)
|
||||
export _H3="X-CSRFToken: $csrf_header"
|
||||
|
||||
response="$(_post "username=$username&password=$password&otpkey=" $url)"
|
||||
url="https://1984.hosting/api/auth/"
|
||||
response="$(_post "username=$username&password=$password&otpkey=$otpkey" "$url")"
|
||||
response="$(echo "$response" | _normalizeJson)"
|
||||
_debug2 response "$response"
|
||||
|
||||
if _contains "$response" '"loggedin": true'; then
|
||||
One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | tr -d ';')"
|
||||
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
|
||||
One984HOSTING_SESSIONID_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'cookie1984nammnamm=[^;]*;' | _head_n 1 | tr -d ';')"
|
||||
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | _head_n 1 | tr -d ';')"
|
||||
export One984HOSTING_SESSIONID_COOKIE
|
||||
export One984HOSTING_CSRFTOKEN_COOKIE
|
||||
_saveaccountconf_mutable One984HOSTING_Username "$One984HOSTING_Username"
|
||||
_saveaccountconf_mutable One984HOSTING_Password "$One984HOSTING_Password"
|
||||
if [ -n "$One984HOSTING_TOTP_Secret" ]; then
|
||||
_saveaccountconf_mutable One984HOSTING_TOTP_Secret "$One984HOSTING_TOTP_Secret"
|
||||
else
|
||||
_clearaccountconf_mutable One984HOSTING_TOTP_Secret
|
||||
fi
|
||||
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
|
||||
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
|
||||
return 0
|
||||
|
|
@ -161,6 +185,7 @@ _1984hosting_login() {
|
|||
_check_credentials() {
|
||||
One984HOSTING_Username="${One984HOSTING_Username:-$(_readaccountconf_mutable One984HOSTING_Username)}"
|
||||
One984HOSTING_Password="${One984HOSTING_Password:-$(_readaccountconf_mutable One984HOSTING_Password)}"
|
||||
One984HOSTING_TOTP_Secret="${One984HOSTING_TOTP_Secret:-$(_readaccountconf_mutable One984HOSTING_TOTP_Secret)}"
|
||||
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
|
||||
One984HOSTING_Username=""
|
||||
One984HOSTING_Password=""
|
||||
|
|
@ -225,9 +250,15 @@ _get_root() {
|
|||
|
||||
# Usage: _get_zone_id url domain.com
|
||||
# Returns zone id for domain.com
|
||||
# Memoized per-domain so add/rm don't re-fetch the same zone list within a run.
|
||||
# Keyed on domain (not url) since the url is always the domains listing.
|
||||
_get_zone_id() {
|
||||
url=$1
|
||||
domain=$2
|
||||
if [ "$_zone_id_for" = "$domain" ] && [ -n "$_zone_id" ]; then
|
||||
_debug2 _zone_id "$_zone_id (cached)"
|
||||
return 0
|
||||
fi
|
||||
_htmlget "$url" "$domain"
|
||||
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
|
||||
_debug2 _zone_id "$_zone_id"
|
||||
|
|
@ -235,6 +266,7 @@ _get_zone_id() {
|
|||
_err "Error getting _zone_id for $2."
|
||||
return 1
|
||||
fi
|
||||
_zone_id_for="$domain"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -257,9 +289,8 @@ _htmlget() {
|
|||
|
||||
# Add extra headers to request
|
||||
_authpost() {
|
||||
url="https://1984.hosting/domains"
|
||||
_get_zone_id "$url" "$_domain"
|
||||
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
|
||||
_get_zone_id "https://1984.hosting/domains" "$_domain"
|
||||
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | sed 's/csrftoken=//' | _head_n 1)"
|
||||
export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
|
||||
export _H2="Referer: https://1984.hosting/domains/$_zone_id"
|
||||
export _H3="X-CSRFToken: $csrf_header"
|
||||
|
|
|
|||
490
dnsapi/dns_arubabusiness.sh
Normal file
490
dnsapi/dns_arubabusiness.sh
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
dns_arubabusiness_info='ArubaBusiness
|
||||
Site: business.aruba.it
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_arubabusiness
|
||||
Options:
|
||||
AB_Key Your ArubaBusiness API Key
|
||||
AB_User Your account user
|
||||
AB_Pass Your account password
|
||||
'
|
||||
|
||||
#
|
||||
# A word of warning: as of this writing, api.arubabusiness.it only supports oauth authentication using the "password" grant type.
|
||||
# If you are REALLY sure you want to use it, it would be wise set up a dedicated technical user without administrative privileges
|
||||
#
|
||||
|
||||
ARUBABUSINESS_API='https://api.arubabusiness.it'
|
||||
|
||||
######## Public functions ########
|
||||
|
||||
#
|
||||
# Usage: dns_arubabusiness_add _acme-challenge.www.domain.com aaaabbbbcccc111122223333
|
||||
#
|
||||
# Add a new TXT record whose name and value match the given domain and value
|
||||
#
|
||||
# Variables
|
||||
# _full_domain: $1 - the name of the TXT record
|
||||
# _txt_value: $2 - the value of the TXT record
|
||||
# _body
|
||||
# dns_details
|
||||
# domain_id
|
||||
# dns_record_id
|
||||
# response
|
||||
#
|
||||
dns_arubabusiness_add() {
|
||||
_full_domain=$1
|
||||
_txt_value=$2
|
||||
|
||||
if ! _ab_authenticate; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _ab_domain_id "$_full_domain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _ab_dns_record_id "$_full_domain" "$_txt_value" "$dns_details"; then
|
||||
# This is very unlikely, but allow the process to use the existing record
|
||||
_info "A TXT record with name: $_full_domain and value: $_txt_value already exists (id: $dns_record_id)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_body="{ \"IdDomain\": $domain_id, \"Type\": \"TXT\", \"Name\": \"$_full_domain\", \"Content\": \"\\\"$_txt_value\\\"\" }"
|
||||
|
||||
_debug "Adding TXT record with name: $_full_domain and value: $_txt_value"
|
||||
|
||||
if ! _ab_rest POST "api/domains/dns/record" "$_body" || ! _contains "$response" "DomainId"; then
|
||||
_err "Failed to add TXT record with name: $_full_domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Sleeping 10 seconds to let ArubaBusiness do its magic"
|
||||
_sleep 10
|
||||
|
||||
# Refresh dns details and check that the record was really added
|
||||
if ! _ab_dns_details "$root_domain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _ab_dns_record_id "$_full_domain" "$_txt_value" "$dns_details"; then
|
||||
# This should never happen
|
||||
_err "The TXT record with name: $_full_domain was not set"
|
||||
_err "Please check that the dns records are clean"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Added TXT record with id: $dns_record_id"
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: dns_arubabusiness_rm _acme-challenge.www.domain.com aaaabbbbcccc111122223333
|
||||
#
|
||||
# Remove the TXT record whose name and value match the given domain and value
|
||||
#
|
||||
# Variables
|
||||
# _full_domain: $1 - the name of the TXT record
|
||||
# _txt_value: $2 - the value of the TXT record
|
||||
# dns_details
|
||||
# dns_record_id
|
||||
#
|
||||
dns_arubabusiness_rm() {
|
||||
_full_domain=$1
|
||||
_txt_value=$2
|
||||
|
||||
if ! _ab_authenticate; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _ab_domain_id "$_full_domain"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _ab_dns_record_id "$_full_domain" "$_txt_value" "$dns_details" || [ -z "$dns_record_id" ]; then
|
||||
_err "Could not retrieve the record id for: $_full_domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "Deleting TXT record: $dns_record_id"
|
||||
if ! _ab_rest DELETE "api/domains/dns/record/$dns_record_id" || ! _contains "$response" "DomainId"; then
|
||||
_err "Failed to delete TXT record: $dns_record_id"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Deleted TXT record: $dns_record_id"
|
||||
return 0
|
||||
}
|
||||
|
||||
######## Private functions ########
|
||||
|
||||
#
|
||||
# Usage: _ab_domain_id _acme-challenge.www.domain.com
|
||||
#
|
||||
# Split the input domain into subdomain + root domain and get the id of the root domain
|
||||
#
|
||||
# Variables
|
||||
# _full_domain: $1 - the domain whose root needs to be extracted
|
||||
# _domain_sections
|
||||
# _current_index
|
||||
# _candidate_subdomain
|
||||
# _candidate_domain
|
||||
# sub_domain
|
||||
# root_domain
|
||||
# domain_id
|
||||
# dns_details: a json containing all dns records registered on the root domain
|
||||
#
|
||||
# Example
|
||||
# _get_root _acme-challenge.www.domain.com
|
||||
#
|
||||
# Should return
|
||||
# sub_domain=_acme-challenge.www
|
||||
# root_domain=domain.com
|
||||
# domain_id=123123123123
|
||||
# dns_details="{JSON_CONTENT}"
|
||||
#
|
||||
_ab_domain_id() {
|
||||
_full_domain=$1
|
||||
|
||||
_info "Attempting to retrieve root domain details for: $_full_domain"
|
||||
|
||||
_domain_sections=$(_math "$(printf "%s" "$_full_domain" | tr '.' '\n' | wc -l)" + 1)
|
||||
|
||||
if [ "$_domain_sections" -lt 1 ]; then
|
||||
_err "Invalid input $_full_domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_current_index=1
|
||||
while true; do
|
||||
_candidate_subdomain=$(if [ "$_current_index" = "1" ]; then printf ""; else printf "%s" "$_full_domain" | cut -d . -f 1-"$(_math "$_current_index" - 1)"; fi)
|
||||
_candidate_domain=$(printf "%s" "$_full_domain" | cut -d . -f "$_current_index"-"$_domain_sections")
|
||||
|
||||
if ! _ab_dns_details "$_candidate_domain"; then
|
||||
_debug2 "Could not fetch dns details for: $_candidate_domain"
|
||||
_current_index=$(_math "$_current_index" + 1)
|
||||
|
||||
# Fail if there are no candidates left
|
||||
if [ "$_current_index" -gt "$_domain_sections" ]; then
|
||||
_err "Could not determine the root domain for: $_full_domain"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
sub_domain="$_candidate_subdomain"
|
||||
root_domain="$_candidate_domain"
|
||||
# Extract the domain id, which is an integer and contains no commas
|
||||
domain_id="$(printf "%s" "$dns_details" | _egrep_o '"Id":[^,]*' | _head_n 1 | cut -d : -f 2 | tr -d ' "')"
|
||||
|
||||
if [ -z "$domain_id" ]; then
|
||||
_err "Could not determine the domain id for: $root_domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "Retrieved root domain id: $domain_id"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: _ab_dns_record_id _acme-challenge.www.domain.com "aaaabbbbcccc111122223333" "{JSON_CONTENT}"
|
||||
#
|
||||
# Extract the record id of the first TXT record whose name and content match the input values
|
||||
#
|
||||
# Variables
|
||||
# _record_name: $1
|
||||
# _txt_value: $2
|
||||
# _dns_details: $3 - the json returned by a previous call to '_ab_dns_details() $root_domain'
|
||||
# _record_ids
|
||||
# _record_names
|
||||
# _record_types
|
||||
# _record_contents
|
||||
# _record_ids_count
|
||||
# _record_names_count
|
||||
# _record_types_count
|
||||
# _record_contents_count
|
||||
# _i
|
||||
# dns_record_id
|
||||
#
|
||||
# Notes
|
||||
# TXT correspond to record type 5
|
||||
# ArubaBusiness appends a terminating dot (.) to the record name
|
||||
# The content field may contain the following character sequence: \"
|
||||
# All record names are always converted to lowercase
|
||||
#
|
||||
_ab_dns_record_id() {
|
||||
_record_name=$1
|
||||
_txt_value=$2
|
||||
_dns_details=$3
|
||||
|
||||
_record_name_lowercase=$(printf "%s" "$_record_name" | _lower_case)
|
||||
|
||||
# Extract the record ids, which are integers and contain no commas, colons or spaces
|
||||
# The first id is skipped because it refers to the domain id
|
||||
_record_ids=$(printf "%s" "$_dns_details" | sed 's/"Id":/\n"Id":/g' | _egrep_o '"Id":[^,]*' | _tail_n +2 | cut -d : -f 2 | tr -d ' ' | tr '\n' ' ')
|
||||
|
||||
# Extract the record names, which are strings but cannot contain commas, colons, spaces and quotes
|
||||
# The first name is skipped because it refers to the domain name
|
||||
_record_names=$(printf "%s" "$_dns_details" | sed 's/"Name":/\n"Name":/g' | _egrep_o '"Name":[^,]*' | _tail_n +2 | cut -d : -f 2 | tr -d ' "' | tr '\n' ' ')
|
||||
|
||||
# Extract the record types, which are integers (except for the first one) and contain no commas, colons or spaces
|
||||
# The first type is skipped because it refers to the domain type
|
||||
_record_types=$(printf "%s" "$_dns_details" | sed 's/"Type":/\n"Type":/g' | _egrep_o '"Type":[^,]*' | _tail_n +2 | cut -d : -f 2 | tr -d ' ' | tr '\n' ' ')
|
||||
|
||||
# Extract the record contents, which are strings and may contain no quotes except for TXT records, which must be delimited by two \" literals
|
||||
# Note: There is no domain related entry here
|
||||
# Note: A " character is appended at the end of each content to make it easier to process the list later
|
||||
_record_contents=$(printf "%s" "$_dns_details" | sed 's/"Content":/\n"Content":/g' | sed 's/\\"//g' | _egrep_o '"Content": *"[^"]*"' | cut -d : -f 2- | sed -n 's/"\(.*\)"/\1/p' | tr '\n' '#')
|
||||
|
||||
_info "IDS: $_record_ids"
|
||||
_info "NAMES: $_record_names"
|
||||
_info "TYPEs: $_record_types"
|
||||
_info "CONTENTS: $_record_contents"
|
||||
|
||||
_record_ids_count=$(printf "%s" "$_record_ids" | tr ' ' '\n' | wc -l)
|
||||
_record_names_count=$(printf "%s" "$_record_names" | tr ' ' '\n' | wc -l)
|
||||
_record_types_count=$(printf "%s" "$_record_types" | tr ' ' '\n' | wc -l)
|
||||
_record_contents_count=$(printf "%s" "$_record_contents" | tr '#' '\n' | wc -l)
|
||||
|
||||
_info "Ids: $_record_ids_count, names: $_record_names_count, types: $_record_types_count, contents: $_record_contents_count"
|
||||
|
||||
if [ "$_record_ids_count" != "$_record_names_count" ] || [ "$_record_ids_count" != "$_record_types_count" ] || [ "$_record_ids_count" != "$_record_contents_count" ]; then
|
||||
_err "Failed to parse record elements. Ids: $_record_ids_count, names: $_record_names_count, types: $_record_types_count, contents: $_record_contents_count"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Looking for a TXT record matching inputs - name: $_record_name_lowercase value: $_txt_value"
|
||||
|
||||
_i=1
|
||||
while [ "$_i" -le "$_record_ids_count" ]; do
|
||||
_current_name=$(printf "%s" "$_record_names" | cut -d " " -f "$_i")
|
||||
_current_type=$(printf "%s" "$_record_types" | cut -d " " -f "$_i")
|
||||
_current_content=$(printf "%s" "$_record_contents" | cut -d "#" -f "$_i")
|
||||
|
||||
if [ "$_record_name_lowercase." = "$_current_name" ] && [ "5" = "$_current_type" ] && [ "$_txt_value" = "$_current_content" ]; then
|
||||
dns_record_id=$(printf "%s" "$_record_ids" | cut -d " " -f "$_i")
|
||||
_info "Found matching record with id: $dns_record_id"
|
||||
return 0
|
||||
else
|
||||
_debug2 "Record does not match - type: '$_current_type' name: '$_current_name' value: '$_current_content'; Expected '$_record_name_lowercase.' '5' '$_txt_value'"
|
||||
fi
|
||||
_i=$(_math "$_i" + 1)
|
||||
done
|
||||
|
||||
_debug2 "No matching record was found in $_dns_details"
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: _ab_dns_details domain.com
|
||||
#
|
||||
# Retrieve dns info for the given input domain
|
||||
#
|
||||
# Variables
|
||||
# _domain: $1
|
||||
# dns_details: the json returned by the call to $ARUBABUSINESS_API/api/domains/dns/$_domain/details (if return status is 0)
|
||||
# response
|
||||
#
|
||||
_ab_dns_details() {
|
||||
_domain=$1
|
||||
|
||||
if ! _ab_rest GET "api/domains/dns/$_domain/details" || ! _contains "$response" "DomainId"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
dns_details="$response"
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: _ab_authenticate
|
||||
#
|
||||
# Read account conf, update domain conf and perform user authentication to acquire an access token
|
||||
#
|
||||
# Variables
|
||||
# AB_Key
|
||||
# AB_User
|
||||
# AB_Pass
|
||||
# AB_Token
|
||||
#
|
||||
_ab_authenticate() {
|
||||
AB_Key="${AB_Key:-$(_readaccountconf_mutable AB_Key)}"
|
||||
AB_User="${AB_User:-$(_readaccountconf_mutable AB_User)}"
|
||||
AB_Pass="${AB_Pass:-$(_readaccountconf_mutable AB_Pass)}"
|
||||
|
||||
if [ -z "$AB_Key" ] || [ -z "$AB_User" ] || [ -z "$AB_Pass" ]; then
|
||||
AB_Key=""
|
||||
AB_User=""
|
||||
AB_Pass=""
|
||||
_err "Either the ArubaBusiness API key, the user or the password has not been defined yet."
|
||||
_err "Please configure them and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable AB_Key "$AB_Key"
|
||||
_saveaccountconf_mutable AB_User "$AB_User"
|
||||
_saveaccountconf_mutable AB_Pass "$AB_Pass"
|
||||
|
||||
if ! _ab_get_token || [ -z "$AB_Token" ]; then
|
||||
_err "Failed to acquire an access token"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: _ab_get_token
|
||||
#
|
||||
# Try acquiring a temporary access token. The token should have a 24h lifespan
|
||||
#
|
||||
# Variables
|
||||
# _ab_user_enc
|
||||
# _ab_pass_enc
|
||||
# _ab_authdata
|
||||
# AB_User
|
||||
# AB_Pass
|
||||
# AB_Token
|
||||
# response
|
||||
# _H2
|
||||
#
|
||||
_ab_get_token() {
|
||||
_ab_user_enc=$(printf "%s" "$AB_User" | _url_encode)
|
||||
_ab_pass_enc=$(printf "%s" "$AB_Pass" | _url_encode)
|
||||
_ab_authdata="grant_type=password&username=$_ab_user_enc&password=$_ab_pass_enc"
|
||||
|
||||
_H2="Content-Type: application/x-www-form-urlencoded"
|
||||
|
||||
if ! _ab_rest POST "auth/token" "$_ab_authdata" || ! _contains "$response" "access_token"; then
|
||||
_err "Authentication failure"
|
||||
return 1
|
||||
fi
|
||||
|
||||
AB_Token="$(printf "%s" "$response" | _egrep_o '"access_token":"[^\"]*"' | cut -d : -f 2 | tr -d '"')"
|
||||
|
||||
if [ -z "$AB_Token" ]; then
|
||||
_err "Could not extract access token"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "Acquired access token"
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: _ab_rest POST "example/endpoint" "password=123"
|
||||
#
|
||||
# Perform a REST request using the given method, endpoint and data
|
||||
#
|
||||
# Variables
|
||||
# _method: $1 - The http method
|
||||
# _endpoint: $2 - The api path (relative to $ARUBABUSINESS_API)
|
||||
# _data: $3 - The body of the request (optional)
|
||||
# _key_trimmed
|
||||
# _token_trimmed
|
||||
# _ret_code
|
||||
# AB_Key
|
||||
# AB_Token
|
||||
# ARUBABUSINESS_API
|
||||
# _H1
|
||||
# _H2
|
||||
# _H3
|
||||
# _H4
|
||||
#
|
||||
_ab_rest() {
|
||||
_method=$1
|
||||
_endpoint="$2"
|
||||
_data="$3"
|
||||
|
||||
_key_trimmed=$(printf "%s" "$AB_Key" | tr -d '"')
|
||||
_token_trimmed=$(printf "%s" "$AB_Token" | tr -d '"')
|
||||
|
||||
_H1="Accept: application/json"
|
||||
|
||||
if [ -z "$_H2" ]; then
|
||||
# Default to application/json
|
||||
_H2="Content-Type: application/json"
|
||||
fi
|
||||
|
||||
if [ "$_key_trimmed" ]; then
|
||||
_H3="Authorization-Key: $_key_trimmed"
|
||||
else
|
||||
_err "Missing Api Key"
|
||||
_ab_cleanup_headers
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$_token_trimmed" ]; then
|
||||
_H4="Authorization: Bearer $_token_trimmed"
|
||||
else
|
||||
_debug "No access token set"
|
||||
fi
|
||||
|
||||
if [ "$_method" != "GET" ]; then
|
||||
response="$(_post "$_data" "$ARUBABUSINESS_API/$_endpoint" "" "$_method")"
|
||||
else
|
||||
response="$(_get "$ARUBABUSINESS_API/$_endpoint")"
|
||||
fi
|
||||
|
||||
_ret_code=$?
|
||||
|
||||
if [ "$_ret_code" = "0" ] && _ab_call_is_success; then
|
||||
# Normalize the json response
|
||||
response="$(printf "%s" "$response" | _normalizeJson)"
|
||||
_ret_code=0
|
||||
else
|
||||
_err "Failed to call endpoint: $_endpoint"
|
||||
_ret_code=1
|
||||
fi
|
||||
|
||||
_ab_cleanup_headers
|
||||
|
||||
return $_ret_code
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: _ab_cleanup_headers
|
||||
#
|
||||
# Unset header variables to avoid interfering with other calls
|
||||
#
|
||||
# Variables
|
||||
# _H1
|
||||
# _H2
|
||||
# _H3
|
||||
# _H4
|
||||
#
|
||||
_ab_cleanup_headers() {
|
||||
# Cleanup request headers
|
||||
unset _H1 _H2 _H3 _H4 _H5
|
||||
|
||||
# Cleanup response headers
|
||||
if [ -f "$HTTP_HEADER" ]; then
|
||||
: >"$HTTP_HEADER"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Usage: _ab_call_is_success
|
||||
#
|
||||
# Check whether a call's response http status is one of 200, 201, 202 or 204 (other 2xx are not handled)
|
||||
#
|
||||
# Variables
|
||||
# _status
|
||||
# _http_status
|
||||
# _success_http_codes
|
||||
# HTTP_HEADER
|
||||
#
|
||||
_ab_call_is_success() {
|
||||
_success_http_codes="200 201 202 204"
|
||||
if [ -f "$HTTP_HEADER" ]; then
|
||||
_http_status=$(_egrep_o "^HTTP[\/0-9. ]*" <"$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)
|
||||
for _status in $_success_http_codes; do
|
||||
if [ "$_status" = "$_http_status" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
|
@ -11,7 +11,8 @@ Options:
|
|||
# All `_sleep` commands are included to avoid Route53 throttling, see
|
||||
# https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests
|
||||
|
||||
AWS_HOST="route53.amazonaws.com"
|
||||
# Updated from "route53.amazonaws.com"
|
||||
AWS_HOST="route53.global.api.aws"
|
||||
AWS_URL="https://$AWS_HOST"
|
||||
|
||||
AWS_WIKI="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-Amazon-Route53-API"
|
||||
|
|
|
|||
|
|
@ -49,26 +49,95 @@ Options:
|
|||
Baidu_SK SecretAccessKey
|
||||
OptionsAlt:
|
||||
Baidu_BCD_Host API host, default: bcd.baidubce.com
|
||||
Baidu_DNS_Host New DNS API host, default: dns.baidubce.com
|
||||
Baidu_API_Preference Engine preference, default: auto
|
||||
Baidu_BCD_Version API version number, default: 1
|
||||
Baidu_BCD_Expire Signature expiration seconds, default: 3600
|
||||
Baidu_View Resolve view, default: DEFAULT
|
||||
Baidu_Line New DNS line, default: default
|
||||
Baidu_TTL Resolve ttl seconds, default: 300
|
||||
Baidu_RM_Max Max records to delete in one run, default: 20
|
||||
'
|
||||
|
||||
BAIDU_BCD_DEFAULT_HOST="bcd.baidubce.com"
|
||||
BAIDU_DNS_DEFAULT_HOST="dns.baidubce.com"
|
||||
|
||||
# --- Public API ---
|
||||
dns_baidu_add() {
|
||||
fulldomain=$(_idn "$1")
|
||||
txtvalue=$2
|
||||
|
||||
if ! _baidu_prepare_record "$fulldomain"; then
|
||||
_baidu_err "baidu_prepare_record failed for add: $fulldomain"
|
||||
if ! _baidu_run_with_fallback "add" "$fulldomain" "$txtvalue"; then
|
||||
_baidu_err "all baidu api engines failed for add: $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
|
||||
return 0
|
||||
}
|
||||
|
||||
dns_baidu_rm() {
|
||||
fulldomain=$(_idn "$1")
|
||||
txtvalue=$2
|
||||
|
||||
if ! _baidu_run_with_fallback "rm" "$fulldomain" "$txtvalue"; then
|
||||
_baidu_err "all baidu api engines failed for delete: $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_baidu_run_with_fallback() {
|
||||
_action="$1"
|
||||
_fulldomain="$2"
|
||||
_txtvalue="$3"
|
||||
|
||||
if ! _baidu_load_credentials; then
|
||||
_baidu_err "baidu_load_credentials failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for _baidu_api_engine in $(_baidu_engine_order); do
|
||||
if ! _baidu_prepare_record "$_fulldomain"; then
|
||||
_baidu_info "prepare failed for engine: $_baidu_api_engine"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$_action" = "add" ]; then
|
||||
if _baidu_add_record "$_txtvalue"; then
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
if _baidu_rm_record "$_txtvalue"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
_baidu_info "engine failed, try next if available: $_baidu_api_engine"
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_baidu_engine_order() {
|
||||
_pref="$(_lower_case "$(_baidu_trim_ws "${Baidu_API_Preference:-auto}")")"
|
||||
case "$_pref" in
|
||||
legacy)
|
||||
printf "%s" "legacy new"
|
||||
;;
|
||||
new)
|
||||
printf "%s" "new legacy"
|
||||
;;
|
||||
*)
|
||||
printf "%s" "new legacy"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_baidu_add_record() {
|
||||
_txtvalue="$1"
|
||||
|
||||
if ! _baidu_find_record_ids_current "$_zone_name" "$_record_domain" "TXT" "$_txtvalue"; then
|
||||
_baidu_err "baidu_find_record_ids failed for add: $_record_domain.$_zone_name"
|
||||
return 1
|
||||
fi
|
||||
|
|
@ -85,16 +154,28 @@ dns_baidu_add() {
|
|||
_ttl="300"
|
||||
;;
|
||||
esac
|
||||
_view="$(_baidu_trim_ws "${Baidu_View:-DEFAULT}")"
|
||||
txtvalue="$(_baidu_trim_ws "$txtvalue")"
|
||||
|
||||
txtvalue="$(_baidu_trim_ws "$_txtvalue")"
|
||||
_record_domain="$(_baidu_trim_ws "$_record_domain")"
|
||||
_zone_name="$(_baidu_trim_ws "$_zone_name")"
|
||||
|
||||
_body="$(_baidu_payload_add_txt "$_zone_name" "$_record_domain" "$txtvalue" "$_ttl" "$_view")"
|
||||
|
||||
if ! _baidu_bcd_post "/domain/resolve/add" "$_body"; then
|
||||
_baidu_err "baidu_bcd_post failed: add record"
|
||||
return 1
|
||||
if [ "$_baidu_api_engine" = "new" ]; then
|
||||
_line="$(_baidu_trim_ws "${Baidu_Line:-default}")"
|
||||
if [ -z "$_line" ]; then
|
||||
_line="default"
|
||||
fi
|
||||
_body="$(_baidu_payload_add_txt_dns "$_record_domain" "$txtvalue" "$_ttl" "$_line")"
|
||||
if ! _baidu_dns_call "POST" "/v1/dns/zone/${_zone_name}/record" "$_body"; then
|
||||
_baidu_err "baidu_dns_call failed: add record"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
_view="$(_baidu_trim_ws "${Baidu_View:-DEFAULT}")"
|
||||
_body="$(_baidu_payload_add_txt "$_zone_name" "$_record_domain" "$txtvalue" "$_ttl" "$_view")"
|
||||
if ! _baidu_bcd_post "/domain/resolve/add" "$_body"; then
|
||||
_baidu_err "baidu_bcd_post failed: add record"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if _baidu_is_api_error "$response"; then
|
||||
|
|
@ -105,16 +186,10 @@ dns_baidu_add() {
|
|||
return 0
|
||||
}
|
||||
|
||||
dns_baidu_rm() {
|
||||
fulldomain=$(_idn "$1")
|
||||
txtvalue=$2
|
||||
_baidu_rm_record() {
|
||||
_txtvalue="$1"
|
||||
|
||||
if ! _baidu_prepare_record "$fulldomain"; then
|
||||
_baidu_err "baidu_prepare_record failed for delete: $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
|
||||
if ! _baidu_find_record_ids_current "$_zone_name" "$_record_domain" "TXT" "$_txtvalue"; then
|
||||
_baidu_err "baidu_find_record_ids failed for delete: $_record_domain.$_zone_name"
|
||||
return 1
|
||||
fi
|
||||
|
|
@ -138,28 +213,37 @@ dns_baidu_rm() {
|
|||
fi
|
||||
|
||||
for _rid in $_ids; do
|
||||
_body="$(_baidu_payload_delete "$_zone_name" "$_rid")"
|
||||
if ! _baidu_bcd_post "/domain/resolve/delete" "$_body"; then
|
||||
_baidu_err "baidu_bcd_post failed: delete recordId=$_rid"
|
||||
return 1
|
||||
fi
|
||||
if _baidu_is_api_error "$response"; then
|
||||
_baidu_err "$response"
|
||||
return 1
|
||||
if [ "$_baidu_api_engine" = "new" ]; then
|
||||
if ! _baidu_dns_call "DELETE" "/v1/dns/zone/${_zone_name}/record/${_rid}" ""; then
|
||||
_baidu_err "baidu_dns_call failed: delete recordId=$_rid"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
_body="$(_baidu_payload_delete "$_zone_name" "$_rid")"
|
||||
if ! _baidu_bcd_post "/domain/resolve/delete" "$_body"; then
|
||||
_baidu_err "baidu_bcd_post failed: delete recordId=$_rid"
|
||||
return 1
|
||||
fi
|
||||
if _baidu_is_api_error "$response"; then
|
||||
_baidu_err "$response"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
|
||||
_baidu_err "baidu_find_record_ids failed for delete verify: $_record_domain.$_zone_name"
|
||||
return 1
|
||||
fi
|
||||
_left_ids="$_BAIDU_FIND_RESULT"
|
||||
if [ -z "$_left_ids" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ -n "$_left_ids" ]; then
|
||||
_baidu_err "delete verification failed: $_record_domain.$_zone_name still has TXT records"
|
||||
return 1
|
||||
if [ "$_baidu_api_engine" = "legacy" ]; then
|
||||
if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$_txtvalue"; then
|
||||
_baidu_err "baidu_find_record_ids failed for delete verify: $_record_domain.$_zone_name"
|
||||
return 1
|
||||
fi
|
||||
_left_ids="$_BAIDU_FIND_RESULT"
|
||||
if [ -z "$_left_ids" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ -n "$_left_ids" ]; then
|
||||
_baidu_err "delete verification failed: $_record_domain.$_zone_name still has TXT records"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
|
|
@ -182,6 +266,7 @@ _baidu_load_credentials() {
|
|||
_saveaccountconf_mutable Baidu_SK "$Baidu_SK"
|
||||
|
||||
BAIDU_BCD_HOST="${Baidu_BCD_Host:-$BAIDU_BCD_DEFAULT_HOST}"
|
||||
BAIDU_DNS_HOST="${Baidu_DNS_Host:-$BAIDU_DNS_DEFAULT_HOST}"
|
||||
BAIDU_BCD_VERSION="${Baidu_BCD_Version:-1}"
|
||||
|
||||
return 0
|
||||
|
|
@ -189,13 +274,16 @@ _baidu_load_credentials() {
|
|||
|
||||
_baidu_prepare_record() {
|
||||
_fulldomain="$1"
|
||||
if ! _baidu_load_credentials; then
|
||||
_baidu_err "baidu_load_credentials failed"
|
||||
return 1
|
||||
fi
|
||||
if ! _baidu_get_root "$_fulldomain"; then
|
||||
_baidu_err "Could not find zone for $_fulldomain"
|
||||
return 1
|
||||
if [ "$_baidu_api_engine" = "new" ]; then
|
||||
if ! _baidu_get_root_dns "$_fulldomain"; then
|
||||
_baidu_err "Could not find zone by new dns api for $_fulldomain"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if ! _baidu_get_root "$_fulldomain"; then
|
||||
_baidu_err "Could not find zone by legacy bcd api for $_fulldomain"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_record_domain="$_sub_domain"
|
||||
_zone_name="$_domain"
|
||||
|
|
@ -234,6 +322,43 @@ _baidu_get_root() {
|
|||
done
|
||||
}
|
||||
|
||||
_baidu_get_root_dns() {
|
||||
domain=$1
|
||||
i=1
|
||||
p=1
|
||||
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||
if [ -z "$h" ]; then
|
||||
_baidu_err "invalid domain: $domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _baidu_dns_call "GET" "/v1/dns/zone/${h}/record" ""; then
|
||||
_baidu_info "baidu_dns_call failed: list zones"
|
||||
elif ! _baidu_is_api_error "$response" && (_contains "$response" "\"records\"" || _contains "$response" "\"maxKeys\""); then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||
_domain=$h
|
||||
if [ "$_sub_domain" = "$_domain" ]; then
|
||||
_sub_domain="@"
|
||||
fi
|
||||
_baidu_info "zone matched by dns api: $_domain (host: $_sub_domain)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
}
|
||||
|
||||
_baidu_find_record_ids_current() {
|
||||
if [ "$_baidu_api_engine" = "new" ]; then
|
||||
_baidu_find_record_ids_dns "$@"
|
||||
else
|
||||
_baidu_find_record_ids "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
_baidu_find_record_ids() {
|
||||
_zone_name="$1"
|
||||
_record_domain="$2"
|
||||
|
|
@ -293,6 +418,39 @@ EOF
|
|||
_BAIDU_FIND_RESULT="$_ids"
|
||||
}
|
||||
|
||||
_baidu_find_record_ids_dns() {
|
||||
_zone_name="$1"
|
||||
_record_domain="$2"
|
||||
_rdtype="$3"
|
||||
_rdata="$4"
|
||||
_BAIDU_FIND_RESULT=""
|
||||
|
||||
if ! _baidu_dns_call "GET" "/v1/dns/zone/${_zone_name}/record" ""; then
|
||||
_baidu_err "baidu_dns_call failed: list records"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _baidu_is_api_error "$response"; then
|
||||
_baidu_err "baidu_dns error: $(_baidu_json_get_str "$response" "code") $(_baidu_json_get_str "$response" "message")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_normalized="$(printf "%s" "$response" | _normalizeJson)"
|
||||
_records=$(printf "%s" "$_normalized" | sed 's/},{/}\n{/g')
|
||||
_ids=""
|
||||
|
||||
while IFS= read -r _line; do
|
||||
_id="$(_baidu_match_record_id_dns "$_line" "$_record_domain" "$_rdtype" "$_rdata")"
|
||||
if [ "$_id" ]; then
|
||||
_ids="$_ids $_id"
|
||||
fi
|
||||
done <<EOF
|
||||
$_records
|
||||
EOF
|
||||
|
||||
_BAIDU_FIND_RESULT="$_ids"
|
||||
}
|
||||
|
||||
# --- HTTP ---
|
||||
_baidu_bcd_post() {
|
||||
_api_path="$1"
|
||||
|
|
@ -317,18 +475,17 @@ _baidu_bcd_post() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
_H1="Authorization: $_auth"
|
||||
_H2="x-bce-date: $_ts"
|
||||
_H3="x-bce-content-sha256: $_payload_hash"
|
||||
_H4="Host: $BAIDU_BCD_HOST"
|
||||
_H5=""
|
||||
|
||||
_url="https://${BAIDU_BCD_HOST}${_uri}"
|
||||
_signed_headers_dbg="$(printf "%s" "$_auth" | cut -d / -f 5)"
|
||||
_baidu_info "POST ${_uri}"
|
||||
_baidu_info "signedHeaders: $_signed_headers_dbg"
|
||||
_baidu_info "payload_sha256: $_payload_hash"
|
||||
_baidu_debug "baidu_bcd.http.payload" "$(_baidu_dbg_trim "$(_baidu_redact_txt "$_payload")")"
|
||||
_H1="Authorization: $_auth"
|
||||
_H2="x-bce-date: $_ts"
|
||||
_H3="x-bce-content-sha256: $_payload_hash"
|
||||
_H4="Host: $BAIDU_BCD_HOST"
|
||||
_H5=""
|
||||
response="$(_post "$_payload" "$_url" "" "POST" "$_content_type")"
|
||||
_ret="$?"
|
||||
_baidu_info "ret: $_ret"
|
||||
|
|
@ -344,6 +501,56 @@ _baidu_bcd_post() {
|
|||
return 0
|
||||
}
|
||||
|
||||
_baidu_dns_call() {
|
||||
_method="$1"
|
||||
_uri="$2"
|
||||
_payload="$3"
|
||||
_content_type="application/json"
|
||||
_attempt=1
|
||||
_max_attempts=3
|
||||
|
||||
while [ "$_attempt" -le "$_max_attempts" ]; do
|
||||
_ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
_payload_hash="$(printf "%s" "$_payload" | _digest sha256 hex)"
|
||||
|
||||
if ! _baidu_bce_auth "$_method" "$_uri" "" "$BAIDU_DNS_HOST" "$_ts" "${Baidu_BCD_Expire:-3600}" "$_content_type" "$_payload_hash"; then
|
||||
_baidu_err "baidu_dns auth failed"
|
||||
return 1
|
||||
fi
|
||||
_auth="$_BAIDU_BCE_AUTH_RESULT"
|
||||
_url="https://${BAIDU_DNS_HOST}${_uri}"
|
||||
|
||||
# Route through acme.sh's _get/_post (they honor _H1.._H5); no raw curl.
|
||||
_H1="Authorization: $_auth"
|
||||
_H2="x-bce-date: $_ts"
|
||||
_H3="x-bce-content-sha256: $_payload_hash"
|
||||
_H4="Host: $BAIDU_DNS_HOST"
|
||||
_H5="Content-Type: $_content_type"
|
||||
|
||||
if [ "$_method" = "GET" ]; then
|
||||
response="$(_get "$_url")"
|
||||
elif [ "$_method" = "DELETE" ]; then
|
||||
response="$(_post "" "$_url" "" "DELETE")"
|
||||
else
|
||||
response="$(_post "$_payload" "$_url")"
|
||||
fi
|
||||
_ret="$?"
|
||||
_baidu_info "${_method} ${_uri} ret=${_ret}"
|
||||
|
||||
# Baidu may return a business error (Exception / 平台服务繁忙) inside HTTP 200.
|
||||
if [ "$_ret" = "0" ] && ! _contains "$response" "\"code\":\"Exception\"" && ! _contains "$response" "平台服务繁忙"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$_attempt" -lt "$_max_attempts" ]; then
|
||||
sleep 2
|
||||
fi
|
||||
_attempt=$(_math "$_attempt" + 1)
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- Auth / Signing ---
|
||||
_baidu_bce_auth() {
|
||||
# Signing algorithm (bce-auth-v1):
|
||||
|
|
@ -499,6 +706,14 @@ _baidu_payload_add_txt() {
|
|||
printf "%s" "{\"domain\":\"${_domain}\",\"view\":\"${_view}\",\"rdType\":\"TXT\",\"ttl\":${_ttl},\"rdata\":\"${_rdata}\",\"zoneName\":\"${_zoneName}\"}"
|
||||
}
|
||||
|
||||
_baidu_payload_add_txt_dns() {
|
||||
_rr="$(printf "%s" "$1" | tr -d '\r\n' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
_value="$(printf "%s" "$2" | tr -d '\r\n' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
_ttl="$3"
|
||||
_line="$(printf "%s" "$4" | tr -d '\r\n' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
printf "%s" "{\"rr\":\"${_rr}\",\"type\":\"TXT\",\"value\":\"${_value}\",\"ttl\":${_ttl},\"line\":\"${_line}\",\"description\":\"acme.sh\"}"
|
||||
}
|
||||
|
||||
_baidu_payload_delete() {
|
||||
_zoneName="$(_baidu_json_escape "$1")"
|
||||
_recordId="$2"
|
||||
|
|
@ -541,6 +756,18 @@ _baidu_match_record_id() {
|
|||
printf "%s" "$_line" | _egrep_o "\"recordId\": *[0-9]*" | _head_n 1 | cut -d : -f 2 | tr -d " "
|
||||
}
|
||||
|
||||
_baidu_match_record_id_dns() {
|
||||
_line="$1"
|
||||
_rr="$(printf "%s" "$2" | tr -d '\r\n' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
_type="$(printf "%s" "$3" | tr -d '\r\n' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
_value="$(printf "%s" "$4" | tr -d '\r\n' | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
||||
case "$_line" in
|
||||
*"\"rr\":\"${_rr}\""*"\"type\":\"${_type}\""*"\"value\":\"${_value}\""*)
|
||||
printf "%s" "$_line" | sed -n 's/.*"id":"\{0,1\}\([^",}]*\)"\{0,1\}.*/\1/p' | _head_n 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_baidu_hmac_sha256_hexkey() {
|
||||
_key_hex="$1"
|
||||
_msg="$2"
|
||||
|
|
|
|||
137
dnsapi/dns_cdmon.sh
Normal file
137
dnsapi/dns_cdmon.sh
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
dns_cdmon_info='cdmon
|
||||
Site: www.cdmon.com
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cdmon
|
||||
Options:
|
||||
CDMON_Key API Key
|
||||
'
|
||||
|
||||
CDMON_Api="https://api-domains.cdmon.services/api-domains"
|
||||
|
||||
######## Public functions #####################
|
||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||
# Used to add txt record
|
||||
dns_cdmon_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
CDMON_Key="${CDMON_Key:-$(_readaccountconf_mutable CDMON_Key)}"
|
||||
|
||||
if [ -z "$CDMON_Key" ]; then
|
||||
CDMON_Key=""
|
||||
_err "You didn't specify your cdmon api key yet."
|
||||
_err "Please create your key and try again."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable CDMON_Key "$CDMON_Key"
|
||||
|
||||
_debug "First, we detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
_info "Adding record"
|
||||
if _cdmon_rest "dnsrecords/create" "{\"data\":{\"type\":\"TXT\",\"domain\":\"$_domain\",\"value\":\"$txtvalue\",\"ttl\":120,\"host\":\"$_sub_domain\"}}"; then
|
||||
if _contains "$response" "\"status\":\"ok\""; then
|
||||
_info "Added, OK"
|
||||
return 0
|
||||
else
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_err "Add txt record error."
|
||||
return 1
|
||||
}
|
||||
|
||||
# Usage: fulldomain txtvalue
|
||||
# Used to remove the txt record after validation
|
||||
dns_cdmon_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
||||
CDMON_Key="${CDMON_Key:-$(_readaccountconf_mutable CDMON_Key)}"
|
||||
_debug "First, we detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_info "Removing record"
|
||||
if _cdmon_rest "dnsrecords/delete" "{\"data\":{\"value\":\"$txtvalue\",\"type\":\"TXT\",\"domain\":\"$_domain\",\"host\":\"$_sub_domain\"}}"; then
|
||||
if _contains "$response" "\"status\":\"ok\""; then
|
||||
_info "Deleted, OK"
|
||||
return 0
|
||||
else
|
||||
_err "Delete txt record error."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
_err "Delete txt record error."
|
||||
return 1
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
#_acme-challenge.www.domain.com
|
||||
#returns
|
||||
# _sub_domain=_acme-challenge.www
|
||||
# _domain=domain.com
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=1
|
||||
p=1
|
||||
|
||||
if ! _cdmon_rest "domains/list"; then
|
||||
return 1
|
||||
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 _contains "$response" "\"domain\":\"$h\""; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||
_domain=$h
|
||||
return 0
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
_cdmon_rest() {
|
||||
ep="$1"
|
||||
data="$2"
|
||||
_debug "$ep"
|
||||
|
||||
key_trimmed=$(echo "$CDMON_Key" | tr -d '"')
|
||||
|
||||
export _H1="Content-Type: application/json"
|
||||
export _H2="apikey: $key_trimmed"
|
||||
|
||||
_debug data "$data"
|
||||
response="$(_post "$data" "$CDMON_Api/$ep")"
|
||||
_ret="$?"
|
||||
|
||||
unset _H1 _H2
|
||||
|
||||
if [ "$_ret" != "0" ]; then
|
||||
_err "error $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ dns_czechia_add() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
_nres="$(_normalizeJson "$_res")"
|
||||
_nres="$(printf '%s' "$_res" | _normalizeJson)"
|
||||
if [ "$?" -ne 0 ] || [ -z "$_nres" ]; then
|
||||
_nres="$_res"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ dns_desec_add() {
|
|||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
_sub_domain=$(echo "$_sub_domain" | _lower_case)
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ dns_desec_add() {
|
|||
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
|
||||
|
||||
if [ "$_code" = "200" ]; then
|
||||
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"[^ ]*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||
_debug "existing TXT found"
|
||||
_debug oldtxtvalues "$oldtxtvalues"
|
||||
if [ -n "$oldtxtvalues" ]; then
|
||||
|
|
@ -100,7 +101,7 @@ dns_desec_rm() {
|
|||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_sub_domain=$(echo "$_sub_domain" | _lower_case)
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
|
|
@ -110,7 +111,7 @@ dns_desec_rm() {
|
|||
_desec_rest GET "$REST_API/$_domain/rrsets/$_sub_domain/TXT/"
|
||||
|
||||
if [ "$_code" = "200" ]; then
|
||||
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"\\S*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||
oldtxtvalues="$(echo "$response" | _egrep_o "\"records\":\\[\"[^ ]*\"\\]" | cut -d : -f 2 | tr -d "[]\\\\\"" | sed "s/,/ /g")"
|
||||
_debug "existing TXT found"
|
||||
_debug oldtxtvalues "$oldtxtvalues"
|
||||
if [ -n "$oldtxtvalues" ]; then
|
||||
|
|
@ -150,6 +151,8 @@ _desec_rest() {
|
|||
if [ "$m" != "GET" ]; then
|
||||
_secure_debug2 data "$data"
|
||||
response="$(_post "$data" "$ep" "" "$m")"
|
||||
_info "Sleeping 1s to respect deSEC write rate limit"
|
||||
_sleep 1
|
||||
else
|
||||
response="$(_get "$ep")"
|
||||
fi
|
||||
|
|
|
|||
263
dnsapi/dns_glesys.sh
Normal file
263
dnsapi/dns_glesys.sh
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_glesys_info='Glesys
|
||||
Site: Glesys.se
|
||||
Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_glesys
|
||||
Options:
|
||||
GLESYS_API_KEY Generated API key.
|
||||
GLESYS_PROJECT_ID Project ID for the API key (e.g. cl12345).
|
||||
GLESYS_API API endpoint. Default "https://api.glesys.com/domain".
|
||||
GLESYS_TTL TXT record TTL. Default 120.
|
||||
Issues: https://github.com/acmesh-official/acme.sh/issues/7057
|
||||
Author: Toni Karppi
|
||||
'
|
||||
|
||||
GLESYS_API_DEFAULT="https://api.glesys.com/domain"
|
||||
GLESYS_TTL_DEFAULT="120"
|
||||
|
||||
######## Public functions #####################################################
|
||||
|
||||
# Usage:
|
||||
# dns_glesys_add _acme-challenge.www.example.com "txt-value"
|
||||
dns_glesys_add() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_debug fulldomain "$fulldomain"
|
||||
_debug txtvalue "$txtvalue"
|
||||
|
||||
_glesys_init || return 1
|
||||
|
||||
if ! _glesys_get_root "$fulldomain"; then
|
||||
_err "Could not find root zone for $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug _domain "$_domain"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
|
||||
host_value="${_sub_domain:-@}"
|
||||
_debug _host_value "$host_value"
|
||||
|
||||
data="{\"domainname\":\"$_domain\",\"host\":\"$host_value\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":\"$GLESYS_TTL\"}"
|
||||
|
||||
_debug2 data "$data"
|
||||
|
||||
if ! _glesys_rest POST "/addrecord" "$data"; then
|
||||
_err "Failed to send HTTP request to add TXT record"
|
||||
return 1
|
||||
fi
|
||||
|
||||
response_code=$(
|
||||
printf "%s" "$response" |
|
||||
tr -d '\r\n\t ' |
|
||||
_egrep_o '"code":"?[0-9]+' |
|
||||
_egrep_o '[0-9]+$'
|
||||
)
|
||||
|
||||
_debug response_code "$response_code"
|
||||
|
||||
if [ "$response_code" != "200" ]; then
|
||||
_err "GleSYS API responded with an unexpected status when attempting to add TXT record"
|
||||
_debug2 "API response" "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "TXT record added"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Usage:
|
||||
# dns_glesys_rm _acme-challenge.www.example.com "txt-value"
|
||||
dns_glesys_rm() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
|
||||
_debug fulldomain "$fulldomain"
|
||||
_debug txtvalue "$txtvalue"
|
||||
|
||||
_glesys_init || return 1
|
||||
|
||||
if ! _glesys_get_root "$fulldomain"; then
|
||||
_err "Could not find root zone for $fulldomain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _glesys_find_record_id "$txtvalue"; then
|
||||
_info "TXT record not present, skip removal"
|
||||
return 0
|
||||
fi
|
||||
|
||||
_debug _record_id "$_record_id"
|
||||
|
||||
if ! _glesys_rest POST "/deleterecord" "{\"recordid\":$_record_id}"; then
|
||||
_err "Failed to send HTTP request to remove TXT record"
|
||||
return 1
|
||||
fi
|
||||
|
||||
response_code=$(
|
||||
printf "%s" "$response" |
|
||||
tr -d '\r\n\t ' |
|
||||
_egrep_o '"code":"?[0-9]+' |
|
||||
_egrep_o '[0-9]+$'
|
||||
)
|
||||
|
||||
_debug response_code "$response_code"
|
||||
|
||||
if [ "$response_code" != "200" ]; then
|
||||
_err "GleSYS API responded with unexpected status when attempting to remove TXT record"
|
||||
_debug2 "API response" "$response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "TXT record removed"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
######## Private functions ####################################################
|
||||
|
||||
_glesys_find_record_id() {
|
||||
txtvalue="$1"
|
||||
|
||||
_debug txtvalue "$txtvalue"
|
||||
|
||||
if [ -z "$txtvalue" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_record_id=""
|
||||
|
||||
_debug "Looking for TXT record with value" "$txtvalue"
|
||||
|
||||
if ! _glesys_rest GET "/listrecords?domainname=$_domain"; then
|
||||
_err "Failed to list DNS records"
|
||||
return 1
|
||||
fi
|
||||
|
||||
records="$(
|
||||
printf "%s" "$response" |
|
||||
tr -d '\r\n\t ' |
|
||||
sed 's/},{/}\
|
||||
{/g'
|
||||
)"
|
||||
|
||||
_debug2 records "$records"
|
||||
|
||||
expected_data="\"data\":\"$txtvalue\""
|
||||
|
||||
_record_id="$(
|
||||
printf "%s\n" "$records" |
|
||||
while IFS= read -r record; do
|
||||
printf "%s" "$record" | grep -q '"type":"TXT"' || continue
|
||||
printf "%s" "$record" | grep -Fq "$expected_data" || continue
|
||||
|
||||
printf "%s" "$record" |
|
||||
grep -E -o '"recordid":"?[0-9]+' |
|
||||
grep -E -o '[0-9]+$'
|
||||
|
||||
break
|
||||
done
|
||||
)"
|
||||
|
||||
_debug _record_id "$_record_id"
|
||||
|
||||
if [ -z "$_record_id" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Finds:
|
||||
# _domain example.com
|
||||
# _sub_domain _acme-challenge.www
|
||||
_glesys_get_root() {
|
||||
domain="$1"
|
||||
i=1
|
||||
|
||||
while true; do
|
||||
h="$(printf "%s" "$domain" | cut -d . -f "$i"-100)"
|
||||
|
||||
if [ -z "$h" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _glesys_rest GET "/listrecords?domainname=$h"; then
|
||||
response_code=$(
|
||||
printf "%s" "$response" |
|
||||
tr -d '\r\n\t ' |
|
||||
_egrep_o '"code":"?[0-9]+' |
|
||||
_egrep_o '[0-9]+$'
|
||||
)
|
||||
|
||||
_debug response_code "$response_code"
|
||||
|
||||
if [ "$response_code" = "200" ]; then
|
||||
cut_len="$((${#domain} - ${#h} - 1))"
|
||||
_domain="$h"
|
||||
_sub_domain="$(printf "%s" "$domain" | cut -c "1-$cut_len")"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
i="$((i + 1))"
|
||||
done
|
||||
}
|
||||
|
||||
_glesys_init() {
|
||||
[ -z "$GLESYS_API" ] && GLESYS_API="$GLESYS_API_DEFAULT"
|
||||
[ -z "$GLESYS_TTL" ] && GLESYS_TTL="$GLESYS_TTL_DEFAULT"
|
||||
|
||||
_debug GLESYS_API "$GLESYS_API"
|
||||
_debug GLESYS_TTL "$GLESYS_TTL"
|
||||
|
||||
GLESYS_API_KEY="${GLESYS_API_KEY:-$(_readaccountconf_mutable GLESYS_API_KEY)}"
|
||||
GLESYS_PROJECT_ID="${GLESYS_PROJECT_ID:-$(_readaccountconf_mutable GLESYS_PROJECT_ID)}"
|
||||
|
||||
if [ -z "$GLESYS_API_KEY" ] || [ -z "$GLESYS_PROJECT_ID" ]; then
|
||||
_err "GLESYS_API_KEY and GLESYS_PROJECT_ID must be set for this provider"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_secure_debug GLESYS_API_KEY "$GLESYS_API_KEY"
|
||||
_secure_debug GLESYS_PROJECT_ID "$GLESYS_PROJECT_ID"
|
||||
|
||||
_glesys_basic_auth="$(printf "%s:%s" "$GLESYS_PROJECT_ID" "$GLESYS_API_KEY" | _base64)"
|
||||
_secure_debug2 _glesys_basic_auth "$_glesys_basic_auth"
|
||||
|
||||
_saveaccountconf_mutable GLESYS_API_KEY "$GLESYS_API_KEY"
|
||||
_saveaccountconf_mutable GLESYS_PROJECT_ID "$GLESYS_PROJECT_ID"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_glesys_rest() {
|
||||
method="$1"
|
||||
path="$2"
|
||||
data="$3"
|
||||
|
||||
export _H1="Authorization: Basic $_glesys_basic_auth"
|
||||
export _H2="Content-Type: application/json"
|
||||
export _H3="Accept: application/json"
|
||||
|
||||
url="$GLESYS_API$path"
|
||||
_debug "$method $url"
|
||||
|
||||
if [ "$method" = "GET" ]; then
|
||||
response="$(_get "$url")"
|
||||
else
|
||||
response="$(_post "$data" "$url" "" "$method")"
|
||||
fi
|
||||
|
||||
ret="$?"
|
||||
_debug2 response "$response"
|
||||
_debug ret "$ret"
|
||||
|
||||
if [ "$ret" != "0" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
|
@ -6,13 +6,13 @@ Site: hostup.se
|
|||
Docs: https://developer.hostup.se/
|
||||
Options:
|
||||
HOSTUP_API_KEY Required. HostUp API key with read:dns + write:dns + read:domains scopes.
|
||||
HOSTUP_API_BASE Optional. Override API base URL (default: https://cloud.hostup.se/api).
|
||||
HOSTUP_API_BASE Optional. Override API base URL (default: https://cloud.hostup.se/api/v2).
|
||||
HOSTUP_TTL Optional. TTL for TXT records (default: 60 seconds).
|
||||
HOSTUP_ZONE_ID Optional. Force a specific zone ID (skip auto-detection).
|
||||
HOSTUP_ZONE_ID Optional. Force a specific v2 zone ID (zone_...) and skip auto-detection.
|
||||
Author: HostUp (https://cloud.hostup.se/contact/en)
|
||||
'
|
||||
|
||||
HOSTUP_API_BASE_DEFAULT="https://cloud.hostup.se/api"
|
||||
HOSTUP_API_BASE_DEFAULT="https://cloud.hostup.se/api/v2"
|
||||
HOSTUP_DEFAULT_TTL=60
|
||||
|
||||
# Public: add TXT record
|
||||
|
|
@ -20,6 +20,7 @@ HOSTUP_DEFAULT_TTL=60
|
|||
dns_hostup_add() {
|
||||
fulldomain="$1"
|
||||
txtvalue="$2"
|
||||
hostup_add_txtvalue="$2"
|
||||
|
||||
_info "Using HostUp DNS API"
|
||||
|
||||
|
|
@ -34,31 +35,34 @@ dns_hostup_add() {
|
|||
|
||||
record_name="$(_hostup_record_name "$fulldomain" "$HOSTUP_ZONE_DOMAIN")"
|
||||
record_name="$(_hostup_sanitize_name "$record_name")"
|
||||
record_value="$(_hostup_json_escape "$txtvalue")"
|
||||
hostup_add_record_value="$(_hostup_json_escape "$hostup_add_txtvalue")"
|
||||
|
||||
ttl="${HOSTUP_TTL:-$HOSTUP_DEFAULT_TTL}"
|
||||
raw_ttl="${HOSTUP_TTL:-$HOSTUP_DEFAULT_TTL}"
|
||||
ttl="$(_hostup_normalize_ttl "$raw_ttl")"
|
||||
if [ -z "$ttl" ]; then
|
||||
_err "HOSTUP_TTL must be a whole number between 60 and 86400 seconds."
|
||||
return 1
|
||||
fi
|
||||
if [ -n "$HOSTUP_TTL" ]; then
|
||||
HOSTUP_TTL="$ttl"
|
||||
_saveaccountconf_mutable HOSTUP_TTL "$HOSTUP_TTL"
|
||||
fi
|
||||
|
||||
_debug "zone_id" "$HOSTUP_ZONE_ID"
|
||||
_debug "zone_domain" "$HOSTUP_ZONE_DOMAIN"
|
||||
_debug "record_name" "$record_name"
|
||||
_debug "ttl" "$ttl"
|
||||
|
||||
request_body="{\"name\":\"$record_name\",\"type\":\"TXT\",\"value\":\"$record_value\",\"ttl\":$ttl}"
|
||||
|
||||
if ! _hostup_rest "POST" "/dns/zones/$HOSTUP_ZONE_ID/records" "$request_body"; then
|
||||
return 1
|
||||
record_name_fqdn="$(_hostup_fqdn "$fulldomain")"
|
||||
if _hostup_find_record "$HOSTUP_ZONE_ID" "$record_name_fqdn" "$hostup_add_txtvalue"; then
|
||||
_info "TXT record already exists for $fulldomain"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! _contains "$_hostup_response" '"success":true'; then
|
||||
_err "HostUp DNS API: failed to create TXT record for $fulldomain"
|
||||
_debug2 "_hostup_response" "$_hostup_response"
|
||||
return 1
|
||||
fi
|
||||
request_body="{\"name\":\"$record_name\",\"type\":\"TXT\",\"value\":\"$hostup_add_record_value\",\"ttl\":$ttl}"
|
||||
|
||||
record_id="$(_hostup_extract_record_id "$_hostup_response")"
|
||||
if [ -n "$record_id" ]; then
|
||||
_hostup_save_record_id "$HOSTUP_ZONE_ID" "$fulldomain" "$record_id"
|
||||
_debug "hostup_saved_record_id" "$record_id"
|
||||
if ! _hostup_rest "POST" "/dns-zones/$HOSTUP_ZONE_ID/records" "$request_body"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "Added TXT record for $fulldomain"
|
||||
|
|
@ -85,20 +89,9 @@ dns_hostup_rm() {
|
|||
record_name_fqdn="$(_hostup_fqdn "$fulldomain")"
|
||||
record_value="$txtvalue"
|
||||
|
||||
record_id_cached="$(_hostup_get_saved_record_id "$HOSTUP_ZONE_ID" "$fulldomain")"
|
||||
if [ -n "$record_id_cached" ]; then
|
||||
_debug "hostup_record_id_cached" "$record_id_cached"
|
||||
if _hostup_delete_record_by_id "$HOSTUP_ZONE_ID" "$record_id_cached"; then
|
||||
_info "Deleted TXT record $record_id_cached"
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain"
|
||||
HOSTUP_ZONE_ID=""
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! _hostup_find_record "$HOSTUP_ZONE_ID" "$record_name_fqdn" "$record_value"; then
|
||||
_info "TXT record not found for $record_name_fqdn. Skipping removal."
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain"
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain" "$record_value"
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
|
@ -109,7 +102,7 @@ dns_hostup_rm() {
|
|||
fi
|
||||
|
||||
_info "Deleted TXT record $HOSTUP_RECORD_ID"
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain"
|
||||
_hostup_clear_record_id "$HOSTUP_ZONE_ID" "$fulldomain" "$record_value"
|
||||
HOSTUP_ZONE_ID=""
|
||||
return 0
|
||||
}
|
||||
|
|
@ -127,21 +120,18 @@ _hostup_init() {
|
|||
if [ -z "$HOSTUP_API_BASE" ]; then
|
||||
HOSTUP_API_BASE="$HOSTUP_API_BASE_DEFAULT"
|
||||
fi
|
||||
HOSTUP_API_BASE="$(_hostup_normalize_api_base "$HOSTUP_API_BASE")"
|
||||
|
||||
if [ -z "$HOSTUP_API_KEY" ]; then
|
||||
HOSTUP_API_KEY=""
|
||||
_err "HOSTUP_API_KEY is not set."
|
||||
_err "Please export your HostUp API key with read:dns and write:dns scopes."
|
||||
_err "Please export your HostUp API key with read:dns, write:dns, and read:domains scopes."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_saveaccountconf_mutable HOSTUP_API_KEY "$HOSTUP_API_KEY"
|
||||
_saveaccountconf_mutable HOSTUP_API_BASE "$HOSTUP_API_BASE"
|
||||
|
||||
if [ -n "$HOSTUP_TTL" ]; then
|
||||
_saveaccountconf_mutable HOSTUP_TTL "$HOSTUP_TTL"
|
||||
fi
|
||||
|
||||
if [ -n "$HOSTUP_ZONE_ID" ]; then
|
||||
_saveaccountconf_mutable HOSTUP_ZONE_ID "$HOSTUP_ZONE_ID"
|
||||
fi
|
||||
|
|
@ -149,11 +139,80 @@ _hostup_init() {
|
|||
return 0
|
||||
}
|
||||
|
||||
_hostup_normalize_api_base() {
|
||||
api_base="${1%/}"
|
||||
|
||||
case "$api_base" in
|
||||
*/api/v2)
|
||||
printf "%s" "$api_base"
|
||||
;;
|
||||
*/api)
|
||||
printf "%s/v2" "$api_base"
|
||||
;;
|
||||
*)
|
||||
printf "%s" "$api_base"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_hostup_normalize_ttl() {
|
||||
ttl_value="$1"
|
||||
|
||||
case "$ttl_value" in
|
||||
"" | *[!0-9]*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
while [ "${ttl_value#0}" != "$ttl_value" ]; do
|
||||
ttl_value="${ttl_value#0}"
|
||||
done
|
||||
[ -z "$ttl_value" ] && ttl_value=0
|
||||
|
||||
case "$ttl_value" in
|
||||
??????*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$ttl_value" -lt 60 ] || [ "$ttl_value" -gt 86400 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf "%s" "$ttl_value"
|
||||
}
|
||||
|
||||
_hostup_domain_in_zone() {
|
||||
host="$(printf "%s" "${1%.}" | _lower_case)"
|
||||
zone="$(printf "%s" "${2%.}" | _lower_case)"
|
||||
|
||||
if [ -z "$host" ] || [ -z "$zone" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$host" = "$zone" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
case "$host" in
|
||||
*."$zone")
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_hostup_detect_zone() {
|
||||
fulldomain="$1"
|
||||
|
||||
if [ -n "$HOSTUP_ZONE_ID" ] && [ -n "$HOSTUP_ZONE_DOMAIN" ]; then
|
||||
return 0
|
||||
if _hostup_domain_in_zone "$fulldomain" "$HOSTUP_ZONE_DOMAIN"; then
|
||||
return 0
|
||||
fi
|
||||
_debug "hostup_cached_zone_mismatch" "$HOSTUP_ZONE_DOMAIN"
|
||||
HOSTUP_ZONE_ID=""
|
||||
HOSTUP_ZONE_DOMAIN=""
|
||||
fi
|
||||
|
||||
HOSTUP_ZONE_DOMAIN=""
|
||||
|
|
@ -162,16 +221,16 @@ _hostup_detect_zone() {
|
|||
if [ -n "$HOSTUP_ZONE_ID" ] && [ -z "$HOSTUP_ZONE_DOMAIN" ]; then
|
||||
# Attempt to fetch domain name for provided zone ID
|
||||
if _hostup_fetch_zone_details "$HOSTUP_ZONE_ID"; then
|
||||
return 0
|
||||
if _hostup_domain_in_zone "$fulldomain" "$HOSTUP_ZONE_DOMAIN"; then
|
||||
return 0
|
||||
fi
|
||||
_debug "hostup_forced_zone_mismatch" "$HOSTUP_ZONE_DOMAIN"
|
||||
fi
|
||||
HOSTUP_ZONE_ID=""
|
||||
HOSTUP_ZONE_DOMAIN=""
|
||||
fi
|
||||
|
||||
if ! _hostup_load_zones; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_domain_candidate="$(printf "%s" "$fulldomain" | _lower_case)"
|
||||
_domain_candidate="$(printf "%s" "${fulldomain%.}" | _lower_case)"
|
||||
_debug "hostup_initial_candidate" "$_domain_candidate"
|
||||
|
||||
while [ -n "$_domain_candidate" ]; do
|
||||
|
|
@ -240,11 +299,11 @@ _hostup_fqdn() {
|
|||
_hostup_fetch_zone_details() {
|
||||
zone_id="$1"
|
||||
|
||||
if ! _hostup_rest "GET" "/dns/zones/$zone_id/records" ""; then
|
||||
if ! _hostup_rest "GET" "/dns-zones/$zone_id/records" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
zonedomain="$(printf "%s" "$_hostup_response" | _egrep_o '"domain":"[^"]*"' | sed -n '1p' | cut -d ':' -f 2 | tr -d '"')"
|
||||
zonedomain="$(_hostup_json_extract "name" "$_hostup_response")"
|
||||
if [ -n "$zonedomain" ]; then
|
||||
HOSTUP_ZONE_DOMAIN="$zonedomain"
|
||||
return 0
|
||||
|
|
@ -254,7 +313,7 @@ _hostup_fetch_zone_details() {
|
|||
}
|
||||
|
||||
_hostup_load_zones() {
|
||||
if ! _hostup_rest "GET" "/dns/zones" ""; then
|
||||
if ! _hostup_rest "GET" "/dns-zones?limit=1000" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
|
@ -263,9 +322,9 @@ _hostup_load_zones() {
|
|||
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
*'"domain_id"'*'"domain"'*)
|
||||
zone_id="$(printf "%s" "$line" | _hostup_json_extract "domain_id")"
|
||||
zone_domain="$(printf "%s" "$line" | _hostup_json_extract "domain")"
|
||||
*'"id"'*'"name"'*)
|
||||
zone_id="$(_hostup_json_extract "id" "$line")"
|
||||
zone_domain="$(_hostup_json_extract "name" "$line")"
|
||||
if [ -n "$zone_id" ] && [ -n "$zone_domain" ]; then
|
||||
HOSTUP_ZONES_CACHE="${HOSTUP_ZONES_CACHE}${zone_domain}|${zone_id}
|
||||
"
|
||||
|
|
@ -290,9 +349,30 @@ _hostup_lookup_zone() {
|
|||
_lookup_zone_id=""
|
||||
_lookup_zone_domain=""
|
||||
|
||||
encoded_domain="$(printf "%s" "$lookup_domain" | _url_encode)"
|
||||
if _hostup_rest "GET" "/dns-zones?name=$encoded_domain&limit=1" ""; then
|
||||
zone_id="$(_hostup_json_extract "id" "$_hostup_response")"
|
||||
zone_domain="$(_hostup_json_extract "name" "$_hostup_response")"
|
||||
if [ -n "$zone_id" ] && [ -n "$zone_domain" ]; then
|
||||
zone_domain_lower="$(printf "%s" "$zone_domain" | _lower_case)"
|
||||
if [ "$zone_domain_lower" = "$lookup_domain" ]; then
|
||||
_lookup_zone_domain="$zone_domain"
|
||||
_lookup_zone_id="$zone_id"
|
||||
HOSTUP_ZONE_DOMAIN="$zone_domain"
|
||||
HOSTUP_ZONE_ID="$zone_id"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$HOSTUP_ZONES_CACHE" ] && ! _hostup_load_zones; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
while IFS='|' read -r domain zone_id; do
|
||||
[ -z "$domain" ] && continue
|
||||
if [ "$domain" = "$lookup_domain" ]; then
|
||||
domain_lower="$(printf "%s" "$domain" | _lower_case)"
|
||||
if [ "$domain_lower" = "$lookup_domain" ]; then
|
||||
_lookup_zone_domain="$domain"
|
||||
_lookup_zone_id="$zone_id"
|
||||
HOSTUP_ZONE_DOMAIN="$domain"
|
||||
|
|
@ -307,50 +387,50 @@ EOF
|
|||
}
|
||||
|
||||
_hostup_find_record() {
|
||||
zone_id="$1"
|
||||
fqdn="$2"
|
||||
txtvalue="$3"
|
||||
_hostup_find_zone_id="$1"
|
||||
_hostup_find_fqdn="$2"
|
||||
_hostup_find_txtvalue="$3"
|
||||
|
||||
if ! _hostup_rest "GET" "/dns/zones/$zone_id/records" ""; then
|
||||
_hostup_find_encoded_name="$(printf "%s" "$_hostup_find_fqdn" | _url_encode)"
|
||||
if ! _hostup_rest "GET" "/dns-zones/$_hostup_find_zone_id/records?type=TXT&name=$_hostup_find_encoded_name" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
HOSTUP_RECORD_ID=""
|
||||
records="$(printf "%s" "$_hostup_response" | tr '{' '\n')"
|
||||
_hostup_find_records="$(printf "%s" "$_hostup_response" | tr '{' '\n')"
|
||||
|
||||
while IFS= read -r line; do
|
||||
while IFS= read -r _hostup_find_line; do
|
||||
# Normalize line to make TXT value matching reliable
|
||||
line_clean="$(printf "%s" "$line" | tr -d '\r\n')"
|
||||
line_value_clean="$(printf "%s" "$line_clean" | sed 's/\\"//g')"
|
||||
_hostup_find_line_clean="$(printf "%s" "$_hostup_find_line" | tr -d '\r\n')"
|
||||
_hostup_find_line_value_clean="$(printf "%s" "$_hostup_find_line_clean" | sed 's/\\"//g')"
|
||||
|
||||
case "$line_clean" in
|
||||
*'"type":"TXT"'*'"name"'*'"value"'*)
|
||||
name_value="$(_hostup_json_extract "name" "$line_clean")"
|
||||
record_value="$(_hostup_json_extract "value" "$line_value_clean")"
|
||||
_hostup_find_record_type="$(_hostup_json_extract "type" "$_hostup_find_line_clean")"
|
||||
[ "$_hostup_find_record_type" != "TXT" ] && continue
|
||||
|
||||
_debug "hostup_record_raw" "$record_value"
|
||||
if [ "${record_value#\"}" != "$record_value" ] && [ "${record_value%\"}" != "$record_value" ]; then
|
||||
record_value="${record_value#\"}"
|
||||
record_value="${record_value%\"}"
|
||||
fi
|
||||
if [ "${record_value#\'}" != "$record_value" ] && [ "${record_value%\'}" != "$record_value" ]; then
|
||||
record_value="${record_value#\'}"
|
||||
record_value="${record_value%\'}"
|
||||
fi
|
||||
record_value="$(printf "%s" "$record_value" | tr -d '\r\n')"
|
||||
_debug "hostup_record_value" "$record_value"
|
||||
_hostup_find_name_value="$(_hostup_json_extract "name" "$_hostup_find_line_clean")"
|
||||
_hostup_find_record_value="$(_hostup_json_extract "value" "$_hostup_find_line_value_clean")"
|
||||
|
||||
if [ "$name_value" = "$fqdn" ] && [ "$record_value" = "$txtvalue" ]; then
|
||||
record_id="$(_hostup_json_extract "id" "$line_clean")"
|
||||
if [ -n "$record_id" ]; then
|
||||
HOSTUP_RECORD_ID="$record_id"
|
||||
return 0
|
||||
fi
|
||||
_debug "hostup_record_raw" "$_hostup_find_record_value"
|
||||
if [ "${_hostup_find_record_value#\"}" != "$_hostup_find_record_value" ] && [ "${_hostup_find_record_value%\"}" != "$_hostup_find_record_value" ]; then
|
||||
_hostup_find_record_value="${_hostup_find_record_value#\"}"
|
||||
_hostup_find_record_value="${_hostup_find_record_value%\"}"
|
||||
fi
|
||||
if [ "${_hostup_find_record_value#\'}" != "$_hostup_find_record_value" ] && [ "${_hostup_find_record_value%\'}" != "$_hostup_find_record_value" ]; then
|
||||
_hostup_find_record_value="${_hostup_find_record_value#\'}"
|
||||
_hostup_find_record_value="${_hostup_find_record_value%\'}"
|
||||
fi
|
||||
_hostup_find_record_value="$(printf "%s" "$_hostup_find_record_value" | tr -d '\r\n')"
|
||||
_debug "hostup_record_value" "$_hostup_find_record_value"
|
||||
|
||||
if [ "$_hostup_find_name_value" = "$_hostup_find_fqdn" ] && [ "$_hostup_find_record_value" = "$_hostup_find_txtvalue" ]; then
|
||||
_hostup_find_record_id="$(_hostup_json_extract "id" "$_hostup_find_line_clean")"
|
||||
if [ -n "$_hostup_find_record_id" ]; then
|
||||
HOSTUP_RECORD_ID="$_hostup_find_record_id"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done <<EOF
|
||||
$records
|
||||
$_hostup_find_records
|
||||
EOF
|
||||
|
||||
return 1
|
||||
|
|
@ -361,22 +441,22 @@ _hostup_json_extract() {
|
|||
input="${2:-$line}"
|
||||
|
||||
# First try to extract quoted values (strings)
|
||||
quoted_match="$(printf "%s" "$input" | _egrep_o "\"$key\":\"[^\"]*\"" | _head_n 1)"
|
||||
quoted_match="$(printf "%s" "$input" | _egrep_o "\"$key\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | _head_n 1)"
|
||||
if [ -n "$quoted_match" ]; then
|
||||
printf "%s" "$quoted_match" |
|
||||
cut -d : -f2- |
|
||||
sed 's/^"//' |
|
||||
sed 's/"$//' |
|
||||
sed 's/^[[:space:]]*"//' |
|
||||
sed 's/"[[:space:]]*$//' |
|
||||
sed 's/\\"/"/g'
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback for unquoted values (e.g., numeric IDs)
|
||||
unquoted_match="$(printf "%s" "$input" | _egrep_o "\"$key\":[^,}]*" | _head_n 1)"
|
||||
unquoted_match="$(printf "%s" "$input" | _egrep_o "\"$key\"[[:space:]]*:[[:space:]]*[^,}]*" | _head_n 1)"
|
||||
if [ -n "$unquoted_match" ]; then
|
||||
printf "%s" "$unquoted_match" |
|
||||
cut -d : -f2- |
|
||||
tr -d '", ' |
|
||||
tr -d '", ' |
|
||||
tr -d '\r\n'
|
||||
return 0
|
||||
fi
|
||||
|
|
@ -391,58 +471,56 @@ _hostup_json_escape() {
|
|||
_hostup_record_key() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
txtvalue="$3"
|
||||
safe_zone="$(printf "%s" "$zone_id" | sed 's/[^A-Za-z0-9]/_/g')"
|
||||
safe_domain="$(printf "%s" "$domain" | _lower_case | sed 's/[^a-z0-9]/_/g')"
|
||||
if [ -n "$txtvalue" ]; then
|
||||
safe_value="$(printf "%s" "$txtvalue" | sed 's/[^A-Za-z0-9]/_/g')"
|
||||
printf "%s_%s_%s" "$safe_zone" "$safe_domain" "$safe_value"
|
||||
return 0
|
||||
fi
|
||||
printf "%s_%s" "$safe_zone" "$safe_domain"
|
||||
}
|
||||
|
||||
_hostup_save_record_id() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
record_id="$3"
|
||||
key="$(_hostup_record_key "$zone_id" "$domain")"
|
||||
_saveaccountconf_mutable "HOSTUP_RECORD_$key" "$record_id"
|
||||
}
|
||||
|
||||
_hostup_get_saved_record_id() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
key="$(_hostup_record_key "$zone_id" "$domain")"
|
||||
_readaccountconf_mutable "HOSTUP_RECORD_$key"
|
||||
}
|
||||
|
||||
_hostup_clear_record_id() {
|
||||
zone_id="$1"
|
||||
domain="$2"
|
||||
key="$(_hostup_record_key "$zone_id" "$domain")"
|
||||
txtvalue="$3"
|
||||
key="$(_hostup_record_key "$zone_id" "$domain" "$txtvalue")"
|
||||
_clearaccountconf_mutable "HOSTUP_RECORD_$key"
|
||||
}
|
||||
|
||||
_hostup_extract_record_id() {
|
||||
record_id="$(_hostup_json_extract "id" "$1")"
|
||||
if [ -n "$record_id" ]; then
|
||||
printf "%s" "$record_id"
|
||||
return 0
|
||||
legacy_key="$(_hostup_record_key "$zone_id" "$domain")"
|
||||
if [ "$legacy_key" != "$key" ]; then
|
||||
_clearaccountconf_mutable "HOSTUP_RECORD_$legacy_key"
|
||||
fi
|
||||
|
||||
printf "%s" "$1" | _egrep_o '"id":[0-9]+' | _head_n 1 | cut -d: -f2
|
||||
}
|
||||
|
||||
_hostup_delete_record_by_id() {
|
||||
zone_id="$1"
|
||||
record_id="$2"
|
||||
|
||||
if ! _hostup_rest "DELETE" "/dns/zones/$zone_id/records/$record_id" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! _contains "$_hostup_response" '"success":true'; then
|
||||
if ! _hostup_rest "DELETE" "/dns-zones/$zone_id/records/$record_id" ""; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
_hostup_problem_error() {
|
||||
problem_code="$(_hostup_json_extract "code" "$_hostup_response")"
|
||||
problem_detail="$(_hostup_json_extract "detail" "$_hostup_response")"
|
||||
|
||||
if [ -n "$problem_detail" ]; then
|
||||
if [ -n "$problem_code" ]; then
|
||||
_err "HostUp API error ($problem_code): $problem_detail"
|
||||
else
|
||||
_err "HostUp API error: $problem_detail"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_hostup_rest() {
|
||||
method="$1"
|
||||
route="$2"
|
||||
|
|
@ -451,8 +529,7 @@ _hostup_rest() {
|
|||
_hostup_response=""
|
||||
|
||||
export _H1="Authorization: Bearer $HOSTUP_API_KEY"
|
||||
export _H2="Content-Type: application/json"
|
||||
export _H3="Accept: application/json"
|
||||
export _H2="Accept: application/json"
|
||||
|
||||
if [ "$method" = "GET" ]; then
|
||||
_hostup_response="$(_get "$HOSTUP_API_BASE$route")"
|
||||
|
|
@ -464,7 +541,6 @@ _hostup_rest() {
|
|||
|
||||
unset _H1
|
||||
unset _H2
|
||||
unset _H3
|
||||
|
||||
if [ "$ret" != "0" ]; then
|
||||
_err "HTTP request failed for $route"
|
||||
|
|
@ -478,23 +554,23 @@ _hostup_rest() {
|
|||
case "$http_status" in
|
||||
200 | 201 | 204) return 0 ;;
|
||||
401)
|
||||
_err "HostUp API returned 401 Unauthorized. Check HOSTUP_API_KEY scopes and IP restrictions."
|
||||
_hostup_problem_error || _err "HostUp API returned 401 Unauthorized. Check HOSTUP_API_KEY scopes and IP restrictions."
|
||||
return 1
|
||||
;;
|
||||
403)
|
||||
_err "HostUp API returned 403 Forbidden. The API key lacks required DNS scopes."
|
||||
_hostup_problem_error || _err "HostUp API returned 403 Forbidden. The API key lacks required DNS/domain scopes."
|
||||
return 1
|
||||
;;
|
||||
404)
|
||||
_err "HostUp API returned 404 Not Found for $route"
|
||||
_hostup_problem_error || _err "HostUp API returned 404 Not Found for $route"
|
||||
return 1
|
||||
;;
|
||||
429)
|
||||
_err "HostUp API rate limit exceeded. Please retry later."
|
||||
_hostup_problem_error || _err "HostUp API rate limit exceeded. Please retry later."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
_err "HostUp API request failed with status $http_status"
|
||||
_hostup_problem_error || _err "HostUp API request failed with status $http_status"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -85,12 +85,10 @@ dns_infomaniak_add() {
|
|||
|
||||
# API call
|
||||
response=$(_post "$data" "${INFOMANIAK_API_URL}/2/zones/${zone}/records")
|
||||
if [ -n "$response" ]; then
|
||||
if [ ! "$(echo "$response" | _contains '"result":"success"')" ]; then
|
||||
_info "Record added"
|
||||
_debug "response: $response"
|
||||
return 0
|
||||
fi
|
||||
if _contains "$response" '"result":"success"'; then
|
||||
_info "Record added"
|
||||
_debug "response: $response"
|
||||
return 0
|
||||
fi
|
||||
_err "Could not create record."
|
||||
_debug "Response: $response"
|
||||
|
|
@ -131,7 +129,7 @@ dns_infomaniak_rm() {
|
|||
fi
|
||||
|
||||
export _H1="Authorization: Bearer $INFOMANIAK_API_TOKEN"
|
||||
export _H2="ContentType: application/json"
|
||||
export _H2="Content-Type: application/json"
|
||||
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
|
|
@ -169,11 +167,10 @@ dns_infomaniak_rm() {
|
|||
|
||||
# API call
|
||||
response=$(_post "" "${INFOMANIAK_API_URL}/2/zones/${zone}/records/${record_id}" "" DELETE)
|
||||
if [ -n "$response" ]; then
|
||||
if [ ! "$(echo "$response" | _contains '"result":"success"')" ]; then
|
||||
_info "Record deleted"
|
||||
return 0
|
||||
fi
|
||||
if _contains "$response" '"result":"success"'; then
|
||||
_info "Record deleted"
|
||||
_debug "response: $response"
|
||||
return 0
|
||||
fi
|
||||
_err "Could not delete record."
|
||||
_debug "Response: $response"
|
||||
|
|
|
|||
|
|
@ -312,6 +312,22 @@ _get_root() {
|
|||
_domain="$h"
|
||||
return 0
|
||||
fi
|
||||
# IDN fallback: INWX returns Unicode zone names; when $h is ACE/punycode,
|
||||
# encode each zone name via _idn() and compare — no python dependency.
|
||||
if _contains "$h" "xn--"; then
|
||||
_zone_unicode=$(printf "%s" "$response" | _egrep_o '<string>[^<]*' |
|
||||
sed 's/<[^>]*>//g' | while IFS= read -r _z; do
|
||||
if [ "$(_idn "$_z")" = "$h" ]; then
|
||||
printf "%s" "$_z"
|
||||
break
|
||||
fi
|
||||
done)
|
||||
if [ -n "$_zone_unicode" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||
_domain="$_zone_unicode"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ IONOS_TXT_TTL=60 # minimum accepted by API
|
|||
IONOS_TXT_PRIO=10
|
||||
|
||||
dns_ionos_add() {
|
||||
fulldomain=$1
|
||||
fulldomain="$(echo "$1" | _lower_case)"
|
||||
txtvalue=$2
|
||||
|
||||
if ! _ionos_init; then
|
||||
|
|
@ -34,7 +34,7 @@ dns_ionos_add() {
|
|||
}
|
||||
|
||||
dns_ionos_rm() {
|
||||
fulldomain=$1
|
||||
fulldomain="$(echo "$1" | _lower_case)"
|
||||
txtvalue=$2
|
||||
|
||||
if ! _ionos_init; then
|
||||
|
|
@ -146,7 +146,7 @@ _ionos_rest() {
|
|||
|
||||
if [ "$method" != "GET" ]; then
|
||||
export _H2="Accept: application/json"
|
||||
export _H3="Content-Type: application/json"
|
||||
export _H3=
|
||||
|
||||
_response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -35,9 +35,28 @@ dns_joker_add() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
# Joker's /nic/replace overwrites all TXT records at the label on every call,
|
||||
# and the API is not readable, so accumulate the values locally (keyed by the
|
||||
# full record name) and re-send the whole set each time. This is required so a
|
||||
# wildcard cert (base + *.domain both validating under the same
|
||||
# _acme-challenge label) does not overwrite its own first challenge value.
|
||||
_joker_conf_key=$(printf "%s" "JOKER_TXT_${fulldomain}" | tr '.-' '_')
|
||||
_joker_values=$(_readdomainconf "$_joker_conf_key")
|
||||
if [ -z "$_joker_values" ]; then
|
||||
_joker_values="$txtvalue"
|
||||
elif ! _contains " $_joker_values " " $txtvalue "; then
|
||||
_joker_values="$_joker_values $txtvalue"
|
||||
fi
|
||||
|
||||
_joker_value_params=""
|
||||
for _joker_v in $_joker_values; do
|
||||
_joker_value_params="$_joker_value_params&value=$_joker_v"
|
||||
done
|
||||
|
||||
_info "Adding TXT record"
|
||||
if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT&value=$txtvalue"; then
|
||||
if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT$_joker_value_params"; then
|
||||
if _startswith "$response" "OK"; then
|
||||
_savedomainconf "$_joker_conf_key" "$_joker_values"
|
||||
_info "Added, OK"
|
||||
return 0
|
||||
fi
|
||||
|
|
@ -59,10 +78,36 @@ dns_joker_rm() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
# Remove only this value from the accumulated set and replace the label with
|
||||
# whatever remains (an empty value clears the label's TXT records entirely).
|
||||
_joker_conf_key=$(printf "%s" "JOKER_TXT_${fulldomain}" | tr '.-' '_')
|
||||
_joker_values=$(_readdomainconf "$_joker_conf_key")
|
||||
_joker_remaining=""
|
||||
for _joker_v in $_joker_values; do
|
||||
if [ "$_joker_v" != "$txtvalue" ]; then
|
||||
_joker_remaining="$_joker_remaining $_joker_v"
|
||||
fi
|
||||
done
|
||||
_joker_remaining=$(printf "%s" "$_joker_remaining" | sed 's/^ *//')
|
||||
|
||||
_joker_value_params=""
|
||||
for _joker_v in $_joker_remaining; do
|
||||
_joker_value_params="$_joker_value_params&value=$_joker_v"
|
||||
done
|
||||
if [ -z "$_joker_value_params" ]; then
|
||||
_joker_value_params="&value="
|
||||
fi
|
||||
|
||||
_info "Removing TXT record"
|
||||
# TXT record is removed by setting its value to empty.
|
||||
if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT&value="; then
|
||||
# TXT record is removed by replacing the label with the remaining values
|
||||
# (or an empty value, which clears all TXT records at the label).
|
||||
if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT$_joker_value_params"; then
|
||||
if _startswith "$response" "OK"; then
|
||||
if [ -z "$_joker_remaining" ]; then
|
||||
_cleardomainconf "$_joker_conf_key"
|
||||
else
|
||||
_savedomainconf "$_joker_conf_key" "$_joker_remaining"
|
||||
fi
|
||||
_info "Removed, OK"
|
||||
return 0
|
||||
fi
|
||||
|
|
|
|||
197
dnsapi/dns_level27.sh
Normal file
197
dnsapi/dns_level27.sh
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_level27_info='Level27
|
||||
Site: Level27.be
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_level27
|
||||
Options:
|
||||
LEVEL27_API_KEY API key. Get one from the Level27 control panel (https://app.level27.eu/account/profile/security).
|
||||
OptionsAlt:
|
||||
LEVEL27_API API base URL. Optional. Default "https://api.level27.eu/v1".
|
||||
Issues: github.com/acmesh-official/acme.sh/issues
|
||||
Author: Jeroen Moors <jeroen.moors@level27.be>
|
||||
'
|
||||
|
||||
LEVEL27_API_DEFAULT="https://api.level27.eu/v1"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
# Usage: dns_level27_add _acme-challenge.www.example.com "TXT-value"
|
||||
dns_level27_add() {
|
||||
fulldomain="$(_idn "$1")"
|
||||
txtvalue="$2"
|
||||
|
||||
_info "Using Level27 to add a TXT record for $fulldomain"
|
||||
|
||||
if ! _level27_init; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "Could not determine the root zone for $fulldomain at Level27."
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_level27_data="{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\"}"
|
||||
if ! _level27_rest POST "domains/$_domain_id/records" "$_level27_data"; then
|
||||
_err "Could not add the TXT record."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if _contains "$response" "\"id\":"; then
|
||||
_info "TXT record added."
|
||||
return 0
|
||||
fi
|
||||
|
||||
_err "Unexpected response while adding the TXT record."
|
||||
return 1
|
||||
}
|
||||
|
||||
# Usage: dns_level27_rm _acme-challenge.www.example.com "TXT-value"
|
||||
dns_level27_rm() {
|
||||
fulldomain="$(_idn "$1")"
|
||||
txtvalue="$2"
|
||||
|
||||
_info "Using Level27 to remove the TXT record for $fulldomain"
|
||||
|
||||
if ! _level27_init; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "Could not determine the root zone for $fulldomain at Level27."
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
if ! _level27_rest GET "domains/$_domain_id/records?type=TXT"; then
|
||||
_err "Could not list the existing TXT records."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_record_id="$(_level27_find_record_id "$response" "$txtvalue")"
|
||||
if [ -z "$_record_id" ]; then
|
||||
_info "No matching TXT record found; nothing to remove."
|
||||
return 0
|
||||
fi
|
||||
_debug _record_id "$_record_id"
|
||||
|
||||
if ! _level27_rest DELETE "domains/$_domain_id/records/$_record_id"; then
|
||||
_err "Could not remove the TXT record."
|
||||
return 1
|
||||
fi
|
||||
|
||||
_info "TXT record removed."
|
||||
return 0
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
# Reads and validates the API credentials and endpoint, and stores them for renewals.
|
||||
_level27_init() {
|
||||
LEVEL27_API_KEY="${LEVEL27_API_KEY:-$(_readaccountconf_mutable LEVEL27_API_KEY)}"
|
||||
if [ -z "$LEVEL27_API_KEY" ]; then
|
||||
LEVEL27_API_KEY=""
|
||||
_err "You must export the variable LEVEL27_API_KEY before using the Level27 DNS API."
|
||||
_err "Get an API key from the Level27 control panel (https://app.level27.eu/account/profile/security)."
|
||||
return 1
|
||||
fi
|
||||
LEVEL27_API_KEY="$(echo "$LEVEL27_API_KEY" | tr -d '"')"
|
||||
_saveaccountconf_mutable LEVEL27_API_KEY "$LEVEL27_API_KEY"
|
||||
|
||||
LEVEL27_API="${LEVEL27_API:-$(_readaccountconf_mutable LEVEL27_API)}"
|
||||
if [ -z "$LEVEL27_API" ]; then
|
||||
LEVEL27_API="$LEVEL27_API_DEFAULT"
|
||||
fi
|
||||
_saveaccountconf_mutable LEVEL27_API "$LEVEL27_API"
|
||||
|
||||
# Remove a trailing slash so endpoints can be appended consistently.
|
||||
LEVEL27_API="$(echo "$LEVEL27_API" | sed 's#/$##')"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Usage: _get_root _acme-challenge.www.example.com
|
||||
# Splits the full domain into the registered zone and the subdomain part.
|
||||
# Sets: _domain, _domain_id, _sub_domain
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=1
|
||||
p=1
|
||||
|
||||
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 ! _level27_rest GET "domains?filter=$h"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
_level27_zones="$(echo "$response" | _normalizeJson)"
|
||||
if _contains "$_level27_zones" "\"fullname\":\"$h\""; then
|
||||
_domain_line="$(echo "$_level27_zones" | sed 's/},{/}\n{/g' | grep "\"fullname\":\"$h\"" | _head_n 1)"
|
||||
_domain_id="$(echo "$_domain_line" | _egrep_o '"id":[0-9]*' | _head_n 1 | cut -d : -f 2)"
|
||||
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
|
||||
}
|
||||
|
||||
# Usage: _level27_find_record_id "<records-json>" "<txtvalue>"
|
||||
# Prints the id of the TXT record whose content matches the value, or nothing.
|
||||
_level27_find_record_id() {
|
||||
_records="$(echo "$1" | _normalizeJson | sed 's/},{/}\n{/g')"
|
||||
_wanted="$2"
|
||||
_record_line="$(echo "$_records" | grep "\"content\":\"$_wanted\"" | _head_n 1)"
|
||||
if [ -z "$_record_line" ]; then
|
||||
# Some APIs store TXT content wrapped in quotes.
|
||||
_record_line="$(echo "$_records" | grep "\"content\":\"\\\\\"$_wanted\\\\\"\"" | _head_n 1)"
|
||||
fi
|
||||
if [ -z "$_record_line" ]; then
|
||||
return 0
|
||||
fi
|
||||
echo "$_record_line" | _egrep_o '"id":[0-9]*' | _head_n 1 | cut -d : -f 2
|
||||
}
|
||||
|
||||
# Usage: _level27_rest <method> <endpoint> [data]
|
||||
# Performs an authenticated API call and stores the body in $response.
|
||||
_level27_rest() {
|
||||
m="$1"
|
||||
ep="$2"
|
||||
data="$3"
|
||||
_debug "$ep"
|
||||
|
||||
export _H1="Authorization: $LEVEL27_API_KEY"
|
||||
export _H2="Content-Type: application/json"
|
||||
export _H3="Accept: application/json"
|
||||
|
||||
if [ "$m" != "GET" ]; then
|
||||
_debug2 data "$data"
|
||||
response="$(_post "$data" "$LEVEL27_API/$ep" "" "$m")"
|
||||
else
|
||||
response="$(_get "$LEVEL27_API/$ep")"
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
_err "Error querying the Level27 API endpoint: $ep"
|
||||
return 1
|
||||
fi
|
||||
_debug2 response "$response"
|
||||
return 0
|
||||
}
|
||||
|
|
@ -264,8 +264,16 @@ _set_namecheap_TXT() {
|
|||
_debug hosts "$hosts"
|
||||
|
||||
if [ -z "$hosts" ]; then
|
||||
_err "Hosts not found"
|
||||
return 1
|
||||
# An empty host list is only acceptable when the API positively confirms
|
||||
# a successful getHosts reply: setHosts below REPLACES all records, so
|
||||
# proceeding on a malformed/unparsed response would wipe the whole zone.
|
||||
# https://github.com/acmesh-official/acme.sh/issues/6963
|
||||
if _contains "$response" "Status=\"OK\"" && _contains "$response" "DomainDNSGetHostsResult"; then
|
||||
_debug "No existing host records, adding the TXT record as the first one"
|
||||
else
|
||||
_err "Hosts not found"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
_namecheap_reset_hostList
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ dns_namesilo_rm() {
|
|||
if _namesilo_rest GET "dnsListRecords?version=1&type=xml&key=$Namesilo_Key&domain=$_domain"; then
|
||||
retcode=$(printf "%s\n" "$response" | _egrep_o "<code>300")
|
||||
if [ "$retcode" ]; then
|
||||
_record_id=$(echo "$response" | _egrep_o "<record_id>([^<]*)</record_id><type>TXT</type><host>$fulldomain</host>" | _egrep_o "<record_id>([^<]*)</record_id>" | sed -r "s/<record_id>([^<]*)<\/record_id>/\1/" | tail -n 1)
|
||||
_record_id=$(echo "$response" | _egrep_o "<record_id>([^<]*)</record_id><type>TXT</type><host>$_sub_domain</host><value>$txtvalue</value>" | _egrep_o "<record_id>([^<]*)</record_id>" | sed -r "s/<record_id>([^<]*)<\/record_id>/\1/" | tail -n 1)
|
||||
_debug _record_id "$_record_id"
|
||||
if [ "$_record_id" ]; then
|
||||
_info "Successfully retrieved the record id for ACME challenge."
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ dns_selfhost_add() {
|
|||
_debug fulldomain "$fulldomain"
|
||||
_debug txtvalue "$txt"
|
||||
|
||||
SELFHOSTDNS_UPDATE_URL="https://selfhost.de/cgi-bin/api.pl"
|
||||
SELFHOSTDNS_UPDATE_URL="https://account.selfhost.de/cgi-bin/api.pl"
|
||||
|
||||
# Get values, but don't save until we successfully validated
|
||||
SELFHOSTDNS_USERNAME="${SELFHOSTDNS_USERNAME:-$(_readaccountconf_mutable SELFHOSTDNS_USERNAME)}"
|
||||
|
|
|
|||
297
dnsapi/dns_volcengine.sh
Executable file
297
dnsapi/dns_volcengine.sh
Executable file
|
|
@ -0,0 +1,297 @@
|
|||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034
|
||||
dns_volcengine_info='Volcano Engine DNS API
|
||||
Site: https://www.volcengine.com/docs/6758/155086
|
||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_volcengine
|
||||
Options:
|
||||
Volcengine_ACCESS_KEY_ID API Key ID
|
||||
Volcengine_SECRET_ACCESS_KEY API Secret
|
||||
Volcengine_SESSION_TOKEN Session Token. Optional, only needed when using temporary STS credentials.
|
||||
Issues: github.com/acmesh-official/acme.sh/issues/7064
|
||||
'
|
||||
|
||||
Volcengine_HOST="dns.volcengineapi.com"
|
||||
Volcengine_URL="https://$Volcengine_HOST"
|
||||
|
||||
######## Public functions #####################
|
||||
|
||||
#fulldomain txtvalue
|
||||
dns_volcengine_add() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_record_id=""
|
||||
|
||||
Volcengine_ACCESS_KEY_ID="${Volcengine_ACCESS_KEY_ID:-$(_readaccountconf_mutable Volcengine_ACCESS_KEY_ID)}"
|
||||
Volcengine_SECRET_ACCESS_KEY="${Volcengine_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable Volcengine_SECRET_ACCESS_KEY)}"
|
||||
|
||||
if [ -z "$Volcengine_ACCESS_KEY_ID" ] || [ -z "$Volcengine_SECRET_ACCESS_KEY" ]; then
|
||||
Volcengine_ACCESS_KEY_ID=""
|
||||
Volcengine_SECRET_ACCESS_KEY=""
|
||||
_err "You haven't specified the volcengine dns api key id and api key secret yet."
|
||||
return 1
|
||||
fi
|
||||
|
||||
#save the api key and email to the account conf file.
|
||||
_saveaccountconf_mutable Volcengine_ACCESS_KEY_ID "$Volcengine_ACCESS_KEY_ID"
|
||||
_saveaccountconf_mutable Volcengine_SECRET_ACCESS_KEY "$Volcengine_SECRET_ACCESS_KEY"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
_sleep 1
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
# _info "Getting existing records for $fulldomain"
|
||||
if ! volcengine_rest POST "" "Action=ListRecords&Version=2018-08-01" "{\"ZID\":$_domain_id,\"Host\":\"$_sub_domain\",\"Type\":\"TXT\",\"Value\":\"$txtvalue\",\"SearchMode\":\"exact\"}"; then
|
||||
_sleep 1
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ListRecords already filtered by ZID + Host + Value + SearchMode:exact,
|
||||
# so any returned record is our target. Don't match on FQDN: Volcengine
|
||||
# lowercases the Host/FQDN in the response, which would break a
|
||||
# case-sensitive string compare against $fulldomain.
|
||||
_record_id="$(echo "$response" | _egrep_o "\"RecordID\":\"[0-9]+\"," | cut -d: -f2 | cut -d, -f1 | tr -d '"')"
|
||||
_debug "_record_id" "$_record_id"
|
||||
|
||||
if [ "$_record_id" ] && _contains "$response" "$txtvalue"; then
|
||||
_info "The TXT record already exists. Skipping."
|
||||
_sleep 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
_debug "Adding records"
|
||||
|
||||
if volcengine_rest POST "" "Action=CreateRecord&Version=2018-08-01" "{\"ZID\":$_domain_id,\"Host\":\"$_sub_domain\",\"Type\":\"TXT\",\"Value\":\"$txtvalue\",\"Remark\":\"acme.sh\"}"; then
|
||||
_info "TXT record updated successfully."
|
||||
_sleep 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
_sleep 1
|
||||
return 1
|
||||
}
|
||||
|
||||
#fulldomain txtvalue
|
||||
dns_volcengine_rm() {
|
||||
fulldomain=$1
|
||||
txtvalue=$2
|
||||
_record_id=""
|
||||
|
||||
Volcengine_ACCESS_KEY_ID="${Volcengine_ACCESS_KEY_ID:-$(_readaccountconf_mutable Volcengine_ACCESS_KEY_ID)}"
|
||||
Volcengine_SECRET_ACCESS_KEY="${Volcengine_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable Volcengine_SECRET_ACCESS_KEY)}"
|
||||
|
||||
_debug "First detect the root zone"
|
||||
if ! _get_root "$fulldomain"; then
|
||||
_err "invalid domain"
|
||||
_sleep 1
|
||||
return 1
|
||||
fi
|
||||
_debug _domain_id "$_domain_id"
|
||||
_debug _sub_domain "$_sub_domain"
|
||||
_debug _domain "$_domain"
|
||||
|
||||
_info "Getting existing records for $fulldomain"
|
||||
|
||||
if ! volcengine_rest POST "" "Action=ListRecords&Version=2018-08-01" "{\"ZID\":$_domain_id,\"Host\":\"$_sub_domain\",\"Type\":\"TXT\",\"Value\":\"$txtvalue\",\"SearchMode\":\"exact\"}"; then
|
||||
_sleep 1
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ListRecords already filtered by ZID + Host + Value + SearchMode:exact,
|
||||
# so any returned record is our target. Don't match on FQDN: Volcengine
|
||||
# lowercases the Host/FQDN in the response, which would break a
|
||||
# case-sensitive string compare against $fulldomain.
|
||||
_record_id="$(echo "$response" | _egrep_o "\"RecordID\":\"[0-9]+\"," | cut -d: -f2 | cut -d, -f1 | tr -d '"')"
|
||||
_debug "_record_id" "$_record_id"
|
||||
|
||||
if [ -z "$_record_id" ]; then
|
||||
_debug "no records exist, skip"
|
||||
_sleep 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
if volcengine_rest POST "" "Action=DeleteRecord&Version=2018-08-01" "{\"RecordID\":\"$_record_id\"}"; then
|
||||
_info "TXT record deleted successfully."
|
||||
_sleep 1
|
||||
return 0
|
||||
fi
|
||||
_sleep 1
|
||||
return 1
|
||||
}
|
||||
|
||||
#################### Private functions below ##################################
|
||||
|
||||
_get_root() {
|
||||
domain=$1
|
||||
i=1
|
||||
p=1
|
||||
|
||||
# iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
|
||||
while true; do
|
||||
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||
_debug "Checking domain: $h"
|
||||
if [ -z "$h" ]; then
|
||||
_err "invalid domain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# iterate over paginated result for list_hosted_zones
|
||||
if ! volcengine_rest POST "" "Action=ListZones&Version=2018-08-01" "{\"Key\":\"$h\",\"SearchMode\":\"exact\"}"; then
|
||||
return 1
|
||||
fi
|
||||
if _contains "$response" "\"ZoneName\":\"$h\""; then
|
||||
_domain_id=$(printf "%s" "$response" | _egrep_o "\"ZID\":[0-9]+," | cut -d: -f2 | cut -d, -f1)
|
||||
if [ "$_domain_id" ]; then
|
||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||
_domain=$h
|
||||
return 0
|
||||
fi
|
||||
_err "Can't find domain with id: $h"
|
||||
return 1
|
||||
fi
|
||||
p=$i
|
||||
i=$(_math "$i" + 1)
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
#method uri qstr data
|
||||
volcengine_rest() {
|
||||
mtd="$1"
|
||||
ep="$2"
|
||||
qsr="$3"
|
||||
data="$4"
|
||||
|
||||
_debug mtd "$mtd"
|
||||
_debug ep "$ep"
|
||||
_debug qsr "$qsr"
|
||||
_debug data "$data"
|
||||
|
||||
# clear any header state left over from a previous request so that
|
||||
# conditionally-set headers (e.g. x-content-sha256, x-security-token)
|
||||
# can't leak into the next request
|
||||
_H1=""
|
||||
_H2=""
|
||||
_H3=""
|
||||
_H4=""
|
||||
_H5=""
|
||||
|
||||
CanonicalURI="/$ep"
|
||||
_debug2 CanonicalURI "$CanonicalURI"
|
||||
|
||||
CanonicalQueryString="$qsr"
|
||||
_debug2 CanonicalQueryString "$CanonicalQueryString"
|
||||
|
||||
RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
|
||||
_debug2 RequestDate "$RequestDate"
|
||||
|
||||
Hash="sha256"
|
||||
|
||||
_H1="X-Date: $RequestDate"
|
||||
_debug2 _H1 "$_H1"
|
||||
|
||||
volcengine_host="$Volcengine_HOST"
|
||||
CanonicalHeaders="host:$volcengine_host\n"
|
||||
SignedHeaders="host"
|
||||
|
||||
if [ -n "$data" ]; then
|
||||
XContentSha256="$(printf "%s" "$data" | _digest "$Hash" hex)"
|
||||
_H4="x-content-sha256: $XContentSha256"
|
||||
_debug2 _H4 "$_H4"
|
||||
|
||||
CanonicalHeaders="${CanonicalHeaders}x-content-sha256:$XContentSha256\n"
|
||||
SignedHeaders="${SignedHeaders};x-content-sha256"
|
||||
fi
|
||||
|
||||
CanonicalHeaders="${CanonicalHeaders}x-date:$RequestDate\n"
|
||||
SignedHeaders="${SignedHeaders};x-date"
|
||||
|
||||
if [ -n "$Volcengine_SESSION_TOKEN" ]; then
|
||||
_H3="x-security-token: $Volcengine_SESSION_TOKEN"
|
||||
CanonicalHeaders="${CanonicalHeaders}x-security-token:$Volcengine_SESSION_TOKEN\n"
|
||||
SignedHeaders="${SignedHeaders};x-security-token"
|
||||
fi
|
||||
|
||||
_debug2 CanonicalHeaders "$CanonicalHeaders"
|
||||
_debug2 SignedHeaders "$SignedHeaders"
|
||||
|
||||
RequestPayload="$data"
|
||||
_debug2 RequestPayload "$RequestPayload"
|
||||
|
||||
CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
|
||||
_debug2 CanonicalRequest "$CanonicalRequest"
|
||||
|
||||
HashedCanonicalRequest="$(printf '%b' "$CanonicalRequest" | _digest "$Hash" hex)"
|
||||
_debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
|
||||
|
||||
Algorithm="HMAC-SHA256"
|
||||
_debug2 Algorithm "$Algorithm"
|
||||
|
||||
RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
|
||||
_debug2 RequestDateOnly "$RequestDateOnly"
|
||||
|
||||
Region="cn-beijing"
|
||||
Service="dns"
|
||||
|
||||
CredentialScope="$RequestDateOnly/$Region/$Service/request"
|
||||
_debug2 CredentialScope "$CredentialScope"
|
||||
|
||||
StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
|
||||
|
||||
_debug2 StringToSign "$StringToSign"
|
||||
|
||||
kSecret="$Volcengine_SECRET_ACCESS_KEY"
|
||||
|
||||
_secure_debug2 kSecret "$kSecret"
|
||||
|
||||
kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
|
||||
_secure_debug2 kSecretH "$kSecretH"
|
||||
|
||||
kDateH="$(printf "%s" "$RequestDateOnly" | _hmac "$Hash" "$kSecretH" hex)"
|
||||
_debug2 kDateH "$kDateH"
|
||||
|
||||
kRegionH="$(printf "%s" "$Region" | _hmac "$Hash" "$kDateH" hex)"
|
||||
_debug2 kRegionH "$kRegionH"
|
||||
|
||||
kServiceH="$(printf "%s" "$Service" | _hmac "$Hash" "$kRegionH" hex)"
|
||||
_debug2 kServiceH "$kServiceH"
|
||||
|
||||
kSigningH="$(printf "%s" "request" | _hmac "$Hash" "$kServiceH" hex)"
|
||||
_debug2 kSigningH "$kSigningH"
|
||||
|
||||
signature="$(printf '%b' "$StringToSign" | _hmac "$Hash" "$kSigningH" hex)"
|
||||
_debug2 signature "$signature"
|
||||
|
||||
Authorization="$Algorithm Credential=$Volcengine_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
|
||||
_debug2 Authorization "$Authorization"
|
||||
|
||||
_H2="Authorization: $Authorization"
|
||||
_debug2 _H2 "$_H2"
|
||||
|
||||
url="$Volcengine_URL/$ep"
|
||||
if [ "$qsr" ]; then
|
||||
url="$Volcengine_URL/$ep?$qsr"
|
||||
fi
|
||||
|
||||
if [ "$mtd" = "GET" ]; then
|
||||
response="$(_get "$url")"
|
||||
else
|
||||
response="$(_post "$data" "$url" "" "POST" "application/json")"
|
||||
fi
|
||||
|
||||
_ret="$?"
|
||||
_debug response "$response"
|
||||
if [ "$_ret" = "0" ]; then
|
||||
if _contains "$response" "\"Error\":{"; then
|
||||
_err "Response error:$response"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return "$_ret"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue