diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 615e5d8b..727ba315 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -65,7 +65,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- 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
@@ -113,7 +113,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install tools
run: brew install socat
- name: Clone acmetest
@@ -164,7 +164,7 @@ jobs:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install cygwin base packages with chocolatey
run: |
choco config get cacheLocation
@@ -204,7 +204,7 @@ jobs:
FreeBSD:
- runs-on: macos-12
+ runs-on: ubuntu-latest
needs: Windows
env:
TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -223,10 +223,10 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- 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@v0
+ - uses: vmactions/freebsd-vm@v1
with:
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
@@ -255,7 +255,7 @@ jobs:
OpenBSD:
- runs-on: macos-12
+ runs-on: ubuntu-latest
needs: FreeBSD
env:
TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -274,10 +274,10 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- 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@v0
+ - uses: vmactions/openbsd-vm@v1
with:
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
@@ -306,7 +306,7 @@ jobs:
NetBSD:
- runs-on: macos-12
+ runs-on: ubuntu-latest
needs: OpenBSD
env:
TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -325,14 +325,14 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- 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@v0
+ - uses: vmactions/netbsd-vm@v1
with:
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 curl socat
+ /usr/sbin/pkg_add curl socat
usesh: true
copyback: false
run: |
@@ -358,7 +358,7 @@ jobs:
DragonFlyBSD:
- runs-on: macos-12
+ runs-on: ubuntu-latest
needs: NetBSD
env:
TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -377,14 +377,14 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- 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@v0
+ - uses: vmactions/dragonflybsd-vm@v1
with:
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 curl socat
+ pkg install -y curl socat libnghttp2
usesh: true
copyback: false
run: |
@@ -413,7 +413,7 @@ jobs:
Solaris:
- runs-on: macos-12
+ runs-on: ubuntu-latest
needs: DragonFlyBSD
env:
TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -433,10 +433,10 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- 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@v0
+ - uses: vmactions/solaris-vm@v1
with:
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}}'
copyback: false
@@ -463,3 +463,52 @@ jobs:
./letest.sh
+ Omnios:
+ runs-on: ubuntu-latest
+ needs: Solaris
+ env:
+ TEST_DNS : ${{ secrets.TEST_DNS }}
+ TestingDomain: ${{ secrets.TestingDomain }}
+ TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
+ TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
+ TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
+ CASE: le_test_dnsapi
+ TEST_LOCAL: 1
+ DEBUG: ${{ secrets.DEBUG }}
+ http_proxy: ${{ secrets.http_proxy }}
+ https_proxy: ${{ secrets.https_proxy }}
+ HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Omnios doesn't accept the expired ISRG X1 root
+ TokenName1: ${{ secrets.TokenName1}}
+ TokenName2: ${{ secrets.TokenName2}}
+ TokenName3: ${{ secrets.TokenName3}}
+ TokenName4: ${{ secrets.TokenName4}}
+ TokenName5: ${{ secrets.TokenName5}}
+ steps:
+ - uses: actions/checkout@v4
+ - 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:
+ 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}}'
+ copyback: false
+ prepare: pkg install socat
+ run: |
+ if [ "${{ secrets.TokenName1}}" ] ; then
+ export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
+ fi
+ if [ "${{ secrets.TokenName2}}" ] ; then
+ export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
+ fi
+ if [ "${{ secrets.TokenName3}}" ] ; then
+ export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
+ fi
+ if [ "${{ secrets.TokenName4}}" ] ; then
+ export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
+ fi
+ if [ "${{ secrets.TokenName5}}" ] ; then
+ export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
+ fi
+ cd ../acmetest
+ ./letest.sh
+
+
diff --git a/.github/workflows/DragonFlyBSD.yml b/.github/workflows/DragonFlyBSD.yml
index 6daa9be4..f360f85c 100644
--- a/.github/workflows/DragonFlyBSD.yml
+++ b/.github/workflows/DragonFlyBSD.yml
@@ -1,71 +1,71 @@
-name: DragonFlyBSD
-on:
- push:
- branches:
- - '*'
- paths:
- - '*.sh'
- - '.github/workflows/DragonFlyBSD.yml'
-
- pull_request:
- branches:
- - dev
- paths:
- - '*.sh'
- - '.github/workflows/DragonFlyBSD.yml'
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-
-
-
-jobs:
- DragonFlyBSD:
- strategy:
- matrix:
- include:
- - TEST_ACME_Server: "LetsEncrypt.org_test"
- CA_ECDSA: ""
- CA: ""
- CA_EMAIL: ""
- TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
- #- TEST_ACME_Server: "ZeroSSL.com"
- # CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
- # CA: "ZeroSSL RSA Domain Secure Site CA"
- # CA_EMAIL: "githubtest@acme.sh"
- # TEST_PREFERRED_CHAIN: ""
- runs-on: macos-12
- 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@v3
- - uses: vmactions/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/dragonflybsd-vm@v0
- with:
- envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
- copyback: "false"
- nat: |
- "8080": "80"
- prepare: |
- pkg install -y curl socat
- usesh: true
- run: |
- cd ../acmetest \
- && ./letest.sh
-
-
+name: DragonFlyBSD
+on:
+ push:
+ branches:
+ - '*'
+ paths:
+ - '*.sh'
+ - '.github/workflows/DragonFlyBSD.yml'
+
+ pull_request:
+ branches:
+ - dev
+ paths:
+ - '*.sh'
+ - '.github/workflows/DragonFlyBSD.yml'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+
+
+jobs:
+ DragonFlyBSD:
+ strategy:
+ matrix:
+ include:
+ - TEST_ACME_Server: "LetsEncrypt.org_test"
+ CA_ECDSA: ""
+ CA: ""
+ CA_EMAIL: ""
+ TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+ #- TEST_ACME_Server: "ZeroSSL.com"
+ # CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+ # CA: "ZeroSSL RSA Domain Secure Site CA"
+ # CA_EMAIL: "githubtest@acme.sh"
+ # TEST_PREFERRED_CHAIN: ""
+ runs-on: ubuntu-latest
+ env:
+ TEST_LOCAL: 1
+ TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+ CA_ECDSA: ${{ matrix.CA_ECDSA }}
+ CA: ${{ matrix.CA }}
+ CA_EMAIL: ${{ matrix.CA_EMAIL }}
+ TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+ ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: vmactions/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/dragonflybsd-vm@v1
+ with:
+ envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+ nat: |
+ "8080": "80"
+ prepare: |
+ pkg install -y curl socat libnghttp2
+ usesh: true
+ copyback: false
+ run: |
+ cd ../acmetest \
+ && ./letest.sh
+
+
diff --git a/.github/workflows/FreeBSD.yml b/.github/workflows/FreeBSD.yml
index 0fa55fd4..b90c9ccd 100644
--- a/.github/workflows/FreeBSD.yml
+++ b/.github/workflows/FreeBSD.yml
@@ -41,7 +41,7 @@ jobs:
# CA: "ZeroSSL RSA Domain Secure Site CA"
# CA_EMAIL: "githubtest@acme.sh"
# TEST_PREFERRED_CHAIN: ""
- runs-on: macos-12
+ runs-on: ubuntu-latest
env:
TEST_LOCAL: 1
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -51,7 +51,7 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
id: tunnel
with:
@@ -61,7 +61,7 @@ jobs:
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/freebsd-vm@v0
+ - uses: vmactions/freebsd-vm@v1
with:
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
diff --git a/.github/workflows/Linux.yml b/.github/workflows/Linux.yml
index 156fa5df..33e43483 100644
--- a/.github/workflows/Linux.yml
+++ b/.github/workflows/Linux.yml
@@ -33,7 +33,7 @@ jobs:
TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
TEST_ACME_Server: "LetsEncrypt.org_test"
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Clone acmetest
run: |
cd .. \
diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml
index c1f29769..c3f046ab 100644
--- a/.github/workflows/MacOS.yml
+++ b/.github/workflows/MacOS.yml
@@ -44,7 +44,7 @@ jobs:
CA_EMAIL: ${{ matrix.CA_EMAIL }}
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install tools
run: brew install socat
- name: Clone acmetest
diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml
index 25872c42..4574bef5 100644
--- a/.github/workflows/NetBSD.yml
+++ b/.github/workflows/NetBSD.yml
@@ -1,71 +1,71 @@
-name: NetBSD
-on:
- push:
- branches:
- - '*'
- paths:
- - '*.sh'
- - '.github/workflows/NetBSD.yml'
-
- pull_request:
- branches:
- - dev
- paths:
- - '*.sh'
- - '.github/workflows/NetBSD.yml'
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-
-
-
-jobs:
- NetBSD:
- strategy:
- matrix:
- include:
- - TEST_ACME_Server: "LetsEncrypt.org_test"
- CA_ECDSA: ""
- CA: ""
- CA_EMAIL: ""
- TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
- #- TEST_ACME_Server: "ZeroSSL.com"
- # CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
- # CA: "ZeroSSL RSA Domain Secure Site CA"
- # CA_EMAIL: "githubtest@acme.sh"
- # TEST_PREFERRED_CHAIN: ""
- runs-on: macos-12
- 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@v3
- - uses: vmactions/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/netbsd-vm@v0
- with:
- envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
- nat: |
- "8080": "80"
- prepare: |
- pkg_add curl socat
- usesh: true
- copyback: false
- run: |
- cd ../acmetest \
- && ./letest.sh
-
-
+name: NetBSD
+on:
+ push:
+ branches:
+ - '*'
+ paths:
+ - '*.sh'
+ - '.github/workflows/NetBSD.yml'
+
+ pull_request:
+ branches:
+ - dev
+ paths:
+ - '*.sh'
+ - '.github/workflows/NetBSD.yml'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+
+
+jobs:
+ NetBSD:
+ strategy:
+ matrix:
+ include:
+ - TEST_ACME_Server: "LetsEncrypt.org_test"
+ CA_ECDSA: ""
+ CA: ""
+ CA_EMAIL: ""
+ TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+ #- TEST_ACME_Server: "ZeroSSL.com"
+ # CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+ # CA: "ZeroSSL RSA Domain Secure Site CA"
+ # CA_EMAIL: "githubtest@acme.sh"
+ # TEST_PREFERRED_CHAIN: ""
+ runs-on: ubuntu-latest
+ env:
+ TEST_LOCAL: 1
+ TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+ CA_ECDSA: ${{ matrix.CA_ECDSA }}
+ CA: ${{ matrix.CA }}
+ CA_EMAIL: ${{ matrix.CA_EMAIL }}
+ TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+ ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: vmactions/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/netbsd-vm@v1
+ with:
+ envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+ nat: |
+ "8080": "80"
+ prepare: |
+ /usr/sbin/pkg_add curl socat
+ usesh: true
+ copyback: false
+ run: |
+ cd ../acmetest \
+ && ./letest.sh
+
+
diff --git a/.github/workflows/Omnios.yml b/.github/workflows/Omnios.yml
new file mode 100644
index 00000000..e3da0be8
--- /dev/null
+++ b/.github/workflows/Omnios.yml
@@ -0,0 +1,75 @@
+name: Omnios
+on:
+ push:
+ branches:
+ - '*'
+ paths:
+ - '*.sh'
+ - '.github/workflows/Omnios.yml'
+
+ pull_request:
+ branches:
+ - dev
+ paths:
+ - '*.sh'
+ - '.github/workflows/Omnios.yml'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+
+
+jobs:
+ Omnios:
+ strategy:
+ matrix:
+ include:
+ - TEST_ACME_Server: "LetsEncrypt.org_test"
+ CA_ECDSA: ""
+ CA: ""
+ CA_EMAIL: ""
+ TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+ - TEST_ACME_Server: "LetsEncrypt.org_test"
+ CA_ECDSA: ""
+ CA: ""
+ CA_EMAIL: ""
+ TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+ ACME_USE_WGET: 1
+ #- TEST_ACME_Server: "ZeroSSL.com"
+ # CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+ # CA: "ZeroSSL RSA Domain Secure Site CA"
+ # CA_EMAIL: "githubtest@acme.sh"
+ # TEST_PREFERRED_CHAIN: ""
+ runs-on: ubuntu-latest
+ env:
+ TEST_LOCAL: 1
+ TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+ CA_ECDSA: ${{ matrix.CA_ECDSA }}
+ CA: ${{ matrix.CA }}
+ CA_EMAIL: ${{ matrix.CA_EMAIL }}
+ TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+ ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: vmactions/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/omnios-vm@v1
+ with:
+ envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+ nat: |
+ "8080": "80"
+ prepare: pkg install socat wget
+ copyback: false
+ run: |
+ cd ../acmetest \
+ && ./letest.sh
+
+
diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml
index 7746645a..e141c47b 100644
--- a/.github/workflows/OpenBSD.yml
+++ b/.github/workflows/OpenBSD.yml
@@ -41,7 +41,7 @@ jobs:
# CA: "ZeroSSL RSA Domain Secure Site CA"
# CA_EMAIL: "githubtest@acme.sh"
# TEST_PREFERRED_CHAIN: ""
- runs-on: macos-12
+ runs-on: ubuntu-latest
env:
TEST_LOCAL: 1
TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -51,7 +51,7 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
id: tunnel
with:
@@ -61,12 +61,12 @@ jobs:
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/openbsd-vm@v0
+ - uses: vmactions/openbsd-vm@v1
with:
envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
nat: |
"8080": "80"
- prepare: pkg_add socat curl wget
+ prepare: pkg_add socat curl wget libnghttp2
usesh: true
copyback: false
run: |
diff --git a/.github/workflows/PebbleStrict.yml b/.github/workflows/PebbleStrict.yml
index 9f3a98ce..3f8fdb62 100644
--- a/.github/workflows/PebbleStrict.yml
+++ b/.github/workflows/PebbleStrict.yml
@@ -33,7 +33,7 @@ jobs:
TEST_CA: "Pebble Intermediate CA"
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get install -y socat
- name: Run Pebble
@@ -58,7 +58,7 @@ jobs:
TEST_IPCERT: 1
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get install -y socat
- name: Run Pebble
diff --git a/.github/workflows/Solaris.yml b/.github/workflows/Solaris.yml
index 34d31a59..bdd3f040 100644
--- a/.github/workflows/Solaris.yml
+++ b/.github/workflows/Solaris.yml
@@ -1,74 +1,75 @@
-name: Solaris
-on:
- push:
- branches:
- - '*'
- paths:
- - '*.sh'
- - '.github/workflows/Solaris.yml'
-
- pull_request:
- branches:
- - dev
- paths:
- - '*.sh'
- - '.github/workflows/Solaris.yml'
-
-
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- Solaris:
- strategy:
- matrix:
- include:
- - TEST_ACME_Server: "LetsEncrypt.org_test"
- CA_ECDSA: ""
- CA: ""
- CA_EMAIL: ""
- TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
- - TEST_ACME_Server: "LetsEncrypt.org_test"
- CA_ECDSA: ""
- CA: ""
- CA_EMAIL: ""
- TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
- ACME_USE_WGET: 1
- #- TEST_ACME_Server: "ZeroSSL.com"
- # CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
- # CA: "ZeroSSL RSA Domain Secure Site CA"
- # CA_EMAIL: "githubtest@acme.sh"
- # TEST_PREFERRED_CHAIN: ""
- runs-on: macos-12
- env:
- TEST_LOCAL: 1
- TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
- CA_ECDSA: ${{ matrix.CA_ECDSA }}
- CA: ${{ matrix.CA }}
- CA_EMAIL: ${{ matrix.CA_EMAIL }}
- TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
- ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
- steps:
- - uses: actions/checkout@v3
- - uses: vmactions/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/solaris-vm@v0
- with:
- envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
- copyback: "false"
- nat: |
- "8080": "80"
- prepare: pkgutil -y -i socat curl wget
- run: |
- cd ../acmetest \
- && ./letest.sh
-
+name: Solaris
+on:
+ push:
+ branches:
+ - '*'
+ paths:
+ - '*.sh'
+ - '.github/workflows/Solaris.yml'
+
+ pull_request:
+ branches:
+ - dev
+ paths:
+ - '*.sh'
+ - '.github/workflows/Solaris.yml'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+
+
+jobs:
+ Solaris:
+ strategy:
+ matrix:
+ include:
+ - TEST_ACME_Server: "LetsEncrypt.org_test"
+ CA_ECDSA: ""
+ CA: ""
+ CA_EMAIL: ""
+ TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+ - TEST_ACME_Server: "LetsEncrypt.org_test"
+ CA_ECDSA: ""
+ CA: ""
+ CA_EMAIL: ""
+ TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+ ACME_USE_WGET: 1
+ #- TEST_ACME_Server: "ZeroSSL.com"
+ # CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+ # CA: "ZeroSSL RSA Domain Secure Site CA"
+ # CA_EMAIL: "githubtest@acme.sh"
+ # TEST_PREFERRED_CHAIN: ""
+ runs-on: ubuntu-latest
+ env:
+ TEST_LOCAL: 1
+ TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+ CA_ECDSA: ${{ matrix.CA_ECDSA }}
+ CA: ${{ matrix.CA }}
+ CA_EMAIL: ${{ matrix.CA_EMAIL }}
+ TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+ ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: vmactions/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/solaris-vm@v1
+ with:
+ envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+ nat: |
+ "8080": "80"
+ prepare: pkgutil -y -i socat curl wget
+ copyback: false
+ run: |
+ cd ../acmetest \
+ && ./letest.sh
+
+
diff --git a/.github/workflows/Ubuntu.yml b/.github/workflows/Ubuntu.yml
index 4bf2ba29..53cc1060 100644
--- a/.github/workflows/Ubuntu.yml
+++ b/.github/workflows/Ubuntu.yml
@@ -70,7 +70,7 @@ jobs:
TestingDomain: ${{ matrix.TestingDomain }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get install -y socat wget
- name: Start StepCA
diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml
index c02e2f77..61ef5ad8 100644
--- a/.github/workflows/Windows.yml
+++ b/.github/workflows/Windows.yml
@@ -49,7 +49,7 @@ jobs:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install cygwin base packages with chocolatey
run: |
choco config get cacheLocation
diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml
index 48c44429..ea446d84 100644
--- a/.github/workflows/dockerhub.yml
+++ b/.github/workflows/dockerhub.yml
@@ -41,7 +41,7 @@ jobs:
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
steps:
- name: checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
diff --git a/.github/workflows/pr_dns.yml b/.github/workflows/pr_dns.yml
index 5faa9105..4d7a325d 100644
--- a/.github/workflows/pr_dns.yml
+++ b/.github/workflows/pr_dns.yml
@@ -4,8 +4,6 @@ on:
pull_request_target:
types:
- opened
- branches:
- - 'dev'
paths:
- 'dnsapi/*.sh'
@@ -22,9 +20,11 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
body: `**Welcome**
- Please make sure you're read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
+ First thing: don't send PR to the master branch, please send to the dev branch instead.
+ Please make sure you've read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
Then reply on this message, otherwise, your code will not be reviewed or merged.
We look forward to reviewing your Pull request shortly ✨
+ 注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试.
`
})
diff --git a/.github/workflows/pr_notify.yml b/.github/workflows/pr_notify.yml
index 4844e297..3b0e3e4b 100644
--- a/.github/workflows/pr_notify.yml
+++ b/.github/workflows/pr_notify.yml
@@ -22,7 +22,7 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
body: `**Welcome**
- Please make sure you're read our [Code-of-conduct](../wiki/Code-of-conduct) and add the usage here: [notify](../wiki/notify).
+ Please make sure you've read our [Code-of-conduct](../wiki/Code-of-conduct) and add the usage here: [notify](../wiki/notify).
Then reply on this message, otherwise, your code will not be reviewed or merged.
We look forward to reviewing your Pull request shortly ✨
`
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
index a5a08bbf..746727d4 100644
--- a/.github/workflows/shellcheck.yml
+++ b/.github/workflows/shellcheck.yml
@@ -22,7 +22,7 @@ jobs:
ShellCheck:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install Shellcheck
run: sudo apt-get install -y shellcheck
- name: DoShellcheck
@@ -31,7 +31,7 @@ jobs:
shfmt:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install shfmt
run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
- name: shfmt
diff --git a/README.md b/README.md
index 15bc4089..9a5c106b 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
[![Windows](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
[![Solaris](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)
-
+[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)
![Shellcheck](https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg)
![PebbleStrict](https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/badge.svg)
@@ -51,14 +51,12 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
- [ruby-china.org](https://ruby-china.org/topics/31983)
- [Proxmox](https://pve.proxmox.com/wiki/Certificate_Management)
- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89)
-- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt)
- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty)
- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709)
- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html)
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
-- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh)
- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
-- [CentOS Web Panel](http://centos-webpanel.com/)
+- [CentOS Web Panel](https://control-webpanel.com)
- [lnmp.org](https://lnmp.org/)
- [more...](https://github.com/acmesh-official/acme.sh/wiki/Blogs-and-tutorials)
@@ -75,20 +73,21 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
|7|[![OpenBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
|8|[![NetBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
|9|[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
-|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
-|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
-|12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
-|13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
-|14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
-|15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
-|16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
-|17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
-|18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
-|19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
-|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
-|11|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
-|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
-|23|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
+|10|[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)|Omnios
+|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
+|12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
+|13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
+|14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
+|15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
+|16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
+|17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
+|18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
+|19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
+|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
+|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
+|22|-----| Cloud Linux https://github.com/acmesh-official/acme.sh/issues/111
+|23|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
+|24|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
Check our [testing project](https://github.com/acmesh-official/acmetest):
@@ -508,10 +507,6 @@ Support this project with your organization. Your logo will show up here with a
-#### Sponsors
-
-[![quantumca-acmesh-logo](https://user-images.githubusercontent.com/8305679/183255712-634ee1db-bb61-4c03-bca0-bacce99e078c.svg)](https://www.quantumca.com.cn/?__utm_source=acmesh-donation)
-
# 19. License & Others
diff --git a/acme.sh b/acme.sh
index 8fbc16d0..a51b0249 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env sh
-VER=3.0.6
+VER=3.0.8
PROJECT_NAME="acme.sh"
@@ -102,12 +102,12 @@ ECC_SUFFIX="${ECC_SEP}ecc"
LOG_LEVEL_1=1
LOG_LEVEL_2=2
LOG_LEVEL_3=3
-DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
+DEFAULT_LOG_LEVEL="$LOG_LEVEL_2"
DEBUG_LEVEL_1=1
DEBUG_LEVEL_2=2
DEBUG_LEVEL_3=3
-DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
+DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_2
DEBUG_LEVEL_NONE=0
DOH_CLOUDFLARE=1
@@ -923,8 +923,16 @@ _sed_i() {
fi
}
+if [ "$(echo abc | egrep -o b 2>/dev/null)" = "b" ]; then
+ __USE_EGREP=1
+else
+ __USE_EGREP=""
+fi
+
_egrep_o() {
- if ! egrep -o "$1" 2>/dev/null; then
+ if [ "$__USE_EGREP" ]; then
+ egrep -o -- "$1" 2>/dev/null
+ else
sed -n 's/.*\('"$1"'\).*/\1/p'
fi
}
@@ -1422,6 +1430,9 @@ _toPkcs() {
else
${ACME_OPENSSL_BIN:-openssl} pkcs12 -export -out "$_cpfx" -inkey "$_ckey" -in "$_ccert" -certfile "$_cca"
fi
+ if [ "$?" == "0" ]; then
+ _savedomainconf "Le_PFXPassword" "$pfxPassword"
+ fi
}
@@ -1553,7 +1564,7 @@ createDomainKey() {
createCSR() {
_info "Creating csr"
if [ -z "$1" ]; then
- _usage "Usage: $PROJECT_ENTRY --create-csr --domain [--domain ...]"
+ _usage "Usage: $PROJECT_ENTRY --create-csr --domain [--domain ...] [--ecc]"
return
fi
@@ -1787,6 +1798,10 @@ _date2time() {
if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
return
fi
+ #Omnios
+ if da="$(echo "$1" | tr -d "Z" | tr "T" ' ')" perl -MTime::Piece -e 'print Time::Piece->strptime($ENV{da}, "%Y-%m-%d %H:%M:%S")->epoch, "\n";' 2>/dev/null; then
+ return
+ fi
_err "Can not parse _date2time $1"
return 1
}
@@ -2101,12 +2116,18 @@ _head_n() {
}
_tail_n() {
- if ! tail -n "$1" 2>/dev/null; then
+ if _is_solaris; then
#fix for solaris
tail -"$1"
+ else
+ tail -n "$1"
fi
}
+_tail_c() {
+ tail -c "$1" 2>/dev/null || tail -"$1"c
+}
+
# url payload needbase64 keyfile
_send_signed_request() {
url=$1
@@ -2116,6 +2137,7 @@ _send_signed_request() {
if [ -z "$keyfile" ]; then
keyfile="$ACCOUNT_KEY_PATH"
fi
+ _debug "=======Begin Send Signed Request======="
_debug url "$url"
_debug payload "$payload"
@@ -2277,7 +2299,7 @@ _setopt() {
if [ ! -f "$__conf" ]; then
touch "$__conf"
fi
- if [ -n "$(tail -c 1 <"$__conf")" ]; then
+ if [ -n "$(_tail_c 1 <"$__conf")" ]; then
echo >>"$__conf"
fi
@@ -2377,13 +2399,18 @@ _migratedomainconf() {
_old_key="$1"
_new_key="$2"
_b64encode="$3"
- _value=$(_readdomainconf "$_old_key")
- if [ -z "$_value" ]; then
- return 1 # oldkey is not found
- fi
- _savedomainconf "$_new_key" "$_value" "$_b64encode"
+ _old_value=$(_readdomainconf "$_old_key")
_cleardomainconf "$_old_key"
- _debug "Domain config $_old_key has been migrated to $_new_key"
+ if [ -z "$_old_value" ]; then
+ return 1 # migrated failed: old value is empty
+ fi
+ _new_value=$(_readdomainconf "$_new_key")
+ if [ -n "$_new_value" ]; then
+ _debug "Domain config new key exists, old key $_old_key='$_old_value' has been removed."
+ return 1 # migrated failed: old value replaced by new value
+ fi
+ _savedomainconf "$_new_key" "$_old_value" "$_b64encode"
+ _debug "Domain config $_old_key has been migrated to $_new_key."
}
#_migratedeployconf oldkey newkey base64encode
@@ -2480,10 +2507,10 @@ _startserver() {
_debug Le_Listen_V6 "$Le_Listen_V6"
_NC="socat"
- if [ "$Le_Listen_V4" ]; then
- _NC="$_NC -4"
- elif [ "$Le_Listen_V6" ]; then
+ if [ "$Le_Listen_V6" ]; then
_NC="$_NC -6"
+ else
+ _NC="$_NC -4"
fi
if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
@@ -2500,22 +2527,34 @@ _startserver() {
_content_len="$(printf "%s" "$content" | wc -c)"
_debug _content_len "$_content_len"
_debug "_NC" "$_NC $SOCAT_OPTIONS"
+ export _SOCAT_ERR="$(_mktemp)"
$_NC $SOCAT_OPTIONS SYSTEM:"sleep 1; \
echo 'HTTP/1.0 200 OK'; \
echo 'Content-Length\: $_content_len'; \
echo ''; \
-printf '%s' '$content';" &
+printf '%s' '$content';" 2>"$_SOCAT_ERR" &
serverproc="$!"
+ if [ -f "$_SOCAT_ERR" ]; then
+ if grep "Permission denied" "$_SOCAT_ERR" >/dev/null; then
+ _err "socat: $(cat $_SOCAT_ERR)"
+ _err "Can not listen for user: $(whoami)"
+ _err "Maybe try with root again?"
+ rm -f "$_SOCAT_ERR"
+ return 1
+ fi
+ fi
}
_stopserver() {
pid="$1"
_debug "pid" "$pid"
if [ -z "$pid" ]; then
+ rm -f "$_SOCAT_ERR"
return
fi
kill $pid
+ rm -f "$_SOCAT_ERR"
}
@@ -2884,6 +2923,7 @@ _initpath() {
fi
fi
_debug DOMAIN_PATH "$DOMAIN_PATH"
+ export DOMAIN_PATH
fi
if [ -z "$DOMAIN_BACKUP_PATH" ]; then
@@ -2935,22 +2975,6 @@ _initpath() {
}
-_exec() {
- if [ -z "$_EXEC_TEMP_ERR" ]; then
- _EXEC_TEMP_ERR="$(_mktemp)"
- fi
-
- if [ "$_EXEC_TEMP_ERR" ]; then
- eval "$@ 2>>$_EXEC_TEMP_ERR"
- else
- eval "$@"
- fi
-}
-
-_exec_err() {
- [ "$_EXEC_TEMP_ERR" ] && _err "$(cat "$_EXEC_TEMP_ERR")" && echo "" >"$_EXEC_TEMP_ERR"
-}
-
_apachePath() {
_APACHECTL="apachectl"
if ! _exists apachectl; then
@@ -2963,8 +2987,7 @@ _apachePath() {
fi
fi
- if ! _exec $_APACHECTL -V >/dev/null; then
- _exec_err
+ if ! $_APACHECTL -V >/dev/null; then
return 1
fi
@@ -3016,8 +3039,7 @@ _restoreApache() {
cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" >"$httpdconf"
_debug "Restored: $httpdconf."
- if ! _exec $_APACHECTL -t; then
- _exec_err
+ if ! $_APACHECTL -t; then
_err "Sorry, restore apache config error, please contact me."
return 1
fi
@@ -3035,8 +3057,7 @@ _setApache() {
#test the conf first
_info "Checking if there is an error in the apache config file before starting."
- if ! _exec "$_APACHECTL" -t >/dev/null; then
- _exec_err
+ if ! $_APACHECTL -t >/dev/null; then
_err "The apache config file has error, please fix it first, then try again."
_err "Don't worry, there is nothing changed to your system."
return 1
@@ -3097,8 +3118,7 @@ Allow from all
chmod 755 "$ACME_DIR"
fi
- if ! _exec "$_APACHECTL" graceful; then
- _exec_err
+ if ! $_APACHECTL graceful; then
_err "$_APACHECTL graceful error, please contact me."
_restoreApache
return 1
@@ -3129,7 +3149,7 @@ _setNginx() {
_err "nginx command is not found."
return 1
fi
- NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
+ NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "\-\-conf-path=[^ ]* " | tr -d " ")"
_debug NGINX_CONF "$NGINX_CONF"
NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
_debug NGINX_CONF "$NGINX_CONF"
@@ -3183,8 +3203,8 @@ _setNginx() {
return 1
fi
_info "Check the nginx conf before setting up."
- if ! _exec "nginx -t" >/dev/null; then
- _exec_err
+ if ! nginx -t >/dev/null 2>&1; then
+ _err "It seems that nginx conf is not correct, cannot continue."
return 1
fi
@@ -3211,16 +3231,14 @@ location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
fi
_debug3 "Modified config:$(cat $FOUND_REAL_NGINX_CONF)"
_info "nginx conf is done, let's check it again."
- if ! _exec "nginx -t" >/dev/null; then
- _exec_err
+ if ! nginx -t >/dev/null 2>&1; then
_err "It seems that nginx conf was broken, let's restore."
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
return 1
fi
_info "Reload nginx"
- if ! _exec "nginx -s reload" >/dev/null; then
- _exec_err
+ if ! nginx -s reload >/dev/null 2>&1; then
_err "It seems that nginx reload error, let's restore."
cat "$_backup_conf" >"$FOUND_REAL_NGINX_CONF"
return 1
@@ -3345,8 +3363,7 @@ _restoreNginx() {
done
_info "Reload nginx"
- if ! _exec "nginx -s reload" >/dev/null; then
- _exec_err
+ if ! nginx -s reload >/dev/null; then
_err "It seems that nginx reload error, please report bug."
return 1
fi
@@ -3759,7 +3776,7 @@ _regAccount() {
eab_sign_t="$eab_protected64.$eab_payload64"
_debug3 eab_sign_t "$eab_sign_t"
- key_hex="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 multi | _hex_dump | tr -d ' ')"
+ key_hex="$(_durl_replace_base64 "$_eab_hmac_key" | _dbase64 | _hex_dump | tr -d ' ')"
_debug3 key_hex "$key_hex"
eab_signature=$(printf "%s" "$eab_sign_t" | _hmac sha256 $key_hex | _base64 | _url_replace)
@@ -4519,7 +4536,7 @@ issue() {
vlist="$Le_Vlist"
_cleardomainconf "Le_Vlist"
- _info "Getting domain auth token for each domain"
+ _debug "Getting domain auth token for each domain"
sep='#'
dvsep=','
if [ -z "$vlist" ]; then
@@ -4575,12 +4592,22 @@ issue() {
if [ "$_notAfter" ]; then
_newOrderObj="$_newOrderObj,\"notAfter\": \"$_notAfter\""
fi
+ _debug "STEP 1, Ordering a Certificate"
if ! _send_signed_request "$ACME_NEW_ORDER" "$_newOrderObj}"; then
_err "Create new order error."
_clearup
_on_issue_err "$_post_hook"
return 1
fi
+ if _contains "$response" "invalid"; then
+ if echo "$response" | _normalizeJson | grep '"status":"invalid"' >/dev/null 2>&1; then
+ _err "Create new order with invalid status."
+ _err "$response"
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
+ fi
Le_LinkOrder="$(echo "$responseHeaders" | grep -i '^Location.*$' | _tail_n 1 | tr -d "\r\n " | cut -d ":" -f 2-)"
_debug Le_LinkOrder "$Le_LinkOrder"
@@ -4605,6 +4632,7 @@ issue() {
return 1
fi
+ _debug "STEP 2, Get the authorizations of each domain"
#domain and authz map
_authorizations_map=""
for _authz_url in $(echo "$_authorizations_seg" | tr ',' ' '); do
@@ -4613,6 +4641,7 @@ issue() {
_err "get to authz error."
_err "_authorizations_seg" "$_authorizations_seg"
_err "_authz_url" "$_authz_url"
+ _err "$response"
_clearup
_on_issue_err "$_post_hook"
return 1
@@ -4620,14 +4649,23 @@ issue() {
response="$(echo "$response" | _normalizeJson)"
_debug2 response "$response"
+ if echo "$response" | grep '"status":"invalid"' >/dev/null 2>&1; then
+ _err "get authz objec with invalid status, please try again later."
+ _err "_authorizations_seg" "$_authorizations_seg"
+ _err "$response"
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
+ fi
_d="$(echo "$response" | _egrep_o '"value" *: *"[^"]*"' | cut -d : -f 2- | tr -d ' "')"
if _contains "$response" "\"wildcard\" *: *true"; then
_d="*.$_d"
fi
_debug2 _d "$_d"
- _authorizations_map="$_d,$response
+ _authorizations_map="$_d,$response#$_authz_url
$_authorizations_map"
done
+
_debug2 _authorizations_map "$_authorizations_map"
_index=0
@@ -4679,33 +4717,32 @@ $_authorizations_map"
_on_issue_err "$_post_hook"
return 1
fi
-
+ _authz_url="$(echo "$_candidates" | sed "s/$_idn_d,//" | _egrep_o "#.*" | sed "s/^#//")"
+ _debug _authz_url "$_authz_url"
if [ -z "$thumbprint" ]; then
thumbprint="$(__calc_account_thumbprint)"
fi
+ keyauthorization=""
+
+ if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
+ _debug "$d is already valid."
+ keyauthorization="$STATE_VERIFIED"
+ _debug keyauthorization "$keyauthorization"
+ fi
+
entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
_debug entry "$entry"
- keyauthorization=""
- if [ -z "$entry" ]; then
- if ! _startswith "$d" '*.'; then
- _debug "Not a wildcard domain, lets check whether the validation is already valid."
- if echo "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
- _debug "$d is already valid."
- keyauthorization="$STATE_VERIFIED"
- _debug keyauthorization "$keyauthorization"
- fi
- fi
- if [ -z "$keyauthorization" ]; then
- _err "Error, can not get domain token entry $d for $vtype"
- _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
- if [ "$_supported_vtypes" ]; then
- _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype"
- fi
- _clearup
- _on_issue_err "$_post_hook"
- return 1
+
+ if [ -z "$keyauthorization" -a -z "$entry" ]; then
+ _err "Error, can not get domain token entry $d for $vtype"
+ _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
+ if [ "$_supported_vtypes" ]; then
+ _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype"
fi
+ _clearup
+ _on_issue_err "$_post_hook"
+ return 1
fi
if [ -z "$keyauthorization" ]; then
@@ -4731,15 +4768,9 @@ $_authorizations_map"
fi
keyauthorization="$token.$thumbprint"
_debug keyauthorization "$keyauthorization"
-
- if printf "%s" "$response" | grep '"status":"valid"' >/dev/null 2>&1; then
- _debug "$d is already verified."
- keyauthorization="$STATE_VERIFIED"
- _debug keyauthorization "$keyauthorization"
- fi
fi
- dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
+ dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot$sep$_authz_url"
_debug dvlist "$dvlist"
vlist="$vlist$dvlist$dvsep"
@@ -4756,6 +4787,7 @@ $_authorizations_map"
keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
+ _authz_url=$(echo "$ventry" | cut -d "$sep" -f 6)
_debug d "$d"
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_debug "$d is already verified, skip $vtype."
@@ -4881,7 +4913,7 @@ $_authorizations_map"
uri=$(echo "$ventry" | cut -d "$sep" -f 3)
vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
_currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
-
+ _authz_url=$(echo "$ventry" | cut -d "$sep" -f 6)
if [ "$keyauthorization" = "$STATE_VERIFIED" ]; then
_info "$d is already verified, skip $vtype."
continue
@@ -4891,6 +4923,7 @@ $_authorizations_map"
_debug "d" "$d"
_debug "keyauthorization" "$keyauthorization"
_debug "uri" "$uri"
+ _debug "_authz_url" "$_authz_url"
removelevel=""
token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
@@ -4960,18 +4993,6 @@ $_authorizations_map"
if ! chmod a+r "$wellknown_path/$token"; then
_debug "chmod failed, but we just continue."
fi
- if [ ! "$usingApache" ]; then
- if webroot_owner=$(_stat "$_currentRoot"); then
- _debug "Changing owner/group of .well-known to $webroot_owner"
- if ! _exec "chown -R \"$webroot_owner\" \"$_currentRoot/.well-known\""; then
- _debug "$(cat "$_EXEC_TEMP_ERR")"
- _exec_err >/dev/null 2>&1
- fi
- else
- _debug "not changing owner/group of webroot"
- fi
- fi
-
fi
elif [ "$vtype" = "$VTYPE_ALPN" ]; then
acmevalidationv1="$(printf "%s" "$keyauthorization" | _digest "sha256" "hex")"
@@ -5010,6 +5031,7 @@ $_authorizations_map"
MAX_RETRY_TIMES=30
fi
+ _debug "Lets check the status of the authz"
while true; do
waittimes=$(_math "$waittimes" + 1)
if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ]; then
@@ -5033,9 +5055,9 @@ $_authorizations_map"
errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
_debug2 errordetail "$errordetail"
if [ "$errordetail" ]; then
- _err "$d:Verify error:$errordetail"
+ _err "Invalid status, $d:Verify error detail:$errordetail"
else
- _err "$d:Verify error:$error"
+ _err "Invalid status, $d:Verify error:$error"
fi
if [ "$DEBUG" ]; then
if [ "$vtype" = "$VTYPE_HTTP" ]; then
@@ -5057,12 +5079,12 @@ $_authorizations_map"
break
fi
- if [ "$status" = "pending" ]; then
+ if _contains "$status" "pending"; then
_info "Pending, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
- elif [ "$status" = "processing" ]; then
+ elif _contains "$status" "processing"; then
_info "Processing, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
else
- _err "$d:Verify error:$response"
+ _err "Unknown status: $status, $d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
_on_issue_err "$_post_hook" "$vlist"
@@ -5072,10 +5094,10 @@ $_authorizations_map"
_sleep 2
_debug "checking"
- _send_signed_request "$uri"
+ _send_signed_request "$_authz_url"
if [ "$?" != "0" ]; then
- _err "$d:Verify error:$response"
+ _err "Invalid code, $d:Verify error:$response"
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
_on_issue_err "$_post_hook" "$vlist"
@@ -5319,6 +5341,12 @@ $_authorizations_map"
_savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
_savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
+ #convert to pkcs12
+ if [ "$Le_PFXPassword" ]; then
+ _toPkcs "$CERT_PFX_PATH" "$CERT_KEY_PATH" "$CERT_PATH" "$CA_CERT_PATH" "$Le_PFXPassword"
+ fi
+ export CERT_PFX_PATH
+
if [ "$_real_cert$_real_key$_real_ca$_reload_cmd$_real_fullchain" ]; then
_savedomainconf "Le_RealCertPath" "$_real_cert"
_savedomainconf "Le_RealCACertPath" "$_real_ca"
@@ -6011,6 +6039,7 @@ installcronjob() {
fi
_t=$(_time)
random_minute=$(_math $_t % 60)
+ random_hour=$(_math $_t / 60 % 24)
if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
_CRONTAB="fcrontab"
@@ -6035,16 +6064,14 @@ installcronjob() {
_info "Installing cron job"
if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
if _exists uname && uname -a | grep SunOS >/dev/null; then
- $_CRONTAB -l | {
- cat
- echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
- } | $_CRONTAB --
+ _CRONTAB_STDIN="$_CRONTAB --"
else
- $_CRONTAB -l | {
- cat
- echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
- } | $_CRONTAB -
+ _CRONTAB_STDIN="$_CRONTAB -"
fi
+ $_CRONTAB -l | {
+ cat
+ echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
+ } | $_CRONTAB_STDIN
fi
if [ "$?" != "0" ]; then
_err "Install cron job failed. You need to manually renew your certs."
@@ -6917,7 +6944,7 @@ Parameters:
-f, --force Force install, force cert renewal or override sudo restrictions.
--staging, --test Use staging server, for testing.
- --debug [0|1|2|3] Output debug info. Defaults to 1 if argument is omitted.
+ --debug [0|1|2|3] Output debug info. Defaults to $DEBUG_LEVEL_DEFAULT if argument is omitted.
--output-insecure Output all the sensitive messages.
By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
-w, --webroot Specifies the web root folder for web root mode.
@@ -6935,7 +6962,7 @@ Parameters:
-k, --keylength Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
-ak, --accountkeylength Specifies the account key length: 2048, 3072, 4096
--log [file] Specifies the log file. Defaults to \"$DEFAULT_LOG_FILE\" if argument is omitted.
- --log-level <1|2> Specifies the log level, default is 1.
+ --log-level <1|2> Specifies the log level, default is $DEFAULT_LOG_LEVEL.
--syslog <0|3|6|7> Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
--eab-kid Key Identifier for External Account Binding.
--eab-hmac-key HMAC key for External Account Binding.
@@ -6945,7 +6972,7 @@ Parameters:
These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert:
- --cert-file Path to copy the cert file to after issue/renew..
+ --cert-file Path to copy the cert file to after issue/renew.
--key-file Path to copy the key file to after issue/renew.
--ca-file Path to copy the intermediate cert file to after issue/renew.
--fullchain-file Path to copy the fullchain cert file to after issue/renew.
@@ -6975,7 +7002,8 @@ Parameters:
--no-profile Only valid for '--install' command, which means: do not install aliases to user profile.
--no-color Do not output color text.
--force-color Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails.
- --ecc Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--to-pkcs12' and '--create-csr'
+ --ecc Specifies use of the ECC cert. Only valid for '--install-cert', '--renew', '--remove ', '--revoke',
+ '--deploy', '--to-pkcs8', '--to-pkcs12' and '--create-csr'.
--csr Specifies the input csr.
--pre-hook Command to be run before obtaining any certificates.
--post-hook Command to be run after attempting to obtain/renew certificates. Runs regardless of whether obtain/renew succeeded or failed.
diff --git a/deploy/docker.sh b/deploy/docker.sh
index 3aa1b2cd..c9815d5b 100755
--- a/deploy/docker.sh
+++ b/deploy/docker.sh
@@ -273,16 +273,27 @@ _check_curl_version() {
_minor="$(_getfield "$_cversion" 2 '.')"
_debug2 "_minor" "$_minor"
- if [ "$_major$_minor" -lt "740" ]; then
+ if [ "$_major" -ge "8" ]; then
+ #ok
+ return 0
+ fi
+ if [ "$_major" = "7" ]; then
+ if [ "$_minor" -lt "40" ]; then
+ _err "curl v$_cversion doesn't support unit socket"
+ _err "Please upgrade to curl 7.40 or later."
+ return 1
+ fi
+ if [ "$_minor" -lt "50" ]; then
+ _debug "Use short host name"
+ export _CURL_NO_HOST=1
+ else
+ export _CURL_NO_HOST=
+ fi
+ return 0
+ else
_err "curl v$_cversion doesn't support unit socket"
_err "Please upgrade to curl 7.40 or later."
return 1
fi
- if [ "$_major$_minor" -lt "750" ]; then
- _debug "Use short host name"
- export _CURL_NO_HOST=1
- else
- export _CURL_NO_HOST=
- fi
- return 0
+
}
diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh
index c255059d..c8491d92 100644
--- a/deploy/haproxy.sh
+++ b/deploy/haproxy.sh
@@ -36,6 +36,19 @@
# Note: This functionality requires HAProxy was compiled against
# a version of OpenSSL that supports this.
#
+# export DEPLOY_HAPROXY_HOT_UPDATE="yes"
+# export DEPLOY_HAPROXY_STATS_SOCKET="UNIX:/run/haproxy/admin.sock"
+#
+# OPTIONAL: Deploy the certificate over the HAProxy stats socket without
+# needing to reload HAProxy. Default is "no".
+#
+# Require the socat binary. DEPLOY_HAPROXY_STATS_SOCKET variable uses the socat
+# address format.
+#
+# export DEPLOY_HAPROXY_MASTER_CLI="UNIX:/run/haproxy-master.sock"
+#
+# OPTIONAL: To use the master CLI with DEPLOY_HAPROXY_HOT_UPDATE="yes" instead
+# of a stats socket, use this variable.
######## Public functions #####################
@@ -46,6 +59,7 @@ haproxy_deploy() {
_ccert="$3"
_cca="$4"
_cfullchain="$5"
+ _cmdpfx=""
# Some defaults
DEPLOY_HAPROXY_PEM_PATH_DEFAULT="/etc/haproxy"
@@ -53,6 +67,8 @@ haproxy_deploy() {
DEPLOY_HAPROXY_BUNDLE_DEFAULT="no"
DEPLOY_HAPROXY_ISSUER_DEFAULT="no"
DEPLOY_HAPROXY_RELOAD_DEFAULT="true"
+ DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT="no"
+ DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT="UNIX:/run/haproxy/admin.sock"
_debug _cdomain "${_cdomain}"
_debug _ckey "${_ckey}"
@@ -86,6 +102,11 @@ haproxy_deploy() {
_savedomainconf Le_Deploy_haproxy_pem_name "${Le_Deploy_haproxy_pem_name}"
elif [ -z "${Le_Deploy_haproxy_pem_name}" ]; then
Le_Deploy_haproxy_pem_name="${DEPLOY_HAPROXY_PEM_NAME_DEFAULT}"
+ # We better not have '*' as the first character
+ if [ "${Le_Deploy_haproxy_pem_name%%"${Le_Deploy_haproxy_pem_name#?}"}" = '*' ]; then
+ # removes the first characters and add a _ instead
+ Le_Deploy_haproxy_pem_name="_${Le_Deploy_haproxy_pem_name#?}"
+ fi
fi
# BUNDLE is optional. If not provided then assume "${DEPLOY_HAPROXY_BUNDLE_DEFAULT}"
@@ -118,6 +139,36 @@ haproxy_deploy() {
Le_Deploy_haproxy_reload="${DEPLOY_HAPROXY_RELOAD_DEFAULT}"
fi
+ # HOT_UPDATE is optional. If not provided then assume "${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}"
+ _getdeployconf DEPLOY_HAPROXY_HOT_UPDATE
+ _debug2 DEPLOY_HAPROXY_HOT_UPDATE "${DEPLOY_HAPROXY_HOT_UPDATE}"
+ if [ -n "${DEPLOY_HAPROXY_HOT_UPDATE}" ]; then
+ Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE}"
+ _savedomainconf Le_Deploy_haproxy_hot_update "${Le_Deploy_haproxy_hot_update}"
+ elif [ -z "${Le_Deploy_haproxy_hot_update}" ]; then
+ Le_Deploy_haproxy_hot_update="${DEPLOY_HAPROXY_HOT_UPDATE_DEFAULT}"
+ fi
+
+ # STATS_SOCKET is optional. If not provided then assume "${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}"
+ _getdeployconf DEPLOY_HAPROXY_STATS_SOCKET
+ _debug2 DEPLOY_HAPROXY_STATS_SOCKET "${DEPLOY_HAPROXY_STATS_SOCKET}"
+ if [ -n "${DEPLOY_HAPROXY_STATS_SOCKET}" ]; then
+ Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET}"
+ _savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}"
+ elif [ -z "${Le_Deploy_haproxy_stats_socket}" ]; then
+ Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_STATS_SOCKET_DEFAULT}"
+ fi
+
+ # MASTER_CLI is optional. No defaults are used. When the master CLI is used,
+ # all commands are sent with a prefix.
+ _getdeployconf DEPLOY_HAPROXY_MASTER_CLI
+ _debug2 DEPLOY_HAPROXY_MASTER_CLI "${DEPLOY_HAPROXY_MASTER_CLI}"
+ if [ -n "${DEPLOY_HAPROXY_MASTER_CLI}" ]; then
+ Le_Deploy_haproxy_stats_socket="${DEPLOY_HAPROXY_MASTER_CLI}"
+ _savedomainconf Le_Deploy_haproxy_stats_socket "${Le_Deploy_haproxy_stats_socket}"
+ _cmdpfx="@1 " # command prefix used for master CLI only.
+ fi
+
# Set the suffix depending if we are creating a bundle or not
if [ "${Le_Deploy_haproxy_bundle}" = "yes" ]; then
_info "Bundle creation requested"
@@ -142,12 +193,13 @@ haproxy_deploy() {
_issuer="${_pem}.issuer"
_ocsp="${_pem}.ocsp"
_reload="${Le_Deploy_haproxy_reload}"
+ _statssock="${Le_Deploy_haproxy_stats_socket}"
_info "Deploying PEM file"
# Create a temporary PEM file
_temppem="$(_mktemp)"
_debug _temppem "${_temppem}"
- cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
+ cat "${_ccert}" "${_cca}" "${_ckey}" | grep . >"${_temppem}"
_ret="$?"
# Check that we could create the temporary file
@@ -265,15 +317,86 @@ haproxy_deploy() {
fi
fi
- # Reload HAProxy
- _debug _reload "${_reload}"
- eval "${_reload}"
- _ret=$?
- if [ "${_ret}" != "0" ]; then
- _err "Error code ${_ret} during reload"
- return ${_ret}
+ if [ "${Le_Deploy_haproxy_hot_update}" = "yes" ]; then
+ # set the socket name for messages
+ if [ -n "${_cmdpfx}" ]; then
+ _socketname="master CLI"
+ else
+ _socketname="stats socket"
+ fi
+
+ # Update certificate over HAProxy stats socket or master CLI.
+ if _exists socat; then
+ # look for the certificate on the stats socket, to chose between updating or creating one
+ _socat_cert_cmd="echo '${_cmdpfx}show ssl cert' | socat '${_statssock}' - | grep -q '^${_pem}$'"
+ _debug _socat_cert_cmd "${_socat_cert_cmd}"
+ eval "${_socat_cert_cmd}"
+ _ret=$?
+ if [ "${_ret}" != "0" ]; then
+ _newcert="1"
+ _info "Creating new certificate '${_pem}' over HAProxy ${_socketname}."
+ # certificate wasn't found, it's a new one. We should check if the crt-list exists and creates/inserts the certificate.
+ _socat_crtlist_show_cmd="echo '${_cmdpfx}show ssl crt-list' | socat '${_statssock}' - | grep -q '^${Le_Deploy_haproxy_pem_path}$'"
+ _debug _socat_crtlist_show_cmd "${_socat_crtlist_show_cmd}"
+ eval "${_socat_crtlist_show_cmd}"
+ _ret=$?
+ if [ "${_ret}" != "0" ]; then
+ _err "Couldn't find '${Le_Deploy_haproxy_pem_path}' in haproxy 'show ssl crt-list'"
+ return "${_ret}"
+ fi
+ # create a new certificate
+ _socat_new_cmd="echo '${_cmdpfx}new ssl cert ${_pem}' | socat '${_statssock}' - | grep -q 'New empty'"
+ _debug _socat_new_cmd "${_socat_new_cmd}"
+ eval "${_socat_new_cmd}"
+ _ret=$?
+ if [ "${_ret}" != "0" ]; then
+ _err "Couldn't create '${_pem}' in haproxy"
+ return "${_ret}"
+ fi
+ else
+ _info "Update existing certificate '${_pem}' over HAProxy ${_socketname}."
+ fi
+ _socat_cert_set_cmd="echo -e '${_cmdpfx}set ssl cert ${_pem} <<\n$(cat "${_pem}")\n' | socat '${_statssock}' - | grep -q 'Transaction created'"
+ _debug _socat_cert_set_cmd "${_socat_cert_set_cmd}"
+ eval "${_socat_cert_set_cmd}"
+ _ret=$?
+ if [ "${_ret}" != "0" ]; then
+ _err "Can't update '${_pem}' in haproxy"
+ return "${_ret}"
+ fi
+ _socat_cert_commit_cmd="echo '${_cmdpfx}commit ssl cert ${_pem}' | socat '${_statssock}' - | grep -q '^Success!$'"
+ _debug _socat_cert_commit_cmd "${_socat_cert_commit_cmd}"
+ eval "${_socat_cert_commit_cmd}"
+ _ret=$?
+ if [ "${_ret}" != "0" ]; then
+ _err "Can't commit '${_pem}' in haproxy"
+ return ${_ret}
+ fi
+ if [ "${_newcert}" = "1" ]; then
+ # if this is a new certificate, it needs to be inserted into the crt-list`
+ _socat_cert_add_cmd="echo '${_cmdpfx}add ssl crt-list ${Le_Deploy_haproxy_pem_path} ${_pem}' | socat '${_statssock}' - | grep -q 'Success!'"
+ _debug _socat_cert_add_cmd "${_socat_cert_add_cmd}"
+ eval "${_socat_cert_add_cmd}"
+ _ret=$?
+ if [ "${_ret}" != "0" ]; then
+ _err "Can't update '${_pem}' in haproxy"
+ return "${_ret}"
+ fi
+ fi
+ else
+ _err "'socat' is not available, couldn't update over ${_socketname}"
+ fi
else
- _info "Reload successful"
+ # Reload HAProxy
+ _debug _reload "${_reload}"
+ eval "${_reload}"
+ _ret=$?
+ if [ "${_ret}" != "0" ]; then
+ _err "Error code ${_ret} during reload"
+ return ${_ret}
+ else
+ _info "Reload successful"
+ fi
fi
return 0
diff --git a/deploy/panos.sh b/deploy/panos.sh
index ef622ded..0dc1b2f0 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -7,11 +7,18 @@
#
# Firewall admin with superuser and IP address is required.
#
-# export PANOS_USER="" # required
-# export PANOS_PASS="" # required
-# export PANOS_HOST="" # required
+# REQURED:
+# export PANOS_HOST=""
+# export PANOS_USER="" #User *MUST* have Commit and Import Permissions in XML API for Admin Role
+# export PANOS_PASS=""
+#
+# OPTIONAL
+# export PANOS_TEMPLATE="" #Template Name of panorama managed devices
+#
+# The script will automatically generate a new API key if
+# no key is found, or if a saved key has expired or is invalid.
-# This function is to parse the XML
+# This function is to parse the XML response from the firewall
parse_response() {
type=$2
if [ "$type" = 'keygen' ]; then
@@ -23,25 +30,46 @@ parse_response() {
message="PAN-OS Key could not be set."
fi
else
- status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
- message=$(echo "$1" | sed 's/^.*\(.*\)<\/result.*/\1/g')
+ status=$(echo "$1" | tr -d '\n' | sed 's/^.*"\([a-z]*\)".*/\1/g')
+ message=$(echo "$1" | tr -d '\n' | sed 's/.*\(\|\|\)\([^<]*\).*/\2/g')
+ _debug "Firewall message: $message"
+ if [ "$type" = 'keytest' ] && [ "$status" != "success" ]; then
+ _debug "**** API Key has EXPIRED or is INVALID ****"
+ unset _panos_key
+ fi
fi
return 0
}
+#This function is used to deploy to the firewall
deployer() {
content=""
- type=$1 # Types are keygen, cert, key, commit
- _debug "**** Deploying $type *****"
+ type=$1 # Types are keytest, keygen, cert, key, commit
panos_url="https://$_panos_host/api/"
+
+ #Test API Key by performing a lookup
+ if [ "$type" = 'keytest' ]; then
+ _debug "**** Testing saved API Key ****"
+ _H1="Content-Type: application/x-www-form-urlencoded"
+ # Get Version Info to test key
+ content="type=version&key=$_panos_key"
+ ## Exclude all scopes for the empty commit
+ #_exclude_scope="excludeexcludeexclude"
+ #content="type=commit&action=partial&key=$_panos_key&cmd=$_exclude_scopeacmekeytest"
+ fi
+
+ # Generate API Key
if [ "$type" = 'keygen' ]; then
+ _debug "**** Generating new API Key ****"
_H1="Content-Type: application/x-www-form-urlencoded"
content="type=keygen&user=$_panos_user&password=$_panos_pass"
# content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
fi
+ # Deploy Cert or Key
if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
- #Generate DEIM
+ _debug "**** Deploying $type ****"
+ #Generate DELIM
delim="-----MultipartDelimiter$(date "+%s%N")"
nl="\015\012"
#Set Header
@@ -53,6 +81,9 @@ deployer() {
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cfullchain")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cfullchain")"
+ if [ "$_panos_template" ]; then
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
+ fi
fi
if [ "$type" = 'key' ]; then
panos_url="${panos_url}?type=import"
@@ -61,7 +92,10 @@ deployer() {
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cdomain.key")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
+ if [ "$_panos_template" ]; then
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
+ fi
fi
#Close multipart
content="$content${nl}--$delim--${nl}${nl}"
@@ -69,16 +103,25 @@ deployer() {
content=$(printf %b "$content")
fi
+ # Commit changes
if [ "$type" = 'commit' ]; then
+ _debug "**** Committing changes ****"
export _H1="Content-Type: application/x-www-form-urlencoded"
- cmd=$(printf "%s" "<$_panos_user>$_panos_user>" | _url_encode)
- content="type=commit&key=$_panos_key&cmd=$cmd"
+ #Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
+ if [ "$FORCE" ]; then
+ _debug "Force switch detected. Committing ALL changes to the firewall."
+ cmd=$(printf "%s" "$_panos_user" | _url_encode)
+ else
+ _exclude_scope="excludeexclude"
+ cmd=$(printf "%s" "$_exclude_scope$_panos_user" | _url_encode)
+ fi
+ content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
fi
+
response=$(_post "$content" "$panos_url" "" "POST")
parse_response "$response" "$type"
# Saving response to variables
response_status=$status
- #DEBUG
_debug response_status "$response_status"
if [ "$response_status" = "success" ]; then
_debug "Successfully deployed $type"
@@ -92,43 +135,95 @@ deployer() {
# This is the main function that will call the other functions to deploy everything.
panos_deploy() {
- _cdomain="$1"
+ _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
_ckey="$2"
_cfullchain="$5"
- # PANOS ENV VAR check
- if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
- _debug "No ENV variables found lets check for saved variables"
- _getdeployconf PANOS_USER
- _getdeployconf PANOS_PASS
- _getdeployconf PANOS_HOST
- _panos_user=$PANOS_USER
- _panos_pass=$PANOS_PASS
- _panos_host=$PANOS_HOST
- if [ -z "$_panos_user" ] && [ -z "$_panos_pass" ] && [ -z "$_panos_host" ]; then
- _err "No host, user and pass found.. If this is the first time deploying please set PANOS_HOST, PANOS_USER and PANOS_PASS in environment variables. Delete them after you have succesfully deployed certs."
- return 1
- else
- _debug "Using saved env variables."
- fi
+
+ # VALID FILE CHECK
+ if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
+ _err "Unable to find a valid key and/or cert. If this is an ECDSA/ECC cert, use the --ecc flag when deploying."
+ return 1
+ fi
+
+ # PANOS_HOST
+ if [ "$PANOS_HOST" ]; then
+ _debug "Detected ENV variable PANOS_HOST. Saving to file."
+ _savedeployconf PANOS_HOST "$PANOS_HOST" 1
else
- _debug "Detected ENV variables to be saved to the deploy conf."
- # Encrypt and save user
+ _debug "Attempting to load variable PANOS_HOST from file."
+ _getdeployconf PANOS_HOST
+ fi
+
+ # PANOS USER
+ if [ "$PANOS_USER" ]; then
+ _debug "Detected ENV variable PANOS_USER. Saving to file."
_savedeployconf PANOS_USER "$PANOS_USER" 1
+ else
+ _debug "Attempting to load variable PANOS_USER from file."
+ _getdeployconf PANOS_USER
+ fi
+
+ # PANOS_PASS
+ if [ "$PANOS_PASS" ]; then
+ _debug "Detected ENV variable PANOS_PASS. Saving to file."
_savedeployconf PANOS_PASS "$PANOS_PASS" 1
- _savedeployconf PANOS_HOST "$PANOS_HOST" 1
- _panos_user="$PANOS_USER"
- _panos_pass="$PANOS_PASS"
- _panos_host="$PANOS_HOST"
+ else
+ _debug "Attempting to load variable PANOS_PASS from file."
+ _getdeployconf PANOS_PASS
+ fi
+
+ # PANOS_KEY
+ _getdeployconf PANOS_KEY
+ if [ "$PANOS_KEY" ]; then
+ _debug "Detected saved key."
+ _panos_key=$PANOS_KEY
+ else
+ _debug "No key detected"
+ unset _panos_key
fi
- _debug "Let's use username and pass to generate token."
- if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
- _err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
+
+ # PANOS_TEMPLATE
+ if [ "$PANOS_TEMPLATE" ]; then
+ _debug "Detected ENV variable PANOS_TEMPLATE. Saving to file."
+ _savedeployconf PANOS_TEMPLATE "$PANOS_TEMPLATE" 1
+ else
+ _debug "Attempting to load variable PANOS_TEMPLATE from file."
+ _getdeployconf PANOS_TEMPLATE
+ fi
+
+ #Store variables
+ _panos_host=$PANOS_HOST
+ _panos_user=$PANOS_USER
+ _panos_pass=$PANOS_PASS
+ _panos_template=$PANOS_TEMPLATE
+
+ #Test API Key if found. If the key is invalid, the variable _panos_key will be unset.
+ if [ "$_panos_host" ] && [ "$_panos_key" ]; then
+ _debug "**** Testing API KEY ****"
+ deployer keytest
+ fi
+
+ # Check for valid variables
+ if [ -z "$_panos_host" ]; then
+ _err "No host found. If this is your first time deploying, please set PANOS_HOST in ENV variables. You can delete it after you have successfully deployed the certs."
+ return 1
+ elif [ -z "$_panos_user" ]; then
+ _err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed the certs."
+ return 1
+ elif [ -z "$_panos_pass" ]; then
+ _err "No password found. If this is your first time deploying, please set PANOS_PASS in ENV variables. You can delete it after you have successfully deployed the certs."
return 1
else
- _debug "Getting PANOS KEY"
- deployer keygen
+ # Generate a new API key if no valid API key is found
+ if [ -z "$_panos_key" ]; then
+ _debug "**** Generating new PANOS API KEY ****"
+ deployer keygen
+ _savedeployconf PANOS_KEY "$_panos_key" 1
+ fi
+
+ # Confirm that a valid key was generated
if [ -z "$_panos_key" ]; then
- _err "Missing apikey."
+ _err "Unable to generate an API key. The user and pass may be invalid or not authorized to generate a new key. Please check the PANOS_USER and PANOS_PASS credentials and try again"
return 1
else
deployer cert
diff --git a/deploy/proxmoxve.sh b/deploy/proxmoxve.sh
index 216a8fc7..f9de590c 100644
--- a/deploy/proxmoxve.sh
+++ b/deploy/proxmoxve.sh
@@ -99,11 +99,11 @@ proxmoxve_deploy() {
_proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
_savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
fi
- _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY _proxmoxve_api_token_key
+ _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY "$_proxmoxve_api_token_key"
# PVE API Token header value. Used in "Authorization: PVEAPIToken".
_proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
- _debug2 "Auth Header" _proxmoxve_header_api_token
+ _debug2 "Auth Header" "$_proxmoxve_header_api_token"
# Ugly. I hate putting heredocs inside functions because heredocs don't
# account for whitespace correctly but it _does_ work and is several times
@@ -124,8 +124,8 @@ HEREDOC
)
_debug2 Payload "$_json_payload"
- # Push certificates to server.
- export _HTTPS_INSECURE=1
+ _info "Push certificates to server"
+ export HTTPS_INSECURE=1
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
_post "$_json_payload" "$_target_url" "" POST "application/json"
diff --git a/deploy/routeros.sh b/deploy/routeros.sh
index c4c9470d..d1779b8d 100644
--- a/deploy/routeros.sh
+++ b/deploy/routeros.sh
@@ -137,7 +137,7 @@ routeros_deploy() {
return $_err_code
fi
- DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=$ROUTER_OS_USERNAME \
+ DEPLOY_SCRIPT_CMD="/system script add name=\"LECertDeploy-$_cdomain\" owner=$ROUTER_OS_USERNAME \
comment=\"generated by routeros deploy script in acme.sh\" \
source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
\n/certificate remove [ find name=$_cdomain.cer_1 ];\
@@ -158,11 +158,11 @@ source=\"/certificate remove [ find name=$_cdomain.cer_0 ];\
return $_err_code
fi
- if ! _ssh_remote_cmd "/system script run \"LE Cert Deploy - $_cdomain\""; then
+ if ! _ssh_remote_cmd "/system script run \"LECertDeploy-$_cdomain\""; then
return $_err_code
fi
- if ! _ssh_remote_cmd "/system script remove \"LE Cert Deploy - $_cdomain\""; then
+ if ! _ssh_remote_cmd "/system script remove \"LECertDeploy-$_cdomain\""; then
return $_err_code
fi
diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index c31a5df0..342fb32e 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -1,34 +1,53 @@
-#!/usr/bin/env sh
-
-# Here is a script to deploy cert to Synology DSM
-#
-# It requires following environment variables:
-#
-# SYNO_Username - Synology Username to login (must be an administrator)
-# SYNO_Password - Synology Password to login
-# SYNO_Certificate - Certificate description to target for replacement
-#
-# The following environmental variables may be set if you don't like their
-# default values:
-#
-# SYNO_Scheme - defaults to http
-# SYNO_Hostname - defaults to localhost
-# SYNO_Port - defaults to 5000
-# SYNO_DID - device ID to skip OTP - defaults to empty
-# SYNO_TOTP_SECRET - TOTP secret to generate OTP - defaults to empty
-#
-# Dependencies:
-# -------------
-# - jq and curl
-# - oathtool (When using 2 Factor Authentication and SYNO_TOTP_SECRET is set)
-#
-#returns 0 means success, otherwise error.
+#!/bin/bash
-######## Public functions #####################
+################################################################################
+# ACME.sh 3rd party deploy plugin for Synology DSM
+################################################################################
+# Authors: Brian Hartvigsen (creator), https://github.com/tresni
+# Martin Arndt (contributor), https://troublezone.net/
+# Updated: 2023-07-03
+# Issues: https://github.com/acmesh-official/acme.sh/issues/2727
+################################################################################
+# Usage (shown values are the examples):
+# 1. Set required environment variables:
+# - use automatically created temp admin user to authenticate
+# export SYNO_USE_TEMP_ADMIN=1
+# - or provide your own admin user credential to authenticate
+# 1. export SYNO_USERNAME="adminUser"
+# 2. export SYNO_PASSWORD="adminPassword"
+# 2. Set optional environment variables
+# - common optional variables
+# - export SYNO_SCHEME="http" - defaults to "http"
+# - export SYNO_HOSTNAME="localhost" - defaults to "localhost"
+# - export SYNO_PORT="5000" - defaults to "5000"
+# - export SYNO_CREATE=1 - to allow creating the cert if it doesn't exist
+# - export SYNO_CERTIFICATE="" - to replace a specific cert by its
+# description
+# - temp admin optional variables
+# - export SYNO_LOCAL_HOSTNAME=1 - if set to 1, force to treat hostname is
+# targeting current local machine (since
+# this method only locally supported)
+# - exsiting admin 2FA-OTP optional variables
+# - export SYNO_OTP_CODE="XXXXXX" - if set, script won't require to
+# interactive input the OTP code
+# - export SYNO_DEVICE_NAME="CertRenewal" - if set, script won't require to
+# interactive input the device name
+# - export SYNO_DEVICE_ID="" - (deprecated, auth with OTP code instead)
+# required for omitting 2FA-OTP
+# 3. Run command:
+# acme.sh --deploy --deploy-hook synology_dsm -d example.com
+################################################################################
+# Dependencies:
+# - curl
+# - synouser & synogroup (When available and SYNO_USE_TEMP_ADMIN is set)
+################################################################################
+# Return value:
+# 0 means success, otherwise error.
+################################################################################
+########## Public functions ####################################################
#domain keyfile certfile cafile fullchain
synology_dsm_deploy() {
-
_cdomain="$1"
_ckey="$2"
_ccert="$3"
@@ -36,148 +55,380 @@ synology_dsm_deploy() {
_debug _cdomain "$_cdomain"
- # Get Username and Password, but don't save until we successfully authenticate
- _getdeployconf SYNO_Username
- _getdeployconf SYNO_Password
- _getdeployconf SYNO_Create
- _getdeployconf SYNO_DID
- _getdeployconf SYNO_TOTP_SECRET
- if [ -z "${SYNO_Username:-}" ] || [ -z "${SYNO_Password:-}" ]; then
- _err "SYNO_Username & SYNO_Password must be set"
- return 1
- fi
- _debug2 SYNO_Username "$SYNO_Username"
- _secure_debug2 SYNO_Password "$SYNO_Password"
+ # Get username and password, but don't save until we authenticated successfully
+ _migratedeployconf SYNO_Username SYNO_USERNAME
+ _migratedeployconf SYNO_Password SYNO_PASSWORD
+ _migratedeployconf SYNO_Device_ID SYNO_DEVICE_ID
+ _migratedeployconf SYNO_Device_Name SYNO_DEVICE_NAME
+ _getdeployconf SYNO_USERNAME
+ _getdeployconf SYNO_PASSWORD
+ _getdeployconf SYNO_DEVICE_ID
+ _getdeployconf SYNO_DEVICE_NAME
+
+ # Prepare to use temp admin if SYNO_USE_TEMP_ADMIN is set
+ _debug2 SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
+ _getdeployconf SYNO_USE_TEMP_ADMIN
+ _check2cleardeployconfexp SYNO_USE_TEMP_ADMIN
+ _debug2 SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
+
+ if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
+ if ! _exists synouser || ! _exists synogroup; then
+ _err "Tools are missing for creating temp admin user, please set SYNO_USERNAME and SYNO_PASSWORD instead."
+ return 1
+ fi
- # Optional scheme, hostname, and port for Synology DSM
- _getdeployconf SYNO_Scheme
- _getdeployconf SYNO_Hostname
- _getdeployconf SYNO_Port
+ [ -n "$SYNO_USERNAME" ] || _savedeployconf SYNO_USERNAME ""
+ [ -n "$SYNO_PASSWORD" ] || _savedeployconf SYNO_PASSWORD ""
+
+ _debug "Setting temp admin user credential..."
+ SYNO_USERNAME=sc-acmesh-tmp
+ SYNO_PASSWORD=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
+ # Set 2FA-OTP settings to empty consider they won't be needed.
+ SYNO_DEVICE_ID=
+ SYNO_DEVICE_NAME=
+ SYNO_OTP_CODE=
+ else
+ _debug2 SYNO_USERNAME "$SYNO_USERNAME"
+ _secure_debug2 SYNO_PASSWORD "$SYNO_PASSWORD"
+ _debug2 SYNO_DEVICE_NAME "$SYNO_DEVICE_NAME"
+ _secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
+ fi
- # default vaules for scheme, hostname, and port
- # defaulting to localhost and http because it's localhost...
- [ -n "${SYNO_Scheme}" ] || SYNO_Scheme="http"
- [ -n "${SYNO_Hostname}" ] || SYNO_Hostname="localhost"
- [ -n "${SYNO_Port}" ] || SYNO_Port="5000"
+ if [ -z "$SYNO_USERNAME" ] || [ -z "$SYNO_PASSWORD" ]; then
+ _err "You must set either SYNO_USE_TEMP_ADMIN, or set both SYNO_USERNAME and SYNO_PASSWORD."
+ return 1
+ fi
- _savedeployconf SYNO_Scheme "$SYNO_Scheme"
- _savedeployconf SYNO_Hostname "$SYNO_Hostname"
- _savedeployconf SYNO_Port "$SYNO_Port"
+ # Optional scheme, hostname and port for Synology DSM
+ _migratedeployconf SYNO_Scheme SYNO_SCHEME
+ _migratedeployconf SYNO_Hostname SYNO_HOSTNAME
+ _migratedeployconf SYNO_Port SYNO_PORT
+ _getdeployconf SYNO_SCHEME
+ _getdeployconf SYNO_HOSTNAME
+ _getdeployconf SYNO_PORT
- _debug2 SYNO_Scheme "$SYNO_Scheme"
- _debug2 SYNO_Hostname "$SYNO_Hostname"
- _debug2 SYNO_Port "$SYNO_Port"
+ # Default values for scheme, hostname and port
+ # Defaulting to localhost and http, because it's localhost…
+ [ -n "$SYNO_SCHEME" ] || SYNO_SCHEME="http"
+ [ -n "$SYNO_HOSTNAME" ] || SYNO_HOSTNAME="localhost"
+ [ -n "$SYNO_PORT" ] || SYNO_PORT="5000"
+ _savedeployconf SYNO_SCHEME "$SYNO_SCHEME"
+ _savedeployconf SYNO_HOSTNAME "$SYNO_HOSTNAME"
+ _savedeployconf SYNO_PORT "$SYNO_PORT"
+ _debug2 SYNO_SCHEME "$SYNO_SCHEME"
+ _debug2 SYNO_HOSTNAME "$SYNO_HOSTNAME"
+ _debug2 SYNO_PORT "$SYNO_PORT"
- # Get the certificate description, but don't save it until we verfiy it's real
- _getdeployconf SYNO_Certificate
- _debug SYNO_Certificate "${SYNO_Certificate:-}"
+ # Get the certificate description, but don't save it until we verify it's real
+ _migratedeployconf SYNO_Certificate SYNO_CERTIFICATE "base64"
+ _getdeployconf SYNO_CERTIFICATE
+ _check2cleardeployconfexp SYNO_CERTIFICATE
+ _debug SYNO_CERTIFICATE "${SYNO_CERTIFICATE:-}"
# shellcheck disable=SC1003 # We are not trying to escape a single quote
- if printf "%s" "$SYNO_Certificate" | grep '\\'; then
+ if printf "%s" "$SYNO_CERTIFICATE" | grep '\\'; then
_err "Do not use a backslash (\) in your certificate description"
return 1
fi
- _base_url="$SYNO_Scheme://$SYNO_Hostname:$SYNO_Port"
+ _debug "Getting API version..."
+ _base_url="$SYNO_SCHEME://$SYNO_HOSTNAME:$SYNO_PORT"
_debug _base_url "$_base_url"
-
- _debug "Getting API version"
response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
+ api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([^"]*\)".*/\1/p')
api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
_debug3 response "$response"
+ _debug3 api_path "$api_path"
_debug3 api_version "$api_version"
- # Login, get the token from JSON and session id from cookie
- _info "Logging into $SYNO_Hostname:$SYNO_Port"
- encoded_username="$(printf "%s" "$SYNO_Username" | _url_encode)"
- encoded_password="$(printf "%s" "$SYNO_Password" | _url_encode)"
+ # Login, get the session ID and SynoToken from JSON
+ _info "Logging into $SYNO_HOSTNAME:$SYNO_PORT..."
+ encoded_username="$(printf "%s" "$SYNO_USERNAME" | _url_encode)"
+ encoded_password="$(printf "%s" "$SYNO_PASSWORD" | _url_encode)"
+
+ # ## START ## - DEPRECATED, for backward compatibility
+ _getdeployconf SYNO_TOTP_SECRET
- otp_code=""
if [ -n "$SYNO_TOTP_SECRET" ]; then
- if _exists oathtool; then
- otp_code="$(oathtool --base32 --totp "${SYNO_TOTP_SECRET}" 2>/dev/null)"
- else
+ _info "WARNING: Usage of SYNO_TOTP_SECRET is deprecated!"
+ _info " See synology_dsm.sh script or ACME.sh Wiki page for details:"
+ _info " https://github.com/acmesh-official/acme.sh/wiki/Synology-NAS-Guide"
+ if ! _exists oathtool; then
_err "oathtool could not be found, install oathtool to use SYNO_TOTP_SECRET"
return 1
fi
- fi
+ DEPRECATED_otp_code="$(oathtool --base32 --totp "$SYNO_TOTP_SECRET" 2>/dev/null)"
- if [ -n "$SYNO_DID" ]; then
- _H1="Cookie: did=$SYNO_DID"
- export _H1
- _debug3 H1 "${_H1}"
+ if [ -z "$SYNO_DEVICE_ID" ]; then
+ _getdeployconf SYNO_DID
+ [ -n "$SYNO_DID" ] || SYNO_DEVICE_ID="$SYNO_DID"
+ fi
+ if [ -n "$SYNO_DEVICE_ID" ]; then
+ _H1="Cookie: did=$SYNO_DEVICE_ID"
+ export _H1
+ _debug3 H1 "${_H1}"
+ fi
+
+ response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$DEPRECATED_otp_code&device_name=certrenewal&device_id=$SYNO_DEVICE_ID" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
+ _debug3 response "$response"
+ # ## END ## - DEPRECATED, for backward compatibility
+ # If SYNO_DEVICE_ID or SYNO_OTP_CODE is set, we treat current account enabled 2FA-OTP.
+ # Notice that if SYNO_USE_TEMP_ADMIN=1, both variables will be unset
+ else
+ if [ -n "$SYNO_DEVICE_ID" ] || [ -n "$SYNO_OTP_CODE" ]; then
+ response='{"error":{"code":403}}'
+ # Assume the current account disabled 2FA-OTP, try to log in right away.
+ else
+ if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
+ _getdeployconf SYNO_LOCAL_HOSTNAME
+ _debug SYNO_LOCAL_HOSTNAME "${SYNO_LOCAL_HOSTNAME:-}"
+ if [ "$SYNO_LOCAL_HOSTNAME" != "1" ] && [ "$SYNO_LOCAL_HOSTNAME" == "$SYNO_HOSTNAME" ]; then
+ if [ "$SYNO_HOSTNAME" != "localhost" ] && [ "$SYNO_HOSTNAME" != "127.0.0.1" ]; then
+ _err "SYNO_USE_TEMP_ADMIN=1 Only support locally deployment, if you are sure that hostname $SYNO_HOSTNAME is targeting to your **current local machine**, execute 'export SYNO_LOCAL_HOSTNAME=1' then rerun."
+ return 1
+ fi
+ fi
+ _debug "Creating temp admin user in Synology DSM..."
+ if synogroup --help | grep -q '\-\-memberadd '; then
+ _temp_admin_create "$SYNO_USERNAME" "$SYNO_PASSWORD"
+ synogroup --memberadd administrators "$SYNO_USERNAME" >/dev/null
+ elif synogroup --help | grep -q '\-\-member '; then
+ # For supporting DSM 6.x which only has `--member` parameter.
+ cur_admins=$(synogroup --get administrators | awk -F '[][]' '/Group Members/,0{if(NF>1)printf "%s ", $2}')
+ if [ -n "$cur_admins" ]; then
+ _temp_admin_create "$SYNO_USERNAME" "$SYNO_PASSWORD"
+ _secure_debug3 admin_users "$cur_admins$SYNO_USERNAME"
+ # shellcheck disable=SC2086
+ synogroup --member administrators $cur_admins $SYNO_USERNAME >/dev/null
+ else
+ _err "Tool synogroup may be broken, please set SYNO_USERNAME and SYNO_PASSWORD instead."
+ return 1
+ fi
+ else
+ _err "Unsupported synogroup tool detected, please set SYNO_USERNAME and SYNO_PASSWORD instead."
+ return 1
+ fi
+ # havig a workaround to temporary disable enforce 2FA-OTP
+ otp_enforce_option=$(synogetkeyvalue /etc/synoinfo.conf otp_enforce_option)
+ if [ -n "$otp_enforce_option" ] && [ "${otp_enforce_option:-"none"}" != "none" ]; then
+ synosetkeyvalue /etc/synoinfo.conf otp_enforce_option none
+ _info "Temporary disabled enforce 2FA-OTP to complete authentication."
+ _info "previous_otp_enforce_option" "$otp_enforce_option"
+ else
+ otp_enforce_option=""
+ fi
+ fi
+ response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes")
+ if [ -n "$SYNO_USE_TEMP_ADMIN" ] && [ -n "$otp_enforce_option" ]; then
+ synosetkeyvalue /etc/synoinfo.conf otp_enforce_option "$otp_enforce_option"
+ _info "Restored previous enforce 2FA-OTP option."
+ fi
+ _debug3 response "$response"
+ fi
fi
- response=$(_post "method=login&account=$encoded_username&passwd=$encoded_password&api=SYNO.API.Auth&version=$api_version&enable_syno_token=yes&otp_code=$otp_code&device_name=certrenewal&device_id=$SYNO_DID" "$_base_url/webapi/auth.cgi?enable_syno_token=yes")
- token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
- _debug3 response "$response"
- _debug token "$token"
+ error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
+ _debug2 error_code "$error_code"
+ # Account has 2FA-OTP enabled, since error 403 reported.
+ # https://global.download.synology.com/download/Document/Software/DeveloperGuide/Firmware/DSM/All/enu/Synology_DiskStation_Administration_CLI_Guide.pdf
+ if [ "$error_code" == "403" ]; then
+ if [ -z "$SYNO_DEVICE_NAME" ]; then
+ printf "Enter device name or leave empty for default (CertRenewal): "
+ read -r SYNO_DEVICE_NAME
+ [ -n "$SYNO_DEVICE_NAME" ] || SYNO_DEVICE_NAME="CertRenewal"
+ fi
+
+ if [ -n "$SYNO_DEVICE_ID" ]; then
+ # Omit OTP code with SYNO_DEVICE_ID.
+ response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_DEVICE_NAME&device_id=$SYNO_DEVICE_ID")
+ _secure_debug3 response "$response"
+ else
+ # Require the OTP code if still unset.
+ if [ -z "$SYNO_OTP_CODE" ]; then
+ printf "Enter OTP code for user '%s': " "$SYNO_USERNAME"
+ read -r SYNO_OTP_CODE
+ fi
+ _secure_debug SYNO_OTP_CODE "${SYNO_OTP_CODE:-}"
+
+ if [ -z "$SYNO_OTP_CODE" ]; then
+ response='{"error":{"code":404}}'
+ else
+ response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_DEVICE_NAME&otp_code=$SYNO_OTP_CODE")
+ _secure_debug3 response "$response"
- if [ -z "$token" ]; then
- _err "Unable to authenticate to $SYNO_Hostname:$SYNO_Port using $SYNO_Scheme."
- _err "Check your username and password."
- _err "If two-factor authentication is enabled for the user, set SYNO_TOTP_SECRET."
+ id_property='device_id'
+ [ "${api_version}" -gt '6' ] || id_property='did'
+ SYNO_DEVICE_ID=$(echo "$response" | grep "$id_property" | sed -n 's/.*"'$id_property'" *: *"\([^"]*\).*/\1/p')
+ _secure_debug2 SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
+ fi
+ fi
+ error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
+ _debug2 error_code "$error_code"
+ fi
+
+ if [ -n "$error_code" ]; then
+ if [ "$error_code" == "403" ] && [ -n "$SYNO_DEVICE_ID" ]; then
+ _cleardeployconf SYNO_DEVICE_ID
+ _err "Failed to authenticate with SYNO_DEVICE_ID (may expired or invalid), please try again in a new terminal window."
+ elif [ "$error_code" == "404" ]; then
+ _err "Failed to authenticate with provided 2FA-OTP code, please try again in a new terminal window."
+ elif [ "$error_code" == "406" ]; then
+ if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
+ _err "SYNO_USE_TEMP_ADMIN=1 is not supported if enforce auth with 2FA-OTP is enabled."
+ else
+ _err "Enforce auth with 2FA-OTP enabled, please configure the user to enable 2FA-OTP to continue."
+ fi
+ elif [ "$error_code" == "400" ] || [ "$error_code" == "401" ] || [ "$error_code" == "408" ] || [ "$error_code" == "409" ] || [ "$error_code" == "410" ]; then
+ _err "Failed to authenticate with a non-existent or disabled account, or the account password is incorrect or has expired."
+ else
+ _err "Failed to authenticate with error: $error_code."
+ fi
+ _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
return 1
fi
+
sid=$(echo "$response" | grep "sid" | sed -n 's/.*"sid" *: *"\([^"]*\).*/\1/p')
+ token=$(echo "$response" | grep "synotoken" | sed -n 's/.*"synotoken" *: *"\([^"]*\).*/\1/p')
+ _debug "Session ID" "$sid"
+ _debug SynoToken "$token"
+ if [ -z "$sid" ] || [ -z "$token" ]; then
+ # Still can't get necessary info even got no errors, may Synology have API updated?
+ _err "Unable to authenticate to $_base_url, you may report the full log to the community."
+ _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
+ return 1
+ fi
_H1="X-SYNO-TOKEN: $token"
export _H1
_debug2 H1 "${_H1}"
- # Now that we know the username and password are good, save them
- _savedeployconf SYNO_Username "$SYNO_Username"
- _savedeployconf SYNO_Password "$SYNO_Password"
- _savedeployconf SYNO_DID "$SYNO_DID"
- _savedeployconf SYNO_TOTP_SECRET "$SYNO_TOTP_SECRET"
+ # Now that we know the username and password are good, save them if not in temp admin mode.
+ if [ -n "$SYNO_USE_TEMP_ADMIN" ]; then
+ _cleardeployconf SYNO_USERNAME
+ _cleardeployconf SYNO_PASSWORD
+ _cleardeployconf SYNO_DEVICE_ID
+ _cleardeployconf SYNO_DEVICE_NAME
+ _savedeployconf SYNO_USE_TEMP_ADMIN "$SYNO_USE_TEMP_ADMIN"
+ _savedeployconf SYNO_LOCAL_HOSTNAME "$SYNO_HOSTNAME"
+ else
+ _savedeployconf SYNO_USERNAME "$SYNO_USERNAME"
+ _savedeployconf SYNO_PASSWORD "$SYNO_PASSWORD"
+ _savedeployconf SYNO_DEVICE_ID "$SYNO_DEVICE_ID"
+ _savedeployconf SYNO_DEVICE_NAME "$SYNO_DEVICE_NAME"
+ fi
- _info "Getting certificates in Synology DSM"
+ _info "Getting certificates in Synology DSM..."
response=$(_post "api=SYNO.Core.Certificate.CRT&method=list&version=1&_sid=$sid" "$_base_url/webapi/entry.cgi")
_debug3 response "$response"
- escaped_certificate="$(printf "%s" "$SYNO_Certificate" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
+ escaped_certificate="$(printf "%s" "$SYNO_CERTIFICATE" | sed 's/\([].*^$[]\)/\\\1/g;s/"/\\\\"/g')"
_debug escaped_certificate "$escaped_certificate"
id=$(echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\"id\":\"\([^\"]*\).*/\1/p")
_debug2 id "$id"
- if [ -z "$id" ] && [ -z "${SYNO_Create:-}" ]; then
- _err "Unable to find certificate: $SYNO_Certificate and \$SYNO_Create is not set"
+ error_code=$(echo "$response" | grep '"error":' | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
+ _debug2 error_code "$error_code"
+ if [ -n "$error_code" ]; then
+ if [ "$error_code" -eq 105 ]; then
+ _err "Current user is not administrator and does not have sufficient permission for deploying."
+ else
+ _err "Failed to fetch certificate info with error: $error_code, please try again or contact Synology to learn more."
+ fi
+ _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
return 1
fi
- # we've verified this certificate description is a thing, so save it
- _savedeployconf SYNO_Certificate "$SYNO_Certificate" "base64"
+ _migratedeployconf SYNO_Create SYNO_CREATE
+ _getdeployconf SYNO_CREATE
+ _debug2 SYNO_CREATE "$SYNO_CREATE"
+
+ if [ -z "$id" ] && [ -z "$SYNO_CREATE" ]; then
+ _err "Unable to find certificate: $SYNO_CERTIFICATE and $SYNO_CREATE is not set."
+ _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
+ return 1
+ fi
- _info "Generate form POST request"
+ # We've verified this certificate description is a thing, so save it
+ _savedeployconf SYNO_CERTIFICATE "$SYNO_CERTIFICATE" "base64"
+
+ _info "Generating form POST request..."
nl="\0015\0012"
delim="--------------------------$(_utc_date | tr -d -- '-: ')"
content="--$delim${nl}Content-Disposition: form-data; name=\"key\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")\0012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"cert\"; filename=\"$(basename "$_ccert")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ccert")\0012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"inter_cert\"; filename=\"$(basename "$_cca")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_cca")\0012"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"id\"${nl}${nl}$id"
- content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_Certificate}"
+ content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"desc\"${nl}${nl}${SYNO_CERTIFICATE}"
if echo "$response" | sed -n "s/.*\"desc\":\"$escaped_certificate\",\([^{]*\).*/\1/p" | grep -- 'is_default":true' >/dev/null; then
- _debug2 default "this is the default certificate"
+ _debug2 default "This is the default certificate"
content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"as_default\"${nl}${nl}true"
else
- _debug2 default "this is NOT the default certificate"
+ _debug2 default "This is NOT the default certificate"
fi
content="$content${nl}--$delim--${nl}"
content="$(printf "%b_" "$content")"
content="${content%_}" # protect trailing \n
- _info "Upload certificate to the Synology DSM"
+ _info "Upload certificate to the Synology DSM."
response=$(_post "$content" "$_base_url/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=$token&_sid=$sid" "" "POST" "multipart/form-data; boundary=${delim}")
_debug3 response "$response"
if ! echo "$response" | grep '"error":' >/dev/null; then
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
- _info "http services were restarted"
+ _info "Restart HTTP services succeeded."
else
- _info "http services were NOT restarted"
+ _info "Restart HTTP services failed."
fi
+ _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
+ _logout
return 0
else
- _err "Unable to update certificate, error code $response"
+ _temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
+ _err "Unable to update certificate, got error response: $response."
+ _logout
return 1
fi
}
+
+#################### 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)
+ response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=logout&_sid=$sid")
+ _debug3 response "$response"
+}
+
+_temp_admin_create() {
+ _username="$1"
+ _password="$2"
+ synouser --del "$_username" >/dev/null 2>/dev/null
+ synouser --add "$_username" "$_password" "" 0 "scruelt@hotmail.com" 0 >/dev/null
+}
+
+_temp_admin_cleanup() {
+ _flag=$1
+ _username=$2
+
+ if [ -n "${_flag}" ]; then
+ _debug "Cleanuping temp admin info..."
+ synouser --del "$_username" >/dev/null
+ fi
+}
+
+#_cleardeployconf key
+_cleardeployconf() {
+ _cleardomainconf "SAVED_$1"
+}
+
+# key
+_check2cleardeployconfexp() {
+ _key="$1"
+ _clear_key="CLEAR_$_key"
+ # Clear saved settings if explicitly requested
+ if [ -n "$(eval echo \$"$_clear_key")" ]; then
+ _debug2 "$_key: value cleared from config, exported value will be ignored."
+ _cleardeployconf "$_key"
+ eval "$_key"=
+ export "$_key"=
+ eval SAVED_"$_key"=
+ export SAVED_"$_key"=
+ fi
+}
diff --git a/dnsapi/dns_1984hosting.sh b/dnsapi/dns_1984hosting.sh
index 6accc597..e4ef2e4b 100755
--- a/dnsapi/dns_1984hosting.sh
+++ b/dnsapi/dns_1984hosting.sh
@@ -1,46 +1,46 @@
#!/usr/bin/env sh
-#This file name is "dns_1984hosting.sh"
-#So, here must be a method dns_1984hosting_add()
-#Which will be called by acme.sh to add the txt record to your api system.
-#returns 0 means success, otherwise error.
+# This file name is "dns_1984hosting.sh"
+# So, here must be a method dns_1984hosting_add()
+# Which will be called by acme.sh to add the txt record to your api system.
+# returns 0 means success, otherwise error.
-#Author: Adrian Fedoreanu
-#Report Bugs here: https://github.com/acmesh-official/acme.sh
+# Author: Adrian Fedoreanu
+# Report Bugs here: https://github.com/acmesh-official/acme.sh
# or here... https://github.com/acmesh-official/acme.sh/issues/2851
-#
-######## Public functions #####################
+
+######## Public functions #####################
# Export 1984HOSTING username and password in following variables
#
# One984HOSTING_Username=username
# One984HOSTING_Password=password
#
-# sessionid cookie is saved in ~/.acme.sh/account.conf
-# username/password need to be set only when changed.
+# username/password and csrftoken/sessionid cookies are saved in ~/.acme.sh/account.conf
-#Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Usage: dns_1984hosting_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Add a text record.
dns_1984hosting_add() {
fulldomain=$1
txtvalue=$2
- _info "Add TXT record using 1984Hosting"
+ _info "Add TXT record using 1984Hosting."
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _1984hosting_login; then
- _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
+ _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
return 1
fi
- _debug "First detect the root zone"
+ _debug "First detect the root zone."
if ! _get_root "$fulldomain"; then
- _err "invalid domain" "$fulldomain"
+ _err "Invalid domain '$fulldomain'."
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- _debug "Add TXT record $fulldomain with value '$txtvalue'"
+ _debug "Add TXT record $fulldomain with value '$txtvalue'."
value="$(printf '%s' "$txtvalue" | _url_encode)"
url="https://1984.hosting/domains/entry/"
@@ -53,92 +53,96 @@ dns_1984hosting_add() {
_debug2 postdata "$postdata"
_authpost "$postdata" "$url"
- response="$(echo "$_response" | _normalizeJson)"
- _debug2 response "$response"
-
- if _contains "$response" '"haserrors": true'; then
- _err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post"
+ if _contains "$_response" '"haserrors": true'; then
+ _err "1984Hosting failed to add TXT record for $_sub_domain bad RC from _post."
return 1
- elif _contains "$response" "html>"; then
- _err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file"
+ elif _contains "$_response" "html>"; then
+ _err "1984Hosting failed to add TXT record for $_sub_domain. Check $HTTP_HEADER file."
return 1
- elif _contains "$response" '"auth": false'; then
- _err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie"
+ elif _contains "$_response" '"auth": false'; then
+ _err "1984Hosting failed to add TXT record for $_sub_domain. Invalid or expired cookie."
return 1
fi
- _info "Added acme challenge TXT record for $fulldomain at 1984Hosting"
+ _info "Added acme challenge TXT record for $fulldomain at 1984Hosting."
return 0
}
-#Usage: fulldomain txtvalue
-#Remove the txt record after validation.
+# Usage: fulldomain txtvalue
+# Remove the txt record after validation.
dns_1984hosting_rm() {
fulldomain=$1
txtvalue=$2
- _info "Delete TXT record using 1984Hosting"
+ _info "Delete TXT record using 1984Hosting."
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _1984hosting_login; then
- _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file"
+ _err "1984Hosting login failed for user $One984HOSTING_Username. Check $HTTP_HEADER file."
return 1
fi
- _debug "First detect the root zone"
+ _debug "First detect the root zone."
if ! _get_root "$fulldomain"; then
- _err "invalid domain" "$fulldomain"
+ _err "Invalid domain '$fulldomain'."
return 1
fi
_debug _sub_domain "$_sub_domain"
_debug _domain "$_domain"
- _debug "Delete $fulldomain TXT record"
+ _debug "Delete $fulldomain TXT record."
url="https://1984.hosting/domains"
if ! _get_zone_id "$url" "$_domain"; then
- _err "invalid zone" "$_domain"
+ _err "Invalid zone '$_domain'."
return 1
fi
_htmlget "$url/$_zone_id" "$txtvalue"
- _debug2 _response "$_response"
entry_id="$(echo "$_response" | _egrep_o 'entry_[0-9]+' | sed 's/entry_//')"
_debug2 entry_id "$entry_id"
if [ -z "$entry_id" ]; then
- _err "Error getting TXT entry_id for $1"
+ _err "Error getting TXT entry_id for $1."
return 1
fi
_authpost "entry=$entry_id" "$url/delentry/"
- response="$(echo "$_response" | _normalizeJson)"
- _debug2 response "$response"
-
- if ! _contains "$response" '"ok": true'; then
- _err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post"
+ if ! _contains "$_response" '"ok": true'; then
+ _err "1984Hosting failed to delete TXT record for $entry_id bad RC from _post."
return 1
fi
- _info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting"
+ _info "Deleted acme challenge TXT record for $fulldomain at 1984Hosting."
return 0
}
#################### Private functions below ##################################
-
-# usage: _1984hosting_login username password
-# returns 0 success
_1984hosting_login() {
if ! _check_credentials; then return 1; fi
if _check_cookies; then
- _debug "Already logged in"
+ _debug "Already logged in."
return 0
fi
- _debug "Login to 1984Hosting as user $One984HOSTING_Username"
+ _debug "Login to 1984Hosting as user $One984HOSTING_Username."
username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
- url="https://1984.hosting/accounts/checkuserauth/"
+ url="https://1984.hosting/api/auth/"
+
+ _get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
+ csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
+ sessionid="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'sessionid=[^;]*;' | tr -d ';')"
+
+ if [ -z "$csrftoken" ] || [ -z "$sessionid" ]; then
+ _err "One or more cookies are empty: '$csrftoken', '$sessionid'."
+ return 1
+ fi
+
+ export _H1="Cookie: $csrftoken; $sessionid"
+ export _H2="Referer: https://1984.hosting/accounts/login/"
+ csrf_header=$(echo "$csrftoken" | sed 's/csrftoken=//' | _head_n 1)
+ export _H3="X-CSRFToken: $csrf_header"
response="$(_post "username=$username&password=$password&otpkey=" $url)"
response="$(echo "$response" | _normalizeJson)"
@@ -149,6 +153,8 @@ _1984hosting_login() {
One984HOSTING_CSRFTOKEN_COOKIE="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
export One984HOSTING_SESSIONID_COOKIE
export One984HOSTING_CSRFTOKEN_COOKIE
+ _saveaccountconf_mutable One984HOSTING_Username "$One984HOSTING_Username"
+ _saveaccountconf_mutable One984HOSTING_Password "$One984HOSTING_Password"
_saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
_saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
return 0
@@ -157,9 +163,13 @@ _1984hosting_login() {
}
_check_credentials() {
+ One984HOSTING_Username="${One984HOSTING_Username:-$(_readaccountconf_mutable One984HOSTING_Username)}"
+ One984HOSTING_Password="${One984HOSTING_Password:-$(_readaccountconf_mutable One984HOSTING_Password)}"
if [ -z "$One984HOSTING_Username" ] || [ -z "$One984HOSTING_Password" ]; then
One984HOSTING_Username=""
One984HOSTING_Password=""
+ _clearaccountconf_mutable One984HOSTING_Username
+ _clearaccountconf_mutable One984HOSTING_Password
_err "You haven't specified 1984Hosting username or password yet."
_err "Please export as One984HOSTING_Username / One984HOSTING_Password and try again."
return 1
@@ -171,42 +181,43 @@ _check_cookies() {
One984HOSTING_SESSIONID_COOKIE="${One984HOSTING_SESSIONID_COOKIE:-$(_readaccountconf_mutable One984HOSTING_SESSIONID_COOKIE)}"
One984HOSTING_CSRFTOKEN_COOKIE="${One984HOSTING_CSRFTOKEN_COOKIE:-$(_readaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE)}"
if [ -z "$One984HOSTING_SESSIONID_COOKIE" ] || [ -z "$One984HOSTING_CSRFTOKEN_COOKIE" ]; then
- _debug "No cached cookie(s) found"
+ _debug "No cached cookie(s) found."
return 1
fi
- _authget "https://1984.hosting/accounts/loginstatus/"
- if _contains "$response" '"ok": true'; then
- _debug "Cached cookies still valid"
+ _authget "https://1984.hosting/api/auth/"
+ if _contains "$_response" '"ok": true'; then
+ _debug "Cached cookies still valid."
return 0
fi
- _debug "Cached cookies no longer valid"
+
+ _debug "Cached cookies no longer valid. Clearing cookies."
One984HOSTING_SESSIONID_COOKIE=""
One984HOSTING_CSRFTOKEN_COOKIE=""
- _saveaccountconf_mutable One984HOSTING_SESSIONID_COOKIE "$One984HOSTING_SESSIONID_COOKIE"
- _saveaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE "$One984HOSTING_CSRFTOKEN_COOKIE"
+ _clearaccountconf_mutable One984HOSTING_SESSIONID_COOKIE
+ _clearaccountconf_mutable One984HOSTING_CSRFTOKEN_COOKIE
return 1
}
-#_acme-challenge.www.domain.com
-#returns
-# _sub_domain=_acme-challenge.www
-# _domain=domain.com
+# _acme-challenge.www.domain.com
+# Returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
_get_root() {
domain="$1"
i=1
p=1
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ # not valid
if [ -z "$h" ]; then
- #not valid
return 1
fi
_authget "https://1984.hosting/domains/soacheck/?zone=$h&nameserver=ns0.1984.is."
if _contains "$_response" "serial" && ! _contains "$_response" "null"; then
- _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
_domain="$h"
return 0
fi
@@ -216,46 +227,47 @@ _get_root() {
return 1
}
-#usage: _get_zone_id url domain.com
-#returns zone id for domain.com
+# Usage: _get_zone_id url domain.com
+# Returns zone id for domain.com
_get_zone_id() {
url=$1
domain=$2
_htmlget "$url" "$domain"
- _debug2 _response "$_response"
_zone_id="$(echo "$_response" | _egrep_o 'zone\/[0-9]+' | _head_n 1)"
_debug2 _zone_id "$_zone_id"
if [ -z "$_zone_id" ]; then
- _err "Error getting _zone_id for $2"
+ _err "Error getting _zone_id for $2."
return 1
fi
return 0
}
-# add extra headers to request
+# Add extra headers to request
_authget() {
- export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
+ export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
_response=$(_get "$1" | _normalizeJson)
_debug2 _response "$_response"
}
-# truncate huge HTML response
-# echo: Argument list too long
+# Truncate huge HTML response
+# Echo: Argument list too long
_htmlget() {
- export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
+ export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
_response=$(_get "$1" | grep "$2")
if _contains "$_response" "@$2"; then
_response=$(echo "$_response" | grep -v "[@]" | _head_n 1)
fi
+ _debug2 _response "$_response"
}
-# add extra headers to request
+# Add extra headers to request
_authpost() {
url="https://1984.hosting/domains"
_get_zone_id "$url" "$_domain"
csrf_header="$(echo "$One984HOSTING_CSRFTOKEN_COOKIE" | _egrep_o "=[^=][0-9a-zA-Z]*" | tr -d "=")"
- export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE;$One984HOSTING_SESSIONID_COOKIE"
+ export _H1="Cookie: $One984HOSTING_CSRFTOKEN_COOKIE; $One984HOSTING_SESSIONID_COOKIE"
export _H2="Referer: https://1984.hosting/domains/$_zone_id"
export _H3="X-CSRFToken: $csrf_header"
- _response=$(_post "$1" "$2")
+ _response="$(_post "$1" "$2" | _normalizeJson)"
+ _debug2 _response "$_response"
}
diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh
index c2105672..c69839dc 100755
--- a/dnsapi/dns_ali.sh
+++ b/dnsapi/dns_ali.sh
@@ -117,7 +117,7 @@ _ali_urlencode() {
_ali_nonce() {
#_head_n 1 b.c.d -> c.d -> d)
while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100 | sed 's/\./\\./g')
_debug "Checking domain: $h"
if [ -z "$h" ]; then
_error "invalid domain"
@@ -207,24 +206,40 @@ _use_container_role() {
}
_use_instance_role() {
- _url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
- _debug "_url" "$_url"
- if ! _get "$_url" true 1 | _head_n 1 | grep -Fq 200; then
+ _instance_role_name_url="http://169.254.169.254/latest/meta-data/iam/security-credentials/"
+
+ if _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 401; then
+ _debug "Using IMDSv2"
+ _token_url="http://169.254.169.254/latest/api/token"
+ export _H1="X-aws-ec2-metadata-token-ttl-seconds: 21600"
+ _token="$(_post "" "$_token_url" "" "PUT")"
+ _secure_debug3 "_token" "$_token"
+ if [ -z "$_token" ]; then
+ _debug "Unable to fetch IMDSv2 token from instance metadata"
+ return 1
+ fi
+ export _H1="X-aws-ec2-metadata-token: $_token"
+ fi
+
+ if ! _get "$_instance_role_name_url" true 1 | _head_n 1 | grep -Fq 200; then
_debug "Unable to fetch IAM role from instance metadata"
return 1
fi
- _aws_role=$(_get "$_url" "" 1)
- _debug "_aws_role" "$_aws_role"
- _use_metadata "$_url$_aws_role"
+
+ _instance_role_name=$(_get "$_instance_role_name_url" "" 1)
+ _debug "_instance_role_name" "$_instance_role_name"
+ _use_metadata "$_instance_role_name_url$_instance_role_name" "$_token"
+
}
_use_metadata() {
+ export _H1="X-aws-ec2-metadata-token: $2"
_aws_creds="$(
_get "$1" "" 1 |
_normalizeJson |
tr '{,}' '\n' |
while read -r _line; do
- _key="$(echo "${_line%%:*}" | tr -d '"')"
+ _key="$(echo "${_line%%:*}" | tr -d '\"')"
_value="${_line#*:}"
_debug3 "_key" "$_key"
_secure_debug3 "_value" "$_value"
diff --git a/dnsapi/dns_bookmyname.sh b/dnsapi/dns_bookmyname.sh
new file mode 100644
index 00000000..62548fd0
--- /dev/null
+++ b/dnsapi/dns_bookmyname.sh
@@ -0,0 +1,89 @@
+#!/usr/bin/env sh
+
+#Here is a sample custom api script.
+#This file name is "dns_bookmyname.sh"
+#So, here must be a method dns_bookmyname_add()
+#Which will be called by acme.sh to add the txt record to your api system.
+#returns 0 means success, otherwise error.
+#
+#Author: Neilpang
+#Report Bugs here: https://github.com/acmesh-official/acme.sh
+#
+######## Public functions #####################
+
+# Please Read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
+
+# BookMyName urls:
+# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=add&value="XXXXXXXX"'
+# https://BOOKMYNAME_USERNAME:BOOKMYNAME_PASSWORD@www.bookmyname.com/dyndns/?hostname=_acme-challenge.domain.tld&type=txt&ttl=300&do=remove&value="XXXXXXXX"'
+
+# Output:
+#good: update done, cid 123456, domain id 456789, type txt, ip XXXXXXXX
+#good: remove done 1, cid 123456, domain id 456789, ttl 300, type txt, ip XXXXXXXX
+
+# Be careful, BMN DNS servers can be slow to pick up changes; using dnssleep is thus advised.
+
+# Usage:
+# export BOOKMYNAME_USERNAME="ABCDE-FREE"
+# export BOOKMYNAME_PASSWORD="MyPassword"
+# /usr/local/ssl/acme.sh/acme.sh --dns dns_bookmyname --dnssleep 600 --issue -d domain.tld
+
+#Usage: dns_bookmyname_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_bookmyname_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using bookmyname"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
+ BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
+
+ if [ -z "$BOOKMYNAME_USERNAME" ] || [ -z "$BOOKMYNAME_PASSWORD" ]; then
+ BOOKMYNAME_USERNAME=""
+ BOOKMYNAME_PASSWORD=""
+ _err "You didn't specify BookMyName username and password yet."
+ _err "Please specify them and try again."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable BOOKMYNAME_USERNAME "$BOOKMYNAME_USERNAME"
+ _saveaccountconf_mutable BOOKMYNAME_PASSWORD "$BOOKMYNAME_PASSWORD"
+
+ uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
+ data="?hostname=${fulldomain}&type=TXT&ttl=300&do=add&value=${txtvalue}"
+ result="$(_get "${uri}${data}")"
+ _debug "Result: $result"
+
+ if ! _startswith "$result" 'good: update done, cid '; then
+ _err "Can't add $fulldomain"
+ return 1
+ fi
+
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_bookmyname_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using bookmyname"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ BOOKMYNAME_USERNAME="${BOOKMYNAME_USERNAME:-$(_readaccountconf_mutable BOOKMYNAME_USERNAME)}"
+ BOOKMYNAME_PASSWORD="${BOOKMYNAME_PASSWORD:-$(_readaccountconf_mutable BOOKMYNAME_PASSWORD)}"
+
+ uri="https://${BOOKMYNAME_USERNAME}:${BOOKMYNAME_PASSWORD}@www.bookmyname.com/dyndns/"
+ data="?hostname=${fulldomain}&type=TXT&ttl=300&do=remove&value=${txtvalue}"
+ result="$(_get "${uri}${data}")"
+ _debug "Result: $result"
+
+ if ! _startswith "$result" 'good: remove done 1, cid '; then
+ _info "Can't remove $fulldomain"
+ fi
+
+}
+
+#################### Private functions below ##################################
diff --git a/dnsapi/dns_dnsexit.sh b/dnsapi/dns_dnsexit.sh
new file mode 100644
index 00000000..62d7d757
--- /dev/null
+++ b/dnsapi/dns_dnsexit.sh
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#use dns-01 at DNSExit.com
+
+#Author: Samuel Jimenez
+#Report Bugs here: https://github.com/acmesh-official/acme.sh
+
+#DNSEXIT_API_KEY=ABCDEFGHIJ0123456789abcdefghij
+#DNSEXIT_AUTH_USER=login@email.address
+#DNSEXIT_AUTH_PASS=aStrongPassword
+DNSEXIT_API_URL="https://api.dnsexit.com/dns/"
+DNSEXIT_HOSTS_URL="https://update.dnsexit.com/ipupdate/hosts.jsp"
+
+######## Public functions #####################
+#Usage: dns_dnsexit_add _acme-challenge.*.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_dnsexit_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using DNSExit.com"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ _debug 'Load account auth'
+ if ! get_account_info; then
+ return 1
+ fi
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"add\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":0,\"overwrite\":false}}"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_dnsexit_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using DNSExit.com"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ _debug 'Load account auth'
+ if ! get_account_info; then
+ return 1
+ fi
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ _err "$response"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"delete\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\"}}"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+ while true; do
+ _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$_domain"
+ if [ -z "$_domain" ]; then
+ return 1
+ fi
+
+ _debug login "$DNSEXIT_AUTH_USER"
+ _debug password "$DNSEXIT_AUTH_PASS"
+ _debug domain "$_domain"
+
+ _dnsexit_http "login=$DNSEXIT_AUTH_USER&password=$DNSEXIT_AUTH_PASS&domain=$_domain"
+
+ if _contains "$response" "0=$_domain"; then
+ _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
+ return 0
+ else
+ _debug "Go to next level of $_domain"
+ fi
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_dnsexit_rest() {
+ m=POST
+ ep=""
+ data="$1"
+ _debug _dnsexit_rest "$ep"
+ _debug data "$data"
+
+ api_key_trimmed=$(echo "$DNSEXIT_API_KEY" | tr -d '"')
+
+ export _H1="apikey: $api_key_trimmed"
+ export _H2='Content-Type: application/json'
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$DNSEXIT_API_URL/$ep" "" "$m")"
+ else
+ response="$(_get "$DNSEXIT_API_URL/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "Error $ep"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
+
+_dnsexit_http() {
+ m=GET
+ param="$1"
+ _debug param "$param"
+ _debug get "$DNSEXIT_HOSTS_URL?$param"
+
+ response="$(_get "$DNSEXIT_HOSTS_URL?$param")"
+
+ _debug response "$response"
+
+ if [ "$?" != "0" ]; then
+ _err "Error $param"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
+
+get_account_info() {
+
+ DNSEXIT_API_KEY="${DNSEXIT_API_KEY:-$(_readaccountconf_mutable DNSEXIT_API_KEY)}"
+ if test -z "$DNSEXIT_API_KEY"; then
+ DNSEXIT_API_KEY=''
+ _err 'DNSEXIT_API_KEY was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable DNSEXIT_API_KEY "$DNSEXIT_API_KEY"
+
+ DNSEXIT_AUTH_USER="${DNSEXIT_AUTH_USER:-$(_readaccountconf_mutable DNSEXIT_AUTH_USER)}"
+ if test -z "$DNSEXIT_AUTH_USER"; then
+ DNSEXIT_AUTH_USER=""
+ _err 'DNSEXIT_AUTH_USER was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable DNSEXIT_AUTH_USER "$DNSEXIT_AUTH_USER"
+
+ DNSEXIT_AUTH_PASS="${DNSEXIT_AUTH_PASS:-$(_readaccountconf_mutable DNSEXIT_AUTH_PASS)}"
+ if test -z "$DNSEXIT_AUTH_PASS"; then
+ DNSEXIT_AUTH_PASS=""
+ _err 'DNSEXIT_AUTH_PASS was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable DNSEXIT_AUTH_PASS "$DNSEXIT_AUTH_PASS"
+
+ return 0
+}
diff --git a/dnsapi/dns_do.sh b/dnsapi/dns_do.sh
deleted file mode 100755
index 3850890c..00000000
--- a/dnsapi/dns_do.sh
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/env sh
-
-# DNS API for Domain-Offensive / Resellerinterface / Domainrobot
-
-# Report bugs at https://github.com/seidler2547/acme.sh/issues
-
-# set these environment variables to match your customer ID and password:
-# DO_PID="KD-1234567"
-# DO_PW="cdfkjl3n2"
-
-DO_URL="https://soap.resellerinterface.de/"
-
-######## Public functions #####################
-
-#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_do_add() {
- fulldomain=$1
- txtvalue=$2
- if _dns_do_authenticate; then
- _info "Adding TXT record to ${_domain} as ${fulldomain}"
- _dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300
- if _contains "${response}" '>success<'; then
- return 0
- fi
- _err "Could not create resource record, check logs"
- fi
- return 1
-}
-
-#fulldomain
-dns_do_rm() {
- fulldomain=$1
- if _dns_do_authenticate; then
- if _dns_do_list_rrs; then
- _dns_do_had_error=0
- for _rrid in ${_rr_list}; do
- _info "Deleting resource record $_rrid for $_domain"
- _dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}"
- if ! _contains "${response}" '>success<'; then
- _dns_do_had_error=1
- _err "Could not delete resource record for ${_domain}, id ${_rrid}"
- fi
- done
- return $_dns_do_had_error
- fi
- fi
- return 1
-}
-
-#################### Private functions below ##################################
-_dns_do_authenticate() {
- _info "Authenticating as ${DO_PID}"
- _dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}"
- if _contains "${response}" '>success<'; then
- _get_root "$fulldomain"
- _debug "_domain $_domain"
- return 0
- else
- _err "Authentication failed, are DO_PID and DO_PW set correctly?"
- fi
- return 1
-}
-
-_dns_do_list_rrs() {
- _dns_do_soap getRRList origin "${_domain}"
- if ! _contains "${response}" 'SOAP-ENC:Array'; then
- _err "getRRList origin ${_domain} failed"
- return 1
- fi
- _rr_list="$(echo "${response}" |
- tr -d "\n\r\t" |
- sed -e 's/- /\n/g' |
- grep ">$(_regexcape "$fulldomain")" |
- sed -e 's/<\/item>/\n/g' |
- grep '>id[0-9]{1,16}<' |
- tr -d '><')"
- [ "${_rr_list}" ]
-}
-
-_dns_do_soap() {
- func="$1"
- shift
- # put the parameters to xml
- body=""
- while [ "$1" ]; do
- _k="$1"
- shift
- _v="$1"
- shift
- body="$body<$_k>$_v$_k>"
- done
- body="$body"
- _debug2 "SOAP request ${body}"
-
- # build SOAP XML
- _xml='
-
- '"$body"'
-'
-
- # set SOAP headers
- export _H1="SOAPAction: ${DO_URL}#${func}"
-
- if ! response="$(_post "${_xml}" "${DO_URL}")"; then
- _err "Error <$1>"
- return 1
- fi
- _debug2 "SOAP response $response"
-
- # retrieve cookie header
- _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)"
- export _H2
-
- return 0
-}
-
-_get_root() {
- domain=$1
- i=1
-
- _dns_do_soap getDomainList
- _all_domains="$(echo "${response}" |
- tr -d "\n\r\t " |
- _egrep_o 'domain]+>[^<]+' |
- sed -e 's/^domain<\/key>]*>//g')"
-
- while true; do
- h=$(printf "%s" "$domain" | cut -d . -f $i-100)
- if [ -z "$h" ]; then
- return 1
- fi
-
- if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then
- _domain="$h"
- return 0
- fi
-
- i=$(_math $i + 1)
- done
- _debug "$domain not found"
-
- return 1
-}
-
-_regexcape() {
- echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g'
-}
diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh
index 931da883..6092f45c 100644
--- a/dnsapi/dns_gandi_livedns.sh
+++ b/dnsapi/dns_gandi_livedns.sh
@@ -1,7 +1,8 @@
#!/usr/bin/env sh
# Gandi LiveDNS v5 API
-# https://doc.livedns.gandi.net/
+# https://api.gandi.net/docs/livedns/
+# https://api.gandi.net/docs/authentication/ for token + apikey (deprecated) authentication
# currently under beta
#
# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
@@ -12,20 +13,27 @@
#
######## Public functions #####################
-GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5"
+GANDI_LIVEDNS_API="https://api.gandi.net/v5/livedns"
#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_gandi_livedns_add() {
fulldomain=$1
txtvalue=$2
- if [ -z "$GANDI_LIVEDNS_KEY" ]; then
- _err "No API key specified for Gandi LiveDNS."
- _err "Create your key and export it as GANDI_LIVEDNS_KEY"
+ if [ -z "$GANDI_LIVEDNS_KEY" ] && [ -z "$GANDI_LIVEDNS_TOKEN" ]; then
+ _err "No Token or API key (deprecated) specified for Gandi LiveDNS."
+ _err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively"
return 1
fi
- _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+ # Keep only one secret in configuration
+ if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+ _saveaccountconf GANDI_LIVEDNS_TOKEN "$GANDI_LIVEDNS_TOKEN"
+ _clearaccountconf GANDI_LIVEDNS_KEY
+ elif [ -n "$GANDI_LIVEDNS_KEY" ]; then
+ _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+ _clearaccountconf GANDI_LIVEDNS_TOKEN
+ fi
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -70,7 +78,7 @@ dns_gandi_livedns_rm() {
_gandi_livedns_rest PUT \
"domains/$_domain/records/$_sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_new_rrset_values}" &&
- _contains "$response" '{"message": "DNS Record Created"}' &&
+ _contains "$response" '{"message":"DNS Record Created"}' &&
_info "Removing record $(__green "success")"
}
@@ -126,7 +134,7 @@ _dns_gandi_append_record() {
_debug new_rrset_values "$_rrset_values"
_gandi_livedns_rest PUT "domains/$_domain/records/$sub_domain/TXT" \
"{\"rrset_ttl\": 300, \"rrset_values\": $_rrset_values}" &&
- _contains "$response" '{"message": "DNS Record Created"}' &&
+ _contains "$response" '{"message":"DNS Record Created"}' &&
_info "Adding record $(__green "success")"
}
@@ -136,11 +144,11 @@ _dns_gandi_existing_rrset_values() {
if ! _gandi_livedns_rest GET "domains/$domain/records/$sub_domain"; then
return 1
fi
- if ! _contains "$response" '"rrset_type": "TXT"'; then
+ if ! _contains "$response" '"rrset_type":"TXT"'; then
_debug "Does not have a _acme-challenge TXT record yet."
return 1
fi
- if _contains "$response" '"rrset_values": \[\]'; then
+ if _contains "$response" '"rrset_values":\[\]'; then
_debug "Empty rrset_values for TXT record, no previous TXT record."
return 1
fi
@@ -157,7 +165,12 @@ _gandi_livedns_rest() {
_debug "$ep"
export _H1="Content-Type: application/json"
- export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+
+ if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+ export _H2="Authorization: Bearer $GANDI_LIVEDNS_TOKEN"
+ else
+ export _H2="Authorization: Apikey $GANDI_LIVEDNS_KEY"
+ fi
if [ "$m" = "GET" ]; then
response="$(_get "$GANDI_LIVEDNS_API/$ep")"
diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh
index 2788ad59..dc82c09d 100755
--- a/dnsapi/dns_gcloud.sh
+++ b/dnsapi/dns_gcloud.sh
@@ -42,7 +42,7 @@ dns_gcloud_rm() {
echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
_dns_gcloud_execute_tr || return $?
- _info "$fulldomain record added"
+ _info "$fulldomain record removed"
}
#################### Private functions below ##################################
diff --git a/dnsapi/dns_gcore.sh b/dnsapi/dns_gcore.sh
index d549a650..5f7f037e 100755
--- a/dnsapi/dns_gcore.sh
+++ b/dnsapi/dns_gcore.sh
@@ -4,8 +4,8 @@
#GCORE_Key='773$7b7adaf2a2b32bfb1b83787b4ff32a67eb178e3ada1af733e47b1411f2461f7f4fa7ed7138e2772a46124377bad7384b3bb8d87748f87b3f23db4b8bbe41b2bb'
#
-GCORE_Api="https://api.gcorelabs.com/dns/v2"
-GCORE_Doc="https://apidocs.gcore.com/dns"
+GCORE_Api="https://api.gcore.com/dns/v2"
+GCORE_Doc="https://api.gcore.com/docs/dns"
######## Public functions #####################
diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh
index ba789da9..e483c0e8 100755
--- a/dnsapi/dns_inwx.sh
+++ b/dnsapi/dns_inwx.sh
@@ -194,7 +194,7 @@ _inwx_login() {
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
- INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+ INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
_H1=$INWX_Cookie
export _H1
export INWX_Cookie
diff --git a/dnsapi/dns_kappernet.sh b/dnsapi/dns_kappernet.sh
index 83a7e5f8..914671d6 100644
--- a/dnsapi/dns_kappernet.sh
+++ b/dnsapi/dns_kappernet.sh
@@ -6,8 +6,7 @@
#KAPPERNETDNS_Key="yourKAPPERNETapikey"
#KAPPERNETDNS_Secret="yourKAPPERNETapisecret"
-
-KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
+#KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
###############################################################################
# called with
@@ -19,10 +18,9 @@ dns_kappernet_add() {
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
+ KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
- KAPPERNETDNS_Key=""
- KAPPERNETDNS_Secret=""
_err "Please specify your kapper.net api key and secret."
_err "If you have not received yours - send your mail to"
_err "support@kapper.net to get your key and secret."
@@ -41,12 +39,12 @@ dns_kappernet_add() {
_debug _domain "DOMAIN: $_domain"
_info "Trying to add TXT DNS Record"
- data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
+ data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%22300%22%2C%22prio%22%3A%22%22%7D"
if _kappernet_api GET "action=new&subject=$_domain&data=$data"; then
if _contains "$response" "{\"OK\":true"; then
- _info "Waiting 120 seconds for DNS to spread the new record"
- _sleep 120
+ _info "Waiting 1 second for DNS to spread the new record"
+ _sleep 1
return 0
else
_err "Error creating a TXT DNS Record: $fullhostname TXT $txtvalue"
@@ -66,10 +64,9 @@ dns_kappernet_rm() {
KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
+ KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
- KAPPERNETDNS_Key=""
- KAPPERNETDNS_Secret=""
_err "Please specify your kapper.net api key and secret."
_err "If you have not received yours - send your mail to"
_err "support@kapper.net to get your key and secret."
@@ -81,7 +78,7 @@ dns_kappernet_rm() {
_saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret"
_info "Trying to remove the TXT Record: $fullhostname containing $txtvalue"
- data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
+ data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%22300%22%2C%22prio%22%3A%22%22%7D"
if _kappernet_api GET "action=del&subject=$fullhostname&data=$data"; then
if _contains "$response" "{\"OK\":true"; then
return 0
@@ -141,7 +138,7 @@ _kappernet_api() {
if [ "$method" = "GET" ]; then
response="$(_get "$url")"
else
- _err "Unsupported method"
+ _err "Unsupported method or missing Secret/Key"
return 1
fi
diff --git a/dnsapi/dns_limacity.sh b/dnsapi/dns_limacity.sh
new file mode 100644
index 00000000..649550ae
--- /dev/null
+++ b/dnsapi/dns_limacity.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env sh
+
+# Created by Laraveluser
+#
+# Pass credentials before "acme.sh --issue --dns dns_limacity ..."
+# --
+# export LIMACITY_APIKEY=""
+# --
+#
+# Pleas note: APIKEY must have following roles: dns.admin, domains.reader
+
+######## Public functions #####################
+
+LIMACITY_APIKEY="${LIMACITY_APIKEY:-$(_readaccountconf_mutable LIMACITY_APIKEY)}"
+AUTH=$(printf "%s" "api:$LIMACITY_APIKEY" | _base64 -w 0)
+export _H1="Authorization: Basic $AUTH"
+export _H2="Content-Type: application/json"
+APIBASE=https://www.lima-city.de/usercp
+
+#Usage: dns_limacity_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_limacity_add() {
+ _debug LIMACITY_APIKEY "$LIMACITY_APIKEY"
+ if [ "$LIMACITY_APIKEY" = "" ]; then
+ _err "No Credentials given"
+ return 1
+ fi
+
+ # save the dns server and key to the account conf file.
+ _saveaccountconf_mutable LIMACITY_APIKEY "${LIMACITY_APIKEY}"
+
+ fulldomain=$1
+ txtvalue=$2
+ if ! _lima_get_domain_id "$fulldomain"; then return 1; fi
+
+ msg=$(_post "{\"nameserver_record\":{\"name\":\"${fulldomain}\",\"type\":\"TXT\",\"content\":\"${txtvalue}\",\"ttl\":60}}" "${APIBASE}/domains/${LIMACITY_DOMAINID}/records.json" "" "POST")
+ _debug "$msg"
+
+ if [ "$(echo "$msg" | _egrep_o "\"status\":\"ok\"")" = "" ]; then
+ _err "$msg"
+ return 1
+ fi
+
+ return 0
+}
+
+#Usage: dns_limacity_rm _acme-challenge.www.domain.com
+dns_limacity_rm() {
+
+ fulldomain=$1
+ txtvalue=$2
+ if ! _lima_get_domain_id "$fulldomain"; then return 1; fi
+
+ for recordId in $(_get "${APIBASE}/domains/${LIMACITY_DOMAINID}/records.json" | _egrep_o "{\"id\":[0-9]*[^}]*,\"name\":\"${fulldomain}\"" | _egrep_o "[0-9]*"); do
+ _post "" "${APIBASE}/domains/${LIMACITY_DOMAINID}/records/${recordId}" "" "DELETE"
+ done
+
+ return 0
+}
+
+#################### Private functions below ##################################
+
+_lima_get_domain_id() {
+ domain="$1"
+ _debug "$domain"
+ i=2
+ p=1
+
+ domains=$(_get "${APIBASE}/domains.json")
+ if [ "$(echo "$domains" | _egrep_o "\{.*""domains""")" ]; then
+ response="$(echo "$domains" | 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 "\{.*""unicode_fqdn""[^,]+""$h"".*\}")"
+ if [ "$hostedzone" ]; then
+ LIMACITY_DOMAINID=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$LIMACITY_DOMAINID" ]; 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
+}
diff --git a/dnsapi/dns_loopia.sh b/dnsapi/dns_loopia.sh
index 399c7867..60d072e0 100644
--- a/dnsapi/dns_loopia.sh
+++ b/dnsapi/dns_loopia.sh
@@ -107,7 +107,7 @@ _loopia_load_config() {
fi
if _contains "$LOOPIA_Password" "'" || _contains "$LOOPIA_Password" '"'; then
- _err "Password contains quoute or double quoute and this is not supported by dns_loopia.sh"
+ _err "Password contains a quotation mark or double quotation marks and this is not supported by dns_loopia.sh"
return 1
fi
diff --git a/dnsapi/dns_opnsense.sh b/dnsapi/dns_opnsense.sh
index c2806a1b..d40cbe28 100755
--- a/dnsapi/dns_opnsense.sh
+++ b/dnsapi/dns_opnsense.sh
@@ -137,7 +137,7 @@ _get_root() {
domain=$1
i=2
p=1
- if _opns_rest "GET" "/domain/searchMasterDomain"; then
+ if _opns_rest "GET" "/domain/searchPrimaryDomain"; then
_domain_response="$response"
else
return 1
@@ -150,7 +150,7 @@ _get_root() {
return 1
fi
_debug h "$h"
- id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"master\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"primary\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
if [ -n "$id" ]; then
_debug id "$id"
_host=$(printf "%s" "$domain" | cut -d . -f 1-$p)
diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh
index 5e35011b..e1a958f6 100755
--- a/dnsapi/dns_ovh.sh
+++ b/dnsapi/dns_ovh.sh
@@ -14,6 +14,9 @@
#'ovh-eu'
OVH_EU='https://eu.api.ovh.com/1.0'
+#'ovh-us'
+OVH_US='https://api.us.ovhcloud.com/1.0'
+
#'ovh-ca':
OVH_CA='https://ca.api.ovh.com/1.0'
@@ -29,9 +32,6 @@ SYS_EU='https://eu.api.soyoustart.com/1.0'
#'soyoustart-ca'
SYS_CA='https://ca.api.soyoustart.com/1.0'
-#'runabove-ca'
-RAV_CA='https://api.runabove.com/1.0'
-
wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-OVH-domain-api"
ovh_success="https://github.com/acmesh-official/acme.sh/wiki/OVH-Success"
@@ -45,6 +45,10 @@ _ovh_get_api() {
printf "%s" $OVH_EU
return
;;
+ ovh-us | ovhus)
+ printf "%s" $OVH_US
+ return
+ ;;
ovh-ca | ovhca)
printf "%s" $OVH_CA
return
@@ -65,14 +69,15 @@ _ovh_get_api() {
printf "%s" $SYS_CA
return
;;
- runabove-ca | runaboveca)
- printf "%s" $RAV_CA
+ # raw API url starts with https://
+ https*)
+ printf "%s" "$1"
return
;;
*)
- _err "Unknown parameter : $1"
+ _err "Unknown endpoint : $1"
return 1
;;
esac
diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh
index f5986827..81973e07 100644
--- a/dnsapi/dns_pleskxml.sh
+++ b/dnsapi/dns_pleskxml.sh
@@ -41,11 +41,15 @@ pleskxml_init_checks_done=0
NEWLINE='\
'
-pleskxml_tplt_get_domains=""
+pleskxml_tplt_get_domains=""
# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh
# Also used to test credentials and URI.
# No params.
+pleskxml_tplt_get_additional_domains=""
+# Get a list of additional domains that PLESK can manage, so we can check root domain + host for acme.sh
+# No params.
+
pleskxml_tplt_get_dns_records="%s"
# Get all DNS records for a Plesk domain ID.
# PARAM = Plesk domain id to query
@@ -145,22 +149,25 @@ dns_pleskxml_rm() {
)"
if [ -z "$reclist" ]; then
- _err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting."
+ _err "No TXT records found for root domain $fulldomain (Plesk domain ID ${root_domain_id}). Exiting."
return 1
fi
- _debug "Got list of DNS TXT records for root domain '$root_domain_name':"
+ _debug "Got list of DNS TXT records for root Plesk domain ID ${root_domain_id} of root domain $fulldomain:"
_debug "$reclist"
+ # Extracting the id of the TXT record for the full domain (NOT case-sensitive) and corresponding value
recid="$(
_value "$reclist" |
- grep "${fulldomain}." |
+ grep -i "${fulldomain}." |
grep "${txtvalue}" |
sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/'
)"
+ _debug "Got id from line: $recid"
+
if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then
- _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
+ _err "DNS records for root domain '${fulldomain}.' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
_err "Cannot delete TXT record. Exiting."
return 1
fi
@@ -251,9 +258,12 @@ _call_api() {
# Detect any that isn't "ok". None of the used calls should fail if the API is working correctly.
# Also detect if there simply aren't any status lines (null result?) and report that, as well.
+ # Remove structure from result string, since it might contain values that are related to the status of the domain and not to the API request
- statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *[^<]* *$')"
- statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *ok *$')"
+ statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | sed '//,/<\/data>/d' | grep -c '^ *[^<]* *$')"
+ statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | sed '//,/<\/data>/d' | grep -c '^ *ok *$')"
+ _debug "statuslines_count_total=$statuslines_count_total."
+ _debug "statuslines_count_okay=$statuslines_count_okay."
if [ -z "$statuslines_count_total" ]; then
@@ -369,16 +379,44 @@ _pleskxml_get_root_domain() {
return 1
fi
- # Generate a crude list of domains known to this Plesk account.
+ # Generate a crude list of domains known to this Plesk account based on subscriptions.
+ # We convert tags to so it'll flag on a hit with either or fields,
+ # for non-Western character sets.
+ # Output will be one line per known domain, containing 2 tages and a single tag
+ # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
+
+ output="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')"
+ debug_output="$(printf "%s" "$output" | sed -n 's:.*\(.*\).*:\1:p')"
+
+ _debug 'Domains managed by Plesk server are:'
+ _debug "$debug_output"
+
+ _debug "Querying Plesk server for list of additional managed domains..."
+
+ _call_api "$pleskxml_tplt_get_additional_domains"
+ if [ "$pleskxml_retcode" -ne 0 ]; then
+ return 1
+ fi
+
+ # Generate a crude list of additional domains known to this Plesk account based on sites.
# We convert tags to so it'll flag on a hit with either or fields,
# for non-Western character sets.
# Output will be one line per known domain, containing 2 tages and a single tag
# We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
- output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')"
+ output_additional="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')"
+ debug_additional="$(printf "%s" "$output_additional" | sed -n 's:.*\(.*\).*:\1:p')"
+
+ _debug 'Additional domains managed by Plesk server are:'
+ _debug "$debug_additional"
+
+ # Concate the two outputs together.
+
+ output="$(printf "%s" "$output $NEWLINE $output_additional")"
+ debug_output="$(printf "%s" "$output" | sed -n 's:.*\(.*\).*:\1:p')"
- _debug 'Domains managed by Plesk server are (ignore the hacked output):'
- _debug "$output"
+ _debug 'Domains (including additional) managed by Plesk server are:'
+ _debug "$debug_output"
# loop and test if domain, or any parent domain, is managed by Plesk
# Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain
diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
new file mode 100644
index 00000000..2f8d3b67
--- /dev/null
+++ b/dnsapi/dns_tencent.sh
@@ -0,0 +1,211 @@
+#!/usr/bin/env sh
+Tencent_API="https://dnspod.tencentcloudapi.com"
+
+#Tencent_SecretId="AKIDz81d2cd22cdcdc2dcd1cc1d1A"
+#Tencent_SecretKey="Gu5t9abcabcaabcbabcbbbcbcbbccbbcb"
+
+#Usage: dns_tencent_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_tencent_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+ Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+ if [ -z "$Tencent_SecretId" ] || [ -z "$Tencent_SecretKey" ]; then
+ Tencent_SecretId=""
+ Tencent_SecretKey=""
+ _err "You don't specify tencent api SecretId and SecretKey yet."
+ return 1
+ fi
+
+ #save the api SecretId and SecretKey to the account conf file.
+ _saveaccountconf_mutable Tencent_SecretId "$Tencent_SecretId"
+ _saveaccountconf_mutable Tencent_SecretKey "$Tencent_SecretKey"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ _debug "Add record"
+ _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "CreateRecord"
+}
+
+dns_tencent_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+ Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ _debug "Get record list"
+ attempt=1
+ max_attempts=5
+ while [ -z "$record_id" ] && [ "$attempt" -le $max_attempts ]; do
+ _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList"
+ record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+ _debug2 record_id "$record_id"
+ if [ -z "$record_id" ]; then
+ _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying"
+ _sleep 10
+ attempt=$(_math "$attempt + 1")
+ fi
+ done
+
+ record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+ _debug2 record_id "$record_id"
+
+ if [ -z "$record_id" ]; then
+ _debug "record not found after $max_attempts attempts, skip"
+ else
+ _debug "Delete record"
+ _delete_record_query "$record_id" && _tencent_rest "DeleteRecord"
+ fi
+}
+
+#################### Private functions below ##################################
+
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ _describe_records_query "$h" "@"
+ if ! _tencent_rest "DescribeRecordList" "ignore"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"TotalCount\":"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _debug _sub_domain "$_sub_domain"
+ _domain="$h"
+ _debug _domain "$_domain"
+ return 0
+ fi
+ p="$i"
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_tencent_rest() {
+ action=$1
+ service="dnspod"
+ payload="${query}"
+ timestamp=$(date -u +%s)
+
+ token=$(tencent_signature_v3 $service "$action" "$payload" "$timestamp")
+ version="2021-03-23"
+
+ if ! response="$(tencent_api_request $service $version "$action" "$payload" "$timestamp")"; then
+ _err "Error <$1>"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ if [ -z "$2" ]; then
+ message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+ if [ "$message" ]; then
+ _err "$message"
+ return 1
+ fi
+ fi
+}
+
+_add_record_query() {
+ query="{\"Domain\":\"$1\",\"SubDomain\":\"$2\",\"RecordType\":\"TXT\",\"RecordLineId\":\"0\",\"RecordLine\":\"0\",\"Value\":\"$3\",\"TTL\":600}"
+}
+
+_describe_records_query() {
+ query="{\"Domain\":\"$1\",\"Limit\":3000}"
+}
+
+_delete_record_query() {
+ query="{\"Domain\":\"$_domain\",\"RecordId\":$1}"
+}
+
+_check_exist_query() {
+ _domain="$1"
+ _subdomain="$2"
+ _value="$3"
+ query="{\"Domain\":\"$_domain\",\"SubDomain\":\"$_subdomain\",\"RecordValue\":\"$_value\"}"
+}
+
+# shell client for tencent cloud api v3 | @author: rehiy
+
+tencent_sha256() {
+ printf %b "$@" | _digest sha256 hex
+}
+
+tencent_hmac_sha256() {
+ k=$1
+ shift
+ hex_key=$(printf %b "$k" | _hex_dump | tr -d ' ')
+ printf %b "$@" | _hmac sha256 "$hex_key" hex
+}
+
+tencent_hmac_sha256_hexkey() {
+ k=$1
+ shift
+ printf %b "$@" | _hmac sha256 "$k" hex
+}
+
+tencent_signature_v3() {
+ service=$1
+ action=$(echo "$2" | _lower_case)
+ payload=${3:-'{}'}
+ timestamp=${4:-$(date +%s)}
+
+ domain="$service.tencentcloudapi.com"
+ secretId=${Tencent_SecretId:-'tencent-cloud-secret-id'}
+ secretKey=${Tencent_SecretKey:-'tencent-cloud-secret-key'}
+
+ algorithm='TC3-HMAC-SHA256'
+ date=$(date -u -d "@$timestamp" +%Y-%m-%d 2>/dev/null)
+ [ -z "$date" ] && date=$(date -u -r "$timestamp" +%Y-%m-%d)
+
+ canonicalUri='/'
+ canonicalQuery=''
+ canonicalHeaders="content-type:application/json\nhost:$domain\nx-tc-action:$action\n"
+
+ signedHeaders='content-type;host;x-tc-action'
+ canonicalRequest="POST\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$(tencent_sha256 "$payload")"
+
+ credentialScope="$date/$service/tc3_request"
+ stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
+
+ secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
+ secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")
+ secretSigning=$(tencent_hmac_sha256_hexkey "$secretService" 'tc3_request')
+ signature=$(tencent_hmac_sha256_hexkey "$secretSigning" "$stringToSign")
+
+ echo "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature"
+}
+
+tencent_api_request() {
+ service=$1
+ version=$2
+ action=$3
+ payload=${4:-'{}'}
+ timestamp=${5:-$(date +%s)}
+
+ token=$(tencent_signature_v3 "$service" "$action" "$payload" "$timestamp")
+
+ _H1="Content-Type: application/json"
+ _H2="Authorization: $token"
+ _H3="X-TC-Version: $version"
+ _H4="X-TC-Timestamp: $timestamp"
+ _H5="X-TC-Action: $action"
+
+ _post "$payload" "$Tencent_API" "" "POST" "application/json"
+}
diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh
index a35b8f0f..aa743807 100644
--- a/dnsapi/dns_variomedia.sh
+++ b/dnsapi/dns_variomedia.sh
@@ -69,7 +69,7 @@ dns_variomedia_rm() {
return 1
fi
- _record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
+ _record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
_debug _record_id "$_record_id"
if [ "$_record_id" ]; then
_info "Successfully retrieved the record id for ACME challenge."
@@ -93,11 +93,11 @@ dns_variomedia_rm() {
# _sub_domain=_acme-challenge.www
# _domain=domain.com
_get_root() {
- fulldomain=$1
+ domain=$1
i=1
+ p=1
while true; do
- h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
- _debug h "$h"
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
return 1
fi
@@ -106,17 +106,14 @@ _get_root() {
return 1
fi
- if _startswith "$response" "\{\"data\":"; then
- if _contains "$response" "\"id\":\"$h\""; then
- _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
- _domain=$h
- return 0
- fi
+ if _contains "$response" "\"id\":\"$h\""; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
+ _domain="$h"
+ return 0
fi
+ p=$i
i=$(_math "$i" + 1)
done
-
- _debug "root domain not found"
return 1
}
diff --git a/dnsapi/dns_west_cn.sh b/dnsapi/dns_west_cn.sh
new file mode 100644
index 00000000..d0bb7d49
--- /dev/null
+++ b/dnsapi/dns_west_cn.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env sh
+
+# West.cn Domain api
+#WEST_Username="username"
+#WEST_Key="sADDsdasdgdsf"
+#Set key at https://www.west.cn/manager/API/APIconfig.asp
+
+REST_API="https://api.west.cn/API/v2"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_west_cn_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+ WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+ if [ -z "$WEST_Username" ] || [ -z "$WEST_Key" ]; then
+ WEST_Username=""
+ WEST_Key=""
+ _err "You don't specify west api key and username yet."
+ _err "Please set you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable WEST_Username "$WEST_Username"
+ _saveaccountconf_mutable WEST_Key "$WEST_Key"
+
+ add_record "$fulldomain" "$txtvalue"
+}
+
+#Usage: rm _acme-challenge.www.domain.com
+dns_west_cn_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+ WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+
+ if ! _rest POST "domain/dns/" "act=dnsrec.list&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT"; then
+ _err "dnsrec.list error."
+ return 1
+ fi
+
+ if _contains "$response" 'no records'; then
+ _info "Don't need to remove."
+ return 0
+ fi
+
+ record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"record_id"' | cut -d : -f 2 | cut -d ',' -f 1)
+ _debug record_id "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id."
+ return 1
+ fi
+
+ if ! _rest POST "domain/dns/" "act=dnsrec.remove&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_id=$record_id"; then
+ _err "dnsrec.remove error."
+ return 1
+ fi
+
+ _contains "$response" "success"
+}
+
+#add the txt record.
+#usage: add fulldomain txtvalue
+add_record() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Adding record"
+
+ if ! _rest POST "domain/dns/" "act=dnsrec.add&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT&record_value=$txtvalue"; then
+ return 1
+ fi
+
+ _contains "$response" "success"
+}
+
+#Usage: method URI data
+_rest() {
+ m="$1"
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+ url="$REST_API/$ep"
+
+ _debug url "$url"
+
+ if [ "$m" = "GET" ]; then
+ response="$(_get "$url" | tr -d '\r')"
+ else
+ _debug2 data "$data"
+ response="$(_post "$data" "$url" | tr -d '\r')"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/notify/aws_ses.sh b/notify/aws_ses.sh
new file mode 100644
index 00000000..30db45ad
--- /dev/null
+++ b/notify/aws_ses.sh
@@ -0,0 +1,226 @@
+#!/usr/bin/env sh
+
+#
+#AWS_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#AWS_SECRET_ACCESS_KEY="xxxxxxx"
+#
+#AWS_SES_REGION="us-east-1"
+#
+#AWS_SES_TO="xxxx@xxx.com"
+#
+#AWS_SES_FROM="xxxx@cccc.com"
+#
+#AWS_SES_FROM_NAME="Something something"
+#This is the Amazon SES api wrapper for acme.sh
+AWS_WIKI="https://docs.aws.amazon.com/ses/latest/dg/send-email-api.html"
+
+aws_ses_send() {
+ _subject="$1"
+ _content="$2"
+ _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
+ _debug "_statusCode" "$_statusCode"
+
+ AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-$(_readaccountconf_mutable AWS_ACCESS_KEY_ID)}"
+ AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable AWS_SECRET_ACCESS_KEY)}"
+ AWS_SES_REGION="${AWS_SES_REGION:-$(_readaccountconf_mutable AWS_SES_REGION)}"
+
+ if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
+ _use_container_role || _use_instance_role
+ fi
+
+ if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then
+ AWS_ACCESS_KEY_ID=""
+ AWS_SECRET_ACCESS_KEY=""
+ _err "You haven't specified the aws SES api key id and and api key secret yet."
+ _err "Please create your key and try again. see $(__green $AWS_WIKI)"
+ return 1
+ fi
+
+ if [ -z "$AWS_SES_REGION" ]; then
+ AWS_SES_REGION=""
+ _err "You haven't specified the aws SES api region yet."
+ _err "Please specify your region and try again. see https://docs.aws.amazon.com/general/latest/gr/ses.html"
+ return 1
+ fi
+ _saveaccountconf_mutable AWS_SES_REGION "$AWS_SES_REGION"
+
+ #save for future use, unless using a role which will be fetched as needed
+ if [ -z "$_using_role" ]; then
+ _saveaccountconf_mutable AWS_ACCESS_KEY_ID "$AWS_ACCESS_KEY_ID"
+ _saveaccountconf_mutable AWS_SECRET_ACCESS_KEY "$AWS_SECRET_ACCESS_KEY"
+ fi
+
+ AWS_SES_TO="${AWS_SES_TO:-$(_readaccountconf_mutable AWS_SES_TO)}"
+ if [ -z "$AWS_SES_TO" ]; then
+ AWS_SES_TO=""
+ _err "You didn't specify an email to AWS_SES_TO receive messages."
+ return 1
+ fi
+ _saveaccountconf_mutable AWS_SES_TO "$AWS_SES_TO"
+
+ AWS_SES_FROM="${AWS_SES_FROM:-$(_readaccountconf_mutable AWS_SES_FROM)}"
+ if [ -z "$AWS_SES_FROM" ]; then
+ AWS_SES_FROM=""
+ _err "You didn't specify an email to AWS_SES_FROM receive messages."
+ return 1
+ fi
+ _saveaccountconf_mutable AWS_SES_FROM "$AWS_SES_FROM"
+
+ AWS_SES_FROM_NAME="${AWS_SES_FROM_NAME:-$(_readaccountconf_mutable AWS_SES_FROM_NAME)}"
+ _saveaccountconf_mutable AWS_SES_FROM_NAME "$AWS_SES_FROM_NAME"
+
+ AWS_SES_SENDFROM="$AWS_SES_FROM_NAME <$AWS_SES_FROM>"
+
+ AWS_SES_ACTION="Action=SendEmail"
+ AWS_SES_SOURCE="Source=$AWS_SES_SENDFROM"
+ AWS_SES_TO="Destination.ToAddresses.member.1=$AWS_SES_TO"
+ AWS_SES_SUBJECT="Message.Subject.Data=$_subject"
+ AWS_SES_MESSAGE="Message.Body.Text.Data=$_content"
+
+ _data="${AWS_SES_ACTION}&${AWS_SES_SOURCE}&${AWS_SES_TO}&${AWS_SES_SUBJECT}&${AWS_SES_MESSAGE}"
+
+ response="$(aws_rest POST "" "" "$_data")"
+}
+
+_use_metadata() {
+ _aws_creds="$(
+ _get "$1" "" 1 |
+ _normalizeJson |
+ tr '{,}' '\n' |
+ while read -r _line; do
+ _key="$(echo "${_line%%:*}" | tr -d '"')"
+ _value="${_line#*:}"
+ _debug3 "_key" "$_key"
+ _secure_debug3 "_value" "$_value"
+ case "$_key" in
+ AccessKeyId) echo "AWS_ACCESS_KEY_ID=$_value" ;;
+ SecretAccessKey) echo "AWS_SECRET_ACCESS_KEY=$_value" ;;
+ Token) echo "AWS_SESSION_TOKEN=$_value" ;;
+ esac
+ done |
+ paste -sd' ' -
+ )"
+ _secure_debug "_aws_creds" "$_aws_creds"
+
+ if [ -z "$_aws_creds" ]; then
+ return 1
+ fi
+
+ eval "$_aws_creds"
+ _using_role=true
+}
+
+#method uri qstr data
+aws_rest() {
+ mtd="$1"
+ ep="$2"
+ qsr="$3"
+ data="$4"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+ _debug qsr "$qsr"
+ _debug data "$data"
+
+ CanonicalURI="/$ep"
+ _debug2 CanonicalURI "$CanonicalURI"
+
+ CanonicalQueryString="$qsr"
+ _debug2 CanonicalQueryString "$CanonicalQueryString"
+
+ RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
+ _debug2 RequestDate "$RequestDate"
+
+ #RequestDate="20161120T141056Z" ##############
+
+ export _H1="x-amz-date: $RequestDate"
+
+ aws_host="email.$AWS_SES_REGION.amazonaws.com"
+ CanonicalHeaders="host:$aws_host\nx-amz-date:$RequestDate\n"
+ SignedHeaders="host;x-amz-date"
+ if [ -n "$AWS_SESSION_TOKEN" ]; then
+ export _H3="x-amz-security-token: $AWS_SESSION_TOKEN"
+ CanonicalHeaders="${CanonicalHeaders}x-amz-security-token:$AWS_SESSION_TOKEN\n"
+ SignedHeaders="${SignedHeaders};x-amz-security-token"
+ fi
+ _debug2 CanonicalHeaders "$CanonicalHeaders"
+ _debug2 SignedHeaders "$SignedHeaders"
+
+ RequestPayload="$data"
+ _debug2 RequestPayload "$RequestPayload"
+
+ Hash="sha256"
+
+ CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
+ _debug2 CanonicalRequest "$CanonicalRequest"
+
+ HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)"
+ _debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
+
+ Algorithm="AWS4-HMAC-SHA256"
+ _debug2 Algorithm "$Algorithm"
+
+ RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
+ _debug2 RequestDateOnly "$RequestDateOnly"
+
+ Region="$AWS_SES_REGION"
+ Service="ses"
+
+ CredentialScope="$RequestDateOnly/$Region/$Service/aws4_request"
+ _debug2 CredentialScope "$CredentialScope"
+
+ StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
+
+ _debug2 StringToSign "$StringToSign"
+
+ kSecret="AWS4$AWS_SECRET_ACCESS_KEY"
+
+ #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
+
+ _secure_debug2 kSecret "$kSecret"
+
+ kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
+ _secure_debug2 kSecretH "$kSecretH"
+
+ kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
+ _debug2 kDateH "$kDateH"
+
+ kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)"
+ _debug2 kRegionH "$kRegionH"
+
+ kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
+ _debug2 kServiceH "$kServiceH"
+
+ kSigningH="$(printf "%s" "aws4_request" | _hmac "$Hash" "$kServiceH" hex)"
+ _debug2 kSigningH "$kSigningH"
+
+ signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
+ _debug2 signature "$signature"
+
+ Authorization="$Algorithm Credential=$AWS_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
+ _debug2 Authorization "$Authorization"
+
+ _H2="Authorization: $Authorization"
+ _debug _H2 "$_H2"
+
+ url="https://$aws_host/$ep"
+ if [ "$qsr" ]; then
+ url="https://$aws_host/$ep?$qsr"
+ fi
+
+ if [ "$mtd" = "GET" ]; then
+ response="$(_get "$url")"
+ else
+ response="$(_post "$data" "$url")"
+ fi
+
+ _ret="$?"
+ _debug2 response "$response"
+ if [ "$_ret" = "0" ]; then
+ if _contains "$response" "