#!/bin/bash # ============================================================================== # Script: create_pma_gateway.sh # Purpose: Create a time-limited gateway URL for phpMyAdmin on Virtuozzo LLSMP. # Usage: create_pma_gateway.sh --validity=30 [--slug=myalias] # Outputs: Prints the generated URL. # ============================================================================== set -euo pipefail SLUG="" VALIDITY=30 # minutes for arg in "$@"; do case $arg in --slug=*) SLUG="${arg#*=}" ;; --validity=*) VALIDITY="${arg#*=}" ;; *) echo "Unknown argument $arg"; exit 1 ;; esac done if [[ -z "$SLUG" ]]; then SLUG=$(openssl rand -hex 4) # 8-char random fi # Determine environment public host (no node prefix) if [[ -n "${JELASTIC_ENV_DOMAIN:-}" ]]; then ENV_HOST="$JELASTIC_ENV_DOMAIN" else ENV_HOST=$(hostname -f) ENV_HOST=${ENV_HOST#node*-} # strip nodeXXXX- fi # ============================================================================== # Step 1: Ensure xmlstarlet is installed for safe XML parsing # ============================================================================== if ! command -v xmlstarlet &> /dev/null; then echo "xmlstarlet not found. Installing for safe XML parsing..." >&2 if ! sudo dnf install -y xmlstarlet; then echo "FATAL: Failed to install xmlstarlet. Cannot safely read LiteSpeed config." >&2 exit 1 fi fi # ============================================================================== # Step 2: Dynamically read SSL configuration from main LiteSpeed config # ============================================================================== LITESPEED_CONFIG="/var/www/conf/httpd_config.xml" KEY_FILE_PATH="" CERT_FILE_PATH="" if [[ -f "$LITESPEED_CONFIG" ]]; then echo "Reading SSL configuration from LiteSpeed main config..." >&2 # Query the main HTTPS listener (port 443) for keyFile and certFile # This is the most specific and robust XPath KEY_FILE_PATH=$(sudo xmlstarlet sel -t -v \ "//httpServerConfig/listenerList/listener[name='HTTPS' and secure='1' and address='*:443'][1]/keyFile" \ "$LITESPEED_CONFIG" 2>/dev/null | xargs) CERT_FILE_PATH=$(sudo xmlstarlet sel -t -v \ "//httpServerConfig/listenerList/listener[name='HTTPS' and secure='1' and address='*:443'][1]/certFile" \ "$LITESPEED_CONFIG" 2>/dev/null | xargs) fi # ============================================================================== # Step 3: Implement fallback to default self-signed certificate # ============================================================================== if [[ -z "$KEY_FILE_PATH" ]] || [[ -z "$CERT_FILE_PATH" ]]; then echo "No custom SSL certificate found. Falling back to default self-signed certificate." >&2 # Use SINGLE quotes to write the literal string "$SERVER_ROOT" to the config, # not the shell variable. This is critical. KEY_FILE_PATH='$SERVER_ROOT/ssl/litespeed.key' CERT_FILE_PATH='$SERVER_ROOT/ssl/litespeed.crt' else echo "Using SSL certificate: $CERT_FILE_PATH" >&2 fi PMADB_DIR="/usr/share/phpMyAdmin" GATEWAY_FILE="$PMADB_DIR/access-db-$SLUG.php" SECRET_FILE="/var/lib/jelastic/keys/mbadmin_secret" sudo mkdir -p "$(dirname $SECRET_FILE)" if [[ ! -f "$SECRET_FILE" ]]; then sudo sh -c "openssl rand -hex 32 > $SECRET_FILE" fi sudo chown litespeed:litespeed "$SECRET_FILE" sudo chmod 644 "$SECRET_FILE" SECRET=$(sudo cat "$SECRET_FILE" | xargs) now=$(date +%s) expires=$((now + VALIDITY*60)) # token = base64("$SLUG:$expires") . '.' . HMAC_SHA256(secret, data) data="$SLUG:$expires" base=$(printf "%s" "$data" | base64 | tr -d '\n') mac=$(php -r "echo hash_hmac('sha256', '$data', '$SECRET');") token="$base.$mac" # Secure the phpMyAdmin vhost with Rewrite Rules to block direct access VHOST_CONFIG="/usr/share/phpMyAdmin/vhost.conf" NEEDS_RESTART=0 # If vhost config is missing or empty, recreate it from a known-good default. if [ ! -s "$VHOST_CONFIG" ]; then echo "Warning: $VHOST_CONFIG is empty or missing. Recreating from default." >&2 sudo tee "$VHOST_CONFIG" > /dev/null <<'EOF' /usr/share/phpMyAdmin/ 1 0 $SERVER_ROOT/logs/error.log DEBUG 10M 0 $SERVER_ROOT/logs/access.log 10M 30 0 0 index.php, index.html 0 /_autoindex/default.php 404 /error404.html 31 .htaccess 1 0 0 gif, jpeg, jpg 1 1 * 10 /tmp/lscache/vhosts/$VH_NAME 0 0 RewriteCond %{HTTP_USER_AGENT} ^NameOfBadRobot RewriteRule ^/nospider/ - [F] __KEY_FILE_PLACEHOLDER__ __CERT_FILE_PLACEHOLDER__ 1 0 0 0 $VH_ROOT/awstats /awstats/ localhost 127.0.0.1 localhost 86400 0 EOF # ============================================================================== # Step 5: Inject the discovered certificate paths using sed # ============================================================================== # Escape special characters (/, $, &, \, ') in paths for use with sed ESCAPED_KEY_PATH=$(printf '%s\n' "$KEY_FILE_PATH" | sed 's/[\/&$\\'"'"']/\\&/g') ESCAPED_CERT_PATH=$(printf '%s\n' "$CERT_FILE_PATH" | sed 's/[\/&$\\'"'"']/\\&/g') # Replace placeholders with actual certificate paths sudo sed -i "s|__KEY_FILE_PLACEHOLDER__|$ESCAPED_KEY_PATH|g" "$VHOST_CONFIG" sudo sed -i "s|__CERT_FILE_PLACEHOLDER__|$ESCAPED_CERT_PATH|g" "$VHOST_CONFIG" echo "SSL certificate paths injected into vhost configuration." >&2 NEEDS_RESTART=1 fi if [ -f "$VHOST_CONFIG" ]; then MARKER="# PMA Gateway Security Rules" # If rules are not already in place, add them. if ! sudo grep -qF "$MARKER" "$VHOST_CONFIG"; then # Ensure xmlstarlet is installed, as it's the safest way to edit XML. if ! command -v xmlstarlet &> /dev/null; then echo "xmlstarlet not found. Installing for safe XML editing..." >&2 if ! sudo dnf install -y xmlstarlet; then echo "FATAL: Failed to install xmlstarlet. Cannot safely modify vhost." >&2 exit 1 fi fi # Define the new rules content. Note the lack of indentation. # xmlstarlet will handle the formatting. NEW_RULES_CONTENT=$(cat <<'EOF' # PMA Gateway Security Rules # Allow access to the gateway scripts themselves RewriteCond %{REQUEST_URI} ^/access-db-.*\.php$ RewriteRule .* - [L] # For all other requests, block if the security cookie is not present RewriteCond %{HTTP_COOKIE} !pma_access_granted RewriteRule .* - [F,L] EOF ) # Use xmlstarlet to atomically update the rewrite block in-place. # This is far safer than sed/awk for structured XML. if ! sudo xmlstarlet ed -L \ -u "//virtualHostConfig/rewrite/enable" -v "1" \ -u "//virtualHostConfig/rewrite/rules" -v "$NEW_RULES_CONTENT" \ "$VHOST_CONFIG"; then echo "FATAL: xmlstarlet failed to update $VHOST_CONFIG." >&2 exit 1 fi NEEDS_RESTART=1 fi else echo "Warning: phpMyAdmin vhost config not found at $VHOST_CONFIG. Cannot apply security rules." >&2 fi sudo tee "$GATEWAY_FILE" >/dev/null <<'PHP' intval($exp)) { unlink(__FILE__); // Self-destruct if expired deny(); } $secret = trim(file_get_contents('/var/lib/jelastic/keys/mbadmin_secret')); if (!hash_equals($sig, hash_hmac('sha256', $data, $secret))) { deny(); } // Issue a short-lived cookie that the rewrite rule looks for. // This cookie acts as the temporary pass. setcookie('pma_access_granted', $sig, intval($exp), '/', '', true, true); header('Location: /'); exit; ?> PHP sudo chown litespeed:litespeed "$GATEWAY_FILE" sudo chmod 644 "$GATEWAY_FILE" # Restart LiteSpeed if we modified the config if [[ "${NEEDS_RESTART:-0}" -eq 1 ]]; then echo "Applying security rules and restarting LiteSpeed..." >&2 if ! sudo systemctl restart lsws; then echo "Warning: LiteSpeed restart failed. Manual restart may be required." >&2 fi fi URL="https://$ENV_HOST:8443/access-db-$SLUG.php?token=$token" echo "$URL"