Fix PMA gateway LE cert domain resolution

main
Anthony 2026-02-26 20:30:22 +08:00
parent 20d3d016c2
commit 7dd77c6ccd
2 changed files with 90 additions and 30 deletions

View File

@ -1029,7 +1029,7 @@ actions:
user: root user: root
commands: commands:
- bash /home/litespeed/mbmanager/scripts/dbreset.sh >/dev/null - bash /home/litespeed/mbmanager/scripts/dbreset.sh >/dev/null
- bash /home/litespeed/mbmanager/pma-gateway/create_pma_gateway.sh --validity="${settings.validity}" --slug="${settings.slug}" - bash /home/litespeed/mbmanager/pma-gateway/create_pma_gateway.sh --validity="${settings.validity}" --slug="${settings.slug}" --cert-domain="${env.domain}" --email="${user.email}"
- return: - return:
type: info type: info
message: "Gateway URL: ${response.out}" message: "Gateway URL: ${response.out}"

View File

@ -4,19 +4,23 @@
# Script: create_pma_gateway.sh # Script: create_pma_gateway.sh
# Purpose: Create a time-limited gateway URL for phpMyAdmin on Virtuozzo LLSMP. # Purpose: Create a time-limited gateway URL for phpMyAdmin on Virtuozzo LLSMP.
# Uses Let's Encrypt cert in the dedicated phpMyAdmin vhost (port 8443). # Uses Let's Encrypt cert in the dedicated phpMyAdmin vhost (port 8443).
# Dynamically detects Let's Encrypt certificate paths. # Resolves certs for the environment domain and can issue cert on demand.
# Usage: create_pma_gateway.sh --validity=30 [--slug=myalias] # Usage: create_pma_gateway.sh --validity=30 [--slug=myalias] [--cert-domain=env.example.com] [--email=admin@example.com]
# Outputs: Prints the generated URL. # Outputs: Prints the generated URL.
# ============================================================================== # ==============================================================================
set -euo pipefail set -euo pipefail
SLUG="" SLUG=""
VALIDITY=30 # minutes VALIDITY=30 # minutes
CERT_DOMAIN=""
CONTACT_EMAIL=""
for arg in "$@"; do for arg in "$@"; do
case $arg in case $arg in
--slug=*) SLUG="${arg#*=}" ;; --slug=*) SLUG="${arg#*=}" ;;
--validity=*) VALIDITY="${arg#*=}" ;; --validity=*) VALIDITY="${arg#*=}" ;;
--cert-domain=*) CERT_DOMAIN="${arg#*=}" ;;
--email=*) CONTACT_EMAIL="${arg#*=}" ;;
*) echo "ERROR: Unknown argument $arg"; exit 1 ;; *) echo "ERROR: Unknown argument $arg"; exit 1 ;;
esac esac
done done
@ -25,13 +29,30 @@ if [[ -z "$SLUG" ]]; then
SLUG=$(openssl rand -hex 4) # 8-char random SLUG=$(openssl rand -hex 4) # 8-char random
fi fi
# Determine environment public host (no node prefix) - used for certificate lookup and URL # Reject unresolved template placeholders if they are passed through literally.
if [[ -n "${JELASTIC_ENV_DOMAIN:-}" ]]; then if [[ "$CERT_DOMAIN" == *'${'* ]]; then
ENV_HOST="$JELASTIC_ENV_DOMAIN" CERT_DOMAIN=""
else
ENV_HOST=$(hostname -f)
ENV_HOST=${ENV_HOST#node*-} # strip nodeXXXX-
fi fi
if [[ "$CONTACT_EMAIL" == *'${'* ]]; then
CONTACT_EMAIL=""
fi
# Prefer explicit cert domain and fallback to Jelastic environment domain.
if [[ -z "$CERT_DOMAIN" ]]; then
if [[ -n "${JELASTIC_ENV_DOMAIN:-}" ]]; then
CERT_DOMAIN="$JELASTIC_ENV_DOMAIN"
else
CERT_DOMAIN=$(hostname -f)
CERT_DOMAIN=${CERT_DOMAIN#node*-} # strip nodeXXXX-
fi
fi
# Fallback for email when not explicitly passed.
if [[ -z "$CONTACT_EMAIL" ]] && [[ -n "${JELASTIC_USER_EMAIL:-}" ]]; then
CONTACT_EMAIL="$JELASTIC_USER_EMAIL"
fi
ENV_HOST="$CERT_DOMAIN"
PMADB_DIR="/usr/share/phpMyAdmin" PMADB_DIR="/usr/share/phpMyAdmin"
GATEWAY_FILE="$PMADB_DIR/access-db-$SLUG.php" GATEWAY_FILE="$PMADB_DIR/access-db-$SLUG.php"
@ -57,41 +78,80 @@ token="$base.$mac"
VHOST_CONFIG="/usr/share/phpMyAdmin/vhost.conf" VHOST_CONFIG="/usr/share/phpMyAdmin/vhost.conf"
NEEDS_RESTART=0 NEEDS_RESTART=0
# --- MODIFICATION: Dynamically determine Let's Encrypt Cert Paths --- # --- Let's Encrypt certificate resolution for ENV_HOST ---
LE_LIVE_DIR="/etc/letsencrypt/live" LE_LIVE_DIR="/etc/letsencrypt/live"
LE_CERT_DIR="" LE_CERT_DIR=""
# Attempt to find the directory matching ENV_HOST # Attempt exact match first.
if [[ -d "$LE_LIVE_DIR/$ENV_HOST" ]]; then if [[ -d "$LE_LIVE_DIR/$ENV_HOST" ]] && [[ -f "$LE_LIVE_DIR/$ENV_HOST/privkey.pem" ]] && [[ -f "$LE_LIVE_DIR/$ENV_HOST/fullchain.pem" ]]; then
LE_CERT_DIR="$LE_LIVE_DIR/$ENV_HOST" LE_CERT_DIR="$LE_LIVE_DIR/$ENV_HOST"
echo "INFO: Found Let's Encrypt cert directory matching ENV_HOST: $LE_CERT_DIR" >&2 echo "INFO: Found exact Let's Encrypt cert directory: $LE_CERT_DIR" >&2
else fi
# If not found, iterate through subdirectories in $LE_LIVE_DIR to find a suitable one
# This is a fallback, assuming there might be only one relevant cert directory. # Then try certbot-suffixed directories (e.g., domain-0001).
# Or, you could add more logic here to match patterns if needed. if [[ -z "$LE_CERT_DIR" ]]; then
for dir in "$LE_LIVE_DIR"/*/; do for dir in "$LE_LIVE_DIR/$ENV_HOST"-*/; do
if [[ -d "$dir" ]]; then if [[ -d "$dir" ]] && [[ -f "$dir/privkey.pem" ]] && [[ -f "$dir/fullchain.pem" ]]; then
candidate_dir=$(basename "$dir") LE_CERT_DIR="${dir%/}"
echo "INFO: Checking Let's Encrypt cert directory: $candidate_dir" >&2 echo "INFO: Found suffixed Let's Encrypt cert directory: $LE_CERT_DIR" >&2
# Add more specific checks here if multiple domains exist and you need to pick the right one.
# For now, just pick the first one that has the required files.
if [[ -f "$dir/privkey.pem" ]] && [[ -f "$dir/fullchain.pem" ]]; then
LE_CERT_DIR="$dir"
echo "INFO: Found Let's Encrypt cert directory with required files: $LE_CERT_DIR" >&2
break break
fi fi
done
fi
# If no cert exists yet, attempt one-time issuance for ENV_HOST.
if [[ -z "$LE_CERT_DIR" ]]; then
echo "WARNING: No Let's Encrypt certificate found for '$ENV_HOST'. Attempting to issue one now..." >&2
CERTBOT_CMD=""
if command -v certbot >/dev/null 2>&1; then
CERTBOT_CMD="certbot"
elif [[ -x "/opt/certbot/certbot-auto" ]]; then
CERTBOT_CMD="/opt/certbot/certbot-auto"
fi
if [[ -z "$CERTBOT_CMD" ]]; then
echo "FATAL: certbot is not available and no existing Let's Encrypt certificate was found for '$ENV_HOST'." >&2
exit 1
fi
WEBROOT_PATH="/var/www/webroot/ROOT"
ACME_CHALLENGE_DIR="$WEBROOT_PATH/.well-known/acme-challenge"
sudo mkdir -p "$ACME_CHALLENGE_DIR"
if [[ -n "$CONTACT_EMAIL" ]]; then
if ! sudo "$CERTBOT_CMD" certonly --webroot -w "$WEBROOT_PATH" -d "$ENV_HOST" --non-interactive --agree-tos --email "$CONTACT_EMAIL"; then
echo "FATAL: Failed to issue Let's Encrypt certificate for '$ENV_HOST' using contact email '$CONTACT_EMAIL'." >&2
exit 1
fi
else
if ! sudo "$CERTBOT_CMD" certonly --webroot -w "$WEBROOT_PATH" -d "$ENV_HOST" --non-interactive --agree-tos --register-unsafely-without-email; then
echo "FATAL: Failed to issue Let's Encrypt certificate for '$ENV_HOST' without contact email." >&2
exit 1
fi
fi
# Re-check exact and suffixed certificate directories after issuance.
if [[ -d "$LE_LIVE_DIR/$ENV_HOST" ]] && [[ -f "$LE_LIVE_DIR/$ENV_HOST/privkey.pem" ]] && [[ -f "$LE_LIVE_DIR/$ENV_HOST/fullchain.pem" ]]; then
LE_CERT_DIR="$LE_LIVE_DIR/$ENV_HOST"
else
for dir in "$LE_LIVE_DIR/$ENV_HOST"-*/; do
if [[ -d "$dir" ]] && [[ -f "$dir/privkey.pem" ]] && [[ -f "$dir/fullchain.pem" ]]; then
LE_CERT_DIR="${dir%/}"
break
fi fi
done done
fi fi
fi
# Set the final key and cert file paths based on the found directory # Set the final key and cert file paths based on the found directory
if [[ -n "$LE_CERT_DIR" ]]; then if [[ -n "$LE_CERT_DIR" ]]; then
LE_KEY_FILE="$LE_CERT_DIR/privkey.pem" LE_KEY_FILE="$LE_CERT_DIR/privkey.pem"
LE_CERT_FILE="$LE_CERT_DIR/fullchain.pem" LE_CERT_FILE="$LE_CERT_DIR/fullchain.pem"
else else
echo "FATAL: Let's Encrypt certificate directory could not be found in $LE_LIVE_DIR for ENV_HOST: $ENV_HOST" >&2 echo "FATAL: Let's Encrypt certificate directory could not be found for ENV_HOST: $ENV_HOST" >&2
echo " Checked specific path: $LE_LIVE_DIR/$ENV_HOST" >&2 echo " Checked specific path: $LE_LIVE_DIR/$ENV_HOST" >&2
echo " Checked other subdirectories for privkey.pem and fullchain.pem." >&2 echo " Checked suffixed paths: $LE_LIVE_DIR/${ENV_HOST}-*" >&2
exit 1 exit 1
fi fi