From 7dd77c6ccdb5d30d64384af93e09ccb01c3d822f Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 20:30:22 +0800 Subject: [PATCH] Fix PMA gateway LE cert domain resolution --- mbadmin.jps | 2 +- scripts/pma-gateway/create_pma_gateway.sh | 118 ++++++++++++++++------ 2 files changed, 90 insertions(+), 30 deletions(-) diff --git a/mbadmin.jps b/mbadmin.jps index d78f008..06ba0d9 100644 --- a/mbadmin.jps +++ b/mbadmin.jps @@ -1029,7 +1029,7 @@ actions: user: root commands: - 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: type: info message: "Gateway URL: ${response.out}" diff --git a/scripts/pma-gateway/create_pma_gateway.sh b/scripts/pma-gateway/create_pma_gateway.sh index d608d4f..a5c0771 100644 --- a/scripts/pma-gateway/create_pma_gateway.sh +++ b/scripts/pma-gateway/create_pma_gateway.sh @@ -4,19 +4,23 @@ # Script: create_pma_gateway.sh # Purpose: Create a time-limited gateway URL for phpMyAdmin on Virtuozzo LLSMP. # Uses Let's Encrypt cert in the dedicated phpMyAdmin vhost (port 8443). -# Dynamically detects Let's Encrypt certificate paths. -# Usage: create_pma_gateway.sh --validity=30 [--slug=myalias] +# Resolves certs for the environment domain and can issue cert on demand. +# Usage: create_pma_gateway.sh --validity=30 [--slug=myalias] [--cert-domain=env.example.com] [--email=admin@example.com] # Outputs: Prints the generated URL. # ============================================================================== set -euo pipefail SLUG="" VALIDITY=30 # minutes +CERT_DOMAIN="" +CONTACT_EMAIL="" for arg in "$@"; do case $arg in --slug=*) SLUG="${arg#*=}" ;; --validity=*) VALIDITY="${arg#*=}" ;; + --cert-domain=*) CERT_DOMAIN="${arg#*=}" ;; + --email=*) CONTACT_EMAIL="${arg#*=}" ;; *) echo "ERROR: Unknown argument $arg"; exit 1 ;; esac done @@ -25,13 +29,30 @@ if [[ -z "$SLUG" ]]; then SLUG=$(openssl rand -hex 4) # 8-char random fi -# Determine environment public host (no node prefix) - used for certificate lookup and URL -if [[ -n "${JELASTIC_ENV_DOMAIN:-}" ]]; then - ENV_HOST="$JELASTIC_ENV_DOMAIN" -else - ENV_HOST=$(hostname -f) - ENV_HOST=${ENV_HOST#node*-} # strip nodeXXXX- +# Reject unresolved template placeholders if they are passed through literally. +if [[ "$CERT_DOMAIN" == *'${'* ]]; then + CERT_DOMAIN="" 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" GATEWAY_FILE="$PMADB_DIR/access-db-$SLUG.php" @@ -57,41 +78,80 @@ token="$base.$mac" VHOST_CONFIG="/usr/share/phpMyAdmin/vhost.conf" 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_CERT_DIR="" -# Attempt to find the directory matching ENV_HOST -if [[ -d "$LE_LIVE_DIR/$ENV_HOST" ]]; then +# Attempt exact match first. +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" - echo "INFO: Found Let's Encrypt cert directory matching ENV_HOST: $LE_CERT_DIR" >&2 -else - # 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. - # Or, you could add more logic here to match patterns if needed. - for dir in "$LE_LIVE_DIR"/*/; do - if [[ -d "$dir" ]]; then - candidate_dir=$(basename "$dir") - echo "INFO: Checking Let's Encrypt cert directory: $candidate_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 - fi + echo "INFO: Found exact Let's Encrypt cert directory: $LE_CERT_DIR" >&2 +fi + +# Then try certbot-suffixed directories (e.g., domain-0001). +if [[ -z "$LE_CERT_DIR" ]]; then + 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%/}" + echo "INFO: Found suffixed Let's Encrypt cert directory: $LE_CERT_DIR" >&2 + break 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 + done + fi +fi + # Set the final key and cert file paths based on the found directory if [[ -n "$LE_CERT_DIR" ]]; then LE_KEY_FILE="$LE_CERT_DIR/privkey.pem" LE_CERT_FILE="$LE_CERT_DIR/fullchain.pem" 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 other subdirectories for privkey.pem and fullchain.pem." >&2 + echo " Checked suffixed paths: $LE_LIVE_DIR/${ENV_HOST}-*" >&2 exit 1 fi