committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 653 additions and 185 deletions
-
67.github/copilot-instructions.md
-
38.github/workflows/DNS.yml
-
6.github/workflows/DragonFlyBSD.yml
-
6.github/workflows/FreeBSD.yml
-
6.github/workflows/Haiku.yml
-
9.github/workflows/Linux.yml
-
9.github/workflows/MacOS.yml
-
6.github/workflows/NetBSD.yml
-
6.github/workflows/Omnios.yml
-
6.github/workflows/OpenBSD.yml
-
6.github/workflows/OpenIndiana.yml
-
4.github/workflows/PebbleStrict.yml
-
6.github/workflows/Solaris.yml
-
2.github/workflows/Ubuntu.yml
-
9.github/workflows/Windows.yml
-
2.github/workflows/dockerhub.yml
-
4.github/workflows/shellcheck.yml
-
2.github/workflows/wiki-monitor.yml
-
19Dockerfile
-
27acme.sh
-
3deploy/kemplm.sh
-
157deploy/localcopy.sh
-
23deploy/panos.sh
-
6deploy/proxmoxbs.sh
-
6deploy/proxmoxve.sh
-
2deploy/ssh.sh
-
6dnsapi/dns_cyon.sh
-
103dnsapi/dns_infomaniak.sh
-
2dnsapi/dns_me.sh
-
105dnsapi/dns_nsupdate.sh
-
158dnsapi/dns_opusdns.sh
-
14dnsapi/dns_technitium.sh
-
13notify/telegram.sh
@ -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. |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
@ -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 |
|||
} |
|||
@ -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 |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue