DomainShift CP: CloudPanel Domain Update Automation Tool
-
Enhanced Error Handling:
- Uses
set -euo pipefail
for strict error handling - Comprehensive error checking for all critical operations
- Detailed error messages with logging
- Uses
-
Logging System:
- Added timestamp-based logging
- Logs are written to both console and log file
- Maintains audit trail of all domain updates
-
Backup System:
- Automatically creates timestamped backups before making changes
- Backs up nginx config, SSL certificates, and basic auth files
- Allows for easy rollback if needed
-
Better Organization:
- Modular design with separate functions for each major operation
- Clear section separation with comments
- Improved readability and maintainability
-
Security Improvements:
- Proper permission setting for SSL keys
- Input validation for domain names
- Checks for required privileges and dependencies
-
SQLite Operations:
- Uses transactions for database operations
- Better error handling for database queries
- More robust certificate management
-
Prerequisites Checking:
- Verifies all required tools are installed
- Checks for necessary directories and files
- Validates running privileges
To use the script:
-
Save it as
update_domain.sh
-
Make it executable:
chmod +x update_domain.sh
-
Make a Directory name
cloudpanel
invar/log
mkdir /var/log/cloudpanel
-
Create a file named
domain_updates.log
touch /var/log/cloudpanel/domain_updates.log
-
Run it as root:
sudo ./update_domain.sh old.domain.com new.domain.com
The script will:
- Create a backup of all relevant files
- Update all necessary configurations
- Generate new SSL certificates
- Update the CloudPanel database
- Log all actions
- Provide detailed feedback about the process
Logs can be found at /var/log/cloudpanel/domain_updates.log
for audit purposes.
#!/bin/bash
# =============================================================================
# DomainShift CP - CloudPanel Domain Migration Tool
# -----------------------------------------------------------------------------
# Purpose: Updates domain names across nginx configuration, SSL certificates,
# and CloudPanel database while maintaining all associated settings.
# Usage: ./update_domain.sh old.domain.com new.domain.com
# Author: hhf
# Last Updated: 2024-11-09
# =============================================================================
set -euo pipefail # Exit on error, undefined vars, and pipe failures
# =============================================================================
# Configuration
# =============================================================================
readonly DB_FILE="/home/clp/htdocs/app/data/db.sq3"
readonly NGINX_CONF_DIR="/etc/nginx/sites-enabled"
readonly NGINX_SSL_DIR="/etc/nginx/ssl-certificates"
readonly NGINX_AUTH_DIR="/etc/nginx/basic-auth"
readonly LOG_FILE="/var/log/cloudpanel/domain_updates.log"
readonly DOMAIN_REGEX="^([a-zA-Z0-9](([a-zA-Z0-9\-]){0,61}[a-zA-Z0-9])?\.)+([a-zA-Z]{2,}|xn--[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$"
# =============================================================================
# Helper Functions
# =============================================================================
log() {
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] $1" | tee -a "$LOG_FILE"
}
error_exit() {
log "ERROR: $1"
exit 1
}
check_prerequisites() {
# Check if running as root
[[ $EUID -eq 0 ]] || error_exit "This script must be run as root"
# Check if required tools are installed
command -v sqlite3 >/dev/null 2>&1 || error_exit "sqlite3 is required but not installed"
command -v openssl >/dev/null 2>&1 || error_exit "openssl is required but not installed"
command -v clpctl >/dev/null 2>&1 || error_exit "clpctl is required but not installed"
# Check if required directories exist
[[ -d "$NGINX_CONF_DIR" ]] || error_exit "Nginx config directory not found: $NGINX_CONF_DIR"
[[ -d "$NGINX_SSL_DIR" ]] || error_exit "SSL directory not found: $NGINX_SSL_DIR"
[[ -f "$DB_FILE" ]] || error_exit "Database file not found: $DB_FILE"
}
validate_domains() {
local old_domain="$1"
local new_domain="$2"
[[ -z "$old_domain" || -z "$new_domain" ]] && {
echo "Usage: $0 old.domain.com new.domain.com"
error_exit "Domain name parameters not set"
}
[[ "$old_domain" =~ $DOMAIN_REGEX ]] || error_exit "Invalid old domain format: $old_domain"
[[ "$new_domain" =~ $DOMAIN_REGEX ]] || error_exit "Invalid new domain format: $new_domain"
}
backup_files() {
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
local backup_dir="/root/domain_update_backup_${timestamp}"
mkdir -p "$backup_dir"
# Backup nginx configuration
cp "$NGINX_CONF_DIR/$OLD_DOMAIN.conf" "$backup_dir/" 2>/dev/null || true
# Backup SSL certificates
cp "$NGINX_SSL_DIR/$OLD_DOMAIN".* "$backup_dir/" 2>/dev/null || true
# Backup basic auth file
[[ -f "$NGINX_AUTH_DIR/$OLD_DOMAIN" ]] && cp "$NGINX_AUTH_DIR/$OLD_DOMAIN" "$backup_dir/" || true
log "Backup created at: $backup_dir"
}
generate_ssl_certificate() {
local domain="$1"
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-subj "/CN=$domain/" \
-addext "subjectAltName=DNS:$domain" \
-keyout "$NGINX_SSL_DIR/$domain.key" \
-out "$NGINX_SSL_DIR/$domain.crt" || error_exit "Failed to generate SSL certificate"
chmod 600 "$NGINX_SSL_DIR/$domain.key"
}
update_nginx_config() {
local old_domain="$1"
local new_domain="$2"
# Update nginx configuration content
sed -i "s/$old_domain/$new_domain/g" "$NGINX_CONF_DIR/$old_domain.conf" || \
error_exit "Failed to update nginx configuration content"
# Rename nginx configuration file
mv "$NGINX_CONF_DIR/$old_domain.conf" "$NGINX_CONF_DIR/$new_domain.conf" || \
error_exit "Failed to rename nginx configuration file"
}
update_database() {
local old_domain="$1"
local new_domain="$2"
local site_id
# Get site ID
site_id=$(sqlite3 "$DB_FILE" "SELECT id FROM site WHERE domain_name = '$old_domain';") || \
error_exit "Failed to query site ID"
[[ -z "$site_id" ]] && error_exit "Domain $old_domain not found in CloudPanel database"
# Update site information
sqlite3 "$DB_FILE" "BEGIN TRANSACTION;
UPDATE site
SET domain_name = '$new_domain',
vhost_template = REPLACE(vhost_template, '$old_domain', '$new_domain')
WHERE id = $site_id;
DELETE FROM certificate
WHERE site_id = $site_id
AND default_certificate = 1
AND type = 1;
COMMIT;" || error_exit "Failed to update database"
return "$site_id"
}
# =============================================================================
# Main Script
# =============================================================================
main() {
local OLD_DOMAIN="$1"
local NEW_DOMAIN="$2"
log "Starting domain update process: $OLD_DOMAIN -> $NEW_DOMAIN"
# Preliminary checks
check_prerequisites
validate_domains "$OLD_DOMAIN" "$NEW_DOMAIN"
# Create backup
backup_files
# Update nginx configuration
log "Updating nginx configuration..."
update_nginx_config "$OLD_DOMAIN" "$NEW_DOMAIN"
# Handle SSL certificates
log "Managing SSL certificates..."
rm -f "$NGINX_SSL_DIR/$OLD_DOMAIN".{key,crt}
generate_ssl_certificate "$NEW_DOMAIN"
# Update basic auth if exists
if [[ -f "$NGINX_AUTH_DIR/$OLD_DOMAIN" ]]; then
log "Updating nginx basic auth..."
mv "$NGINX_AUTH_DIR/$OLD_DOMAIN" "$NGINX_AUTH_DIR/$NEW_DOMAIN" || \
error_exit "Failed to update basic auth"
fi
# Update database
log "Updating CloudPanel database..."
local site_id
site_id=$(update_database "$OLD_DOMAIN" "$NEW_DOMAIN")
# Install new certificate
log "Installing new self-signed certificate..."
clpctl site:install:certificate \
--domainName="$NEW_DOMAIN" \
--privateKey="$NGINX_SSL_DIR/$NEW_DOMAIN.key" \
--certificate="$NGINX_SSL_DIR/$NEW_DOMAIN.crt" || \
error_exit "Failed to install new certificate"
# Update certificate settings
local cert_id
cert_id=$(sqlite3 "$DB_FILE" "SELECT certificate_id FROM site WHERE id = $site_id")
sqlite3 "$DB_FILE" "UPDATE certificate SET default_certificate = 1, type = 1 WHERE id = $cert_id" || \
error_exit "Failed to update certificate settings"
# Restart nginx
log "Restarting nginx..."
systemctl restart nginx || error_exit "Failed to restart nginx"
log "Domain update completed successfully"
}
# Execute main function with all arguments
main "$@"