acmesh-official / acme.sh

A pure Unix shell script implementing ACME client protocol
https://acme.sh
GNU General Public License v3.0
37.63k stars 4.84k forks source link

dns_constellix fails for wildcard domains #3143

Open resaperk opened 3 years ago

resaperk commented 3 years ago

When trying to issue a cert for example.com and *.example.com, using dns-01 with constellix, dns_constellix.sh attempts to create the same TXT record for "_acme-challenge.example.com" twice, and fails "already exists" on the second

Steps to reproduce

acme.sh --issue \
    --dns dns_constellix
    -d example.com
    -d *.example.com

Debug log

Lets find script dir.
[Wed Sep  2 16:21:34 CDT 2020] _SCRIPT_='/usr/local/sbin/acme.sh'
[Wed Sep  2 16:21:34 CDT 2020] _script='/usr/local/sbin/acme.sh'
[Wed Sep  2 16:21:34 CDT 2020] _script_home='/usr/local/sbin'
[Wed Sep  2 16:21:34 CDT 2020] Using config home:/var/db/acme/.acme.sh
[Wed Sep  2 16:21:34 CDT 2020] Running cmd: issue
[Wed Sep  2 16:21:34 CDT 2020] _main_domain='example.com'
[Wed Sep  2 16:21:34 CDT 2020] _alt_domains='*.example.com'
[Wed Sep  2 16:21:34 CDT 2020] Using config home:/var/db/acme/.acme.sh
[Wed Sep  2 16:21:34 CDT 2020] ACME_DIRECTORY='https://acme-v02.api.letsencrypt.org/directory'
[Wed Sep  2 16:21:34 CDT 2020] DOMAIN_PATH='/var/db/acme/certs/example.com'
[Wed Sep  2 16:21:34 CDT 2020] Using ACME_DIRECTORY: https://acme-v02.api.letsencrypt.org/directory
[Wed Sep  2 16:21:34 CDT 2020] _init api for server: https://acme-v02.api.letsencrypt.org/directory
[Wed Sep  2 16:21:34 CDT 2020] GET
[Wed Sep  2 16:21:34 CDT 2020] url='https://acme-v02.api.letsencrypt.org/directory'
[Wed Sep  2 16:21:34 CDT 2020] timeout=
[Wed Sep  2 16:21:34 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:35 CDT 2020] ret='0'
[Wed Sep  2 16:21:35 CDT 2020] ACME_KEY_CHANGE='https://acme-v02.api.letsencrypt.org/acme/key-change'
[Wed Sep  2 16:21:35 CDT 2020] ACME_NEW_AUTHZ
[Wed Sep  2 16:21:35 CDT 2020] ACME_NEW_ORDER='https://acme-v02.api.letsencrypt.org/acme/new-order'
[Wed Sep  2 16:21:35 CDT 2020] ACME_NEW_ACCOUNT='https://acme-v02.api.letsencrypt.org/acme/new-acct'
[Wed Sep  2 16:21:35 CDT 2020] ACME_REVOKE_CERT='https://acme-v02.api.letsencrypt.org/acme/revoke-cert'
[Wed Sep  2 16:21:35 CDT 2020] ACME_AGREEMENT='https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf'
[Wed Sep  2 16:21:35 CDT 2020] ACME_NEW_NONCE='https://acme-v02.api.letsencrypt.org/acme/new-nonce'
[Wed Sep  2 16:21:35 CDT 2020] ACME_VERSION='2'
[Wed Sep  2 16:21:35 CDT 2020] Le_NextRenewTime
[Wed Sep  2 16:21:35 CDT 2020] _on_before_issue
[Wed Sep  2 16:21:35 CDT 2020] _chk_main_domain='example.com'
[Wed Sep  2 16:21:35 CDT 2020] _chk_alt_domains='*.example.com'
[Wed Sep  2 16:21:35 CDT 2020] Le_LocalAddress
[Wed Sep  2 16:21:35 CDT 2020] d='example.com'
[Wed Sep  2 16:21:35 CDT 2020] Check for domain='example.com'
[Wed Sep  2 16:21:35 CDT 2020] _currentRoot='dns_constellix'
[Wed Sep  2 16:21:35 CDT 2020] d='*.example.com'
[Wed Sep  2 16:21:35 CDT 2020] Check for domain='*.example.com'
[Wed Sep  2 16:21:35 CDT 2020] _currentRoot='dns_constellix'
[Wed Sep  2 16:21:35 CDT 2020] d
[Wed Sep  2 16:21:35 CDT 2020] _saved_account_key_hash is not changed, skip register account.
[Wed Sep  2 16:21:35 CDT 2020] Read key length:
[Wed Sep  2 16:21:35 CDT 2020] _createcsr
[Wed Sep  2 16:21:35 CDT 2020] Multi domain='DNS:example.com,DNS:*.example.com'
[Wed Sep  2 16:21:35 CDT 2020] Getting domain auth token for each domain
[Wed Sep  2 16:21:35 CDT 2020] d='*.example.com'
[Wed Sep  2 16:21:35 CDT 2020] d
[Wed Sep  2 16:21:35 CDT 2020] url='https://acme-v02.api.letsencrypt.org/acme/new-order'
[Wed Sep  2 16:21:35 CDT 2020] payload='{"identifiers": [{"type":"dns","value":"example.com"},{"type":"dns","value":"*.example.com"}]}'
[Wed Sep  2 16:21:35 CDT 2020] RSA key
[Wed Sep  2 16:21:36 CDT 2020] HEAD
[Wed Sep  2 16:21:36 CDT 2020] _post_url='https://acme-v02.api.letsencrypt.org/acme/new-nonce'
[Wed Sep  2 16:21:36 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g  -I  '
[Wed Sep  2 16:21:36 CDT 2020] _ret='0'
[Wed Sep  2 16:21:36 CDT 2020] POST
[Wed Sep  2 16:21:36 CDT 2020] _post_url='https://acme-v02.api.letsencrypt.org/acme/new-order'
[Wed Sep  2 16:21:36 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:36 CDT 2020] _ret='0'
[Wed Sep  2 16:21:36 CDT 2020] code='201'
[Wed Sep  2 16:21:36 CDT 2020] Le_LinkOrder='https://acme-v02.api.letsencrypt.org/acme/order/95626260/4990664827'
[Wed Sep  2 16:21:36 CDT 2020] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/95626260/4990664827'
[Wed Sep  2 16:21:36 CDT 2020] url='https://acme-v02.api.letsencrypt.org/acme/authz-v3/6944698181'
[Wed Sep  2 16:21:36 CDT 2020] payload
[Wed Sep  2 16:21:36 CDT 2020] POST
[Wed Sep  2 16:21:36 CDT 2020] _post_url='https://acme-v02.api.letsencrypt.org/acme/authz-v3/6944698181'
[Wed Sep  2 16:21:36 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:36 CDT 2020] _ret='0'
[Wed Sep  2 16:21:36 CDT 2020] code='200'
[Wed Sep  2 16:21:36 CDT 2020] url='https://acme-v02.api.letsencrypt.org/acme/authz-v3/6944698183'
[Wed Sep  2 16:21:36 CDT 2020] payload
[Wed Sep  2 16:21:37 CDT 2020] POST
[Wed Sep  2 16:21:37 CDT 2020] _post_url='https://acme-v02.api.letsencrypt.org/acme/authz-v3/6944698183'
[Wed Sep  2 16:21:37 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:37 CDT 2020] _ret='0'
[Wed Sep  2 16:21:37 CDT 2020] code='200'
[Wed Sep  2 16:21:37 CDT 2020] d='example.com'
[Wed Sep  2 16:21:37 CDT 2020] Getting webroot for domain='example.com'
[Wed Sep  2 16:21:37 CDT 2020] _w='dns_constellix'
[Wed Sep  2 16:21:37 CDT 2020] _currentRoot='dns_constellix'
[Wed Sep  2 16:21:37 CDT 2020] entry='"type":"dns-01","status":"pending","url":"https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698183/FymoNA","token":"j41baYKDje_ZmgUnskkV-AcJK19AE_dv020NPM-pmsQ"'
[Wed Sep  2 16:21:37 CDT 2020] token='j41baYKDje_ZmgUnskkV-AcJK19AE_dv020NPM-pmsQ'
[Wed Sep  2 16:21:37 CDT 2020] uri='https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698183/FymoNA'
[Wed Sep  2 16:21:37 CDT 2020] keyauthorization='j41baYKDje_ZmgUnskkV-AcJK19AE_dv020NPM-pmsQ.dZuJpkthTd62ojXVw9FgMB8GNrd60pwCQ6Mi1kJeBrI'
[Wed Sep  2 16:21:37 CDT 2020] dvlist='example.com#j41baYKDje_ZmgUnskkV-AcJK19AE_dv020NPM-pmsQ.dZuJpkthTd62ojXVw9FgMB8GNrd60pwCQ6Mi1kJeBrI#https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698183/FymoNA#dns-01#dns_constellix'
[Wed Sep  2 16:21:37 CDT 2020] d='*.example.com'
[Wed Sep  2 16:21:37 CDT 2020] Getting webroot for domain='*.example.com'
[Wed Sep  2 16:21:37 CDT 2020] _w='dns_constellix'
[Wed Sep  2 16:21:37 CDT 2020] _currentRoot='dns_constellix'
[Wed Sep  2 16:21:37 CDT 2020] entry='"type":"dns-01","status":"pending","url":"https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698181/nHBtYw","token":"bkYdRxGZmg5FdMJIyPC9NbVNc_BrQ2rL9rT97LqaGlw"'
[Wed Sep  2 16:21:37 CDT 2020] token='bkYdRxGZmg5FdMJIyPC9NbVNc_BrQ2rL9rT97LqaGlw'
[Wed Sep  2 16:21:37 CDT 2020] uri='https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698181/nHBtYw'
[Wed Sep  2 16:21:37 CDT 2020] keyauthorization='bkYdRxGZmg5FdMJIyPC9NbVNc_BrQ2rL9rT97LqaGlw.dZuJpkthTd62ojXVw9FgMB8GNrd60pwCQ6Mi1kJeBrI'
[Wed Sep  2 16:21:37 CDT 2020] dvlist='*.example.com#bkYdRxGZmg5FdMJIyPC9NbVNc_BrQ2rL9rT97LqaGlw.dZuJpkthTd62ojXVw9FgMB8GNrd60pwCQ6Mi1kJeBrI#https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698181/nHBtYw#dns-01#dns_constellix'
[Wed Sep  2 16:21:37 CDT 2020] d
[Wed Sep  2 16:21:37 CDT 2020] vlist='example.com#j41baYKDje_ZmgUnskkV-AcJK19AE_dv020NPM-pmsQ.dZuJpkthTd62ojXVw9FgMB8GNrd60pwCQ6Mi1kJeBrI#https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698183/FymoNA#dns-01#dns_constellix,*.example.com#bkYdRxGZmg5FdMJIyPC9NbVNc_BrQ2rL9rT97LqaGlw.dZuJpkthTd62ojXVw9FgMB8GNrd60pwCQ6Mi1kJeBrI#https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698181/nHBtYw#dns-01#dns_constellix,'
[Wed Sep  2 16:21:37 CDT 2020] d='example.com'
[Wed Sep  2 16:21:37 CDT 2020] _d_alias
[Wed Sep  2 16:21:37 CDT 2020] txtdomain='_acme-challenge.example.com'
[Wed Sep  2 16:21:37 CDT 2020] txt='_Qjiww7s-E5PUx8Gcwky30JSxgLFOOdM4oJQ85kd86w'
[Wed Sep  2 16:21:37 CDT 2020] d_api='/var/db/acme/.acme.sh/dnsapi/dns_constellix.sh'
[Wed Sep  2 16:21:37 CDT 2020] Found domain api file: /var/db/acme/.acme.sh/dnsapi/dns_constellix.sh
[Wed Sep  2 16:21:37 CDT 2020] Adding txt value: _Qjiww7s-E5PUx8Gcwky30JSxgLFOOdM4oJQ85kd86w for domain:  _acme-challenge.example.com
[Wed Sep  2 16:21:37 CDT 2020] Detecting root zone
[Wed Sep  2 16:21:37 CDT 2020] domains/search?exact=example.com
[Wed Sep  2 16:21:37 CDT 2020] GET
[Wed Sep  2 16:21:37 CDT 2020] url='https://api.dns.constellix.com/v1/domains/search?exact=example.com'
[Wed Sep  2 16:21:37 CDT 2020] timeout=
[Wed Sep  2 16:21:37 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:37 CDT 2020] ret='0'
[Wed Sep  2 16:21:37 CDT 2020] response='[{"id":440174,"name":"example.com"}]'
[Wed Sep  2 16:21:37 CDT 2020] _domain_id='440174'
[Wed Sep  2 16:21:37 CDT 2020] _sub_domain='_acme-challenge'
[Wed Sep  2 16:21:37 CDT 2020] _domain='example.com'
[Wed Sep  2 16:21:37 CDT 2020] Adding TXT record
[Wed Sep  2 16:21:37 CDT 2020] domains/440174/records
[Wed Sep  2 16:21:37 CDT 2020] data='[{"type":"txt","add":true,"set":{"name":"_acme-challenge","ttl":120,"roundRobin":[{"value":"_Qjiww7s-E5PUx8Gcwky30JSxgLFOOdM4oJQ85kd86w"}]}}]'
[Wed Sep  2 16:21:37 CDT 2020] POST
[Wed Sep  2 16:21:37 CDT 2020] _post_url='https://api.dns.constellix.com/v1/domains/440174/records'
[Wed Sep  2 16:21:37 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:38 CDT 2020] _ret='0'
[Wed Sep  2 16:21:38 CDT 2020] response='{"success":"1 record(s) added, 0 record(s) updated, 0 record(s) deleted"}'
[Wed Sep  2 16:21:38 CDT 2020] Added
[Wed Sep  2 16:21:38 CDT 2020] The txt record is added: Success.
[Wed Sep  2 16:21:38 CDT 2020] d='*.example.com'
[Wed Sep  2 16:21:38 CDT 2020] _d_alias
[Wed Sep  2 16:21:38 CDT 2020] txtdomain='_acme-challenge.example.com'
[Wed Sep  2 16:21:38 CDT 2020] txt='KuNJGIYWRbFj4vlZ2zeuV2EkDww0IlLvo75a9q2sLY4'
[Wed Sep  2 16:21:38 CDT 2020] d_api='/var/db/acme/.acme.sh/dnsapi/dns_constellix.sh'
[Wed Sep  2 16:21:38 CDT 2020] Found domain api file: /var/db/acme/.acme.sh/dnsapi/dns_constellix.sh
[Wed Sep  2 16:21:38 CDT 2020] Adding txt value: KuNJGIYWRbFj4vlZ2zeuV2EkDww0IlLvo75a9q2sLY4 for domain:  _acme-challenge.example.com
[Wed Sep  2 16:21:38 CDT 2020] Detecting root zone
[Wed Sep  2 16:21:38 CDT 2020] domains/search?exact=example.com
[Wed Sep  2 16:21:38 CDT 2020] GET
[Wed Sep  2 16:21:38 CDT 2020] url='https://api.dns.constellix.com/v1/domains/search?exact=example.com'
[Wed Sep  2 16:21:38 CDT 2020] timeout=
[Wed Sep  2 16:21:38 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:41 CDT 2020] ret='0'
[Wed Sep  2 16:21:41 CDT 2020] response='[{"id":440174,"name":"example.com"}]'
[Wed Sep  2 16:21:41 CDT 2020] _domain_id='440174'
[Wed Sep  2 16:21:41 CDT 2020] _sub_domain='_acme-challenge'
[Wed Sep  2 16:21:41 CDT 2020] _domain='example.com'
[Wed Sep  2 16:21:41 CDT 2020] Adding TXT record
[Wed Sep  2 16:21:41 CDT 2020] domains/440174/records
[Wed Sep  2 16:21:41 CDT 2020] data='[{"type":"txt","add":true,"set":{"name":"_acme-challenge","ttl":120,"roundRobin":[{"value":"KuNJGIYWRbFj4vlZ2zeuV2EkDww0IlLvo75a9q2sLY4"}]}}]'
[Wed Sep  2 16:21:41 CDT 2020] POST
[Wed Sep  2 16:21:41 CDT 2020] _post_url='https://api.dns.constellix.com/v1/domains/440174/records'
[Wed Sep  2 16:21:41 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:43 CDT 2020] _ret='0'
[Wed Sep  2 16:21:43 CDT 2020] response='{"errors":["\"TXT\" record with name \"_acme-challenge\" in GTD Region \"Default\" of domain \"example.com\" already exists"]}'
[Wed Sep  2 16:21:43 CDT 2020] Error adding TXT record
[Wed Sep  2 16:21:43 CDT 2020] Error add txt for domain:_acme-challenge.example.com
[Wed Sep  2 16:21:43 CDT 2020] _on_issue_err
[Wed Sep  2 16:21:43 CDT 2020] Please check log file for more details: /var/log/acme.sh.log
[Wed Sep  2 16:21:44 CDT 2020] url='https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698183/FymoNA'
[Wed Sep  2 16:21:44 CDT 2020] payload='{}'
[Wed Sep  2 16:21:44 CDT 2020] POST
[Wed Sep  2 16:21:44 CDT 2020] _post_url='https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698183/FymoNA'
[Wed Sep  2 16:21:44 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:44 CDT 2020] _ret='0'
[Wed Sep  2 16:21:44 CDT 2020] code='200'
[Wed Sep  2 16:21:44 CDT 2020] url='https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698181/nHBtYw'
[Wed Sep  2 16:21:44 CDT 2020] payload='{}'
[Wed Sep  2 16:21:44 CDT 2020] POST
[Wed Sep  2 16:21:44 CDT 2020] _post_url='https://acme-v02.api.letsencrypt.org/acme/chall-v3/6944698181/nHBtYw'
[Wed Sep  2 16:21:44 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:44 CDT 2020] _ret='0'
[Wed Sep  2 16:21:44 CDT 2020] code='200'
[Wed Sep  2 16:21:44 CDT 2020] pid
[Wed Sep  2 16:21:44 CDT 2020] No need to restore nginx, skip.
[Wed Sep  2 16:21:44 CDT 2020] _clearupdns
[Wed Sep  2 16:21:44 CDT 2020] dns_entries='example.com,_acme-challenge.example.com,,dns_constellix,_Qjiww7s-E5PUx8Gcwky30JSxgLFOOdM4oJQ85kd86w,/var/db/acme/.acme.sh/dnsapi/dns_constellix.sh
'
[Wed Sep  2 16:21:44 CDT 2020] Removing DNS records.
[Wed Sep  2 16:21:44 CDT 2020] d='example.com'
[Wed Sep  2 16:21:44 CDT 2020] txtdomain='_acme-challenge.example.com'
[Wed Sep  2 16:21:44 CDT 2020] aliasDomain='_acme-challenge.example.com'
[Wed Sep  2 16:21:44 CDT 2020] _currentRoot='dns_constellix'
[Wed Sep  2 16:21:44 CDT 2020] txt='_Qjiww7s-E5PUx8Gcwky30JSxgLFOOdM4oJQ85kd86w'
[Wed Sep  2 16:21:44 CDT 2020] d_api='/var/db/acme/.acme.sh/dnsapi/dns_constellix.sh'
[Wed Sep  2 16:21:44 CDT 2020] Removing txt: _Qjiww7s-E5PUx8Gcwky30JSxgLFOOdM4oJQ85kd86w for domain: _acme-challenge.example.com
[Wed Sep  2 16:21:44 CDT 2020] Detecting root zone
[Wed Sep  2 16:21:44 CDT 2020] domains/search?exact=example.com
[Wed Sep  2 16:21:44 CDT 2020] GET
[Wed Sep  2 16:21:44 CDT 2020] url='https://api.dns.constellix.com/v1/domains/search?exact=example.com'
[Wed Sep  2 16:21:44 CDT 2020] timeout=
[Wed Sep  2 16:21:44 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:45 CDT 2020] ret='0'
[Wed Sep  2 16:21:45 CDT 2020] response='[{"id":440174,"name":"example.com"}]'
[Wed Sep  2 16:21:45 CDT 2020] _domain_id='440174'
[Wed Sep  2 16:21:45 CDT 2020] _sub_domain='_acme-challenge'
[Wed Sep  2 16:21:45 CDT 2020] _domain='example.com'
[Wed Sep  2 16:21:45 CDT 2020] Removing TXT record
[Wed Sep  2 16:21:45 CDT 2020] domains/440174/records
[Wed Sep  2 16:21:45 CDT 2020] data='[{"type":"txt","delete":true,"filter":{"field":"name","op":"eq","value":"_acme-challenge"}}]'
[Wed Sep  2 16:21:45 CDT 2020] POST
[Wed Sep  2 16:21:45 CDT 2020] _post_url='https://api.dns.constellix.com/v1/domains/440174/records'
[Wed Sep  2 16:21:45 CDT 2020] _CURL='curl -L --silent --dump-header /var/db/acme/.acme.sh/http.header  -g '
[Wed Sep  2 16:21:47 CDT 2020] _ret='0'
[Wed Sep  2 16:21:47 CDT 2020] response='{"success":"0 record(s) added, 0 record(s) updated, 1 record(s) deleted"}'
[Wed Sep  2 16:21:47 CDT 2020] Removed
[Wed Sep  2 16:21:47 CDT 2020] Removed: Success
ally9696 commented 3 years ago

Alternatively try to set the DNS records with own API using your DNS provider template and look if the issue persists.

resaperk commented 3 years ago

Using api, and the constellix admin panel, multiple text records with the same name will error

They expect (in this case) both challenge TXT records to be added at once for the domain, i.e.

[
    {
        "type":"txt",
        "add":true,
        "set":{
            "name":"_acme-challenge",
            "ttl":120,
            "roundRobin":[
                {"value":"CHALLENGE1ABCD1234ABCD1234ABCD1234ABCD1234A"},
                {"value":"CHALLENGE2ABCD1234ABCD1234ABCD1234ABCD1234A"}
            ]
        }
    }
]
mrlindstrom commented 3 years ago

Adding a response from the Constellix team regarding this:

So just to clarify, the way records are organized in our system is instead of having multiple TXT records with the same hostname, it is organized as one TXT record with multiple values (the body of the call I pasted in my last reply is 4 values in 1 record). If you already have an existing TXT record of the same hostname then you would need to do a PUT instead of POST. The best way to manage this is to first GET the TXT record, then add additional values into the body of the call and send it back to our API as a PUT. Please let us know if you have further questions, we're happy to help!

So it looks like possibly changing to a PUT might address the issue for multiple records.

woutd commented 3 years ago

I have added support for updating existing TXT records that is required for certain wildcard certificates.

https://github.com/woutd/acme.sh/blob/constellix-wildcard-support/dnsapi/dns_constellix.sh

I will create a PR.