Browse Source

Merge pull request #6832 from acmesh-official/dev

sync
master
neil 5 days ago
committed by GitHub
parent
commit
d08b4de794
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 67
      .github/copilot-instructions.md
  2. 38
      .github/workflows/DNS.yml
  3. 6
      .github/workflows/DragonFlyBSD.yml
  4. 6
      .github/workflows/FreeBSD.yml
  5. 6
      .github/workflows/Haiku.yml
  6. 9
      .github/workflows/Linux.yml
  7. 9
      .github/workflows/MacOS.yml
  8. 6
      .github/workflows/NetBSD.yml
  9. 6
      .github/workflows/Omnios.yml
  10. 6
      .github/workflows/OpenBSD.yml
  11. 6
      .github/workflows/OpenIndiana.yml
  12. 4
      .github/workflows/PebbleStrict.yml
  13. 6
      .github/workflows/Solaris.yml
  14. 2
      .github/workflows/Ubuntu.yml
  15. 9
      .github/workflows/Windows.yml
  16. 2
      .github/workflows/dockerhub.yml
  17. 4
      .github/workflows/shellcheck.yml
  18. 2
      .github/workflows/wiki-monitor.yml
  19. 19
      Dockerfile
  20. 27
      acme.sh
  21. 3
      deploy/kemplm.sh
  22. 157
      deploy/localcopy.sh
  23. 23
      deploy/panos.sh
  24. 6
      deploy/proxmoxbs.sh
  25. 6
      deploy/proxmoxve.sh
  26. 2
      deploy/ssh.sh
  27. 6
      dnsapi/dns_cyon.sh
  28. 103
      dnsapi/dns_infomaniak.sh
  29. 2
      dnsapi/dns_me.sh
  30. 105
      dnsapi/dns_nsupdate.sh
  31. 158
      dnsapi/dns_opusdns.sh
  32. 14
      dnsapi/dns_technitium.sh
  33. 13
      notify/telegram.sh

67
.github/copilot-instructions.md

@ -0,0 +1,67 @@
# GitHub Copilot Shell Scripting (sh) Review Instructions
## 🎯 Overall Goal
Your role is to act as a rigorous yet helpful senior engineer, reviewing Shell script code (`.sh` files). Ensure the code exhibits the highest levels of robustness, security, and portability.
The review must focus on risks unique to Shell scripting, such as proper quoting, robust error handling, and the secure execution of external commands.
## 📝 Required Output Format
Please adhere to the previous format: organize the feedback into a single, structured report, using the three-level marking system:
1. **🔴 Critical Issues (Must Fix Before Merge)**
2. **🟡 Suggestions (Improvements to Consider)**
3. **✅ Good Practices (Points to Commend)**
---
## 🔍 Focus Areas and Rules for Shell
### 1. Robustness and Error Handling
* **Shebang:** Check that the script starts with the correct Shebang, must be "#!/usr/bin/env sh".
* **Startup Options:** **(🔴 Critical)** Enforce the use of the following combination at the start of the script for safety and robustness:
* `set -e`: Exit immediately if a command exits with a non-zero status.
* `set -u`: Treat unset variables as an error and exit.
* `set -o pipefail`: Ensure the whole pipeline fails if any command in the pipe fails.
* **Exit Codes:** Ensure functions and the main script use `exit 0` for success and a non-zero exit code upon failure.
* **Temporary Files:** Check for the use of `mktemp` when creating temporary files to prevent race conditions and security risks.
### 2. Security and Quoting
* **Variable Quoting:** **(🔴 Critical)** Check that all variable expansions (like `$VAR` and `$(COMMAND)`) are properly enclosed in **double quotes** (i.e., `"$VAR"` and `"$(COMMAND)"`) to prevent **Word Splitting** and **Globbing**.
* **Hardcoded Secrets:** **(🔴 Critical)** Find and flag any hardcoded passwords, keys, tokens, or authentication details.
* **Untrusted Input:** Verify that all user input, command-line arguments (`$1`, `$2`, etc.), or environment variables are rigorously validated and sanitized before use.
* **Avoid `eval`:** Warn against and suggest alternatives to using `eval`, as it can lead to arbitrary code execution.
### 3. Readability and Maintainability
* **Function Usage:** Recommend wrapping complex or reusable logic within clearly named functions.
* **Local Variables:** Check that variables inside functions are declared using the `local` keyword to avoid unintentionally modifying global state.
* **Naming Convention:** Variable names should use uppercase letters and underscores (e.g., `MY_VARIABLE`), or follow established project conventions.
* **Test Conditions:** Encourage the use of Bash's **double brackets `[[ ... ]]`** for conditional tests, as it is generally safer and more powerful (e.g., supports pattern matching and avoids Word Splitting) than single brackets `[ ... ]`.
* **Command Substitution:** Encourage using `$(command)` over backticks `` `command` `` for command substitution, as it is easier to nest and improves readability.
### 4. External Commands and Environment
* **`for` Loops:** Warn against patterns like `for i in $(cat file)` or `for i in $(ls)` and recommend the more robust `while IFS= read -r line` pattern for safely processing file contents or filenames that might contain spaces.
* **Use existing acme.sh functions whenever possible.** For example: do not use `tr '[:upper:]' '[:lower:]'`, use `_lower_case` instead.
* **Do not use `head -n`.** Use the `_head_n()` function instead.
* **Do not use `curl` or `wget`.** Use the `_post()` and `_get()` functions instead.
---
### 5. Review Rules for Files Under `dnsapi/`:
* **Each file must contain a `{filename}_add` function** for adding DNS TXT records. It should use `_readaccountconf_mutable` to read the API key and `_saveaccountconf_mutable` to save it. Do not use `_saveaccountconf` or `_readaccountconf`.
## ❌ Things to Avoid
* Do not comment on purely stylistic issues like spacing or indentation, which should be handled by tools like ShellCheck or Prettier.
* Do not be overly verbose unless a significant issue is found. Keep feedback concise and actionable.

38
.github/workflows/DNS.yml

@ -66,7 +66,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- name: Set env file
@ -114,7 +114,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install tools
run: brew install socat
- name: Clone acmetest
@ -165,7 +165,7 @@ jobs:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install cygwin base packages with chocolatey
run: |
choco config get cacheLocation
@ -224,7 +224,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/freebsd-vm@v1
@ -251,7 +251,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"
@ -279,7 +279,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/openbsd-vm@v1
@ -306,7 +306,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"
@ -334,7 +334,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/netbsd-vm@v1
@ -362,7 +362,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"
@ -390,7 +390,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/dragonflybsd-vm@v1
@ -418,7 +418,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"
@ -450,7 +450,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/solaris-vm@v1
@ -480,7 +480,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"
@ -508,7 +508,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/omnios-vm@v1
@ -534,7 +534,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"
@ -563,7 +563,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/openindiana-vm@v1
@ -589,7 +589,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"
@ -618,7 +618,7 @@ jobs:
TokenName4: ${{ secrets.TokenName4}}
TokenName5: ${{ secrets.TokenName5}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Clone acmetest
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/
- uses: vmactions/haiku-vm@v1
@ -648,7 +648,7 @@ jobs:
fi
cd ../acmetest
./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

6
.github/workflows/DragonFlyBSD.yml

@ -45,8 +45,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -67,7 +67,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

6
.github/workflows/FreeBSD.yml

@ -51,8 +51,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -72,7 +72,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

6
.github/workflows/Haiku.yml

@ -52,8 +52,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -75,7 +75,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

9
.github/workflows/Linux.yml

@ -33,7 +33,14 @@ jobs:
TEST_PREFERRED_CHAIN: (STAGING)
TEST_ACME_Server: "LetsEncrypt.org_test"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
port: 80
- name: Set envs
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
- name: Clone acmetest
run: |
cd .. \

9
.github/workflows/MacOS.yml

@ -44,9 +44,16 @@ jobs:
CA_EMAIL: ${{ matrix.CA_EMAIL }}
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install tools
run: brew install socat
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
port: 80
- name: Set envs
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
- name: Clone acmetest
run: |
cd .. \

6
.github/workflows/NetBSD.yml

@ -45,8 +45,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -67,7 +67,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

6
.github/workflows/Omnios.yml

@ -51,8 +51,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -71,7 +71,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

6
.github/workflows/OpenBSD.yml

@ -51,8 +51,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -72,7 +72,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

6
.github/workflows/OpenIndiana.yml

@ -51,8 +51,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -71,7 +71,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

4
.github/workflows/PebbleStrict.yml

@ -33,7 +33,7 @@ jobs:
TEST_CA: "Pebble Intermediate CA"
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- 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@v4
- uses: actions/checkout@v6
- name: Install tools
run: sudo apt-get install -y socat
- name: Run Pebble

6
.github/workflows/Solaris.yml

@ -51,8 +51,8 @@ jobs:
TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: vmactions/cf-tunnel@v0
- uses: actions/checkout@v6
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
@ -73,7 +73,7 @@ jobs:
run: |
cd ../acmetest \
&& ./letest.sh
- name: onError
- name: DebugOnError
if: ${{ failure() }}
run: |
echo "See how to debug in VM:"

2
.github/workflows/Ubuntu.yml

@ -70,7 +70,7 @@ jobs:
TestingDomain: ${{ matrix.TestingDomain }}
ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install tools
run: sudo apt-get install -y socat wget
- name: Start StepCA

9
.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@v4
- uses: actions/checkout@v6
- name: Install cygwin base packages with chocolatey
run: |
choco config get cacheLocation
@ -67,6 +67,13 @@ jobs:
shell: cmd
run: |
echo "PATH=%PATH%"
- uses: anyvm-org/cf-tunnel@v0
id: tunnel
with:
protocol: http
port: 80
- name: Set envs
run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
- name: Clone acmetest
shell: cmd
run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git && cp -r acme.sh acmetest/

2
.github/workflows/dockerhub.yml

@ -43,7 +43,7 @@ jobs:
if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
steps:
- name: checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up QEMU

4
.github/workflows/shellcheck.yml

@ -22,7 +22,7 @@ jobs:
ShellCheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- 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@v4
- uses: actions/checkout@v6
- 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

2
.github/workflows/wiki-monitor.yml

@ -9,7 +9,7 @@ jobs:
if: github.actor != 'neilpang'
steps:
- name: Checkout wiki repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
repository: ${{ github.repository }}.wiki
path: wiki

19
Dockerfile

@ -14,12 +14,14 @@ RUN apk --no-cache add -f \
libidn \
jq \
yq-go \
cronie
supercronic
ENV LE_WORKING_DIR=/acmebin
ENV LE_CONFIG_HOME=/acme.sh
ENV HOME=/acme.sh
ARG AUTO_UPGRADE=1
ENV AUTO_UPGRADE=$AUTO_UPGRADE
@ -30,10 +32,13 @@ COPY ./deploy /install_acme.sh/deploy
COPY ./dnsapi /install_acme.sh/dnsapi
COPY ./notify /install_acme.sh/notify
RUN addgroup -g 1000 acme && adduser -h $LE_CONFIG_HOME -s /bin/sh -G acme -D -H -u 1000 acme
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
RUN ln -s $LE_WORKING_DIR/acme.sh /usr/local/bin/acme.sh
RUN ln -s $LE_WORKING_DIR/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null#> /proc/1/fd/1 2>/proc/1/fd/2#' | crontab -
RUN chown -R acme:acme $LE_CONFIG_HOME
RUN for verb in help \
version \
@ -72,7 +77,15 @@ RUN for verb in help \
RUN printf "%b" '#!'"/usr/bin/env sh\n \
if [ \"\$1\" = \"daemon\" ]; then \n \
exec crond -n -s -m off \n \
if [ ! -f \"\$LE_CONFIG_HOME/crontab\" ]; then \n \
echo \"\$LE_CONFIG_HOME/crontab not found, generating one\" \n \
time=\$(date -u \"+%s\") \n \
random_minute=\$((\$time % 60)) \n \
random_hour=\$((\$time / 60 % 24)) \n \
echo \"\$random_minute \$random_hour * * * \\\"\$LE_WORKING_DIR\\\"/acme.sh --cron --home \\\"\$LE_WORKING_DIR\\\" --config-home \\\"\$LE_CONFIG_HOME\\\"\" > \"\$LE_CONFIG_HOME\"/crontab \n \
fi \n \
echo \"Running Supercronic using crontab at \$LE_CONFIG_HOME/crontab\" \n \
exec -- /usr/bin/supercronic \"\$LE_CONFIG_HOME/crontab\" \n \
else \n \
exec -- \"\$@\"\n \
fi\n" >/entry.sh && chmod +x /entry.sh && chmod -R o+rwx $LE_WORKING_DIR && chmod -R o+rwx $LE_CONFIG_HOME

27
acme.sh

@ -595,11 +595,6 @@ if [ "$(printf '\x41')" != 'A' ]; then
_URGLY_PRINTF=1
fi
_ESCAPE_XARGS=""
if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
_ESCAPE_XARGS=1
fi
_h2b() {
if _exists xxd; then
if _contains "$(xxd --help 2>&1)" "assumes -c30"; then
@ -618,17 +613,8 @@ _h2b() {
jc=""
_debug2 _URGLY_PRINTF "$_URGLY_PRINTF"
if [ -z "$_URGLY_PRINTF" ]; then
if [ "$_ESCAPE_XARGS" ] && _exists xargs; then
_debug2 "xargs"
echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/g' | xargs printf
else
for h in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/ \1/g'); do
if [ -z "$h" ]; then
break
fi
printf "\x$h%s"
done
fi
# shellcheck disable=SC2059
printf "$(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\{2\}\)/\\x\1/g')"
else
for c in $(echo "$hex" | _upper_case | sed 's/\([0-9A-F]\)/ \1/g'); do
if [ -z "$ic" ]; then
@ -5195,7 +5181,12 @@ $_authorizations_map"
if [ "$DEBUG" ]; then
if [ "$vtype" = "$VTYPE_HTTP" ]; then
_debug "Debug: GET token URL."
_get "http://$d/.well-known/acme-challenge/$token" "" 1
if _isIPv6 "$d"; then
host="[$d]"
else
host="$d"
fi
_get "http://$host/.well-known/acme-challenge/$token" "" 1
fi
fi
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
@ -5675,7 +5666,7 @@ renewAll() {
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
_debug "_set_level" "$_set_level"
export _ACME_IN_RENEWALL=1
for di in "${CERT_HOME}"/*.*/; do
for di in "${CERT_HOME}"/*[.:]*/; do
_debug di "$di"
if ! [ -d "$di" ]; then
_debug "Not a directory, skipping: $di"

3
deploy/kemplm.sh

@ -56,7 +56,7 @@ kemplm_deploy() {
_info "Check if certificate is already present"
_list_request="{\"cmd\": \"listcert\", \"apikey\": \"${DEPLOY_KEMP_TOKEN}\"}"
_debug3 _list_request "${_list_request}"
_kemp_cert_count=$(HTTPS_INSECURE=1 _post "${_list_request}" "${DEPLOY_KEMP_URL}/accessv2" | jq -r '.cert[] | .name' | grep -c "${_kemp_domain}")
_kemp_cert_count=$(HTTPS_INSECURE=1 _post "${_list_request}" "${DEPLOY_KEMP_URL}/accessv2" | jq -r '.cert[] | .name' | grep -c "^${_kemp_domain}$")
_debug2 _kemp_cert_count "${_kemp_cert_count}"
_kemp_replace_cert=1
@ -86,6 +86,7 @@ kemplm_deploy() {
_info "Upload successful"
else
_err "Upload failed: ${_kemp_post_message}"
_retval=1
fi
else
_err "Upload failed"

157
deploy/localcopy.sh

@ -0,0 +1,157 @@
#!/usr/bin/env sh
# Deploy-hook to very simply copy files to set directories and then
# execute whatever reloadcmd the admin needs afterwards. This can be
# useful for configurations where the "multideploy" hook (in development)
# is used or when an admin wants ACME.SH to renew certs but needs to
# manually configure deployment via an external script
# (e.g. The deploy-freenas script for TrueNAS Core/Scale
# https://github.com/danb35/deploy-freenas/ )
#
# If the same file is configured for the certificate key
# and the certificate and/or full chain, a combined PEM file will
# be output instead.
#
# Environment variables to be utilized are as follows:
#
# DEPLOY_LOCALCOPY_CERTKEY - /path/to/target/cert.key
# DEPLOY_LOCALCOPY_CERTIFICATE - /path/to/target/cert.cer
# DEPLOY_LOCALCOPY_FULLCHAIN - /path/to/target/fullchain.cer
# DEPLOY_LOCALCOPY_CA - /path/to/target/ca.cer
# DEPLOY_LOCALCOPY_PFX - /path/to/target/cert.pfx
# DEPLOY_LOCALCOPY_RELOADCMD - "echo 'this is my cmd'"
######## Public functions #####################
#domain keyfile certfile cafile fullchain
localcopy_deploy() {
_cdomain="$1"
_ckey="$2"
_ccert="$3"
_cca="$4"
_cfullchain="$5"
_cpfx="$6"
_debug _cdomain "$_cdomain"
_debug _ckey "$_ckey"
_debug _ccert "$_ccert"
_debug _cca "$_cca"
_debug _cfullchain "$_cfullchain"
_debug _cpfx "$_cpfx"
_getdeployconf DEPLOY_LOCALCOPY_CERTIFICATE
_getdeployconf DEPLOY_LOCALCOPY_CERTKEY
_getdeployconf DEPLOY_LOCALCOPY_FULLCHAIN
_getdeployconf DEPLOY_LOCALCOPY_CA
_getdeployconf DEPLOY_LOCALCOPY_RELOADCMD
_getdeployconf DEPLOY_LOCALCOPY_PFX
_combined_target=""
_combined_srccert=""
# Create PEM file
if [ "$DEPLOY_LOCALCOPY_CERTKEY" ] &&
{ [ "$DEPLOY_LOCALCOPY_CERTKEY" = "$DEPLOY_LOCALCOPY_FULLCHAIN" ] ||
[ "$DEPLOY_LOCALCOPY_CERTKEY" = "$DEPLOY_LOCALCOPY_CERTIFICATE" ]; }; then
_combined_target="$DEPLOY_LOCALCOPY_CERTKEY"
_savedeployconf DEPLOY_LOCALCOPY_CERTKEY "$DEPLOY_LOCALCOPY_CERTKEY"
if [ "$DEPLOY_LOCALCOPY_CERTKEY" = "$DEPLOY_LOCALCOPY_CERTIFICATE" ]; then
_combined_srccert="$_ccert"
_savedeployconf DEPLOY_LOCALCOPY_CERTIFICATE "$DEPLOY_LOCALCOPY_CERTIFICATE"
DEPLOY_LOCALCOPY_CERTIFICATE=""
fi
if [ "$DEPLOY_LOCALCOPY_CERTKEY" = "$DEPLOY_LOCALCOPY_FULLCHAIN" ]; then
_combined_srccert="$_cfullchain"
_savedeployconf DEPLOY_LOCALCOPY_FULLCHAIN "$DEPLOY_LOCALCOPY_FULLCHAIN"
DEPLOY_LOCALCOPY_FULLCHAIN=""
fi
DEPLOY_LOCALCOPY_CERTKEY=""
_info "Creating combined PEM"
_debug "Creating combined PEM at $_combined_target"
if ! [ -f "$_combined_target" ]; then
touch "$_combined_target" || return 1
chmod 600 "$_combined_target"
fi
if ! cat "$_combined_srccert" "$_ckey" >"$_combined_target"; then
_err "Failed to create PEM file"
return 1
fi
fi
if [ "$DEPLOY_LOCALCOPY_CERTIFICATE" ]; then
_info "Copying certificate"
_debug "Copying $_ccert to $DEPLOY_LOCALCOPY_CERTIFICATE"
if ! cat "$_ccert" >"$DEPLOY_LOCALCOPY_CERTIFICATE"; then
_err "Failed to copy certificate, aborting."
return 1
fi
_savedeployconf DEPLOY_LOCALCOPY_CERTIFICATE "$DEPLOY_LOCALCOPY_CERTIFICATE"
fi
if [ "$DEPLOY_LOCALCOPY_CERTKEY" ]; then
_info "Copying certificate key"
_debug "Copying $_ckey to $DEPLOY_LOCALCOPY_CERTKEY"
if ! [ -f "$DEPLOY_LOCALCOPY_CERTKEY" ]; then
touch "$DEPLOY_LOCALCOPY_CERTKEY" || return 1
chmod 600 "$DEPLOY_LOCALCOPY_CERTKEY"
fi
if ! cat "$_ckey" >"$DEPLOY_LOCALCOPY_CERTKEY"; then
_err "Failed to copy certificate key, aborting."
return 1
fi
_savedeployconf DEPLOY_LOCALCOPY_CERTKEY "$DEPLOY_LOCALCOPY_CERTKEY"
fi
if [ "$DEPLOY_LOCALCOPY_FULLCHAIN" ]; then
_info "Copying fullchain"
_debug "Copying $_cfullchain to $DEPLOY_LOCALCOPY_FULLCHAIN"
if ! cat "$_cfullchain" >"$DEPLOY_LOCALCOPY_FULLCHAIN"; then
_err "Failed to copy fullchain, aborting."
return 1
fi
_savedeployconf DEPLOY_LOCALCOPY_FULLCHAIN "$DEPLOY_LOCALCOPY_FULLCHAIN"
fi
if [ "$DEPLOY_LOCALCOPY_CA" ]; then
_info "Copying CA"
_debug "Copying $_cca to $DEPLOY_LOCALCOPY_CA"
if ! cat "$_cca" >"$DEPLOY_LOCALCOPY_CA"; then
_err "Failed to copy CA, aborting."
return 1
fi
_savedeployconf DEPLOY_LOCALCOPY_CA "$DEPLOY_LOCALCOPY_CA"
fi
if [ "$DEPLOY_LOCALCOPY_PFX" ]; then
_info "Copying PFX"
_debug "Copying $_cpfx to $DEPLOY_LOCALCOPY_PFX"
if ! [ -f "$DEPLOY_LOCALCOPY_PFX" ]; then
touch "$DEPLOY_LOCALCOPY_PFX" || return 1
chmod 600 "$DEPLOY_LOCALCOPY_PFX"
fi
if ! cat "$_cpfx" >"$DEPLOY_LOCALCOPY_PFX"; then
_err "Failed to copy PFX, aborting."
return 1
fi
_savedeployconf DEPLOY_LOCALCOPY_PFX "$DEPLOY_LOCALCOPY_PFX"
fi
_reload=$DEPLOY_LOCALCOPY_RELOADCMD
_debug "Running reloadcmd $_reload"
if [ -z "$_reload" ]; then
_info "Reloadcmd not provided, skipping."
else
_info "Reloading"
if eval "$_reload"; then
_info "Reload successful."
_savedeployconf DEPLOY_LOCALCOPY_RELOADCMD "$DEPLOY_LOCALCOPY_RELOADCMD" "base64"
else
_err "Reload failed."
return 1
fi
fi
_info "$(__green "'localcopy' deploy success")"
return 0
}

23
deploy/panos.sh

@ -207,13 +207,12 @@ panos_deploy() {
fi
# PANOS_KEY
_getdeployconf PANOS_KEY
if [ "$PANOS_KEY" ]; then
_debug "Detected saved key."
_panos_key=$PANOS_KEY
_debug "Detected ENV variable PANOS_KEY. Saving to file."
_savedeployconf PANOS_KEY "$PANOS_KEY" 1
else
_debug "No key detected"
unset _panos_key
_debug "Attempting to load variable PANOS_KEY from file."
_getdeployconf PANOS_KEY
fi
# PANOS_TEMPLATE
@ -256,6 +255,7 @@ panos_deploy() {
_panos_host=$PANOS_HOST
_panos_user=$PANOS_USER
_panos_pass=$PANOS_PASS
_panos_key=$PANOS_KEY
_panos_template=$PANOS_TEMPLATE
_panos_template_stack=$PANOS_TEMPLATE_STACK
_panos_vsys=$PANOS_VSYS
@ -271,12 +271,6 @@ panos_deploy() {
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
# Use certificate name based on the first domain on the certificate if no custom certificate name is set
if [ -z "$_panos_certname" ]; then
@ -286,6 +280,13 @@ panos_deploy() {
# Generate a new API key if no valid API key is found
if [ -z "$_panos_key" ]; then
if [ -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
fi
_debug "**** Generating new PANOS API KEY ****"
deployer keygen
_savedeployconf PANOS_KEY "$_panos_key" 1

6
deploy/proxmoxbs.sh

@ -116,13 +116,15 @@ HEREDOC
export HTTPS_INSECURE=1
export _H1="Authorization: PBSAPIToken=${_proxmoxbs_header_api_token}"
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
response="$(echo "$response" | _json_decode | _normalizeJson)"
message=$(echo "$response" | _egrep_o '"message":"[^"]*' | cut -d : -f 2 | tr -d '"')
_retval=$?
if [ "${_retval}" -eq 0 ]; then
if [ "${_retval}" -eq 0 ] && [ -z "$message" ]; then
_debug3 response "$response"
_info "Certificate successfully deployed"
return 0
else
_err "Certificate deployment failed"
_err "Certificate deployment failed: $message"
_debug "Response" "$response"
return 1
fi

6
deploy/proxmoxve.sh

@ -128,13 +128,15 @@ HEREDOC
export HTTPS_INSECURE=1
export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
response="$(echo "$response" | _json_decode | _normalizeJson)"
message=$(echo "$response" | _egrep_o '"message":"[^"]*' | cut -d : -f 2 | tr -d '"')
_retval=$?
if [ "${_retval}" -eq 0 ]; then
if [ "${_retval}" -eq 0 ] && [ -z "$message" ]; then
_debug3 response "$response"
_info "Certificate successfully deployed"
return 0
else
_err "Certificate deployment failed"
_err "Certificate deployment failed: $message"
_debug "Response" "$response"
return 1
fi

2
deploy/ssh.sh

@ -238,6 +238,8 @@ then rm -rf \"\$fn\"; echo \"Backup \$fn deleted as older than 180 days\"; fi; d
return $_err_code
fi
else
# If file doesn't exist, create it and change its permissions.
_cmdstr="$_cmdstr test ! -f $DEPLOY_SSH_KEYFILE && touch $DEPLOY_SSH_KEYFILE && chmod 600 $DEPLOY_SSH_KEYFILE;"
# ssh echo to the file
_cmdstr="$_cmdstr echo \"$(cat "$_ckey")\" > $DEPLOY_SSH_KEYFILE;"
_info "will copy private key to remote file $DEPLOY_SSH_KEYFILE"

6
dnsapi/dns_cyon.sh

@ -332,11 +332,11 @@ _cyon_get_response_message() {
}
_cyon_get_response_status() {
_egrep_o '"status":[a-zA-z0-9]*' | cut -d : -f 2
_egrep_o '"status":[a-zA-Z0-9]*' | cut -d : -f 2
}
_cyon_get_validation_status() {
_egrep_o '"valid":[a-zA-z0-9]*' | cut -d : -f 2
_egrep_o '"valid":[a-zA-Z0-9]*' | cut -d : -f 2
}
_cyon_get_response_success() {
@ -344,7 +344,7 @@ _cyon_get_response_success() {
}
_cyon_get_environment_change_status() {
_egrep_o '"authenticated":[a-zA-z0-9]*' | cut -d : -f 2
_egrep_o '"authenticated":[a-zA-Z0-9]*' | cut -d : -f 2
}
_cyon_check_if_2fa_missed() {

103
dnsapi/dns_infomaniak.sh

@ -6,14 +6,16 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_infomaniak
Options:
INFOMANIAK_API_TOKEN API Token
Issues: github.com/acmesh-official/acme.sh/issues/3188
'
# To use this API you need visit the API dashboard of your account
# once logged into https://manager.infomaniak.com add /api/dashboard to the URL
#
# To use this API you need visit the API dashboard of your account.
# Note: the URL looks like this:
# https://manager.infomaniak.com/v3/<account_id>/api/dashboard
# Then generate a token with the scope Domain
# https://manager.infomaniak.com/v3/<account_id>/ng/profile/user/token/list
# Then generate a token with following scopes :
# - domain:read
# - dns:read
# - dns:write
# this is given as an environment variable INFOMANIAK_API_TOKEN
# base variables
@ -65,33 +67,32 @@ dns_infomaniak_add() {
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
fqdn=${fulldomain#_acme-challenge.}
# guess which base domain to add record to
zone_and_id=$(_find_zone "$fqdn")
if [ -z "$zone_and_id" ]; then
_err "cannot find zone to modify"
zone=$(_get_zone "$fulldomain")
if [ -z "$zone" ]; then
_err "cannot find zone:<${zone}> to modify"
return 1
fi
zone=${zone_and_id% *}
domain_id=${zone_and_id#* }
# extract first part of domain
key=${fulldomain%."$zone"}
_debug "zone:$zone id:$domain_id key:$key"
_debug "key:$key"
_debug "txtvalue: $txtvalue"
# payload
data="{\"type\": \"TXT\", \"source\": \"$key\", \"target\": \"$txtvalue\", \"ttl\": $INFOMANIAK_TTL}"
# API call
response=$(_post "$data" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record")
if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then
_info "Record added"
_debug "Response: $response"
return 0
response=$(_post "$data" "${INFOMANIAK_API_URL}/2/zones/${zone}/records")
if [ -n "$response" ]; then
if [ ! "$(echo "$response" | _contains '"result":"success"')" ]; then
_info "Record added"
_debug "response: $response"
return 0
fi
fi
_err "could not create record"
_err "Could not create record."
_debug "Response: $response"
return 1
}
@ -106,7 +107,7 @@ dns_infomaniak_rm() {
if [ -z "$INFOMANIAK_API_TOKEN" ]; then
INFOMANIAK_API_TOKEN=""
_err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN"
_err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN."
return 1
fi
@ -138,63 +139,53 @@ dns_infomaniak_rm() {
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
fqdn=${fulldomain#_acme-challenge.}
# guess which base domain to add record to
zone_and_id=$(_find_zone "$fqdn")
if [ -z "$zone_and_id" ]; then
_err "cannot find zone to modify"
zone=$(_get_zone "$fulldomain")
if [ -z "$zone" ]; then
_err "cannot find zone:<$zone> to modify"
return 1
fi
zone=${zone_and_id% *}
domain_id=${zone_and_id#* }
# extract first part of domain
key=${fulldomain%."$zone"}
key=$(echo "$key" | _lower_case)
_debug "zone:$zone id:$domain_id key:$key"
_debug "zone:$zone"
_debug "key:$key"
# find previous record
# shellcheck disable=SC1004
record_id=$(_get "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record" | sed 's/.*"data":\[\(.*\)\]}/\1/; s/},{/}\
{/g' | sed -n 's/.*"id":"*\([0-9]*\)"*.*"source_idn":"'"$fulldomain"'".*"target_idn":"'"$txtvalue"'".*/\1/p')
# shellcheck disable=SC2086
response=$(_get "${INFOMANIAK_API_URL}/2/zones/${zone}/records" | sed 's/.*"data":\[\(.*\)\]}/\1/; s/},{/}{/g')
record_id=$(echo "$response" | sed -n 's/.*"id":"*\([0-9]*\)"*.*"source":"'"$key"'".*"target":"\\"'"$txtvalue"'\\"".*/\1/p')
_debug "key: $key"
_debug "txtvalue: $txtvalue"
_debug "record_id: $record_id"
if [ -z "$record_id" ]; then
_err "could not find record to delete"
_debug "response: $response"
return 1
fi
_debug "record_id: $record_id"
# API call
response=$(_post "" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record/$record_id" "" DELETE)
if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then
_info "Record deleted"
return 0
response=$(_post "" "${INFOMANIAK_API_URL}/2/zones/${zone}/records/${record_id}" "" DELETE)
if [ -n "$response" ]; then
if [ ! "$(echo "$response" | _contains '"result":"success"')" ]; then
_info "Record deleted"
return 0
fi
fi
_err "could not delete record"
_err "Could not delete record."
_debug "Response: $response"
return 1
}
#################### Private functions below ##################################
_get_domain_id() {
_get_zone() {
domain="$1"
# Whatever the domain is, you can get the fqdn with the following.
# shellcheck disable=SC1004
_get "${INFOMANIAK_API_URL}/1/product?service_name=domain&customer_name=$domain" | sed 's/.*"data":\[{\(.*\)}\]}/\1/; s/,/\
/g' | sed -n 's/^"id":\(.*\)/\1/p'
}
_find_zone() {
zone="$1"
# find domain in list, removing . parts sequentialy
while _contains "$zone" '\.'; do
_debug "testing $zone"
id=$(_get_domain_id "$zone")
if [ -n "$id" ]; then
echo "$zone $id"
return
fi
zone=${zone#*.}
done
response=$(_get "${INFOMANIAK_API_URL}/2/domains/${domain}/zones" | sed 's/.*\[{"fqdn"\:"\(.*\)/\1/')
echo "${response%%\"*}"
}

2
dnsapi/dns_me.sh

@ -53,6 +53,8 @@ dns_me_add() {
_info "Added"
#todo: check if the record takes effect
return 0
elif printf -- "%s" "$response" | grep -q "already exists"; then
_info "Record already exists, skipping."
else
_err "Add txt record error."
return 1

105
dnsapi/dns_nsupdate.sh

@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_nsupdate
Options:
NSUPDATE_SERVER Server hostname. Default: "localhost".
NSUPDATE_SERVER_PORT Server port. Default: "53".
NSUPDATE_KEY File path to TSIG key.
NSUPDATE_KEY File path to TSIG key. Default: "". Optional.
NSUPDATE_ZONE Domain zone to update. Optional.
'
@ -22,8 +22,6 @@ dns_nsupdate_add() {
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
NSUPDATE_OPT="${NSUPDATE_OPT:-$(_readaccountconf_mutable NSUPDATE_OPT)}"
_checkKeyFile || return 1
# save the dns server and key to the account conf file.
_saveaccountconf_mutable NSUPDATE_SERVER "${NSUPDATE_SERVER}"
_saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}"
@ -33,27 +31,52 @@ dns_nsupdate_add() {
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
[ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
[ -n "${NSUPDATE_KEY}" ] || NSUPDATE_KEY=""
[ -n "${NSUPDATE_OPT}" ] || NSUPDATE_OPT=""
NSUPDATE_SERVER_LIST=$(printf "%s" "$NSUPDATE_SERVER" | tr ',' ' ')
_info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
for NS_SERVER in $NSUPDATE_SERVER_LIST; do
_info "Updating DNS server: $NS_SERVER"
if [ -z "${NSUPDATE_ZONE}" ]; then
#shellcheck disable=SC2086
if [ -z "${NSUPDATE_KEY}" ]; then
nsupdate $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
update add ${fulldomain}. 60 in txt "${txtvalue}"
send
EOF
else
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
update add ${fulldomain}. 60 in txt "${txtvalue}"
send
EOF
fi
else
#shellcheck disable=SC2086
if [ -z "${NSUPDATE_KEY}" ]; then
nsupdate $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}.
update add ${fulldomain}. 60 in txt "${txtvalue}"
send
EOF
fi
else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}.
update add ${fulldomain}. 60 in txt "${txtvalue}"
send
EOF
fi
fi
done
if [ $? -ne 0 ]; then
_err "error updating domain"
return 1
@ -72,28 +95,53 @@ dns_nsupdate_rm() {
NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
NSUPDATE_OPT="${NSUPDATE_OPT:-$(_readaccountconf_mutable NSUPDATE_OPT)}"
_checkKeyFile || return 1
[ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
[ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
[ -n "${NSUPDATE_KEY}" ] || NSUPDATE_KEY=""
NSUPDATE_SERVER_LIST=$(printf "%s" "$NSUPDATE_SERVER" | tr ',' ' ')
_info "removing ${fulldomain}. txt"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
[ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
if [ -z "${NSUPDATE_ZONE}" ]; then
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
for NS_SERVER in $NSUPDATE_SERVER_LIST; do
_info "Updating DNS server: $NS_SERVER"
if [ -z "${NSUPDATE_ZONE}" ]; then
#shellcheck disable=SC2086
if [ -z "${NSUPDATE_KEY}" ]; then
nsupdate $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
update delete ${fulldomain}. txt
send
EOF
else
#shellcheck disable=SC2086
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NSUPDATE_SERVER} ${NSUPDATE_SERVER_PORT}
else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
update delete ${fulldomain}. txt
send
EOF
fi
else
#shellcheck disable=SC2086
if [ -z "${NSUPDATE_KEY}" ]; then
nsupdate $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}.
update delete ${fulldomain}. txt
send
EOF
fi
else
nsupdate -k "${NSUPDATE_KEY}" $nsdebug $NSUPDATE_OPT <<EOF
server ${NS_SERVER} ${NSUPDATE_SERVER_PORT}
zone ${NSUPDATE_ZONE}.
update delete ${fulldomain}. txt
send
EOF
fi
fi
done
if [ $? -ne 0 ]; then
_err "error updating domain"
return 1
@ -101,16 +149,3 @@ EOF
return 0
}
#################### Private functions below ##################################
_checkKeyFile() {
if [ -z "${NSUPDATE_KEY}" ]; then
_err "you must specify a path to the nsupdate key file"
return 1
fi
if [ ! -r "${NSUPDATE_KEY}" ]; then
_err "key ${NSUPDATE_KEY} is unreadable"
return 1
fi
}

158
dnsapi/dns_opusdns.sh

@ -0,0 +1,158 @@
#!/usr/bin/env sh
# shellcheck disable=SC2034
dns_opusdns_info='OpusDNS.com
Site: OpusDNS.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_opusdns
Options:
OPUSDNS_API_Key API Key. Can be created at https://dashboard.opusdns.com/settings/api-keys
OPUSDNS_API_Endpoint API Endpoint URL. Default "https://api.opusdns.com". Optional.
OPUSDNS_TTL TTL for DNS challenge records in seconds. Default "60". Optional.
Issues: github.com/acmesh-official/acme.sh/issues/XXXX
Author: OpusDNS Team <https://github.com/opusdns>
'
OPUSDNS_API_Endpoint_Default="https://api.opusdns.com"
OPUSDNS_TTL_Default=60
######## Public functions ###########
# Add DNS TXT record
dns_opusdns_add() {
fulldomain=$1
txtvalue=$2
_info "Using OpusDNS DNS API"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _opusdns_init; then
return 1
fi
if ! _get_zone "$fulldomain"; then
return 1
fi
_info "Zone: $_zone, Record: $_record_name"
if ! _opusdns_api PATCH "/v1/dns/$_zone/records" "{\"ops\":[{\"op\":\"upsert\",\"record\":{\"name\":\"$_record_name\",\"type\":\"TXT\",\"ttl\":$OPUSDNS_TTL,\"rdata\":\"\\\"$txtvalue\\\"\"}}]}"; then
_err "Failed to add TXT record"
return 1
fi
_info "TXT record added successfully"
return 0
}
# Remove DNS TXT record
dns_opusdns_rm() {
fulldomain=$1
txtvalue=$2
_info "Removing OpusDNS DNS record"
_debug fulldomain "$fulldomain"
_debug txtvalue "$txtvalue"
if ! _opusdns_init; then
return 1
fi
if ! _get_zone "$fulldomain"; then
_err "Zone not found, cleanup skipped"
return 0
fi
_info "Zone: $_zone, Record: $_record_name"
if ! _opusdns_api PATCH "/v1/dns/$_zone/records" "{\"ops\":[{\"op\":\"remove\",\"record\":{\"name\":\"$_record_name\",\"type\":\"TXT\",\"ttl\":$OPUSDNS_TTL,\"rdata\":\"\\\"$txtvalue\\\"\"}}]}"; then
_err "Warning: Failed to remove TXT record"
return 0
fi
_info "TXT record removed successfully"
return 0
}
######## Private functions ###########
# Initialize and validate configuration
_opusdns_init() {
OPUSDNS_API_Key="${OPUSDNS_API_Key:-$(_readaccountconf_mutable OPUSDNS_API_Key)}"
OPUSDNS_API_Endpoint="${OPUSDNS_API_Endpoint:-$(_readaccountconf_mutable OPUSDNS_API_Endpoint)}"
OPUSDNS_TTL="${OPUSDNS_TTL:-$(_readaccountconf_mutable OPUSDNS_TTL)}"
if [ -z "$OPUSDNS_API_Key" ]; then
_err "OPUSDNS_API_Key not set"
return 1
fi
[ -z "$OPUSDNS_API_Endpoint" ] && OPUSDNS_API_Endpoint="$OPUSDNS_API_Endpoint_Default"
[ -z "$OPUSDNS_TTL" ] && OPUSDNS_TTL="$OPUSDNS_TTL_Default"
_saveaccountconf_mutable OPUSDNS_API_Key "$OPUSDNS_API_Key"
_saveaccountconf_mutable OPUSDNS_API_Endpoint "$OPUSDNS_API_Endpoint"
_saveaccountconf_mutable OPUSDNS_TTL "$OPUSDNS_TTL"
_debug "Endpoint: $OPUSDNS_API_Endpoint"
return 0
}
# Make API request
# Usage: _opusdns_api METHOD PATH [DATA]
_opusdns_api() {
method=$1
path=$2
data=$3
export _H1="X-Api-Key: $OPUSDNS_API_Key"
export _H2="Content-Type: application/json"
url="$OPUSDNS_API_Endpoint$path"
_debug2 "API: $method $url"
[ -n "$data" ] && _debug2 "Data: $data"
if [ -n "$data" ]; then
response=$(_post "$data" "$url" "" "$method")
else
response=$(_get "$url")
fi
if [ $? -ne 0 ]; then
_err "API request failed"
_debug "Response: $response"
return 1
fi
_debug2 "Response: $response"
return 0
}
# Detect zone from FQDN
# Sets: _zone, _record_name
_get_zone() {
domain=$(echo "$1" | sed 's/\.$//')
_debug "Finding zone for: $domain"
i=1
p=1
while true; do
h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
if [ -z "$h" ]; then
_err "No valid zone found for: $domain"
return 1
fi
_debug "Trying: $h"
if _opusdns_api GET "/v1/dns/$h" && _contains "$response" '"dnssec_status"'; then
_zone="$h"
_record_name=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
[ -z "$_record_name" ] && _record_name="@"
return 0
fi
p="$i"
i=$(_math "$i" + 1)
done
}

14
dnsapi/dns_technitium.sh

@ -6,6 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_technitium
Options:
Technitium_Server Server Address
Technitium_Token API Token
Technitium_Expiry_Ttl Number of seconds before DNS server auto-deletes the acme record
Issues: github.com/acmesh-official/acme.sh/issues/6116
Author: Henning Reich <acmesh@qupfer.de>
'
@ -15,7 +16,10 @@ dns_technitium_add() {
_Technitium_account
fulldomain=$1
txtvalue=$2
response="$(_get "$Technitium_Server/api/zones/records/add?token=$Technitium_Token&domain=$fulldomain&type=TXT&text=${txtvalue}")"
expiryTtl=${Technitium_Expirty_Ttl:-$(_readaccountconf_mutable Technitium_Expiry_Ttl)}
expiryTtl=${expiryTtl:-0}
response="$(_get "$Technitium_Server/api/zones/records/add?token=$Technitium_Token&domain=$fulldomain&type=TXT&text=${txtvalue}&expiryTtl=$expiryTtl")"
if _contains "$response" '"status":"ok"'; then
return 0
fi
@ -28,6 +32,14 @@ dns_technitium_rm() {
_Technitium_account
fulldomain=$1
txtvalue=$2
expiryTtl=${Technitium_Expirty_Ttl:-$(_readaccountconf_mutable Technitium_Expiry_Ttl)}
expiryTtl=${expiryTtl:-0}
if [ "$expiryTtl" -ne 0 ]; then
_info "DNS record is configured to be auto-removed after $expiryTtl seconds. Remove operation is bypassed."
return 0
fi
response="$(_get "$Technitium_Server/api/zones/records/delete?token=$Technitium_Token&domain=$fulldomain&type=TXT&text=${txtvalue}")"
if _contains "$response" '"status":"ok"'; then
return 0

13
notify/telegram.sh

@ -5,6 +5,10 @@
#TELEGRAM_BOT_APITOKEN=""
#TELEGRAM_BOT_CHATID=""
#TELEGRAM_BOT_URLBASE=""
#TELEGRAM_BOT_THREADID=""
# To get TELEGRAM_BOT_THREADID, just copy the link of the message from the thread.
# https://t.me/c/123456789/XXX/1520 - XXX is the TELEGRAM_BOT_THREADID
telegram_send() {
_subject="$1"
@ -28,6 +32,12 @@ telegram_send() {
fi
_saveaccountconf_mutable TELEGRAM_BOT_CHATID "$TELEGRAM_BOT_CHATID"
TELEGRAM_BOT_THREADID="${TELEGRAM_BOT_THREADID:-$(_readaccountconf_mutable TELEGRAM_BOT_THREADID)}"
if [ -z "$TELEGRAM_BOT_THREADID" ]; then
TELEGRAM_BOT_THREADID=""
fi
_saveaccountconf_mutable TELEGRAM_BOT_THREADID "$TELEGRAM_BOT_THREADID"
TELEGRAM_BOT_URLBASE="${TELEGRAM_BOT_URLBASE:-$(_readaccountconf_mutable TELEGRAM_BOT_URLBASE)}"
if [ -z "$TELEGRAM_BOT_URLBASE" ]; then
TELEGRAM_BOT_URLBASE="https://api.telegram.org"
@ -39,6 +49,9 @@ telegram_send() {
_content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)"
_data="{\"text\": \"$_content\", "
_data="$_data\"chat_id\": \"$TELEGRAM_BOT_CHATID\", "
if [ -n "$TELEGRAM_BOT_THREADID" ]; then
_data="$_data\"message_thread_id\": \"$TELEGRAM_BOT_THREADID\", "
fi
_data="$_data\"parse_mode\": \"MarkdownV2\", "
_data="$_data\"disable_web_page_preview\": \"1\"}"

Loading…
Cancel
Save