50 Commits

Author SHA1 Message Date
Alexey Berezhok
317e3e215e Fixes 2026-05-23 22:45:01 +03:00
Alexey Berezhok
4f999094f3 Fixes 2026-05-20 23:24:30 +03:00
Alexey Berezhok
5e32af0148 Fixes 2026-05-19 23:04:15 +03:00
Alexey Berezhok
6ff9d67909 Fixes 2026-05-16 00:10:12 +03:00
Alexey Berezhok
af9e6bfb82 Fixes 2026-05-15 00:34:14 +03:00
Alexey Berezhok
e0419fea4b Fixes 2026-05-14 00:32:06 +03:00
Alexey Berezhok
3cc428df43 Fixes 2026-05-12 01:27:01 +03:00
Alexey Berezhok
9d1afcdf28 Fixes 2026-05-11 23:28:47 +03:00
Alexey Berezhok
adef1a98bb Fixes 2026-05-10 23:48:56 +03:00
Alexey Berezhok
3263dbecab Fixes 7 2026-05-03 23:58:54 +03:00
Alexey Berezhok
3c5a5924d6 Fixes 5 2026-05-03 19:47:42 +03:00
Alexey Berezhok
8b911372af Fixes 4 2026-05-03 19:47:10 +03:00
Alexey Berezhok
a3888d10b4 Fixes 3 2026-05-03 16:32:53 +03:00
Alexey Berezhok
8add078e7c Added update module 2026-05-03 00:54:14 +03:00
Alexey Berezhok
54f549db62 Fixes2 2026-05-02 17:50:56 +03:00
Alexey Berezhok
fd67d9c546 Fixes 2026-05-01 23:34:16 +03:00
Alexey Berezhok
86bf9fccf5 Added fixes ti bunkerweb module 2026-05-01 22:53:57 +03:00
Alexey Berezhok
f64f034b7b Added setup script 2026-05-01 18:11:49 +03:00
Alexey Berezhok
dae9aca295 Add support of bunkerweb part 2 2026-04-28 00:26:58 +03:00
Alexey Berezhok
592b954a9f Added API for bunkerweb 2026-04-27 00:47:57 +03:00
Alexey Berezhok
4b023ea671 Added install fixes 2026-04-12 00:50:31 +03:00
Alexey Berezhok
4cb55905cc Try to support bunkerweb 2026-04-11 00:41:04 +03:00
Alexey Berezhok
d76624ff43 Fixed updater 2026-03-29 22:41:21 +03:00
alexey
27b237a3c7 Added support ansible instead of puppet 2026-03-29 12:21:10 +03:00
alexey
5349d46d71 Fixed resore proxy_fcgi 2026-03-29 11:07:01 +03:00
alexey
75df7a2554 Fixes for php remi 2026-03-28 20:16:08 +03:00
alexey
479a65e0dd Added remi php install fixes 2026-03-28 18:55:38 +03:00
alexey
aae561716c Fixes remi instllation 2026-03-28 18:31:39 +03:00
alexey
8a2ec261f5 Added fixes for non php-fpm installation 2026-03-28 01:29:23 +03:00
alexey
ec4bb73609 Fixed database size usage and disk usage output on the info pages 2026-03-26 22:52:45 +03:00
alexey
e37e8a0d1e Fixed nginx mod_rewrite templates 2026-03-21 23:17:24 +03:00
alexey
f398decba9 Added nginx+mod_rewrite support 2026-03-19 22:46:46 +03:00
Alexey Berezhok
ae8b90d66e Rename default rpm repository 2026-01-16 12:33:14 +03:00
Alexey Berezhok
5610859f17 Disable of MSVSphere build because of MSVSphere doesn.t exists anymore 2026-01-16 11:24:21 +03:00
Alexey Berezhok
598c9abdfb Added remit to local php script 2026-01-16 10:58:18 +03:00
Alexey Berezhok
5d461cc39b Fixed installation error for local php 2026-01-15 23:20:18 +03:00
Alexey Berezhok
8b2c076e2e Added php versions to upgrade.conf, fixed errors 2025-11-18 23:14:29 +03:00
Alexey Berezhok
f44fe71c28 Added php versions to upgrade.conf 2025-11-18 22:50:59 +03:00
Alexey Berezhok
7e9f0f1074 Merge branch 'devel' of ssh://dev.brepo.ru:9453/bayrepo/hestiacp into devel 2025-11-18 22:40:27 +03:00
Alexey Berezhok
be9c2de988 Fixed remi php version 2025-11-18 22:40:01 +03:00
Alexey Berezhok
f433895670 Update spec 2025-11-18 21:56:33 +03:00
Alexey Berezhok
e2c8835b6b Added php 8.4, 8.5 2025-11-17 23:56:25 +03:00
Alexey Berezhok
751814c48a Add fixes of installation 2025-11-17 23:50:57 +03:00
Alexey Berezhok
113cd37b9d Added php 8.4 and 8.5 2025-11-17 23:38:17 +03:00
Alexey Berezhok
ce83ca97a9 Added documentation fixes 2025-11-13 00:19:59 +03:00
Alexey Berezhok
bb547d1d9a Added local path of phpmyadmin 2025-11-11 23:46:20 +03:00
Alexey Berezhok
fd667cd08b Merge branch 'master' into devel 2025-10-18 23:45:07 +03:00
Alexey Berezhok
46fb25bcc0 Added default domain 2025-10-18 23:42:21 +03:00
Alexey Berezhok
bd56dcf5f5 Added fixes for last apache update 2025-10-14 23:08:27 +03:00
Alexey Berezhok
cc59572a59 Added fix for memory calculation 2025-06-05 23:14:09 +03:00
241 changed files with 10361 additions and 5827 deletions

View File

@@ -2,12 +2,23 @@
All notable changes to this project will be documented in this file.
## [1.9.6.rpm] - Release
- Fix error on all web and mail domains after Apache 2.4.64 update
- Fix error on local php installation and extension activation
- Added templates for nginx mod_rewrite activation
- Added nginx with mod_rewrite
- Fixed database size usage and disk usage output on the info pages
- Fixed installation of panel without PHP-FPM
- Fixed mod_php, fcgid, fcgi mode
## [1.9.5.rpm] - Release
- Added support installation of alternative php not only remi
- Fixed separate edition of php.ini in multiphp mode
- Added usemirrorclamav for install script for uieng Russian mirror for Clamav databases
- Added devel mode of installation for installing not from github by default. With this parameter installation will be from devel repo dev.brepo.ru
- Fixed memory calculation in service list
## [1.9.4.rpm] - Release

View File

@@ -79,9 +79,9 @@ EOF
chown root:$user $fastcgi
chmod 640 $fastcgi
str="fastcgi_cache_path /var/cache/nginx/micro/$domain levels=1:2"
str="fastcgi_cache_path /usr/local/hestia/nginx-system/var/cache/nginx/micro/$domain levels=1:2"
str="$str keys_zone=$domain:10m max_size=512m inactive=30m use_temp_path=off;"
conf='/etc/nginx/conf.d/fastcgi_cache_pool.conf'
conf='/usr/local/hestia/nginx-system/etc/nginx/conf.d/fastcgi_cache_pool.conf'
if [ -f "$conf" ]; then
if [ -z "$(grep "=${domain}:" $conf)" ]; then
echo "$str" >> $conf
@@ -90,7 +90,7 @@ else
echo "$str" >> $conf
fi
mkdir -p /var/cache/nginx/micro/$domain
mkdir -p /usr/local/hestia/nginx-system/var/cache/nginx/micro/$domain
#----------------------------------------------------------#
# Hestia #

View File

@@ -9,7 +9,7 @@
# www.domain.tld type will be automatically assigned to the domain unless
# "none" is transmited as argument. If ip have associated dns name, this
# domain will also get the alias domain-tpl.$ipname. An alias with the ip
# name is useful during the site testing while dns isn't moved to server yet.
# name is useful during the site testing while dns isn't moved to server yaliet.
#----------------------------------------------------------#
# Variables & Functions #
@@ -251,6 +251,15 @@ check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ]; then
$BIN/v-ext-modules-run bunkerweb_module add "$domain" "$ip"
if [ -n "%$ALIAS" ]; then
$BIN/v-ext-modules-run bunkerweb_module alias "$domain" "$ALIAS"
fi
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Added new web domain (Name: $domain)."
log_event "$OK" "$ARGUMENTS"

View File

@@ -108,6 +108,13 @@ check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ]; then
get_domain_values 'web'
$BIN/v-ext-modules-run bunkerweb_module alias "$domain" "$ALIAS"
fi
$BIN/v-log-action "$user" "Info" "Web" "Added new web domain alias (Alias: $aliases, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"

View File

@@ -136,6 +136,25 @@ if [ -n "$UPDATE_SSL_SCRIPT" ]; then
eval "$UPDATE_SSL_SCRIPT $user $domain"
fi
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ] && [ "$BUNKERWEB" = "yes" ]; then
BUNKW_DIR=$HOMEDIR/$user/conf/web/$domain/ssl/bunkerweb
if [ ! -d "$BUNKW_DIR" ]; then
mkdir -p "$BUNKW_DIR"
chmod 0755 "$BUNKW_DIR"
fi
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.crt" "$BUNKW_DIR/"
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.key" "$BUNKW_DIR/"
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.pem" "$BUNKW_DIR/"
if [ -e "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.ca" ]; then
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.ca" "$BUNKW_DIR/"
fi
chown root:nginx "$BUNKW_DIR"/*
chmod 0640 "$BUNKW_DIR"/*
$BIN/v-ext-modules-run bunkerweb_module addssl "$domain" "$BUNKW_DIR/$domain.pem" "$BUNKW_DIR/$domain.key"
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Added certificate and enabled SSL (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"

View File

@@ -84,10 +84,10 @@ if [ "$LOCAL_PHP" == "yes" ]; then
fi
else
# Packages in Remi repo have names with php$version-php- prefixes
mph="php$version-php-fpm php$version-php-cgi php$version-php-mysqlnd php$version-php-pgsql
php$version-php-pdo php$version-php-common php$version-php-pecl-imagick php$version-php-imap
php$version-php-ldap php$version-php-pecl-apcu php$version-php-pecl-zip php$version-php-cli
php$version-php-opcache php$version-php-xml php$version-php-gd php$version-php-intl
mph="php$version-php-fpm php$version-php-cgi php$version-php-mysqlnd php$version-php-pgsql
php$version-php-pdo php$version-php-common php$version-php-pecl-imagick php$version-php-imap
php$version-php-ldap php$version-php-pecl-apcu php$version-php-pecl-zip php$version-php-cli
php$version-php-opcache php$version-php-xml php$version-php-gd php$version-php-intl
php$version-php-mbstring php$version-php-pspell php$version-php-readline"
if [ "$version" = "56" ]; then
mph=$(sed -e "s/php$version-php-pecl-apcu//")
@@ -130,10 +130,10 @@ echo
if [ "$LOCAL_PHP" == "yes" ]; then
for mod in $php_modules_install; do
enable_local_php_extension "$version" "$mod"
fn_enable_local_php_extension "$version" "$mod"
done
for mod in $php_modules_disable; do
disable_local_php_extension "$version" "$mod"
fn_disable_local_php_extension "$version" "$mod"
done
fi
@@ -156,23 +156,15 @@ else
fi
# Check if required modules for apache2 are enabled
if [ "$WEB_SYSTEM" = "apache2" ]; then
if [ -f /etc/redhat-release ]; then
if ! httpd -M | grep 'proxy_fcgi_module' ; then
sed 's/#LoadModule proxy_fcgi_module/LoadModule proxy_fcgi_module/' -i /etc/httpd/conf.modules.d/00-proxy.conf
sed 's/#LoadModule proxy_module/LoadModule proxy_module/' -i /etc/httpd/conf.modules.d/00-proxy.conf
fi
if ! httpd -M | grep 'setenvif_module' ; then
sed 's/#LoadModule setenvif_module/LoadModule setenvif_module/' -i /etc/httpd/conf.modules.d/00-base.conf
fi
else
if ! a2query -q -m proxy_fcgi; then
a2enmod -q proxy_fcgi
fi
if ! a2query -q -m setenvif; then
a2enmod -q setenvif
fi
if [ "$WEB_SYSTEM" = "httpd" ]; then
if ! httpd -M | grep 'proxy_fcgi_module' ; then
sed 's/#LoadModule proxy_fcgi_module/LoadModule proxy_fcgi_module/' -i /etc/httpd/conf.modules.d/00-proxy.conf
sed 's/#LoadModule proxy_module/LoadModule proxy_module/' -i /etc/httpd/conf.modules.d/00-proxy.conf
fi
if ! httpd -M | grep 'setenvif_module' ; then
sed 's/#LoadModule setenvif_module/LoadModule setenvif_module/' -i /etc/httpd/conf.modules.d/00-base.conf
fi
$BIN/v-restart-web "yes"
fi

574
bin/v-bunkerweb-migrate Executable file
View File

@@ -0,0 +1,574 @@
#!/opt/brepo/ruby33/bin/ruby
# info: utility to prepare existing server with hestiacp to use bunkerweb
# do not run this script n the server, where bunkerweb was installed with hestiacp
# installation
# options: COMMAND
#
# example: v-bunkerweb-migrate migrate-nginx
#
# Commands:
# migratenginx - move old nginx configs to the new port and path
# migratetobunkerweb - create items of sites in the bunkerweb database
#
#------------------------------------------#
# 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 "HestiaBunkerWebApi"
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('stage0') 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('stage1') do
if $BUNKERWEB.nil?
hestia_change_sys_config_value("BUNKERWEB", "yes")
end
end
log_migrate_stage('stage2') 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('stage3') 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('stage4') 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(/(?<!\/usr\/local\/hestia\/nginx-system)\/etc\/nginx/, '/usr/local/hestia/nginx-system/etc/nginx')
if new_content != content
hestia_print_info_message_to_cli "Changed path to config in file #{path}"
File.open(path, 'w') { |f| f.write(new_content) }
end
end
end
log_migrate_stage('stage4.1') do
# Parse nginx.conf file to replace paths
if migrate_nginx_config_from_file("/usr/local/hestia/nginx-system/etc/nginx/nginx.conf")
hestia_print_info_message_to_cli "Completed migration of nginx.conf paths"
else
hestia_print_error_message_to_cli "Warning: Could not migrate nginx.conf from #{File.expand_path('/usr/local/hestia/nginx-system/etc/nginx/nginx.conf')}"
end
end
log_migrate_stage('stage5') do
nginx_conf_dir = '/usr/local/hestia/nginx-system/etc/nginx'
# Read proxy ports from configuration
#proxy_port = $PROXY_PORT.nil? || $PROXY_PORT.empty? ? '80' : $PROXY_PORT
#proxy_ssl_port = $PROXY_SSL_PORT.nil? || $PROXY_SSL_PORT.empty? ? '443' : $PROXY_SSL_PORT
proxy_port = '80'
proxy_ssl_port = '443'
hestia_print_info_message_to_cli "Migrating ports: #{proxy_port} -> 8078, #{proxy_ssl_port} -> 8079"
# Find and replace port in all .conf files
Dir.glob(File.join(nginx_conf_dir, '**', '*.conf')).each do |conf_file|
hestia_print_info_message_to_cli "stage 5 processing file #{conf_file}"
content = File.read(conf_file)
modified = false
# Process line by line - only replace ports in listen directives
new_lines = []
content.each_line do |line|
# Check if line is a listen directive (starts with optional whitespace then 'listen')
if /^\s*listen\s+/i.match?(line) || /^listen\s+/i.match?(line)
# This is a listen line - process it
new_line = parse_and_replace_listen_directive(line, proxy_port, proxy_ssl_port)
modified = true unless new_line == line
new_lines << new_line
else
# Not a listen line, keep as is
new_lines << line
end
end
# Write changes back if modified
if modified
File.open(conf_file, 'w') { |f| f.write(new_lines.join) }
hestia_print_info_message_to_cli " Updated: #{conf_file}"
end
end
end
log_migrate_stage('stage6') do
hestia_change_sys_config_value("PROXY_PORT", "8078")
hestia_change_sys_config_value("PROXY_SSL_PORT", "8079")
end
log_migrate_stage('stage7') do
if system('/usr/local/hestia/bin/v-ext-modules enable update_module')
output = IO.popen("/usr/local/hestia/bin/v-ext-modules state update_module json").read
begin
parsed = JSON.parse(output)
if parsed.is_a?(Array) && parsed.first && parsed.first['STATE'] == 'enabled'
system('/usr/local/hestia/bin/v-ext-modules-run update_module synctemplates')
else
hestia_print_error_message_to_cli "update_module not enabled after enable command"
exit 1
end
rescue JSON::ParserError => 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('stage8') do
hestia_change_sys_config_value("PROXY_PORT", "8078")
hestia_change_sys_config_value("PROXY_SSL_PORT", "8079")
end
#stage8 активация из запуск nginx-system
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')
# Start nginx-system service
system('systemctl enable nginx-system')
system('systemctl start nginx-system')
end
when :migratetobunkerweb
log_migrate_stage('stage10') do
if system('/usr/local/hestia/bin/v-ext-modules enable bunkerweb_module')
output = IO.popen("/usr/local/hestia/bin/v-ext-modules state bunkerweb_module json").read
begin
parsed = JSON.parse(output)
if parsed.is_a?(Array) && parsed.first && parsed.first['STATE'] == 'enabled'
result = system('/usr/local/hestia/bin/v-ext-modules-run bunkerweb_module configure')
unless result
hestia_print_error_message_to_cli "bunkerweb_module configure command failed"
exit 1
end
else
hestia_print_error_message_to_cli "bunkerweb_module not enabled after enable command"
exit 1
end
rescue JSON::ParserError => 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 bunkerweb_module"
exit 1
end
hestia_print_info_message_to_cli "Ожидаем минуту для перезапуска сервиса bunkerweb..."
sleep 60
end
LIST_DOMAINS=[]
log_migrate_stage('stage11') do
hestia_print_info_message_to_cli "Stage 11: Migrating users and domains to BunkerWeb..."
# Get all users from HestiaCP in JSON format
user_list_output = IO.popen("/usr/local/hestia/bin/v-list-users json").read
begin
user_data = JSON.parse(user_list_output)
rescue JSON::ParserError => e
hestia_print_error_message_to_cli "Failed to parse users JSON: #{e.message}"
exit 1
end
# Iterate over each user
user_data.each do |username, user_info|
next unless user_info.is_a?(Hash)
web_domains_count = user_info['U_WEB_DOMAINS']
next unless web_domains_count && web_domains_count.to_i > 0
hestia_print_info_message_to_cli "Processing user: #{username} (#{web_domains_count} domains)"
# Get web domains for this user in JSON format
domain_list_output = IO.popen("/usr/local/hestia/bin/v-list-web-domains #{username} json").read
begin
domain_data = JSON.parse(domain_list_output)
rescue JSON::ParserError => e
hestia_print_error_message_to_cli "Failed to parse domains JSON for user #{username}: #{e.message}"
next
end
# Process each domain
domain_data.each do |domain_name, domain_info|
next unless domain_info.is_a?(Hash)
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?
# Hestia uses comma-separated, pass as-is to v-bunkerweb-module (it handles conversion internally)
aliases_list = raw_aliases
else
aliases_list = domain_name # No aliases, use domain name only
end
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)
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
end
# Add aliases to the domain via module script
begin
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
end
# Handle SSL configuration if enabled
if is_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")
hestia_print_error_message_to_cli "Warning: SSL files not found for #{domain_name}"
LIST_DOMAINS << {
user: username,
domain: domain_name,
is_ssl: true,
path_to_ssl: nil
}
next
end
# Define bunkerweb directory for this domain's certificates
bunkerweb_ssl_dir = "/home/#{username}/conf/web/#{domain_name}/ssl/bunkerweb"
begin
# Create bunkerweb SSL directory if it doesn't exist
FileUtils.mkdir_p(bunkerweb_ssl_dir)
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"
# Copy all SSL files if they exist (matching v-add-web-domain-ssl behavior)
original_crt = "#{original_ssl_dir}/#{domain_name}.crt"
original_pem = "#{original_ssl_dir}/#{domain_name}.pem"
original_key = "#{original_ssl_dir}/#{domain_name}.key"
original_ca = "#{original_ssl_dir}/#{domain_name}.ca"
# Copy .crt file if exists
if File.exist?(original_crt)
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(original_pem, cert_path_bunkerweb)
end
# Copy .key file if exists
if File.exist?(original_key)
FileUtils.cp(original_key, key_path_bunkerweb)
end
# Copy .ca file if exists
if File.exist?(original_ca)
FileUtils.cp(original_ca, "#{bunkerweb_ssl_dir}/#{domain_name}.ca")
end
# Set ownership and permissions (nginx user can read, others cannot)
FileUtils.chown('root', 'nginx', bunkerweb_ssl_dir)
FileUtils.chmod(0755, bunkerweb_ssl_dir)
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)
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)
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 = crt_path_bunkerweb
rescue => e
hestia_print_error_message_to_cli "Failed to configure SSL for #{domain_name}: #{e.message}"
end
end
# Populate LIST_DOMAINS array with domain info
LIST_DOMAINS << {
user: username,
domain: domain_name,
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
}
hestia_print_info_message_to_cli "Successfully migrated #{domain_name} for user #{username}"
end
end
# Output the populated LIST_DOMAINS array
if !LIST_DOMAINS.empty?
hestia_print_info_message_to_cli "\n=== Populated LIST_DOMAINS ==="
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
else
hestia_print_error_message_to_cli "No domains were migrated to BunkerWeb"
end
hestia_print_info_message_to_cli "Stage 11 completed."
end
else
hestia_print_error_message_to_cli "unknown command (use migratetobunkerweb or migratenginx)"
log_event E_ARGS, $ARGUMENTS
exit 1
end
exit 0

267
bin/v-bunkerweb-module Executable file
View File

@@ -0,0 +1,267 @@
#!/opt/brepo/ruby33/bin/ruby
# info: action with bunkerweb API
# options: COMMAND [SERVICE_NAME | SSL_CERT | SSL_KEY | FORMAT]
#
# example: v-bunkerweb-module list json
#
# This function enables and disables additional modules
#
# Commands:
# add [domain_name] [ip]
# addssl [domain_name] [SSL_CERT_PATH] [SSL_KEY_PATH]
# delete [domain_name]
# updssl [domain] [SSL_CERT_PATH] [SSL_KEY_PATH]
# list
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
v_command = ARGV[0]
v_format = nil
require "/usr/local/hestia/func_ruby/global_options"
load_ruby_options_defaults
$HESTIA = load_hestia_default_path_from_env
require "main"
require "modules"
require "HestiaBunkerWebApi"
require 'json' unless defined?(JSON)
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 [COMMAND_OPTIONS] [ACTION]"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
case v_command.to_sym
when :add
v_domain = ARGV[1].strip
v_ip = ARGV[2] ? ARGV[2].strip : nil
v_format = ARGV[3] unless ARGV[2].nil?
if v_domain.nil? || v_domain == "" || v_ip.nil? || v_ip == ""
hestia_print_error_message_to_cli "domain and ip should not be empty"
log_event E_ARGS, $ARGUMENTS
exit 1
else
begin
api = HestiaBunkerWebApi.new("http://127.0.0.1:8888")
existing_services = api.list_services()
if existing_services.nil?
result_arr = []
else
if existing_services["services"]
if existing_services["services"].any? { |s| s["id"] == v_domain }
hestia_print_error_message_to_cli "domain already exists"
log_event E_EXISTS, $ARGUMENTS
exit 1
end
result_arr = existing_services["services"]
else
result_arr = []
end
end
api.create_service(v_domain, {
ssl: "no",
reverse_proxy_host: "http://#{v_ip}:#{$PROXY_PORT}"
})
rescue BunkerWebApiError => e
hestia_print_error_message_to_cli "[ERROR] Ошибка API: #{e.message}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
end
when :alias
v_domain = ARGV[1].strip
v_alias = ARGV[2].strip
v_format = ARGV[3] unless ARGV[3].nil?
if v_domain.nil? || v_domain == ""
hestia_print_error_message_to_cli "domain should not be empty"
log_event E_ARGS, $ARGUMENTS
exit 1
else
begin
api = HestiaBunkerWebApi.new("http://127.0.0.1:8888")
existing_services = api.list_services()
if existing_services.nil?
result_arr = []
else
if existing_services["services"]
result_arr = existing_services["services"]
else
result_arr = []
end
end
unless result_arr.any? { |s| s["id"] == v_domain }
hestia_print_error_message_to_cli "domain does not exist"
log_event E_NOTEXIST, $ARGUMENTS
exit 1
end
api.set_alias(v_domain, v_alias)
rescue BunkerWebApiError => e
hestia_print_error_message_to_cli "[ERROR] Ошибка API: #{e.message}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
end
when :delete
v_domain = ARGV[1].strip
v_format = ARGV[2] unless ARGV[2].nil?
if v_domain.nil? || v_domain == ""
hestia_print_error_message_to_cli "domain should not be empty"
log_event E_ARGS, $ARGUMENTS
exit 1
else
begin
api = HestiaBunkerWebApi.new("http://127.0.0.1:8888")
existing_services = api.list_services()
if existing_services.nil?
result_arr = []
else
if existing_services["services"]
result_arr = existing_services["services"]
else
result_arr = []
end
end
unless result_arr.any? { |s| s["id"] == v_domain }
hestia_print_error_message_to_cli "domain does not exist"
log_event E_NOTEXIST, $ARGUMENTS
exit 1
end
api.delete_service(v_domain)
rescue BunkerWebApiError => e
hestia_print_error_message_to_cli "[ERROR] Ошибка API: #{e.message}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
end
when :addssl, :updssl
v_domain = ARGV[1].strip
v_ssl_cert = ARGV[2]
v_ssl_key = ARGV[3]
v_format = ARGV[4] unless ARGV[4].nil?
if v_domain.nil? || v_domain == "" || v_ssl_cert.nil? || v_ssl_key.nil? || !File.exist?(v_ssl_cert) || !File.exist?(v_ssl_key)
hestia_print_error_message_to_cli "domain, SSL cert and key must be provided and must exist"
log_event E_ARGS, $ARGUMENTS
exit 1
else
begin
api = HestiaBunkerWebApi.new("http://127.0.0.1:8888")
existing_services = api.list_services()
if existing_services.nil?
result_arr = []
else
if existing_services["services"]
result_arr = existing_services["services"]
else
result_arr = []
end
end
unless result_arr.any? { |s| s["id"] == v_domain }
hestia_print_error_message_to_cli "domain does not exist"
log_event E_NOTEXIST, $ARGUMENTS
exit 1
end
api.update_service_ssl(v_domain, v_ssl_cert, v_ssl_key)
rescue BunkerWebApiError => e
hestia_print_error_message_to_cli "[ERROR] Ошибка API: #{e.message}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
end
when :deletessl
v_domain = ARGV[1].strip
v_format = ARGV[2] unless ARGV[2].nil?
if v_domain.nil? || v_domain == ""
hestia_print_error_message_to_cli "domain should not be empty"
log_event E_ARGS, $ARGUMENTS
exit 1
else
begin
api = HestiaBunkerWebApi.new("http://127.0.0.1:8888")
existing_services = api.list_services()
if existing_services.nil?
result_arr = []
else
if existing_services["services"]
result_arr = existing_services["services"]
else
result_arr = []
end
end
unless result_arr.any? { |s| s["id"] == v_domain }
hestia_print_error_message_to_cli "domain does not exist"
log_event E_NOTEXIST, $ARGUMENTS
exit 1
end
api.delete_service_ssl(v_domain)
rescue BunkerWebApiError => e
hestia_print_error_message_to_cli "[ERROR] Ошибка API: #{e.message}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
end
when :list
v_format = ARGV[1] unless ARGV[1].nil?
begin
api = HestiaBunkerWebApi.new("http://127.0.0.1:8888")
existing_services = api.list_services()
if existing_services.nil?
result_arr = []
else
if existing_services["services"]
result_arr = existing_services["services"]
else
result_arr = []
end
end
hestia_print_array_of_hashes(result_arr, v_format, "id, method, is_draft, creation_date, last_update, template, security_mode")
rescue BunkerWebApiError => e
hestia_print_error_message_to_cli "[ERROR] Ошибка API: #{e.message}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
else
hestia_print_error_message_to_cli "unknown command"
log_event E_ARGS, $ARGUMENTS
exit 1
end
exit 0

488
bin/v-bunkerweb-module-install Executable file
View File

@@ -0,0 +1,488 @@
#!/opt/brepo/ruby33/bin/ruby
# info: action with bunkerweb API
# options: [SSL_CERT_PATH SSL_KEY_PATH]
#
# example: v-bunkerweb-module-install
#
# This function enables and disables additional modules
#
#------------------------------------------#
# Variables & Functions #
#------------------------------------------#
# Argument definition
require "/usr/local/hestia/func_ruby/global_options"
load_ruby_options_defaults
$HESTIA = load_hestia_default_path_from_env
require "main"
require "modules"
require "HestiaBunkerWebApi"
require "securerandom"
require "socket"
require 'json' unless defined?(JSON)
require 'etc'
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"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#------------------------------------------#
# Action #
#------------------------------------------#
puts "========================================================"
puts "=== AUTOMATED BUNKERWEB SETUP SCRIPT ==="
puts "========================================================"
puts ""
# Parse command line arguments for SSL cert and key paths
SSL_CERT_PATH = ARGV[0] || nil # First argument: SSL certificate path
SSL_KEY_PATH = ARGV[1] || nil # Second argument: SSL key path
# Generate secure passwords (meeting BunkerWeb password policy requirements)
api_env_file = "/etc/bunkerweb/api.env"
api_password = nil
if File.exist?(api_env_file)
File.foreach(api_env_file) do |line|
if line =~ /^\s*API_PASSWORD=(.*)/
val = $1.strip
api_password = val unless val.empty?
break
end
end
end
specials = '!@#$%^&*()-_=+[]{}|;:,.<>?'
API_PASSWORD = api_password || (SecureRandom.alphanumeric(24) + specials.chars.sample(3).join).chars.shuffle.join
ui_env_file = "/etc/bunkerweb/ui.env"
admin_password = nil
if File.exist?(ui_env_file)
File.foreach(ui_env_file) do |line|
if line =~ /^\s*ADMIN_PASSWORD=(.*)/
val = $1.strip
admin_password = val unless val.empty?
break
end
end
end
ADMIN_PASSWORD = admin_password || (SecureRandom.alphanumeric(24) + specials.chars.sample(3).join).chars.shuffle.join
puts "[INFO] Get server IP address"
server_ip_addr = "127.0.0.1" # default fallback
# Attempt to retrieve server IP via Hestia utility
begin
cmd = "/usr/local/hestia/bin/v-list-sys-ips json"
ips_output = `#{cmd}`.strip
unless ips_output.empty?
parsed_ips = JSON.parse(ips_output)
parsed_ips.each do |ip, details|
if details["OWNER"] == "admin"
server_ip_addr = ip
break
end
end
end
rescue JSON::ParserError, Errno::ENOENT
# If the command fails or output is invalid, keep default fallback
end
puts ""
# Compute nginx group ID once
nginx_gid = Etc.getgrnam('nginx').gid
# Step 1: Create /etc/bunkerweb/api.env configuration file
puts "[INFO] Creating API configuration at /etc/bunkerweb/api.env..."
api_env_content = <<~APIENV
# ==============================
# BunkerWeb API Configuration
# This file lists all supported API environment variables with their defaults.
# Uncomment and adjust as needed. Lines starting with # are ignored.
# ==============================
# --- Network & Proxy ---
# Listen address/port for the API
LISTEN_ADDR=127.0.0.1
LISTEN_PORT=8888
# Trusted proxy IPs for X-Forwarded-* headers (comma-separated).
# Default is restricted to loopback for security.
FORWARDED_ALLOW_IPS=127.0.0.1,::1
# Trusted proxy IPs for PROXY protocol (comma-separated).
# Defaults to FORWARDED_ALLOW_IPS when unset.
PROXY_ALLOW_IPS=127.0.0.1,::1
# --- Logging & Runtime ---
# LOG_LEVEL affects most components; CUSTOM_LOG_LEVEL overrides when provided.
# LOG_LEVEL=info
LOG_TYPES=file
LOG_FILE_PATH=/var/log/bunkerweb/api.log
# Number of workers/threads (auto if unset).
# MAX_WORKERS=<auto>
# MAX_THREADS=<auto>
# --- Authentication & Authorization ---
# Optional admin Bearer token (grants full access when provided).
# API_TOKEN=#{API_PASSWORD}
# Bootstrap admin user (created/validated on startup if provided).
API_USERNAME=admin
API_PASSWORD=#{API_PASSWORD}
# Force re-applying bootstrap admin credentials on startup (use with care).
# OVERRIDE_API_CREDS=no
# Fine-grained ACLs can be enabled/disabled here.
# API_ACL_BOOTSTRAP_FILE=
# --- IP allowlist ---
# Enable and shape inbound IP allowlist for the API.
API_WHITELIST_ENABLED=yes
WHITELIST_IPS=127.0.0.1
# --- FastAPI surface ---
# Customize or disable documentation endpoints. Use 'disabled' to turn off.
# API_TITLE=BunkerWeb API
# API_DOCS_URL=/docs
# API_REDOC_URL=/redoc
# API_OPENAPI_URL=/openapi.json
# Mount the API under a subpath (useful behind reverse proxies).
# API_ROOT_PATH=
# --- TLS/SSL ---
# Enable TLS for the API listener (requires cert and key).
# API_SSL_ENABLED=no
# Path to PEM-encoded certificate and private key.
# API_SSL_CERTFILE=/etc/ssl/certs/bunkerweb-api.crt
# API_SSL_KEYFILE=/etc/ssl/private/bunkerweb-api.key
# Optional chain/CA bundle and cipher suite.
# API_SSL_CA_CERTS=
# API_SSL_CIPHERS_CUSTOM=
# API_SSL_CIPHERS_LEVEL=modern # choices: modern|intermediate
# --- Biscuit keys & policy ---
# Bind token to client IP (except private ranges).
# CHECK_PRIVATE_IP=yes
# Biscuit token lifetime in seconds (0 disables expiry).
# API_BISCUIT_TTL_SECONDS=3600
# Provide Biscuit keys via env (hex) instead of files.
# BISCUIT_PUBLIC_KEY=
# BISCUIT_PRIVATE_KEY=
# --- Rate limiting ---
# Enable/disable and shape rate limiting.
API_RATE_LIMIT_ENABLED=no
API_RATE_LIMIT_HEADERS_ENABLED=no
# Global default limit (times per seconds).
# API_RATE_LIMIT_TIMES=100
# API_RATE_LIMIT_SECONDS=60
# Authentication endpoint limit.
# API_RATE_LIMIT_AUTH_TIMES=10
# API_RATE_LIMIT_AUTH_SECONDS=60
# Advanced limits and rules (CSV/JSON/YAML).
# API_RATE_LIMIT_DEFAULTS="200/minute"
# API_RATE_LIMIT_APPLICATION_LIMITS=
# API_RATE_LIMIT_RULES=
# Strategy: fixed-window | moving-window | sliding-window-counter
# API_RATE_LIMIT_STRATEGY=fixed-window
# Key selector: ip | user | path | method | header:<Name>
# API_RATE_LIMIT_KEY=ip
# Exempt IPs (space or comma-separated CIDRs).
# API_RATE_LIMIT_EXEMPT_IPS=
# Storage options in JSON (merged with Redis settings if USE_REDIS=yes).
# API_RATE_LIMIT_STORAGE_OPTIONS=
# --- Redis (optional, for rate limiting storage) ---
# USE_REDIS=no
# REDIS_HOST=
# REDIS_PORT=6379
# REDIS_DATABASE=0
# REDIS_USERNAME=
# REDIS_PASSWORD=
# REDIS_SSL=no
# REDIS_SSL_VERIFY=yes
# REDIS_TIMEOUT=1000
# REDIS_KEEPALIVE_POOL=10
# REDIS_SENTINEL_HOSTS=sentinel1:26379 sentinel2:26379
# REDIS_SENTINEL_MASTER=mymaster
# REDIS_SENTINEL_USERNAME=
# REDIS_SENTINEL_PASSWORD=
APIENV
File.write("/etc/bunkerweb/api.env", api_env_content)
File.chmod(0o660, "/etc/bunkerweb/api.env")
File.chown(0, nginx_gid, "/etc/bunkerweb/api.env")
puts "[SUCCESS] API configuration file created at /etc/bunkerweb/api.env"
puts ""
variables_env_content = <<~VENV
DNS_RESOLVERS=9.9.9.9 149.112.112.112 8.8.8.8 8.8.4.4
HTTP_PORT=80
HTTPS_PORT=443
API_LISTEN_IP=127.0.0.1
MULTISITE=yes
UI_HOST=http://127.0.0.1:7000
SERVER_NAME=
API_WHITELIST_IP=127.0.0.0/8
USE_SERVE_FILES=no
VENV
File.write("/etc/bunkerweb/variables.env", variables_env_content)
File.chmod(0o660, "/etc/bunkerweb/variables.env")
File.chown(0, nginx_gid, "/etc/bunkerweb/variables.env")
puts "[SUCCESS] Variables configuration file created at /etc/bunkerweb/variables.env"
puts ""
# Step 2: Enable and start bunkerweb-api service, wait for it to be running
puts "[INFO] Enabling bunkerweb-api service..."
system("systemctl enable bunkerweb-api")
puts "[INFO] Starting bunkerweb-api service..."
system("systemctl start bunkerweb-api")
sleep(30)
# Wait for the service to be ready (max 30 seconds)
puts "[INFO] Waiting for bunkerweb-api service to be running..."
max_attempts = 60 # Wait up to 30 seconds (check every half second)
attempt = 0
while attempt < max_attempts
status_output = `systemctl is-active bunkerweb-api 2>&1`
status = status_output.strip
if status == "active" || status == "running"
puts "[SUCCESS] bunkerweb-api service is running!"
break
elsif status == "failed"
puts "[ERROR] bunkerweb-api service failed to start!"
exit 1
else
print "."
sleep(0.5)
attempt += 1
end
end
if attempt >= max_attempts
puts ""
puts "[ERROR] bunkerweb-api service did not become active within timeout"
puts "[INFO] Current status: #{status_output.strip}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
puts ""
# Step 3: Configure UI settings based on documentation at https://docs.bunkerweb.io/latest/web-ui/
puts "[INFO] Configuring Web UI..."
# Determine SSL settings for UI
ui_ssl_enabled = "no"
if SSL_CERT_PATH && SSL_KEY_PATH && File.exist?(SSL_CERT_PATH) && File.exist?(SSL_KEY_PATH)
ui_ssl_enabled = "yes"
end
ui_env_content = <<~UIENV
# ==============================
# BunkerWeb UI Configuration
# This file configures the Web UI settings.
# ==============================
# --- Listener & TLS ---
# Bind address for the UI (use server IP for external access)
UI_LISTEN_ADDR=127.0.0.1
# Bind port for the UI
UI_LISTEN_PORT=7000
# Enable TLS in the UI container
UI_SSL_ENABLED=#{ui_ssl_enabled}
UIENV
# Add SSL cert/key paths if provided
if ui_ssl_enabled == "yes"
ui_env_content += <<~SSLCONF
# SSL Certificate and Key paths
UI_SSL_CERTFILE=#{SSL_CERT_PATH}
UI_SSL_KEYFILE=#{SSL_KEY_PATH}
SSLCONF
end
ui_env_content += <<~UIENV2
# --- Admin Authentication ---
# Seed admin account
ADMIN_USERNAME=admin
ADMIN_PASSWORD=#{ADMIN_PASSWORD}
# --- Proxy settings ---
# Trusted proxy IPs for X-Forwarded-* headers
# UI_FORWARDED_ALLOW_IPS=127.0.0.1,::1
UIENV2
File.write("/etc/bunkerweb/ui.env", ui_env_content)
File.chmod(0o660, "/etc/bunkerweb/ui.env")
File.chown(0, nginx_gid, "/etc/bunkerweb/ui.env")
puts "[SUCCESS] UI configuration file created at /etc/bunkerweb/ui.env"
puts ""
# Step 4: Reload the bunkerweb-ui service to apply new configuration
puts "[INFO] Reloading bunkerweb-ui service..."
system("systemctl restart bunkerweb-ui")
# Wait for UI to be ready (max 10 seconds)
sleep(2)
if system("systemctl is-active bunkerweb-ui >/dev/null 2>&1")
puts "[SUCCESS] bunkerweb-ui service is running!"
else
puts "[WARN] bunkerweb-ui service status could not be verified"
end
puts ""
# Step 5: Now proceed with the original service creation logic
puts "[INFO] Connecting to BunkerWeb API..."
API_URL = "http://127.0.0.1:8888"
USERNAME = "admin"
PASSWORD = API_PASSWORD
# Default services to create after setup
DEFAULT_SERVICES = [
{
name: "#{server_ip_addr}",
options: {
ssl: "no",
reverse_proxy_host: "http://127.0.0.1:7000",
use_template: "ui",
reverse_proxy_url: "/kormilo",
use_reverse_proxy: "yes"
}
},
# Add more services here if needed:
# {
# name: "secure.example.com",
# options: {
# ssl: "yes",
# certificate_path: "/etc/ssl/certs/example.crt",
# key_path: "/etc/ssl/private/example.key"
# }
# }
]
begin
api = HestiaBunkerWebApi.new(API_URL, USERNAME, PASSWORD)
puts ""
puts "[SUCCESS] API connected successfully!"
puts ""
# List existing services
services = api.list_services()
services = api.list_services()
if services && services.is_a?(Hash) && services.key?('services')
services = services['services']
services = nil if services.is_a?(Array) && services.empty?
else
services = nil
end
if services.nil?
puts "[INFO] No services found - creating default configuration..."
DEFAULT_SERVICES.each do |service_config|
begin
puts "[INFO] Creating service: #{service_config[:name]}"
result = api.create_service(service_config[:name], service_config[:options])
puts " ✓ Service '#{service_config[:name]}' created"
rescue BunkerWebApiError => e
if e.message.include?("already exists")
puts " Service '#{service_config[:name]}' already exists, skipping..."
else
raise
end
end
end
puts "[SUCCESS] Default services created!"
else
puts "[INFO] Existing services:"
services.each { |s| puts " - #{s['server_name']}" }
puts ""
end
# Reload configuration on all instances
puts "[INFO] Reloading configuration..."
api.reload_instance()
puts "[INFO] Restarting bunkerweb service..."
system("systemctl restart bunkerweb")
puts "[INFO] Restarting bunkerweb-scheduler service..."
system("systemctl restart bunkerweb-scheduler")
puts ""
puts "======================================================"
puts "=== SETUP COMPLETED SUCCESSFULLY ==="
puts "======================================================"
puts ""
puts "Web UI is now accessible at:"
if ui_ssl_enabled == "yes"
puts " https://#{server_ip_addr}/kormilo"
else
puts " http://#{server_ip_addr}/kormilo"
end
puts ""
puts "API URL: #{API_URL}"
puts ""
puts "API Credentials:"
puts " Username: admin"
puts " Password: #{PASSWORD}"
puts ""
puts "UI Credentials:"
puts " Username: admin"
puts " Password: #{ADMIN_PASSWORD}"
puts ""
rescue BunkerWebApiError => e
if e.message.include?("Authentication") || e.message.include?("Connection refused")
puts "[ERROR] Could not connect to BunkerWeb API"
puts "[INFO] This means the setup has NOT been completed correctly"
puts ""
puts "Please verify that:"
puts " 1. bunkerweb-api service is running: systemctl status bunkerweb-api"
puts " 2. API configuration file exists at /etc/bunkerweb/api.env"
puts " 3. Check logs: journalctl -u bunkerweb-api -f"
puts ""
log_event E_INVALID, $ARGUMENTS
exit 1
else
puts "[ERROR] #{e.message}"
log_event E_INVALID, $ARGUMENTS
exit 1
end
rescue => e
puts "[ERROR] Unexpected error: #{e.message}"
puts "Backtrace:"
puts e.backtrace.inspect
log_event E_INVALID, $ARGUMENTS
exit 1
end
exit 0

View File

@@ -68,13 +68,13 @@ if [ "$type" = "pma" ] || [ "$type" = "PMA" ] || [ "$type" = "phpmyadmin" ]; the
$BIN/v-restart-service httpd
fi
if [ -e "/etc/nginx/conf.d/phpmyadmin.inc" ]; then
rm -f /etc/nginx/conf.d/phpmyadmin.inc
cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/phpmyadmin.inc
sed -i "s|%pma_alias%|$alias|g" /etc/nginx/conf.d/phpmyadmin.inc
if [ -e "/usr/local/hestia/nginx-system/etc/nginx/conf.d/phpmyadmin.inc" ]; then
rm -f /usr/local/hestia/nginx-system/etc/nginx/conf.d/phpmyadmin.inc
cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /usr/local/hestia/nginx-system/etc/nginx/conf.d/phpmyadmin.inc
sed -i "s|%pma_alias%|$alias|g" /usr/local/hestia/nginx-system/etc/nginx/conf.d/phpmyadmin.inc
# Restart services
$BIN/v-restart-service nginx
$BIN/v-restart-service nginx-system
fi
fi
@@ -105,13 +105,13 @@ if [ "$type" = "pga" ] || [ "$type" = "PGA" ] || [ "$type" = "phppgadmin" ]; the
$BIN/v-restart-service httpd
fi
if [ -e "/etc/nginx/conf.d/phppgadmin.inc" ]; then
rm -f /etc/nginx/conf.d/phppgadmin.inc
cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/phppgadmin.inc
sed -i "s|%pga_alias%|$alias|g" /etc/nginx/conf.d/phppgadmin.inc
if [ -e "/usr/local/hestia/nginx-system/etc/nginx/conf.d/phppgadmin.inc" ]; then
rm -f /usr/local/hestia/nginx-system/etc/nginx/conf.d/phppgadmin.inc
cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /usr/local/hestia/nginx-system/etc/nginx/conf.d/phppgadmin.inc
sed -i "s|%pga_alias%|$alias|g" /usr/local/hestia/nginx-system/etc/nginx/conf.d/phppgadmin.inc
# Restart services
$BIN/v-restart-service nginx
$BIN/v-restart-service nginx-system
fi
fi

View File

@@ -65,18 +65,18 @@ fi
# Defining dst config path
case $service in
nginx) dst='/etc/nginx/nginx.conf' ;;
nginx) dst='/usr/local/hestia/nginx-system/etc/nginx/nginx.conf' ;;
httpd) dst='/etc/httpd/conf/httpd.conf' ;;
apache2) dst='/etc/apache2/apache2.conf' ;;
exim) dst='/etc/exim/exim.conf' ;;
exim4) dst='/etc/exim4/exim4.conf.template' ;;
vsftpd) dst=$(find /etc/vsftpd* -name 'vsftpd.conf') ;;
proftpd) dst=$(find /etc/proftpd* -name 'proftpd.conf') ;;
php)
php)
if [ "$LOCAL_PHP" == "yes" ]; then
dst=$(find /opt/brepo/php${php_v} -name php.ini)
dst=$(find /opt/brepo/php${php_v} -name php.ini)
else
dst=$(find /etc/opt/remi/php${php_v} -name php.ini)
dst=$(find /etc/opt/remi/php${php_v} -name php.ini)
fi
;;
mysql) dst=$(find /etc/my* -name my.cnf) ;;
@@ -136,7 +136,7 @@ if [ "$update" = 'yes' ] && [ "$restart" != 'no' ]; then
fi
fi
$BIN/v-restart-service "$service" > /dev/null 2>&1
$BIN/v-restart-service "$service" > /dev/null 2>&1
if [ $? -ne 0 ]; then
for config in $dst; do

View File

@@ -88,6 +88,25 @@ check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ] && [ "$BUNKERWEB" = "yes" ]; then
BUNKW_DIR=$HOMEDIR/$user/conf/web/$domain/ssl/bunkerweb
if [ ! -d "$BUNKW_DIR" ]; then
mkdir -p "$BUNKW_DIR"
chmod 0755 "$BUNKW_DIR"
fi
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.crt" "$BUNKW_DIR/"
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.key" "$BUNKW_DIR/"
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.pem" "$BUNKW_DIR/"
if [ -e "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.ca" ]; then
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.ca" "$BUNKW_DIR/"
fi
chown root:nginx "$BUNKW_DIR"/*
chmod 0640 "$BUNKW_DIR"/*
$BIN/v-ext-modules-run bunkerweb_module addssl "$domain" "$BUNKW_DIR/$domain.pem" "$BUNKW_DIR/$domain.key"
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "SSL certificate changed (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"

View File

@@ -31,7 +31,7 @@ SERVICE_NAME="$1"
case "$SERVICE_NAME" in
nginx )
/usr/sbin/nginx -t >> "$DEBUG_LOG_FILE" 2>&1
/usr/local/hestia/nginx-system/sbin/nginx -t >> "$DEBUG_LOG_FILE" 2>&1
V_RESULT=$?
exit $V_RESULT
;;
@@ -47,5 +47,3 @@ esac
# Something like error, we shouldn't be here
exit 1

105
bin/v-default-domain Executable file
View File

@@ -0,0 +1,105 @@
#!/bin/bash
# info: add web/dns/mail domain
# options: USER DOMAIN COMMAND
#
# example: v-default-domain admin example.com set
# v-default-domain admin example.com delete
# v-default-domain admin example.com check
# v-default-domain admin example.com check-default
#
# This function set user's domain as default or reset it or get default domain or check is domain default.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
command=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN COMMAND'
is_format_valid 'user' 'domain'
if [ -n "$ip" ]; then
is_format_valid 'ip'
fi
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
confd="conf.h.d"
if [[ $command == check* ]]; then
default_conf="/etc/httpd/$confd/domains/00000000000000000_default.conf"
if [ -e "$default_conf" ]; then
file_name=$(readlink -f "$default_conf")
s_username=$(echo "$file_name" | cut -d"/" -f3 )
s_domain=$(echo "$file_name" | rev | cut -d"/" -f2 | rev )
if [ "$command" == "check-default" ]; then
echo "$s_username:$s_domain"
else
if [ "$user" == "$s_username" ] && [ "$domain" == "$s_domain" ]; then
echo "true"
else
echo "false"
fi
fi
else
echo "no default domain"
fi
else
# Working on web domain
if [ -n "$WEB_SYSTEM" ] && [ "$WEB_SYSTEM" == "httpd" ]; then
domain_path="/etc/httpd/$confd/domains/$domain.conf"
domain_ssl_path="/etc/httpd/$confd/domains/$domain.ssl.conf"
if [ -e "$domain_ssl_path" ] || [ -e "$domain_path" ]; then
path_name="$domain_path"
if [ -e "$domain_ssl_path" ]; then
path_name="$domain_ssl_path"
fi
if [ "$command" == "delete" ]; then
if [ -e "/etc/httpd/$confd/domains/00000000000000000_default.conf" ]; then
mv -f "/etc/httpd/$confd/domains/00000000000000000_default.conf" "/etc/httpd/$confd/domains/00000000000000000_default.conf.trash"
fi
else
ln -sf "${path_name}" "/etc/httpd/$confd/domains/00000000000000000_default.conf"
fi
echo "true"
else
echo "false"
fi
fi
# Restarting services
$BIN/v-restart-web "yes"
check_result $? "can't restart web" > /dev/null
$BIN/v-restart-proxy "yes"
check_result $? "can't restart proxy" > /dev/null
$BIN/v-restart-dns "yes"
check_result $? "can't restart dns" > /dev/null
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
exit

View File

@@ -49,7 +49,7 @@ if [ -f "$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.fastcgi_cache.conf" ]; then
rm -rf $HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.fastcgi_cache.conf
fi
conf='/etc/nginx/conf.d/fastcgi_cache_pool.conf'
conf='/usr/local/hestia/nginx-system/etc/nginx/conf.d/fastcgi_cache_pool.conf'
if [ -f "$conf" ]; then
sed -i "/ keys_zone=$domain/d" $conf
if [ ! -s "$conf" ]; then
@@ -58,7 +58,7 @@ if [ -f "$conf" ]; then
fi
# Delete FastCGI cache folder
if [ -d "/var/cache/nginx/micro/$domain" ]; then
if [ -d "/usr/local/hestia/nginx-system/var/cache/nginx/micro/$domain" ]; then
rm -rf /var/cache/nginx/micro/$domain
fi

View File

@@ -170,6 +170,12 @@ check_result $? "Proxy restart failed" > /dev/null
$BIN/v-restart-web-backend "$restart" "$version"
check_result $? "PHP restart failed" > /dev/null
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ]; then
$BIN/v-ext-modules-run bunkerweb_module delete "$domain"
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Deleted web domain (Name: $domain)."
log_event "$OK" "$ARGUMENTS"

View File

@@ -92,6 +92,13 @@ check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ]; then
get_domain_values 'web'
$BIN/v-ext-modules-run bunkerweb_module alias "$domain" "$ALIAS"
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Deleted web domain alias (Alias: $dom_alias, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"

View File

@@ -90,6 +90,16 @@ check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ] && [ "$BUNKERWEB" = "yes" ]; then
BUNKW_DIR=$HOMEDIR/$user/conf/web/$domain/ssl/bunkerweb
if [ -d "$BUNKW_DIR" ]; then
$BIN/v-ext-modules-run bunkerweb_module deletessl "$domain"
rm -f $BUNKW_DIR/$domain.*
fi
fi
# Logging
$BIN/v-log-action "$user" "Warning" "Web" "SSL disabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"

View File

@@ -65,7 +65,7 @@ csv_list() {
#----------------------------------------------------------#
# Defining config path
config_path='/etc/nginx/nginx.conf'
config_path='/usr/local/hestia/nginx-system/etc/nginx/nginx.conf'
# Defining keys
keys="worker_processes |worker_connections |send_timeout"

View File

@@ -34,7 +34,8 @@ json_list() {
"STATE": "'$STATE'",
"CPU": "'$CPU'",
"MEM": "'$MEM'",
"RTIME": "'$RTIME'"
"RTIME": "'$RTIME'",
"PMEM": "'$PHM'"
}'
if [ "$i" -lt "$objects" ]; then
echo ','
@@ -49,11 +50,11 @@ json_list() {
# SHELL list function
shell_list() {
IFS=$'\n'
echo "NAME STATE CPU MEM UPTIME"
echo "---- ----- --- --- ------"
echo "NAME STATE CPU MEM UPTIME PMEM"
echo "---- ----- --- --- ------ ----"
while read str; do
parse_object_kv_list "$str"
echo "$NAME $STATE $CPU $MEM $RTIME"
echo "$NAME $STATE $CPU $MEM $RTIME $PHM"
done < <(echo -e "$data" | grep NAME)
}
@@ -62,17 +63,17 @@ plain_list() {
IFS=$'\n'
while read str; do
parse_object_kv_list "$str"
echo -e "$NAME\t$SYSTEM\t$STATE\t$CPU\t$MEM\t$RTIME"
echo -e "$NAME\t$SYSTEM\t$STATE\t$CPU\t$MEM\t$RTIME\t$PHM"
done < <(echo -e "$data" | grep NAME)
}
# CSV list function
csv_list() {
IFS=$'\n'
echo "NAME,SYSTEM,STATE,CPU,MEM,RTIME"
echo "NAME,SYSTEM,STATE,CPU,MEM,RTIME,PMEM"
while read str; do
parse_object_kv_list "$str"
echo "$NAME,\"$SYSTEM\",$STATE,$CPU,$MEM,$RTIME"
echo "$NAME,\"$SYSTEM\",$STATE,$CPU,$MEM,$RTIME,$PHM"
done < <(echo -e "$data" | grep NAME)
}
@@ -84,6 +85,7 @@ get_srv_state() {
mem=0
cpu=0
rtime="0"
pmem=0
# Searching related pids
if [ "$name" = "php-fpm" ]; then
@@ -91,7 +93,11 @@ get_srv_state() {
for php_pid in $(pidof php-fpm); do
process_info=$(ps -p "$php_pid" -o args | tail -n1 | grep "$srv")
if [ -n "$process_info" ]; then
pids="${pids}|${php_pid}"
if [ -z "$pids" ];then
pids="${php_pid}"
else
pids="${pids}|${php_pid}"
fi
fi
done
else
@@ -123,6 +129,9 @@ get_srv_state() {
mem=$(echo "$pids" | awk '{sum += $3} END {print sum/1024 }')
mem=$(echo "${mem%%.*}")
pmem=$(echo "$pids" | awk '{sum += $4} END {print sum/1024 }')
pmem=$(echo "${pmem%%.*}")
# Searching pid file
pid_file=''
if [ -e "/run/$srv.pid" ]; then
@@ -151,6 +160,7 @@ get_srv_state() {
mem=0
cpu=0
rtime="0"
pmem=0
fi
}
@@ -160,7 +170,7 @@ get_srv_state() {
# Saving current proccess list
tmp_file=$(mktemp)
ps -eo pid,pcpu,size > $tmp_file
ps -eo pid,pcpu,size,rss > $tmp_file
# Checking current time
ctime=$(date +%s)
@@ -169,7 +179,7 @@ ctime=$(date +%s)
if [ -n "$WEB_SYSTEM" ] && [ "$WEB_SYSTEM" != 'remote' ]; then
get_srv_state $WEB_SYSTEM
data="NAME='$WEB_SYSTEM' SYSTEM='web server' STATE='$state' CPU='$cpu'"
data="$data MEM='$mem' RTIME='$rtime'"
data="$data MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking PHP intepreter
@@ -179,18 +189,18 @@ if [ -n "$WEB_BACKEND" ] && [ "$WEB_BACKEND" != 'remote' ]; then
for version in $php_versions; do
proc_name="php-fpm"
service_name="fpm${version}"
get_srv_state "$proc_name"
get_srv_state "/opt/brepo/php${version}" "$proc_name"
data="$data\nNAME='brepo-php-$service_name' SYSTEM='php interpreter' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
done
else
php_versions=$(ls /opt/remi/php*/root/sbin/php-fpm | cut -d'/' -f4 | sed 's|php||')
for version in $php_versions; do
proc_name="php-fpm"
service_name="php${version}"
get_srv_state "$proc_name"
get_srv_state "/opt/remi/php${version}" "$proc_name"
data="$data\nNAME='$service_name-php-fpm' SYSTEM='php interpreter' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
done
fi
fi
@@ -199,7 +209,7 @@ fi
if [ -n "$PROXY_SYSTEM" ] && [ "$PROXY_SYSTEM" != 'remote' ]; then
get_srv_state "$PROXY_SYSTEM"
data="$data\nNAME='$PROXY_SYSTEM' SYSTEM='reverse proxy' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# DNS
@@ -208,21 +218,21 @@ if [ -n "$service" ] && [ "$service" != 'remote' ]; then
proc_name='named'
get_srv_state $service $proc_name
data="$data\nNAME='$service' SYSTEM='dns server' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking MAIL system
if [ -n "$MAIL_SYSTEM" ] && [ "$MAIL_SYSTEM" != 'remote' ]; then
get_srv_state "$MAIL_SYSTEM"
data="$data\nNAME='$MAIL_SYSTEM' SYSTEM='mail server' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking MAIL IMAP
if [ -n "$IMAP_SYSTEM" ] && [ "$IMAP_SYSTEM" != 'remote' ]; then
get_srv_state "$IMAP_SYSTEM"
data="$data\nNAME='$IMAP_SYSTEM' SYSTEM='imap/pop3 server' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking MAIL ANTIVIRUS
@@ -244,7 +254,7 @@ if [ -n "$ANTIVIRUS_SYSTEM" ] && [ "$ANTIVIRUS_SYSTEM" != 'remote' ]; then
get_srv_state "$ANTIVIRUS_SYSTEM" "$proc_name"
fi
data="$data\nNAME='$ANTIVIRUS_SYSTEM' SYSTEM='email anti-virus'"
data="$data STATE='$state' CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data STATE='$state' CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
proc_name=''
fi
@@ -252,7 +262,7 @@ fi
if [ -n "$ANTISPAM_SYSTEM" ] && [ "$ANTISPAM_SYSTEM" != 'remote' ]; then
get_srv_state "$ANTISPAM_SYSTEM" "spamd"
data="$data\nNAME='$ANTISPAM_SYSTEM' SYSTEM='email spam filter'"
data="$data STATE='$state' CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data STATE='$state' CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking DB system
@@ -299,7 +309,7 @@ if [ -n "$DB_SYSTEM" ] && [ "$DB_SYSTEM" != 'remote' ]; then
fi
get_srv_state "$service" "$proc_name"
data="$data\nNAME='$service' SYSTEM='database server' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
proc_name=''
done
fi
@@ -308,21 +318,21 @@ fi
if [ -n "$FTP_SYSTEM" ] && [ "$FTP_SYSTEM" != 'remote' ]; then
get_srv_state "$FTP_SYSTEM"
data="$data\nNAME='$FTP_SYSTEM' SYSTEM='ftp server' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking CRON system
if [ -n "$CRON_SYSTEM" ] && [ "$CRON_SYSTEM" != 'remote' ]; then
get_srv_state "$CRON_SYSTEM"
data="$data\nNAME='$CRON_SYSTEM' SYSTEM='job scheduler' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking SSH daemon
if [ -e "/etc/ssh/sshd_config" ]; then
get_srv_state ssh
data="$data\nNAME='ssh' SYSTEM='ssh server' STATE='$state'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Checking FIREWALL system
@@ -339,7 +349,7 @@ if [ -n "$FIREWALL_SYSTEM" ] && [ "$FIREWALL_SYSTEM" != 'remote' ]; then
fi
fi
data="$data\nNAME='$FIREWALL_SYSTEM' SYSTEM='firewall'"
data="$data STATE='$state' CPU='0' MEM='0' RTIME='0'"
data="$data STATE='$state' CPU='0' MEM='0' RTIME='0' PHM='0'"
fi
# Checking FIREWALL Fail2ban extention
@@ -354,7 +364,7 @@ if [ -n "$FIREWALL_EXTENSION" ]; then
get_srv_state "$FIREWALL_EXTENSION" fail2ban-server script
fi
data="$data\nNAME='$FIREWALL_EXTENSION' SYSTEM='brute-force protection'"
data="$data STATE='$state' CPU='$cpu' MEM='$mem' RTIME='$rtime'"
data="$data STATE='$state' CPU='$cpu' MEM='$mem' RTIME='$rtime' PHM='$pmem'"
fi
# Listing data

View File

@@ -48,13 +48,13 @@ conf=$(grep "DOMAIN='$domain'" "$USER_DATA/web.conf")
parse_object_kv_list "$conf"
# Purge nginx FastCGI cache
if [ -d "/var/cache/nginx/micro/$domain" ]; then
rm -rf /var/cache/nginx/micro/$domain/*
if [ -d "/usr/local/hestia/nginx-system/var/cache/nginx/micro/$domain" ]; then
rm -rf /usr/local/hestia/nginx-system/var/cache/nginx/micro/$domain/*
fi
# Purge nginx proxy cache
if [ -d "/var/cache/nginx/$domain" ]; then
rm -rf /var/cache/nginx/$domain/*
if [ -d "/usr/local/hestia/nginx-system/var/cache/nginx/$domain" ]; then
rm -rf /usr/local/hestia/nginx-system/var/cache/nginx/$domain/*
fi
#----------------------------------------------------------#

View File

@@ -25,10 +25,14 @@ send_email_report() {
email=$(echo "$email" | cut -f 2 -d "'")
tmpfile=$(mktemp)
subj="$(hostname): $PROXY_SYSTEM restart failed"
nginx -t >> $tmpfile 2>&1
/usr/local/hestia/nginx-system/sbin/nginx -t >> $tmpfile 2>&1
if [ "$1" == "DO_RESTART" ]; then
service "$PROXY_SYSTEM" restart >> $tmpfile 2>&1
if [ "$PROXY_SYSTEM" = "nginx" ]; then
service nginx-system restart >> $tmpfile 2>&1
else
service "$PROXY_SYSTEM" restart >> $tmpfile 2>&1
fi
fi
cat "$tmpfile" | $SENDMAIL -s "$subj" "$email"
if [ "$DEBUG_MODE" = "true" ]; then

View File

@@ -73,7 +73,7 @@ for service in $service_list; do
$BIN/v-stop-firewall
$BIN/v-update-firewall
elif [ "$restart" = "ssl" ] && [ "$service" = "nginx" ]; then
service $service upgrade >> $log 2>&1
service nginx-system upgrade >> $log 2>&1
elif [ -z "$restart" -o "$restart" = "no" ] && [ \
"$service" = "nginx" -o \
"$service" = "httpd" -o \
@@ -86,10 +86,19 @@ for service in $service_list; do
"$service" = "proftpd" -o \
"$service" = "ssh" -o \
"$service" = "fail2ban" ]; then
systemctl reload-or-restart "$service" >> $log 2>&1
if [ "$service" = "nginx" ]; then
systemctl reload-or-restart nginx-system >> $log 2>&1
else
systemctl reload-or-restart "$service" >> $log 2>&1
fi
else
systemctl reset-failed "$service" >> $log 2>&1
systemctl restart "$service" >> $log 2>&1
if [ "$service" = "nginx" ]; then
systemctl reset-failed nginx-system >> $log 2>&1
systemctl restart nginx-system >> $log 2>&1
else
systemctl reset-failed "$service" >> $log 2>&1
systemctl restart "$service" >> $log 2>&1
fi
fi
# Check the result of the service restart and report whether it failed.

View File

@@ -47,7 +47,11 @@ for service in $service_list; do
if [ "$service" = "iptables" ]; then
$BIN/v-stop-firewall
else
systemctl stop "$service"
if [ "$service" = "nginx" ]; then
systemctl stop nginx-system
else
systemctl stop "$service"
fi
result=$?
if [ "$result" -ne 0 ]; then
$BIN/v-log-action "system" "Error" "System" "Service failed to stop (Name: $service)."

282
bin/v-system-nginx-migrate Executable file
View File

@@ -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(/(?<!\/usr\/local\/hestia\/nginx-system)\/etc\/nginx/, '/usr/local/hestia/nginx-system/etc/nginx')
if new_content != content
hestia_print_info_message_to_cli "Changed path to config in file #{path}"
File.open(path, 'w') { |f| f.write(new_content) }
end
end
end
log_migrate_stage('nstage4.1') do
# Parse nginx.conf file to replace paths
if migrate_nginx_config_from_file("/usr/local/hestia/nginx-system/etc/nginx/nginx.conf")
hestia_print_info_message_to_cli "Completed migration of nginx.conf paths"
else
hestia_print_error_message_to_cli "Warning: Could not migrate nginx.conf from #{File.expand_path('/usr/local/hestia/nginx-system/etc/nginx/nginx.conf')}"
end
end
log_migrate_stage('nstage7') do
if system('/usr/local/hestia/bin/v-ext-modules enable update_module')
output = IO.popen("/usr/local/hestia/bin/v-ext-modules state update_module json").read
begin
parsed = JSON.parse(output)
if parsed.is_a?(Array) && parsed.first && parsed.first['STATE'] == 'enabled'
system('/usr/local/hestia/bin/v-ext-modules-run update_module synctemplates')
else
hestia_print_error_message_to_cli "update_module not enabled after enable command"
exit 1
end
rescue JSON::ParserError => 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

View File

@@ -86,7 +86,11 @@ if [ -n "$old_ip" ]; then
# Updating PROXY
if [ -n "$PROXY_SYSTEM" ]; then
cd /etc/$PROXY_SYSTEM/$pconfd
if [ "$PROXY_SYSTEM" = "nginx" ]; then
cd /usr/local/hestia/nginx-system/etc/nginx/$pconfd
else
cd /etc/$PROXY_SYSTEM/$pconfd
fi
if [ -e "$old_ip.conf" ]; then
mv $old_ip.conf $new_ip.conf
sed -i "s/$old_ip/$new_ip/g" $new_ip.conf
@@ -95,7 +99,11 @@ if [ -n "$old_ip" ]; then
# Updating WEB
if [ -n "$WEB_SYSTEM" ]; then
cd /etc/$WEB_SYSTEM/$confd
if [ "$WEB_SYSTEM" = "nginx" ]; then
cd /usr/local/hestia/nginx-system/etc/nginx/$confd
else
cd /etc/$WEB_SYSTEM/$confd
fi
if [ -e "$old_ip.conf" ]; then
mv $old_ip.conf $new_ip.conf
@@ -161,8 +169,8 @@ for ip in $ips; do
prefixlen="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[].addr_info[] | if .local == $IP then .prefixlen else empty end')"
netmask="$(convert_cidr "$prefixlen")"
$BIN/v-add-sys-ip "$ip" "$netmask" "$interface"
elif [ -e "/etc/nginx/conf.d/$ip.conf" ]; then
process_http2_directive "/etc/nginx/conf.d/$ip.conf"
elif [ -e "/usr/local/hestia/nginx-system/etc/nginx/conf.d/$ip.conf" ]; then
process_http2_directive "/usr/local/hestia/nginx-system/etc/nginx/conf.d/$ip.conf"
fi
done

View File

@@ -90,6 +90,25 @@ check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Execute bunkerweb_module if it's enabled
module_state=$($BIN/v-ext-modules state bunkerweb_module json | jq -r '.[0].STATE')
if [ "$module_state" = "enabled" ] && [ "$BUNKERWEB" = "yes" ]; then
BUNKW_DIR=$HOMEDIR/$user/conf/web/$domain/ssl/bunkerweb
if [ ! -d "$BUNKW_DIR" ]; then
mkdir -p "$BUNKW_DIR"
chmod 0755 "$BUNKW_DIR"
fi
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.crt" "$BUNKW_DIR/"
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.key" "$BUNKW_DIR/"
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.pem" "$BUNKW_DIR/"
if [ -e "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.ca" ]; then
cp -f "$HOMEDIR/$user/conf/web/$domain/ssl/$domain.ca" "$BUNKW_DIR/"
fi
chown root:nginx "$BUNKW_DIR"/*
chmod 0640 "$BUNKW_DIR"/*
$BIN/v-ext-modules-run bunkerweb_module updssl "$domain" "$BUNKW_DIR/$domain.pem" "$BUNKW_DIR/$domain.key"
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "SSL certificate updated (Domain: $domain)."
log_event "$OK" "$EVENT"

View File

@@ -1,158 +1,224 @@
import { defineConfig } from 'vitepress';
import { version } from '../../package.json';
import { defineConfig } from "vitepress";
import { version } from "../../package.json";
export default defineConfig({
lang: 'en-US',
title: 'Hestia Control Panel',
description: 'Open-source web server control panel.',
lang: "en-US",
title: "Hestia Control Panel",
description: "Open-source web server control panel.",
lastUpdated: true,
cleanUrls: false,
lastUpdated: true,
cleanUrls: false,
head: [
['link', { rel: 'icon', sizes: 'any', href: '/favicon.ico' }],
['link', { rel: 'icon', type: 'image/svg+xml', sizes: '16x16', href: '/logo.svg' }],
['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
['link', { rel: 'manifest', href: '/site.webmanifest' }],
['meta', { name: 'theme-color', content: '#b7236a' }],
],
head: [
["link", { rel: "icon", sizes: "any", href: "/favicon.ico" }],
[
"link",
{ rel: "icon", type: "image/svg+xml", sizes: "16x16", href: "/logo.svg" },
],
[
"link",
{
rel: "apple-touch-icon",
sizes: "180x180",
href: "/apple-touch-icon.png",
},
],
["link", { rel: "manifest", href: "/site.webmanifest" }],
["meta", { name: "theme-color", content: "#b7236a" }],
],
themeConfig: {
logo: '/logo.svg',
themeConfig: {
logo: "/logo.svg",
nav: nav(),
nav: nav(),
socialLinks: [
{ icon: 'github', link: 'https://dev.brepo.ru/bayrepo/hestiacp' },
{ icon: 'github', link: 'https://github.com/bayrepo/hestiacp-rpm' },
{ icon: 'github', link: 'https://github.com/hestiacp/hestiacp' },
],
socialLinks: [
{ icon: "github", link: "https://dev.brepo.ru/bayrepo/hestiacp" },
{ icon: "github", link: "https://github.com/bayrepo/hestiacp-rpm" },
{ icon: "github", link: "https://github.com/hestiacp/hestiacp" },
],
sidebar: { '/docs/': sidebarDocs() },
sidebar: { "/docs/": sidebarDocs() },
outline: [2, 3],
outline: [2, 3],
footer: {
message: 'Выпущена под лицензией GPLv3.',
copyright: 'Copyright © 2019-present Hestia Control Panel и некоторые RPM based компоненты принадлежат bayrepo',
},
},
footer: {
message: "Выпущена под лицензией GPLv3.",
copyright:
"Copyright © 2019-present Hestia Control Panel и некоторые RPM based компоненты принадлежат bayrepo",
},
},
});
/** @returns {import("vitepress").DefaultTheme.NavItem[]} */
function nav() {
return [
{ text: 'Характеристики', link: '/features.md' },
{ text: 'Установка', link: '/install.md' },
{ text: 'Документация', link: '/docs/introduction/getting-started.md', activeMatch: '/docs/' },
{
text: `v${version}`,
items: [
{
text: 'Changelog',
link: 'https://dev.brepo.ru/bayrepo/hestiacp/src/branch/master/CHANGELOG.md',
},
{
text: 'Содействие в разработке',
link: 'https://dev.brepo.ru/bayrepo/hestiacp/src/branch/master/CONTRIBUTING.md',
},
{
text: 'Политика безопасности',
link: 'https://dev.brepo.ru/bayrepo/hestiacp/src/branch/master/SECURITY.md',
},
],
},
];
return [
{ text: "Характеристики", link: "/features.md" },
{ text: "Установка", link: "/install.md" },
{
text: "Документация",
link: "/docs/introduction/getting-started.md",
activeMatch: "/docs/",
},
{
text: `v${version}`,
items: [
{
text: "Changelog",
link: "https://dev.brepo.ru/bayrepo/hestiacp/src/branch/master/CHANGELOG.md",
},
{
text: "Содействие в разработке",
link: "https://dev.brepo.ru/bayrepo/hestiacp/src/branch/master/CONTRIBUTING.md",
},
{
text: "Политика безопасности",
link: "https://dev.brepo.ru/bayrepo/hestiacp/src/branch/master/SECURITY.md",
},
],
},
];
}
/** @returns {import("vitepress").DefaultTheme.SidebarItem[]} */
function sidebarDocs() {
return [
{
text: 'Знакомство',
collapsed: false,
items: [
{ text: 'С чего начать', link: '/docs/introduction/getting-started.md' },
{ text: 'Рекомендации', link: '/docs/introduction/best-practices.md' },
],
},
{
text: 'Инструкция пользователя',
collapsed: false,
items: [
{ text: 'Аккаунт', link: '/docs/user-guide/account.md' },
{ text: 'Резервные копии', link: '/docs/user-guide/backups.md' },
{ text: 'Cron задачи', link: '/docs/user-guide/cron-jobs.md' },
{ text: 'Базы данных', link: '/docs/user-guide/databases.md' },
{ text: 'DNS', link: '/docs/user-guide/dns.md' },
{ text: 'Менеджер файлов', link: '/docs/user-guide/file-manager.md' },
{ text: 'Почтовые домены', link: '/docs/user-guide/mail-domains.md' },
{ text: 'Оповещения', link: '/docs/user-guide/notifications.md' },
{ text: 'Пакеты', link: '/docs/user-guide/packages.md' },
{ text: 'Статистика', link: '/docs/user-guide/statistics.md' },
{ text: 'Пользователи', link: '/docs/user-guide/users.md' },
{ text: 'Веб домены', link: '/docs/user-guide/web-domains.md' },
],
},
{
text: 'Администрирование сервера',
collapsed: false,
items: [
{ text: 'Создание резервных копий и восстановление', link: '/docs/server-administration/backup-restore.md' },
{ text: 'Конфигурация', link: '/docs/server-administration/configuration.md' },
{ text: 'Персональная настройка', link: '/docs/server-administration/customisation.md' },
{ text: 'Базы данных и phpMyAdmin', link: '/docs/server-administration/databases.md' },
{ text: 'DNS кластера & DNSSEC', link: '/docs/server-administration/dns.md' },
{ text: 'Email', link: '/docs/server-administration/email.md' },
{ text: 'Менеджер файлов', link: '/docs/server-administration/file-manager.md' },
{ text: 'Firewall', link: '/docs/server-administration/firewall.md' },
{ text: 'Обновления ОС', link: '/docs/server-administration/os-upgrades.md' },
{ text: 'Rest API', link: '/docs/server-administration/rest-api.md' },
{ text: 'SSL сертификаты', link: '/docs/server-administration/ssl-certificates.md' },
{ text: 'Веб шаблоны и кэширование', link: '/docs/server-administration/web-templates.md' },
{ text: 'Troubleshooting', link: '/docs/server-administration/troubleshooting.md' },
],
},
{
text: 'Содейтсвие в разработке',
collapsed: false,
items: [
{ text: 'Сборка пакетов', link: '/docs/contributing/building.md' },
{ text: 'Разработка', link: '/docs/contributing/development.md' },
{ text: 'Документация', link: '/docs/contributing/documentation.md' },
{ text: 'Установка приложений', link: '/docs/contributing/quick-install-app.md' },
{ text: 'Тестирование', link: '/docs/contributing/testing.md' },
{ text: 'Переводы', link: '/docs/contributing/translations.md' },
],
},
{
text: 'Сообщество',
collapsed: false,
items: [
{ text: 'Hestia Nginx Cache', link: '/docs/community/hestia-nginx-cache.md' },
{
text: 'Ioncube installer for Hestia',
link: '/docs/community/ioncube-hestia-installer.md',
},
{ text: 'Генератор установочной команды', link: '/docs/community/install-script-generator.md' },
],
},
{
text: 'Ссылки',
collapsed: false,
items: [
{ text: 'API', link: '/docs/reference/api.md' },
{ text: 'CLI', link: '/docs/reference/cli.md' },
],
},
{
text: 'Дополнения',
collapsed: false,
items: [
{ text: 'PHP cli селектор', link: '/docs/extensions/php-cli-selector.md' },
{ text: 'Расширенные модули', link: '/docs/extensions/extended-modules.md' },
{ text: 'Настройка Local PHP', link: '/docs/extensions/local-php.md' },
],
},
];
return [
{
text: "Знакомство",
collapsed: false,
items: [
{
text: "С чего начать",
link: "/docs/introduction/getting-started.md",
},
{ text: "Рекомендации", link: "/docs/introduction/best-practices.md" },
],
},
{
text: "Инструкция пользователя",
collapsed: false,
items: [
{ text: "Аккаунт", link: "/docs/user-guide/account.md" },
{ text: "Резервные копии", link: "/docs/user-guide/backups.md" },
{ text: "Cron задачи", link: "/docs/user-guide/cron-jobs.md" },
{ text: "Базы данных", link: "/docs/user-guide/databases.md" },
{ text: "DNS", link: "/docs/user-guide/dns.md" },
{ text: "Менеджер файлов", link: "/docs/user-guide/file-manager.md" },
{ text: "Почтовые домены", link: "/docs/user-guide/mail-domains.md" },
{ text: "Оповещения", link: "/docs/user-guide/notifications.md" },
{ text: "Пакеты", link: "/docs/user-guide/packages.md" },
{ text: "Статистика", link: "/docs/user-guide/statistics.md" },
{ text: "Пользователи", link: "/docs/user-guide/users.md" },
{ text: "Веб домены", link: "/docs/user-guide/web-domains.md" },
],
},
{
text: "Администрирование сервера",
collapsed: false,
items: [
{
text: "Создание резервных копий и восстановление",
link: "/docs/server-administration/backup-restore.md",
},
{
text: "Конфигурация",
link: "/docs/server-administration/configuration.md",
},
{
text: "Персональная настройка",
link: "/docs/server-administration/customisation.md",
},
{
text: "Базы данных и phpMyAdmin",
link: "/docs/server-administration/databases.md",
},
{
text: "DNS кластера & DNSSEC",
link: "/docs/server-administration/dns.md",
},
{ text: "Email", link: "/docs/server-administration/email.md" },
{
text: "Менеджер файлов",
link: "/docs/server-administration/file-manager.md",
},
{ text: "Firewall", link: "/docs/server-administration/firewall.md" },
{
text: "Обновления ОС",
link: "/docs/server-administration/os-upgrades.md",
},
{ text: "Rest API", link: "/docs/server-administration/rest-api.md" },
{
text: "SSL сертификаты",
link: "/docs/server-administration/ssl-certificates.md",
},
{
text: "Веб шаблоны и кэширование",
link: "/docs/server-administration/web-templates.md",
},
{
text: "Troubleshooting",
link: "/docs/server-administration/troubleshooting.md",
},
],
},
{
text: "Содейтсвие в разработке",
collapsed: false,
items: [
{ text: "Сборка пакетов", link: "/docs/contributing/building.md" },
{ text: "Разработка", link: "/docs/contributing/development.md" },
{ text: "Документация", link: "/docs/contributing/documentation.md" },
{
text: "Установка приложений",
link: "/docs/contributing/quick-install-app.md",
},
{ text: "Тестирование", link: "/docs/contributing/testing.md" },
{ text: "Переводы", link: "/docs/contributing/translations.md" },
],
},
{
text: "Сообщество",
collapsed: false,
items: [
{
text: "Hestia Nginx Cache",
link: "/docs/community/hestia-nginx-cache.md",
},
{
text: "Ioncube installer for Hestia",
link: "/docs/community/ioncube-hestia-installer.md",
},
{
text: "Генератор установочной команды",
link: "/docs/community/install-script-generator.md",
},
],
},
{
text: "Ссылки",
collapsed: false,
items: [
{ text: "API", link: "/docs/reference/api.md" },
{ text: "CLI", link: "/docs/reference/cli.md" },
],
},
{
text: "Дополнения",
collapsed: false,
items: [
{
text: "PHP cli селектор",
link: "/docs/extensions/php-cli-selector.md",
},
{
text: "Расширенные модули",
link: "/docs/extensions/extended-modules.md",
},
{ text: "Настройка Local PHP", link: "/docs/extensions/local-php.md" },
{
text: "nginx+mod_rewrite",
link: "/docs/extensions/nginx-mod-rewrite.md",
},
],
},
];
}

View File

@@ -22,6 +22,7 @@ export const webDomains = [
{ text: 'PHP 8.2' },
{ text: 'PHP 8.3' },
{ text: 'PHP 8.4' },
{ text: 'PHP 8.5' },
],
},
];

View File

@@ -148,4 +148,4 @@ bundle install
bundle install --path=vendor
```
для установки пользователю необходимых гемов локально.
для установки пользователю необходимых гемов локально.

View File

@@ -0,0 +1,15 @@
# nginx с поддержкой mdo_rewrite
HestiaCP RPM edition поддерживает установку приложений для доменов, таких как Wordpress, Joomla, Drupal и т.д.
Данные приложения написаны на PHP и требуют особого режима доступа к файлам, который реализуется с помощью .htaccess файла, в котором опианы эти правила преобразования URL.
Но .htaccess файл доступен только для Apache, для реализации работоспособнсти приложений в контрольной панели организованы шаблоны конфигурации nginx, которые по сути являются аналогами конфигурации из .htaccess.
Эти шаблоны размещены в каталоге `/usr/local/hestia/data/templates/web/nginx/php-fpm`.
# nginx + mod_rewrite
Но с версии 1.28.2 nginx добавлен экспериментальный модуль mod_rewrite для nginx, который позволяет понимать конфигурации из .htaccess и для nginx.
Для активации данной конфигурации, необходимо в настройках домена в `Расширенных настройках` выбрать в поле `Шаблон прокси` шаблон `nginx-php-rewrite`. Данный шаблон автоматически определит версию PHP-FPM для домена и активирует работу модуля mod_rewrite для nginx. Для данного домена Apache больше не будет принимать участие в обработке запроса, все будет обрабатываться в связке nginx-PHP-FPM.

View File

@@ -61,3 +61,4 @@
- [PHP cli селектор](/docs/extensions/php-cli-selector.md)
- [Расширенные модули](/docs/extensions/extended-modules.md)
- [Настройка Local PHP](/docs/extensions/local-php.md)
- [nginx + mod_rewrite](/docs/extensions/nginx-mod-rewrite.md)

View File

@@ -112,11 +112,11 @@ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files $uri =404;
fastcgi_pass %backend_lsnr%;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
}
```
Добавьте следующие строки под `include /etc/nginx/fastcgi_params;`:
Добавьте следующие строки под `include /usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;`:
```bash
include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;

View File

@@ -141,4 +141,14 @@
Когда включено кэширование Nginx (с использованием кэша FastCGI или с шаблоном с поддержкой кэширования), вы можете очистить кэш с помощью кнопки **<i class="fas fa-fw fa-trash"></i> Очистить кэш Nginx**.
При использовании только Nginx вы можете включить кэширование FastCGI с помощью поля **Включить кэш FastCGI**. Если этот флажок установлен, отображается опция, позволяющая определить, в течение какого времени кэш считается действительным.
При использовании только Nginx вы можете включить кэширование FastCGI с помощью поля **Включить кэш FastCGI**. Если этот флажок установлен, отображается опция, позволяющая определить, в течение какого времени кэш считается действительным.
### Домен по умолчанию
Для вебсервера Apache при обращении по IP адресу без указания домена доменом по умолчанию является первый загруженный виртуальный хост.
Для изменения домена по умолчанию необходимо выбрать среди доменов интересующий и в настройках домена необходимо установить флажок:
**Установить текущий домен как домент по умолчанию при обращении к серверу по IP**
затем нажмите кнопку **<i class="fas fa-fw fa-save"></i> Сохранить** в правом верхнем углу.

View File

@@ -102,7 +102,7 @@ prepare_web_backend() {
# Accept first function argument as backend template otherwise fallback to $template global variable
local backend_template=${1:-$template}
php_type=$(cat "$HESTIA/conf/hestia.conf" | grep "LOCAL_PHP" | grep "yes")
if [ -n "$php_type" ]; then
pool=$(find -L /opt/brepo/ -name "$domain.conf" -exec dirname {} \; 2>/dev/null)
else
@@ -359,7 +359,7 @@ add_web_config() {
-e "s|%ssl_ca%|$ssl_ca|g" \
-e "s|%docrtpriv%|$docrtpriv|g" \
> $conf
if [ "$TPLNM" == "srvproxy.tpl" -o "$TPLNM" == "srvproxy.stpl" ];then
proxy_backend_port_internal=$(convert_proxy_ngix_internal_redirect "$3")
cat "$conf" \
@@ -383,7 +383,11 @@ add_web_config() {
if [[ "$TPLNM" =~ stpl$ ]]; then
rm -f /etc/$1/$confd/domains/$domain.ssl.conf
ln -s $conf /etc/$1/$confd/domains/$domain.ssl.conf
if [ "$1" = "nginx" ]; then
ln -s $conf /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$domain.ssl.conf
else
ln -s $conf /etc/$1/$confd/domains/$domain.ssl.conf
fi
# Rename/Move extra SSL config files
find=$(find $HOMEDIR/$user/conf/web/*.$domain.org* 2> /dev/null)
@@ -399,8 +403,13 @@ add_web_config() {
fi
done
else
rm -f /etc/$1/$confd/domains/$domain.conf
ln -s $conf /etc/$1/$confd/domains/$domain.conf
if [ "$1" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$domain.conf
ln -s $conf /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$domain.conf
else
rm -f /etc/$1/$confd/domains/$domain.conf
ln -s $conf /etc/$1/$confd/domains/$domain.conf
fi
# Rename/Move extra config files
find=$(find $HOMEDIR/$user/conf/web/*.$domain.org* 2> /dev/null)
for f in $find; do
@@ -419,11 +428,41 @@ add_web_config() {
done
fi
#----
php_type=$(cat "$HESTIA/conf/hestia.conf" | grep "LOCAL_PHP" | grep "yes")
MOD_CONF="/etc/httpd/conf.modules.d/09-mod-php.conf"
PHP_DEFAULT="/usr/bin/php-cgi"
if [ -e $MOD_CONF ]; then
php_ver=$(grep -m1 '^LoadModule php_module ' "$MOD_CONF" | grep -oP 'php\d{2}')
else
php_ver=$(find /etc/httpd/conf.modules.d -maxdepth 1 -type f -name '*-php*-php.conf' -print -quit | sed -n 's/.*-\(php[0-9]\+\)-php\.conf$/\1/p')
fi
php_cgi_path=$PHP_DEFAULT
if [ -n "$php_ver" ]; then
if [ -n "$php_type" ]; then
php_cgi_path="/opt/brepo/${php_ver}/bin/php-cgi"
else
php_cgi_path="/opt/remi/${php_ver}/root/bin/php-cgi"
fi
fi
if [[ -x "$php_cgi_path" ]]; then
:
else
php_cgi_path=$PHP_DEFAULT
fi
#----
trigger="${TPLNM/.*pl/.sh}"
if [ -x "${WEBTPL_LOCATION}/$trigger" ]; then
$WEBTPL_LOCATION/$trigger \
$user $domain $local_ip $HOMEDIR \
$HOMEDIR/$user/web/$domain/public_html
$HOMEDIR/$user/web/$domain/public_html \
$php_cgi_path
fi
}
@@ -488,17 +527,29 @@ del_web_config() {
rm -f $legacyconf
# Remove old global includes file
rm -f /etc/$1/$confd/hestia.conf
if [ "$1" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$1/$confd/hestia.conf
else
rm -f /etc/$1/$confd/hestia.conf
fi
fi
# Remove domain configuration files and clean up symbolic links
rm -f "$conf"
if [ -n "$WEB_SYSTEM" ] && [ "$WEB_SYSTEM" = "$1" ]; then
rm -f "/etc/$WEB_SYSTEM/$confd/domains/$confname"
if [ "$WEB_SYSTEM" = "nginx" ]; then
rm -f "/usr/local/hestia/nginx-system/etc/$WEB_SYSTEM/$confd/domains/$confname"
else
rm -f "/etc/$WEB_SYSTEM/$confd/domains/$confname"
fi
fi
if [ -n "$PROXY_SYSTEM" ] && [ "$PROXY_SYSTEM" = "$1" ]; then
rm -f "/etc/$PROXY_SYSTEM/$confd/domains/$confname"
if [ "$PROXY_SYSTEM" = "nginx" ]; then
rm -f "/usr/local/hestia/nginx-system/etc/$PROXY_SYSTEM/$confd/domains/$confname"
else
rm -f "/etc/$PROXY_SYSTEM/$confd/domains/$confname"
fi
fi
}
@@ -887,8 +938,16 @@ del_mail_ssl_config() {
# Remove SSL vhost configuration
rm -f $HOMEDIR/$user/conf/mail/$domain/*.*ssl.conf
rm -f /etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
rm -f /etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
if [ "$WEB_SYSTEM" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
else
rm -f /etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
fi
if [ "$PROXY_SYSTEM" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
else
rm -f /etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
fi
# Remove SSL certificates
rm -f $HOMEDIR/$user/conf/mail/$domain/ssl/*
@@ -976,13 +1035,23 @@ add_webmail_config() {
if [[ "$2" =~ stpl$ ]]; then
if [ -n "$WEB_SYSTEM" ]; then
forcessl="$HOMEDIR/$user/conf/mail/$domain/$WEB_SYSTEM.forcessl.conf"
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
if [ "$1" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
ln -s $conf /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
else
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
fi
fi
if [ -n "$PROXY_SYSTEM" ]; then
forcessl="$HOMEDIR/$user/conf/mail/$domain/$PROXY_SYSTEM.forcessl.conf"
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
if [ "$1" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
ln -s $conf /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
else
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
fi
fi
# Add rewrite rules to force HTTPS/SSL connections
@@ -997,12 +1066,22 @@ add_webmail_config() {
find $HOMEDIR/$user/conf/mail/ -maxdepth 1 -type f \( -name "$domain.*" -o -name "ssl.$domain.*" -o -name "*nginx.$domain.*" \) -exec rm {} \;
else
if [ -n "$WEB_SYSTEM" ]; then
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
if [ "$1" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
ln -s $conf /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
else
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
fi
fi
if [ -n "$PROXY_SYSTEM" ]; then
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
if [ "$1" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
ln -s $conf /usr/local/hestia/nginx-system/etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
else
rm -f /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
ln -s $conf /etc/$1/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
fi
fi
# Clear old configurations
find $HOMEDIR/$user/conf/mail/ -maxdepth 1 -type f \( -name "$domain.*" \) -exec rm {} \;
@@ -1023,12 +1102,20 @@ del_webmail_config() {
fi
if [ -n "$WEB_SYSTEM" ]; then
rm -f $HOMEDIR/$user/$confd/mail/$domain/$WEB_SYSTEM.conf
rm -f /etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
if [ "$WEB_SYSTEM" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
else
rm -f /etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.conf
fi
fi
if [ -n "$PROXY_SYSTEM" ]; then
rm -f $HOMEDIR/$user/conf/mail/$domain/$PROXY_SYSTEM.*conf
rm -f /etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.conf
if [ "$PROXY_SYSTEM" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.conf
else
rm -f /etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.conf
fi
fi
}
@@ -1046,12 +1133,20 @@ del_webmail_ssl_config() {
fi
if [ -n "$WEB_SYSTEM" ]; then
rm -f $HOMEDIR/$user/conf/mail/$domain/$WEB_SYSTEM.*ssl.conf
rm -f /etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
if [ "$WEB_SYSTEM" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
else
rm -f /etc/$WEB_SYSTEM/$confd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
fi
fi
if [ -n "$PROXY_SYSTEM" ]; then
rm -f $HOMEDIR/$user/conf/mail/$domain/$PROXY_SYSTEM.*ssl.conf
rm -f /etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
if [ "$PROXY_SYSTEM" = "nginx" ]; then
rm -f /usr/local/hestia/nginx-system/etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
else
rm -f /etc/$PROXY_SYSTEM/$pconfd/domains/$WEBMAIL_ALIAS.$domain.ssl.conf
fi
fi
}
@@ -1157,14 +1252,14 @@ is_base_domain_owner() {
#----------------------------------------------------------#
process_http2_directive() {
if [ -e /etc/nginx/conf.d/http2-directive.conf ]; then
if [ -e /usr/local/hestia/nginx-system/etc/nginx/conf.d/http2-directive.conf ]; then
while IFS= read -r old_param; do
new_param="$(echo "$old_param" | sed 's/\shttp2//')"
sed -i "s/$old_param/$new_param/" "$1"
done < <(grep -E "listen.*(\bssl\b(\s|.+){1,}\bhttp2\b|\bhttp2\b(\s|.+){1,}\bssl\b).*;" "$1")
else
if version_ge "$(nginx -v 2>&1 | cut -d'/' -f2)" "1.25.1"; then
echo "http2 on;" > /etc/nginx/conf.d/http2-directive.conf
if version_ge "$(/usr/local/hestia/nginx-system/sbin/nginx -v 2>&1 | cut -d'/' -f2)" "1.25.1"; then
echo "http2 on;" > /usr/local/hestia/nginx-system/etc/nginx/conf.d/http2-directive.conf
while IFS= read -r old_param; do
new_param="$(echo "$old_param" | sed 's/\shttp2//')"

View File

@@ -56,11 +56,12 @@ upgrade_health_check() {
upgrade_welcome_message() {
echo
echo ' _ _ _ _ ____ ____ '
echo ' | | | | ___ ___| |_(_) __ _ / ___| _ \ '
echo ' | |_| |/ _ \/ __| __| |/ _` | | | |_) | '
echo ' | _ | __/\__ \ |_| | (_| | |___| __/ '
echo ' |_| |_|\___||___/\__|_|\__,_|\____|_| '
echo ' _ _ _ _ ____ ____ '
echo ' | | | | ___ ___| |_(_) __ _ / ___| _ \ _ _ . . '
echo ' | |_| |/ _ \/ __| __| |/ _` | | | |_) | | \| \|\/| '
echo ' | _ | __/\__ \ |_| | (_| | |___| __/ |_/|_/| | '
echo ' |_| |_|\___||___/\__|_|\__,_|\____|_| | \| | | '
echo " "
echo " "
echo " Hestia Control Panel Software Update "
echo " Version: ${DISPLAY_VER}"
@@ -535,7 +536,7 @@ upgrade_cloudflare_ip() {
cf_ips="$(curl -fsLm5 --retry 2 https://api.cloudflare.com/client/v4/ips)"
if [ -n "$cf_ips" ] && [ "$(echo "$cf_ips" | jq -r '.success//""')" = "true" ]; then
cf_inc="/etc/nginx/conf.d/cloudflare.inc"
cf_inc="/usr/local/hestia/nginx-system/etc/nginx/conf.d/cloudflare.inc"
echo "[ * ] Updating Cloudflare IP Ranges for NGINX..."
echo "# Cloudflare IP Ranges" > $cf_inc

View File

@@ -0,0 +1,630 @@
#!/usr/bin/env ruby
require 'json'
require 'net/http'
require 'uri'
require 'openssl'
class BunkerWebApiError < StandardError; end
class HestiaBunkerWebApi
# Override puts to accumulate logs into @extra_info
def puts(*args)
@extra_info ||= ""
@extra_info << args.join("\n") << "\n"
end
# Accessor for @extra_info
def extra_info
@extra_info || ""
end
end
# Hook to wrap methods of HestiaBunkerWebApi to reset @extra_info at start
class Module
alias_method :orig_method_added, :method_added
def method_added(name)
orig_method_added(name)
# Skip wrapping for the overridden puts method
return if name == :puts
if self.name == 'HestiaBunkerWebApi'
@__wrapping ||= false
return if @__wrapping
@__wrapping = true
original = instance_method(name)
define_method(name) do |*args, &block|
@extra_info = ""
original.bind(self).call(*args, &block)
end
@__wrapping = false
end
end
end
class HestiaBunkerWebApi
# Retrieve API username and password from /etc/bunkerweb/api.env if available
def get_api_user_password
env_path = "/etc/bunkerweb/api.env"
return nil unless File.file?(env_path)
username = nil
password = nil
File.foreach(env_path) do |line|
line.strip!
next if line.empty? || line.start_with?('#')
key, value = line.split('=', 2)
next unless key && value
case key
when 'API_USERNAME'
username = value
when 'API_PASSWORD'
password = value
end
end
if username && password
[username, password]
else
nil
end
end
def initialize(api_url, username = nil, password = nil)
@api_base = api_url
if username.nil?
result = get_api_user_password
if result.nil?
raise BunkerWebApiError.new("Authentication error: no username or password")
else
@username = result[0]
@password = result[1]
end
else
@username = username
@password = password
end
@token = nil
@extra_info = ""
# Authenticate and get token
authenticate!
puts "[INFO] Successfully authenticated with BunkerWeb API"
end
def authenticate!
uri = URI(@api_base)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")
# Try both Basic Auth and JSON body with credentials
request = Net::HTTP::Post.new("/auth")
request.content_type = "application/json"
request.body = { username: @username, password: @password }.to_json
response = http.request(request)
if response.code != '200'
raise BunkerWebApiError.new("Authentication failed: #{response.code} - #{response.message}")
end
body = JSON.parse(response.body)
unless body['token']
raise BunkerWebApiError.new("Authentication succeeded but no token received")
end
@token = body['token']
rescue => e
raise BunkerWebApiError.new("Authentication error: #{e.message}")
end
def api_call(method, path, headers = {}, body = nil)
uri = URI(@api_base + path)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")
request = case method
when "GET"
Net::HTTP::Get.new(uri.path)
when "POST"
req = Net::HTTP::Post.new(uri.path)
req.body = body if body
req
when "PATCH"
req = Net::HTTP::Patch.new(uri.path)
req.body = body if body
req
when "DELETE"
Net::HTTP::Delete.new(uri.path)
else
raise BunkerWebApiError.new("Unsupported HTTP method: #{method}")
end
# Add Authorization header with token for most operations (except /auth)
unless path == "/auth"
headers["Authorization"] = "Bearer #{@token}" if @token
end
# Add Content-Type if not already set and we have a body
if body && !headers.key?("Content-Type")
headers["Content-Type"] = "application/json"
end
headers.each { |k, v| request[k] = v }
response = http.request(request)
parsed_body = begin
JSON.parse(response.body)
rescue => e
nil
end
{ status: response.code.to_i, body: parsed_body || {}, raw_body: response.body }
rescue => e
raise BunkerWebApiError.new("API call to #{path} failed: #{e.message}")
end
# === Service Operations ===
def create_service(service_name, options = {})
@extra_info = ""
# Create a new service with the given configuration.
#
# Args:
# service_name (String): The domain name for this service (server_name)
# options (Hash): Service configuration including:
# - USE_TEMPLATE (default: "high")
# - USE_SSL (default: "no") - if not "no", you need CERTIFICATE and KEY paths
# - REVERSE_PROXY_HOST (optional)
# - REVERSE_PROXY_URL (optional, default: "~ ^/(.*)$")
# - Additional options like:
# - USE_REVERSE_PROXY (default: "yes" if REVERSE_PROXY_HOST is set)
# - USE_REAL_IP, REAL_IP_FROM
# - USE_MODSECURITY, USE_ANTIBOT
# - LISTEN_HTTP_PORT, LISTEN_HTTPS_PORT
# - CERTIFICATE_FILE_PATH (if USE_SSL != "no")
# - KEY_FILE_PATH (if USE_SSL != "no")
# Returns: Hash with creation response
variables = {
"USE_TEMPLATE" => options[:use_template] || "high",
"USE_REVERSE_PROXY" => options[:reverse_proxy_host].nil? ? "no" : "yes",
"LIMIT_REQ_RATE" => options[:limit_req_rate] || "10r/s",
}
# SSL configuration - only if USE_SSL != "no"
ssl_enabled = options[:ssl] && options[:ssl] != "no"
variables["USE_CUSTOM_SSL"] = ssl_enabled ? "yes" : "no"
if ssl_enabled
unless options[:certificate_path] && options[:key_path]
raise BunkerWebApiError.new("Certificate and Key paths are required when USE_SSL is enabled")
end
# Set certificate paths in variables
variables["CUSTOM_SSL_CERT"] = options[:certificate_path]
variables["CUSTOM_SSL_KEY"] = options[:key_path]
variables["LISTEN_HTTPS_PORT"] = (options[:https_port] || "443").to_s
variables["USE_REVERSE_PROXY_SSL"] = options[:reverse_proxy_ssl] || "yes"
else
# No SSL - HTTP only
# API expects string "null", not nil/JSON null
variables["LISTEN_HTTPS_PORT"] = "null"
variables["LISTEN_HTTP_PORT"] = (options[:http_port] || "80").to_s
end
# Reverse proxy configuration if specified
if options[:reverse_proxy_host]
variables["REVERSE_PROXY_HOST"] = options[:reverse_proxy_host]
variables["REVERSE_PROXY_URL"] = options[:reverse_proxy_url] || "~ ^(?!/challenge)(.*)$"
# Real IP settings for reverse proxy
unless options[:real_ip_from].nil?
variables["USE_REAL_IP"] = "yes"
variables["REAL_IP_FROM"] = options[:real_ip_from]
end
# Additional security settings from High template
variables["USE_MODSECURITY"] = options[:use_modsecurity] || "yes"
variables["USE_ANTIBOT"] = options[:anti_bot] || "captcha"
end
variables["ANTIBOT_IGNORE_URI"] = options[:anti_bot_ignore_uri] || "^/\.well-known/acme-challenge/.+$"
variables["LETS_ENCRYPT_PASSTHROUGH"] = options[:lets_encrypt_passthrough] || "yes"
service_body = {
server_name: service_name,
is_draft: false,
variables: variables
}
response = api_call("POST", "/services", {}, JSON.generate(service_body))
# Accept both 201 (Created) and 200 (OK) for successful creation
if [201, 200].include?(response[:status])
puts "[INFO] Service '#{service_name}' created successfully"
elsif response[:status] == 409
raise BunkerWebApiError.new("Service '#{service_name}' already exists")
else
raise BunkerWebApiError.new("Failed to create service: status=#{response[:status]}, body=#{response[:raw_body]}")
end
return response[:body] || {}
end
def update_service_ssl(service_name, certificate_path, key_path, https_port = nil)
# Update or change the SSL certificate path for an existing service.
#
# Args:
# service_name (String): Name of the service to update
# certificate_path (String): Path to the SSL certificate file
# key_path (String): Path to the SSL private key file
# https_port (Integer, optional): HTTPS port (default 443)
# Returns: Hash with update response
@extra_info = ""
# First get current service configuration to preserve existing settings
get_service_response = api_call("GET", "/services/#{service_name}", {})
if get_service_response[:status] != 200
raise BunkerWebApiError.new("Service '#{service_name}' not found")
end
# Extract variables from config - API stores them in 'config' not 'variables'
# Each variable is a hash with 'value' as the actual setting value
current_vars = {}
if get_service_response[:body]["config"]
get_service_response[:body]["config"].each do |key, value_hash|
if value_hash.is_a?(Hash) && value_hash.key?("value")
current_vars[key] = value_hash["value"]
else
current_vars[key] = value_hash
end
end
end
# Update SSL settings
updated_vars = {
"USE_CUSTOM_SSL" => "yes",
"CUSTOM_SSL_CERT" => certificate_path,
"CUSTOM_SSL_KEY" => key_path,
"LISTEN_HTTPS_PORT" => (https_port || "443").to_s,
"USE_REVERSE_PROXY_SSL" => "yes"
}
# Merge with existing variables (keep non-SSL settings)
final_vars = current_vars.merge(updated_vars)
service_body = {
server_name: nil, # Not changing name
is_draft: false, # Keep as online
variables: final_vars
}
response = api_call("PATCH", "/services/#{service_name}", {}, JSON.generate(service_body))
if response[:status] == 200
puts "[INFO] SSL configuration updated for service '#{service_name}'"
else
raise BunkerWebApiError.new("Failed to update SSL configuration: status=#{response[:status]}, body=#{response[:raw_body]}")
end
return response[:body] || {}
end
def set_alias(service_name, list_aliases)
@extra_info = ""
# First get current service configuration to preserve existing settings
get_service_response = api_call("GET", "/services/#{service_name}", {})
if get_service_response[:status] != 200
raise BunkerWebApiError.new("Service '#{service_name}' not found")
end
# Save the entire current configuration (except server_name which will be replaced)
current_config = get_service_response[:body]
# Extract variables from config - API stores them in 'config' not 'variables'
# Each variable is a hash with 'value' as the actual setting value
current_vars = {}
if current_config["config"]
current_config["config"].each do |key, value_hash|
if key != "SERVER_NAME"
if value_hash.is_a?(Hash) && value_hash.key?("value")
current_vars[key] = value_hash["value"]
else
current_vars[key] = value_hash
end
end
end
end
# Clean the list_aliases string according to the rules
cleaned_aliases = list_aliases.to_s
# Replace commas (with or without space) with a single space
cleaned_aliases.gsub!(/,\s?/, ' ')
# Replace multiple spaces with a single space
cleaned_aliases.gsub!(/\s{2,}/, ' ')
# Strip leading/trailing whitespace
cleaned_aliases.strip!
# Ensure the main service name appears first in the alias list
aliases_array = cleaned_aliases.split(' ')
if aliases_array.include?(service_name)
aliases_array.delete(service_name)
end
# Rebuild cleaned string
cleaned_aliases = aliases_array.join(' ')
current_vars["SERVER_NAME"]=cleaned_aliases
# Step 1: Delete the old service
delete_response = api_call("DELETE", "/services/#{service_name}")
if delete_response[:status] != 200 && delete_response[:status] != 204
raise BunkerWebApiError.new("Failed to delete service '#{service_name}': status=#{delete_response[:status]}")
end
puts "[INFO] Service '#{service_name}' deleted"
# Step 2: Create a new service with the cleaned alias list as server_name
# and preserve all existing configuration variables
service_body = {
server_name: service_name, # Use the full alias list including service_name first
is_draft: current_config["is_draft"] || false,
variables: current_vars # Preserve all existing variables from the original service
}
post_response = api_call("POST", "/services", {}, JSON.generate(service_body))
if post_response[:status] == 200 || post_response[:status] == 201
puts "[INFO] Service recreated successfully with aliases: #{cleaned_aliases}"
elsif post_response[:status] == 409
raise BunkerWebApiError.new("Service '#{service_name}' already exists")
else
raise BunkerWebApiError.new("Failed to create service: status=#{post_response[:status]}, body=#{post_response[:raw_body]}")
end
return post_response || {}
end
def delete_service_ssl(service_name)
@extra_info = ""
# First get current service configuration to preserve existing settings
get_service_response = api_call("GET", "/services/#{service_name}", {})
if get_service_response[:status] != 200
raise BunkerWebApiError.new("Service '#{service_name}' not found")
end
# Extract variables from config - API stores them in 'config' not 'variables'
# Each variable is a hash with 'value' as the actual setting value
current_vars = {}
if get_service_response[:body]["config"]
get_service_response[:body]["config"].each do |key, value_hash|
if value_hash.is_a?(Hash) && value_hash.key?("value")
current_vars[key] = value_hash["value"]
else
current_vars[key] = value_hash
end
end
end
# Update SSL settings
updated_vars = {
"USE_CUSTOM_SSL" => "no",
"CUSTOM_SSL_CERT" => "",
"CUSTOM_SSL_KEY" => ""
}
# Merge with existing variables (keep non-SSL settings)
final_vars = current_vars.merge(updated_vars)
service_body = {
server_name: nil, # Not changing name
is_draft: false, # Keep as online
variables: final_vars
}
response = api_call("PATCH", "/services/#{service_name}", {}, JSON.generate(service_body))
if response[:status] == 200
puts "[INFO] SSL configuration updated for service '#{service_name}'"
else
raise BunkerWebApiError.new("Failed to update SSL configuration: status=#{response[:status]}, body=#{response[:raw_body]}")
end
return response[:body] || {}
end
def delete_service(service_name)
# Delete a service by its name.
#
# Args:
# service_name (String): Name of the service to delete
# Returns: Hash with deletion response
@extra_info = ""
# Verify service exists first
get_response = api_call("GET", "/services/#{service_name}", {})
if get_response[:status] != 200
raise BunkerWebApiError.new("Service '#{service_name}' not found")
end
response = api_call("DELETE", "/services/#{service_name}")
if response[:status] == 200 || response[:status] == 204
puts "[INFO] Service '#{service_name}' deleted successfully"
return response[:body] || {}
else
raise BunkerWebApiError.new("Failed to delete service: status=#{response[:status]}, body=#{response[:raw_body]}")
end
end
# === Additional Utility Methods ===
def list_services(drafts = false)
# List all services.
#
# Args:
# drafts (Boolean): Include draft services (default: false, set to true to include drafts)
# Returns: Array of service objects
@extra_info = ""
response = api_call("GET", "/services")
if response[:status] != 200
raise BunkerWebApiError.new("Failed to list services: status=#{response[:status]}")
end
return response[:body] || []
end
def get_service(service_name)
# Get details of a specific service.
#
# Args:
# service_name (String): Name of the service to retrieve
# Returns: Hash with service configuration
@extra_info = ""
response = api_call("GET", "/services/#{service_name}")
if response[:status] != 200
raise BunkerWebApiError.new("Service '#{service_name}' not found")
end
return response[:body] || {}
end
def reload_instance(instance_hostname = nil)
# Reload configuration on an instance.
#
# Args:
# instance_hostname (String, optional): Instance hostname to reload (if nil, reloads all instances)
# Returns: Hash with reload response
@extra_info = ""
path = if instance_hostname.nil?
"/instances/reload"
else
"/instances/#{instance_hostname}/reload"
end
response = api_call("POST", "#{path}?test=no")
if response[:status] == 200 || response[:status] == 201
puts "[INFO] Configuration reloaded successfully"
return response[:body] || {}
else
raise BunkerWebApiError.new("Failed to reload configuration: status=#{response[:status]}")
end
end
def list_instances()
# List all registered instances.
# Returns: Array of instance objects
@extra_info = ""
response = api_call("GET", "/instances")
if response[:status] != 200
raise BunkerWebApiError.new("Failed to list instances: status=#{response[:status]}")
end
return response[:body] || []
end
def create_instance(hostname, name = nil, port = 8888, https_port = nil)
# Create/register a new BunkerWeb instance (worker node).
#
# Args:
# hostname (String): IP address or hostname of the worker node
# name (String, optional): Human-readable name for the instance
# port (Integer): API port on the worker node (default 8888)
# https_port (Integer, optional): HTTPS port
# Returns: Hash with creation response
@extra_info = ""
instance_body = {
hostname: hostname,
name: name || "BunkerWeb Instance",
port: port,
listen_https: !https_port.nil?,
https_port: https_port,
server_name: hostname,
method: "api" # Using API deployment method
}
response = api_call("POST", "/instances", {}, JSON.generate(instance_body))
if response[:status] == 201
puts "[INFO] Instance '#{hostname}' registered successfully"
elsif response[:status] == 409
# Instance already exists - that's OK, we just want to use it
puts "[INFO] Instance '#{hostname}' already exists, will be used for this service"
else
raise BunkerWebApiError.new("Failed to create instance: status=#{response[:status]}, body=#{response[:raw_body]}")
end
return response[:body] || {}
end
def delete_instance(hostname)
"""
Delete a registered instance.
Args:
hostname (String): Hostname of the instance to delete
"""
@extra_info = ""
response = api_call("DELETE", "/instances/#{hostname}")
if response[:status] == 200 || response[:status] == 204
puts "[INFO] Instance '#{hostname}' deleted successfully"
return true
else
raise BunkerWebApiError.new("Failed to delete instance: status=#{response[:status]}")
end
end
end
# === Example Usage (can be run as script) ===
if __FILE__ == $0
# Example usage demonstration
begin
api = HestiaBunkerWebApi.new(
"http://127.0.0.1:8888",
"admin",
"your_password"
)
puts ""
puts "[INFO] Creating service 'example.my.domain'"
result = api.create_service("example.my.domain", {
reverse_proxy_host: "http://192.168.3.51:8078",
ssl: "no",
use_template: "high"
})
puts ""
puts "[INFO] Listing services:"
services = api.list_services()
services.each { |s| puts "- #{s['server_name']}" }
rescue BunkerWebApiError => e
puts "[ERROR] #{e.message}"
exit 1
end
end

View File

@@ -0,0 +1,467 @@
# HestiaBunkerWebApi - Ruby класс для работы с BunkerWeb API
## Описание
Класс `HestiaBunkerWebApi` предоставляет простой интерфейс для управления сервисами BunkerWeb через REST API. Класс реализует:
- **Аутентификацию** с получением токена
- **Управление сервисами** (создание, обновление, удаление)
- **Управление SSL сертификатами**
- **Управление instances** (worker nodes)
- **Полное исключение ошибок** при любых проблемах
## Установка и импорт
```bash
# Ruby 3.3+ рекомендуется
ruby --version
# ruby 3.3.x or later
# Класс использует стандартные библиотеки Ruby:
# - json (для JSON парсинга)
# - net/http (для HTTP запросов)
# - uri (для URL парсинга)
```
## Использование класса
### Базовое использование
```ruby
require_relative "HestiaBunkerWebApi.rb"
# Создаём экземпляр API с аутентификацией
api = HestiaBunkerWebApi.new(
"http://127.0.0.1:8888", # URL API (можно https://)
"admin", # username
"password" # password
)
# или
api = HestiaBunkerWebApi.new(
"http://127.0.0.1:8888", # URL API (можно https://)
)
# в этом случае пароль и логин читаются автоматически из файла /etc/bunkerweb/api.env
# При создании экземпляра автоматически происходит аутентификация
# Если ошибка - выбрасывается BunkerWebApiError с описанием проблемы
```
### Обработка ошибок
Все ошибки наследуются от `StandardError` через класс `BunkerWebApiError`:
```ruby
begin
api.create_service("example.domain", options)
rescue BunkerWebApiError => e
puts "[ERROR] Ошибка API: #{e.message}"
# Примеры возможных ошибок:
# - "Authentication failed: 401 - Unauthorized"
# - "Service 'x' already exists"
# - "Certificate and Key paths are required when USE_SSL is enabled"
# - "Failed to create service: status=500, body={...}"
exit 1
end
```
## Методы класса
### Конструктор
```ruby
HestiaBunkerWebApi.new(api_url, username, password)
```
**Параметры:**
- `api_url` - URL BunkerWeb API в формате `http://ip:port` или `https://ip:port`
- `username` - имя администратора для аутентификации
- `password` - пароль для аутентификации
**Действие:** При создании автоматически пытается аутентифицироваться через POST /auth и сохраняет токен.
### Создание сервиса
```ruby
api.create_service(service_name, options = {})
```
**Параметры:**
- `service_name` - имя домена/сервиса (например, "example.my.domain")
- `options` - хэш с конфигурацией:
| Параметр | Тип | Описание | Пример |
|----------|-----|----------|--------|
| `ssl` | String | "yes" для SSL, "no" для HTTP | `"no"` |
| `certificate_path` | String | Путь к SSL сертификату (если ssl="yes") | `"/etc/ssl/certs/example.crt"` |
| `key_path` | String | Путь к приватному ключу (если ssl="yes") | `"/etc/ssl/private/example.key"` |
| `use_template` | String | Безопасность шаблона | `"high"` (default) |
| `reverse_proxy_host` | String | Target reverse proxy | `"http://192.168.3.51:8078"` |
| `reverse_proxy_url` | String | URL трансформация | `"~ ^/(.*)$"` |
| `real_ip_from` | String | CIDR trusted network для RealIP | `"192.168.3.0/24"` |
| `use_modsecurity` | String | WAF включение | `"yes"` (default) |
| `anti_bot` | String | Bot protection | `"captcha"` (default) |
| `http_port` | Integer/nil | HTTP порт | `"80"` или `null` |
| `https_port` | Integer | HTTPS порт | `443` или `null` |
**Пример - создание reverse proxy сервиса без SSL:**
```ruby
result = api.create_service("example.my.domain", {
ssl: "no",
reverse_proxy_host: "http://192.168.3.51:8078"
})
# Генерирует variables:
# - USE_TEMPLATE: "high"
# - USE_SSL: "no"
# - LISTEN_HTTPS_PORT: "null"
# - LISTEN_HTTP_PORT: "80"
# - USE_REVERSE_PROXY: "yes"
# - REVERSE_PROXY_HOST: "http://192.168.3.51:8078"
```
**Пример - создание сервиса с SSL + reverse proxy:**
```ruby
result = api.create_service("example.my.domain", {
ssl: "yes", # Включаем SSL
certificate_path: "/etc/ssl/certs/example.crt", # Путь к сертификату
key_path: "/etc/ssl/private/example.key", # Путь к ключу
reverse_proxy_host: "http://192.168.3.51:8078", # Reverse proxy target
use_template: "high",
anti_bot: "captcha"
})
# Генерирует variables:
# - USE_TEMPLATE: "high"
# - USE_SSL: "yes"
# - SSL_CERTIFICATE_FILE_PATH: "/etc/ssl/certs/example.crt"
# - SSL_KEY_FILE_PATH: "/etc/ssl/private/example.key"
# - LISTEN_HTTPS_PORT: "443"
# - LISTEN_HTTP_PORT: "80"
```
### Обновление SSL сертификата для существующего сервиса
```ruby
api.update_service_ssl(service_name, certificate_path, key_path, https_port = nil)
```
**Параметры:**
- `service_name` - имя уже созданного сервиса
- `certificate_path` - новый путь к SSL сертификату
- `key_path` - новый путь к приватному ключу
- `https_port` (optional) - HTTPS порт (default: 443)
**Пример:**
```ruby
api.update_service_ssl(
"example.my.domain",
"/etc/ssl/certs/example.crt",
"/etc/ssl/private/example.key"
)
# Обновляет существующий сервис, сохраняя reverse proxy настройки
```
### Удаление сервиса
```ruby
api.delete_service(service_name)
```
**Параметры:**
- `service_name` - имя сервиса для удаления
**Пример:**
```ruby
api.delete_service("example.my.domain")
# Удаляет сервис и конфигурацию
```
### Получение списка всех сервисов
```ruby
api.list_services(drafts = false)
```
**Параметры:**
- `drafts` (optional) - включать draft сервисы (default: false)
**Возвращает:** Array of service objects
**Пример:**
```ruby
services = api.list_services()
services.each { |s| puts "- #{s['server_name']}" }
```
### Получение деталей конкретного сервиса
```ruby
api.get_service(service_name)
```
**Параметры:**
- `service_name` - имя сервиса для получения деталей
**Возвращает:** Hash with service configuration (variables, settings, etc.)
**Пример:**
```ruby
config = api.get_service("example.my.domain")
puts config.inspect
```
### Перезагрузка конфигурации на instance
```ruby
api.reload_instance(instance_hostname = nil)
```
**Параметры:**
- `instance_hostname` (optional) - hostname instance для перезагрузки (если nil, reloads all instances)
**Пример:**
```ruby
# Reload все instances
api.reload_instance()
# Reload конкретный instance
api.reload_instance("192.168.3.50")
```
### Получение списка всех instances
```ruby
api.list_instances()
```
**Возвращает:** Array of instance objects (hostname, name, port, etc.)
### Создание/регистрация BunkerWeb instance (worker node)
```ruby
api.create_instance(hostname, name = nil, port = 8888, https_port = nil)
```
**Параметры:**
- `hostname` - IP address или hostname worker node
- `name` (optional) - Human-readable имя instance
- `port` - API port на worker node (default: 8888)
- `https_port` (optional) - HTTPS port если есть
**Пример:**
```ruby
api.create_instance(
"192.168.3.50", # IP worker node
"BunkerWeb Worker Node", # Optional name
8888 # API port
)
# Возвращает: { status: 201/409, body: {...} }
# Если статус 201 - instance создан
# Если статус 409 - instance уже существует (это OK)
```
### Удаление BunkerWeb instance
```ruby
api.delete_instance(hostname)
```
**Параметры:**
- `hostname` - hostname instance для удаления
## Примеры полного использования
### Пример 1: Создание и управление сервисом
```ruby
require_relative "HestiaBunkerWebApi.rb"
begin
# 1. Подключаемся к API
api = HestiaBunkerWebApi.new(
"http://127.0.0.1:8888",
"admin",
"your_password"
)
# 2. Создаём reverse proxy сервис без SSL
result = api.create_service("u4.my.brp", {
ssl: "no",
reverse_proxy_host: "http://192.168.3.51:8078"
})
puts "[INFO] Service created: #{result.inspect}"
# 3. Добавляем SSL сертификат позже (если нужно)
api.update_service_ssl(
"u4.my.brp",
"/etc/ssl/certs/u4.crt",
"/etc/ssl/private/u4.key"
)
# 4. Проверяем список сервисов
services = api.list_services()
puts "[INFO] All services:"
services.each { |s| puts "- #{s['server_name']}" }
# 5. Удаление сервиса (при необходимости)
api.delete_service("u4.my.brp")
rescue BunkerWebApiError => e
puts "[ERROR] Ошибка API: #{e.message}"
exit 1
end
```
### Пример 2: Управление несколькими сервисами
```ruby
require_relative "HestiaBunkerWebApi.rb"
api = HestiaBunkerWebApi.new("http://127.0.0.1:8888", "admin", "password")
# Создаём несколько сервисов с разными конфигурациями
services_to_create = [
{ name: "service1.domain", ssl: "no", reverse_proxy_host: "http://192.168.3.50:80" },
{ name: "service2.domain", ssl: "yes", certificate_path: "/certs/service2.crt", key_path: "/keys/service2.key", reverse_proxy_host: "http://192.168.3.51:8078" }
]
services_to_create.each do |opts|
begin
api.create_service(opts[:name], opts)
rescue BunkerWebApiError => e
puts "[ERROR] #{e.message}" if e.message.include?("already exists")
end
end
# Reload конфигурации на instance
api.reload_instance("192.168.3.50")
```
### Пример 3: Обработка ошибок и логирование
```ruby
require_relative "HestiaBunkerWebApi.rb"
def safe_create_service(api_url, username, password, service_name, options)
begin
api = HestiaBunkerWebApi.new(api_url, username, password)
result = api.create_service(service_name, options)
return { success: true, data: result }
rescue BunkerWebApiError => e
if e.message.include?("Authentication")
puts "[FATAL] Authentication failed: #{e.message}"
elsif e.message.include?("already exists")
begin
existing = api.get_service(service_name)
return { success: false, already_exists: true, service: existing }
rescue => get_error
return { success: false, error: "Can't retrieve service: #{get_error.message}" }
else
return { success: false, error: e.message }
end
end
{ success: false, error: "Unknown error" }
end
# Использование
result = safe_create_service("http://127.0.0.1:8888", "admin", "password", "example.domain", { ssl: "yes" })
if result[:success]
puts "[SUCCESS] Service created"
elsif result[:already_exists]
puts "[INFO] Service exists:"
puts JSON.generate(result[:service])
else
puts "[FAILED] #{result[:error]}"
end
```
## Ошибки и их обработка
### Типичные ошибки:
| Код ответа | Описание | Пример сообщения |
|------------|----------|------------------|
| **401** | Authentication failed | "Authentication failed: 401 - Unauthorized" |
| **200 (no token)** | Auth passed but no token | "Authentication succeeded but no token received" |
| **Connection error** | API недоступен | "Authentication error: Connection refused" |
| **409 Conflict** | Service already exists | "Service 'x' already exists" |
| **422 Unprocessable Entity** | Invalid data (например, LISTEN_HTTPS_PORT = nil) | "Failed to create service: status=422..." |
| **429 Too Many Requests** | Rate limit exceeded | "Rate limit exceeded: 10 per 1 minute" |
### Решение проблем с rate limiting
Если получаете ошибку `429` (rate limit), нужно отключить или увеличить лимит в `/etc/bunkerweb/api.env`:
```bash
# Откройте конфиг и найдите секцию Rate limiting
nano /etc/bunkerweb/api.env
# Добавьте/измените:
API_RATE_LIMIT_ENABLED=no # Отключение rate limiting
# или
API_RATE_LIMIT=1000/minute # Увеличение лимита до 1000/m
```
Затем перезагрузите API service:
```bash
systemctl reload bunkerweb-api.service
```
## Особенности реализации
### 1. SSL сертификатные пути
Когда `ssl: "no"` - API ожидает `"LISTEN_HTTPS_PORT" => "null"` (строка), а не JSON null (`nil`):
```ruby
# ❌ Ошибка:
variables["LISTEN_HTTPS_PORT"] = nil # → 422 error
# ✅ Правильно:
variables["LISTEN_HTTPS_PORT"] = "null" # → 200 OK
```
### 2. Статусы ответа для создания сервиса
BunkerWeb API возвращает **200 OK** вместо стандартного **201 Created**:
```ruby
# Класс принимает оба статуса как успех:
if [201, 200].include?(response[:status])
puts "[INFO] Service created successfully"
end
```
### 3. Поддержка HTTP методов
Класс поддерживает все основные HTTP методы для API операций:
- **GET** - получение данных (services, instances)
- **POST** - создание (services, instances, auth)
- **PATCH** - обновление (services)
- **DELETE** - удаление (services, instances)
## Совместимость
- **Ruby**: 3.0+
- **BunkerWeb API**: 1.6.x и выше
- **ZooKeeper/Redis**: не требуются для этого класса (работает через HTTP API напрямую)
## Дополнительные ресурсы
- [Документация BunkerWeb API](https://docs.bunkerweb.io/api.md)
- [API Swagger docs at /docs](http://127.0.0.1:8888/docs)
- [OpenAPI schema](http://127.0.0.1:8888/openapi.json)

View File

@@ -0,0 +1,240 @@
#!/opt/brepo/ruby33/bin/ruby
require 'shellwords'
class BunkerwebWorker < Kernel::ModuleCoreWorker
MODULE_ID = "bunkerweb_module"
def info
{
ID: 5,
NAME: MODULE_ID,
DESCR: "Bunkerweb enabling",
REQ: "",
CONF: "yes",
}
end
def enable
log_file = get_log
f_inst_pp = get_module_paydata("bunkerweb_installer.yml")
if !check
inf = info
log("Req error, needed #{inf[:REQ]}")
"Req error, needed #{inf[:REQ]}"
else
begin
log("install packages for bunkerweb support: /usr/bin/ansible-playbook -vv #{f_inst_pp}")
result_action = `LC_ALL=C.UTF-8 /usr/bin/ansible-playbook -vv "#{f_inst_pp}" 2>&1`
ex_status = $?.exitstatus
if ex_status.to_i == 0 || ex_status.to_i == 2
log(result_action)
super
end
rescue => e
log("module installation error #{e.message} #{e.backtrace.first}")
"module installation error. See log #{log_file}"
end
end
end
def command(args)
return log_return("Not enough arguments. Needed command") if args.length < 1
log_file = get_log
m_command = args[0].strip
case m_command
when "add"
m_domain = args[1].strip unless args[1].nil?
m_ip = args[2].strip unless args[2].nil?
if m_domain.nil? || m_ip.nil?
log_return("Domain and IP should be specified. #{args}")
else
log("add domain to bunkerweb protection")
output = `/usr/local/hestia/bin/v-bunkerweb-module add #{m_domain} #{m_ip} shell`
exit_status = $?.exitstatus
if exit_status != 0
log_return("Command failed with status #{exit_status}")
else
ACTION_OK
end
end
when "delete"
m_domain = args[1].strip unless args[1].nil?
if m_domain.nil?
log_return("Domain should be specified. #{args}")
else
log("add domain to bunkerweb protection")
output = `/usr/local/hestia/bin/v-bunkerweb-module delete #{m_domain} shell`
exit_status = $?.exitstatus
if exit_status != 0
log_return("Command failed with status #{exit_status}")
else
ACTION_OK
end
end
when "addssl"
m_domain = args[1].strip unless args[1].nil?
m_ssl_cert = args[2].strip unless args[2].nil?
m_ssl_key = args[3].strip unless args[3].nil?
if m_domain.nil? || m_ssl_cert.nil? || m_ssl_key.nil? || m_ssl_cert.empty? || m_ssl_key.empty?
log_return("Domain, SSL cert and SSL key must be specified. #{args}")
else
log("add ssl cert to bunkerweb protection")
output = `/usr/local/hestia/bin/v-bunkerweb-module addssl #{m_domain} #{m_ssl_cert} #{m_ssl_key} shell`
exit_status = $?.exitstatus
if exit_status != 0
log_return("Command failed with status #{exit_status}")
else
ACTION_OK
end
end
when "updssl"
m_domain = args[1].strip unless args[1].nil?
m_ssl_cert = args[2].strip unless args[2].nil?
m_ssl_key = args[3].strip unless args[3].nil?
if m_domain.nil? || m_ssl_cert.nil? || m_ssl_key.nil? || m_ssl_cert.empty? || m_ssl_key.empty?
log_return("Domain, SSL cert and SSL key must be specified. #{args}")
else
log("update ssl cert to bunkerweb protection")
output = `/usr/local/hestia/bin/v-bunkerweb-module updssl #{m_domain} #{m_ssl_cert} #{m_ssl_key} shell`
exit_status = $?.exitstatus
if exit_status != 0
log_return("Command failed with status #{exit_status}")
else
ACTION_OK
end
end
when "deletessl"
m_domain = args[1].strip unless args[1].nil?
if m_domain.nil?
log_return("Domain should be specified. #{args}")
else
log("delete ssl cert to bunkerweb protection")
output = `/usr/local/hestia/bin/v-bunkerweb-module deletessl #{m_domain} shell`
exit_status = $?.exitstatus
if exit_status != 0
log_return("Command failed with status #{exit_status}")
else
ACTION_OK
end
end
when "list"
format = (args[1].nil? ? "shell" : args[1].strip)
log("list of services")
output = `/usr/local/hestia/bin/v-bunkerweb-module list #{format}`
exit_status = $?.exitstatus
if exit_status != 0
log_return("Command failed with status #{exit_status}")
else
puts output
ACTION_OK
end
when "passwd"
format = (args[1].nil? ? "shell" : args[1].strip)
cred = {}
api_file = "/etc/bunkerweb/api.env"
if File.exist?(api_file)
File.readlines(api_file).each do |line|
line.strip!
next if line.empty? || line.start_with?('#')
key, value = line.split('=', 2)
if %w[API_USERNAME API_PASSWORD].include?(key)
cred[key] = value
end
end
else
cred["API_USERNAME"] = nil
cred["API_PASSWORD"] = nil
end
cred["API_USERNAME"] ||= nil
cred["API_PASSWORD"] ||= nil
ui_file = "/etc/bunkerweb/ui.env"
if File.exist?(ui_file)
File.readlines(ui_file).each do |line|
line.strip!
next if line.empty? || line.start_with?('#')
key, value = line.split('=', 2)
if %w[ADMIN_USERNAME ADMIN_PASSWORD].include?(key)
cred[key] = value
end
end
else
cred["ADMIN_USERNAME"] = nil
cred["ADMIN_PASSWORD"] = nil
end
cred["ADMIN_USERNAME"] ||= nil
cred["ADMIN_PASSWORD"] ||= nil
result = []
result << cred
hestia_print_array_of_hashes(result, format, "API_USERNAME,API_PASSWORD,ADMIN_USERNAME,ADMIN_PASSWORD")
ACTION_OK
when "configure"
param1 = args[1]
param2 = args[2]
if param1 && param2 && !param1.strip.empty? && !param2.strip.empty?
cmd = "/usr/local/hestia/bin/v-bunkerweb-module-install #{Shellwords.escape(param1)} #{Shellwords.escape(param2)}"
else
cmd = "/usr/local/hestia/bin/v-bunkerweb-module-install"
end
output = `#{cmd} 2>&1`
exit_status = $?.exitstatus
if exit_status != 0
log_return("#{output}\nCommand failed with status #{exit_status}")
else
puts output
ACTION_OK
end
when "alias"
m_domain = args[1].strip unless args[1].nil?
m_alias = args[2].strip unless args[2].nil?
if m_domain.nil?
log_return("Domain should be specified. #{args}")
else
log("add alias #{m_alias} to domain #{m_domain} to bunkerweb protection")
output = `/usr/local/hestia/bin/v-bunkerweb-module alias #{m_domain} "#{m_alias}" shell`
exit_status = $?.exitstatus
if exit_status != 0
log_return("Command failed with status #{exit_status}")
else
ACTION_OK
end
end
when "help"
puts "#{$0} bunkerweb_module COMMAND [OPTIONS] [json|csv|plain]"
puts "COMMANDS:"
puts " add domain - add domain to bunkerweb"
puts " delete domain - delete domain from bunkerweb"
puts " addssl domain [path_to_cert] [path_to_key] - add existsing certificate to bunkerweb domain"
puts " updssl domain [path_to_cert] [path_to_key] - update existsing certificate to bunkerweb domain"
puts " passwd - get ui and api passwd"
puts " configure [path_to_cert] [path_to_key] - start initial setup of bunkerweb should do only once"
puts " help - help"
ACTION_OK
else
log_return("Unknown command. #{args}")
end
end
implements IPluginInterface
end
module BunkerwebModule
def get_object
Proc.new { BunkerwebWorker.new }
end
module_function :get_object
end
class Kernel::PluginConfiguration
include BunkerwebModule
@@loaded_plugins[BunkerwebWorker::MODULE_ID] = BunkerwebModule.get_object
end

View File

@@ -0,0 +1 @@
7

View File

@@ -20,15 +20,15 @@ class PassengerWorker < Kernel::ModuleCoreWorker
ID: 2,
NAME: MODULE_ID,
DESCR: "Added passenger support for nginx",
REQ: "puppet_installer",
REQ: "",
CONF: "yes",
}
end
def enable
log_file = get_log
f_inst_pp = get_module_paydata("passenger_installer.pp")
f_uninst_pp = get_module_paydata("passenger_uninstaller.pp")
f_inst_pp = get_module_paydata("passenger_installer.yml")
f_uninst_pp = get_module_paydata("passenger_uninstaller.yml")
if !check
inf = info
log("Req error, needed #{inf[:REQ]}")
@@ -36,16 +36,16 @@ class PassengerWorker < Kernel::ModuleCoreWorker
else
begin
prepare_default_ruby_conf
log("install packages for passenger + nginx support: /usr/bin/puppet apply --detailed-exitcodes #{f_inst_pp}")
result_action = `/usr/bin/puppet apply --detailed-exitcodes "#{f_inst_pp}" 2>&1`
log("install packages for passenger + nginx support: /usr/bin/ansible-playbook -vv #{f_inst_pp}")
result_action = `LC_ALL=C.UTF-8 /usr/bin/ansible-playbook -vv "#{f_inst_pp}" 2>&1`
ex_status = $?.exitstatus
if ex_status.to_i == 0 || ex_status.to_i == 2
log(result_action)
super
else
log(result_action)
log("Try to disable action: /usr/bin/puppet apply --detailed-exitcodes #{f_uninst_pp}")
result_action = `/usr/bin/puppet apply --detailed-exitcodes "#{f_uninst_pp}" 2>&1`
log("Try to disable action: /usr/bin/ansible-playbook -vv #{f_uninst_pp}")
result_action = `LC_ALL=C.UTF-8 /usr/bin/ansible-playbook -vv "#{f_uninst_pp}" 2>&1`
"module installation error. See log #{log_file}"
end
rescue => e
@@ -57,14 +57,14 @@ class PassengerWorker < Kernel::ModuleCoreWorker
def disable
log_file = get_log
f_uninst_pp = get_module_paydata("passenger_uninstaller.pp")
f_uninst_pp = get_module_paydata("passenger_uninstaller.yml")
if !check_domains_with_passenger
return log_return("Presents domains with passenger support disable it first")
end
begin
log("uninstall packages for passenger + nginx support")
log("Try to disable action: /usr/bin/puppet apply --detailed-exitcodes #{f_uninst_pp}")
result_action = `/usr/bin/puppet apply --detailed-exitcodes "#{f_uninst_pp}" 2>&1`
log("Try to disable action: /usr/bin/ansible-playbook -vv #{f_uninst_pp}")
result_action = `LC_ALL=C.UTF-8 /usr/bin/ansible-playbook -vv "#{f_uninst_pp}" 2>&1`
ex_status = $?.exitstatus
if ex_status.to_i == 0 || ex_status.to_i == 2
log(result_action)

View File

@@ -0,0 +1,14 @@
---
- name: Install Bunkerweb on localhost
hosts: localhost
connection: local
become: true
gather_facts: false
environment:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
tasks:
- name: Install bunkerweb
ansible.builtin.dnf:
name: bunkerweb
state: present

View File

@@ -0,0 +1,69 @@
---
- name: Install Passenger and configure Nginx on localhost
hosts: localhost
connection: local
become: true
gather_facts: false
environment:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
tasks:
# Устанавливаем Ruby и зависимости
- name: Install rubygems-devel
ansible.builtin.dnf:
name: rubygems-devel
state: present
- name: Install rubygem-rake
ansible.builtin.dnf:
name: rubygem-rake
state: present
- name: Install ruby-devel
ansible.builtin.dnf:
name: ruby-devel
state: present
- name: Install rubygem-rack
ansible.builtin.dnf:
name: rubygem-rack
state: present
- name: Install alt-brepo-ruby33-devel
ansible.builtin.dnf:
name: alt-brepo-ruby33-devel
state: present
- name: Install alt-brepo-ruby33-rubygem-rake
ansible.builtin.dnf:
name: alt-brepo-ruby33-rubygem-rake
state: present
# Устанавливаем Passenger и модуль Nginx
- name: Install passenger-devel
ansible.builtin.dnf:
name: passenger-devel
state: present
- name: Install passenger
ansible.builtin.dnf:
name: passenger
state: present
- name: Install nginx-mod-http-passenger
ansible.builtin.dnf:
name: nginx-mod-http-passenger
state: present
# Конфигурируем Nginx для Passenger
- name: Create passenger.conf
ansible.builtin.copy:
dest: /usr/local/hestia/nginx-system/etc/nginx/conf.d/passenger.conf
content: |
passenger_root /usr/share/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby /usr/bin/ruby;
passenger_instance_registry_dir /var/run/passenger-instreg;
passenger_user_switching on;
passenger_env_var PASSENGER_COMPILE_NATIVE_SUPPORT_BINARY 0;
passenger_env_var PASSENGER_DOWNLOAD_NATIVE_SUPPORT_BINARY 0;
- name: Create passenger_includer.conf
ansible.builtin.copy:
dest: /usr/local/hestia/nginx-system/etc/nginx/conf.d/main/passenger.conf
content: |
load_module modules/ngx_http_passenger_module.so;
# Перезапускаем Nginx
- name: Restart nginx service
ansible.builtin.service:
name: nginx-system
state: restarted

View File

@@ -0,0 +1,38 @@
---
- name: Uninstall Passenger and configure Nginx on localhost
hosts: localhost
connection: local
become: true
gather_facts: false
environment:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
tasks:
# Удалаем модуль nginx-passenger
- name: Remove nginx-mod-http-passenger package
ansible.builtin.dnf:
name: nginx-mod-http-passenger
state: absent
# Удалаем passenger и зависимости
- name: Remove passenger-devel package
ansible.builtin.dnf:
name: passenger-devel
state: absent
- name: Remove passenger package
ansible.builtin.dnf:
name: passenger
state: absent
# Удаляем конфигурационные файлы Nginx
- name: Remove passenger.conf
ansible.builtin.file:
path: /usr/local/hestia/nginx-system/etc/nginx/conf.d/passenger.conf
state: absent
- name: Remove passenger_includer.conf
ansible.builtin.file:
path: /usr/local/hestia/nginx-system/etc/nginx/conf.d/main/passenger.conf
state: absent
# Перезапускаем Nginx (необязательно, но полезно)
- name: Restart nginx service
ansible.builtin.service:
name: nginx-system
state: restarted

View File

@@ -1,6 +1,6 @@
#!/opt/brepo/ruby33/bin/ruby
class EmptyWorker < Kernel::ModuleCoreWorker
class PHPWorker < Kernel::ModuleCoreWorker
MODULE_ID = "php_brepo_modules"
def info
@@ -241,16 +241,16 @@ class EmptyWorker < Kernel::ModuleCoreWorker
implements IPluginInterface
end
module EmptyModule
module PHPModule
def get_object
Proc.new { EmptyWorker.new }
Proc.new { PHPWorker.new }
end
module_function :get_object
end
class Kernel::PluginConfiguration
include EmptyModule
include PHPModule
@@loaded_plugins[EmptyWorker::MODULE_ID] = EmptyModule.get_object
@@loaded_plugins[PHPWorker::MODULE_ID] = PHPModule.get_object
end

View File

@@ -0,0 +1,122 @@
#!/opt/brepo/ruby33/bin/ruby
require 'pathname'
require 'fileutils'
require 'digest'
class UpdateWorker < Kernel::ModuleCoreWorker
MODULE_ID = "update_module"
def info
{
ID: 6,
NAME: MODULE_ID,
DESCR: "Module for updating HestiaCP data and templates",
REQ: "",
CONF: "yes",
}
end
def file_changed?(new_file, old_file)
return true unless File.exist?(old_file)
new_hash = Digest::SHA256.file(new_file).hexdigest
old_hash = Digest::SHA256.file(old_file).hexdigest
new_hash != old_hash
end
def get_templates_map()
{ :templates=>
[
{:new=>"/usr/local/hestia/install/rpm/templates/web/awstats", :old=>"/usr/local/hestia/data/templates/web/awstats"},
{:new=>"/usr/local/hestia/install/rpm/templates/web/httpd", :old=>"/usr/local/hestia/data/templates/web/httpd"},
{:new=>"/usr/local/hestia/install/rpm/templates/web/nginx", :old=>"/usr/local/hestia/data/templates/web/nginx"},
{:new=>"/usr/local/hestia/install/rpm/templates/web/php-fpm", :old=>"/usr/local/hestia/data/templates/web/php-fpm"}
]
}
end
# New helper method to get list of changed template files
def get_changed_template_files
templates_map = get_templates_map()[:templates]
result = []
templates_map.each do |tpl|
new_dir = tpl[:new]
old_dir = tpl[:old]
Dir.glob(File.join(new_dir, '**', '*')).each do |new_file|
next if File.directory?(new_file)
rel_path = Pathname.new(new_file).relative_path_from(Pathname.new(new_dir)).to_s
old_file = File.join(old_dir, rel_path)
result << [new_dir, new_file, old_file] if file_changed?(new_file, old_file)
end
end
result
end
def command(args)
return log_return("Not enough arguments. Needed command") if args.length < 1
log_file = get_log
m_command = args[0].strip
case m_command
when "synctemplates"
result = get_changed_template_files
result.each do |new_dir, new_file, old_file|
if !File.exist?(old_file)
FileUtils.cp(new_file, old_file)
else
stat = File.stat(old_file)
uid = stat.uid
gid = stat.gid
mode = stat.mode & 0o7777
FileUtils.cp(new_file, old_file)
File.chown(uid, gid, old_file)
File.chmod(mode, old_file)
end
end
ACTION_OK
when "listsynctemplates"
format = (args[1].nil? ? "shell" : args[1].strip)
list = get_changed_template_files
result = []
result = list.map do |new_dir, new_file, old_file|
file_name = Pathname.new(new_file).relative_path_from(Pathname.new(new_dir)).to_s
dir_name = File.basename(new_dir)
relative_path = Pathname.new(new_file).relative_path_from(Pathname.new(new_dir)).to_s
file_name = File.join(dir_name, relative_path)
{
"FILE_NAME" => file_name,
"NEW_SIZE" => File.size(new_file),
"OLD_SIZE" => File.exist?(old_file) ? File.size(old_file) : "-"
}
end
hestia_print_array_of_hashes(result, format, "FILE_NAME,NEW_SIZE,OLD_SIZE")
ACTION_OK
when "help"
puts "#{$0} update_module COMMAND [json|csv|plain]"
puts "COMMANDS:"
puts " synctemplates - sync web templates"
puts " listsynctemplates - show changed web templates"
puts " help - help"
ACTION_OK
else
log_return("Unknown command. #{args}")
end
end
implements IPluginInterface
end
module UpdateModule
def get_object
Proc.new { UpdateWorker.new }
end
module_function :get_object
end
class Kernel::PluginConfiguration
include UpdateModule
@@loaded_plugins[UpdateWorker::MODULE_ID] = UpdateModule.get_object
end

View File

@@ -77,6 +77,10 @@ class File
end
end
def hestia_print_info_message_to_cli(error_message)
puts "Info: #{error_message}"
end
def hestia_print_error_message_to_cli(error_message)
puts "Error: #{error_message}"
end
@@ -315,3 +319,80 @@ def hestia_save_file_key_pair(file, key, value)
end
end
end
def hestia_change_sys_config_value(key, value)
# Privileged access check
hestia_check_privileged_user unless Process.uid == 0
config_file = "/usr/local/hestia/conf/hestia.conf"
if File.exist?(config_file)
# First pass: read entire file to check if key exists and get all lines
content = nil
File.open(config_file, "r") do |f|
content = f.read
end
if content
lines = content.split("\n")
# Check if key exists in the configuration file
existing_line_index = -1
lines.each_with_index do |line, idx|
line_stripped = line.strip
# Skip comment lines
next if line_stripped.start_with?("#")
next if line_stripped.empty?
key_match = line_stripped.match(/^\s*#{Regexp.escape(key)}='\s*(.*?)\s*$/)
if key_match
existing_line_index = idx + 1
break
end
end
if existing_line_index.nil? || existing_line_index == -1
# Key doesn't exist - append new line to file
File.open(config_file, "a") do |append_f|
append_f.puts("#{key}='#{value}'")
end
OK
else
# Key exists - update value using Ruby operators (in-place edit)
# Use temporary file for safety and atomic replacement
temp_file = "#{config_file}.tmp"
# Second pass: rebuild the content with updated value
new_lines = []
lines.each do |line|
line_stripped = line.strip
# Skip comment lines
next if line_stripped.start_with?("#")
next if line_stripped.empty?
# Match and replace the key-value pair
if line.match(/^\s*#{Regexp.escape(key)}='[^']*'/)
new_lines << "#{key}='#{value}'"
else
new_lines << line
end
end
File.open(temp_file, "w") do |output_f|
new_lines.each { |l| output_f.puts(l) }
end
# Atomic file replacement
File.rename(temp_file, config_file)
OK
end
else
OK
end
else
check_result error_code: E_NOTEXIST, error_message: "Configuration file #{config_file} does not exist"
end
end

View File

View File

@@ -13,7 +13,7 @@ location /%pma_alias% {
location ~ ^/%pma_alias%/(.*\.php)$ {
alias /usr/share/phpmyadmin/$1;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
fastcgi_param SCRIPT_FILENAME $request_filename;

View File

@@ -3,7 +3,7 @@ location /%pga_alias% {
location ~ ^/%pga_alias%/(.*\.php)$ {
alias /usr/share/phppgadmin/$1;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
fastcgi_param SCRIPT_FILENAME $request_filename;

View File

@@ -39,7 +39,7 @@ server {
}
location ~ ^/(.*\.php)$ {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -32,7 +32,7 @@ server {
}
location ~ ^/(.*\.php)$ {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $request_filename;

View File

@@ -34,7 +34,7 @@ server {
}
location ~ ^/(.*\.php)$ {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -27,7 +27,7 @@ server {
}
location ~ ^/(.*\.php)$ {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $request_filename;

View File

@@ -70,7 +70,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -61,7 +61,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -40,7 +40,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -31,7 +31,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -56,7 +56,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -47,7 +47,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -61,7 +61,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -51,7 +51,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -111,7 +111,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -102,7 +102,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -38,7 +38,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -29,7 +29,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -42,7 +42,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -33,7 +33,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -68,7 +68,7 @@ server {
location ~ [^/]\.php(/|$)|^/update.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -60,7 +60,7 @@ server {
location ~ [^/]\.php(/|$)|^/update.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -68,7 +68,7 @@ server {
location ~ [^/]\.php(/|$)|^/update.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -59,7 +59,7 @@ server {
location ~ [^/]\.php(/|$)|^/update.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -78,7 +78,7 @@ server {
location ~ [^/]\.php(/|$)|^/update.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -69,7 +69,7 @@ server {
location ~ [^/]\.php(/|$)|^/update.php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -32,7 +32,7 @@ server {
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -23,7 +23,7 @@ server {
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -40,7 +40,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -31,7 +31,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -57,7 +57,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -47,7 +47,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -51,7 +51,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -42,7 +42,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -32,7 +32,7 @@ server {
root %sdocroot%;
location ~ ^/setup/index.php {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
@@ -57,7 +57,7 @@ server {
root %sdocroot%;
location ~ ^/update/index.php {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
@@ -171,7 +171,7 @@ server {
location ~ (index|get|static|report|404|503)\.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_buffers 1024 4k;
fastcgi_connect_timeout 600s;

View File

@@ -28,7 +28,7 @@ server {
fastcgi_pass %backend_lsnr%;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;
}
@@ -46,7 +46,7 @@ server {
root %docroot%;
location ~ ^/update/index.php {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param PATH_INFO $fastcgi_path_info;
@@ -159,7 +159,7 @@ server {
location ~ (index|get|static|report|404|503)\.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_buffers 1024 4k;
fastcgi_connect_timeout 600s;

View File

@@ -68,7 +68,7 @@ server {
}
location ~ [^/]\.php(/|$) {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -60,7 +60,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -51,7 +51,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -66,7 +66,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $fastcgi_script_name =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_intercept_errors on;

View File

@@ -57,7 +57,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $fastcgi_script_name =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_intercept_errors on;

View File

@@ -40,7 +40,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -31,7 +31,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -76,7 +76,7 @@ server {
}
location ~ \.php$ {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

View File

@@ -67,7 +67,7 @@ server {
}
location ~ \.php$ {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param PATH_INFO $path_info;

View File

@@ -63,7 +63,7 @@ server {
try_files $uri $uri/ /index.php;
location ~ \.php(?:$|/) {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
#fastcgi_param HTTPS on;

View File

@@ -54,7 +54,7 @@ server {
try_files $uri $uri/ /index.php;
location ~ \.php(?:$|/) {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
#fastcgi_param HTTPS on;
fastcgi_param PATH_INFO $fastcgi_path_info;

View File

@@ -50,7 +50,7 @@ server {
# Pass the php scripts to FastCGI server specified in upstream declaration.
location ~ \.php(/|$) {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
@@ -81,7 +81,7 @@ server {
# Pass the php scripts to fastcgi server specified in upstream declaration.
location ~ \.php(/|$) {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.*)$;

View File

@@ -41,7 +41,7 @@ server {
# Pass the php scripts to FastCGI server specified in upstream declaration.
location ~ \.php(/|$) {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
@@ -71,7 +71,7 @@ server {
# Pass the php scripts to fastcgi server specified in upstream declaration.
location ~ \.php(/|$) {
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.*)$;

View File

@@ -50,7 +50,7 @@ server {
location ~* ^/(?:index|piwik)\.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -41,7 +41,7 @@ server {
location ~* ^/(?:index|piwik)\.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_paramsystem/etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@@ -120,7 +120,7 @@ server {
location ~ [^/]\.php(/|$) {
try_files $fastcgi_script_name /index.php$uri&$args =404;
include /etc/nginx/fastcgi_params;
include /usr/local/hestia/nginx-system/etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_param HTTP_EARLY_DATA $rfc_early_data if_not_empty;

Some files were not shown because too many files have changed in this diff Show More