diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 84a17470..5417068f 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -66,7 +66,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/
- name: Set env file
@@ -114,7 +114,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Install tools
run: |
brew untap aws/tap || true
@@ -167,7 +167,7 @@ jobs:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Install cygwin base packages with chocolatey
run: |
choco config get cacheLocation
@@ -231,13 +231,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/freebsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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
@@ -290,13 +289,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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 }}
- cache-after-prepare: true
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
@@ -347,13 +345,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/openbsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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_add socat curl libiconv
usesh: true
@@ -404,13 +401,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/netbsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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: |
/usr/sbin/pkg_add curl socat
@@ -462,13 +458,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/dragonflybsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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 libnghttp2
@@ -524,13 +519,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/midnightbsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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: mport install socat curl || true
usesh: true
@@ -582,13 +576,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/solaris-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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: |
@@ -642,13 +635,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/omnios-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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: pkg install socat
@@ -699,13 +691,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/openindiana-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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: pkg install socat
@@ -756,13 +747,12 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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 }}
- cache-after-prepare: true
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
@@ -813,140 +803,19 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v7
+ - 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/haiku-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
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: rsync
copyback: false
prepare: |
mkdir -p /boot/home/.cache
pkgman install -y cronie
-
- 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"
-
-
-
- Hurd:
- runs-on: ubuntu-latest
- needs: Haiku
- 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
- TokenName1: ${{ secrets.TokenName1}}
- TokenName2: ${{ secrets.TokenName2}}
- TokenName3: ${{ secrets.TokenName3}}
- TokenName4: ${{ secrets.TokenName4}}
- TokenName5: ${{ secrets.TokenName5}}
- steps:
- - uses: actions/checkout@v7
- - name: Clone acmetest
- run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- - uses: vmactions/hurd-vm@v1
- with:
- debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
- 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: rsync
- copyback: false
- usesh: true
- prepare: |
- apt-get update -y
- apt-get install -y curl cron
- 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"
-
-
-
- OpenEuler:
- runs-on: ubuntu-latest
- needs: Hurd
- 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
- TokenName1: ${{ secrets.TokenName1}}
- TokenName2: ${{ secrets.TokenName2}}
- TokenName3: ${{ secrets.TokenName3}}
- TokenName4: ${{ secrets.TokenName4}}
- TokenName5: ${{ secrets.TokenName5}}
- steps:
- - uses: actions/checkout@v7
- - name: Clone acmetest
- run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- - uses: vmactions/openeuler-vm@v1
- with:
- debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
- 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: rsync
- copyback: false
- usesh: true
- prepare: dnf install -y curl socat cronie tar gzip
+
run: |
if [ "${{ secrets.TokenName1}}" ] ; then
export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
diff --git a/.github/workflows/DragonFlyBSD.yml b/.github/workflows/DragonFlyBSD.yml
index 20c61dcc..c8cbc985 100644
--- a/.github/workflows/DragonFlyBSD.yml
+++ b/.github/workflows/DragonFlyBSD.yml
@@ -58,7 +58,6 @@ jobs:
- uses: vmactions/dragonflybsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/FreeBSD.yml b/.github/workflows/FreeBSD.yml
index 88ef0a6e..50fcab32 100644
--- a/.github/workflows/FreeBSD.yml
+++ b/.github/workflows/FreeBSD.yml
@@ -64,7 +64,6 @@ jobs:
- uses: vmactions/freebsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/GhostBSD.yml b/.github/workflows/GhostBSD.yml
index 04510c15..c77fdf2e 100644
--- a/.github/workflows/GhostBSD.yml
+++ b/.github/workflows/GhostBSD.yml
@@ -66,7 +66,6 @@ jobs:
- uses: vmactions/ghostbsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/Haiku.yml b/.github/workflows/Haiku.yml
index b133dd18..9884ebeb 100644
--- a/.github/workflows/Haiku.yml
+++ b/.github/workflows/Haiku.yml
@@ -65,7 +65,6 @@ jobs:
- uses: vmactions/haiku-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/Hurd.yml b/.github/workflows/Hurd.yml
deleted file mode 100644
index fee80d29..00000000
--- a/.github/workflows/Hurd.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: Hurd
-on:
- push:
- branches:
- - '*'
- paths:
- - '*.sh'
- - '.github/workflows/Hurd.yml'
-
- pull_request:
- branches:
- - dev
- paths:
- - '*.sh'
- - '.github/workflows/Hurd.yml'
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-
-
-jobs:
- Hurd:
- strategy:
- matrix:
- include:
- - TEST_ACME_Server: "LetsEncrypt.org_test"
- CA_ECDSA: ""
- CA: ""
- CA_EMAIL: ""
- TEST_PREFERRED_CHAIN: (STAGING)
- 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 }}
- steps:
- - uses: actions/checkout@v7
- - 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/hurd-vm@v1
- with:
- debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
- envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
- nat: |
- "8080": "80"
- # Do NOT install socat: socat's SYSTEM: address is broken on GNU Hurd
- # (the child shell output goes to socat's stdout instead of the socket,
- # so clients get an empty reply). Without socat, acme.sh standalone
- # mode falls back to its python3 server, which works on Hurd.
- prepare: |
- apt-get update -y
- apt-get install -y curl cron
- usesh: true
- sync: rsync
- copyback: false
- 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"
diff --git a/.github/workflows/MidnightBSD.yml b/.github/workflows/MidnightBSD.yml
index ce499e4e..15024833 100644
--- a/.github/workflows/MidnightBSD.yml
+++ b/.github/workflows/MidnightBSD.yml
@@ -58,7 +58,6 @@ jobs:
- uses: vmactions/midnightbsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml
index 6695f71e..16d0ae2d 100644
--- a/.github/workflows/NetBSD.yml
+++ b/.github/workflows/NetBSD.yml
@@ -58,7 +58,6 @@ jobs:
- uses: vmactions/netbsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/Omnios.yml b/.github/workflows/Omnios.yml
index aabb168b..eb486b35 100644
--- a/.github/workflows/Omnios.yml
+++ b/.github/workflows/Omnios.yml
@@ -64,7 +64,6 @@ jobs:
- uses: vmactions/omnios-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml
index 46318163..4fdb76c5 100644
--- a/.github/workflows/OpenBSD.yml
+++ b/.github/workflows/OpenBSD.yml
@@ -64,7 +64,6 @@ jobs:
- uses: vmactions/openbsd-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/OpenEuler.yml b/.github/workflows/OpenEuler.yml
deleted file mode 100644
index 2b4bd0ab..00000000
--- a/.github/workflows/OpenEuler.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: OpenEuler
-on:
- push:
- branches:
- - '*'
- paths:
- - '*.sh'
- - '.github/workflows/OpenEuler.yml'
-
- pull_request:
- branches:
- - dev
- paths:
- - '*.sh'
- - '.github/workflows/OpenEuler.yml'
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-
-
-jobs:
- OpenEuler:
- strategy:
- matrix:
- include:
- - TEST_ACME_Server: "LetsEncrypt.org_test"
- CA_ECDSA: ""
- CA: ""
- CA_EMAIL: ""
- TEST_PREFERRED_CHAIN: (STAGING)
- 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 }}
- steps:
- - uses: actions/checkout@v7
- - 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/openeuler-vm@v1
- with:
- debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
- envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
- nat: |
- "8080": "80"
- prepare: dnf install -y curl socat cronie tar gzip
- usesh: true
- sync: rsync
- copyback: false
- 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"
diff --git a/.github/workflows/OpenIndiana.yml b/.github/workflows/OpenIndiana.yml
index e3119f8e..b5061ba7 100644
--- a/.github/workflows/OpenIndiana.yml
+++ b/.github/workflows/OpenIndiana.yml
@@ -64,7 +64,6 @@ jobs:
- uses: vmactions/openindiana-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/Solaris.yml b/.github/workflows/Solaris.yml
index 30e4e291..3393269a 100644
--- a/.github/workflows/Solaris.yml
+++ b/.github/workflows/Solaris.yml
@@ -64,7 +64,6 @@ jobs:
- uses: vmactions/solaris-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/Tribblix.yml b/.github/workflows/Tribblix.yml
index 68e61dc8..cd43e0e3 100644
--- a/.github/workflows/Tribblix.yml
+++ b/.github/workflows/Tribblix.yml
@@ -64,7 +64,6 @@ jobs:
- uses: vmactions/tribblix-vm@v1
with:
debug-on-error: ${{ vars.DEBUG_ON_ERROR }}
- cache-after-prepare: true
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
diff --git a/.github/workflows/vtag.yml b/.github/workflows/vtag.yml
deleted file mode 100644
index e9f7e8df..00000000
--- a/.github/workflows/vtag.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: Mirror version tag
-
-# Historical release tags are plain version numbers ("3.1.3") and cannot be
-# renamed. When a plain version tag is pushed (including the tag created by
-# publishing a GitHub release), mirror it as a "v"-prefixed tag ("v3.1.3")
-# pointing to the same object, so both forms exist.
-# No retrigger loop: the tag filter never matches a "v"-prefixed tag, and
-# refs created with GITHUB_TOKEN do not fire workflows anyway.
-
-on:
- push:
- tags:
- - '[0-9]*'
-
-permissions:
- contents: write
-
-jobs:
- vtag:
- if: github.repository == 'acmesh-official/acme.sh'
- runs-on: ubuntu-latest
- steps:
- - name: Create the v-prefixed tag
- env:
- GH_TOKEN: ${{ github.token }}
- run: |
- if gh api "repos/${{ github.repository }}/git/ref/tags/v${{ github.ref_name }}" >/dev/null 2>&1; then
- echo "Tag v${{ github.ref_name }} already exists, nothing to do."
- exit 0
- fi
- gh api "repos/${{ github.repository }}/git/refs" -f ref="refs/tags/v${{ github.ref_name }}" -f sha="${{ github.sha }}"
- echo "Created tag v${{ github.ref_name }} -> ${{ github.sha }}"
diff --git a/README.md b/README.md
index 90280e94..b93a8a50 100644
--- a/README.md
+++ b/README.md
@@ -36,8 +36,6 @@
-
-
@@ -132,8 +130,6 @@
|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
-|28|[](https://github.com/acmesh-official/acme.sh/actions/workflows/Hurd.yml)|GNU Hurd
-|29|[](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenEuler.yml)|openEuler
> ๐งช Check our [testing project](https://github.com/acmesh-official/acmetest)
diff --git a/acme.sh b/acme.sh
index 4d4f6cbe..a5f53469 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env sh
-VER=3.1.5
+VER=3.1.4
PROJECT_NAME="acme.sh"
@@ -2715,21 +2715,6 @@ _clearcaconf() {
_clear_conf "$CA_CONF" "$1"
}
-#Starts a socat listener in the background, the pid is set to _socat_pid.
-#It uses the content, _content_len, _NC and _SOCAT_ERR of _startserver.
-#options
-_startsocat() {
- _socat_opts="$1"
- _debug "_NC" "$_NC $_socat_opts"
- $_NC $_socat_opts SYSTEM:"sleep 1; \
-echo 'HTTP/1.0 200 OK'; \
-echo 'Content-Length\: $_content_len'; \
-echo ''; \
-printf '%s' '$content';" 2>>"$_SOCAT_ERR" &
- _socat_pid="$!"
- _debug "_socat_pid" "$_socat_pid"
-}
-
# content localaddress
_startserver() {
content="$1"
@@ -2743,24 +2728,16 @@ _startserver() {
_debug Le_Listen_V4 "$Le_Listen_V4"
_debug Le_Listen_V6 "$Le_Listen_V6"
- _serverproc_v6=""
if _exists "socat"; then
_NC="socat"
- SOCAT_OPTIONS6=""
- if [ "$Le_Listen_V6" ] && [ -z "$Le_Listen_V4" ]; then
+ if [ "$Le_Listen_V6" ]; then
_NC="$_NC -6"
SOCAT_OPTIONS=TCP6-LISTEN
- elif [ "$Le_Listen_V4" ] && [ -z "$Le_Listen_V6" ]; then
+ elif [ "$Le_Listen_V4" ]; then
_NC="$_NC -4"
SOCAT_OPTIONS=TCP4-LISTEN
- elif [ "$ncaddr" ]; then
- #a single local address belongs to a single family, let socat pick it
- SOCAT_OPTIONS=TCP-LISTEN
else
- #listen on both ipv4 and ipv6, with one socket for each family:
- #ipv4-mapped ipv6 addresses are not available everywhere.
- SOCAT_OPTIONS=TCP4-LISTEN
- SOCAT_OPTIONS6=TCP6-LISTEN
+ SOCAT_OPTIONS=TCP-LISTEN
fi
if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
@@ -2768,10 +2745,6 @@ _startserver() {
fi
SOCAT_OPTIONS=$SOCAT_OPTIONS:$Le_HTTPPort,crlf,reuseaddr,fork
- if [ "$SOCAT_OPTIONS6" ]; then
- #ipv6only keeps this socket from colliding with the ipv4 one
- SOCAT_OPTIONS6=$SOCAT_OPTIONS6:$Le_HTTPPort,crlf,reuseaddr,fork,ipv6only=1
- fi
#Adding bind to local-address
if [ "$ncaddr" ]; then
@@ -2780,14 +2753,14 @@ _startserver() {
_content_len="$(printf "%s" "$content" | wc -c)"
_debug _content_len "$_content_len"
+ _debug "_NC" "$_NC $SOCAT_OPTIONS"
export _SOCAT_ERR="$(_mktemp)"
- _startsocat "$SOCAT_OPTIONS"
- serverproc="$_socat_pid"
- if [ "$SOCAT_OPTIONS6" ]; then
- #best effort, the host may have no ipv6 support at all
- _startsocat "$SOCAT_OPTIONS6"
- _serverproc_v6="$_socat_pid"
- fi
+ $_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; \
+echo 'HTTP/1.0 200 OK'; \
+echo 'Content-Length\: $_content_len'; \
+echo ''; \
+printf '%s' '$content';" 2>"$_SOCAT_ERR" &
+ serverproc="$!"
else
_PYTHON=""
if _exists "python3"; then
@@ -2799,40 +2772,21 @@ _startserver() {
fi
if [ "$_PYTHON" ]; then
_debug "Using python: $_PYTHON"
- #a comma separated list of addresses to listen on, one socket for each
- _BIND_ADDR="0.0.0.0,::"
- if [ "$Le_Listen_V6" ] && [ -z "$Le_Listen_V4" ]; then
+ _AF="socket.AF_INET"
+ _BIND_ADDR="0.0.0.0"
+ if [ "$Le_Listen_V6" ]; then
+ _AF="socket.AF_INET6"
_BIND_ADDR="::"
- elif [ "$Le_Listen_V4" ] && [ -z "$Le_Listen_V6" ]; then
- _BIND_ADDR="0.0.0.0"
fi
if [ "$ncaddr" ]; then
_BIND_ADDR="$ncaddr"
fi
- _debug "_BIND_ADDR" "$_BIND_ADDR"
export _SOCAT_ERR="$(_mktemp)"
- $_PYTHON -c "import socket,sys,select
-res='HTTP/1.0 200 OK\r\nContent-Length: '+str(len(sys.argv[3]))+'\r\n\r\n'+sys.argv[3]
-ads=sys.argv[2].split(',')
-ls=[]
-for ad in ads:
- try:
- sk=socket.socket(socket.AF_INET6 if ':' in ad else socket.AF_INET,socket.SOCK_STREAM)
- sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
- if ':' in ad and len(ads)>1:
- sk.setsockopt(socket.IPPROTO_IPV6,socket.IPV6_V6ONLY,1)
- sk.bind((ad,int(sys.argv[1])))
- sk.listen(5)
- ls.append(sk)
- except Exception:
- sys.stderr.write(str(sys.exc_info()[1])+'\n')
-if not ls:
- sys.exit(1)
+ $_PYTHON -c "import socket,sys;s=socket.socket($_AF,socket.SOCK_STREAM);s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1);s.bind((sys.argv[2],int(sys.argv[1])));s.listen(5);res='HTTP/1.0 200 OK\r\nContent-Length: '+str(len(sys.argv[3]))+'\r\n\r\n'+sys.argv[3];
while True:
- for sk in select.select(ls,[],[])[0]:
- c,a=sk.accept()
- c.sendall(res.encode() if hasattr(res, 'encode') else res)
- c.close()" "$Le_HTTPPort" "$_BIND_ADDR" "$content" 2>"$_SOCAT_ERR" &
+ c,a=s.accept()
+ c.sendall(res.encode() if hasattr(res, 'encode') else res)
+ c.close()" "$Le_HTTPPort" "$_BIND_ADDR" "$content" 2>"$_SOCAT_ERR" &
serverproc="$!"
_NC="$_PYTHON"
else
@@ -2855,11 +2809,6 @@ while True:
_stopserver() {
pid="$1"
_debug "pid" "$pid"
- if [ "$_serverproc_v6" ]; then
- _debug "_serverproc_v6" "$_serverproc_v6"
- kill $_serverproc_v6 >/dev/null 2>&1
- _serverproc_v6=""
- fi
if [ -z "$pid" ]; then
rm -f "$_SOCAT_ERR"
return
@@ -2933,11 +2882,9 @@ _starttlsserver() {
_debug Le_Listen_V4 "$Le_Listen_V4"
_debug Le_Listen_V6 "$Le_Listen_V6"
- #openssl s_server binds a single socket, so both options together can only
- #mean: do not force a family, same as when neither of them is given.
- if [ "$Le_Listen_V4" ] && [ -z "$Le_Listen_V6" ]; then
+ if [ "$Le_Listen_V4" ]; then
__S_OPENSSL="$__S_OPENSSL -4"
- elif [ "$Le_Listen_V6" ] && [ -z "$Le_Listen_V4" ]; then
+ elif [ "$Le_Listen_V6" ]; then
__S_OPENSSL="$__S_OPENSSL -6"
fi
@@ -4408,24 +4355,6 @@ deactivateaccount() {
fi
}
-#domain
-#Print the Validation Domain Name where the persistent TXT record must be
-#published: the "_validation-persist" label prepended to the domain being
-#validated (draft-ietf-acme-dns-persist-01 sec 4).
-#A wildcard identifier is validated by the record at its base domain, so the
-#leading "*." label is dropped: the wildcard scope comes from 'policy=wildcard'
-#in the record value, not from a "*" label in the record name (sec 5.1, 10.2).
-_dns_persist_txt_name() {
- _dpt_domain="$1"
- if _startswith "$_dpt_domain" "*."; then
- _dpt_domain="$(echo "$_dpt_domain" | sed 's/^\*\.//')"
- fi
- if [ -z "$_dpt_domain" ]; then
- return 1
- fi
- echo "_validation-persist.$_dpt_domain"
-}
-
#domain wildcard ca_name days
#Print the TXT record(s) the user must add to enable persistent DNS validation
#per draft-ietf-acme-dns-persist-01.
@@ -4440,20 +4369,6 @@ makednspersistvalue() {
return 1
fi
- _txt_name="$(_dns_persist_txt_name "$_mdpv_domain")"
- if [ -z "$_txt_name" ]; then
- _err "Invalid domain: $_mdpv_domain"
- return 1
- fi
- _debug _txt_name "$_txt_name"
-
- #A wildcard identifier can only be issued if the record carries
- #'policy=wildcard', so don't print a record that is guaranteed to fail.
- if _startswith "$_mdpv_domain" "*." && [ "$_mdpv_wildcard" != "1" ]; then
- _info "$_mdpv_domain is a wildcard domain, adding 'policy=wildcard' automatically."
- _mdpv_wildcard="1"
- fi
-
if [ -n "$_mdpv_days" ]; then
case "$_mdpv_days" in
'' | *[!0-9]*)
@@ -4485,6 +4400,8 @@ makednspersistvalue() {
fi
_debug "Account URL" "$_accUri"
+ _txt_name="_validation-persist.$_mdpv_domain"
+
_txt_suffix="; accounturi=$_accUri"
if [ "$_mdpv_wildcard" = "1" ]; then
_txt_suffix="$_txt_suffix; policy=wildcard"
@@ -5002,18 +4919,11 @@ issue() {
if [ -z "$_ACME_IS_RENEW" ]; then
_initpath "$_main_domain" "$_key_length"
mkdir -p "$DOMAIN_PATH"
- elif [ -z "$Le_Vlist" ]; then
- # Whether the saved order is resumed is decided by Le_Vlist below, so key
- # this on Le_Vlist too. With no pending order to resume a new one is
- # created, and a stale order link from the previous issuance must not be
- # reused. https://github.com/acmesh-official/acme.sh/issues/3635
+ elif ! _hasfield "$_web_roots" "$W_DNS"; then
Le_OrderFinalize=""
Le_LinkOrder=""
+ Le_LinkCert=""
fi
- # Per-run state only: it is set after finalize and never read back from the
- # saved domain conf. Carrying it over would make a run that gives up while
- # the order is still 'processing' download the previous certificate again.
- Le_LinkCert=""
if _hasfield "$_web_roots" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then
_err "$_DNS_MANUAL_ERROR"
@@ -6039,17 +5949,12 @@ $_authorizations_map"
_clearaccountconf "HTTPS_INSECURE"
fi
- if [ "$Le_Listen_V4" ] || [ "$Le_Listen_V6" ]; then
- if [ "$Le_Listen_V4" ]; then
- _savedomainconf "Le_Listen_V4" "$Le_Listen_V4"
- else
- _cleardomainconf Le_Listen_V4
- fi
- if [ "$Le_Listen_V6" ]; then
- _savedomainconf "Le_Listen_V6" "$Le_Listen_V6"
- else
- _cleardomainconf Le_Listen_V6
- fi
+ if [ "$Le_Listen_V4" ]; then
+ _savedomainconf "Le_Listen_V4" "$Le_Listen_V4"
+ _cleardomainconf Le_Listen_V6
+ elif [ "$Le_Listen_V6" ]; then
+ _savedomainconf "Le_Listen_V6" "$Le_Listen_V6"
+ _cleardomainconf Le_Listen_V4
fi
if [ "$Le_ForceNewDomainKey" = "1" ]; then
@@ -6173,10 +6078,7 @@ $_authorizations_map"
#some devices and APIs reject them, so the certs are stored back to back.
#https://github.com/acmesh-official/acme.sh/issues/1940
_strip_blank_lines() {
- #spell out space and tab: Solaris sed treats [[:space:]] as a literal
- #bracket set and silently stops matching the blank lines
- _sbl_tab="$(printf '\t')"
- sed "/^[ $_sbl_tab]*\$/d"
+ sed '/^[[:space:]]*$/d'
}
_split_cert_chain() {
@@ -7464,18 +7366,10 @@ deactivate() {
done
}
-#reads the output of "openssl x509 -text" from stdin, prints the hex AKI
-#the value is on the line right after the extension header; "grep -A" is not
-#portable (Solaris /usr/bin/grep: "illegal option -- A"), so select from the
-#header to EOF and keep the second line of that range
-_extractAKI() {
- sed -n '/X509v3 Authority Key Identifier/,$p' | _head_n 2 | _tail_n 1 | tr -d ': ' | sed "s/keyid//"
-}
-
#cert
_getAKI() {
_cert="$1"
- ${ACME_OPENSSL_BIN:-openssl} x509 -in "$_cert" -text -noout | _extractAKI
+ ${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
@@ -7631,15 +7525,6 @@ _installalias() {
_c_home="$1"
_initpath
- _alias_bin="$LE_WORKING_DIR/$PROJECT_ENTRY"
- if [ ! -f "$_alias_bin" ]; then
- #ACME_PACKAGED install: no copy in LE_WORKING_DIR, alias the current script
- _script="$(_readlink "$_SCRIPT_")"
- if [ -f "$_script" ]; then
- _alias_bin="$_script"
- fi
- fi
-
_envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
if [ "$_upgrading" ] && [ "$_upgrading" = "1" ]; then
echo "$(cat "$_envfile")" | sed "s|^LE_WORKING_DIR.*$||" >"$_envfile"
@@ -7657,7 +7542,7 @@ _installalias() {
else
_sed_i "/^export LE_CONFIG_HOME/d" "$_envfile"
fi
- _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$_alias_bin$_c_entry\""
+ _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY.completion" ]; then
#the completion file does nothing when sourced by a non-bash shell
_setopt "$_envfile" ". \"$LE_WORKING_DIR/$PROJECT_ENTRY.completion\""
@@ -7684,7 +7569,7 @@ _installalias() {
else
_sed_i "/^setenv LE_CONFIG_HOME/d" "$_cshfile"
fi
- _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$_alias_bin$_c_entry\""
+ _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
_setopt "$_csh_profile" "source \"$_cshfile\""
fi
@@ -7696,7 +7581,7 @@ _installalias() {
if [ "$_c_home" ]; then
_setopt "$_cshfile" "setenv LE_CONFIG_HOME" " " "\"$LE_CONFIG_HOME\""
fi
- _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$_alias_bin$_c_entry\""
+ _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY$_c_entry\""
_setopt "$_tcsh_profile" "source \"$_cshfile\""
fi
@@ -7770,38 +7655,30 @@ install() {
chmod 700 "$LE_CONFIG_HOME"
fi
- if [ "$ACME_PACKAGED" ]; then
- #the script and its hooks are managed by a system package manager,
- #do not copy them into LE_WORKING_DIR. https://github.com/acmesh-official/acme.sh/issues/7135
- _info "ACME_PACKAGED is set, skipping the script copy."
- else
- cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
+ cp "$PROJECT_ENTRY" "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
- if [ "$?" != "0" ]; then
- _err "Installation failed, cannot copy $PROJECT_ENTRY"
- return 1
- fi
+ if [ "$?" != "0" ]; then
+ _err "Installation failed, cannot copy $PROJECT_ENTRY"
+ return 1
+ fi
- _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
+ _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
- if [ -f "$PROJECT_ENTRY.completion" ]; then
- cp "$PROJECT_ENTRY.completion" "$LE_WORKING_DIR/"
- _debug "Installed bash completion to $LE_WORKING_DIR/$PROJECT_ENTRY.completion"
- fi
+ if [ -f "$PROJECT_ENTRY.completion" ]; then
+ cp "$PROJECT_ENTRY.completion" "$LE_WORKING_DIR/"
+ _debug "Installed bash completion to $LE_WORKING_DIR/$PROJECT_ENTRY.completion"
fi
if [ "$_ACME_IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then
_installalias "$_c_home"
fi
- if [ -z "$ACME_PACKAGED" ]; then
- for subf in $_SUB_FOLDERS; do
- if [ -d "$subf" ]; then
- mkdir -p "$LE_WORKING_DIR/$subf"
- cp "$subf"/* "$LE_WORKING_DIR"/"$subf"/
- fi
- done
- fi
+ for subf in $_SUB_FOLDERS; do
+ if [ -d "$subf" ]; then
+ mkdir -p "$LE_WORKING_DIR/$subf"
+ cp "$subf"/* "$LE_WORKING_DIR"/"$subf"/
+ fi
+ done
if [ ! -f "$ACCOUNT_CONF_PATH" ]; then
_initconf
@@ -7829,7 +7706,7 @@ install() {
installcronjob "$_c_home"
fi
- if [ -z "$NO_DETECT_SH" ] && [ -z "$ACME_PACKAGED" ]; then
+ if [ -z "$NO_DETECT_SH" ]; then
#Modify shebang
if _exists bash; then
_bash_path="$(bash -c "command -v bash 2>/dev/null")"
@@ -7854,9 +7731,7 @@ install() {
if [ "$_accountemail" ]; then
_saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
fi
- if [ -z "$ACME_PACKAGED" ]; then
- _saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
- fi
+ _saveaccountconf "UPGRADE_HASH" "$(_getUpgradeHash)"
_info OK
}
@@ -7870,12 +7745,8 @@ uninstall() {
_uninstallalias
- if [ -z "$ACME_PACKAGED" ]; then
- #don't remove the script when it is managed by a system package manager,
- #LE_WORKING_DIR may point to the packaged files
- rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY"
- rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY.completion"
- fi
+ rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY"
+ rm -f "$LE_WORKING_DIR/$PROJECT_ENTRY.completion"
_info "The keys and certs are in \"$(__green "$LE_CONFIG_HOME")\". You can remove them by yourself."
}
@@ -7911,24 +7782,20 @@ cron() {
_initpath
_info "$(__green "===Starting cron===")"
if [ "$AUTO_UPGRADE" = "1" ]; then
- if [ "$ACME_PACKAGED" ]; then
- _info "ACME_PACKAGED is set, skipping the auto upgrade."
- else
- export LE_WORKING_DIR
- (
- if ! upgrade; then
- _err "Cron: Upgrade failed!"
- return 1
- fi
- )
- . "$LE_WORKING_DIR/$PROJECT_ENTRY" >/dev/null
-
- if [ -t 1 ]; then
- __INTERACTIVE="1"
+ export LE_WORKING_DIR
+ (
+ if ! upgrade; then
+ _err "Cron: Upgrade failed!"
+ return 1
fi
+ )
+ . "$LE_WORKING_DIR/$PROJECT_ENTRY" >/dev/null
- _info "Automatically upgraded to: $VER"
+ if [ -t 1 ]; then
+ __INTERACTIVE="1"
fi
+
+ _info "Automatically upgraded to: $VER"
fi
_TREAT_SKIP_AS_SUCCESS="1"
renewAll
@@ -8170,9 +8037,7 @@ Parameters:
--dns-persist-wildcard Used with '--make-dns-persist-value'. Adds 'policy=wildcard' to the
generated TXT record so the issuer is also authorized for wildcards
- and subdomains (draft-ietf-acme-dns-persist-01). It is implied when
- the domain given to -d is a wildcard (e.g. '*.example.com'); the
- record itself is always published at the base domain.
+ and subdomains (draft-ietf-acme-dns-persist-01).
--dns-persist-ca-name Used with '--make-dns-persist-value'. Use the given CA identity domain
(e.g. 'ssl.com') as the issuer-domain-name in the TXT record. If
omitted, the identities are read from the ACME directory's
@@ -8230,9 +8095,8 @@ Parameters:
--ocsp, --ocsp-must-staple Generate OCSP-Must-Staple extension.
--always-force-new-domain-key Generate new domain key on renewal. Otherwise, the domain key is not changed by default.
--auto-upgrade [0|1] Valid for '--upgrade' command, indicating whether to upgrade automatically in future. Defaults to 1 if argument is omitted.
- --listen-v4 Force standalone/tls server to listen at ipv4 only.
- By default the standalone server listens on both ipv4 and ipv6.
- --listen-v6 Force standalone/tls server to listen at ipv6 only.
+ --listen-v4 Force standalone/tls server to listen at ipv4.
+ --listen-v6 Force standalone/tls server to listen at ipv6.
--request-v4 Force client requests to use ipv4 to connect to the CA server.
--request-v6 Force client requests to use ipv6 to connect to the CA server.
--openssl-bin Specifies a custom openssl bin location.
@@ -8261,10 +8125,6 @@ Parameters:
}
installOnline() {
- if [ "$ACME_PACKAGED" ]; then
- _err "ACME_PACKAGED is set: acme.sh is managed by the system package manager, please use it to upgrade."
- return 1
- fi
_info "Installing from online archive."
_branch="$BRANCH"
@@ -8322,10 +8182,6 @@ _getUpgradeHash() {
}
upgrade() {
- if [ "$ACME_PACKAGED" ]; then
- _err "ACME_PACKAGED is set: acme.sh is managed by the system package manager, please use it to upgrade."
- exit 1
- fi
if (
_initpath
[ -z "$FORCE" ] && [ "$(_getUpgradeHash)" = "$(_readaccountconf "UPGRADE_HASH")" ] && _info "Already up to date!" && exit 0
diff --git a/deploy/byteplus_alb.sh b/deploy/byteplus_alb.sh
index 394b431f..8443bb99 100644
--- a/deploy/byteplus_alb.sh
+++ b/deploy/byteplus_alb.sh
@@ -163,8 +163,8 @@ byteplus_alb_deploy() {
# โโ 3. Read cert and key โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# BytePlus requires NO blank lines between PEM blocks in the certificate chain
- _public_key=$(_strip_blank_lines <"$_cfullchain" | tr -d '\r')
- _private_key=$(_strip_blank_lines <"$_ckey" | tr -d '\r')
+ _public_key=$(sed '/^[[:space:]]*$/d' "$_cfullchain" | tr -d '\r')
+ _private_key=$(sed '/^[[:space:]]*$/d' "$_ckey" | tr -d '\r')
if [ -z "$_public_key" ] || [ -z "$_private_key" ]; then
_err "Failed to read certificate or key file."
diff --git a/deploy/cpanel_uapi.sh b/deploy/cpanel_uapi.sh
index 02ef6b3e..156044eb 100644
--- a/deploy/cpanel_uapi.sh
+++ b/deploy/cpanel_uapi.sh
@@ -87,11 +87,7 @@ cpanel_uapi_deploy() {
# Auto mode
if [ "$DEPLOY_CPANEL_AUTO_ENABLED" = "true" ]; then
# call API for site config
- if [ -n "$_uapi_user" ]; then
- _response=$(uapi --user="$_uapi_user" DomainInfo list_domains)
- else
- _response=$(uapi DomainInfo list_domains)
- fi
+ _response=$(uapi DomainInfo list_domains)
# exit if error in response
if [ -z "$_response" ] || [ "${_response#*"$uapi_error_response"}" != "$_response" ]; then
_err "Error in deploying certificate - cannot retrieve sitelist:"
diff --git a/deploy/ikuai.sh b/deploy/ikuai.sh
deleted file mode 100644
index fa0926dc..00000000
--- a/deploy/ikuai.sh
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/usr/bin/env sh
-
-# Here is a script to deploy cert to ikuai using curl
-#
-# it requires following environment variables:
-#
-# IKUAI_SCHEME="http" - http or https , defaults to "http"
-# IKUAI_HOSTNAME="localhost" - host , defaults to "192.168.9.1"
-# IKUAI_PORT="80" - port , defaults to "80"
-# IKUAI_USERNAME="admin" - username , defaults to "admin"
-# IKUAI_PASSWORD="yourPassword" - password
-# IKUAI_CERT_ID=1 - ikuai cert id , defaults to 1, and only 1 is supported for now !!!
-#
-#returns 0 means success, otherwise error.
-#
-######## Public functions #####################
-#
-#domain keyfile certfile cafile fullchain
-ikuai_deploy() {
- _cdomain="$1"
- _ckey="$2"
- _ccert="$3"
- _cca="$4"
- _cfullchain="$5"
-
- _debug _cdomain "$_cdomain"
- _debug _ckey "$_ckey"
- _debug _ccert "$_ccert"
- _debug _cca "$_cca"
- _debug _cfullchain "$_cfullchain"
-
- # Get deploy conf
- _getdeployconf IKUAI_SCHEME
- _getdeployconf IKUAI_HOSTNAME
- _getdeployconf IKUAI_PORT
- _getdeployconf IKUAI_USERNAME
- _getdeployconf IKUAI_PASSWORD
- _getdeployconf IKUAI_CERT_ID
-
- # Use default if not provided
- [ -n "$IKUAI_SCHEME" ] || IKUAI_SCHEME="http"
- [ -n "$IKUAI_HOSTNAME" ] || IKUAI_HOSTNAME="192.168.9.1"
- [ -n "$IKUAI_PORT" ] || IKUAI_PORT=80
- [ -n "$IKUAI_USERNAME" ] || IKUAI_USERNAME="admin"
- [ -n "$IKUAI_CERT_ID" ] || IKUAI_CERT_ID=1
-
- if [ -z "$IKUAI_PASSWORD" ]; then
- _err "please define IKUAI_PASSWORD."
- return 1
- fi
-
- _debug2 IKUAI_SCHEME "$IKUAI_SCHEME"
- _debug2 IKUAI_HOSTNAME "$IKUAI_HOSTNAME"
- _debug2 IKUAI_PORT "$IKUAI_PORT"
- _debug2 IKUAI_USERNAME "$IKUAI_USERNAME"
- _secure_debug2 IKUAI_PASSWORD "$IKUAI_PASSWORD"
-
- _info "Login to ikuai ..."
- _ikuai_url="$IKUAI_SCHEME://$IKUAI_HOSTNAME:$IKUAI_PORT"
- _pass_md5="$(printf "%s" "$IKUAI_PASSWORD" | _digest md5 hex | _lower_case)"
- _pass_salt="$(printf "salt_11%s" "$IKUAI_PASSWORD" | _base64)"
- _debug2 _ikuai_url "$_ikuai_url"
-
- _login_req="{\"username\":\"$IKUAI_USERNAME\",\"passwd\":\"$_pass_md5\",\"pass\":\"$_pass_salt\",\"remember_password\":\"\"}"
- _response=$(_post "$_login_req" "$_ikuai_url/Action/login" "" "POST" "application/json")
-
- _err_msg="$(printf "%s" "$_response" | _normalizeJson | _egrep_o '"ErrMsg":"[^"]*"' | cut -d'"' -f 4)"
- # check ErrMsg
- if [ "$_err_msg" != "Success" ]; then
- _err "Failed to login to ikuai: $_err_msg"
- return 1
- fi
- # check cookie
- _cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2 | sed 's/;.*//')"
- if [ -z "$_cookie" ]; then
- _err "Fail to get the cookie."
- return 1
- fi
-
- # Set cookie header
- _H1="Cookie: $_cookie; username=$IKUAI_USERNAME; login=1"
-
- _info "Deploy the cert to ikuai ... "
-
- # Should replace \n to @ ," " to #
- _cert_content_single_line="$(tr <"$_cfullchain" '\n' '@' | tr ' ' '#')"
- _key_content_single_line="$(tr <"$_ckey" '\n' '@' | tr ' ' '#')"
-
- _debug2 _cert_content_single_line "$_cert_content_single_line"
- _secure_debug2 _key_content_single_line "$_key_content_single_line"
-
- _key_manager_req="{\"func_name\":\"key_manager\",\"action\":\"save\",\"param\":{\"ca\":\"$_cert_content_single_line\",\"key\":\"$_key_content_single_line\",\"id\":$IKUAI_CERT_ID,\"enabled\":\"yes\",\"comment\":\"\"}}"
- _response=$(_post "$_key_manager_req" "$_ikuai_url/Action/call" "" "POST" "application/json")
-
- _err_msg="$(printf "%s" "$_response" | _normalizeJson | _egrep_o '"ErrMsg":"[^"]*"' | cut -d'"' -f 4)"
- # check ErrMsg
- if [ "$_err_msg" != "Success" ]; then
- _err "Failed to deploy the cert to ikuai: $_err_msg"
- return 1
- fi
-
- _info "Save the deploy config ... "
- # Save the config
- _savedeployconf IKUAI_SCHEME "$IKUAI_SCHEME"
- _savedeployconf IKUAI_HOSTNAME "$IKUAI_HOSTNAME"
- _savedeployconf IKUAI_PORT "$IKUAI_PORT"
- _savedeployconf IKUAI_USERNAME "$IKUAI_USERNAME"
- _savedeployconf IKUAI_PASSWORD "$IKUAI_PASSWORD"
- _savedeployconf IKUAI_CERT_ID "$IKUAI_CERT_ID"
-
- _info "Successfully deployed certificate to ikuai. Enjoy! :>"
-
- return 0
-}
diff --git a/deploy/keyhelp.sh b/deploy/keyhelp.sh
index f66d27ce..97f9c21c 100644
--- a/deploy/keyhelp.sh
+++ b/deploy/keyhelp.sh
@@ -83,7 +83,7 @@ keyhelp_deploy() {
_request_body="submit=1&certificate_name=$certificate_name&add_type=upload&text_private_key=$encoded_key&text_certificate=$encoded_ccert&text_ca_certificate=$encoded_cca"
_H1="Cookie: $_cookie"
_response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=ssl_certificates&action=add" "" "POST")
- _message=$(echo "$_response" | sed -n '//,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
+ _message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/
/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
_info "_message" "$_message"
if [ -z "$_message" ]; then
_err "Fail to upload certificate."
@@ -118,7 +118,7 @@ keyhelp_deploy() {
_request_body="submit=1&id=$DOMAIN_ID&target_type=$target_type&path=$path&is_prefer_https=$is_prefer_https&hsts_enabled=$hsts_enabled&certificate_type=custom&certificate_id=$cert_value&enforce_https=$DEPLOY_KEYHELP_ENFORCE_HTTPS"
_response=$(_post "$_request_body" "$DEPLOY_KEYHELP_BASEURL/index.php?page=domains&action=edit" "" "POST")
- _message=$(echo "$_response" | sed -n '/
/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
+ _message=$(echo "$_response" | grep -A 2 'message-body' | sed -n '/
/,/<\/div>/{//!p;}' | sed 's/<[^>]*>//g' | sed 's/^ *//;s/ *$//')
_info "_message" "$_message"
if [ -z "$_message" ]; then
_err "Fail to apply certificate."
diff --git a/deploy/multideploy.sh b/deploy/multideploy.sh
index 4a8c9dc9..375668ec 100644
--- a/deploy/multideploy.sh
+++ b/deploy/multideploy.sh
@@ -10,10 +10,6 @@
# Usage (shown values are the examples):
# 1. Set optional environment variables
# - export MULTIDEPLOY_FILENAME="multideploy.yaml" - "multideploy.yml" will be automatically used if not set"
-# A name without a leading '/' is looked up in the certificate directory
-# of the domain. An absolute path is used as is, so a single deploy file
-# can be shared by all domains, e.g.
-# - export MULTIDEPLOY_FILENAME="/etc/acme/multideploy.yml"
#
# 2. Run command:
# acme.sh --deploy --deploy-hook multideploy -d example.com
@@ -53,7 +49,7 @@ multideploy_deploy() {
_debug _cfullchain "$_cfullchain"
_debug _cpfx "$_cpfx"
- _getdeployconf MULTIDEPLOY_FILENAME
+ MULTIDEPLOY_FILENAME="${MULTIDEPLOY_FILENAME:-$(_getdeployconf MULTIDEPLOY_FILENAME)}"
if [ -z "$MULTIDEPLOY_FILENAME" ]; then
MULTIDEPLOY_FILENAME="multideploy.yml"
_info "MULTIDEPLOY_FILENAME is not set, so I will use 'multideploy.yml'."
@@ -79,8 +75,7 @@ multideploy_deploy() {
# This function preprocesses the deploy file by checking if 'yq' is installed,
# verifying the existence of the deploy file, and ensuring only one deploy file is present.
# Arguments:
-# $@ - Posible deploy file names. A name starting with '/' is treated as an
-# absolute path, any other name is relative to the domain directory.
+# $@ - Posible deploy file names.
# Usage:
# _preprocess_deployfile "
" "?"
_preprocess_deployfile() {
@@ -92,21 +87,15 @@ _preprocess_deployfile() {
_debug3 "yq is installed."
# Check if deploy file exists
- found_file=""
for file in "$@"; do
- if _startswith "$file" "/"; then
- _multideploy_path="$file"
- else
- _multideploy_path="$DOMAIN_PATH/$file"
- fi
- _debug3 "Checking file" "$_multideploy_path"
- if [ -f "$_multideploy_path" ]; then
+ _debug3 "Checking file" "$DOMAIN_PATH/$file"
+ if [ -f "$DOMAIN_PATH/$file" ]; then
_debug3 "File found"
if [ -n "$found_file" ]; then
_err "Multiple deploy files found. Please keep only one deploy file."
return 1
fi
- found_file="$_multideploy_path"
+ found_file="$file"
else
_debug3 "File not found"
fi
@@ -116,12 +105,12 @@ _preprocess_deployfile() {
_err "Deploy file not found. Go to https://github.com/acmesh-official/acme.sh/wiki/deployhooks#36-deploying-to-multiple-services-with-the-same-hooks to see how to create one."
return 1
fi
- if ! _check_deployfile "$found_file"; then
- _err "Deploy file is not valid: $found_file"
+ if ! _check_deployfile "$DOMAIN_PATH/$found_file"; then
+ _err "Deploy file is not valid: $DOMAIN_PATH/$found_file"
return 1
fi
- echo "$found_file"
+ echo "$DOMAIN_PATH/$found_file"
}
# Description:
diff --git a/deploy/proxmoxbs.sh b/deploy/proxmoxbs.sh
index 30599a44..179b0369 100644
--- a/deploy/proxmoxbs.sh
+++ b/deploy/proxmoxbs.sh
@@ -116,24 +116,17 @@ HEREDOC
export HTTPS_INSECURE=1
export _H1="Authorization: PBSAPIToken=${_proxmoxbs_header_api_token}"
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
- _retval=$?
- # The API errors out with a non-2xx HTTP status and an empty body,
- # so the status line is checked too, not only the response body.
- _status_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
- _debug2 "HTTP status" "$_status_code"
response="$(echo "$response" | _json_decode | _normalizeJson)"
message=$(echo "$response" | _egrep_o '"message":"[^"]*' | cut -d : -f 2 | tr -d '"')
- case "$_status_code" in
- 2[0-9][0-9])
- if [ "${_retval}" -eq 0 ] && [ -z "$message" ]; then
- _debug3 response "$response"
- _info "Certificate successfully deployed"
- return 0
- fi
- ;;
- esac
- _err "Certificate deployment failed (HTTP status $_status_code). $message"
- _debug "Response" "$response"
- return 1
+ _retval=$?
+ if [ "${_retval}" -eq 0 ] && [ -z "$message" ]; then
+ _debug3 response "$response"
+ _info "Certificate successfully deployed"
+ return 0
+ else
+ _err "Certificate deployment failed: $message"
+ _debug "Response" "$response"
+ return 1
+ fi
}
diff --git a/deploy/proxmoxve.sh b/deploy/proxmoxve.sh
index fd8d69d8..b6298ee7 100644
--- a/deploy/proxmoxve.sh
+++ b/deploy/proxmoxve.sh
@@ -128,24 +128,17 @@ HEREDOC
export HTTPS_INSECURE=1
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
- _retval=$?
- # The API errors out with a non-2xx HTTP status and an empty body,
- # so the status line is checked too, not only the response body.
- _status_code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
- _debug2 "HTTP status" "$_status_code"
response="$(echo "$response" | _json_decode | _normalizeJson)"
message=$(echo "$response" | _egrep_o '"message":"[^"]*' | cut -d : -f 2 | tr -d '"')
- case "$_status_code" in
- 2[0-9][0-9])
- if [ "${_retval}" -eq 0 ] && [ -z "$message" ]; then
- _debug3 response "$response"
- _info "Certificate successfully deployed"
- return 0
- fi
- ;;
- esac
- _err "Certificate deployment failed (HTTP status $_status_code). $message"
- _debug "Response" "$response"
- return 1
+ _retval=$?
+ if [ "${_retval}" -eq 0 ] && [ -z "$message" ]; then
+ _debug3 response "$response"
+ _info "Certificate successfully deployed"
+ return 0
+ else
+ _err "Certificate deployment failed: $message"
+ _debug "Response" "$response"
+ return 1
+ fi
}
diff --git a/deploy/shelly.sh b/deploy/shelly.sh
deleted file mode 100644
index dbdab346..00000000
--- a/deploy/shelly.sh
+++ /dev/null
@@ -1,280 +0,0 @@
-#!/usr/bin/env sh
-
-# Here is a script to deploy cert to a Shelly Gen3+ device.
-# Deploy the HTTPS server certificate to a Shelly device on the local network.
-#
-# ```sh
-# export SHELLY_HOST=192.168.1.100
-# export SHELLY_PASSWORD=mysecret # only if auth is enabled on the device
-# acme.sh --deploy -d shelly.example.com --deploy-hook shelly
-# ```
-#
-# Environment variables:
-# SHELLY_HOST (required) IP or hostname of the Shelly device
-# SHELLY_PASSWORD (optional) Admin password for digest authentication.
-# Omit if auth is disabled on the device.
-# SHELLY_USER (optional) Username for auth. Default: admin
-# SHELLY_REBOOT (optional) Set to "0" to skip auto-reboot.
-# Default: 1 (reboot after upload)
-#
-# Requirements:
-# - Shelly Gen3+ device (Gen4 recommended)
-# - Firmware 2.0.0+ for HTTPS server certificate support
-# - curl or wget
-# - openssl (for SHA-256 digest and random cnonce)
-#
-# The device must be reachable via HTTP on the local network.
-# The hook uploads the fullchain.pem and private key,
-# then reboots the device to apply the new certificate.
-#
-# Authentication uses standard RFC 7616 HTTP Digest (SHA-256) since
-# firmware 2.0.0. The JSON-RPC auth object is not used for HTTP transport.
-#
-# returns 0 means success, otherwise error.
-
-######## Public functions #####################
-
-#domain keyfile certfile cafile fullchain
-shelly_deploy() {
- _cdomain="$1"
- _ckey="$2"
- _ccert="$3"
- _cca="$4"
- _cfullchain="$5"
-
- _debug _cdomain "$_cdomain"
- _debug _ckey "$_ckey"
- _debug _ccert "$_ccert"
- _debug _cca "$_cca"
- _debug _cfullchain "$_cfullchain"
-
- _getdeployconf SHELLY_HOST
- _getdeployconf SHELLY_PASSWORD
- _getdeployconf SHELLY_USER
- _getdeployconf SHELLY_REBOOT
-
- _debug SHELLY_HOST "$SHELLY_HOST"
- _debug SHELLY_USER "$SHELLY_USER"
- _secure_debug SHELLY_PASSWORD "$SHELLY_PASSWORD"
- _debug SHELLY_REBOOT "$SHELLY_REBOOT"
-
- if [ -z "$SHELLY_HOST" ]; then
- _err "SHELLY_HOST is required. Please set the IP or hostname of your Shelly device."
- return 1
- fi
-
- SHELLY_USER="${SHELLY_USER:-admin}"
- SHELLY_REBOOT="${SHELLY_REBOOT:-1}"
-
- _savedeployconf SHELLY_HOST "$SHELLY_HOST"
- _savedeployconf SHELLY_PASSWORD "$SHELLY_PASSWORD"
- _savedeployconf SHELLY_USER "$SHELLY_USER"
- _savedeployconf SHELLY_REBOOT "$SHELLY_REBOOT"
-
- # --- Auth handshake (only if password is set) ---
- _shelly_auth_header=""
- if [ -n "$SHELLY_PASSWORD" ]; then
- _info "Authenticating to Shelly device at $SHELLY_HOST"
- if ! _shelly_handshake; then
- _err "Authentication handshake failed. Check SHELLY_PASSWORD and device accessibility."
- return 1
- fi
- _info "Authentication successful"
- fi
-
- # --- Upload certificate ---
- _info "Uploading certificate to Shelly device at $SHELLY_HOST"
- if ! _shelly_upload_cert; then
- _err "Certificate upload failed"
- return 1
- fi
-
- # --- Upload key ---
- _info "Uploading private key to Shelly device"
- if ! _shelly_upload_key; then
- _err "Private key upload failed"
- return 1
- fi
-
- _info "Certificate and key uploaded successfully"
-
- # --- Reboot ---
- if [ "$SHELLY_REBOOT" != "0" ]; then
- _info "Rebooting Shelly device to apply certificate"
- # Reboot may close the connection before sending a response
- _shelly_rpc "Shelly.Reboot" '{}' || _debug "Reboot may have closed connection (expected)"
- _info "Reboot command sent. Device will restart shortly."
- else
- _info "Skipping reboot (SHELLY_REBOOT=0). Certificate will apply on next restart."
- fi
-
- # Clear auth header so it does not leak to other hooks
- export _H1=""
-
- return 0
-}
-
-# --- Helper functions ---
-
-# Perform RFC 7616 HTTP Digest auth handshake.
-# Sets _shelly_auth_header on success (the Authorization header value).
-_shelly_handshake() {
- _inithttp
-
- _debug "Probing device for auth challenge"
-
- # Use a protected method (Shelly.GetStatus) to trigger 401.
- # Shelly.GetDeviceInfo is excluded from auth and would miss the challenge.
- _post '{"id":1,"method":"Shelly.GetStatus"}' \
- "http://${SHELLY_HOST}/rpc" "" "" "application/json"
-
- # Detect auth from HTTP status line rather than response body
- if ! _shelly_has_auth_challenge "$HTTP_HEADER"; then
- # No auth challenge โ device accepted the request without credentials
- _debug "Device responded without auth challenge. Proceeding without auth."
- return 0
- fi
-
- _shelly_realm="$(grep -i '^WWW-Authenticate:' "$HTTP_HEADER" | sed 's/.*realm="//;s/".*//')"
- _shelly_nonce="$(grep -i '^WWW-Authenticate:' "$HTTP_HEADER" | sed 's/.*nonce="//;s/".*//')"
- _shelly_qop="$(grep -i '^WWW-Authenticate:' "$HTTP_HEADER" | sed 's/.*qop="//;s/".*//')"
-
- if [ -z "$_shelly_nonce" ]; then
- _err "Failed to extract nonce from WWW-Authenticate header. Is SHELLY_PASSWORD correct?"
- return 1
- fi
-
- _shelly_qop="${_shelly_qop:-auth}"
-
- _debug "Shelly realm: $_shelly_realm"
- _debug "Shelly qop: $_shelly_qop"
- _secure_debug "Shelly nonce" "$_shelly_nonce"
-
- # ha1 = SHA256(username:realm:password)
- _shelly_ha1="$(printf '%s' "${SHELLY_USER}:${_shelly_realm}:${SHELLY_PASSWORD}" | _digest sha256 hex)"
- _secure_debug "Shelly ha1" "$_shelly_ha1"
-
- # Generate client nonce (openssl is required for _digest, so always available)
- _shelly_cnonce="$(${ACME_OPENSSL_BIN:-openssl} rand -hex 8 2>/dev/null)"
- _debug "Shelly cnonce: $_shelly_cnonce"
-
- # Build the digest Authorization header value (stored for reuse)
- _shelly_nc=1
- _shelly_build_auth_header
-
- return 0
-}
-
-# Check whether the HTTP response headers contain a digest auth challenge.
-# Returns 0 (true) if a 401 with WWW-Authenticate is present.
-_shelly_has_auth_challenge() {
- _shelly_headers_file="$1"
- _shelly_status="$(grep -i '^HTTP/' "$_shelly_headers_file" | _tail_n 1 | awk '{print $2}')"
- [ "$_shelly_status" = "401" ] && grep -qi '^WWW-Authenticate:' "$_shelly_headers_file"
-}
-
-# Build or rebuild the RFC 7616 Authorization header.
-# Uses: _shelly_ha1, _shelly_nonce, _shelly_cnonce, _shelly_qop, _shelly_realm, _shelly_nc
-# Sets: _shelly_auth_header
-_shelly_build_auth_header() {
- _shelly_nc_hex="$(printf '%08x' "$_shelly_nc")"
-
- # ha2 = SHA256(POST:/rpc)
- _shelly_ha2="$(printf '%s' "POST:/rpc" | _digest sha256 hex)"
-
- # response = SHA256(ha1:nonce:nc:cnonce:qop:ha2)
- _shelly_digest_response="$(printf '%s' "${_shelly_ha1}:${_shelly_nonce}:${_shelly_nc_hex}:${_shelly_cnonce}:${_shelly_qop}:${_shelly_ha2}" | _digest sha256 hex)"
-
- # Build the Authorization header value (without the "Authorization: " prefix)
- _shelly_auth_header="Digest username=\"${SHELLY_USER}\", realm=\"${_shelly_realm}\", nonce=\"${_shelly_nonce}\", uri=\"/rpc\", qop=${_shelly_qop}, nc=${_shelly_nc_hex}, cnonce=\"${_shelly_cnonce}\", response=\"${_shelly_digest_response}\", algorithm=SHA-256"
-
- _secure_debug "Authorization header" "$_shelly_auth_header"
-}
-
-# Make a Shelly JSON-RPC call.
-# Usage: _shelly_rpc
-# Returns 0 on success, 1 on error.
-_shelly_rpc() {
- _shelly_method="$1"
- _shelly_params="$2"
-
- _shelly_body='{"id":1,"method":"'"$_shelly_method"'","params":'"$_shelly_params"'}'
-
- _debug "RPC method: $_shelly_method"
- _debug2 "RPC body: $_shelly_body"
-
- # shellcheck disable=SC2090
- if [ -n "$_shelly_auth_header" ]; then
- export _H1="Authorization: $_shelly_auth_header"
- else
- export _H1=""
- fi
-
- _post "$_shelly_body" "http://${SHELLY_HOST}/rpc" "" "" "application/json"
- _shelly_ret=$?
-
- if [ "$_shelly_ret" != "0" ]; then
- _err "HTTP request failed for $_shelly_method (curl/wget error $_shelly_ret)"
- return 1
- fi
-
- # Empty response means something went wrong (auth required but not provided, etc.)
- if [ -z "$response" ]; then
- _err "Empty response from Shelly device. If authentication is enabled on the device, set SHELLY_PASSWORD."
- return 1
- fi
-
- # Validate response looks like a Shelly JSON-RPC response.
- # Catches non-JSON responses such as HTTP 429 "Too Many Requests" which
- # would otherwise pass the empty and "error" checks below.
- if ! _startswith "$response" '{' || ! _contains "$response" '"id"'; then
- _err "Invalid response from Shelly device: $response"
- return 1
- fi
-
- # Check for JSON-RPC error in response
- if _contains "$response" '"error"'; then
- _err "RPC error from Shelly: $response"
- return 1
- fi
-
- _debug "RPC response: $response"
-
- # Increment nonce counter and rebuild auth header for next request
- if [ -n "$_shelly_auth_header" ]; then
- _shelly_nc=$((_shelly_nc + 1))
- _shelly_build_auth_header
- fi
-
- return 0
-}
-
-# Upload the certificate to the device.
-# Note: We do NOT clear the existing certificate first, because the Shelly
-# auto-removes all three files (cert, key, CA) when any one is cleared.
-# Uploading overwrites in place โ no clearing needed.
-_shelly_upload_cert() {
- _shelly_cert_data="$(_json_encode <"$_cfullchain")"
-
- _debug "Uploading certificate"
- if ! _shelly_rpc "Shelly.PutHTTPServerCert" '{"data":"'"$_shelly_cert_data"'"}'; then
- _err "Failed to upload certificate to device"
- return 1
- fi
-
- return 0
-}
-
-# Upload the private key to the device.
-# Note: Do not clear first โ see _shelly_upload_cert for rationale.
-_shelly_upload_key() {
- _shelly_key_data="$(_json_encode <"$_ckey")"
-
- _debug "Uploading key"
- if ! _shelly_rpc "Shelly.PutHTTPServerKey" '{"data":"'"$_shelly_key_data"'"}'; then
- _err "Failed to upload key to device"
- return 1
- fi
-
- return 0
-}
diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index 336980a5..d05e503a 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -344,7 +344,6 @@ synology_dsm_deploy() {
else
_err "Failed to fetch certificate info: $error_code, please try again or contact Synology to learn more."
fi
- _logout
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
return 1
fi
@@ -355,7 +354,6 @@ synology_dsm_deploy() {
if [ -z "$id" ] && [ -z "$SYNO_CREATE" ]; then
_err "Unable to find certificate: $SYNO_CERTIFICATE and \$SYNO_CREATE is not set."
- _logout
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
return 1
fi
@@ -391,13 +389,13 @@ synology_dsm_deploy() {
else
_info "Restart HTTP services not necessary."
fi
- _logout
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
+ _logout
return 0
else
+ _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
_err "Unable to update certificate, got error response: $response."
_logout
- _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
return 1
fi
}
@@ -405,8 +403,6 @@ synology_dsm_deploy() {
#################### Private functions below ##################################
_logout() {
# Logout CERT user only to not occupy a permanent session, e.g. in DSM's "Connected Users" widget (based on previous variables)
- # Must be called before _temp_admin_cleanup: once the temp admin is deleted, its session can no longer be logged out.
- # Note: this overwrites $response, so print any error message that needs it before calling.
response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=logout&_sid=$sid")
_debug3 response "$response"
}
diff --git a/deploy/unifios.sh b/deploy/unifios.sh
deleted file mode 100644
index 82b65c69..00000000
--- a/deploy/unifios.sh
+++ /dev/null
@@ -1,307 +0,0 @@
-#!/usr/bin/env sh
-# Deploy hook for UniFi OS Server (self-hosted).
-#
-# Supports:
-# - UniFi OS Server on macOS
-# - UniFi OS Server on Linux
-# - UniFi OS Server on Windows should also work (runs under WSL2), but
-# has not been tested.
-#
-# Tested on: Ubuntu 26.04 (remote) and macOS 26.6 (local).
-#
-# This is a different product from the Cloud Key / UDM hardware and
-# self-hosted Unifi Controller covered by the `unifi` deploy hook above
-# (that hook already covers Cloud Key running UnifiOS v2.0.0+/Gen2/2+) --
-# this hook targets the separately-installed, self-hosted "UniFi OS Server"
-# application instead, which stores certificates in its own Postgres
-# database via a REST API rather than a Java keystore, so the `unifi`
-# hook's approach does not apply here.
-#
-# UniFi OS Server exposes a REST API on its management port (default
-# 11443) that its own web UI uses for certificate management:
-# POST /api/auth/login - session login (cookie + JWT)
-# GET /api/userCertificates - list uploaded certificates
-# POST /api/userCertificates - upload a new certificate
-# DELETE /api/userCertificates/{id} - remove a certificate
-# PUT /api/userCertificates/{id}/status - activate/deactivate a certificate
-#
-# This was reverse-engineered from the browser's Network tab while using the
-# real GUI upload/activate/delete flow -- it is undocumented but is the same
-# code path the UI uses, so it's far more robust than editing settings.yaml,
-# http/local-certs.conf, or the underlying Postgres user_certificates table
-# directly (all of which are also touched by this API, but only as a result
-# of the app's own internal logic, which handles cert parsing, active-cert
-# bookkeeping, and nginx config regeneration correctly on its own).
-#
-# Auth: POST /api/auth/login returns a `TOKEN` cookie containing a JWT whose
-# payload has a `csrfToken` claim. That value must be echoed back as the
-# `x-csrf-token` header on every subsequent state-changing request (a classic
-# double-submit CSRF pattern). No other cookies were found to be necessary.
-#
-# Uses core acme.sh helpers throughout (_post/_get, _json_encode,
-# _durl_replace_base64, _dbase64, _egrep_o) rather than raw curl -k or
-# python3, so the wget fallback, --debug tracing, and CA_BUNDLE are all
-# honored the same as every other hook. The management API's cert is
-# self-signed (it's a management-only port, not meant for public exposure),
-# so this hook sets HTTPS_INSECURE=1 itself, scoped to its own subshell (see
-# acme.sh's per-hook sourcing in _deploy) -- it does not weaken TLS
-# verification for the rest of the acme.sh run, e.g. the connection to the
-# ACME CA.
-#
-# Design: This hook does not save a certificate ID between renewals. Each
-# upload gets a name unique to that run: the domain name plus a timestamp.
-# This name never collides with an entry from a previous deploy. This is
-# true even if that entry is still active. The hook uploads and activates
-# the new certificate before it removes any old entries. If a failure
-# occurs during this process, the server still has a valid, active
-# certificate. The hook removes old entries only after activation is
-# complete. It removes only entries whose name starts with the domain name,
-# because this is the hook's own naming convention. As a result, this step
-# can only affect entries that this hook created for this domain. It can
-# never affect a certificate that a user uploaded manually, and it can
-# never affect a self-signed certificate.
-#
-# Settings:
-# DEPLOY_UNIFIOS_HOST - base URL of the management API
-# (default: "https://localhost:11443")
-# DEPLOY_UNIFIOS_USERNAME - UniFi OS Server admin username (required)
-# DEPLOY_UNIFIOS_PASSWORD - UniFi OS Server admin password (required)
-#
-# Example:
-# export DEPLOY_UNIFIOS_USERNAME="acmeuser"
-# export DEPLOY_UNIFIOS_PASSWORD="xxxxx"
-# acme.sh --deploy -d example.com --deploy-hook unifios
-#
-# Please report bugs to https://github.com/acmesh-official/acme.sh/issues/7182
-
-_uos_response_code() {
- # tr strips the trailing newline along with form feeds; re-terminate
- # before the second _egrep_o, whose sed fallback (used wherever egrep -o
- # is unavailable) drops an unterminated final line on some platforms.
- _uos_code="$(_egrep_o <"$HTTP_HEADER" "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n")"
- printf '%s\n' "$_uos_code" | _egrep_o "^[0-9][0-9]*"
-}
-
-_uos_response_cookie() {
- # $1 = cookie name
- grep <"$HTTP_HEADER" -i "^Set-Cookie: *$1=" | _tail_n 1 | _egrep_o "$1=[^;]*" | _head_n 1
-}
-
-unifios_deploy() {
- _cdomain="$1"
- _ckey="$2"
- _ccert="$3"
- _cca="$4"
- _cfullchain="$5"
-
- _debug _cdomain "$_cdomain"
- _debug _ckey "$_ckey"
- _debug _ccert "$_ccert"
- _debug _cca "$_cca"
- _debug _cfullchain "$_cfullchain"
-
- # Scoped to this hook's own subshell -- does not affect the rest of the
- # acme.sh run (e.g. the connection to the ACME CA).
- export HTTPS_INSECURE=1
-
- _getdeployconf DEPLOY_UNIFIOS_HOST
- DEPLOY_UNIFIOS_HOST="${DEPLOY_UNIFIOS_HOST:-https://localhost:11443}"
- _savedeployconf DEPLOY_UNIFIOS_HOST "$DEPLOY_UNIFIOS_HOST"
- _debug DEPLOY_UNIFIOS_HOST "$DEPLOY_UNIFIOS_HOST"
-
- _getdeployconf DEPLOY_UNIFIOS_USERNAME
- _getdeployconf DEPLOY_UNIFIOS_PASSWORD
-
- if [ -z "$DEPLOY_UNIFIOS_USERNAME" ] || [ -z "$DEPLOY_UNIFIOS_PASSWORD" ]; then
- _err "DEPLOY_UNIFIOS_USERNAME and DEPLOY_UNIFIOS_PASSWORD must be set."
- return 1
- fi
- _debug DEPLOY_UNIFIOS_USERNAME "$DEPLOY_UNIFIOS_USERNAME"
- _secure_debug DEPLOY_UNIFIOS_PASSWORD "$DEPLOY_UNIFIOS_PASSWORD"
-
- _info "Logging in to UniFi OS Server API at $DEPLOY_UNIFIOS_HOST..."
-
- # _json_encode always appends a trailing "\n" escape, even to input with
- # no trailing newline (it normalizes via `echo`, unconditionally adding
- # one). That's harmless for the key/cert file content below, which
- # legitimately ends in a real newline anyway, but wrong for these plain
- # strings -- strip the spurious escape it leaves behind.
- _uos_user_json="$(printf '%s' "$DEPLOY_UNIFIOS_USERNAME" | _json_encode)"
- _uos_user_json="${_uos_user_json%\\n}"
- _uos_pass_json="$(printf '%s' "$DEPLOY_UNIFIOS_PASSWORD" | _json_encode)"
- _uos_pass_json="${_uos_pass_json%\\n}"
- _login_body="{\"username\":\"$_uos_user_json\",\"password\":\"$_uos_pass_json\",\"token\":\"\",\"rememberMe\":false}"
-
- _login_json="$(_post "$_login_body" "$DEPLOY_UNIFIOS_HOST/api/auth/login" "" "POST" "application/json")"
- _login_code="$(_uos_response_code)"
-
- if [ "$_login_code" != "200" ]; then
- _err "Login failed (HTTP $_login_code)."
- _err "Response: $_login_json"
- return 1
- fi
-
- # Credentials are proven correct now -- save them, rather than only at the
- # very end, so a later step failing doesn't discard a working login.
- # base64-encoded: _save_conf wraps values in single quotes with no
- # escaping, so a literal "'" in the password would otherwise corrupt the
- # domain conf (see deploy/synology_dsm.sh for the same pattern).
- _savedeployconf DEPLOY_UNIFIOS_USERNAME "$DEPLOY_UNIFIOS_USERNAME" "base64"
- _savedeployconf DEPLOY_UNIFIOS_PASSWORD "$DEPLOY_UNIFIOS_PASSWORD" "base64"
-
- _uos_token="$(_uos_response_cookie TOKEN)"
- if [ -z "$_uos_token" ]; then
- _err "Login succeeded but no TOKEN cookie was returned."
- return 1
- fi
-
- _H1="Cookie: $_uos_token"
- export _H1
-
- _uos_jwt_payload="$(echo "$_uos_token" | cut -d '=' -f 2- | cut -d '.' -f 2)"
- _uos_csrf="$(_durl_replace_base64 "$_uos_jwt_payload" | _dbase64 | _egrep_o '"csrfToken":"[^"]*"' | cut -d '"' -f 4)"
- if [ -z "$_uos_csrf" ]; then
- _err "Could not extract csrfToken from session token."
- return 1
- fi
-
- _H2="x-csrf-token: $_uos_csrf"
- export _H2
-
- _info "Uploading new certificate..."
- # "name" is a purely cosmetic label -- the server never validates it
- # against the certificate's actual CN/SAN, and accepts arbitrary text
- # including spaces (confirmed: a cert for example.com served correctly
- # after being uploaded under the unrelated name "totally unrelated label").
- # The only constraint that matters here is uniqueness: the server rejects
- # a second entry with a name it already has, so a bare domain name would
- # collide with the previous deploy's entry on every renewal after the
- # first. A full human-readable timestamp would make that obvious in the
- # UI, but the certificate list's name column is fixed-width and doesn't
- # wrap (confirmed against the real UI: a long name overlaps the Expires
- # column and makes both unreadable), so keep the suffix short instead --
- # Unix epoch seconds are still unique enough for this purpose.
- _uos_name="$_cdomain $(_time)"
- _uos_key_json="$(_json_encode <"$_ckey")"
- _uos_cert_json="$(_json_encode <"$_cfullchain")"
- _create_body="{\"name\":\"$_uos_name\",\"key\":\"$_uos_key_json\",\"cert\":\"$_uos_cert_json\"}"
-
- _create_json="$(_post "$_create_body" "$DEPLOY_UNIFIOS_HOST/api/userCertificates" "" "POST" "application/json")"
- _create_code="$(_uos_response_code)"
-
- if [ "$_create_code" = "201" ]; then
- _new_id="$(echo "$_create_json" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
- if [ -z "$_new_id" ]; then
- _err "Could not determine new certificate ID from upload response."
- return 1
- fi
- elif [ "$_create_code" = "400" ] && echo "$_create_json" | grep -q "USER_CERTIFICATE_DUPLICATE"; then
- # HTTP 400 alone just means "bad request" -- it's the USER_CERTIFICATE_DUPLICATE
- # code in the response body, checked above, that actually confirms this.
- # The name above is unique to this run, so a duplicate here can only be
- # the server's other uniqueness constraint: this exact certificate (by
- # fingerprint) already exists as some other entry -- most likely a retry
- # after a prior run already uploaded it (a real renewal always produces a
- # new fingerprint, so this shouldn't happen in normal cron use). The
- # response body doesn't include the existing entry's id, so look it up
- # by fingerprint instead.
- # The API's own fingerprint field is SHA-1 (20 bytes), not SHA-256 --
- # confirmed against a real response, e.g.
- # "fingerprint":"FC:02:50:9C:3B:3F:B7:79:9D:CA:4D:7C:AC:92:E7:D5:EA:F1:3A:29"
- # (20 colon-separated groups). _fingerprint (core helper) strips the
- # colons that field has, so re-insert them rather than stripping the
- # JSON's own colons, which would also remove the ones separating every
- # key from its value.
- _uos_fingerprint="$(_fingerprint "$_cfullchain" sha1)"
- if [ -z "$_uos_fingerprint" ]; then
- _err "Could not compute the certificate's fingerprint."
- return 1
- fi
- _uos_fingerprint="$(echo "$_uos_fingerprint" | sed 's/\(..\)/\1:/g; s/:$//')"
-
- _list_json="$(_get "$DEPLOY_UNIFIOS_HOST/api/userCertificates")"
- _list_code="$(_uos_response_code)"
- if [ "$_list_code" != "200" ]; then
- _err "Failed to list existing certificates (HTTP $_list_code)."
- _err "Response: $_list_json"
- return 1
- fi
- # _normalizeJson collapses the response to one predictable line (no stray
- # whitespace around colons, no embedded CR/LF the server might emit) but
- # also strips the trailing newline entirely -- re-terminate before the
- # split below, since some sed implementations drop an unterminated final
- # line rather than processing it.
- _list_json="$(echo "$_list_json" | _normalizeJson)"
- # A literal embedded newline (not the two-character "\n", which GNU sed
- # treats as a newline in the replacement but POSIX doesn't define and BSD
- # sed emits literally) splits it one JSON object per line so grep can
- # match a single certificate entry at a time.
- _list_json="$(
- printf '%s\n' "$_list_json" | sed 's/},{/},\
-{/g'
- )"
- _new_id="$(echo "$_list_json" | grep -F "\"fingerprint\":\"$_uos_fingerprint\"" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
- if [ -z "$_new_id" ]; then
- _err "Certificate upload rejected as a duplicate (server reported USER_CERTIFICATE_DUPLICATE), but no existing entry matching this fingerprint was found."
- _err "Response: $_create_json"
- return 1
- fi
- # Reusing the existing entry rather than deleting it and re-uploading
- # under today's name+timestamp: the served content is identical either
- # way, so replacing it would only cost an extra delete+create round trip
- # for no functional benefit. The tradeoff is cosmetic -- this entry keeps
- # whatever name it was given whenever it was originally uploaded, so it
- # won't reflect today's date in the UI.
- _info "Certificate already present as entry $_new_id; reusing it."
- else
- _err "Certificate upload failed (HTTP $_create_code)."
- _err "Response: $_create_json"
- return 1
- fi
-
- _info "Activating certificate $_new_id..."
- _activate_json="$(_post '{"active":true}' "$DEPLOY_UNIFIOS_HOST/api/userCertificates/$_new_id/status" "" "PUT" "application/json")"
- _activate_code="$(_uos_response_code)"
-
- if [ "$_activate_code" != "200" ]; then
- _err "Failed to activate new certificate (HTTP $_activate_code)."
- _err "Response: $_activate_json"
- return 1
- fi
-
- # UniFi OS Server activation is exclusive server-wide. Tests against the
- # real API confirm this: activation of one entry deactivates whichever
- # other entry was active before, no matter its name or domain. As a
- # result, the server serves the certificate that this hook just activated.
- # This certificate is already live. If the removal of old entries below
- # fails, the hook logs the failure. The deploy does not fail because of
- # this.
- _info "Checking for old certificate entries to remove..."
- _list_json="$(_get "$DEPLOY_UNIFIOS_HOST/api/userCertificates")"
- _list_code="$(_uos_response_code)"
- if [ "$_list_code" != "200" ]; then
- _err "Failed to list certificates for cleanup (HTTP $_list_code) -- leaving old entries in place."
- else
- _list_json="$(echo "$_list_json" | _normalizeJson)"
- _list_json="$(
- printf '%s\n' "$_list_json" | sed 's/},{/},\
-{/g'
- )"
- # The pattern below matches the domain name followed by a space. If the
- # space is missing, the pattern can also match a different domain that
- # starts with the same text as this domain.
- _old_ids="$(echo "$_list_json" | grep -F "\"name\":\"$_cdomain " | _egrep_o '"id":"[^"]*"' | cut -d '"' -f 4 | grep -v "^$_new_id$")"
- for _old_id in $_old_ids; do
- _info "Removing old certificate entry $_old_id..."
- _del_json="$(_post "" "$DEPLOY_UNIFIOS_HOST/api/userCertificates/$_old_id" "" "DELETE")"
- _del_code="$(_uos_response_code)"
- if [ "$_del_code" != "204" ] && [ "$_del_code" != "200" ]; then
- _err "Failed to delete old certificate $_old_id (HTTP $_del_code) -- leaving it in place."
- _err "Response: $_del_json"
- fi
- done
- fi
-
- _info "UniFi OS Server certificate deployed and activated successfully."
- return 0
-}
diff --git a/dnsapi/dns_bhosted.sh b/dnsapi/dns_bhosted.sh
index 46ddd5dd..1493c60a 100644
--- a/dnsapi/dns_bhosted.sh
+++ b/dnsapi/dns_bhosted.sh
@@ -323,21 +323,21 @@ _bhosted_extract_id() {
fi
# JSON: "id":12345
- _id="$(printf "%s" "$_resp" | _egrep_o '"id"[ ]*:[ ]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
+ _id="$(printf "%s" "$_resp" | _egrep_o '"id"[[:space:]]*:[[:space:]]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
if [ -n "$_id" ]; then
printf "%s" "$_id"
return 0
fi
# key=value: id=12345
- _id="$(printf "%s" "$_resp" | _egrep_o '(^|[^0-9a-zA-Z])id[ ]*=[ ]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
+ _id="$(printf "%s" "$_resp" | _egrep_o '(^|[[:space:][:punct:]])id[[:space:]]*=[[:space:]]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
if [ -n "$_id" ]; then
printf "%s" "$_id"
return 0
fi
# "record id 12345" / "recordid 12345"
- _id="$(printf "%s" "$_resp" | _egrep_o '(record[ ]*id|recordid)[^0-9]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
+ _id="$(printf "%s" "$_resp" | _egrep_o '(record[[:space:]]*id|recordid)[^0-9]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
if [ -n "$_id" ]; then
printf "%s" "$_id"
return 0
diff --git a/dnsapi/dns_creoline.sh b/dnsapi/dns_creoline.sh
index f4d76f8e..9d04af4f 100644
--- a/dnsapi/dns_creoline.sh
+++ b/dnsapi/dns_creoline.sh
@@ -75,7 +75,7 @@ dns_creoline_rm() {
return 1
fi
- record_id=$(echo "$response" | _egrep_o "\"id\"[ ]*:[ ]*[0-9]+" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
+ record_id=$(echo "$response" | _egrep_o "\"id\"[[:space:]]*:[[:space:]]*[0-9]+" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug "record_id" "$record_id"
if [ -z "$record_id" ]; then
@@ -108,10 +108,10 @@ _get_root() {
return 1
fi
- _sub_domain=$(echo "$response" | _egrep_o "\"subDomain\"[ ]*:[ ]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
+ _sub_domain=$(echo "$response" | _egrep_o "\"subDomain\"[[:space:]]*:[[:space:]]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug _sub_domain "$_sub_domain"
- _domain=$(echo "$response" | _egrep_o "\"domain\"[ ]*:[ ]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
+ _domain=$(echo "$response" | _egrep_o "\"domain\"[[:space:]]*:[[:space:]]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \" | _head_n 1 | tr -d " ")
_debug _domain "$_domain"
if [ -z "$_domain" ] || [ -z "$_sub_domain" ]; then
@@ -171,7 +171,7 @@ _creoline_rest() {
_err "URI:$uri"
return 1
elif _contains "$response" "message"; then
- message=$(echo "$response" | _egrep_o "\"message\"[ ]*:[ ]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \")
+ message=$(echo "$response" | _egrep_o "\"message\"[[:space:]]*:[[:space:]]*\"[^\"]+\"" | cut -d : -f 2 | tr -d \")
_err "Error: $message"
_err "URI:$uri"
return 1
diff --git a/dnsapi/dns_cyon.sh b/dnsapi/dns_cyon.sh
index 6677b32f..d4b6b6e8 100644
--- a/dnsapi/dns_cyon.sh
+++ b/dnsapi/dns_cyon.sh
@@ -285,15 +285,15 @@ _cyon_delete_txt() {
list_txt_url="https://my.cyon.ch/domain/dnseditor/list-async"
- list_txt_response="$(_get "${list_txt_url}")"
+ list_txt_response="$(_get "${list_txt_url}" | sed -e 's/data-hash/\\ndata-hash/g')"
_debug list_txt_response "${list_txt_response}"
if ! _cyon_check_if_2fa_missed "${list_txt_response}"; then return 1; fi
# Find and delete all acme challenge entries for the $fulldomain.
- _dns_entries="$(printf "%s\n" "${list_txt_response}" | _egrep_o 'data-hash=\\"[^"]*\\" data-identifier=\\"[^"]*\\"' | sed 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\"/\1 \2/')"
+ _dns_entries="$(printf "%b\n" "${list_txt_response}" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p')"
- printf "%s\n" "${_dns_entries}" | while read -r _hash _identifier; do
+ printf "%s" "${_dns_entries}" | while read -r _hash _identifier; do
dns_type="$(printf "%s" "$_identifier" | cut -d'|' -f1)"
dns_domain="$(printf "%s" "$_identifier" | cut -d'|' -f2)"
diff --git a/dnsapi/dns_czechia.sh b/dnsapi/dns_czechia.sh
index 6ad60442..e2ffcf50 100644
--- a/dnsapi/dns_czechia.sh
+++ b/dnsapi/dns_czechia.sh
@@ -30,9 +30,8 @@ dns_czechia_add() {
return 1
fi
- _czechia_tab="$(printf '\t')"
- _cz=$(printf "%s" "$_current_zone" | _lower_case | sed "s/[ $_czechia_tab]//g; s/\.\$//")
- _tk=$(printf "%s" "$CZ_AuthorizationToken" | sed "s/^[ $_czechia_tab]*//; s/[ $_czechia_tab]*\$//")
+ _cz=$(printf "%s" "$_current_zone" | _lower_case | sed 's/[[:space:]]//g; s/\.$//')
+ _tk=$(printf "%s" "$CZ_AuthorizationToken" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
if [ -z "$_cz" ] || [ -z "$_tk" ]; then
_err "Missing zone or CZ_AuthorizationToken."
@@ -109,9 +108,8 @@ dns_czechia_rm() {
return 1
fi
- _czechia_tab="$(printf '\t')"
- _cz=$(printf "%s" "$_current_zone" | _lower_case | sed "s/[ $_czechia_tab]//g; s/\.\$//")
- _tk=$(printf "%s" "$CZ_AuthorizationToken" | sed "s/^[ $_czechia_tab]*//; s/[ $_czechia_tab]*\$//")
+ _cz=$(printf "%s" "$_current_zone" | _lower_case | sed 's/[[:space:]]//g; s/\.$//')
+ _tk=$(printf "%s" "$CZ_AuthorizationToken" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
if [ -z "$_cz" ] || [ -z "$_tk" ]; then
_err "Missing zone or CZ_AuthorizationToken."
@@ -182,13 +180,12 @@ _czechia_load_conf() {
}
_czechia_pick_zone() {
- _czechia_pz_tab="$(printf '\t')"
_fd=$(printf "%s" "$1" | _lower_case | sed 's/\.$//')
_best_zone=""
_zones_space=$(printf "%s" "$CZ_Zones" | sed 's/,/ /g')
for _z in $_zones_space; do
- _clean_z=$(printf "%s" "$_z" | _lower_case | sed "s/[ $_czechia_pz_tab]//g; s/\.\$//")
+ _clean_z=$(printf "%s" "$_z" | _lower_case | sed 's/[[:space:]]//g; s/\.$//')
[ -z "$_clean_z" ] && continue
case "$_fd" in
diff --git a/dnsapi/dns_freemyip.sh b/dnsapi/dns_freemyip.sh
index 18d8e7f9..d598a657 100644
--- a/dnsapi/dns_freemyip.sh
+++ b/dnsapi/dns_freemyip.sh
@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_freemyip
Options:
FREEMYIP_Token API Token
Issues: github.com/acmesh-official/acme.sh/issues/6247
-Author: Recolic Keghart , @Giova96, ExtremeFiretop
+Author: Recolic Keghart , @Giova96
'
FREEMYIP_DNS_API="https://freemyip.com/update?"
@@ -68,30 +68,22 @@ dns_freemyip_rm() {
return $?
}
-################ Private functions below ################
+################ Private functions below ################
_get_root() {
_fmi_d="$1"
- echo "$_fmi_d" | sed 's/.*\.\([^.]*\.[^.]*\.[^.]*\)$/\1/'
+ echo "$_fmi_d" | rev | cut -d '.' -f 1-3 | rev
}
# There is random failure while calling freemyip API too fast. This function automatically retry until success.
_freemyip_get_until_ok() {
_fmi_url="$1"
- _fmi_i=1
- while [ "$_fmi_i" -le 8 ]; do
- _debug "HTTP GET freemyip.com API '$_fmi_url', retry $_fmi_i/8..."
- _fmi_response="$(_get "$_fmi_url")"
- printf '%s\n' "$_fmi_response" >&2
-
- if _contains "$_fmi_response" "OK"; then
- return 0
- fi
-
+ for i in $(seq 1 8); do
+ _debug "HTTP GET freemyip.com API '$_fmi_url', retry $i/8..."
+ _get "$_fmi_url" | tee /dev/fd/2 | grep OK && return 0
_sleep 1 # DO NOT send the request too fast
- _fmi_i=$((_fmi_i + 1))
done
- _err "Failed to request freemyip API. Server does not say 'OK'"
+ _err "Failed to request freemyip API: $_fmi_url . Server does not say 'OK'"
return 1
}
@@ -101,16 +93,13 @@ _is_root_domain_published() {
_webroot="$(_get_root "$_fmi_d")"
_info "Verifying '""$_fmi_d""' freemyip webroot (""$_webroot"") is not published yet"
- _fmi_i=1
- while [ "$_fmi_i" -le 3 ]; do
- _debug "'$_webroot' ns lookup, retry $_fmi_i/3..."
-
+ for i in $(seq 1 3); do
+ _debug "'$_webroot' ns lookup, retry $i/3..."
if [ "$(_ns_lookup "$_fmi_d" TXT)" ]; then
_debug "'$_webroot' already has a TXT record published!"
return 0
fi
_sleep 10 # Give it some time to propagate the TXT record
- _fmi_i=$((_fmi_i + 1))
done
return 1
}
diff --git a/dnsapi/dns_hestiacp.sh b/dnsapi/dns_hestiacp.sh
deleted file mode 100644
index 13ee6caf..00000000
--- a/dnsapi/dns_hestiacp.sh
+++ /dev/null
@@ -1,198 +0,0 @@
-#!/usr/bin/env sh
-# shellcheck disable=SC2034
-dns_hestiacp_info='HestiaCP Server API
-Site: hestiacp.com
-Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_hestiacp
-Options:
- HESTIA_HOST Panel URL. E.g. "https://panel.example.com:8083"
- HESTIA_ACCESS API access key
- HESTIA_SECRET API secret key
- HESTIA_USER Username owning the DNS zones. Default "admin". Optional.
-Issues: github.com/acmesh-official/acme.sh/issues/6251
-Author: Radu Malica
-'
-
-######## Public functions #####################
-
-# Usage: dns_hestiacp_add fulldomain txtvalue
-dns_hestiacp_add() {
- fulldomain=$1
- txtvalue=$2
-
- if ! _hestia_init; then
- return 1
- fi
-
- _debug "Detecting the root zone for $fulldomain"
- if ! _hestia_get_root "$fulldomain"; then
- _err "Cannot find a DNS zone for $fulldomain under user $HESTIA_USER"
- return 1
- fi
- _debug _hestia_domain "$_hestia_domain"
- _debug _hestia_sub "$_hestia_sub"
-
- # _hestia_get_root left the zone record listing in _hestia_response
- if _hestia_find_records "$_hestia_sub" "TXT" | grep -F -- "$txtvalue" >/dev/null; then
- _info "The TXT record already exists, skipping"
- return 0
- fi
-
- _info "Adding TXT record for $fulldomain"
- if ! _hestia_rest "v-add-dns-record" "$HESTIA_USER" "$_hestia_domain" "$_hestia_sub" "TXT" "$txtvalue" "" "" "yes" "600"; then
- _err "Error adding TXT record: $_hestia_response"
- return 1
- fi
- _info "TXT record added successfully"
- return 0
-}
-
-# Usage: dns_hestiacp_rm fulldomain txtvalue
-dns_hestiacp_rm() {
- fulldomain=$1
- txtvalue=$2
-
- if ! _hestia_init; then
- return 1
- fi
-
- _debug "Detecting the root zone for $fulldomain"
- if ! _hestia_get_root "$fulldomain"; then
- _err "Cannot find a DNS zone for $fulldomain under user $HESTIA_USER"
- return 1
- fi
- _debug _hestia_domain "$_hestia_domain"
- _debug _hestia_sub "$_hestia_sub"
-
- _hestia_removed=0
- _hestia_failed=0
- while IFS='|' read -r _hestia_id _hestia_value || [ -n "$_hestia_id" ]; do
- if [ -z "$_hestia_id" ]; then
- continue
- fi
- if ! _contains "$_hestia_value" "$txtvalue"; then
- continue
- fi
- _info "Deleting TXT record $_hestia_id"
- if ! _hestia_rest "v-delete-dns-record" "$HESTIA_USER" "$_hestia_domain" "$_hestia_id" "yes"; then
- _err "Error deleting TXT record $_hestia_id: $_hestia_response"
- _hestia_failed=$(_math "$_hestia_failed" + 1)
- continue
- fi
- _hestia_removed=$(_math "$_hestia_removed" + 1)
- done <
+'
+
+LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
+
+######## Public functions #####################
+
+#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_linode_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue"
+
+ if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then
+ _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _resource_id "$_resource_id"
+
+ if [ -z "$_resource_id" ]; then
+ _err "Error adding the domain resource."
+ return 1
+ fi
+
+ _info "Domain resource successfully added."
+ return 0
+ fi
+
+ return 1
+}
+
+#Usage: dns_linode_rm _acme-challenge.www.domain.com
+dns_linode_rm() {
+ fulldomain="${1}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_rm() '${fulldomain}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _parameters="&DomainID=$_domain_id"
+
+ if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+
+ resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
+ if [ "$resource" ]; then
+ _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_resource_id" ]; then
+ _debug _resource_id "$_resource_id"
+
+ _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id"
+
+ if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then
+ _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _resource_id "$_resource_id"
+
+ if [ -z "$_resource_id" ]; then
+ _err "Error deleting the domain resource."
+ return 1
+ fi
+
+ _info "Domain resource successfully deleted."
+ return 0
+ fi
+ fi
+
+ return 1
+ fi
+
+ return 0
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_Linode_API() {
+ if [ -z "$LINODE_API_KEY" ]; then
+ LINODE_API_KEY=""
+
+ _err "You didn't specify the Linode API key yet."
+ _err "Please create your key and try again."
+
+ return 1
+ fi
+
+ _saveaccountconf LINODE_API_KEY "$LINODE_API_KEY"
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if _rest GET "domain.list"; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")"
+ if [ "$hostedzone" ]; then
+ _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _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
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
+
+#method method action data
+_rest() {
+ mtd="$1"
+ ep="$2"
+ data="$3"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+
+ if [ "$mtd" != "GET" ]; then
+ # both POST and DELETE.
+ _debug data "$data"
+ response="$(_post "$data" "$LINODE_API_URL$ep" "" "$mtd")"
+ else
+ response="$(_get "$LINODE_API_URL$ep$data")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh
index 7035640b..cca59735 100755
--- a/dnsapi/dns_namecheap.sh
+++ b/dnsapi/dns_namecheap.sh
@@ -104,9 +104,6 @@ _get_root_by_getList() {
return 1
fi
- _namecheap_domain_list=$(echo "$response" | _egrep_o ']*')
- _debug2 domain_list "$_namecheap_domain_list"
-
i=2
p=1
@@ -123,7 +120,7 @@ _get_root_by_getList() {
return 1
fi
- if ! _namecheap_is_our_dns "$h"; then
+ if ! _contains "$response" "$h"; then
_debug "$h not found"
else
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
@@ -136,29 +133,6 @@ _get_root_by_getList() {
return 1
}
-#Usage: _namecheap_is_our_dns
-#Succeeds only when domains.getList listed exactly AND that entry is
-#served by Namecheap's own DNS. A domain parked on Namecheap's webhosting DNS
-#is listed with IsOurDNS="false", and every dns.getHosts/setHosts call against
-#it is refused with error 2030288 "not using proper DNS servers". Accepting
-#such a domain as the root zone hides a subdomain that IS delegated to
-#Namecheap DNS and that the getHosts probe below would have found.
-#https://github.com/acmesh-official/acme.sh/issues/7178
-_namecheap_is_our_dns() {
- _namecheap_entry=$(echo "$_namecheap_domain_list" | grep -F " Name=\"$1\"" | _head_n 1)
- if [ -z "$_namecheap_entry" ]; then
- return 1
- fi
-
- _namecheap_ourdns=$(echo "$_namecheap_entry" | _egrep_o ' IsOurDNS="[^"]*' | cut -d '"' -f 2)
- _debug2 "$1 IsOurDNS" "$_namecheap_ourdns"
-
- if [ "$_namecheap_ourdns" = "true" ]; then
- return 0
- fi
- return 1
-}
-
_get_root_by_getHosts() {
i=100
p=99
diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh
index 3b291854..8609adf6 100644
--- a/dnsapi/dns_netcup.sh
+++ b/dnsapi/dns_netcup.sh
@@ -33,11 +33,9 @@ dns_netcup_add() {
exit=$(echo "$fulldomain" | tr -dc '.' | wc -c)
exit=$(_math "$exit" + 1)
i=$exit
- _nc_last=$(_nc_lastlevel "$i")
- _nc_found=""
while
- [ "$exit" -ge "$_nc_last" ]
+ [ "$exit" -gt 0 ]
do
tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit")
if [ "$(_math "$i" - "$exit")" -eq 0 ]; then
@@ -53,18 +51,12 @@ dns_netcup_add() {
_err "$msg"
return 1
else
- _nc_found=1
break
fi
fi
fi
exit=$(_math "$exit" - 1)
done
- if [ -z "$_nc_found" ]; then
- _err "$msg"
- _nc_nozone "$fulldomain"
- return 1
- fi
logout
}
@@ -78,11 +70,9 @@ dns_netcup_rm() {
exit=$(_math "$exit" + 1)
i=$exit
rec=""
- _nc_last=$(_nc_lastlevel "$i")
- _nc_found=""
while
- [ "$exit" -ge "$_nc_last" ]
+ [ "$exit" -gt 0 ]
do
tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit")
if [ "$(_math "$i" - "$exit")" -eq 0 ]; then
@@ -99,18 +89,12 @@ dns_netcup_rm() {
_err "$msg"
return 1
else
- _nc_found=1
break
fi
fi
fi
exit=$(_math "$exit" - 1)
done
- if [ -z "$_nc_found" ]; then
- _err "$msg"
- _nc_nozone "$fulldomain"
- return 1
- fi
ida=0000
idv=0001
@@ -141,27 +125,6 @@ dns_netcup_rm() {
logout
}
-# The zone is looked up by walking the challenge name from the right, one
-# label at a time. The leftmost label is the challenge prefix, so the full
-# name itself can never be a zone: asking netcup for it only returns 4013
-# "Validation Error", which would then mask the real 5028 "zone could not be
-# found". Stop one label short, unless the name is too short to have a
-# challenge prefix at all (manual invocation).
-# levels
-_nc_lastlevel() {
- if [ "$1" -ge 3 ]; then
- echo 2
- else
- echo 1
- fi
-}
-
-# fulldomain
-_nc_nozone() {
- _err "No DNS zone for $1 was found at netcup."
- _err "Check that the domain belongs to the account of the configured NC_CID and that its DNS is hosted at netcup."
-}
-
_login() {
tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4)
diff --git a/dnsapi/dns_nexdns.sh b/dnsapi/dns_nexdns.sh
deleted file mode 100755
index e4447c0e..00000000
--- a/dnsapi/dns_nexdns.sh
+++ /dev/null
@@ -1,244 +0,0 @@
-#!/usr/bin/env sh
-# shellcheck disable=SC2034
-dns_nexdns_info='NexDNS
-Site: nexdns.tech
-Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_nexdns
-Options:
- NEXDNS_Token API token. Can be created at https://nexdns.tech/settings/api-keys
- NEXDNS_Api API base url. Default "https://api.nexdns.tech/v1". Optional.
-Issues: github.com/acmesh-official/acme.sh/issues/7179
-Author: NexDNS
-'
-
-NEXDNS_Api_Default="https://api.nexdns.tech/v1"
-
-######## Public functions #####################
-
-#Usage: dns_nexdns_add _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_nexdns_add() {
- fulldomain=$1
- txtvalue=$2
-
- if ! _nexdns_init; then
- return 1
- fi
-
- _saveaccountconf_mutable NEXDNS_Token "$NEXDNS_Token"
- if [ "$NEXDNS_Api" != "$NEXDNS_Api_Default" ]; then
- _saveaccountconf_mutable NEXDNS_Api "$NEXDNS_Api"
- else
- _clearaccountconf_mutable NEXDNS_Api
- fi
-
- _debug "First detect the root zone"
- if ! _get_root "$fulldomain"; then
- _err "Cannot find the zone of $fulldomain in this NexDNS account."
- return 1
- fi
- _debug _sub_domain "$_sub_domain"
- _debug _domain "$_domain"
- _debug _domain_id "$_domain_id"
-
- _info "Adding the TXT record for $fulldomain"
- if ! _nexdns_rest POST "zones/$_domain_id/records" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
- return 1
- fi
-
- _info "The TXT record has been added."
- return 0
-}
-
-#Usage: dns_nexdns_rm _acme-challenge.www.example.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_nexdns_rm() {
- fulldomain=$1
- txtvalue=$2
-
- if ! _nexdns_init; then
- return 1
- fi
-
- _debug "First detect the root zone"
- if ! _get_root "$fulldomain"; then
- _err "Cannot find the zone of $fulldomain in this NexDNS account."
- return 1
- fi
- _debug _sub_domain "$_sub_domain"
- _debug _domain "$_domain"
- _debug _domain_id "$_domain_id"
-
- _info "Removing the TXT record for $fulldomain"
- if ! _nexdns_rest GET "zones/$_domain_id/records?type=TXT&name=$_sub_domain"; then
- return 1
- fi
-
- #All the challenge records share one name and one type, so the value is the
- #only thing that tells them apart. A certificate covering example.com and
- #*.example.com puts two of them at the same name at the same time.
- _record_id="$(echo "$response" | tr '{' "\n" | grep -- "$txtvalue" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
- _debug _record_id "$_record_id"
-
- if [ -z "$_record_id" ]; then
- _info "The TXT record is already gone, nothing to remove."
- return 0
- fi
-
- if ! _nexdns_rest DELETE "zones/$_domain_id/records/$_record_id"; then
- return 1
- fi
-
- _info "The TXT record has been removed."
- return 0
-}
-
-#################### Private functions below ##################################
-
-#Reads the token and the api url, and applies the default url.
-_nexdns_init() {
- NEXDNS_Token="${NEXDNS_Token:-$(_readaccountconf_mutable NEXDNS_Token)}"
- NEXDNS_Api="${NEXDNS_Api:-$(_readaccountconf_mutable NEXDNS_Api)}"
-
- if [ -z "$NEXDNS_Token" ]; then
- _err "You have not set NEXDNS_Token yet."
- _err "Create one at https://nexdns.tech/settings/api-keys, on a plan that includes API access, then:"
- _err "export NEXDNS_Token=\"your-api-token\""
- return 1
- fi
-
- if [ -z "$NEXDNS_Api" ]; then
- NEXDNS_Api="$NEXDNS_Api_Default"
- fi
- #A trailing slash would make every request path begin with a double slash.
- NEXDNS_Api="$(echo "$NEXDNS_Api" | sed 's|/*$||')"
- _debug NEXDNS_Api "$NEXDNS_Api"
-
- return 0
-}
-
-#_acme-challenge.www.example.com
-#returns
-# _sub_domain=_acme-challenge.www
-# _domain=example.com
-# _domain_id=Zm9vYmFy
-_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 ! _nexdns_rest GET "zones?search=$h&per_page=100"; then
- return 1
- fi
-
- #search matches on a substring, so the page can also hold zones that merely
- #contain h. Take the id of the one whose name is exactly h.
- _domain_id="$(echo "$response" | tr '{' "\n" | grep "\"name\":\"$h\"" | _egrep_o '"id":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
- if [ "$_domain_id" ]; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
- _domain=$h
- return 0
- fi
-
- p=$i
- i=$(_math "$i" + 1)
- done
-}
-
-#Usage: _nexdns_rest GET|POST|DELETE path [body] [attempt]
-_nexdns_rest() {
- m=$1
- ep=$2
- data=$3
- attempt=${4:-1}
- _debug "$ep"
-
- export _H1="Authorization: Bearer $NEXDNS_Token"
- export _H2="Content-Type: application/json"
- export _H3="Accept: application/json"
-
- if [ "$m" = "GET" ]; then
- response="$(_get "$NEXDNS_Api/$ep")"
- else
- _debug2 data "$data"
- response="$(_post "$data" "$NEXDNS_Api/$ep" "" "$m" "application/json")"
- fi
-
- if [ "$?" != "0" ]; then
- _err "error $ep"
- return 1
- fi
-
- #A single certificate costs a handful of requests, but a renewal sweep over
- #many of them meets the account's per-minute budget, and that run is
- #unattended. Retry-After is treated as a floor: an api may report the time one
- #token needs at an average rate and name a second when nothing frees for a
- #minute, so the wait grows on its own across attempts.
- if [ "$(grep "^HTTP" "$HTTP_HEADER" 2>/dev/null | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")" = "429" ]; then
- if [ "$attempt" -ge 4 ]; then
- _err "$m $ep failed: rate limited, and the wait budget is spent"
- return 1
- fi
-
- _retry_after="$(grep -i "^Retry-After" "$HTTP_HEADER" 2>/dev/null | _tail_n 1 | cut -d : -f 2 | tr -d " \r\n")"
- _backoff="$(_math "$attempt" \* 15)"
- #The header may also carry an http date. Anything but a plain count of
- #seconds falls through to the backoff rather than being parsed: guessing
- #wrong about a date is worse than waiting a known interval, and comparing a
- #date numerically would abort the hook outright.
- case "$_retry_after" in
- "" | *[!0-9]*) _retry_after="$_backoff" ;;
- *)
- if [ "$_retry_after" -lt "$_backoff" ]; then
- _retry_after="$_backoff"
- fi
- ;;
- esac
-
- #A wait longer than this is a refusal rather than a schedule, and sleeping
- #it out would hold the hook for the length of the window. Hand the run back
- #instead, so the next cron pass picks it up.
- if [ "$_retry_after" -gt 120 ]; then
- _err "$m $ep failed: rate limited for ${_retry_after}s, longer than this hook will wait"
- return 1
- fi
-
- _info "Rate limited by the NexDNS API; retrying in $_retry_after seconds."
- _sleep "$_retry_after"
-
- _nexdns_rest "$m" "$ep" "$data" "$(_math "$attempt" + 1)"
- return $?
- fi
-
- #Whitespace between a key and its value would defeat every match made on the
- #body, here and in the callers.
- response="$(echo "$response" | _normalizeJson)"
- _debug2 response "$response"
-
- #The status line decides success, not the body: a delete answers 204 with no
- #body at all, and a record whose own content contains "error": would otherwise
- #turn a stored value into a reported failure. The body is read only for the
- #message once the status says the request was rejected.
- _code="$(grep "^HTTP" "$HTTP_HEADER" 2>/dev/null | _tail_n 1 | cut -d " " -f 2 | tr -d "\r\n")"
- _debug2 _code "$_code"
- case "$_code" in
- "" | 2*)
- return 0
- ;;
- esac
-
- #A rejected request carries {"error":{"code":..,"message":..}}, so say what the
- #api says went wrong.
- _message="$(echo "$response" | _egrep_o '"message":"[^"]*"' | _head_n 1 | cut -d '"' -f 4)"
- if [ -z "$_message" ]; then
- _message="status $_code"
- fi
- _err "$m $ep failed: $_message"
-
- return 1
-}
diff --git a/dnsapi/dns_poweradmin.sh b/dnsapi/dns_poweradmin.sh
index a4c81835..db31fa4f 100644
--- a/dnsapi/dns_poweradmin.sh
+++ b/dnsapi/dns_poweradmin.sh
@@ -227,7 +227,7 @@ _poweradmin_rest() {
return 1
fi
- if printf '%s' "$response" | grep -q '"success"[ ]*:[ ]*false'; then
+ if printf '%s' "$response" | grep -q '"success"[[:space:]]*:[[:space:]]*false'; then
_err "API reported failure on $method $ep"
_debug "Response: $response"
return 1
diff --git a/dnsapi/dns_rage4.sh b/dnsapi/dns_rage4.sh
index b9abff17..c27fbc5f 100755
--- a/dnsapi/dns_rage4.sh
+++ b/dnsapi/dns_rage4.sh
@@ -71,7 +71,7 @@ dns_rage4_rm() {
_debug "Getting txt records"
_rage4_rest "getrecords/?id=${_domain_id}"
- _record_id=$(echo "$response" | tr '{' '\n' | grep '"TXT"' | grep "\"$txtvalue" | sed -n 's/.*"id":\([0-9][0-9]*\),.*/\1/p')
+ _record_id=$(echo "$response" | tr '{' '\n' | grep '"TXT"' | grep "\"$txtvalue" | sed -rn 's/.*"id":([[:digit:]]+),.*/\1/p')
if [ -z "$_record_id" ]; then
_err "error retrieving the record_id of the new TXT record in order to delete it, got: '$_record_id'."
return 1
diff --git a/dnsapi/dns_rltx.sh b/dnsapi/dns_rltx.sh
deleted file mode 100644
index 065ac177..00000000
--- a/dnsapi/dns_rltx.sh
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env sh
-# shellcheck disable=SC2034
-dns_rltx_info='Realtox Media Cloudpanel DNS API
-Site: realtoxmedia.de
-Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_rltx
-Options:
- RLTX_Key API Key
- RLTX_OrganizationID Organization ID
-'
-
-######## Public functions #####################
-
-#Usage: dns_rltx_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_rltx_add() {
- fulldomain=$1
- txtvalue=$2
-
- _info "Using Realtox Media Cloudpanel DNS API"
- _debug fulldomain "$fulldomain"
- _debug txtvalue "$txtvalue"
-
- if ! _rltx_init; then
- return 1
- fi
-
- if ! _get_root "$fulldomain"; then
- _err "Could not find matching DNS zone for $fulldomain"
- return 1
- fi
-
- _debug _domain_id "$_domain_id"
- _debug _domain "$_domain"
- _debug _sub_domain "$_sub_domain"
-
- data="{\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"ttl\":120}"
- if ! _rltx_rest POST "domains/$_domain_id/dns/acme-txt" "$data"; then
- _err "Add TXT record request failed"
- return 1
- fi
- if _contains "$response" '"status":"added"'; then
- _info "Added TXT record, OK"
- return 0
- fi
- _err "Add TXT record failed: $response"
- return 1
-}
-
-#Usage: fulldomain txtvalue
-#Remove the txt record after validation.
-dns_rltx_rm() {
- fulldomain=$1
- txtvalue=$2
-
- _info "Using Realtox Media Cloudpanel DNS API"
- _debug fulldomain "$fulldomain"
- _debug txtvalue "$txtvalue"
-
- if ! _rltx_init; then
- return 1
- fi
-
- if ! _get_root "$fulldomain"; then
- _err "Could not find matching DNS zone for $fulldomain"
- return 1
- fi
-
- _debug _domain_id "$_domain_id"
- _debug _domain "$_domain"
- _debug _sub_domain "$_sub_domain"
-
- data="{\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"ttl\":120}"
- if ! _rltx_rest DELETE "domains/$_domain_id/dns/acme-txt" "$data"; then
- _err "Remove TXT record request failed"
- return 1
- fi
- if _contains "$response" '"status":"removed"'; then
- _info "Removed TXT record, OK"
- return 0
- fi
- _err "Remove TXT record failed: $response"
- return 1
-}
-
-#################### Private functions below ##################################
-
-_rltx_init() {
- RLTX_Key="${RLTX_Key:-$(_readaccountconf_mutable RLTX_Key)}"
- RLTX_OrganizationID="${RLTX_OrganizationID:-$(_readaccountconf_mutable RLTX_OrganizationID)}"
-
- if [ -z "$RLTX_Key" ] || [ -z "$RLTX_OrganizationID" ]; then
- RLTX_Key=""
- RLTX_OrganizationID=""
- _err "Please specify RLTX_Key and RLTX_OrganizationID."
- _err "You can export them and retry: export RLTX_Key=... RLTX_OrganizationID=..."
- return 1
- fi
-
- _saveaccountconf_mutable RLTX_Key "$RLTX_Key"
- _saveaccountconf_mutable RLTX_OrganizationID "$RLTX_OrganizationID"
-}
-
-_get_root() {
- domain=$1
- fqdn_encoded="$(printf "%s" "$domain" | _url_encode)"
- if ! _rltx_rest GET "domains/dns/acme-zone?fqdn=$fqdn_encoded"; then
- return 1
- fi
- if ! _contains "$response" '"domain_id":"'; then
- return 1
- fi
-
- _domain_id="$(printf "%s" "$response" | _egrep_o '"domain_id":"[^"]*"' | cut -d : -f 2 | tr -d '"' | _head_n 1)"
- _domain="$(printf "%s" "$response" | _egrep_o '"zone":"[^"]*"' | cut -d : -f 2 | tr -d '"' | _head_n 1)"
- _sub_domain="$(printf "%s" "$response" | _egrep_o '"record_name":"[^"]*"' | cut -d : -f 2 | tr -d '"' | _head_n 1)"
-
- if [ -z "$_domain_id" ] || [ -z "$_domain" ] || [ -z "$_sub_domain" ]; then
- return 1
- fi
- return 0
-}
-
-_rltx_rest() {
- m=$1
- ep="$2"
- data="$3"
- _debug "$ep"
-
- export _H1="X-API-Key: $RLTX_Key"
- export _H2="X-Organization-ID: $RLTX_OrganizationID"
- export _H3="Content-Type: application/json"
-
- if [ "$m" = "GET" ]; then
- response="$(_get "https://api.ccp.realtoxmedia.de/api/$ep")"
- else
- _debug2 data "$data"
- response="$(_post "$data" "https://api.ccp.realtoxmedia.de/api/$ep" "" "$m")"
- fi
-
- if [ "$?" != "0" ]; then
- _err "Realtox Media Cloudpanel API request failed: $ep"
- return 1
- fi
- _debug2 response "$response"
- return 0
-}
diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh
index 8ba9a4fb..565f541b 100644
--- a/dnsapi/dns_selectel.sh
+++ b/dnsapi/dns_selectel.sh
@@ -368,7 +368,7 @@ _get_auth_token() {
_data_auth="{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"name\":\"${SL_Login_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"},\"password\":\"${SL_Pswd}\"}}},\"scope\":{\"project\":{\"name\":\"${SL_Project_Name}\",\"domain\":{\"name\":\"${SL_Login_ID}\"}}}}}"
export _H1="Content-Type: application/json"
_result=$(_post "$_data_auth" "$auth_uri")
- _token_keystone=$(grep 'x-subject-token' "$HTTP_HEADER" | cut -d ':' -f 2- | tr -d ' \t\r')
+ _token_keystone=$(grep 'x-subject-token' "$HTTP_HEADER" | sed -nE "s/[[:space:]]*x-subject-token:[[:space:]]*([[:print:]]*)(\r*)/\1/p")
_dt_curr=$(date +%s)
SL_Token_V2="${SL_Login_Name}${_sl_sep}${_token_keystone}${_sl_sep}${SL_Login_ID}${_sl_sep}${SL_Project_Name}${_sl_sep}${_dt_curr}"
_saveaccountconf_mutable SL_Token_V2 "$SL_Token_V2"
diff --git a/dnsapi/dns_selfhost.sh b/dnsapi/dns_selfhost.sh
index 25130146..782a5d5f 100644
--- a/dnsapi/dns_selfhost.sh
+++ b/dnsapi/dns_selfhost.sh
@@ -42,10 +42,7 @@ dns_selfhost_add() {
# only match full domains (at the beginning of the string or with a leading whitespace),
# e.g. don't match mytest.example.com or sub.test.example.com for test.example.com
# if the domain is defined multiple times only the last occurance will be matched
- # prepend a space to each line so "start of line" and "after whitespace"
- # can both be matched as "after a space/tab" (portable BRE, no ERE (^|..))
- _selfhost_tab="$(printf '\t')"
- mapEntry=$(echo "$SELFHOSTDNS_MAP" | sed 's/^/ /' | sed -n "s/.*[ $_selfhost_tab]\($fulldomain:[0-9][0-9]*:\{0,1\}[0-9]*\).*/\1/p")
+ mapEntry=$(echo "$SELFHOSTDNS_MAP" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain)(:[[:digit:]]+)([:]?[[:digit:]]*)(.*)/\2\3\4/p")
_debug2 mapEntry "$mapEntry"
if test -z "$mapEntry"; then
_err "SELFHOSTDNS_MAP must contain the fulldomain incl. prefix and at least one RID"
@@ -57,7 +54,7 @@ dns_selfhost_add() {
rid2=$(echo "$mapEntry" | cut -d: -f3)
# read last used rid domain
- lastUsedRidForDomainEntry=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed 's/^/ /' | sed -n "s/.*[ $_selfhost_tab]\($fulldomain:[0-9][0-9]*\).*/\1/p")
+ lastUsedRidForDomainEntry=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain:[[:digit:]]+)(.*)/\2/p")
_debug2 lastUsedRidForDomainEntry "$lastUsedRidForDomainEntry"
lastUsedRidForDomain=$(echo "$lastUsedRidForDomainEntry" | cut -d: -f2)
diff --git a/dnsapi/dns_udr.sh b/dnsapi/dns_udr.sh
index dbc959d6..656a0557 100644
--- a/dnsapi/dns_udr.sh
+++ b/dnsapi/dns_udr.sh
@@ -145,8 +145,8 @@ _udr_rest() {
_debug data "${data}"
response="$(_post "${data}" "${UDR_API}?s_login=${UDR_USER}&s_pw=${UDR_PASS}" "" "POST")"
- _code=$(echo "$response" | _egrep_o "code = ([0-9]+)" | _head_n 1 | cut -d = -f 2 | tr -d ' \t\r')
- _description=$(echo "$response" | _egrep_o "description = .*" | _head_n 1 | cut -d = -f 2 | tr -d '\r' | sed -e 's/^[ ]*//' -e 's/[ ]*$//')
+ _code=$(echo "$response" | _egrep_o "code = ([0-9]+)" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
+ _description=$(echo "$response" | _egrep_o "description = .*" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
_debug response_code "$_code"
_debug response_description "$_description"
diff --git a/dnsapi/dns_world4you.sh b/dnsapi/dns_world4you.sh
index 0a1cda6b..f59715ac 100644
--- a/dnsapi/dns_world4you.sh
+++ b/dnsapi/dns_world4you.sh
@@ -61,7 +61,7 @@ dns_world4you_add() {
if _contains "$res" "successfully"; then
return 0
else
- msg=$(_w4y_alert_msg "$res")
+ msg=$(echo "$res" | grep -A 20 'alert-notification' | grep 'class="weak-title">[^<]' | sed 's/<[^>]*>//g;s/^\s*//g')
if [ "$msg" = '' ]; then
_err "Unable to add record: Unknown error"
echo "$ret" >'error-01.html'
@@ -125,7 +125,7 @@ dns_world4you_rm() {
if _contains "$res" "successfully"; then
return 0
else
- msg=$(_w4y_alert_msg "$res")
+ msg=$(echo "$res" | grep -A 20 'alert-notification' | grep 'class="weak-title">[^<]' | sed 's/<[^>]*>//g;s/^\s*//g')
if [ "$msg" = '' ]; then
_err "Unable to remove record: Unknown error"
echo "$ret" >'error-01.html'
@@ -145,17 +145,6 @@ dns_world4you_rm() {
################ Private functions ################
-# Usage: _w4y_alert_msg
-# Extracts the error text out of the alert box of a DNS page.
-# "grep -A" is not portable (Solaris /usr/bin/grep: "illegal option -- A"),
-# so select from the alert to EOF and keep the same number of lines.
-# "\s" is a GNU sed extension, use an explicit space/tab bracket instead.
-_w4y_alert_msg() {
- _w4y_tab=$(printf '\t')
- echo "$1" | sed -n '/alert-notification/,$p' | _head_n 21 |
- grep 'class="weak-title">[^<]' | sed "s/<[^>]*>//g;s/^[ $_w4y_tab]*//"
-}
-
# Usage: _login
_login() {
WORLD4YOU_USERNAME="${WORLD4YOU_USERNAME:-$(_readaccountconf_mutable WORLD4YOU_USERNAME)}"
diff --git a/dnsapi/dns_yandex360.sh b/dnsapi/dns_yandex360.sh
index 98841d6b..18d01361 100644
--- a/dnsapi/dns_yandex360.sh
+++ b/dnsapi/dns_yandex360.sh
@@ -149,7 +149,7 @@ _check_variables() {
org_response="$(echo "$org_response" | _normalizeJson)"
YANDEX360_ORG_ID=$(
echo "$org_response" |
- _egrep_o '"id":[ ]*[0-9]+' |
+ _egrep_o '"id":[[:space:]]*[0-9]+' |
cut -d':' -f2
)
_debug 'Automatically retrieved YANDEX360_ORG_ID' "$YANDEX360_ORG_ID"
@@ -216,7 +216,7 @@ _get_token() {
interval=$(
echo "$response" |
- _egrep_o '"interval":[ ]*[0-9]+' |
+ _egrep_o '"interval":[[:space:]]*[0-9]+' |
cut -d':' -f2
)
_debug 'Polling interval' "$interval"
diff --git a/dnsapi/dns_yc.sh b/dnsapi/dns_yc.sh
index cacb5977..36c49ce4 100644
--- a/dnsapi/dns_yc.sh
+++ b/dnsapi/dns_yc.sh
@@ -22,32 +22,21 @@ dns_yc_add() {
fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
txtvalue=$2
- # YC_SA_Key_File_PEM_b64/Path are always persisted to the domain conf below,
- # so they must be recovered from there first (account conf is only a
- # fallback for the YC_Folder_ID case, see the SA_ID/SA_Key_ID save below).
- YC_SA_Key_File_PEM_b64="${YC_SA_Key_File_PEM_b64:-$(_readdomainconf YC_SA_Key_File_PEM_b64)}"
YC_SA_Key_File_PEM_b64="${YC_SA_Key_File_PEM_b64:-$(_readaccountconf_mutable YC_SA_Key_File_PEM_b64)}"
- YC_SA_Key_File_Path="${YC_SA_Key_File_Path:-$(_readdomainconf YC_SA_Key_File_Path)}"
YC_SA_Key_File_Path="${YC_SA_Key_File_Path:-$(_readaccountconf_mutable YC_SA_Key_File_Path)}"
if [ "$YC_SA_Key_File_PEM_b64" ]; then
echo "$YC_SA_Key_File_PEM_b64" | _dbase64 >private.key
YC_SA_Key_File="private.key"
- _yc_key_is_temp=1
_savedomainconf YC_SA_Key_File_PEM_b64 "$YC_SA_Key_File_PEM_b64"
else
YC_SA_Key_File="$YC_SA_Key_File_Path"
- _yc_key_is_temp=""
_savedomainconf YC_SA_Key_File_Path "$YC_SA_Key_File_Path"
fi
- YC_Zone_ID="${YC_Zone_ID:-$(_readdomainconf YC_Zone_ID)}"
YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
- YC_Folder_ID="${YC_Folder_ID:-$(_readdomainconf YC_Folder_ID)}"
YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
- YC_SA_ID="${YC_SA_ID:-$(_readdomainconf YC_SA_ID)}"
YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
- YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readdomainconf YC_SA_Key_ID)}"
YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
if [ "$YC_SA_ID" ] && [ "$YC_SA_Key_ID" ] && [ "$YC_SA_Key_File" ]; then
@@ -76,21 +65,11 @@ dns_yc_add() {
return 1
fi
else
- # Clear both possible stores -- YC_Zone_ID/YC_Folder_ID/key material are
- # persisted to the domain conf, while YC_SA_ID/YC_SA_Key_ID may have been
- # saved account-wide (Folder_ID mode), so a plain _clearaccountconf alone
- # would leave stale values behind in whichever store wasn't touched.
- _cleardomainconf YC_Zone_ID
_clearaccountconf YC_Zone_ID
- _cleardomainconf YC_Folder_ID
_clearaccountconf YC_Folder_ID
- _cleardomainconf YC_SA_ID
- _clearaccountconf_mutable YC_SA_ID
- _cleardomainconf YC_SA_Key_ID
- _clearaccountconf_mutable YC_SA_Key_ID
- _cleardomainconf YC_SA_Key_File_PEM_b64
+ _clearaccountconf YC_SA_ID
+ _clearaccountconf YC_SA_Key_ID
_clearaccountconf YC_SA_Key_File_PEM_b64
- _cleardomainconf YC_SA_Key_File_Path
_clearaccountconf YC_SA_Key_File_Path
_err "You didn't specify a YC_SA_ID or YC_SA_Key_ID or YC_SA_Key_File."
return 1
@@ -131,30 +110,11 @@ dns_yc_rm() {
fulldomain="$(echo "$1". | _lower_case)" # Add dot at end of domain name
txtvalue=$2
- YC_Zone_ID="${YC_Zone_ID:-$(_readdomainconf YC_Zone_ID)}"
YC_Zone_ID="${YC_Zone_ID:-$(_readaccountconf_mutable YC_Zone_ID)}"
- YC_Folder_ID="${YC_Folder_ID:-$(_readdomainconf YC_Folder_ID)}"
YC_Folder_ID="${YC_Folder_ID:-$(_readaccountconf_mutable YC_Folder_ID)}"
- YC_SA_ID="${YC_SA_ID:-$(_readdomainconf YC_SA_ID)}"
YC_SA_ID="${YC_SA_ID:-$(_readaccountconf_mutable YC_SA_ID)}"
- YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readdomainconf YC_SA_Key_ID)}"
YC_SA_Key_ID="${YC_SA_Key_ID:-$(_readaccountconf_mutable YC_SA_Key_ID)}"
- # See dns_yc_add() for why domain conf is checked before account conf.
- YC_SA_Key_File_PEM_b64="${YC_SA_Key_File_PEM_b64:-$(_readdomainconf YC_SA_Key_File_PEM_b64)}"
- YC_SA_Key_File_PEM_b64="${YC_SA_Key_File_PEM_b64:-$(_readaccountconf_mutable YC_SA_Key_File_PEM_b64)}"
- YC_SA_Key_File_Path="${YC_SA_Key_File_Path:-$(_readdomainconf YC_SA_Key_File_Path)}"
- YC_SA_Key_File_Path="${YC_SA_Key_File_Path:-$(_readaccountconf_mutable YC_SA_Key_File_Path)}"
-
- if [ "$YC_SA_Key_File_PEM_b64" ]; then
- echo "$YC_SA_Key_File_PEM_b64" | _dbase64 >private.key
- YC_SA_Key_File="private.key"
- _yc_key_is_temp=1
- else
- YC_SA_Key_File="$YC_SA_Key_File_Path"
- _yc_key_is_temp=""
- fi
-
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
_err "invalid domain"
@@ -164,10 +124,16 @@ dns_yc_rm() {
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- # upsertRecordSets.deletions removes only the given value from the rrset,
- # leaving any other values at the same name (e.g. base + wildcard domain)
- # intact -- no need to read the current data set and recompute it.
- if _yc_rest POST "zones/$_domain_id:upsertRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":[\"$txtvalue\"]}]}"; then
+ _debug "Getting txt records"
+ if _yc_rest GET "zones/${_domain_id}:getRecordSet?type=TXT&name=$_sub_domain"; then
+ exists_txtvalue=$(echo "$response" | _normalizeJson | _egrep_o "\"data\".*\][^,]*" | _egrep_o "[^:]*$")
+ _debug exists_txtvalue "$exists_txtvalue"
+ else
+ _err "Error: $response"
+ return 1
+ fi
+
+ if _yc_rest POST "zones/$_domain_id:updateRecordSets" "{\"deletions\": [ { \"name\":\"$_sub_domain\",\"type\":\"TXT\",\"ttl\":\"120\",\"data\":$exists_txtvalue}]}"; then
if _contains "$response" "\"done\": true"; then
_info "Delete, OK"
return 0
@@ -228,7 +194,7 @@ _get_root() {
return 1
fi
if _contains "$response" "\"zone\": \"$h\""; then
- _domain_id=$(echo "$response" | _normalizeJson | _egrep_o "[^{]*\"zone\":\"$h\"[^}]*" | _egrep_o "\"id\"[^,]*" | _egrep_o "[^:][^:]*$" | tr -d '"')
+ _domain_id=$(echo "$response" | _normalizeJson | _egrep_o "[^{]*\"zone\":\"$h\"[^}]*" | _egrep_o "\"id\"[^,]*" | _egrep_o "[^:]*$" | tr -d '"')
_debug _domain_id "$_domain_id"
if [ "$_domain_id" ]; then
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
@@ -289,9 +255,7 @@ _yc_login() {
_signature=$(printf "%s.%s" "$header" "$payload" | _sign "$YC_SA_Key_File" "sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1" | _url_replace)
_debug2 _signature "$_signature"
- if [ "$_yc_key_is_temp" ]; then
- rm -f "$YC_SA_Key_File"
- fi
+ rm -rf "$YC_SA_Key_File"
_jwt=$(printf "{\"jwt\": \"%s.%s.%s\"}" "$header" "$payload" "$_signature")
_debug2 _jwt "$_jwt"
@@ -300,7 +264,7 @@ _yc_login() {
_iam_response="$(_post "$_jwt" "https://iam.api.cloud.yandex.net/iam/v1/tokens" "" "POST")"
_debug3 _iam_response "$(echo "$_iam_response" | _normalizeJson)"
- YC_Token="$(echo "$_iam_response" | _normalizeJson | _egrep_o "\"iamToken\"[^,]*" | _egrep_o "[^:][^:]*$" | tr -d '"')"
+ YC_Token="$(echo "$_iam_response" | _normalizeJson | _egrep_o "\"iamToken\"[^,]*" | _egrep_o "[^:]*$" | tr -d '"')"
_debug3 YC_Token
return 0
diff --git a/notify/waha.sh b/notify/waha.sh
index 573295e9..989f57ac 100755
--- a/notify/waha.sh
+++ b/notify/waha.sh
@@ -57,8 +57,9 @@ waha_send() {
_debug "_data" "$_data"
+ export _H1="Content-Type: application/json"
if [ "$WAHA_API_KEY" ]; then
- export _H1="X-Api-Key: $WAHA_API_KEY"
+ export _H2="X-Api-Key: $WAHA_API_KEY"
fi
_waha_url="${WAHA_URL}/api/sendText"