From 317e3e215eb5c6100550a72f74f65b0009d9deb3 Mon Sep 17 00:00:00 2001 From: Alexey Berezhok Date: Sat, 23 May 2026 22:45:01 +0300 Subject: [PATCH] Fixes --- bin/v-bunkerweb-migrate | 102 +++++++++----- bin/v-system-nginx-migrate | 282 +++++++++++++++++++++++++++++++++++++ 2 files changed, 351 insertions(+), 33 deletions(-) create mode 100755 bin/v-system-nginx-migrate diff --git a/bin/v-bunkerweb-migrate b/bin/v-bunkerweb-migrate index c9beb9f..1bb821c 100755 --- a/bin/v-bunkerweb-migrate +++ b/bin/v-bunkerweb-migrate @@ -328,6 +328,7 @@ when :migratenginx # Stop nginx service system('systemctl stop nginx') # Start nginx-system service + system('systemctl enable nginx-system') system('systemctl start nginx-system') end when :migratetobunkerweb @@ -354,6 +355,8 @@ when :migratetobunkerweb hestia_print_error_message_to_cli "Failed to enable bunkerweb_module" exit 1 end + hestia_print_info_message_to_cli "Ожидаем минуту для перезапуска сервиса bunkerweb..." + sleep 60 end LIST_DOMAINS=[] @@ -394,6 +397,10 @@ when :migratetobunkerweb ssl_status = domain_info['SSL'] || 'no' is_ssl = ssl_status == 'yes' + # Get IP from domain info - should be available in the parsed JSON + proxy_host = domain_info['IP'] || (domain_info['IP6'].present? ? domain_info['IP6'].strip : "127.0.0.1") + proxy_host = proxy_host.nil? || proxy_host.empty? ? "127.0.0.1" : proxy_host + # Create alias list from domain info (ALIAS field contains comma-separated aliases) raw_aliases = domain_info['ALIAS'] || '' if raw_aliases && !raw_aliases.empty? @@ -403,12 +410,23 @@ when :migratetobunkerweb aliases_list = domain_name # No aliases, use domain name only end - hestia_print_info_message_to_cli "Processing domain: #{domain_name} (SSL: #{is_ssl}, Aliases: #{aliases_list})" + hestia_print_info_message_to_cli "Processing domain: #{domain_name} (SSL: #{is_ssl}, IP: #{proxy_host})" begin # Add domain to BunkerWeb via module script (without SSL) - output = IO.popen("/usr/local/hestia/bin/v-bunkerweb-module add #{domain_name} #{$PROXY_HOST}") - hestia_print_info_message_to_cli " Added #{domain_name}: #{output.strip}" + cmd = "/usr/local/hestia/bin/v-bunkerweb-module add #{domain_name} #{proxy_host}" + puts_cmd = " Added #{domain_name}: Executing command: #{cmd}" + hestia_print_info_message_to_cli puts_cmd + + result = system(cmd) + + if result + # Команда успешно выполнена + hestia_print_info_message_to_cli " Status: Success" + else + hestia_print_error_message_to_cli " Failed to add domain #{domain_name}" + next + end rescue => e hestia_print_error_message_to_cli "Failed to add domain #{domain_name} to BunkerWeb: #{e.message}" next @@ -416,8 +434,18 @@ when :migratetobunkerweb # Add aliases to the domain via module script begin - output = IO.popen("/usr/local/hestia/bin/v-bunkerweb-module alias #{domain_name} \"#{aliases_list}\"") - hestia_print_info_message_to_cli " Added aliases for #{domain_name}: #{output.strip}" + cmd = "/usr/local/hestia/bin/v-bunkerweb-module alias #{domain_name} \"#{aliases_list}\"" + puts_cmd = " Added aliases for #{domain_name}: Executing command: #{cmd}" + hestia_print_info_message_to_cli puts_cmd + + result = system(cmd) + + if result + hestia_print_info_message_to_cli " Status: Success" + else + hestia_print_error_message_to_cli " Failed to set aliases for #{domain_name}" + next + end rescue => e hestia_print_error_message_to_cli "Failed to set aliases for #{domain_name}: #{e.message}" next @@ -425,7 +453,7 @@ when :migratetobunkerweb # Handle SSL configuration if enabled if is_ssl - original_ssl_dir = "/home/#{username}/conf/#{domain_name}/web/ssl" + original_ssl_dir = "/home/#{username}/conf/web/#{domain_name}/ssl" # Check if SSL directory and files exist unless Dir.exist?(original_ssl_dir) || File.exist?("#{original_ssl_dir}/#{domain_name}.pem") @@ -445,9 +473,10 @@ when :migratetobunkerweb begin # Create bunkerweb SSL directory if it doesn't exist FileUtils.mkdir_p(bunkerweb_ssl_dir) - Dir.chmod(bunkerweb_ssl_dir, 0755) + FileUtils.chmod(0755, bunkerweb_ssl_dir) # Define paths in bunkerweb directory + crt_path_bunkerweb = "#{bunkerweb_ssl_dir}/#{domain_name}.crt" cert_path_bunkerweb = "#{bunkerweb_ssl_dir}/#{domain_name}.pem" key_path_bunkerweb = "#{bunkerweb_ssl_dir}/#{domain_name}.key" @@ -459,43 +488,53 @@ when :migratetobunkerweb # Copy .crt file if exists if File.exist?(original_crt) - FileUtils.cp("-f", original_crt, cert_path_bunkerweb) - elsif File.exist?(original_pem) + FileUtils.cp(original_crt, crt_path_bunkerweb) + end + + if File.exist?(original_pem) # Only use .pem if .crt doesn't exist (fallback like the backup script) - FileUtils.cp("-f", original_pem, cert_path_bunkerweb) + FileUtils.cp(original_pem, cert_path_bunkerweb) end # Copy .key file if exists if File.exist?(original_key) - FileUtils.cp("-f", original_key, key_path_bunkerweb) - end - - # Copy .pem file if exists (in addition to .crt for backup purposes) - if File.exist?(original_pem) - FileUtils.cp("-f", original_pem, cert_path_bunkerweb) + FileUtils.cp(original_key, key_path_bunkerweb) end # Copy .ca file if exists if File.exist?(original_ca) - FileUtils.cp("-f", original_ca, "#{bunkerweb_ssl_dir}/#{domain_name}.ca") + FileUtils.cp(original_ca, "#{bunkerweb_ssl_dir}/#{domain_name}.ca") end # Set ownership and permissions (nginx user can read, others cannot) - Dir.chown(bunkerweb_ssl_dir, 'root', 'nginx') - Dir.chmod(bunkerweb_ssl_dir, 0755) + FileUtils.chown('root', 'nginx', bunkerweb_ssl_dir) + FileUtils.chmod(0755, bunkerweb_ssl_dir) - File.chown(cert_path_bunkerweb, 'root', 'nginx') if File.exist?(cert_path_bunkerweb) - File.chmod(cert_path_bunkerweb, 0640) if File.exist?(cert_path_bunkerweb) + FileUtils.chown('root', 'nginx', cert_path_bunkerweb) if File.exist?(cert_path_bunkerweb) + FileUtils.chmod(0640, cert_path_bunkerweb) if File.exist?(cert_path_bunkerweb) - File.chown(key_path_bunkerweb, 'root', 'nginx') if File.exist?(key_path_bunkerweb) - File.chmod(key_path_bunkerweb, 0640) if File.exist?(key_path_bunkerweb) + FileUtils.chown('root', 'nginx', key_path_bunkerweb) if File.exist?(key_path_bunkerweb) + FileUtils.chmod(0640, key_path_bunkerweb) if File.exist?(key_path_bunkerweb) - # Add SSL configuration to BunkerWeb via module script - output = IO.popen("/usr/local/hestia/bin/v-bunkerweb-module addssl #{domain_name} #{cert_path_bunkerweb} #{key_path_bunkerweb}") - hestia_print_error_message_to_cli " Added SSL for #{domain_name}: #{output.strip}" + FileUtils.chown('root', 'nginx', crt_path_bunkerweb) if File.exist?(crt_path_bunkerweb) + FileUtils.chmod(0640, crt_path_bunkerweb) if File.exist?(crt_path_bunkerweb) + + # Add SSL configuration to BunkerWeb via module script (use proxy_host from earlier) + + cmd = "/usr/local/hestia/bin/v-bunkerweb-module addssl #{domain_name} #{crt_path_bunkerweb} #{key_path_bunkerweb}" + puts_cmd = " Added SSL for #{domain_name}: Executing command: #{cmd}" + hestia_print_info_message_to_cli puts_cmd + + result = system(cmd) + + if result + hestia_print_info_message_to_cli " Status: Success" + else + hestia_print_error_message_to_cli " Failed to configure SSL for #{domain_name}" + end # Update path_to_ssl to point to bunkerweb directory - ssl_cert_path = cert_path_bunkerweb + ssl_cert_path = crt_path_bunkerweb rescue => e hestia_print_error_message_to_cli "Failed to configure SSL for #{domain_name}: #{e.message}" end @@ -505,7 +544,8 @@ when :migratetobunkerweb LIST_DOMAINS << { user: username, domain: domain_name, - is_ssl: is_ssl, + proxy_host: proxy_host.to_s, + is_ssl: is_ssl == true ? "yes" : "no", # Convert boolean/string to proper string format path_to_ssl: ssl_cert_path ? ssl_cert_path : nil } @@ -519,10 +559,6 @@ when :migratetobunkerweb LIST_DOMAINS.each_with_index do |entry, idx| hestia_print_info_message_to_cli "#{idx + 1}. user: #{entry[:user]}, domain: #{entry[:domain]}, is_ssl: #{entry[:is_ssl].to_s}, path_to_ssl: #{entry[:path_to_ssl]}" end - - # Output as JSON for potential use in downstream scripts - hestia_print_info_message_to_cli "\n=== LIST_DOMAINS (JSON) ===" - hestia_print_info_message_to_cli JSON.generate(LIST_DOMAINS, { indent: 2 }) else hestia_print_error_message_to_cli "No domains were migrated to BunkerWeb" end @@ -530,7 +566,7 @@ when :migratetobunkerweb hestia_print_info_message_to_cli "Stage 11 completed." end else - hestia_print_error_message_to_cli "unknown command" + hestia_print_error_message_to_cli "unknown command (use migratetobunkerweb or migratenginx)" log_event E_ARGS, $ARGUMENTS exit 1 end diff --git a/bin/v-system-nginx-migrate b/bin/v-system-nginx-migrate new file mode 100755 index 0000000..f77f409 --- /dev/null +++ b/bin/v-system-nginx-migrate @@ -0,0 +1,282 @@ +#!/opt/brepo/ruby33/bin/ruby +# info: utility to prepare existing server with hestiacp to use new alternative nginx +# options: COMMAND +# +# example: v-system-nginx-migrate migrate-nginx +# +# Commands: +# migratenginx - move old nginx configs to the new port and path +# +# + +#------------------------------------------# +# Variables & Functions # +#------------------------------------------# + +# Argument definition +v_command = ARGV[0] + +require "/usr/local/hestia/func_ruby/global_options" + +load_ruby_options_defaults +$HESTIA = load_hestia_default_path_from_env + +require "main" +require "modules" + +require 'json' unless defined?(JSON) +require 'fileutils' +require 'time' +require 'pathname' + + +def copy_nginx_files(src_root, dest_root) + FileUtils.mkdir_p(dest_root) + Dir.foreach(src_root) do |entry| + next if entry == '.' || entry == '..' + next if entry == 'modules' || entry == 'modules-enabled' + src_path = File.join(src_root, entry) + dest_path = File.join(dest_root, entry) + if File.directory?(src_path) + FileUtils.mkdir_p(dest_path) + copy_nginx_files(src_path, dest_path) + else + FileUtils.cp(src_path, dest_path) + end + end +end + +# Function to log and execute migration stages +def log_migrate_stage(stage) + log_file = '/usr/local/hestia/log/bunkerweb_migrate_stages.log' + # Check if the stage has already been recorded + if File.exist?(log_file) && File.readlines(log_file).any? { |line| line.strip == stage } + hestia_print_error_message_to_cli "Stage #{stage} already completed, skipping." + return + end + # Execute the stage block + begin + yield + # Record the successful stage + File.open(log_file, 'a') { |f| f.puts stage } + hestia_print_info_message_to_cli "Stage #{stage} completed." + rescue => e + hestia_print_error_message_to_cli "Stage #{stage} failed: #{e.message}" + exit 1 + end +end + +# Function to parse and migrate nginx.conf from /usr/local/hestia/nginx-system/etc/nginx/nginx.conf +def migrate_nginx_config_from_file(source_path) + return false unless File.exist?(source_path) + + hestia_print_info_message_to_cli "Processing nginx config from: #{source_path}" + + content = File.read(source_path) + original_content = content.dup + + modified = false + + # Replace all paths starting with /var/ to /usr/local/hestia/nginx-system/var/ + # This pattern matches any absolute path that starts with /var/ anywhere in the line + content = content.gsub(/\/var\//, '/usr/local/hestia/nginx-system/var/') + + # Replace pid path from /run/nginx.pid to /run/nginx-system.pid + content = content.gsub(/pid\s+\S+/) { |match| match.gsub('/run/nginx.pid', '/run/nginx-system.pid') } + + if content != original_content + File.write(source_path, content) + hestia_print_info_message_to_cli "Updated config: #{source_path}" + modified = true + end + + modified +end + +def parse_listen(line) + # Попытка найти IP:port + m = line.match(/^\s*listen\s+([^\s:]+):(\d+)/i) + return [m[1], m[2]] if m + # Если только порт после listen + m = line.match(/^\s*listen\s+(\d+);?\s*$/i) + return [nil, m[1]] if m + nil +end + +# Helper function to parse and replace ports in listen directives using temp placeholders +def parse_and_replace_listen_directive(line, proxy_port, proxy_ssl_port) + return line unless line.match?(/\blisten\b/i) + + new_line = line.dup + + # Define target ports (always migrate to these values regardless of input) + target_http_port = '8078' + target_ssl_port = '8079' + + result = parse_listen(line) + return line unless result + ip, port = result + + if port == proxy_port + if ip + new_line.gsub!("#{ip}:#{port}", "#{ip}:#{target_http_port}") + else + new_line.gsub!(port, target_http_port) + end + elsif port == proxy_ssl_port + if ip + new_line.gsub!("#{ip}:#{port}", "#{ip}:#{target_ssl_port}") + else + new_line.gsub!(port, target_ssl_port) + end + end + + new_line +end + +hestia_check_privileged_user + +load_global_bash_variables "/etc/hestiacp/hestia.conf" +if $HESTIA.nil? + hestia_print_error_message_to_cli "Can't find HESTIA base path" + exit 1 +end + +load_global_bash_variables "#{$HESTIA}/conf/hestia.conf" + +#------------------------------------------# +# Verifications # +#------------------------------------------# + +check_args 1, ARGV, "COMMAND" + +# Perform verification if read-only mode is enabled +check_hestia_demo_mode + +#------------------------------------------# +# Action # +#------------------------------------------# + +case v_command.to_sym +when :migratenginx + log_migrate_stage('nstage0') do + # Create backup of /etc/nginx with timestamp + + timestamp = Time.now.strftime('%Y%m%d_%H%M%S') + backup_root = "/etc/nginx_backup_#{timestamp}" + FileUtils.mkdir_p(backup_root) + + src_root = '/etc/nginx' + dest_root = backup_root + + # Custom copy function to handle symlinks in conf.d/domains + def copy_with_symlinks(src, dest) + Dir.foreach(src) do |entry| + next if entry == '.' || entry == '..' + src_path = File.join(src, entry) + dest_path = File.join(dest, entry) + + if File.symlink?(src_path) + # Check if symlink is inside conf.d/domains + if src_path.include?(File.join('conf.d', 'domains')) + # Resolve the target of the symlink + target_path = File.readlink(src_path) + # Resolve relative symlink paths + unless Pathname.new(target_path).absolute? + target_path = File.expand_path(target_path, File.dirname(src_path)) + end + if File.exist?(target_path) && File.file?(target_path) + content = File.read(target_path) + new_file_name = "#{entry}_content.conf" + new_file_path = File.join(dest, new_file_name) + File.write(new_file_path, content) + end + else + # Preserve the symlink as is + FileUtils.mkdir_p(File.dirname(dest_path)) + target = File.readlink(src_path) + FileUtils.ln_s(target, dest_path) + end + elsif File.directory?(src_path) + FileUtils.mkdir_p(dest_path) + copy_with_symlinks(src_path, dest_path) + else + FileUtils.cp(src_path, dest_path) + end + end + end + + copy_with_symlinks(src_root, dest_root) + end + log_migrate_stage('nstage2') do + unless system('yum install -y nginx-system') + hestia_print_error_message_to_cli "Failed to install nginx-system via yum" + log_event E_ARGS, $ARGUMENTS + exit 1 + end + end + log_migrate_stage('nstage3') do + FileUtils.rm_f Dir.glob('/usr/local/hestia/nginx-system/etc/nginx/conf.d/*.conf') + src_root = '/etc/nginx' + dest_root = '/usr/local/hestia/nginx-system/etc/nginx' + copy_nginx_files(src_root, dest_root) + end + log_migrate_stage('nstage4') do + # Find all files under the nginx-system directory + nginx_conf_dir = '/usr/local/hestia/nginx-system/etc/nginx' + Dir.glob(File.join(nginx_conf_dir, '**', '*')).each do |path| + hestia_print_info_message_to_cli "stage 4 processing file #{path}" + next if File.directory?(path) + content = File.read(path) + new_content = content.gsub(/(? e + hestia_print_error_message_to_cli "Failed to parse JSON from state command: #{e.message}" + exit 1 + end + else + hestia_print_error_message_to_cli "Failed to enable update_module" + exit 1 + end + end + log_migrate_stage('stage9') do + # Delete all contents inside /etc/nginx + FileUtils.rm_rf Dir.glob('/etc/nginx/*') + # Stop nginx service + system('systemctl stop nginx') + system('systemctl disable nginx') + # Start nginx-system service + system('systemctl enable nginx-system') + system('systemctl start nginx-system') + end +else + hestia_print_error_message_to_cli "unknown command (use migratenginx)" + log_event E_ARGS, $ARGUMENTS + exit 1 +end + +exit 0