@@ -661,9 +661,65 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Haiku:
|
Tribblix:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: OpenIndiana
|
needs: OpenIndiana
|
||||||
|
env:
|
||||||
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
|
||||||
|
TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
|
||||||
|
TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
|
||||||
|
CASE: le_test_dnsapi
|
||||||
|
TEST_LOCAL: 1
|
||||||
|
DEBUG: ${{ secrets.DEBUG }}
|
||||||
|
http_proxy: ${{ secrets.http_proxy }}
|
||||||
|
https_proxy: ${{ secrets.https_proxy }}
|
||||||
|
HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Tribblix doesn't accept the expired ISRG X1 root
|
||||||
|
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/tribblix-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 HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
|
||||||
|
sync: nfs
|
||||||
|
prepare: zap install socat
|
||||||
|
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"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Haiku:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: Tribblix
|
||||||
env:
|
env:
|
||||||
TEST_DNS : ${{ secrets.TEST_DNS }}
|
TEST_DNS : ${{ secrets.TEST_DNS }}
|
||||||
TestingDomain: ${{ secrets.TestingDomain }}
|
TestingDomain: ${{ secrets.TestingDomain }}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
name: Tribblix
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Tribblix.yml'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- '*.sh'
|
||||||
|
- '.github/workflows/Tribblix.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Tribblix:
|
||||||
|
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
|
||||||
|
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/tribblix-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: zap install socat curl wget
|
||||||
|
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"
|
||||||
@@ -41,6 +41,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: CheckToken
|
needs: CheckToken
|
||||||
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: checkout code
|
- name: checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
@@ -58,6 +61,9 @@ jobs:
|
|||||||
- name: login to docker hub
|
- name: login to docker hub
|
||||||
run: |
|
run: |
|
||||||
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||||
|
- name: login to ghcr
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||||
- name: build and push the image
|
- name: build and push the image
|
||||||
run: |
|
run: |
|
||||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||||
@@ -73,6 +79,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "DOCKER_IMAGE_TAG=${DOCKER_IMAGE_TAG}" >>"$GITHUB_ENV"
|
||||||
|
|
||||||
DOCKER_LABELS=()
|
DOCKER_LABELS=()
|
||||||
while read -r label; do
|
while read -r label; do
|
||||||
DOCKER_LABELS+=(--label "${label}")
|
DOCKER_LABELS+=(--label "${label}")
|
||||||
@@ -84,3 +92,9 @@ jobs:
|
|||||||
--output "type=image,push=true" \
|
--output "type=image,push=true" \
|
||||||
--build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
|
--build-arg AUTO_UPGRADE=${AUTO_UPGRADE} \
|
||||||
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .
|
--platform linux/arm64/v8,linux/amd64,linux/arm/v6,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x .
|
||||||
|
- name: mirror the image to ghcr (best-effort)
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create \
|
||||||
|
--tag ghcr.io/${{ github.repository }}:${DOCKER_IMAGE_TAG} \
|
||||||
|
${DOCKER_IMAGE}:${DOCKER_IMAGE_TAG} \
|
||||||
|
|| echo "::warning::GHCR mirror failed; Docker Hub publish unaffected"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<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/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/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/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/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>
|
||||||
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Haiku.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Haiku.yml/badge.svg" alt="Haiku"></a>
|
<a href="https://github.com/acmesh-official/acme.sh/actions/workflows/Haiku.yml"><img src="https://github.com/acmesh-official/acme.sh/actions/workflows/Haiku.yml/badge.svg" alt="Haiku"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -112,6 +113,7 @@
|
|||||||
|23|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|
|23|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
|
||||||
|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)
|
|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
|
|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
|
||||||
|
|
||||||
|
|
||||||
> 🧪 Check our [testing project](https://github.com/acmesh-official/acmetest)
|
> 🧪 Check our [testing project](https://github.com/acmesh-official/acmetest)
|
||||||
|
|||||||
@@ -4895,7 +4895,7 @@ issue() {
|
|||||||
# (Let's Encrypt) may also reject with a malformed error if the prior cert
|
# (Let's Encrypt) may also reject with a malformed error if the prior cert
|
||||||
# was issued by a different issuer / different CA. Retry without "replaces"
|
# was issued by a different issuer / different CA. Retry without "replaces"
|
||||||
# whenever the failure mentions ARI or the replaces field.
|
# whenever the failure mentions ARI or the replaces field.
|
||||||
if [ "$_replaces_certID" ] && { _contains "$response" "alreadyReplaced" || _contains "$response" "'replaces'" || _contains "$response" "ARI"; }; then
|
if [ "$_replaces_certID" ] && { _contains "$response" "alreadyReplaced" || _contains "$response" "urn:ietf:params:acme:error:malformed" || _contains "$response" "'replaces'" || _contains "$response" "ARI"; }; then
|
||||||
_info "ARI 'replaces' rejected by CA, retrying newOrder without 'replaces'."
|
_info "ARI 'replaces' rejected by CA, retrying newOrder without 'replaces'."
|
||||||
if ! _send_signed_request "$ACME_NEW_ORDER" "$_newOrderObj}"; then
|
if ! _send_signed_request "$ACME_NEW_ORDER" "$_newOrderObj}"; then
|
||||||
_err "Error creating new order."
|
_err "Error creating new order."
|
||||||
@@ -5785,7 +5785,7 @@ renew() {
|
|||||||
_debug "_renewServer" "$_renewServer"
|
_debug "_renewServer" "$_renewServer"
|
||||||
|
|
||||||
_initpath "$Le_Domain" "$_isEcc"
|
_initpath "$Le_Domain" "$_isEcc"
|
||||||
|
_info "Renew: $Le_Domain"
|
||||||
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
|
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
|
||||||
_info "$(__green "Renewing: '$Le_Domain'")"
|
_info "$(__green "Renewing: '$Le_Domain'")"
|
||||||
if [ ! -f "$DOMAIN_CONF" ]; then
|
if [ ! -f "$DOMAIN_CONF" ]; then
|
||||||
@@ -6865,7 +6865,7 @@ deactivate() {
|
|||||||
#cert
|
#cert
|
||||||
_getAKI() {
|
_getAKI() {
|
||||||
_cert="$1"
|
_cert="$1"
|
||||||
${ACME_OPENSSL_BIN:-openssl} x509 -in "$_cert" -text -noout | grep "X509v3 Authority Key Identifier" -A 1 | _tail_n 1 | tr -d ': ' | sed "s/keyid//"
|
${ACME_OPENSSL_BIN:-openssl} x509 -in "$_cert" -text -noout | grep -A 1 "X509v3 Authority Key Identifier" | _tail_n 1 | tr -d ': ' | sed "s/keyid//"
|
||||||
}
|
}
|
||||||
|
|
||||||
#cert
|
#cert
|
||||||
|
|||||||
@@ -1,256 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
dns_hetzner_info='Hetzner.com
|
|
||||||
Site: Hetzner.com
|
|
||||||
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_hetzner
|
|
||||||
Options:
|
|
||||||
HETZNER_Token API Token
|
|
||||||
Issues: github.com/acmesh-official/acme.sh/issues/2943
|
|
||||||
'
|
|
||||||
|
|
||||||
HETZNER_Api="https://dns.hetzner.com/api/v1"
|
|
||||||
|
|
||||||
######## Public functions #####################
|
|
||||||
|
|
||||||
# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
|
||||||
# Used to add txt record
|
|
||||||
# Ref: https://dns.hetzner.com/api-docs/
|
|
||||||
dns_hetzner_add() {
|
|
||||||
full_domain=$1
|
|
||||||
txt_value=$2
|
|
||||||
|
|
||||||
HETZNER_Token="${HETZNER_Token:-$(_readaccountconf_mutable HETZNER_Token)}"
|
|
||||||
|
|
||||||
if [ -z "$HETZNER_Token" ]; then
|
|
||||||
HETZNER_Token=""
|
|
||||||
_err "You didn't specify a Hetzner api token."
|
|
||||||
_err "You can get yours from here https://dns.hetzner.com/settings/api-token."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
#save the api key and email to the account conf file.
|
|
||||||
_saveaccountconf_mutable HETZNER_Token "$HETZNER_Token"
|
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
|
||||||
|
|
||||||
if ! _get_root "$full_domain"; then
|
|
||||||
_err "Invalid domain"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug _domain_id "$_domain_id"
|
|
||||||
_debug _sub_domain "$_sub_domain"
|
|
||||||
_debug _domain "$_domain"
|
|
||||||
|
|
||||||
_debug "Getting TXT records"
|
|
||||||
if ! _find_record "$_sub_domain" "$txt_value"; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$_record_id" ]; then
|
|
||||||
_info "Adding record"
|
|
||||||
if _hetzner_rest POST "records" "{\"zone_id\":\"${HETZNER_Zone_ID}\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txt_value\",\"ttl\":120}"; then
|
|
||||||
if _contains "$response" "$txt_value"; then
|
|
||||||
_info "Record added, OK"
|
|
||||||
_sleep 2
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
_err "Add txt record error${_response_error}"
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
_info "Found record id: $_record_id."
|
|
||||||
_info "Record found, do nothing."
|
|
||||||
return 0
|
|
||||||
# we could modify a record, if the names for txt records for *.example.com and example.com would be not the same
|
|
||||||
#if _hetzner_rest PUT "records/${_record_id}" "{\"zone_id\":\"${HETZNER_Zone_ID}\",\"type\":\"TXT\",\"name\":\"$full_domain\",\"value\":\"$txt_value\",\"ttl\":120}"; then
|
|
||||||
# if _contains "$response" "$txt_value"; then
|
|
||||||
# _info "Modified, OK"
|
|
||||||
# return 0
|
|
||||||
# fi
|
|
||||||
#fi
|
|
||||||
#_err "Add txt record error (modify)."
|
|
||||||
#return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Usage: full_domain txt_value
|
|
||||||
# Used to remove the txt record after validation
|
|
||||||
dns_hetzner_rm() {
|
|
||||||
full_domain=$1
|
|
||||||
txt_value=$2
|
|
||||||
|
|
||||||
HETZNER_Token="${HETZNER_Token:-$(_readaccountconf_mutable HETZNER_Token)}"
|
|
||||||
|
|
||||||
_debug "First detect the root zone"
|
|
||||||
if ! _get_root "$full_domain"; then
|
|
||||||
_err "Invalid domain"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug _domain_id "$_domain_id"
|
|
||||||
_debug _sub_domain "$_sub_domain"
|
|
||||||
_debug _domain "$_domain"
|
|
||||||
|
|
||||||
_debug "Getting TXT records"
|
|
||||||
if ! _find_record "$_sub_domain" "$txt_value"; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$_record_id" ]; then
|
|
||||||
_info "Remove not needed. Record not found."
|
|
||||||
else
|
|
||||||
if ! _hetzner_rest DELETE "records/$_record_id"; then
|
|
||||||
_err "Delete record error${_response_error}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_sleep 2
|
|
||||||
_info "Record deleted"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#################### Private functions below ##################################
|
|
||||||
#returns
|
|
||||||
# _record_id=a8d58f22d6931bf830eaa0ec6464bf81 if found; or 1 if error
|
|
||||||
_find_record() {
|
|
||||||
unset _record_id
|
|
||||||
_record_name=$1
|
|
||||||
_record_value=$2
|
|
||||||
|
|
||||||
if [ -z "$_record_value" ]; then
|
|
||||||
_record_value='[^"]*'
|
|
||||||
fi
|
|
||||||
|
|
||||||
_debug "Getting all records"
|
|
||||||
_hetzner_rest GET "records?zone_id=${_domain_id}"
|
|
||||||
|
|
||||||
if _response_has_error; then
|
|
||||||
_err "Error${_response_error}"
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
_record_id=$(
|
|
||||||
echo "$response" |
|
|
||||||
grep -o "{[^\{\}]*\"name\":\"$_record_name\"[^\}]*}" |
|
|
||||||
grep "\"value\":\"$_record_value\"" |
|
|
||||||
while read -r record; do
|
|
||||||
# test for type and
|
|
||||||
if [ -n "$(echo "$record" | _egrep_o '"type":"TXT"')" ]; then
|
|
||||||
echo "$record" | _egrep_o '"id":"[^"]*"' | cut -d : -f 2 | tr -d \"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#_acme-challenge.www.domain.com
|
|
||||||
#returns
|
|
||||||
# _sub_domain=_acme-challenge.www
|
|
||||||
# _domain=domain.com
|
|
||||||
# _domain_id=sdjkglgdfewsdfg
|
|
||||||
_get_root() {
|
|
||||||
domain=$1
|
|
||||||
i=1
|
|
||||||
p=1
|
|
||||||
|
|
||||||
domain_without_acme=$(echo "$domain" | cut -d . -f 2-)
|
|
||||||
domain_param_name=$(echo "HETZNER_Zone_ID_for_${domain_without_acme}" | sed 's/[\.\-]/_/g')
|
|
||||||
|
|
||||||
_debug "Reading zone_id for '$domain_without_acme' from config..."
|
|
||||||
HETZNER_Zone_ID=$(_readdomainconf "$domain_param_name")
|
|
||||||
if [ "$HETZNER_Zone_ID" ]; then
|
|
||||||
_debug "Found, using: $HETZNER_Zone_ID"
|
|
||||||
if ! _hetzner_rest GET "zones/${HETZNER_Zone_ID}"; then
|
|
||||||
_debug "Zone with id '$HETZNER_Zone_ID' does not exist."
|
|
||||||
_cleardomainconf "$domain_param_name"
|
|
||||||
unset HETZNER_Zone_ID
|
|
||||||
else
|
|
||||||
if _contains "$response" "\"id\":\"$HETZNER_Zone_ID\""; then
|
|
||||||
_domain=$(printf "%s\n" "$response" | _egrep_o '"name":"[^"]*"' | cut -d : -f 2 | tr -d \" | head -n 1)
|
|
||||||
if [ "$_domain" ]; then
|
|
||||||
_cut_length=$((${#domain} - ${#_domain} - 1))
|
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -c "1-$_cut_length")
|
|
||||||
_domain_id="$HETZNER_Zone_ID"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
_debug "Trying to get zone id by domain name for '$domain_without_acme'."
|
|
||||||
while true; do
|
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
|
||||||
if [ -z "$h" ]; then
|
|
||||||
#not valid
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug h "$h"
|
|
||||||
|
|
||||||
_hetzner_rest GET "zones?name=$h"
|
|
||||||
|
|
||||||
if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_entries":1'; then
|
|
||||||
_domain_id=$(echo "$response" | _egrep_o "\[.\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \")
|
|
||||||
if [ "$_domain_id" ]; then
|
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
|
||||||
_domain=$h
|
|
||||||
HETZNER_Zone_ID=$_domain_id
|
|
||||||
_savedomainconf "$domain_param_name" "$HETZNER_Zone_ID"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
p=$i
|
|
||||||
i=$(_math "$i" + 1)
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#returns
|
|
||||||
# _response_error
|
|
||||||
_response_has_error() {
|
|
||||||
unset _response_error
|
|
||||||
|
|
||||||
err_part="$(echo "$response" | _egrep_o '"error":\{[^\}]*\}')"
|
|
||||||
|
|
||||||
if [ -n "$err_part" ]; then
|
|
||||||
err_code=$(echo "$err_part" | _egrep_o '"code":[0-9]+' | cut -d : -f 2)
|
|
||||||
err_message=$(echo "$err_part" | _egrep_o '"message":"[^"]+"' | cut -d : -f 2 | tr -d \")
|
|
||||||
|
|
||||||
if [ -n "$err_code" ] && [ -n "$err_message" ]; then
|
|
||||||
_response_error=" - message: ${err_message}, code: ${err_code}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#returns
|
|
||||||
# response
|
|
||||||
_hetzner_rest() {
|
|
||||||
m=$1
|
|
||||||
ep="$2"
|
|
||||||
data="$3"
|
|
||||||
_debug "$ep"
|
|
||||||
|
|
||||||
key_trimmed=$(echo "$HETZNER_Token" | tr -d \")
|
|
||||||
|
|
||||||
export _H1="Content-TType: application/json"
|
|
||||||
export _H2="Auth-API-Token: $key_trimmed"
|
|
||||||
|
|
||||||
if [ "$m" != "GET" ]; then
|
|
||||||
_debug data "$data"
|
|
||||||
response="$(_post "$data" "$HETZNER_Api/$ep" "" "$m")"
|
|
||||||
else
|
|
||||||
response="$(_get "$HETZNER_Api/$ep")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$?" != "0" ] || _response_has_error; then
|
|
||||||
_debug "Error$_response_error"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
_debug2 response "$response"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
dns_ipprojects_info='IP-Projects DNS
|
||||||
|
Site: ip-projects.de/
|
||||||
|
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_ipprojects
|
||||||
|
Options:
|
||||||
|
IPP_Apikey API Key
|
||||||
|
Issues: github.com/acmesh-official/acme.sh/issues/6958
|
||||||
|
Author: Markus Ebner
|
||||||
|
'
|
||||||
|
|
||||||
|
IPP_Apikey="${IPP_Apikey:-$(_readaccountconf_mutable IPP_Apikey)}"
|
||||||
|
IPP_API="https://api.ip-projects.de/v1/dns/acme"
|
||||||
|
|
||||||
|
######## Public functions ########
|
||||||
|
|
||||||
|
dns_ipprojects_add() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
_info "Using IP-Projects DNS API to add record"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _IPP_load_credentials; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_IPP_api_request "add" "$fulldomain" "$txtvalue"
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_ipprojects_rm() {
|
||||||
|
fulldomain="$1"
|
||||||
|
txtvalue="$2"
|
||||||
|
|
||||||
|
_info "Using IP-Projects DNS API to remove record"
|
||||||
|
_debug fulldomain "$fulldomain"
|
||||||
|
_debug txtvalue "$txtvalue"
|
||||||
|
|
||||||
|
if ! _IPP_load_credentials; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_IPP_api_request "remove" "$fulldomain" "$txtvalue"
|
||||||
|
}
|
||||||
|
|
||||||
|
######## Private helpers ########
|
||||||
|
|
||||||
|
_IPP_load_credentials() {
|
||||||
|
IPP_Apikey="${IPP_Apikey:-$(_readaccountconf_mutable IPP_Apikey)}"
|
||||||
|
|
||||||
|
if [ -z "$IPP_Apikey" ]; then
|
||||||
|
_err "You must export IPP_Apikey"
|
||||||
|
_err "e.g.: export IPP_Apikey=\"your_api_key\""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf_mutable IPP_Apikey "$IPP_Apikey"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_IPP_api_request() {
|
||||||
|
action="$1"
|
||||||
|
domain="$2"
|
||||||
|
value="$3"
|
||||||
|
|
||||||
|
url="$IPP_API/$action"
|
||||||
|
|
||||||
|
data="{\"domain\":\"$domain\",\"key\":\"$domain\",\"value\":\"$value\"}"
|
||||||
|
_debug url "$url"
|
||||||
|
_debug data "$data"
|
||||||
|
export _H1="X-API-Key: $IPP_Apikey"
|
||||||
|
|
||||||
|
response="$(_post "$data" "$url" "" "POST" "application/json")"
|
||||||
|
ret="$?"
|
||||||
|
_ipprojects_last_http_code=$(grep "^HTTP" "${HTTP_HEADER}" | _tail_n 1 | cut -d " " -f 2 | tr -d '\r\n')
|
||||||
|
|
||||||
|
_debug response "$response"
|
||||||
|
|
||||||
|
if [ "$ret" != "0" ]; then
|
||||||
|
_err "HTTP request failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$_ipprojects_last_http_code" != "200" ]; then
|
||||||
|
_err "API returned an error [code: ${_ipprojects_last_http_code}]"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
|
||||||
|
# Credits to the authors of dnsapi/dns_pdns.sh as this reuses much of that code.
|
||||||
|
|
||||||
|
dns_poweradmin_info='Poweradmin API
|
||||||
|
Site: https://www.poweradmin.org/
|
||||||
|
Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_poweradmin
|
||||||
|
Options:
|
||||||
|
POWERADMIN_URL API URL (with scheme). E.g. "https://poweradmin.example.com" or "http://192.168.0.10:8080"
|
||||||
|
POWERADMIN_API_KEY API Token "pwa_xxxx"
|
||||||
|
POWERADMIN_API_VERSION Optionally override Poweradmin API version.
|
||||||
|
Issues: https://github.com/acmesh-official/acme.sh/issues/6912
|
||||||
|
Author: Jakob Næss <https://github.com/InvisibleDuck>
|
||||||
|
'
|
||||||
|
|
||||||
|
######## Public functions ####################
|
||||||
|
|
||||||
|
# Usage: dns_poweradmin_add _acme-challenge.www.domain.com "123456789ABCDEF"
|
||||||
|
# fulldomain
|
||||||
|
# txtvalue
|
||||||
|
dns_poweradmin_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
POWERADMIN_URL="${POWERADMIN_URL:-$(_readaccountconf_mutable POWERADMIN_URL)}"
|
||||||
|
POWERADMIN_API_KEY="${POWERADMIN_API_KEY:-$(_readaccountconf_mutable POWERADMIN_API_KEY)}"
|
||||||
|
POWERADMIN_API_VERSION="${POWERADMIN_API_VERSION:-$(_readaccountconf_mutable POWERADMIN_API_VERSION)}"
|
||||||
|
POWERADMIN_API_VERSION="${POWERADMIN_API_VERSION:-2}"
|
||||||
|
|
||||||
|
if [ -z "$POWERADMIN_URL" ]; then
|
||||||
|
POWERADMIN_URL=""
|
||||||
|
_err "You didn't specify Poweradmin URL."
|
||||||
|
_err "Please set POWERADMIN_URL and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$POWERADMIN_API_KEY" ]; then
|
||||||
|
POWERADMIN_API_KEY=""
|
||||||
|
_err "You didn't specify Poweradmin token."
|
||||||
|
_err "Please set POWERADMIN_API_KEY and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save the api addr, key, and version to the account conf file.
|
||||||
|
_saveaccountconf_mutable POWERADMIN_URL "$POWERADMIN_URL"
|
||||||
|
_saveaccountconf_mutable POWERADMIN_API_KEY "$POWERADMIN_API_KEY"
|
||||||
|
_saveaccountconf_mutable POWERADMIN_API_VERSION "$POWERADMIN_API_VERSION"
|
||||||
|
|
||||||
|
_debug "Detect root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _zone_id "$_zone_id"
|
||||||
|
|
||||||
|
if ! _set_record "$fulldomain" "$txtvalue"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage: dns_poweradmin_rm _acme-challenge.www.domain.com "123456789ABCDEF"
|
||||||
|
# fulldomain
|
||||||
|
# txtvalue
|
||||||
|
dns_poweradmin_rm() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
POWERADMIN_URL="${POWERADMIN_URL:-$(_readaccountconf_mutable POWERADMIN_URL)}"
|
||||||
|
POWERADMIN_API_KEY="${POWERADMIN_API_KEY:-$(_readaccountconf_mutable POWERADMIN_API_KEY)}"
|
||||||
|
POWERADMIN_API_VERSION="${POWERADMIN_API_VERSION:-$(_readaccountconf_mutable POWERADMIN_API_VERSION)}"
|
||||||
|
POWERADMIN_API_VERSION="${POWERADMIN_API_VERSION:-2}"
|
||||||
|
|
||||||
|
_debug "Detect root zone"
|
||||||
|
if ! _get_root "$fulldomain"; then
|
||||||
|
_err "invalid domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug _domain "$_domain"
|
||||||
|
_debug _zone_id "$_zone_id"
|
||||||
|
|
||||||
|
if ! _rm_record "$fulldomain" "$txtvalue"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
######## Private functions below #####################
|
||||||
|
|
||||||
|
_set_record() {
|
||||||
|
_info "Adding TXT record"
|
||||||
|
full=$1
|
||||||
|
new_challenge=$2
|
||||||
|
|
||||||
|
data='{"name":"'$full'","type":"TXT","content":"'$new_challenge'","ttl":60}'
|
||||||
|
|
||||||
|
if ! _poweradmin_rest "POST" "/api/v${POWERADMIN_API_VERSION}/zones/$_zone_id/records" "$data" "application/json"; then
|
||||||
|
_err "Failed to add TXT record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_rm_record() {
|
||||||
|
_info "Remove TXT record"
|
||||||
|
full=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
if ! _poweradmin_rest "GET" "/api/v${POWERADMIN_API_VERSION}/zones/$_zone_id/records"; then
|
||||||
|
_err "Failed to retrieve records"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The API returns: {"success":true,"data":[{"id":..., "name":"...", "type":"TXT", "content":"...", ...}]}
|
||||||
|
_txt_record_obj=$(
|
||||||
|
printf '%s\n' "$response" |
|
||||||
|
sed 's/^.*"data":\[//; s/\],"message":.*$//' |
|
||||||
|
awk '{ gsub(/},{/, "}\n{"); print }' |
|
||||||
|
grep -F "\"name\":\"$full\"" |
|
||||||
|
grep -F "\"type\":\"TXT\"" |
|
||||||
|
grep -F "\"content\":\"$txtvalue\"" |
|
||||||
|
_head_n 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ -z "$_txt_record_obj" ]; then
|
||||||
|
_info "TXT record not found for $full with content $txtvalue"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
record_id=$(printf '%s\n' "$_txt_record_obj" | sed -n 's/.*"id":\([0-9][0-9]*\).*/\1/p' | _head_n 1)
|
||||||
|
record_type=$(printf '%s\n' "$_txt_record_obj" | sed -n 's/.*"type":"\([^"]*\)".*/\1/p' | _head_n 1)
|
||||||
|
record_name=$(printf '%s\n' "$_txt_record_obj" | sed -n 's/.*"name":"\([^"]*\)".*/\1/p' | _head_n 1)
|
||||||
|
record_content=$(printf '%s\n' "$_txt_record_obj" | sed -n 's/.*"content":"\([^"]*\)".*/\1/p' | _head_n 1)
|
||||||
|
|
||||||
|
_debug2 "_txt_record_obj=$_txt_record_obj"
|
||||||
|
_debug2 "record id: $record_id"
|
||||||
|
_debug2 "record type: $record_type"
|
||||||
|
_debug2 "record name: $record_name"
|
||||||
|
_debug2 "record content: $record_content"
|
||||||
|
|
||||||
|
if [ "$record_type" != "TXT" ]; then
|
||||||
|
_err "Refusing to delete non-TXT record id=$record_id type=$record_type name=$full"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _poweradmin_rest "DELETE" "/api/v${POWERADMIN_API_VERSION}/zones/$_zone_id/records/$record_id"; then
|
||||||
|
_err "Failed to delete TXT record"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Record deleted successfully"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# _acme-challenge.www.domain.com
|
||||||
|
# returns
|
||||||
|
# _domain=domain.com
|
||||||
|
# _zone_id=220
|
||||||
|
_get_root() {
|
||||||
|
domain=$1
|
||||||
|
i=1
|
||||||
|
|
||||||
|
if ! _poweradmin_rest "GET" "/api/v${POWERADMIN_API_VERSION}/zones"; then
|
||||||
|
_err "Failed to retrieve zones"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_zones_response="$response"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
|
|
||||||
|
if [ -z "$h" ]; then
|
||||||
|
_debug "Root domain not found for $domain"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
zone_obj=$(
|
||||||
|
printf '%s' "$_zones_response" |
|
||||||
|
sed 's/},{/}\n{/g' |
|
||||||
|
grep -F "\"name\":\"$h\"" |
|
||||||
|
_head_n 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ -n "$zone_obj" ]; then
|
||||||
|
_zone_id=$(printf '%s' "$zone_obj" | _egrep_o '"id":[0-9][0-9]*' | _head_n 1 | cut -d: -f2)
|
||||||
|
_domain="$h"
|
||||||
|
_debug "Found zone: $_domain with id: $_zone_id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
i=$(_math "$i" + 1)
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_poweradmin_rest() {
|
||||||
|
method=$1
|
||||||
|
ep=$2
|
||||||
|
data=$3
|
||||||
|
ct=$4
|
||||||
|
|
||||||
|
export _H1="X-API-Key: $POWERADMIN_API_KEY"
|
||||||
|
|
||||||
|
if [ "$method" = "GET" ]; then
|
||||||
|
response="$(_get "$POWERADMIN_URL$ep")"
|
||||||
|
else
|
||||||
|
_debug "API call: $method $ep"
|
||||||
|
_debug "Content-Type: $ct"
|
||||||
|
_debug "Payload: $data"
|
||||||
|
response="$(_post "$data" "$POWERADMIN_URL$ep" "" "$method" "$ct")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clear _H1 variable
|
||||||
|
unset -v _H1
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
_err "API error on $method $ep"
|
||||||
|
_debug "Response: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if printf '%s' "$response" | grep -q '"success"[[:space:]]*:[[:space:]]*false'; then
|
||||||
|
_err "API reported failure on $method $ep"
|
||||||
|
_debug "Response: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_debug2 "API Response: $response"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
+57
-35
@@ -8,11 +8,7 @@ Options:
|
|||||||
SIMPLY_ApiKey API Key
|
SIMPLY_ApiKey API Key
|
||||||
'
|
'
|
||||||
|
|
||||||
#SIMPLY_Api="https://api.simply.com/2/"
|
SIMPLY_Api="https://api.simply.com/2"
|
||||||
SIMPLY_Api_Default="https://api.simply.com/2"
|
|
||||||
|
|
||||||
#This is used for determining success of REST call
|
|
||||||
SIMPLY_SUCCESS_CODE='"status":200'
|
|
||||||
|
|
||||||
######## Public functions #####################
|
######## Public functions #####################
|
||||||
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
@@ -72,7 +68,16 @@ dns_simply_rm() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
records=$(echo "$response" | tr '{' "\n" | grep 'record_id\|type\|data\|\name' | sed 's/\"record_id/;\"record_id/' | tr "\n" ' ' | tr -d ' ' | tr ';' ' ')
|
case "$_simply_http_code" in
|
||||||
|
2*) ;;
|
||||||
|
*)
|
||||||
|
_err "Failed to fetch DNS records (HTTP $_simply_http_code)"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
records=$(echo "$response" | tr '{' "\n" | grep -E 'record_id|type|data|name' | sed 's/\"record_id/;\"record_id/' | tr "\n" ' ' | tr -d ' ' | tr ';' ' ')
|
||||||
|
|
||||||
nr_of_deleted_records=0
|
nr_of_deleted_records=0
|
||||||
_info "Fetching txt record"
|
_info "Fetching txt record"
|
||||||
@@ -95,7 +100,7 @@ dns_simply_rm() {
|
|||||||
|
|
||||||
if [ "$record_id" -gt 0 ]; then
|
if [ "$record_id" -gt 0 ]; then
|
||||||
|
|
||||||
if ! _simply_delete_record "$_domain" "$_sub_domain" "$record_id"; then
|
if ! _simply_delete_record "$_domain" "$record_id"; then
|
||||||
_err "Record with id $record_id could not be deleted"
|
_err "Record with id $record_id could not be deleted"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -122,14 +127,9 @@ dns_simply_rm() {
|
|||||||
#################### Private functions below ##################################
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
_simply_load_config() {
|
_simply_load_config() {
|
||||||
SIMPLY_Api="${SIMPLY_Api:-$(_readaccountconf_mutable SIMPLY_Api)}"
|
|
||||||
SIMPLY_AccountName="${SIMPLY_AccountName:-$(_readaccountconf_mutable SIMPLY_AccountName)}"
|
SIMPLY_AccountName="${SIMPLY_AccountName:-$(_readaccountconf_mutable SIMPLY_AccountName)}"
|
||||||
SIMPLY_ApiKey="${SIMPLY_ApiKey:-$(_readaccountconf_mutable SIMPLY_ApiKey)}"
|
SIMPLY_ApiKey="${SIMPLY_ApiKey:-$(_readaccountconf_mutable SIMPLY_ApiKey)}"
|
||||||
|
|
||||||
if [ -z "$SIMPLY_Api" ]; then
|
|
||||||
SIMPLY_Api="$SIMPLY_Api_Default"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$SIMPLY_AccountName" ] || [ -z "$SIMPLY_ApiKey" ]; then
|
if [ -z "$SIMPLY_AccountName" ] || [ -z "$SIMPLY_ApiKey" ]; then
|
||||||
SIMPLY_AccountName=""
|
SIMPLY_AccountName=""
|
||||||
SIMPLY_ApiKey=""
|
SIMPLY_ApiKey=""
|
||||||
@@ -144,9 +144,6 @@ _simply_load_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_simply_save_config() {
|
_simply_save_config() {
|
||||||
if [ "$SIMPLY_Api" != "$SIMPLY_Api_Default" ]; then
|
|
||||||
_saveaccountconf_mutable SIMPLY_Api "$SIMPLY_Api"
|
|
||||||
fi
|
|
||||||
_saveaccountconf_mutable SIMPLY_AccountName "$SIMPLY_AccountName"
|
_saveaccountconf_mutable SIMPLY_AccountName "$SIMPLY_AccountName"
|
||||||
_saveaccountconf_mutable SIMPLY_ApiKey "$SIMPLY_ApiKey"
|
_saveaccountconf_mutable SIMPLY_ApiKey "$SIMPLY_ApiKey"
|
||||||
}
|
}
|
||||||
@@ -163,26 +160,39 @@ _simply_get_all_records() {
|
|||||||
|
|
||||||
_get_root() {
|
_get_root() {
|
||||||
domain=$1
|
domain=$1
|
||||||
|
|
||||||
|
if ! _simply_rest GET "my/products/"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$_simply_http_code" in
|
||||||
|
2*) ;;
|
||||||
|
*)
|
||||||
|
_err "Failed to fetch product list (HTTP $_simply_http_code)"
|
||||||
|
_err "$response"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
i=2
|
i=2
|
||||||
p=1
|
p=1
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
#not valid
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _simply_rest GET "my/products/$h/dns/"; then
|
_domain=$(printf "%s" "$response" | tr '}' '\n' |
|
||||||
return 1
|
grep -F -e "\"object\":\"$h\"" -e "\"name\":\"$h\"" -e "\"name_idn\":\"$h\"" |
|
||||||
fi
|
sed -n 's/.*"object":"\([^"]*\)".*/\1/p' |
|
||||||
|
_head_n 1)
|
||||||
|
|
||||||
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
|
if [ -n "$_domain" ]; then
|
||||||
_debug "$h not found"
|
|
||||||
else
|
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
|
||||||
_domain="$h"
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
_debug "No Simply.com product found for $h"
|
||||||
p="$i"
|
p="$i"
|
||||||
i=$(_math "$i" + 1)
|
i=$(_math "$i" + 1)
|
||||||
done
|
done
|
||||||
@@ -194,39 +204,44 @@ _simply_add_record() {
|
|||||||
sub_domain=$2
|
sub_domain=$2
|
||||||
txtval=$3
|
txtval=$3
|
||||||
|
|
||||||
data="{\"name\": \"$sub_domain\", \"type\":\"TXT\", \"data\": \"$txtval\", \"priority\":0, \"ttl\": 3600}"
|
data="{\"name\": \"$sub_domain\", \"type\":\"TXT\", \"data\": \"$txtval\", \"priority\":0, \"ttl\": 120}"
|
||||||
|
|
||||||
if ! _simply_rest POST "my/products/$domain/dns/records/" "$data"; then
|
if ! _simply_rest POST "my/products/$domain/dns/records/" "$data"; then
|
||||||
_err "Adding record not successfull!"
|
_err "Adding record not successful!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
|
case "$_simply_http_code" in
|
||||||
_err "Call to API not sucessfull, see below message for more details"
|
2*) ;;
|
||||||
|
*)
|
||||||
|
_err "Call to API not successful (HTTP $_simply_http_code), see below message for more details"
|
||||||
_err "$response"
|
_err "$response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
_simply_delete_record() {
|
_simply_delete_record() {
|
||||||
domain=$1
|
domain=$1
|
||||||
sub_domain=$2
|
record_id=$2
|
||||||
record_id=$3
|
|
||||||
|
|
||||||
_debug record_id "Delete record with id $record_id"
|
_debug record_id "Delete record with id $record_id"
|
||||||
|
|
||||||
if ! _simply_rest DELETE "my/products/$domain/dns/records/$record_id/"; then
|
if ! _simply_rest DELETE "my/products/$domain/dns/records/$record_id/"; then
|
||||||
_err "Deleting record not successfull!"
|
_err "Deleting record not successful!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
|
case "$_simply_http_code" in
|
||||||
_err "Call to API not sucessfull, see below message for more details"
|
2*) ;;
|
||||||
|
*)
|
||||||
|
_err "Call to API not successful (HTTP $_simply_http_code), see below message for more details"
|
||||||
_err "$response"
|
_err "$response"
|
||||||
return 1
|
return 1
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -248,17 +263,24 @@ _simply_rest() {
|
|||||||
|
|
||||||
export _H2="Content-Type: application/json"
|
export _H2="Content-Type: application/json"
|
||||||
|
|
||||||
|
: >"$HTTP_HEADER"
|
||||||
|
|
||||||
if [ "$m" != "GET" ]; then
|
if [ "$m" != "GET" ]; then
|
||||||
response="$(_post "$data" "$SIMPLY_Api/$ep" "" "$m")"
|
response="$(_post "$data" "$SIMPLY_Api/$ep" "" "$m")"
|
||||||
else
|
else
|
||||||
response="$(_get "$SIMPLY_Api/$ep")"
|
response="$(_get "$SIMPLY_Api/$ep")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$?" != "0" ]; then
|
_ret="$?"
|
||||||
|
unset _H1 _H2
|
||||||
|
|
||||||
|
if [ "$_ret" != "0" ]; then
|
||||||
_err "error $ep"
|
_err "error $ep"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
_simply_http_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d' ' -f2 | tr -d '\r\n')"
|
||||||
|
|
||||||
response="$(echo "$response" | _normalizeJson)"
|
response="$(echo "$response" | _normalizeJson)"
|
||||||
|
|
||||||
_debug2 response "$response"
|
_debug2 response "$response"
|
||||||
|
|||||||
Reference in New Issue
Block a user