2025-08-26 16:26:33 +00:00
|
|
|
|
#!/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.
|
2025-10-20 17:51:39 +00:00
|
|
|
|
#
|
|
|
|
|
|
# SECURITY FEATURES:
|
|
|
|
|
|
# - Input validation prevents path traversal and injection attacks
|
|
|
|
|
|
# - Secret key handling avoids shell expansion vulnerabilities
|
|
|
|
|
|
# - HMAC-signed tokens with time-based expiration
|
|
|
|
|
|
# - Automatic SSL certificate detection and configuration
|
|
|
|
|
|
# - Comprehensive rewrite rules for phpMyAdmin protection
|
|
|
|
|
|
# - Secure cookie-based access control
|
|
|
|
|
|
# - Self-destructing gateway files
|
2025-08-26 16:26:33 +00:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# Prerequisites Check: Verify required sudo privileges
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
if ! sudo -n true 2>/dev/null; then
|
|
|
|
|
|
echo "ERROR: This script requires sudo privileges for:" >&2
|
|
|
|
|
|
echo " - File operations (chown, chmod, mkdir, ln)" >&2
|
|
|
|
|
|
echo " - Package installation (dnf install xmlstarlet)" >&2
|
|
|
|
|
|
echo " - Configuration management (xmlstarlet)" >&2
|
|
|
|
|
|
echo " - Service control (systemctl restart lsws)" >&2
|
|
|
|
|
|
echo "Please run with appropriate sudo access or configure passwordless sudo." >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-08-26 16:26:33 +00:00
|
|
|
|
SLUG=""
|
|
|
|
|
|
VALIDITY=30 # minutes
|
|
|
|
|
|
|
|
|
|
|
|
for arg in "$@"; do
|
|
|
|
|
|
case $arg in
|
|
|
|
|
|
--slug=*) SLUG="${arg#*=}" ;;
|
|
|
|
|
|
--validity=*) VALIDITY="${arg#*=}" ;;
|
2025-10-20 17:51:39 +00:00
|
|
|
|
*) echo "ERROR: Unknown argument $arg"; exit 1 ;;
|
2025-08-26 16:26:33 +00:00
|
|
|
|
esac
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
if [[ -z "$SLUG" ]]; then
|
|
|
|
|
|
SLUG=$(openssl rand -hex 4) # 8-char random
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# Input Validation: Validate command-line arguments for security and robustness
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
# Validate VALIDITY: must be a positive integer
|
|
|
|
|
|
if ! [[ "$VALIDITY" =~ ^[0-9]+$ ]] || (( VALIDITY <= 0 )); then
|
|
|
|
|
|
echo "ERROR: --validity must be a positive integer (e.g., --validity=30)" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# Validate SLUG: allow only alphanumeric characters, hyphens, and underscores
|
|
|
|
|
|
# This prevents path traversal sequences (../) and special character injection
|
|
|
|
|
|
if ! [[ "$SLUG" =~ ^[a-zA-Z0-9_-]{1,64}$ ]]; then
|
|
|
|
|
|
echo "ERROR: --slug must contain only alphanumeric characters, hyphens, or underscores (max 64 chars)" >&2
|
|
|
|
|
|
exit 1
|
2025-08-26 16:26:33 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# Determine public hostname for gateway URL (main domain with valid SSL cert)
|
|
|
|
|
|
# In Jelastic environments, JELASTIC_ENV_DOMAIN is always the canonical public domain
|
|
|
|
|
|
if [[ -z "${JELASTIC_ENV_DOMAIN:-}" ]]; then
|
|
|
|
|
|
echo "FATAL: JELASTIC_ENV_DOMAIN not set. Cannot determine public hostname." >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
PUBLIC_HOST="$JELASTIC_ENV_DOMAIN"
|
|
|
|
|
|
|
|
|
|
|
|
# Use standard Jelastic LLSMP main document root
|
|
|
|
|
|
MAIN_DOCROOT="/var/www/webroot/ROOT"
|
|
|
|
|
|
|
2025-10-20 15:36:39 +00:00
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# 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
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# Correctly capture the output of xmlstarlet without modification
|
2025-10-20 15:36:39 +00:00
|
|
|
|
KEY_FILE_PATH=$(sudo xmlstarlet sel -t -v \
|
|
|
|
|
|
"//httpServerConfig/listenerList/listener[name='HTTPS' and secure='1' and address='*:443'][1]/keyFile" \
|
2025-10-20 17:51:39 +00:00
|
|
|
|
"$LITESPEED_CONFIG" 2>/dev/null)
|
2025-10-20 15:36:39 +00:00
|
|
|
|
|
|
|
|
|
|
CERT_FILE_PATH=$(sudo xmlstarlet sel -t -v \
|
|
|
|
|
|
"//httpServerConfig/listenerList/listener[name='HTTPS' and secure='1' and address='*:443'][1]/certFile" \
|
2025-10-20 17:51:39 +00:00
|
|
|
|
"$LITESPEED_CONFIG" 2>/dev/null)
|
2025-10-20 15:36:39 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
2025-08-26 16:26:33 +00:00
|
|
|
|
PMADB_DIR="/usr/share/phpMyAdmin"
|
|
|
|
|
|
GATEWAY_FILE="$PMADB_DIR/access-db-$SLUG.php"
|
2025-10-20 17:51:39 +00:00
|
|
|
|
PUBLIC_GATEWAY_FILE="$MAIN_DOCROOT/access-db-$SLUG.php"
|
2025-08-26 16:26:33 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
2025-08-26 17:27:43 +00:00
|
|
|
|
sudo chown litespeed:litespeed "$SECRET_FILE"
|
|
|
|
|
|
sudo chmod 644 "$SECRET_FILE"
|
2025-10-20 17:51:39 +00:00
|
|
|
|
|
|
|
|
|
|
# Validate the secret file to ensure it contains only valid hexadecimal characters
|
|
|
|
|
|
if ! [[ "$(cat "$SECRET_FILE")" =~ ^[0-9a-f]{64}$ ]]; then
|
|
|
|
|
|
echo "ERROR: Secret file $SECRET_FILE does not contain a valid 64-character hexadecimal string." >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
2025-08-26 16:26:33 +00:00
|
|
|
|
|
|
|
|
|
|
now=$(date +%s)
|
|
|
|
|
|
expires=$((now + VALIDITY*60))
|
|
|
|
|
|
# token = base64("$SLUG:$expires") . '.' . HMAC_SHA256(secret, data)
|
|
|
|
|
|
data="$SLUG:$expires"
|
2025-08-26 17:27:43 +00:00
|
|
|
|
base=$(printf "%s" "$data" | base64 | tr -d '\n')
|
2025-10-20 17:51:39 +00:00
|
|
|
|
|
|
|
|
|
|
# Security: Use a temporary file to avoid exposing $SECRET in shell expansion
|
|
|
|
|
|
# This prevents the secret from being visible in process listings or shell history
|
|
|
|
|
|
# Note: mktemp creates files with restrictive permissions (600) by default
|
|
|
|
|
|
TEMP_SECRET_FILE=$(mktemp)
|
|
|
|
|
|
trap "rm -f '$TEMP_SECRET_FILE'" EXIT
|
|
|
|
|
|
sudo cat "$SECRET_FILE" > "$TEMP_SECRET_FILE"
|
|
|
|
|
|
chmod 600 "$TEMP_SECRET_FILE" # Ensure only owner can read/write
|
|
|
|
|
|
|
|
|
|
|
|
# Generate HMAC using PHP, reading secret directly from file to avoid injection risk
|
|
|
|
|
|
mac=$(php -r "
|
|
|
|
|
|
\$data = '$data';
|
|
|
|
|
|
\$secret = trim(file_get_contents('$SECRET_FILE'));
|
|
|
|
|
|
if (!is_string(\$secret) || empty(\$secret)) {
|
|
|
|
|
|
fwrite(STDERR, 'ERROR: Failed to read secret file\n');
|
|
|
|
|
|
exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
\$hmac = hash_hmac('sha256', \$data, \$secret);
|
|
|
|
|
|
if (!\$hmac) {
|
|
|
|
|
|
fwrite(STDERR, 'ERROR: hash_hmac() failed\n');
|
|
|
|
|
|
exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
echo \$hmac;
|
|
|
|
|
|
" 2>&1)
|
|
|
|
|
|
|
|
|
|
|
|
# Check PHP exit code and output
|
|
|
|
|
|
php_exit_code=$?
|
|
|
|
|
|
if [[ $php_exit_code -ne 0 ]] || [[ -z "$mac" ]] || [[ ${#mac} -ne 64 ]]; then
|
|
|
|
|
|
echo "ERROR: Failed to generate HMAC token" >&2
|
|
|
|
|
|
echo "PHP error output: $mac" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-08-26 16:26:33 +00:00
|
|
|
|
token="$base.$mac"
|
|
|
|
|
|
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# Create symlinks for phpMyAdmin and gateway in main document root
|
|
|
|
|
|
# This serves both through the main domain (port 443) with valid SSL certificate
|
|
|
|
|
|
sudo mkdir -p "$MAIN_DOCROOT"
|
|
|
|
|
|
|
|
|
|
|
|
# Symlink entire phpMyAdmin directory for full access
|
|
|
|
|
|
sudo ln -sf "/usr/share/phpMyAdmin" "$MAIN_DOCROOT/phpmyadmin"
|
|
|
|
|
|
|
|
|
|
|
|
# Symlink the gateway script for public access
|
|
|
|
|
|
sudo ln -sf "$GATEWAY_FILE" "$PUBLIC_GATEWAY_FILE"
|
|
|
|
|
|
|
|
|
|
|
|
# Remove the phpMyAdmin vhost to avoid exposing port 8443 publicly
|
2025-08-26 17:46:27 +00:00
|
|
|
|
VHOST_CONFIG="/usr/share/phpMyAdmin/vhost.conf"
|
2025-10-20 17:51:39 +00:00
|
|
|
|
if [[ -f "$VHOST_CONFIG" ]]; then
|
|
|
|
|
|
echo "Removing phpMyAdmin vhost configuration to prevent public exposure on port 8443..." >&2
|
|
|
|
|
|
sudo rm -f "$VHOST_CONFIG"
|
2025-08-26 17:50:45 +00:00
|
|
|
|
NEEDS_RESTART=1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# Secure the phpMyAdmin vhost with Rewrite Rules to block direct access
|
|
|
|
|
|
# VHOST_CONFIG="/usr/share/phpMyAdmin/vhost.conf"
|
|
|
|
|
|
# NEEDS_RESTART=0
|
|
|
|
|
|
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
# Step 4: Automatically inject security rules into main vhost configuration
|
|
|
|
|
|
# ==============================================================================
|
|
|
|
|
|
MAIN_VHOST_CONFIG="/var/www/conf/vhosts/$PUBLIC_HOST/vhconf.xml"
|
|
|
|
|
|
|
|
|
|
|
|
# Ensure main vhost config exists (critical for automation)
|
|
|
|
|
|
if [[ ! -f "$MAIN_VHOST_CONFIG" ]]; then
|
|
|
|
|
|
echo "FATAL: Main vhost config not found at $MAIN_VHOST_CONFIG" >&2
|
|
|
|
|
|
echo "Expected location: /var/www/conf/vhosts/$PUBLIC_HOST/vhconf.xml" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
MARKER="# PMA Gateway Security Rules"
|
|
|
|
|
|
if ! sudo grep -qF "$MARKER" "$MAIN_VHOST_CONFIG"; then
|
|
|
|
|
|
echo "Injecting PMA gateway security rules into main vhost..." >&2
|
|
|
|
|
|
|
|
|
|
|
|
# Define comprehensive security rules for phpMyAdmin protection
|
|
|
|
|
|
NEW_RULES=$(cat <<'EOF'
|
2025-08-26 18:07:06 +00:00
|
|
|
|
# PMA Gateway Security Rules
|
|
|
|
|
|
# Allow access to the gateway scripts themselves
|
|
|
|
|
|
RewriteCond %{REQUEST_URI} ^/access-db-.*\.php$
|
|
|
|
|
|
RewriteRule .* - [L]
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# Block all phpMyAdmin paths unless security cookie is present
|
2025-08-26 18:07:06 +00:00
|
|
|
|
RewriteCond %{HTTP_COOKIE} !pma_access_granted
|
2025-10-20 17:51:39 +00:00
|
|
|
|
RewriteCond %{REQUEST_URI} ^/(phpmyadmin/|index\.php|url\.php|js/|css/|libraries/|themes/|favicon\.ico)
|
2025-08-26 18:07:06 +00:00
|
|
|
|
RewriteRule .* - [F,L]
|
2025-08-26 17:46:27 +00:00
|
|
|
|
EOF
|
2025-10-20 17:51:39 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Enable rewrite and inject comprehensive rules
|
|
|
|
|
|
if ! sudo xmlstarlet ed -L \
|
|
|
|
|
|
-u "//virtualHostConfig/rewrite/enable" -v "1" \
|
|
|
|
|
|
-u "//virtualHostConfig/rewrite/rules" -v "$(sudo xmlstarlet sel -t -v "//virtualHostConfig/rewrite/rules" "$MAIN_VHOST_CONFIG" 2>/dev/null)$NEW_RULES" \
|
|
|
|
|
|
"$MAIN_VHOST_CONFIG"; then
|
|
|
|
|
|
echo "FATAL: Failed to update main vhost rewrite rules." >&2
|
|
|
|
|
|
exit 1
|
2025-08-26 17:46:27 +00:00
|
|
|
|
fi
|
2025-10-20 17:51:39 +00:00
|
|
|
|
|
|
|
|
|
|
echo "✅ PMA security rules injected into main vhost configuration" >&2
|
|
|
|
|
|
NEEDS_RESTART=1
|
2025-08-26 17:46:27 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2025-08-26 17:27:43 +00:00
|
|
|
|
sudo tee "$GATEWAY_FILE" >/dev/null <<'PHP'
|
2025-08-26 16:26:33 +00:00
|
|
|
|
<?php
|
2025-08-26 17:27:43 +00:00
|
|
|
|
// Secure phpMyAdmin gateway – auto-generated, do NOT edit manually.
|
|
|
|
|
|
|
2025-08-26 16:26:33 +00:00
|
|
|
|
ini_set('session.cookie_httponly', 1);
|
2025-08-26 17:27:43 +00:00
|
|
|
|
$param = 'token';
|
|
|
|
|
|
|
|
|
|
|
|
function deny() {
|
|
|
|
|
|
http_response_code(403);
|
|
|
|
|
|
echo 'Access denied';
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!isset($_GET[$param])) {
|
|
|
|
|
|
deny();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$token = $_GET[$param];
|
|
|
|
|
|
if (strpos($token, '.') === false) {
|
|
|
|
|
|
deny();
|
|
|
|
|
|
}
|
2025-08-26 16:26:33 +00:00
|
|
|
|
|
2025-08-26 17:27:43 +00:00
|
|
|
|
list($base, $sig) = explode('.', $token, 2);
|
2025-08-26 16:26:33 +00:00
|
|
|
|
$data = base64_decode($base, true);
|
2025-08-26 17:27:43 +00:00
|
|
|
|
if ($data === false) {
|
|
|
|
|
|
deny();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (strpos($data, ':') === false) {
|
|
|
|
|
|
deny();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
list($slug, $exp) = explode(':', $data, 2);
|
|
|
|
|
|
if (time() > intval($exp)) {
|
2025-08-26 17:46:27 +00:00
|
|
|
|
unlink(__FILE__); // Self-destruct if expired
|
2025-08-26 17:27:43 +00:00
|
|
|
|
deny();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$secret = trim(file_get_contents('/var/lib/jelastic/keys/mbadmin_secret'));
|
|
|
|
|
|
if (!hash_equals($sig, hash_hmac('sha256', $data, $secret))) {
|
|
|
|
|
|
deny();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-26 17:46:27 +00:00
|
|
|
|
// 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);
|
2025-10-20 17:51:39 +00:00
|
|
|
|
// Redirect to phpMyAdmin via the symlinked path
|
|
|
|
|
|
header('Location: /phpmyadmin/index.php');
|
2025-08-26 16:26:33 +00:00
|
|
|
|
exit;
|
|
|
|
|
|
?>
|
|
|
|
|
|
PHP
|
|
|
|
|
|
|
2025-08-26 17:27:43 +00:00
|
|
|
|
sudo chown litespeed:litespeed "$GATEWAY_FILE"
|
|
|
|
|
|
sudo chmod 644 "$GATEWAY_FILE"
|
2025-08-26 16:26:33 +00:00
|
|
|
|
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# Set proper permissions on the public symlink as well
|
|
|
|
|
|
if [[ -L "$PUBLIC_GATEWAY_FILE" ]]; then
|
|
|
|
|
|
sudo chown --no-dereference litespeed:litespeed "$PUBLIC_GATEWAY_FILE"
|
|
|
|
|
|
sudo chmod 644 "$PUBLIC_GATEWAY_FILE"
|
|
|
|
|
|
else
|
|
|
|
|
|
echo "WARNING: Public symlink $PUBLIC_GATEWAY_FILE does not exist. Skipping chown/chmod." >&2
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2025-08-26 17:46:27 +00:00
|
|
|
|
# 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
|
|
|
|
|
|
|
2025-10-20 17:51:39 +00:00
|
|
|
|
# Generate URL using public hostname (port 443) with valid SSL certificate
|
|
|
|
|
|
# This bypasses CDN protections and uses the trusted certificate
|
|
|
|
|
|
URL="https://$PUBLIC_HOST/access-db-$SLUG.php?token=$token"
|
2025-08-26 16:26:33 +00:00
|
|
|
|
echo "$URL"
|
2025-10-20 17:51:39 +00:00
|
|
|
|
|
|
|
|
|
|
# Display security information
|
|
|
|
|
|
echo ""
|
|
|
|
|
|
echo "🔐 SECURITY NOTICE:" >&2
|
|
|
|
|
|
echo " • Gateway URL uses valid Let's Encrypt certificate" >&2
|
|
|
|
|
|
echo " • Served through main domain (port 443) with CDN protection" >&2
|
|
|
|
|
|
echo " • Port 8443 exposure has been removed for security" >&2
|
|
|
|
|
|
echo " • phpMyAdmin symlinked to /phpmyadmin/ with full protection" >&2
|
|
|
|
|
|
echo " • Security rules automatically injected into main vhost" >&2
|
|
|
|
|
|
echo "" >&2
|