From 218c7b14adcead2e886654fcbc45667063f71256 Mon Sep 17 00:00:00 2001 From: Raphael Schneeberger Date: Tue, 27 Oct 2020 07:54:29 +0100 Subject: [PATCH 001/223] Adjust first steps to gain ipv6 support for firewall. --- bin/v-add-firewall-ban | 5 +++-- bin/v-add-firewall-chain | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bin/v-add-firewall-ban b/bin/v-add-firewall-ban index 4bbc0a5df9..80ac86fdc4 100755 --- a/bin/v-add-firewall-ban +++ b/bin/v-add-firewall-ban @@ -18,6 +18,7 @@ chain=$(echo $2|tr '[:lower:]' '[:upper:]') # Defining absolute path for iptables and modprobe iptables="/sbin/iptables" +ip6tables="/sbin/ip6tables" # Includes source $HESTIA/func/main.sh @@ -32,16 +33,16 @@ check_args '2' "$#" 'IP CHAIN' is_format_valid 'ip' 'chain' is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' - # Perform verification if read-only mode is enabled check_hestia_demo_mode + #----------------------------------------------------------# # Action # #----------------------------------------------------------# # Checking server ip -if [ -e "$HESTIA/data/ips/$ip" ] || [ "$ip" = '127.0.0.1' ]; then +if [ -e "$HESTIA/data/ips/$ip" ] || [ "$ip" = '127.0.0.1' ] || [ "$ip" = '::1' ]; then exit fi diff --git a/bin/v-add-firewall-chain b/bin/v-add-firewall-chain index 8cdf7ce021..ad59d021a8 100755 --- a/bin/v-add-firewall-chain +++ b/bin/v-add-firewall-chain @@ -1,6 +1,6 @@ #!/bin/bash # info: add firewall chain -# options: CHAIN [PORT] [PROTOCOL] [PROTOCOL] +# options: CHAIN [PORT] [PROTOCOL] # # The function adds new rule to system firewall @@ -15,11 +15,11 @@ source /etc/profile # Argument definition chain=$(echo $1 | tr '[:lower:]' '[:upper:]') port=$2 -protocol=${4-TCP} protocol=$(echo $protocol|tr '[:lower:]' '[:upper:]') # Defining absolute path to iptables iptables="/sbin/iptables" +ip6tables="/sbin/ip6tables" # Get hestia port by reading nginx.conf hestiaport=$(grep 'listen' $HESTIA/nginx/conf/nginx.conf | awk '{print $2}' | sed "s|;||") From 294723250ab8348c1783ea18bbbecf8ab809ab7d Mon Sep 17 00:00:00 2001 From: Raphael Schneeberger Date: Sat, 31 Oct 2020 11:11:31 +0100 Subject: [PATCH 002/223] Add first bunch of ipv6 api scripts, thanks to www.madeit.be --- bin/v-add-firewall-ipv6-rule | 94 ++++++++++++ bin/v-add-sys-ipv6 | 181 +++++++++++++++++++++++ bin/v-change-dns-domain-ipv6 | 120 ++++++++++++++++ bin/v-change-firewall-ipv6-rule | 85 +++++++++++ bin/v-change-sys-ipv6-name | 48 +++++++ bin/v-change-sys-ipv6-owner | 48 +++++++ bin/v-change-sys-ipv6-status | 56 ++++++++ bin/v-change-web-domain-ipv6 | 120 ++++++++++++++++ bin/v-delete-firewall-ipv6-rule | 51 +++++++ bin/v-delete-sys-ipv6 | 149 +++++++++++++++++++ bin/v-list-firewall-ipv6 | 51 +++++++ bin/v-list-firewall-ipv6-rule | 88 ++++++++++++ bin/v-suspend-firewall-ipv6-rule | 49 +++++++ bin/v-update-firewall | 1 + func/ipv6.sh | 224 +++++++++++++++++++++++++++++ install/upgrade/manual/add_ipv6.sh | 69 +++++++++ 16 files changed, 1434 insertions(+) create mode 100644 bin/v-add-firewall-ipv6-rule create mode 100644 bin/v-add-sys-ipv6 create mode 100644 bin/v-change-dns-domain-ipv6 create mode 100644 bin/v-change-firewall-ipv6-rule create mode 100644 bin/v-change-sys-ipv6-name create mode 100644 bin/v-change-sys-ipv6-owner create mode 100644 bin/v-change-sys-ipv6-status create mode 100644 bin/v-change-web-domain-ipv6 create mode 100644 bin/v-delete-firewall-ipv6-rule create mode 100644 bin/v-delete-sys-ipv6 create mode 100644 bin/v-list-firewall-ipv6 create mode 100644 bin/v-list-firewall-ipv6-rule create mode 100644 bin/v-suspend-firewall-ipv6-rule create mode 100644 func/ipv6.sh create mode 100644 install/upgrade/manual/add_ipv6.sh diff --git a/bin/v-add-firewall-ipv6-rule b/bin/v-add-firewall-ipv6-rule new file mode 100644 index 0000000000..1887437c30 --- /dev/null +++ b/bin/v-add-firewall-ipv6-rule @@ -0,0 +1,94 @@ +#!/bin/bash +# info: add firewall rule +# options: ACTION IPV6 PORT [PROTOCOL] [COMMENT] [RULE] +# +# The function adds new rule to system firewall + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +action=$(echo $1|tr '[:lower:]' '[:upper:]') +ipv6=$2 +port_ext=$3 +protocol=${4-TCP} +protocol=$(echo $protocol|tr '[:lower:]' '[:upper:]') +comment=$5 +rule=$6 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + +# Get next firewall rule id +get_next_fw_rule() { + if [ -z "$rule" ]; then + curr_str=$(grep "RULE=" $HESTIA/data/firewallv6/rules.conf |\ + cut -f 2 -d \' | sort -n | tail -n1) + rule="$((curr_str +1))" + fi +} + +sort_fw_rules() { + cat $HESTIA/data/firewallv6/rules.conf |\ + sort -n -k 2 -t \' > $HESTIA/data/firewallv6/rules.conf.tmp + mv -f $HESTIA/data/firewallv6/rules.conf.tmp \ + $HESTIA/data/firewallv6/rules.conf +} + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '3' "$#" 'ACTION IPV6 PORT [PROTOCOL] [COMMENT] [RULE]' +is_format_valid 'action' 'protocol' 'port_ext' 'ipv6' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +get_next_fw_rule +is_format_valid 'rule' +is_object_new '../../data/firewallv6/rules' 'RULE' "$rule" +if [ ! -z "$comment" ]; then + is_format_valid 'comment' +fi + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Generating timestamp +time_n_date=$(date +'%T %F') +time=$(echo "$time_n_date" |cut -f 1 -d \ ) +date=$(echo "$time_n_date" |cut -f 2 -d \ ) + +# Concatenating rule +str="RULE='$rule' ACTION='$action' PROTOCOL='$protocol' PORT='$port_ext'" +str="$str IP6='$ipv6' COMMENT='$comment' SUSPENDED='no'" +str="$str TIME='$time' DATE='$date'" + +# Adding to config +echo "$str" >> $HESTIA/data/firewallv6/rules.conf + +# Changing permissions +chmod 660 $HESTIA/data/firewallv6/rules.conf + +# Sorting firewall rules by id number +sort_fw_rules + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-add-sys-ipv6 b/bin/v-add-sys-ipv6 new file mode 100644 index 0000000000..66a98b66f8 --- /dev/null +++ b/bin/v-add-sys-ipv6 @@ -0,0 +1,181 @@ +#!/bin/bash +# info: add system ip address +# options: IPV6 NETMASK [INTERFACE] [USER] [IP_STATUS] [IP_NAME] +# +# The function adds ipv6 address into a system. It also creates rc scripts. You +# can specify ipv6 name which will be used as root domain for temporary aliases. +# For example, if you set a1.myhosting.com as name, each new domain created on +# this ipv6 will automatically receive alias $domain.a1.myhosting.com. Of course +# you must have wildcard record *.a1.myhosting.com pointed to ipv6. This feature +# is very handy when customer wants to test domain before dns migration. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +ipv6=${1// /} +netmask=$2 +interface="${3-eth0}" +user="${4-admin}" +ip_status="${5-shared}" +ip_name=$6 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/func/ipv6.sh +source $HESTIA/func/domain.sh +source $HESTIA/conf/hestia.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '2' "$#" 'IPV6 NETMASK [INTERFACE] [USER] [STATUS] [NAME]' +is_format_valid 'ipv6' 'netmaskv6' 'interface' 'user' 'ip_status' +is_ipv6_free +is_object_valid 'user' 'USER' "$user" +is_object_unsuspended 'user' 'USER' "$user" +if [ ! -z "$ip_name" ] ; then + is_format_valid 'ip_name' +fi + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# +iface=$(get_ipv6_iface) +#cidr=$(convert_netmaskv6 $netmask) +cidr=$netmask + +sys_ip_check=$(/sbin/ip -6 addr | grep "$ipv6") +if [ -z "$sys_ip_check" ]; then + # Adding sys ip + /sbin/ip addr add $ipv6/$cidr dev $interface + + # Adding RHEL/CentOS/Fedora startup script + if [ -e "/etc/redhat-release" ]; then + sys_ip="# Added by hestia" + sys_ip="$sys_ip\nIPV6INIT=yes" + sys_ip="$sys_ip\nIPV6ADDR=$ipv6/$cidr" + sys_ip="$sys_ip\nIPV6_DEFAULTGW=$interface" + sys_ip="$sys_ip\nIPV6_AUTOCONF=no" + #sys_ip="$sys_ip\nIPV6ADDR_SECONDARIES=""" + echo -e $sys_ip > /etc/sysconfig/network-scripts/ifcfg-$interface + fi + + # Adding Debian/Ubuntu startup script + if [ -e "/etc/debian_version" ]; then + sys_ip="\n# Added by hestia" + sys_ip="$sys_ip\niface $interface inet6 static" + sys_ip="$sys_ip\naddress $ipv6" + sys_ip="$sys_ip\nnetmask $cidr" + echo -e $sys_ip >> /etc/network/interfaces + fi +fi + +# Generating timestamp +time_n_date=$(date +'%T %F') +time=$(echo "$time_n_date" |cut -f 1 -d \ ) +date=$(echo "$time_n_date" |cut -f 2 -d \ ) + +# Adding hestia ip +echo "OWNER='$user' +STATUS='$ip_status' +NAME='$ip_name' +U_SYS_USERS='' +U_WEB_DOMAINS='0' +INTERFACE='$interface' +NETMASK='$netmask' +NAT='' +TIME='$time' +DATE='$date' +VERSION='6'" > $HESTIA/data/ips/$ipv6 +chmod 660 $HESTIA/data/ips/$ipv6 + +# WEB support +if [ ! -z "$WEB_SYSTEM" ]; then + web_conf="/etc/$WEB_SYSTEM/conf.d/$ipv6.conf" + rm -f $web_conf + + if [ "$WEB_SYSTEM" = 'httpd' ] || [ "$WEB_SYSTEM" = 'apache2' ]; then + if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then + echo "NameVirtualHost [$ipv6]:$WEB_PORT" > $web_conf + fi + echo "Listen [$ipv6]:$WEB_PORT" >> $web_conf + fi + + if [ "$WEB_SSL" = 'mod_ssl' ]; then + if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then + echo "NameVirtualHost [$ipv6]:$WEB_SSL_PORT" >> $web_conf + fi + echo "Listen [$ipv6]:$WEB_SSL_PORT" >> $web_conf + fi +fi + +# Proxy support +if [ ! -z "$PROXY_SYSTEM" ]; then + cat $WEBTPL/$PROXY_SYSTEM/proxy_ip.tpl |\ + sed -e "s/%ip%/[$ipv6]/g" \ + -e "s/%web_port%/$WEB_PORT/g" \ + -e "s/%proxy_port%/$PROXY_PORT/g" \ + > /etc/$PROXY_SYSTEM/conf.d/$ipv6.conf + + # mod_rpaf + rpaf_conf="/etc/$WEB_SYSTEM/mods-enabled/rpaf.conf" + if [ -e "$rpaf_conf" ]; then + rpaf_str=$(grep RPAFproxy_ips $rpaf_conf) + rpaf_str="$rpaf_str $ipv6" + sed -i "s/.*RPAFproxy_ips.*/$rpaf_str/" $rpaf_conf + fi + + #mod_remoteip + remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf" + if [ -e "$remoteip_conf" ]; then + if [ $( grep -ic "$ipv6" $remoteip_conf ) -eq 0 ]; then + sed -i "s/<\/IfModule>/RemoteIPInternalProxy $ipv6\n<\/IfModule>/g" $remoteip_conf + fi + fi +fi + + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +# Updating user counters +increase_user_value "$user" '$IPV6_OWNED' +if [ "$user" = 'admin' ]; then + if [ "$ip_status" = 'shared' ]; then + for user in $(ls $HESTIA/data/users); do + increase_user_value "$user" '$IPV6_AVAIL' + done + else + increase_user_value 'admin' '$IPV6_AVAIL' + fi +else + increase_user_value "$user" '$IPV6_AVAIL' + increase_user_value 'admin' '$IPV6_AVAIL' +fi + +# Restarting web server +$BIN/v-restart-web +check_result $? "Web restart failed" >/dev/null + +# Restarting proxy server +if [ ! -z "$PROXY_SYSTEM" ]; then + $BIN/v-restart-proxy + check_result $? "Proxy restart failed" >/dev/null +fi + +# Restarting firewall +if [ ! -z "$FIREWALL_SYSTEM" ]; then + $BIN/v-update-firewall +fi + +# Logging +log_history "added system ipv6 address $ipv6" '' 'admin' +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-change-dns-domain-ipv6 b/bin/v-change-dns-domain-ipv6 new file mode 100644 index 0000000000..13e8092c15 --- /dev/null +++ b/bin/v-change-dns-domain-ipv6 @@ -0,0 +1,120 @@ +#!/bin/bash +# info: change dns domain ip address +# options: USER DOMAIN IPV6 +# +# The function for changing the main ipv6 of DNS zone. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +user=$1 +domain=$2 +domain_idn=$2 +ipv6=$3 +restart=$4 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/func/ipv6.sh +source $HESTIA/func/domain.sh +source $HESTIA/conf/hestia.conf + +# Additional argument formatting +format_domain +format_domain_idn + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '3' "$#" 'USER DOMAIN IPV6' +is_format_valid 'user' 'domain' +is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' +is_object_valid 'user' 'USER' "$user" +is_object_unsuspended 'user' 'USER' "$user" +is_object_valid 'dns' 'DOMAIN' "$domain" +is_object_unsuspended 'dns' 'DOMAIN' "$domain" + +if [ "$ipv6" != "no" ]; then + is_format_valid 'ipv6' +fi +if [ "$ipv6" != "no" ]; then + is_ipv6_valid "$ipv6" "$user" +else + ipv6='' +fi + +# Get old ip +get_domain_values 'dns' +if [ -z @"$ipv6" ] && [ -z "$IP" ]; then + check_result $E_INVALID "IP or IPv6 is required" +fi + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +old=$IP6 + +if [ -z "$old" ]; then + #Create new + # Generating timestamp + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" |cut -f 1 -d \ ) + date=$(echo "$time_n_date" |cut -f 2 -d \ ) + ip="" + add_dns_config_records +else + if [ ! -z "$ipv6" ]; then + # Changing records + sed -i "s/$old/$ipv6/g" $USER_DATA/dns/$domain.conf + else + #Delete configs + ipv6="" + ip=$IP + remove_dns_config_records + fi +fi + +# Changing ip +update_object_value 'dns' 'DOMAIN' "$domain" '$IP6' "$ipv6" + +#update counters +records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f1 -d ' ')" +update_object_value 'dns' 'DOMAIN' "$domain" '$RECORDS' "$records" +records=$(wc -l $USER_DATA/dns/*.conf | cut -f 1 -d ' ') +update_user_value "$user" '$U_DNS_RECORDS' "$records" + +# Updating zone +if [[ "$DNS_SYSTEM" =~ named|bind ]]; then + update_domain_serial + update_domain_zone +fi + +# Updating dns-cluster queue +if [ ! -z "$DNS_CLUSTER" ]; then + # Check for first sync + dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe) + if [ -z "$dlock" ]; then + cmd="$BIN/v-add-remote-dns-domain $user $domain domain yes" + echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe + fi +fi + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Restarting named +$BIN/v-restart-dns $restart +check_result $? "DNS restart failed" >/dev/null + +# Logging +log_history "changed dns ipv6 for $domain to $ipv6" +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-change-firewall-ipv6-rule b/bin/v-change-firewall-ipv6-rule new file mode 100644 index 0000000000..15f96a62c0 --- /dev/null +++ b/bin/v-change-firewall-ipv6-rule @@ -0,0 +1,85 @@ +#!/bin/bash +# info: change firewall rule +# options: RULE ACTION IPV6 PORT [PROTOCOL] [COMMENT] +# +# The function is used for changing existing firewall rule. +# It fully replace rule with new one but keeps same id. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +rule=$1 +action=$(echo $2|tr '[:lower:]' '[:upper:]') +ipv6=$3 +port_ext=$4 +protocol=${5-TCP} +protocol=$(echo $protocol|tr '[:lower:]' '[:upper:]') +comment=$6 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + +# Sort function +sort_fw_rules() { + cat $HESTIA/data/firewallv6/rules.conf |\ + sort -n -k 2 -t \' > $HESTIA/data/firewallv6/rules.conf.tmp + mv -f $HESTIA/data/firewallv6/rules.conf.tmp \ + $HESTIA/data/firewallv6/rules.conf +} + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '5' "$#" 'RULE ACTION IPV6 PORT [PROTOCOL] [COMMENT]' +is_format_valid 'rule' 'action' 'protocol' 'port_ext' 'ipv6' +if [ ! -z "$comment" ]; then + is_format_valid 'comment' +fi +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Generating timestamp +time_n_date=$(date +'%T %F') +time=$(echo "$time_n_date" |cut -f 1 -d \ ) +date=$(echo "$time_n_date" |cut -f 2 -d \ ) + +# Concatenating firewall rule +str="RULE='$rule' ACTION='$action' PROTOCOL='$protocol' PORT='$port_ext'" +str="$str IP6='$ipv6' COMMENT='$comment' SUSPENDED='no'" +str="$str TIME='$time' DATE='$date'" + +# Deleting old rule +sed -i "/RULE='$rule' /d" $HESTIA/data/firewallv6/rules.conf + +# Adding new +echo "$str" >> $HESTIA/data/firewallv6/rules.conf + +# Sorting firewall rules by id number +sort_fw_rules + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-change-sys-ipv6-name b/bin/v-change-sys-ipv6-name new file mode 100644 index 0000000000..341a73761e --- /dev/null +++ b/bin/v-change-sys-ipv6-name @@ -0,0 +1,48 @@ +#!/bin/bash +# info: change ip name +# options: IPV6 NAME +# +# The function for changing the name of the ip. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +ipv6=$1 +ip_name=$2 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/func/ipv6.sh +source $HESTIA/conf/hestia.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '2' "$#" 'IPV6 IP_NAME' +is_format_valid 'ipv6' +is_format_valid 'ip_name' +is_ipv6_valid "$ipv6" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Changing ip name +update_ipv6_value '$NAME' "$ip_name" + + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +# Logging +log_history "changed associated name of $ipv6 to $ip_name" '' 'admin' +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-change-sys-ipv6-owner b/bin/v-change-sys-ipv6-owner new file mode 100644 index 0000000000..341a73761e --- /dev/null +++ b/bin/v-change-sys-ipv6-owner @@ -0,0 +1,48 @@ +#!/bin/bash +# info: change ip name +# options: IPV6 NAME +# +# The function for changing the name of the ip. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +ipv6=$1 +ip_name=$2 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/func/ipv6.sh +source $HESTIA/conf/hestia.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '2' "$#" 'IPV6 IP_NAME' +is_format_valid 'ipv6' +is_format_valid 'ip_name' +is_ipv6_valid "$ipv6" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Changing ip name +update_ipv6_value '$NAME' "$ip_name" + + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +# Logging +log_history "changed associated name of $ipv6 to $ip_name" '' 'admin' +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-change-sys-ipv6-status b/bin/v-change-sys-ipv6-status new file mode 100644 index 0000000000..d957ecceaf --- /dev/null +++ b/bin/v-change-sys-ipv6-status @@ -0,0 +1,56 @@ +#!/bin/bash +# info: change ip status +# options: IPV6 IP_STATUS +# +# The function of changing an ip address's status. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +ipv6=$1 +ip_status=$2 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/func/ipv6.sh +source $HESTIA/conf/hestia.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '2' "$#" 'IPV6 IP_STATUS' +is_format_valid 'ipv6' 'ip_status' +is_ipv6_valid "$ipv6" +if [ "$ip_status" = "$(get_ipv6_value '$STATUS')" ]; then + check_result "$E_EXISTS" "status $ip_status is already set" +fi +web_domains=$(get_ipv6_value '$U_WEB_DOMAINS') +sys_user=$(get_ipv6_value '$U_SYS_USERS') +ip_owner=$(get_ipv6_value '$OWNER') +if [ "$web_domains" -ne '0' ] && [ "$sys_user" != "$ip_owner" ]; then + check_result "$E_INUSE" "ip $ipv6 is used" +fi + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Changing ip name +update_ipv6_value '$STATUS' "$ip_status" + + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +# Logging +log_history "changed $ipv6 status to $ip_status" '' 'admin' +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-change-web-domain-ipv6 b/bin/v-change-web-domain-ipv6 new file mode 100644 index 0000000000..13e8092c15 --- /dev/null +++ b/bin/v-change-web-domain-ipv6 @@ -0,0 +1,120 @@ +#!/bin/bash +# info: change dns domain ip address +# options: USER DOMAIN IPV6 +# +# The function for changing the main ipv6 of DNS zone. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +user=$1 +domain=$2 +domain_idn=$2 +ipv6=$3 +restart=$4 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/func/ipv6.sh +source $HESTIA/func/domain.sh +source $HESTIA/conf/hestia.conf + +# Additional argument formatting +format_domain +format_domain_idn + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '3' "$#" 'USER DOMAIN IPV6' +is_format_valid 'user' 'domain' +is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' +is_object_valid 'user' 'USER' "$user" +is_object_unsuspended 'user' 'USER' "$user" +is_object_valid 'dns' 'DOMAIN' "$domain" +is_object_unsuspended 'dns' 'DOMAIN' "$domain" + +if [ "$ipv6" != "no" ]; then + is_format_valid 'ipv6' +fi +if [ "$ipv6" != "no" ]; then + is_ipv6_valid "$ipv6" "$user" +else + ipv6='' +fi + +# Get old ip +get_domain_values 'dns' +if [ -z @"$ipv6" ] && [ -z "$IP" ]; then + check_result $E_INVALID "IP or IPv6 is required" +fi + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +old=$IP6 + +if [ -z "$old" ]; then + #Create new + # Generating timestamp + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" |cut -f 1 -d \ ) + date=$(echo "$time_n_date" |cut -f 2 -d \ ) + ip="" + add_dns_config_records +else + if [ ! -z "$ipv6" ]; then + # Changing records + sed -i "s/$old/$ipv6/g" $USER_DATA/dns/$domain.conf + else + #Delete configs + ipv6="" + ip=$IP + remove_dns_config_records + fi +fi + +# Changing ip +update_object_value 'dns' 'DOMAIN' "$domain" '$IP6' "$ipv6" + +#update counters +records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f1 -d ' ')" +update_object_value 'dns' 'DOMAIN' "$domain" '$RECORDS' "$records" +records=$(wc -l $USER_DATA/dns/*.conf | cut -f 1 -d ' ') +update_user_value "$user" '$U_DNS_RECORDS' "$records" + +# Updating zone +if [[ "$DNS_SYSTEM" =~ named|bind ]]; then + update_domain_serial + update_domain_zone +fi + +# Updating dns-cluster queue +if [ ! -z "$DNS_CLUSTER" ]; then + # Check for first sync + dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe) + if [ -z "$dlock" ]; then + cmd="$BIN/v-add-remote-dns-domain $user $domain domain yes" + echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe + fi +fi + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Restarting named +$BIN/v-restart-dns $restart +check_result $? "DNS restart failed" >/dev/null + +# Logging +log_history "changed dns ipv6 for $domain to $ipv6" +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-delete-firewall-ipv6-rule b/bin/v-delete-firewall-ipv6-rule new file mode 100644 index 0000000000..f67df1e27c --- /dev/null +++ b/bin/v-delete-firewall-ipv6-rule @@ -0,0 +1,51 @@ +#!/bin/bash +# info: delete firewall rule +# options: RULE +# +# The function deletes firewall rule. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +rule=$1 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE' +is_format_valid 'rule' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Deleting rule +sed -i "/RULE='$rule' /d" $HESTIA/data/firewallv6/rules.conf + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-delete-sys-ipv6 b/bin/v-delete-sys-ipv6 new file mode 100644 index 0000000000..972fad0980 --- /dev/null +++ b/bin/v-delete-sys-ipv6 @@ -0,0 +1,149 @@ +#!/bin/bash +# info: delete system ipv6 +# options: IPV6 +# +# The function for deleting a system ip. It does not allow to delete first ip +# on interface and do not allow to delete ip which is used by a web domain. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +ipv6=$1 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/func/ipv6.sh +source $HESTIA/func/domain.sh +source $HESTIA/conf/hestia.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'IPV6' +is_format_valid 'ipv6' +is_ip_valid "$ipv6" +is_ip_key_empty '$U_WEB_DOMAINS' +is_ip_key_empty '$U_SYS_USERS' + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Import ip variables +source $HESTIA/data/ips/$ipv6 +cidr=$(convert_netmaskv6 $NETMASK) + +# Checking main ip on the interface +interface=$(/sbin/ip -6 addr | grep "$ipv6/$cidr" | awk '{print $NF}') +if [ ! -z "$interface" ] && [ -z "$(echo $interface |cut -s -f2 -d :)" ]; then + echo "Error: can't delete main IP address" + log_event "$E_FORBIDEN" "$ARGUMENTS" + exit $E_FORBIDEN +fi + +# Deleting system ip +if [ ! -z "$interface" ]; then + /sbin/ip -6 addr del $ip/$cidr dev $INTERFACE + if [ "$?" -ne 0 ]; then + echo "Error: can't delete system ip" + log_event "$E_FORBIDEN" "$ARGUMENTS" + exit $E_FORBIDEN + fi +fi + +# Deleting startup conf on RHEL/CentOS/Fedora +if [ -e "/etc/sysconfig/network-scripts/ifcfg-$interface" ]; then + rm -f /etc/sysconfig/network-scripts/ifcfg-$interface +fi + +# Deleting startup conf on Debian/Ubuntu +if [ -e "/etc/network/interfaces" ]; then + ip_str=$(grep -n $ip$ /etc/network/interfaces |cut -f1 -d:) + if [ ! -z "$ip_str" ]; then + first_str=$((ip_str - 3)) + last_str=$((ip_str + 1)) + sed -i "$first_str,$last_str d" /etc/network/interfaces + fi +fi + +# Deleting hestia ip +rm -f $HESTIA/data/ips/$ipv6 + +# Deleting web config +if [ ! -z "$WEB_SYSTEM" ]; then + rm -f /etc/$WEB_SYSTEM/conf.d/$ipv6.conf +fi + +# Deleting proxy config +if [ ! -z "$PROXY_SYSTEM" ]; then + rm -f /etc/$PROXY_SYSTEM/conf.d/$ipv6.conf + + # mod_extract_forwarded + fw_conf="/etc/$WEB_SYSTEM/conf.d/mod_extract_forwarded.conf" + if [ -e "$fw_conf" ]; then + ips=$(grep 'MEFaccept 127.0.0.1' $fw_conf) + new_ips=$(echo "$ips" | sed "s/$ipv6//" ) + sed -i "s/$ips/$new_ips/g" $fw_conf + fi + + # mod_rpaf + rpaf_conf="/etc/$WEB_SYSTEM/mods-enabled/rpaf.conf" + if [ -e "$rpaf_conf" ]; then + ips=$(grep RPAFproxy_ips $rpaf_conf) + new_ips=$(echo "$rpaf_str" | sed "s/$ipv6//") + sed -i "s/$ips/$new_ips/g" $rpaf_conf + fi + + #mod_remoteip + remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf" + if [ -e "$remoteip_conf" ]; then + sed -i "s/RemoteIPInternalProxy $ipv6//g" $remoteip_conf + fi +fi + + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +# Updating user conf +if [ ! -z "$OWNER" ]; then + decrease_user_value "$OWNER" '$IPV6_OWNED' +fi + +if [ "$OWNER" = 'admin' ]; then + if [ "$STATUS" = 'shared' ]; then + for user in $(ls $HESTIA/data/users/); do + decrease_user_value "$user" '$IPV6_AVAIL' + done + fi +else + decrease_user_value "$OWNER" '$IPV6_AVAIL' +fi + +# Restarting web server +$BIN/v-restart-web +check_result $? "Web restart failed" >/dev/null + +# Restarting proxy server +if [ ! -z "$PROXY_SYSTEM" ]; then + $BIN/v-restart-proxy + check_result $? "Proxy restart failed" >/dev/null +fi + +# Restarting firewall +if [ ! -z "$FIREWALL_SYSTEM" ]; then + $BIN/v-update-firewall +fi + +# Logging +log_history "deleted system ip address $ip" +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-list-firewall-ipv6 b/bin/v-list-firewall-ipv6 new file mode 100644 index 0000000000..f67df1e27c --- /dev/null +++ b/bin/v-list-firewall-ipv6 @@ -0,0 +1,51 @@ +#!/bin/bash +# info: delete firewall rule +# options: RULE +# +# The function deletes firewall rule. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +rule=$1 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE' +is_format_valid 'rule' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Deleting rule +sed -i "/RULE='$rule' /d" $HESTIA/data/firewallv6/rules.conf + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-list-firewall-ipv6-rule b/bin/v-list-firewall-ipv6-rule new file mode 100644 index 0000000000..2bb1ca00c9 --- /dev/null +++ b/bin/v-list-firewall-ipv6-rule @@ -0,0 +1,88 @@ +#!/bin/bash +# info: list firewall rule +# options: RULE [FORMAT] +# +# The function of obtaining firewall rule parameters. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +rule=$1 +format=${2-shell} + +# Includes +source $HESTIA/func/main.sh + +json_list() { + echo '{' + echo ' "'$RULE'": { + "ACTION": "'$ACTION'", + "PROTOCOL": "'$PROTOCOL'", + "PORT": "'$PORT'", + "IP6": "'$IP6'", + "COMMENT": "'$COMMENT'", + "SUSPENDED": "'$SUSPENDED'", + "TIME": "'$TIME'", + "DATE": "'$DATE'" + }' + echo '}' +} + +# SHELL list function +shell_list() { + echo "ACTION: $ACTION" + echo "PROTOCOL: $PROTOCOL" + echo "PORT: $PORT" + echo "IP6: $IP6" + echo "COMMENT: $COMMENT" + echo "SUSPENDED: $SUSPENDED" + echo "TIME: $TIME" + echo "DATE: $DATE" +} + +# PLAIN list function +plain_list() { + echo -ne "$RULE\t$ACTION\t$PROTOCOL\t$PORT\t$IP6\t$COMMENT\t" + echo -e "$SUSPENDED\t$TIME\t$DATE" +} + +# CSV list function +csv_list() { + echo "RULE,ACTION,PROTOCOL,PORT,IP6,COMMENT,SUSPENDED,TIME,DATE" + echo "$RULE,$ACTION,$PROTOCOL,$PORT,$IP6,$COMMENT,$SUSPENDED,$TIME,$DATE" +} + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE [FORMAT]' +is_number_format_valid "$rule" "rule id" +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Parsing rules +eval $(grep "RULE='$rule'" $HESTIA/data/firewallv6/rules.conf) + +# Listing data +case $format in + json) json_list ;; + plain) plain_list ;; + csv) csv_list ;; + shell) shell_list ;; +esac + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +exit \ No newline at end of file diff --git a/bin/v-suspend-firewall-ipv6-rule b/bin/v-suspend-firewall-ipv6-rule new file mode 100644 index 0000000000..102407f103 --- /dev/null +++ b/bin/v-suspend-firewall-ipv6-rule @@ -0,0 +1,49 @@ +#!/bin/bash +# info: suspend firewall rule +# options: RULE +# +# The function suspends a certain firewall rule. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +rule=$1 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE' +is_format_valid 'rule' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" +is_object_unsuspended '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Suspending rule +update_object_value ../../data/firewallv6/rules RULE $rule '$SUSPENDED' yes + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-update-firewall b/bin/v-update-firewall index 135e3d6932..62574c6294 100755 --- a/bin/v-update-firewall +++ b/bin/v-update-firewall @@ -11,6 +11,7 @@ # Defining absolute path for iptables and modprobe iptables="/sbin/iptables" +ip6tables="/sbin/ip6tables" modprobe="/sbin/modprobe" sysctl="/sbin/sysctl" diff --git a/func/ipv6.sh b/func/ipv6.sh new file mode 100644 index 0000000000..8d4031b449 --- /dev/null +++ b/func/ipv6.sh @@ -0,0 +1,224 @@ +# Check ipv6 ownership +is_ipv6_owner() { + owner=$(grep 'OWNER=' $HESTIA/data/ips/$ipv6 |cut -f 2 -d \') + if [ "$owner" != "$user" ]; then + check_result $E_FORBIDEN "$ipv6 is not owned by $user" + fi +} + +# Check if ipv6 address is free +is_ipv6_free() { + if [ -e "$HESTIA/data/ips/$ipv6" ]; then + check_result $E_EXISTS "$ipv6 is already exists" + fi +} + +# Get full interface name +get_ipv6_iface() { + i=$(/sbin/ip addr |grep -w $interface |\ + awk '{print $NF}' |tail -n 1 |cut -f 2 -d :) + if [ "$i" = "$interface" ]; then + n=0 + else + n=$((i + 1)) + fi + echo "$interface:$n" +} + + +# Check ipv6 address speciefic value +is_ipv6_key_empty() { + key="$1" + string=$(cat $HESTIA/data/ips/$ipv6) + eval $string + eval value="$key" + if [ ! -z "$value" ] && [ "$value" != '0' ]; then + key="$(echo $key|sed -e "s/\$U_//")" + check_result $E_EXISTS "IP6 is in use / $key = $value" + fi +} + +# Update ipv6 address value +update_ipv6_value() { + key="$1" + value="$2" + conf="$HESTIA/data/ips/$ipv6" + str=$(cat $conf) + eval $str + c_key=$(echo "${key//$/}") + eval old="${key}" + old=$(echo "$old" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + new=$(echo "$value" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + sed -i "$str_number s/$c_key='${old//\*/\\*}'/$c_key='${new//\*/\\*}'/g"\ + $conf +} + +# Get ipv6 name +get_ipv6_alias() { + ip_name=$(grep "NAME=" $HESTIA/data/ips/$ipv6 2> /dev/null |cut -f 2 -d \') + if [ ! -z "$ip_name" ]; then + echo "${1//./-}.$ip_name" + fi +} + +# Increase ipv6 value +increase_ipv6_value() { + sip=${1-ipv6} + if [ "$sip" != "no" ] && [ ! -z "$sip" ]; then + USER=$user + web_key='U_WEB_DOMAINS' + usr_key='U_SYS_USERS' + current_web=$(grep "$web_key=" $HESTIA/data/ips/$sip |cut -f 2 -d \') + current_usr=$(grep "$usr_key=" $HESTIA/data/ips/$sip |cut -f 2 -d \') + if [ -z "$current_web" ]; then + echo "Error: Parsing error" + log_event "$E_PARSING" "$ARGUMENTS" + exit $E_PARSING + fi + new_web=$((current_web + 1)) + if [ -z "$current_usr" ]; then + new_usr="$USER" + else + check_usr=$(echo -e "${current_usr//,/\n}" |grep -w $USER) + if [ -z "$check_usr" ]; then + new_usr="$current_usr,$USER" + else + new_usr="$current_usr" + fi + fi + + sed -i "s/$web_key='$current_web'/$web_key='$new_web'/g" \ + $HESTIA/data/ips/$sip + sed -i "s/$usr_key='$current_usr'/$usr_key='$new_usr'/g" \ + $HESTIA/data/ips/$sip + fi +} + +# Decrease ipv6 value +decrease_ipv6_value() { + sip=${1-ipv6} + if [ "$sip" != "no" ] && [ ! -z "$sip" ]; then + USER=$user + web_key='U_WEB_DOMAINS' + usr_key='U_SYS_USERS' + + current_web=$(grep "$web_key=" $HESTIA/data/ips/$sip |cut -f 2 -d \') + current_usr=$(grep "$usr_key=" $HESTIA/data/ips/$sip |cut -f 2 -d \') + + if [ -z "$current_web" ]; then + check_result $E_PARSING "Parsing error" + fi + + new_web=$((current_web - 1)) + check_ip=$(grep $sip $USER_DATA/web.conf |wc -l) + if [ "$check_ip" -lt 2 ]; then + new_usr=$(echo "$current_usr" |\ + sed "s/,/\n/g"|\ + sed "s/^$user$//g"|\ + sed "/^$/d"|\ + sed ':a;N;$!ba;s/\n/,/g') + else + new_usr="$current_usr" + fi + + sed -i "s/$web_key='$current_web'/$web_key='$new_web'/g" \ + $HESTIA/data/ips/$sip + sed -i "s/$usr_key='$current_usr'/$usr_key='$new_usr'/g" \ + $HESTIA/data/ips/$sip + fi +} + +# Get ipv6 address value +get_ipv6_value() { + key="$1" + string=$(cat $HESTIA/data/ips/$ip) + eval $string + eval value="$key" + echo "$value" +} + + +# Get real ipv6 address +get_real_ipv6() { + if [ -e "$HESTIA/data/ips/$1" ]; then + echo $1 + else + nat=$(grep -H "^NAT='$1'" $HESTIA/data/ips/*) + if [ ! -z "$nat" ]; then + echo "$nat" |cut -f 1 -d : |cut -f 7 -d / + fi + fi +} + +# Convert CIDR to netmask +convert_cidrv6() { + set -- $(( 5 - ($1 / 8) )) 255 255 255 255 \ + $(((255 << (8 - ($1 % 8))) & 255 )) 0 0 0 + if [[ $1 -gt 1 ]]; then + shift $1 + else + shift + fi + echo ${1-0}.${2-0}.${3-0}.${4-0} +} + +# Convert netmask to CIDR +convert_netmaskv6() { + nbits=0 + IFS=. + for dec in $1 ; do + case $dec in + 255) let nbits+=8;; + 254) let nbits+=7;; + 252) let nbits+=6;; + 248) let nbits+=5;; + 240) let nbits+=4;; + 224) let nbits+=3;; + 192) let nbits+=2;; + 128) let nbits+=1;; + 0);; + esac + done + echo "$nbits" +} + +# Get user ips +get_user_ip6s() { + dedicated=$(grep -H -A10 "OWNER='$user'" $HESTIA/data/ips/* |grep "VERSION='6'") + dedicated=$(echo "$dedicated" |cut -f 1 -d '-' |sed 's=.*/==') + shared=$(grep -H -A10 "OWNER='admin'" $HESTIA/data/ips/* |grep -A10 shared |grep "VERSION='6'") + shared=$(echo "$shared" |cut -f 1 -d '-' |sed 's=.*/==' |cut -f 1 -d \-) + for dedicated_ip in $dedicated; do + shared=$(echo "$shared" |grep -v $dedicated_ip) + done + echo -e "$dedicated\n$shared" |sed "/^$/d" +} + +# Get user ipv6 +get_user_ipv6() { + ipv6=$(get_user_ip6s |head -n1) + if [ -z "$ipv6" ]; then + ipv6="no" + #check_result $E_NOTEXIST "no IP6 is available" + fi +} + +# Validate ipv6 address +is_ipv6_valid() { + ipv6="$1" + if [ ! -e "$HESTIA/data/ips/$1" ]; then + check_result $E_NOTEXIST "IP6 $1 doesn't exist" + fi + if [ ! -z $2 ]; then + ip_data=$(cat $HESTIA/data/ips/$1) + ip_owner=$(echo "$ip_data" |grep OWNER= |cut -f2 -d \') + ip_status=$(echo "$ip_data" |grep STATUS= |cut -f2 -d \') + if [ "$ip_owner" != "$user" ] && [ "$ip_status" = 'dedicated' ]; then + check_result $E_FORBIDEN "$user user can't use IP6 $1" + fi + get_user_owner + if [ "$ip_owner" != "$user" ] && [ "$ip_owner" != "$owner" ]; then + check_result $E_FORBIDEN "$user user can't use IP6 $1" + fi + fi +} \ No newline at end of file diff --git a/install/upgrade/manual/add_ipv6.sh b/install/upgrade/manual/add_ipv6.sh new file mode 100644 index 0000000000..dd645d6706 --- /dev/null +++ b/install/upgrade/manual/add_ipv6.sh @@ -0,0 +1,69 @@ +#!/bin/bash +source /etc/profile.d/hestia.sh +source /usr/local/hestia/func/main.sh + +#Download firewallv6 templates +if [ ! -e "$HESTIA/data/firewallv6" ]; then + mkdir -p $HESTIA/data/firewallv6 + chmod 770 $HESTIA/data/firewallv6 + + cp $HESTIA/install/rhel/6/firewallv6/* \ + $HESTIA/data/firewallv6/ + chmod 660 $HESTIA/data/firewallv6/* + +fi + +#download new templates +if [ -z $0 ]; then + $BIN/v-update-web-templates + $BIN/v-update-dns-templates +fi +#testing +#rm -rf /usr/local/hestia/data/templates/* +# cp -rf /usr/local/hestia/install/rhel/7/templates/* /usr/local/hestia/data/templates/es/ + +#set IPv4 version +iplist=$(ls --sort=time $HESTIA/data/ips/) +for ip in $iplist; do + echo "VERSION='4'" >> $HESTIA/data/ips/$ip +done + +#Add IP6 field +ipv6=$(ip addr show | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | grep -ve "^fe80" | tail -1) +ipv6use="" +if [ ! -z "$ipv6" ] && [ "::1" != "$ipv6" ]; then + netmask="ip addr show | grep '$ipv6' | awk -F '/' '{print \$2}' | awk '{print \$1}'" + netmask=$(eval $netmask) + $HESTIA/bin/v-add-sys-ipv6 $ipv6 $netmask + $BIN/v-update-firewall-ipv6 + ipv6use=$ipv6 +fi + +#set IPv6 +userlist=$(ls --sort=time $HESTIA/data/users/) +for user in $userlist; do + USER_DATA="$HESTIA/data/users/$user" + + #UPDATE WEB + conf="$USER_DATA/web.conf" + while read line ; do + eval $line + update_object_value 'web' 'DOMAIN' "$DOMAIN" '$IP6' "$ipv6use" + done < $conf + + #UPDATE DNS + conf="$USER_DATA/dns.conf" + while read line ; do + eval $line + if [ "$(echo $line | grep 'IP6=')" == "" ]; then + sed -i "s/DOMAIN='$DOMAIN' IP='$IP'/DOMAIN='$DOMAIN' IP='$IP' IP6='$ipv6use'/g" "$conf" + else + update_object_value 'dns' 'DOMAIN' "$DOMAIN" '$IP6' "$ipv6use" + fi + done < $conf + $BIN/v-rebuild-user $user +done + +$BIN/v-update-sys-ip-counters + +$BIN/v-add-user-notification admin "IPv6 support" "Your hestia installation supports IPv6!" \ No newline at end of file From b69cc80f574dc648617f398209aa8b9dee07a654 Mon Sep 17 00:00:00 2001 From: Raphael Schneeberger Date: Sat, 31 Oct 2020 11:12:00 +0100 Subject: [PATCH 003/223] Revert "Adjust first steps to gain ipv6 support for firewall." This reverts commit 218c7b14adcead2e886654fcbc45667063f71256. --- bin/v-add-firewall-ban | 5 ++--- bin/v-add-firewall-chain | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/bin/v-add-firewall-ban b/bin/v-add-firewall-ban index 80ac86fdc4..4bbc0a5df9 100755 --- a/bin/v-add-firewall-ban +++ b/bin/v-add-firewall-ban @@ -18,7 +18,6 @@ chain=$(echo $2|tr '[:lower:]' '[:upper:]') # Defining absolute path for iptables and modprobe iptables="/sbin/iptables" -ip6tables="/sbin/ip6tables" # Includes source $HESTIA/func/main.sh @@ -33,16 +32,16 @@ check_args '2' "$#" 'IP CHAIN' is_format_valid 'ip' 'chain' is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' + # Perform verification if read-only mode is enabled check_hestia_demo_mode - #----------------------------------------------------------# # Action # #----------------------------------------------------------# # Checking server ip -if [ -e "$HESTIA/data/ips/$ip" ] || [ "$ip" = '127.0.0.1' ] || [ "$ip" = '::1' ]; then +if [ -e "$HESTIA/data/ips/$ip" ] || [ "$ip" = '127.0.0.1' ]; then exit fi diff --git a/bin/v-add-firewall-chain b/bin/v-add-firewall-chain index ad59d021a8..8cdf7ce021 100755 --- a/bin/v-add-firewall-chain +++ b/bin/v-add-firewall-chain @@ -1,6 +1,6 @@ #!/bin/bash # info: add firewall chain -# options: CHAIN [PORT] [PROTOCOL] +# options: CHAIN [PORT] [PROTOCOL] [PROTOCOL] # # The function adds new rule to system firewall @@ -15,11 +15,11 @@ source /etc/profile # Argument definition chain=$(echo $1 | tr '[:lower:]' '[:upper:]') port=$2 +protocol=${4-TCP} protocol=$(echo $protocol|tr '[:lower:]' '[:upper:]') # Defining absolute path to iptables iptables="/sbin/iptables" -ip6tables="/sbin/ip6tables" # Get hestia port by reading nginx.conf hestiaport=$(grep 'listen' $HESTIA/nginx/conf/nginx.conf | awk '{print $2}' | sed "s|;||") From 3706e4aaf2f0d0c5b1342ccd66055fd0e0f78c16 Mon Sep 17 00:00:00 2001 From: Skull Writter <7103685+skullwritter@users.noreply.github.com> Date: Fri, 18 Jun 2021 21:46:27 +0000 Subject: [PATCH 004/223] add function heal_ip6tables_links it's the same as iptables, the only thing that changes is the name of the functions that have a 6 after ip --- func/firewall.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/func/firewall.sh b/func/firewall.sh index 247658d6aa..9f7e958d50 100644 --- a/func/firewall.sh +++ b/func/firewall.sh @@ -16,3 +16,20 @@ heal_iptables_links() { fi done } +heal_ip6tables_links() { + packages="ip6tables ip6tables-save ip6tables-restore" + for package in $packages; do + if [ ! -e "/sbin/${package}" ]; then + if which ${package}; then + ln -s "$(which ${package})" /sbin/${package} + elif [ -e "/usr/sbin/${package}" ]; then + ln -s /usr/sbin/${package} /sbin/${package} + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ${package}; then + autoiptables=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ${package} | cut -d '' -f 2) + if [ -x "$autoiptables" ]; then + ln -s "$autoiptables" /sbin/${package} + fi + fi + fi + done +} From cd5cbf5c0c257eb5bef93acc7ba84c28be572c43 Mon Sep 17 00:00:00 2001 From: Jaap Marcus <9754650+jaapmarcus@users.noreply.github.com> Date: Sun, 10 Jul 2022 16:36:06 +0200 Subject: [PATCH 005/223] Preliminary change to allow enable IPv6 during installation (#2544) * Remove duplicate code in installer * Update Cloudflare IP range * Preliminary to allow enable IPv6 installation Co-authored-by: Clark Chen <9372896+clarkchentw@users.noreply.github.com> --- install/deb/apache2/status-ipv6.conf | 9 ++ install/deb/nginx/nginx-ipv6.conf | 162 +++++++++++++++++++++++++++ install/deb/nginx/nginx.conf | 29 ++--- install/deb/nginx/status-ipv6.conf | 10 ++ install/deb/vsftpd/vsftpd-ipv6.conf | 41 +++++++ install/hst-install-debian.sh | 38 ++++++- install/hst-install-ubuntu.sh | 81 ++++++++++++-- 7 files changed, 347 insertions(+), 23 deletions(-) create mode 100644 install/deb/apache2/status-ipv6.conf create mode 100644 install/deb/nginx/nginx-ipv6.conf create mode 100644 install/deb/nginx/status-ipv6.conf create mode 100644 install/deb/vsftpd/vsftpd-ipv6.conf diff --git a/install/deb/apache2/status-ipv6.conf b/install/deb/apache2/status-ipv6.conf new file mode 100644 index 0000000000..e9e8cc32ba --- /dev/null +++ b/install/deb/apache2/status-ipv6.conf @@ -0,0 +1,9 @@ +Listen 127.0.0.1:8081 +Listen [::1]:8081 + + SetHandler server-status + Order deny,allow + Deny from all + Allow from 127.0.0.1 + Allow from [::1] + diff --git a/install/deb/nginx/nginx-ipv6.conf b/install/deb/nginx/nginx-ipv6.conf new file mode 100644 index 0000000000..cc08f71597 --- /dev/null +++ b/install/deb/nginx/nginx-ipv6.conf @@ -0,0 +1,162 @@ +# Server globals +user www-data; +worker_processes auto; +worker_rlimit_nofile 65535; +error_log /var/log/nginx/error.log; +pid /var/run/nginx.pid; + +include /etc/nginx/modules-enabled/*.conf; + +# Worker config +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + # Main settings + sendfile on; + tcp_nopush on; + tcp_nodelay on; + client_header_timeout 180s; + client_body_timeout 180s; + client_header_buffer_size 2k; + client_body_buffer_size 256k; + client_max_body_size 256m; + large_client_header_buffers 4 8k; + send_timeout 60s; + keepalive_timeout 30s; + keepalive_requests 100000; + reset_timedout_connection on; + server_tokens off; + server_name_in_redirect off; + server_names_hash_max_size 512; + server_names_hash_bucket_size 512; + charset utf-8; + + # FastCGI settings + fastcgi_buffers 4 256k; + fastcgi_buffer_size 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + fastcgi_connect_timeout 30s; + fastcgi_read_timeout 300s; + fastcgi_send_timeout 180s; + fastcgi_cache_lock on; + fastcgi_cache_lock_timeout 5s; + fastcgi_cache_background_update on; + fastcgi_cache_revalidate on; + + # Proxy settings + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass_header Set-Cookie; + proxy_buffers 32 4k; + proxy_connect_timeout 30s; + proxy_read_timeout 300s; + proxy_send_timeout 180s; + + # Log format + log_format main '$remote_addr - $remote_user [$time_local] $request ' + '"$status" $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + log_format bytes '$body_bytes_sent'; + log_not_found off; + access_log off; + + # Mime settings + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Compression + gzip on; + gzip_static on; + gzip_vary on; + gzip_comp_level 6; + gzip_min_length 1024; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css text/javascript text/js text/xml application/json application/javascript application/x-javascript application/xml application/xml+rss application/x-font-ttf image/svg+xml font/opentype; + gzip_proxied any; + gzip_disable "MSIE [1-6]\."; + + # Cloudflare https://www.cloudflare.com/ips + set_real_ip_from 173.245.48.0/20; + set_real_ip_from 103.21.244.0/22; + set_real_ip_from 103.22.200.0/22; + set_real_ip_from 103.31.4.0/22; + set_real_ip_from 141.101.64.0/18; + set_real_ip_from 108.162.192.0/18; + set_real_ip_from 190.93.240.0/20; + set_real_ip_from 188.114.96.0/20; + set_real_ip_from 197.234.240.0/22; + set_real_ip_from 198.41.128.0/17; + set_real_ip_from 162.158.0.0/15; + set_real_ip_from 104.16.0.0/13; + set_real_ip_from 104.24.0.0/14; + set_real_ip_from 172.64.0.0/13; + set_real_ip_from 131.0.72.0/22; + set_real_ip_from 2400:cb00::/32; + set_real_ip_from 2606:4700::/32; + set_real_ip_from 2803:f800::/32; + set_real_ip_from 2405:b500::/32; + set_real_ip_from 2405:8100::/32; + set_real_ip_from 2a06:98c0::/29; + set_real_ip_from 2c0f:f248::/32; + real_ip_header CF-Connecting-IP; + + # SSL PCI compliance + ssl_session_cache shared:SSL:20m; + ssl_session_timeout 60m; + ssl_buffer_size 1400; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"; + ssl_dhparam /etc/ssl/dhparam.pem; + ssl_ecdh_curve secp384r1; + ssl_session_tickets off; + resolver 1.0.0.1 [2606:4700:4700::1111] 1.0.0.1 [2606:4700:4700::1001] valid=300s ipv6=on; + resolver_timeout 5s; + + # Error pages + error_page 403 /error/404.html; + error_page 404 /error/404.html; + error_page 410 /error/410.html; + error_page 500 501 502 503 504 505 /error/50x.html; + + # Proxy cache + proxy_cache_path /var/cache/nginx levels=2 keys_zone=cache:10m inactive=60m max_size=1024m; + proxy_cache_key "$scheme$request_method$host$request_uri"; + proxy_temp_path /var/cache/nginx/temp; + proxy_ignore_headers Expires Cache-Control; + proxy_cache_use_stale error timeout invalid_header http_502; + proxy_cache_valid any 1d; + + # FastCGI cache + fastcgi_cache_path /var/cache/nginx/micro levels=1:2 keys_zone=microcache:10m max_size=1024m inactive=30m; + fastcgi_cache_key "$scheme$request_method$host$request_uri"; + fastcgi_cache_methods GET HEAD; + fastcgi_cache_use_stale updating error timeout invalid_header http_500 http_503; + fastcgi_ignore_headers Cache-Control Expires Set-Cookie; + add_header X-FastCGI-Cache $upstream_cache_status; + + # Cache bypass + map $http_cookie $no_cache { + default 0; + ~SESS 1; + ~wordpress_logged_in 1; + } + + # File cache (static assets) + open_file_cache max=10000 inactive=30s; + open_file_cache_valid 60s; + open_file_cache_min_uses 2; + open_file_cache_errors off; + + # Wildcard include + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/conf.d/domains/*.conf; +} diff --git a/install/deb/nginx/nginx.conf b/install/deb/nginx/nginx.conf index 92d1fdf111..2a588ad7ad 100644 --- a/install/deb/nginx/nginx.conf +++ b/install/deb/nginx/nginx.conf @@ -84,27 +84,28 @@ http { gzip_disable "MSIE [1-6]\."; # Cloudflare https://www.cloudflare.com/ips + set_real_ip_from 173.245.48.0/20; set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; - set_real_ip_from 104.16.0.0/13; - set_real_ip_from 104.24.0.0/14; - set_real_ip_from 108.162.192.0/18; - set_real_ip_from 131.0.72.0/22; set_real_ip_from 141.101.64.0/18; - set_real_ip_from 162.158.0.0/15; - set_real_ip_from 172.64.0.0/13; - set_real_ip_from 173.245.48.0/20; - set_real_ip_from 188.114.96.0/20; + set_real_ip_from 108.162.192.0/18; set_real_ip_from 190.93.240.0/20; + set_real_ip_from 188.114.96.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.128.0/17; - #set_real_ip_from 2400:cb00::/32; - #set_real_ip_from 2405:b500::/32; - #set_real_ip_from 2606:4700::/32; - #set_real_ip_from 2803:f800::/32; - #set_real_ip_from 2c0f:f248::/32; - #set_real_ip_from 2a06:98c0::/29; + set_real_ip_from 162.158.0.0/15; + set_real_ip_from 104.16.0.0/13; + set_real_ip_from 104.24.0.0/14; + set_real_ip_from 172.64.0.0/13; + set_real_ip_from 131.0.72.0/22; + # set_real_ip_from 2400:cb00::/32; + # set_real_ip_from 2606:4700::/32; + # set_real_ip_from 2803:f800::/32; + # set_real_ip_from 2405:b500::/32; + # set_real_ip_from 2405:8100::/32; + # set_real_ip_from 2a06:98c0::/29; + # set_real_ip_from 2c0f:f248::/32; real_ip_header CF-Connecting-IP; # SSL PCI compliance diff --git a/install/deb/nginx/status-ipv6.conf b/install/deb/nginx/status-ipv6.conf new file mode 100644 index 0000000000..a4e26ca677 --- /dev/null +++ b/install/deb/nginx/status-ipv6.conf @@ -0,0 +1,10 @@ +server { + listen 127.0.0.1:8084 default; + listen [::1]:8084 default; + server_name _; + server_name_in_redirect off; + location / { + stub_status on; + access_log off; + } +} diff --git a/install/deb/vsftpd/vsftpd-ipv6.conf b/install/deb/vsftpd/vsftpd-ipv6.conf new file mode 100644 index 0000000000..8f665f73e6 --- /dev/null +++ b/install/deb/vsftpd/vsftpd-ipv6.conf @@ -0,0 +1,41 @@ +anonymous_enable=NO +local_enable=YES +write_enable=YES +local_umask=022 +anon_umask=022 +anon_upload_enable=NO +dirmessage_enable=YES +xferlog_enable=YES +connect_from_port_20=YES +xferlog_std_format=YES +dual_log_enable=YES +chroot_local_user=YES +listen_ipv6=YES +pam_service_name=vsftpd +ftpd_banner=Welcome! Please note that all activity is logged. +userlist_enable=NO +tcp_wrappers=YES +force_dot_files=YES +ascii_upload_enable=YES +ascii_download_enable=YES +allow_writeable_chroot=YES +seccomp_sandbox=NO +pasv_enable=YES +pasv_promiscuous=YES +pasv_min_port=12000 +pasv_max_port=12100 +max_per_ip=10 +max_clients=100 +use_localtime=YES +utf8_filesystem=YES +ssl_enable=YES +allow_anon_ssl=NO +require_ssl_reuse=NO +ssl_ciphers=ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256 +ssl_sslv2=NO +ssl_sslv3=NO +ssl_tlsv1=NO +force_local_data_ssl=NO +force_local_logins_ssl=NO +rsa_cert_file=/usr/local/hestia/ssl/certificate.crt +rsa_private_key_file=/usr/local/hestia/ssl/certificate.key diff --git a/install/hst-install-debian.sh b/install/hst-install-debian.sh index 01c51b11df..62353cc6af 100755 --- a/install/hst-install-debian.sh +++ b/install/hst-install-debian.sh @@ -82,6 +82,7 @@ help() { -r, --port Change Backend Port default: 8083 -l, --lang Default language default: en -y, --interactive Interactive install [yes|no] default: yes + -6, --ipv6 Enable IPv6 Support [yes|no] default: no -s, --hostname Set hostname -e, --email Set admin email -p, --password Set admin password @@ -221,6 +222,7 @@ for arg; do --port) args="${args}-r " ;; --lang) args="${args}-l " ;; --interactive) args="${args}-y " ;; + --ipv6) args="${args}-6 " ;; --api) args="${args}-d " ;; --hostname) args="${args}-s " ;; --email) args="${args}-e " ;; @@ -257,6 +259,7 @@ while getopts "a:w:v:j:k:m:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:e:p:D:fh" Option; do l) lang=$OPTARG ;; # Language d) api=$OPTARG ;; # Activate API y) interactive=$OPTARG ;; # Interactive install + 6) ipv6=$OPTARG ;; # IPv6 s) servername=$OPTARG ;; # Hostname e) email=$OPTARG ;; # Admin email p) vpass=$OPTARG ;; # Admin password @@ -294,6 +297,7 @@ set_default_value 'iptables' 'yes' set_default_value 'fail2ban' 'yes' set_default_value 'quota' 'no' set_default_value 'interactive' 'yes' +set_default_value 'ipv6' 'no' set_default_value 'api' 'yes' set_default_port '8083' set_default_lang 'en' @@ -640,10 +644,16 @@ if ! [[ "$servername" =~ ^${mask1}${mask2}$ ]]; then servername="example.com" fi echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi if [[ -z $(grep -i "$servername" /etc/hosts) ]]; then echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi # Set email if it wasn't set @@ -1291,6 +1301,10 @@ cp -f $HESTIA_INSTALL_DIR/nginx/agents.conf /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/logrotate/nginx /etc/logrotate.d/ +if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/nginx/nginx-ipv6.conf /etc/nginx/nginx.conf + cp -f $HESTIA_INSTALL_DIR/nginx/status-ipv6.conf /etc/nginx/conf.d/status.conf +fi mkdir -p /etc/nginx/conf.d/domains mkdir -p /etc/nginx/modules-enabled mkdir -p /var/log/nginx/domains @@ -1301,10 +1315,20 @@ for ip in $dns_resolver; do if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then resolver="$ip $resolver" fi + if [ "$ipv6" = 'yes' ]; then + if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + resolver="[$ip] $resolver" + fi + fi done if [ -n "$resolver" ]; then - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + if [ "$ipv6" = 'yes' ]; then + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + else + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + fi fi update-rc.d nginx defaults > /dev/null 2>&1 @@ -1328,6 +1352,10 @@ if [ "$apache" = 'yes' ]; then cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf + fi + # Enable needed modules a2enmod rewrite > /dev/null 2>&1 a2enmod suexec > /dev/null 2>&1 @@ -1422,6 +1450,9 @@ chmod 755 /etc/cron.daily/php-session-cleanup if [ "$vsftpd" = 'yes' ]; then echo "[ * ] Configuring Vsftpd server..." cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf + fi touch /var/log/vsftpd.log chown root:adm /var/log/vsftpd.log chmod 640 /var/log/vsftpd.log @@ -1893,7 +1924,9 @@ $HESTIA/bin/v-update-sys-ip > /dev/null 2>&1 # Get main IP ip=$(ip addr|grep 'inet '|grep global|head -n1|awk '{print $2}'|cut -f1 -d/) +ipv6=$(ip addr|grep 'inet6 '|grep global|head -n1|awk '{print $2}'|cut -f1 -d/) local_ip=$ip +local_ipv6=$ipv6 # Configuring firewall if [ "$iptables" = 'yes' ]; then @@ -1902,6 +1935,7 @@ fi # Get public IP pub_ip=$(curl --ipv4 -s https://ip.hestiacp.com/) +pub_ipv6=$(curl --ipv6 -s https://ip.hestiacp.com/) if [ -n "$pub_ip" ] && [ "$pub_ip" != "$ip" ]; then $HESTIA/bin/v-change-sys-ip-nat $ip $pub_ip > /dev/null 2>&1 diff --git a/install/hst-install-ubuntu.sh b/install/hst-install-ubuntu.sh index 9d5b1576b9..b99b765165 100755 --- a/install/hst-install-ubuntu.sh +++ b/install/hst-install-ubuntu.sh @@ -80,6 +80,7 @@ help() { -r, --port Change Backend Port default: 8083 -l, --lang Default language default: en -y, --interactive Interactive install [yes|no] default: yes + -6, --ipv6 Enable IPv6 Support [yes|no] default: no -s, --hostname Set hostname -e, --email Set admin email -p, --password Set admin password @@ -218,6 +219,7 @@ for arg; do --port) args="${args}-r " ;; --lang) args="${args}-l " ;; --interactive) args="${args}-y " ;; + --ipv6) args="${args}-6 " ;; --api) args="${args}-d " ;; --hostname) args="${args}-s " ;; --email) args="${args}-e " ;; @@ -254,6 +256,7 @@ while getopts "a:w:v:j:k:m:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:e:p:D:fh" Option; do l) lang=$OPTARG ;; # Language d) api=$OPTARG ;; # Activate API y) interactive=$OPTARG ;; # Interactive install + 6) ipv6=$OPTARG ;; # IPv6 s) servername=$OPTARG ;; # Hostname e) email=$OPTARG ;; # Admin email p) vpass=$OPTARG ;; # Admin password @@ -291,6 +294,7 @@ set_default_value 'iptables' 'yes' set_default_value 'fail2ban' 'yes' set_default_value 'quota' 'no' set_default_value 'interactive' 'yes' +set_default_value 'ipv6' 'no' set_default_value 'api' 'yes' set_default_port '8083' set_default_lang 'en' @@ -629,10 +633,16 @@ if ! [[ "$servername" =~ ^${mask1}${mask2}$ ]]; then servername="example.com" fi echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi if [[ -z $(grep -i "$servername" /etc/hosts) ]]; then echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi # Set email if it wasn't set @@ -1052,6 +1062,19 @@ if [ ! -e "/sbin/iptables" ]; then ln -s "$autoiptables" /sbin/iptables fi fi + + if [ "$ipv6" = 'yes' ]; then + if which ip6tables; then + ln -s "$(which ip6tables)" /sbin/ip6tables + elif [ -e "/usr/sbin/ip6tables" ]; then + ln -s /usr/sbin/ip6tables /sbin/ip6tables + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables; then + autoip6tables=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables | cut -d '' -f 2) + if [ -x "$autoip6tables" ]; then + ln -s "$autoip6tables" /sbin/ip6tables + fi + fi + fi fi if [ ! -e "/sbin/iptables-save" ]; then @@ -1065,6 +1088,18 @@ if [ ! -e "/sbin/iptables-save" ]; then ln -s "$autoiptables_save" /sbin/iptables-save fi fi + if [ "$ipv6" = 'yes' ]; then + if which ip6tables-save; then + ln -s "$(which ip6tables-save)" /sbin/ip6tables-save + elif [ -e "/usr/sbin/ip6tables-save" ]; then + ln -s /usr/sbin/ip6tables-save /sbin/ip6tables-save + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-save; then + autoip6tables_save=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-save | cut -d '' -f 2) + if [ -x "$autoip6tables_save" ]; then + ln -s "$autoip6tables_save" /sbin/ip6tables-save + fi + fi + fi fi if [ ! -e "/sbin/iptables-restore" ]; then @@ -1078,6 +1113,18 @@ if [ ! -e "/sbin/iptables-restore" ]; then ln -s "$autoiptables_restore" /sbin/iptables-restore fi fi + if [ "$ipv6" = 'yes' ]; then + if which ip6tables-restore; then + ln -s "$(which ip6tables-restore)" /sbin/ip6tables-restore + elif [ -e "/usr/sbin/ip6tables-restore" ]; then + ln -s /usr/sbin/ip6tables-restore /sbin/ip6tables-restore + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-restore; then + autoip6tables_restore=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-restore | cut -d '' -f 2) + if [ -x "$autoip6tables_restore" ]; then + ln -s "$autoip6tables_restore" /sbin/ip6tables-restore + fi + fi + fi fi # Restrict access to /proc fs @@ -1315,11 +1362,6 @@ chown root:mail $HESTIA/ssl/* chmod 660 $HESTIA/ssl/* rm /tmp/hst.pem -# Adding nologin as a valid system shell -if [ -z "$(grep nologin /etc/shells)" ]; then - echo "/usr/sbin/nologin" >> /etc/shells -fi - # Install dhparam.pem cp -f $HESTIA_INSTALL_DIR/ssl/dhparam.pem /etc/ssl @@ -1363,6 +1405,10 @@ cp -f $HESTIA_INSTALL_DIR/nginx/agents.conf /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/logrotate/nginx /etc/logrotate.d/ +if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/nginx/nginx-ipv6.conf /etc/nginx/nginx.conf + cp -f $HESTIA_INSTALL_DIR/nginx/status-ipv6.conf /etc/nginx/conf.d/status.conf +fi mkdir -p /etc/nginx/conf.d/domains mkdir -p /etc/nginx/modules-enabled mkdir -p /var/log/nginx/domains @@ -1373,10 +1419,20 @@ for ip in $dns_resolver; do if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then resolver="$ip $resolver" fi + if [ "$ipv6" = 'yes' ]; then + if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + resolver="[$ip] $resolver" + fi + fi done if [ -n "$resolver" ]; then - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + if [ "$ipv6" = 'yes' ]; then + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/ + else + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + fi fi update-rc.d nginx defaults > /dev/null 2>&1 @@ -1399,6 +1455,10 @@ if [ "$apache" = 'yes' ]; then cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf + fi + # Enable needed modules a2enmod rewrite > /dev/null 2>&1 a2enmod suexec > /dev/null 2>&1 @@ -1494,6 +1554,9 @@ chmod 755 /etc/cron.daily/php-session-cleanup if [ "$vsftpd" = 'yes' ]; then echo "[ * ] Configuring Vsftpd server..." cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf + fi touch /var/log/vsftpd.log chown root:adm /var/log/vsftpd.log chmod 640 /var/log/vsftpd.log @@ -1953,7 +2016,9 @@ $HESTIA/bin/v-update-sys-ip > /dev/null 2>&1 # Get main IP ip=$(ip addr|grep 'inet '|grep global|head -n1|awk '{print $2}'|cut -f1 -d/) +ipv6=$(ip addr|grep 'inet6 '|grep global|head -n1|awk '{print $2}'|cut -f1 -d/) local_ip=$ip +local_ipv6=$ipv6 # Configuring firewall if [ "$iptables" = 'yes' ]; then @@ -1963,6 +2028,8 @@ fi # Get public IP echo "[ * ] Configuring System IP..." pub_ip=$(curl --ipv4 -s https://ip.hestiacp.com/) +pub_ipv6=$(curl --ipv6 -s https://ip.hestiacp.com/) + if [ -n "$pub_ip" ] && [ "$pub_ip" != "$ip" ]; then if [ -e /etc/rc.local ]; then sed -i '/exit 0/d' /etc/rc.local From b83c60fd6a6a05c5e42e238145d54be679aeb668 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 5 Feb 2023 01:55:52 +0100 Subject: [PATCH 006/223] manually merge and resolve conflictes between old ipv6 branch and actual master --- install/deb/nginx/nginx.conf | 154 +++ install/hst-install-debian.sh | 2180 +++++++++++++++++++++++++++++++ install/hst-install-ubuntu.sh | 2285 +++++++++++++++++++++++++++++++++ 3 files changed, 4619 insertions(+) create mode 100644 install/deb/nginx/nginx.conf create mode 100755 install/hst-install-debian.sh create mode 100755 install/hst-install-ubuntu.sh diff --git a/install/deb/nginx/nginx.conf b/install/deb/nginx/nginx.conf new file mode 100644 index 0000000000..6b7ea09400 --- /dev/null +++ b/install/deb/nginx/nginx.conf @@ -0,0 +1,154 @@ +# Server globals +user www-data; +worker_processes auto; +worker_rlimit_nofile 65535; +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; + +# Worker config +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + # Main settings + sendfile on; + tcp_nopush on; + tcp_nodelay on; + client_header_timeout 180s; + client_body_timeout 180s; + client_header_buffer_size 2k; + client_body_buffer_size 256k; + client_max_body_size 256m; + large_client_header_buffers 4 8k; + send_timeout 60s; + keepalive_timeout 30s; + keepalive_requests 100000; + reset_timedout_connection on; + server_tokens off; + server_name_in_redirect off; + server_names_hash_max_size 512; + server_names_hash_bucket_size 512; + charset utf-8; + # FastCGI settings + fastcgi_buffers 8 256k; + fastcgi_buffer_size 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + fastcgi_connect_timeout 30s; + fastcgi_read_timeout 300s; + fastcgi_send_timeout 180s; + fastcgi_cache_lock on; + fastcgi_cache_lock_timeout 5s; + fastcgi_cache_background_update on; + fastcgi_cache_revalidate on; + # Proxy settings + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass_header Set-Cookie; + proxy_buffers 32 4k; + proxy_buffer_size 8k; + proxy_connect_timeout 30s; + proxy_read_timeout 300s; + proxy_send_timeout 180s; + # Log format + log_format main '$remote_addr - $remote_user [$time_local] $request ' + '"$status" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; + log_format bytes '$body_bytes_sent'; + log_not_found off; + access_log off; + # Mime settings + include /etc/nginx/mime.types; + default_type application/octet-stream; + # Compression + gzip on; + gzip_static on; + gzip_vary on; + gzip_comp_level 6; + gzip_min_length 1024; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css text/javascript text/js text/xml + application/json application/javascript application/x-javascript application/xml + application/xml+rss application/x-font-ttf image/svg+xml font/opentype; + gzip_proxied any; + gzip_disable "MSIE [1-6]\."; + # Cloudflare https://www.cloudflare.com/ips + set_real_ip_from 103.21.244.0/22; + set_real_ip_from 103.22.200.0/22; + set_real_ip_from 103.31.4.0/22; + set_real_ip_from 104.16.0.0/13; + set_real_ip_from 104.24.0.0/14; + set_real_ip_from 108.162.192.0/18; + set_real_ip_from 131.0.72.0/22; + set_real_ip_from 141.101.64.0/18; + set_real_ip_from 162.158.0.0/15; + set_real_ip_from 172.64.0.0/13; + set_real_ip_from 173.245.48.0/20; + set_real_ip_from 188.114.96.0/20; + set_real_ip_from 190.93.240.0/20; + set_real_ip_from 197.234.240.0/22; + set_real_ip_from 198.41.128.0/17; + # set_real_ip_from 2400:cb00::/32; + # set_real_ip_from 2405:8100::/32; + # set_real_ip_from 2405:b500::/32; + # set_real_ip_from 2606:4700::/32; + # set_real_ip_from 2803:f800::/32; + # set_real_ip_from 2a06:98c0::/29; + # set_real_ip_from 2c0f:f248::/32; + real_ip_header CF-Connecting-IP; + # SSL PCI compliance + ssl_session_cache shared:SSL:20m; + ssl_session_timeout 60m; + ssl_buffer_size 1400; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"; + ssl_dhparam /etc/ssl/dhparam.pem; + ssl_ecdh_curve secp384r1; + ssl_session_tickets off; + resolver 1.0.0.1 1.1.1.1 valid=300s ipv6=off; + resolver_timeout 5s; + # Error pages + error_page 403 /error/404.html; + error_page 404 /error/404.html; + error_page 410 /error/410.html; + error_page 500 501 502 503 504 505 /error/50x.html; + # Proxy cache + proxy_cache_path /var/cache/nginx levels=2 keys_zone=cache:10m inactive=60m + max_size=1024m; + proxy_cache_key "$scheme$request_method$host$request_uri"; + proxy_temp_path /var/cache/nginx/temp; + proxy_ignore_headers Expires Cache-Control; + proxy_cache_use_stale error timeout invalid_header http_502; + proxy_cache_valid any 1d; + # FastCGI cache + fastcgi_cache_path /var/cache/nginx/micro levels=1:2 keys_zone=microcache:10m + max_size=1024m inactive=30m; + fastcgi_cache_key "$scheme$request_method$host$request_uri"; + fastcgi_cache_methods GET HEAD; + fastcgi_cache_use_stale updating error timeout invalid_header http_500 http_503; + fastcgi_ignore_headers Cache-Control Expires Set-Cookie; + add_header X-FastCGI-Cache $upstream_cache_status; + + # Cache bypass + map $http_cookie $no_cache { + default 0; + ~SESS 1; + ~wordpress_logged_in 1; + } + + # File cache (static assets) + open_file_cache max=10000 inactive=30s; + open_file_cache_valid 60s; + open_file_cache_min_uses 2; + open_file_cache_errors off; + # Wildcard include + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/conf.d/domains/*.conf; +} \ No newline at end of file diff --git a/install/hst-install-debian.sh b/install/hst-install-debian.sh new file mode 100755 index 0000000000..c4e04c0f21 --- /dev/null +++ b/install/hst-install-debian.sh @@ -0,0 +1,2180 @@ +#!/bin/bash + +# ======================================================== # +# +# Hestia Control Panel Installer for Debian +# https://www.hestiacp.com/ +# +# Currently Supported Versions: +# Debian 10, 11 +# +# ======================================================== # + +#----------------------------------------------------------# +# Variables&Functions # +#----------------------------------------------------------# +export PATH=$PATH:/sbin +export DEBIAN_FRONTEND=noninteractive +RHOST='apt.hestiacp.com' +GPG='gpg.hestiacp.com' +VERSION='debian' +HESTIA='/usr/local/hestia' +LOG="/root/hst_install_backups/hst_install-$(date +%d%m%Y%H%M).log" +memory=$(grep 'MemTotal' /proc/meminfo | tr ' ' '\n' | grep [0-9]) +hst_backups="/root/hst_install_backups/$(date +%d%m%Y%H%M)" +spinner="/-\|" +os='debian' +architecture="$(arch)" +release=$(cat /etc/debian_version | tr "." "\n" | head -n1) +codename="$(cat /etc/os-release | grep VERSION= | cut -f 2 -d \( | cut -f 1 -d \))" +HESTIA_INSTALL_DIR="$HESTIA/install/deb" +HESTIA_COMMON_DIR="$HESTIA/install/common" +VERBOSE='no' + +# Define software versions +HESTIA_INSTALL_VER='1.7.0~alpha' +# Dependencies +multiphp_v=("5.6" "7.0" "7.1" "7.2" "7.3" "7.4" "8.0" "8.1" "8.2") +fpm_v="8.0" +mariadb_v="10.6" + +software="nginx apache2 apache2-utils apache2-suexec-custom + apache2-suexec-pristine libapache2-mod-fcgid libapache2-mod-php$fpm_v + php$fpm_v php$fpm_v-common php$fpm_v-cgi php$fpm_v-mysql php$fpm_v-curl + php$fpm_v-pgsql php$fpm_v-imagick php$fpm_v-imap php$fpm_v-ldap + php$fpm_v-apcu php$fpm_v-zip php$fpm_v-bz2 php$fpm_v-cli + php$fpm_v-gd php$fpm_v-intl php$fpm_v-mbstring + php$fpm_v-opcache php$fpm_v-pspell php$fpm_v-readline php$fpm_v-xml + awstats vsftpd proftpd-basic bind9 exim4 exim4-daemon-heavy + clamav-daemon spamassassin dovecot-imapd dovecot-pop3d dovecot-sieve dovecot-managesieved + net-tools mariadb-client mariadb-common mariadb-server mysql-client mysql-common mysql-server postgresql + postgresql-contrib phppgadmin mc flex whois git idn2 unzip zip sudo bc ftp lsof + rrdtool quota e2fslibs bsdutils e2fsprogs curl imagemagick fail2ban + dnsutils bsdmainutils cron hestia=${HESTIA_INSTALL_VER} hestia-nginx + hestia-php expect libmail-dkim-perl unrar-free vim-common acl sysstat + rsyslog openssh-server util-linux ipset libapache2-mpm-itk zstd + lsb-release jq" + +installer_dependencies="apt-transport-https curl dirmngr gnupg wget ca-certificates" + +# Defining help function +help() { + echo "Usage: $0 [OPTIONS] + -a, --apache Install Apache [yes|no] default: yes + -w, --phpfpm Install PHP-FPM [yes|no] default: yes + -o, --multiphp Install Multi-PHP [yes|no] default: no + -v, --vsftpd Install Vsftpd [yes|no] default: yes + -j, --proftpd Install ProFTPD [yes|no] default: no + -k, --named Install Bind [yes|no] default: yes + -m, --mysql Install MariaDB [yes|no] default: yes + -M, --mysql-classic Install MySQL [yes|no] default: no + -g, --postgresql Install PostgreSQL [yes|no] default: no + -x, --exim Install Exim [yes|no] default: yes + -z, --dovecot Install Dovecot [yes|no] default: yes + -Z, --sieve Install Sieve [yes|no] default: no + -c, --clamav Install ClamAV [yes|no] default: yes + -t, --spamassassin Install SpamAssassin [yes|no] default: yes + -i, --iptables Install Iptables [yes|no] default: yes + -b, --fail2ban Install Fail2ban [yes|no] default: yes + -q, --quota Filesystem Quota [yes|no] default: no + -d, --api Activate API [yes|no] default: yes + -r, --port Change Backend Port default: 8083 + -l, --lang Default language default: en + -y, --interactive Interactive install [yes|no] default: yes + -6, --ipv6 Enable IPv6 Support [yes|no] default: no + -s, --hostname Set hostname + -e, --email Set admin email + -p, --password Set admin password + -D, --with-debs Path to Hestia debs + -f, --force Force installation + -h, --help Print this help + + Example: bash $0 -e demo@hestiacp.com -p p4ssw0rd --multiphp yes" + exit 1 +} + +# Defining file download function +download_file() { + wget $1 -q --show-progress --progress=bar:force +} + +# Defining password-gen function +gen_pass() { + matrix=$1 + length=$2 + if [ -z "$matrix" ]; then + matrix="A-Za-z0-9" + fi + if [ -z "$length" ]; then + length=16 + fi + head /dev/urandom | tr -dc $matrix | head -c$length +} + +# Defining return code check function +check_result() { + if [ $1 -ne 0 ]; then + echo "Error: $2" + exit $1 + fi +} + +# Defining function to set default value +set_default_value() { + eval variable=\$$1 + if [ -z "$variable" ]; then + eval $1=$2 + fi + if [ "$variable" != 'yes' ] && [ "$variable" != 'no' ]; then + eval $1=$2 + fi +} + +# Defining function to set default language value +set_default_lang() { + if [ -z "$lang" ]; then + eval lang=$1 + fi + lang_list="ar az bg bn bs ckb cs da de el en es fa fi fr hr hu id it ja ka ko nl no pl pt pt-br ro ru sk sr sv th tr uk ur vi zh-cn zh-tw" + if ! (echo $lang_list | grep -w $lang > /dev/null 2>&1); then + eval lang=$1 + fi +} + +# Define the default backend port +set_default_port() { + if [ -z "$port" ]; then + eval port=$1 + fi +} + +# Write configuration KEY/VALUE pair to $HESTIA/conf/hestia.conf +write_config_value() { + local key="$1" + local value="$2" + echo "$key='$value'" >> $HESTIA/conf/hestia.conf +} + +# Sort configuration file values +# Write final copy to $HESTIA/conf/hestia.conf for active usage +# Duplicate file to $HESTIA/conf/defaults/hestia.conf to restore known good installation values +sort_config_file() { + sort $HESTIA/conf/hestia.conf -o /tmp/updconf + mv $HESTIA/conf/hestia.conf $HESTIA/conf/hestia.conf.bak + mv /tmp/updconf $HESTIA/conf/hestia.conf + rm -f $HESTIA/conf/hestia.conf.bak + if [ ! -d "$HESTIA/conf/defaults/" ]; then + mkdir -p "$HESTIA/conf/defaults/" + fi + cp $HESTIA/conf/hestia.conf $HESTIA/conf/defaults/hestia.conf +} + +# Validate hostname according to RFC1178 +validate_hostname() { + # remove extra . + servername=$(echo "$servername" | sed -e "s/[.]*$//g") + servername=$(echo "$servername" | sed -e "s/^[.]*//") + if [[ $(echo "$servername" | grep -o "\." | wc -l) -gt 1 ]] && [[ ! $servername =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Hostname valid + return 1 + else + # Hostname invalid + return 0 + fi +} + +validate_email() { + if [[ ! "$email" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]]; then + # Email invalid + return 0 + else + # Email valid + return 1 + fi +} + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +# Creating temporary file +tmpfile=$(mktemp -p /tmp) + +# Translating argument to --gnu-long-options +for arg; do + delim="" + case "$arg" in + --apache) args="${args}-a " ;; + --phpfpm) args="${args}-w " ;; + --vsftpd) args="${args}-v " ;; + --proftpd) args="${args}-j " ;; + --named) args="${args}-k " ;; + --mysql) args="${args}-m " ;; + --mysql-classic) args="${args}-M " ;; + --postgresql) args="${args}-g " ;; + --exim) args="${args}-x " ;; + --dovecot) args="${args}-z " ;; + --sieve) args="${args}-Z " ;; + --clamav) args="${args}-c " ;; + --spamassassin) args="${args}-t " ;; + --iptables) args="${args}-i " ;; + --fail2ban) args="${args}-b " ;; + --multiphp) args="${args}-o " ;; + --quota) args="${args}-q " ;; + --port) args="${args}-r " ;; + --lang) args="${args}-l " ;; + --interactive) args="${args}-y " ;; + --ipv6) args="${args}-6 " ;; + --api) args="${args}-d " ;; + --hostname) args="${args}-s " ;; + --email) args="${args}-e " ;; + --password) args="${args}-p " ;; + --force) args="${args}-f " ;; + --with-debs) args="${args}-D " ;; + --help) args="${args}-h " ;; + *) + [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} " + ;; + esac +done +eval set -- "$args" + +# Parsing arguments +while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:e:p:D:fh" Option; do + case $Option in + a) apache=$OPTARG ;; # Apache + w) phpfpm=$OPTARG ;; # PHP-FPM + o) multiphp=$OPTARG ;; # Multi-PHP + v) vsftpd=$OPTARG ;; # Vsftpd + j) proftpd=$OPTARG ;; # Proftpd + k) named=$OPTARG ;; # Named + m) mysql=$OPTARG ;; # MariaDB + M) mysqlclassic=$OPTARG ;; # MySQL + g) postgresql=$OPTARG ;; # PostgreSQL + x) exim=$OPTARG ;; # Exim + z) dovecot=$OPTARG ;; # Dovecot + Z) sieve=$OPTARG ;; # Sieve + c) clamd=$OPTARG ;; # ClamAV + t) spamd=$OPTARG ;; # SpamAssassin + i) iptables=$OPTARG ;; # Iptables + b) fail2ban=$OPTARG ;; # Fail2ban + q) quota=$OPTARG ;; # FS Quota + r) port=$OPTARG ;; # Backend Port + l) lang=$OPTARG ;; # Language + d) api=$OPTARG ;; # Activate API + y) interactive=$OPTARG ;; # Interactive install + s) servername=$OPTARG ;; # Hostname + e) email=$OPTARG ;; # Admin email + p) vpass=$OPTARG ;; # Admin password + D) withdebs=$OPTARG ;; # Hestia debs path + f) force='yes' ;; # Force install + h) help ;; # Help + *) help ;; # Print help (default) + esac +done + +# Defining default software stack +set_default_value 'nginx' 'yes' +set_default_value 'apache' 'yes' +set_default_value 'phpfpm' 'yes' +set_default_value 'multiphp' 'no' +set_default_value 'vsftpd' 'yes' +set_default_value 'proftpd' 'no' +set_default_value 'named' 'yes' +set_default_value 'mysql' 'yes' +set_default_value 'mysqlclassic' 'no' +set_default_value 'postgresql' 'no' +set_default_value 'exim' 'yes' +set_default_value 'dovecot' 'yes' +set_default_value 'sieve' 'no' +if [ $memory -lt 1500000 ]; then + set_default_value 'clamd' 'no' + set_default_value 'spamd' 'no' +elif [ $memory -lt 3000000 ]; then + set_default_value 'clamd' 'no' + set_default_value 'spamd' 'yes' +else + set_default_value 'clamd' 'yes' + set_default_value 'spamd' 'yes' +fi +set_default_value 'iptables' 'yes' +set_default_value 'fail2ban' 'yes' +set_default_value 'quota' 'no' +set_default_value 'interactive' 'yes' +set_default_value 'ipv6' 'no' +set_default_value 'api' 'yes' +set_default_port '8083' +set_default_lang 'en' + +# Checking software conflicts +if [ "$proftpd" = 'yes' ]; then + vsftpd='no' +fi +if [ "$exim" = 'no' ]; then + clamd='no' + spamd='no' + dovecot='no' +fi +if [ "$dovecot" = "no" ]; then + sieve='no' +fi +if [ "$iptables" = 'no' ]; then + fail2ban='no' +fi +if [ "$apache" = "no" ]; then + phpfpm='yes' +fi +if [ "$mysql" = 'yes' ] && [ "$mysqlclassic" = 'yes' ]; then + mysql='no' +fi + +# Checking root permissions +if [ "x$(id -u)" != 'x0' ]; then + check_result 1 "Script can be run executed only by root" +fi + +if [ -d "/usr/local/hestia" ]; then + check_result 1 "Hestia install detected. Unable to continue" +fi + +# Checking admin user account +if [ -n "$(grep ^admin: /etc/passwd /etc/group)" ] && [ -z "$force" ]; then + echo 'Please remove admin user account before proceeding.' + echo 'If you want to do it automatically run installer with -f option:' + echo -e "Example: bash $0 --force\n" + check_result 1 "User admin exists" +fi + +# Clear the screen once launch permissions have been verified +clear + +# Configure apt to retry downloading on error +if [ ! -f /etc/apt/apt.conf.d/80-retries ]; then + echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries +fi + +# Welcome message +echo "Welcome to the Hestia Control Panel installer!" +echo +echo "Please wait, the installer is now checking for missing dependencies..." +echo + +# Update apt repository +apt-get -qq update + +# Creating backup directory +mkdir -p "$hst_backups" + +# Pre-install packages +echo "[ * ] Installing dependencies..." +apt-get -y install $installer_dependencies >> $LOG +check_result $? "Package installation failed, check log file for more details." + +# Check if apparmor is installed +if [ $(dpkg-query -W -f='${Status}' apparmor 2> /dev/null | grep -c "ok installed") -eq 0 ]; then + apparmor='no' +else + apparmor='yes' +fi + +# Checking repository availability +wget --quiet "https://$GPG/deb_signing.key" -O /dev/null +check_result $? "Unable to connect to the Hestia APT repository" + +# Check installed packages +tmpfile=$(mktemp -p /tmp) +dpkg --get-selections > $tmpfile +conflicts_pkg="exim4 mariadb-server apache2 nginx hestia postfix" + +# Drop postfix from the list if exim should not be installed +if [ "$exim" = 'no' ]; then + conflicts_pkg=$(echo $conflicts_pkg | sed 's/postfix//g' | xargs) +fi + +for pkg in $conflicts_pkg; do + if [ -n "$(grep $pkg $tmpfile)" ]; then + conflicts="$pkg* $conflicts" + fi +done +rm -f $tmpfile +if [ -n "$conflicts" ] && [ -z "$force" ]; then + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + echo 'WARNING: The following packages are already installed' + echo "$conflicts" + echo + echo 'It is highly recommended that you remove them before proceeding.' + echo + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + read -p 'Would you like to remove the conflicting packages? [y/n] ' answer + if [ "$answer" = 'y' ] || [ "$answer" = 'Y' ]; then + apt-get -qq purge $conflicts -y + check_result $? 'apt-get remove failed' + unset $answer + else + check_result 1 "Hestia Control Panel should be installed on a clean server." + fi +fi + +# Check network configuration +if [ -d /etc/netplan ] && [ -z "$force" ]; then + if [ -z "$(ls -A /etc/netplan)" ]; then + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + echo 'WARNING: Your network configuration may not be set up correctly.' + echo 'Details: The netplan configuration directory is empty.' + echo '' + echo 'You may have a network configuration file that was created using' + echo 'systemd-networkd.' + echo '' + echo 'It is strongly recommended to migrate to netplan, which is now the' + echo 'default network configuration system in newer releases of Ubuntu.' + echo '' + echo 'While you can leave your configuration as-is, please note that you' + echo 'will not be able to use additional IPs properly.' + echo '' + echo 'If you wish to continue and force the installation,' + echo 'run this script with -f option:' + echo "Example: bash $0 --force" + echo + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + check_result 1 "Unable to detect netplan configuration." + fi +fi + +# Validate whether installation script matches release version before continuing with install +if [ -z "$withdebs" ] || [ ! -d "$withdebs" ]; then + release_branch_ver=$(curl -s https://raw.githubusercontent.com/hestiacp/hestiacp/release/src/deb/hestia/control | grep "Version:" | awk '{print $2}') + if [ "$HESTIA_INSTALL_VER" != "$release_branch_ver" ]; then + echo + echo -e "\e[91mInstallation aborted\e[0m" + echo "====================================================================" + echo -e "\e[33mERROR: Install script version does not match package version!\e[0m" + echo -e "\e[33mPlease download the installer from the release branch in order to continue:\e[0m" + echo "" + echo -e "\e[33mhttps://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh\e[0m" + echo "" + echo -e "\e[33mTo test pre-release versions, build the .deb packages and re-run the installer:\e[0m" + echo -e " \e[33m./hst_autocompile.sh \e[1m--hestia branchname no\e[21m\e[0m" + echo -e " \e[33m./hst-install.sh .. \e[1m--with-debs /tmp/hestiacp-src/debs\e[21m\e[0m" + echo "" + check_result 1 "Installation aborted" + fi +fi + +case $architecture in + x86_64) + ARCH="amd64" + ;; + aarch64) + ARCH="arm64" + ;; + *) + echo + echo -e "\e[91mInstallation aborted\e[0m" + echo "====================================================================" + echo -e "\e[33mERROR: $architecture is currently not supported!\e[0m" + echo -e "\e[33mPlease verify the achitecture used is currenlty supported\e[0m" + echo "" + echo -e "\e[33mhttps://github.com/hestiacp/hestiacp/blob/main/README.md\e[0m" + echo "" + check_result 1 "Installation aborted" + ;; +esac + +#----------------------------------------------------------# +# Brief Info # +#----------------------------------------------------------# + +install_welcome_message() { + DISPLAY_VER=$(echo $HESTIA_INSTALL_VER | sed "s|~alpha||g" | sed "s|~beta||g") + echo + echo ' _ _ _ _ ____ ____ ' + echo ' | | | | ___ ___| |_(_) __ _ / ___| _ \ ' + echo ' | |_| |/ _ \/ __| __| |/ _` | | | |_) | ' + echo ' | _ | __/\__ \ |_| | (_| | |___| __/ ' + echo ' |_| |_|\___||___/\__|_|\__,_|\____|_| ' + echo " " + echo " Hestia Control Panel " + if [[ "$HESTIA_INSTALL_VER" =~ "beta" ]]; then + echo " BETA RELEASE " + fi + if [[ "$HESTIA_INSTALL_VER" =~ "alpha" ]]; then + echo " DEVELOPMENT SNAPSHOT " + echo " NOT INTENDED FOR PRODUCTION USE " + echo " USE AT YOUR OWN RISK " + fi + echo " ${DISPLAY_VER} " + echo " www.hestiacp.com " + echo + echo "========================================================================" + echo + echo "Thank you for downloading Hestia Control Panel! In a few moments," + echo "we will begin installing the following components on your server:" + echo +} + +# Printing nice ASCII logo +clear +install_welcome_message + +# Web stack + +echo ' - NGINX Web / Proxy Server' +if [ "$apache" = 'yes' ]; then + echo ' - Apache Web Server (as backend)' +fi +if [ "$phpfpm" = 'yes' ] && [ "$multiphp" = 'no' ]; then + echo ' - PHP-FPM Application Server' +fi +if [ "$multiphp" = 'yes' ]; then + phpfpm='yes' + echo ' - Multi-PHP Environment' +fi + +# DNS stack +if [ "$named" = 'yes' ]; then + echo ' - Bind DNS Server' +fi + +# Mail stack +if [ "$exim" = 'yes' ]; then + echo -n ' - Exim Mail Server' + if [ "$clamd" = 'yes' ] || [ "$spamd" = 'yes' ]; then + echo -n ' + ' + if [ "$clamd" = 'yes' ]; then + echo -n 'ClamAV ' + fi + if [ "$spamd" = 'yes' ]; then + if [ "$clamd" = 'yes' ]; then + echo -n '+ ' + fi + echo -n 'SpamAssassin' + fi + fi + echo + if [ "$dovecot" = 'yes' ]; then + echo -n ' - Dovecot POP3/IMAP Server ' + if [ "$sieve" = 'yes' ]; then + echo -n '+ Sieve' + fi + fi +fi + +echo +# Database stack +if [ "$mysql" = 'yes' ]; then + echo ' - MariaDB Database Server' +fi +if [ "$mysqlclassic" = 'yes' ]; then + echo ' - MySQL8 Database Server' +fi +if [ "$postgresql" = 'yes' ]; then + echo ' - PostgreSQL Database Server' +fi + +# FTP stack +if [ "$vsftpd" = 'yes' ]; then + echo ' - Vsftpd FTP Server' +fi +if [ "$proftpd" = 'yes' ]; then + echo ' - ProFTPD FTP Server' +fi + +# Firewall stack +if [ "$iptables" = 'yes' ]; then + echo -n ' - Firewall (iptables)' +fi +if [ "$iptables" = 'yes' ] && [ "$fail2ban" = 'yes' ]; then + echo -n ' + Fail2Ban Access Monitor' +fi +echo -e "\n" +echo "========================================================================" +echo -e "\n" + +# Asking for confirmation to proceed +if [ "$interactive" = 'yes' ]; then + read -p 'Would you like to continue with the installation? [Y/N]: ' answer + if [ "$answer" != 'y' ] && [ "$answer" != 'Y' ]; then + echo 'Goodbye' + exit 1 + fi +fi + +# Validate Email / Hostname even when interactive = no +# Asking for contact email +if [ -z "$email" ]; then + while validate_email; do + echo -e "\nPlease use a valid emailadress (ex. info@domain.tld)." + read -p 'Please enter admin email address: ' email + done +else + if validate_email; then + echo "Please use a valid emailadress (ex. info@domain.tld)." + exit 1 + fi +fi + +# Asking to set FQDN hostname +if [ -z "$servername" ]; then + # Ask and validate FQDN hostname. + read -p "Please enter FQDN hostname [$(hostname -f)]: " servername + + # Set hostname if it wasn't set + if [ -z "$servername" ]; then + servername=$(hostname -f) + fi + + # Validate Hostname, go to loop if the validation fails. + while validate_hostname; do + echo -e "\nPlease use a valid hostname according to RFC1178 (ex. hostname.domain.tld)." + read -p "Please enter FQDN hostname [$(hostname -f)]: " servername + done +else + # Validate FQDN hostname if it is preset + if validate_hostname; then + echo "Please use a valid hostname according to RFC1178 (ex. hostname.domain.tld)." + exit 1 + fi +fi + +# Generating admin password if it wasn't set +displaypass="The password you chose during installation." +if [ -z "$vpass" ]; then + vpass=$(gen_pass) + displaypass=$vpass +fi + +# Set FQDN if it wasn't set +mask1='(([[:alnum:]](-?[[:alnum:]])*)\.)' +mask2='*[[:alnum:]](-?[[:alnum:]])+\.[[:alnum:]]{2,}' +if ! [[ "$servername" =~ ^${mask1}${mask2}$ ]]; then + if [[ -n "$servername" ]]; then + servername="$servername.example.com" + else + servername="example.com" + fi + echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi +fi + +if [[ -z $(grep -i "$servername" /etc/hosts) ]]; then + echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi +fi + +# Set email if it wasn't set +if [[ -z "$email" ]]; then + email="admin@$servername" +fi + +# Defining backup directory +echo -e "Installation backup directory: $hst_backups" + +# Print Log File Path +echo "Installation log file: $LOG" + +# Print new line +echo + +#----------------------------------------------------------# +# Checking swap # +#----------------------------------------------------------# + +# Checking swap on small instances +if [ -z "$(swapon -s)" ] && [ "$memory" -lt 1000000 ]; then + fallocate -l 1G /swapfile + chmod 600 /swapfile + mkswap /swapfile + swapon /swapfile + echo "/swapfile none swap sw 0 0" >> /etc/fstab +fi + +#----------------------------------------------------------# +# Install repository # +#----------------------------------------------------------# + +# Define apt conf location +apt=/etc/apt/sources.list.d + +#create new folder if not all-ready exists +mkdir -p /root/.gnupg/ && chmod 700 /root/.gnupg/ + +# Updating system +echo "Adding required repositories to proceed with installation:" +echo + +# Installing Nginx repo + +echo "[ * ] NGINX" +echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://nginx.org/packages/mainline/$VERSION/ $codename nginx" > $apt/nginx.list +curl -s https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-keyring.gpg > /dev/null 2>&1 + +# Installing sury PHP repo +echo "[ * ] PHP" +echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/sury-keyring.gpg] https://packages.sury.org/php/ $codename main" > $apt/php.list +curl -s https://packages.sury.org/php/apt.gpg | gpg --dearmor | tee /usr/share/keyrings/sury-keyring.gpg > /dev/null 2>&1 + +# Installing sury Apache2 repo +if [ "$apache" = 'yes' ]; then + echo "[ * ] Apache2" + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/apache2-keyring.gpg] https://packages.sury.org/apache2/ $codename main" > $apt/apache2.list + curl -s https://packages.sury.org/apache2/apt.gpg | gpg --dearmor | tee /usr/share/keyrings/apache2-keyring.gpg > /dev/null 2>&1 +fi + +# Installing MariaDB repo +if [ "$mysql" = 'yes' ]; then + echo "[ * ] MariaDB" + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/mariadb-keyring.gpg] https://dlm.mariadb.com/repo/mariadb-server/$mariadb_v/repo/$VERSION $codename main" > $apt/mariadb.list + curl -s https://mariadb.org/mariadb_release_signing_key.asc | gpg --dearmor | tee /usr/share/keyrings/mariadb-keyring.gpg > /dev/null 2>&1 +fi + +# Installing Mysql8 repo +if [ "$mysqlclassic" = 'yes' ]; then + echo "[ * ] Mysql 8" + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/mysql-keyring.gpg] http://repo.mysql.com/apt/debian/ $codename mysql-apt-config" >> /etc/apt/sources.list.d/mysql.list + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/mysql-keyring.gpg] http://repo.mysql.com/apt/debian/ $codename mysql-8.0" >> /etc/apt/sources.list.d/mysql.list + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/mysql-keyring.gpg] http://repo.mysql.com/apt/debian/ $codename mysql-tools" >> /etc/apt/sources.list.d/mysql.list + echo "#deb [arch=$ARCH signed-by=/usr/share/keyrings/mysql-keyring.gpg] http://repo.mysql.com/apt/debian/ $codename mysql-tools-preview" >> /etc/apt/sources.list.d/mysql.list + echo "deb-src [arch=$ARCH signed-by=/usr/share/keyrings/mysql-keyring.gpg] http://repo.mysql.com/apt/debian/ $codename mysql-8.0" >> /etc/apt/sources.list.d/mysql.list + + GNUPGHOME="$(mktemp -d)" + export GNUPGHOME + for keyserver in $(shuf -e ha.pool.sks-keyservers.net hkp://p80.pool.sks-keyservers.net:80 keyserver.ubuntu.com hkp://keyserver.ubuntu.com:80); do + gpg --no-default-keyring --keyring /usr/share/keyrings/mysql-keyring.gpg --keyserver "${keyserver}" --recv-keys "467B942D3A79BD29" > /dev/null 2>&1 && break + done +fi + +# Installing HestiaCP repo +echo "[ * ] Hestia Control Panel" +echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/hestia-keyring.gpg] https://$RHOST/ $codename main" > $apt/hestia.list +gpg --no-default-keyring --keyring /usr/share/keyrings/hestia-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys A189E93654F0B0E5 > /dev/null 2>&1 + +# Installing PostgreSQL repo +if [ "$postgresql" = 'yes' ]; then + echo "[ * ] PostgreSQL" + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/postgresql-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/ $codename-pgdg main" > $apt/postgresql.list + curl -s https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/postgresql-keyring.gpg > /dev/null 2>&1 +fi + +# Echo for a new line +echo + +# Updating system +echo -ne "Updating currently installed packages, please wait... " +apt-get -qq update +apt-get -y upgrade >> $LOG & +BACK_PID=$! + +# Check if package installation is done, print a spinner +spin_i=1 +while kill -0 $BACK_PID > /dev/null 2>&1; do + printf "\b${spinner:spin_i++%${#spinner}:1}" + sleep 0.5 +done + +# Do a blank echo to get the \n back +echo + +# Check Installation result +wait $BACK_PID +check_result $? 'apt-get upgrade failed' + +#----------------------------------------------------------# +# Backup # +#----------------------------------------------------------# + +# Creating backup directory tree +mkdir -p $hst_backups +cd $hst_backups +mkdir nginx apache2 php vsftpd proftpd bind exim4 dovecot clamd +mkdir spamassassin mysql postgresql hestia + +# Backup nginx configuration +systemctl stop nginx > /dev/null 2>&1 +cp -r /etc/nginx/* $hst_backups/nginx > /dev/null 2>&1 + +# Backup Apache configuration +systemctl stop apache2 > /dev/null 2>&1 +cp -r /etc/apache2/* $hst_backups/apache2 > /dev/null 2>&1 +rm -f /etc/apache2/conf.d/* > /dev/null 2>&1 + +# Backup PHP-FPM configuration +systemctl stop php*-fpm > /dev/null 2>&1 +cp -r /etc/php/* $hst_backups/php/ > /dev/null 2>&1 + +# Backup Bind configuration +systemctl stop bind9 > /dev/null 2>&1 +cp -r /etc/bind/* $hst_backups/bind > /dev/null 2>&1 + +# Backup Vsftpd configuration +systemctl stop vsftpd > /dev/null 2>&1 +cp /etc/vsftpd.conf $hst_backups/vsftpd > /dev/null 2>&1 + +# Backup ProFTPD configuration +systemctl stop proftpd > /dev/null 2>&1 +cp /etc/proftpd/* $hst_backups/proftpd > /dev/null 2>&1 + +# Backup Exim configuration +systemctl stop exim4 > /dev/null 2>&1 +cp -r /etc/exim4/* $hst_backups/exim4 > /dev/null 2>&1 + +# Backup ClamAV configuration +systemctl stop clamav-daemon > /dev/null 2>&1 +cp -r /etc/clamav/* $hst_backups/clamav > /dev/null 2>&1 + +# Backup SpamAssassin configuration +systemctl stop spamassassin > /dev/null 2>&1 +cp -r /etc/spamassassin/* $hst_backups/spamassassin > /dev/null 2>&1 + +# Backup Dovecot configuration +systemctl stop dovecot > /dev/null 2>&1 +cp /etc/dovecot.conf $hst_backups/dovecot > /dev/null 2>&1 +cp -r /etc/dovecot/* $hst_backups/dovecot > /dev/null 2>&1 + +# Backup MySQL/MariaDB configuration and data +systemctl stop mysql > /dev/null 2>&1 +killall -9 mysqld > /dev/null 2>&1 +mv /var/lib/mysql $hst_backups/mysql/mysql_datadir > /dev/null 2>&1 +cp -r /etc/mysql/* $hst_backups/mysql > /dev/null 2>&1 +mv -f /root/.my.cnf $hst_backups/mysql > /dev/null 2>&1 + +# Backup Hestia +systemctl stop hestia > /dev/null 2>&1 +cp -r $HESTIA/* $hst_backups/hestia > /dev/null 2>&1 +apt-get -y purge hestia hestia-nginx hestia-php > /dev/null 2>&1 +rm -rf $HESTIA > /dev/null 2>&1 + +#----------------------------------------------------------# +# Package Includes # +#----------------------------------------------------------# + +if [ "$phpfpm" = 'yes' ]; then + fpm="php$fpm_v php$fpm_v-common php$fpm_v-bcmath php$fpm_v-cli + php$fpm_v-curl php$fpm_v-fpm php$fpm_v-gd php$fpm_v-intl + php$fpm_v-mysql php$fpm_v-soap php$fpm_v-xml php$fpm_v-zip + php$fpm_v-mbstring php$fpm_v-bz2 php$fpm_v-pspell + php$fpm_v-imagick" + software="$software $fpm" +fi + +#----------------------------------------------------------# +# Package Excludes # +#----------------------------------------------------------# + +# Excluding packages +software=$(echo "$software" | sed -e "s/apache2.2-common//") + +if [ "$apache" = 'no' ]; then + software=$(echo "$software" | sed -e "s/apache2 //") + software=$(echo "$software" | sed -e "s/apache2-bin//") + software=$(echo "$software" | sed -e "s/apache2-utils//") + software=$(echo "$software" | sed -e "s/apache2-suexec-custom//") + software=$(echo "$software" | sed -e "s/apache2.2-common//") + software=$(echo "$software" | sed -e "s/libapache2-mod-rpaf//") + software=$(echo "$software" | sed -e "s/libapache2-mod-fcgid//") + software=$(echo "$software" | sed -e "s/libapache2-mod-php$fpm_v//") +fi +if [ "$vsftpd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/vsftpd//") +fi +if [ "$proftpd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/proftpd-basic//") + software=$(echo "$software" | sed -e "s/proftpd-mod-vroot//") +fi +if [ "$named" = 'no' ]; then + software=$(echo "$software" | sed -e "s/bind9//") +fi +if [ "$exim" = 'no' ]; then + software=$(echo "$software" | sed -e "s/exim4 //") + software=$(echo "$software" | sed -e "s/exim4-daemon-heavy//") + software=$(echo "$software" | sed -e "s/dovecot-imapd//") + software=$(echo "$software" | sed -e "s/dovecot-pop3d//") + software=$(echo "$software" | sed -e "s/clamav-daemon//") + software=$(echo "$software" | sed -e "s/spamassassin//") + software=$(echo "$software" | sed -e "s/dovecot-sieve//") + software=$(echo "$software" | sed -e "s/dovecot-managesieved//") +fi +if [ "$clamd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/clamav-daemon//") +fi +if [ "$spamd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/spamassassin//") +fi +if [ "$dovecot" = 'no' ]; then + software=$(echo "$software" | sed -e "s/dovecot-imapd//") + software=$(echo "$software" | sed -e "s/dovecot-pop3d//") +fi +if [ "$sieve" = 'no' ]; then + software=$(echo "$software" | sed -e "s/dovecot-sieve//") + software=$(echo "$software" | sed -e "s/dovecot-managesieved//") +fi +if [ "$mysql" = 'no' ]; then + software=$(echo "$software" | sed -e "s/mariadb-server//") + software=$(echo "$software" | sed -e "s/mariadb-client//") + software=$(echo "$software" | sed -e "s/mariadb-common//") +fi +if [ "$mysqlclassic" = 'no' ]; then + software=$(echo "$software" | sed -e "s/mysql-server//") + software=$(echo "$software" | sed -e "s/mysql-client//") + software=$(echo "$software" | sed -e "s/mysql-common//") +fi +if [ "$mysql" = 'no' ] && [ "$mysqlclassic" = 'no' ]; then + software=$(echo "$software" | sed -e "s/php$fpm_v-mysql//") +fi +if [ "$postgresql" = 'no' ]; then + software=$(echo "$software" | sed -e "s/postgresql-contrib//") + software=$(echo "$software" | sed -e "s/postgresql//") + software=$(echo "$software" | sed -e "s/php$fpm_v-pgsql//") + software=$(echo "$software" | sed -e "s/phppgadmin//") +fi +if [ "$fail2ban" = 'no' ]; then + software=$(echo "$software" | sed -e "s/fail2ban//") +fi +if [ "$iptables" = 'no' ]; then + software=$(echo "$software" | sed -e "s/ipset//") + software=$(echo "$software" | sed -e "s/fail2ban//") +fi +if [ "$phpfpm" = 'yes' ]; then + software=$(echo "$software" | sed -e "s/php$fpm_v-cgi//") + software=$(echo "$software" | sed -e "s/libapache2-mpm-itk//") + software=$(echo "$software" | sed -e "s/libapache2-mod-ruid2//") + software=$(echo "$software" | sed -e "s/libapache2-mod-php$fpm_v//") + +fi +if [ -d "$withdebs" ]; then + software=$(echo "$software" | sed -e "s/hestia-nginx//") + software=$(echo "$software" | sed -e "s/hestia-php//") + software=$(echo "$software" | sed -e "s/hestia=${HESTIA_INSTALL_VER}//") +fi + +#----------------------------------------------------------# +# Install packages # +#----------------------------------------------------------# + +# Disabling daemon autostart on apt-get install +echo -e '#!/bin/sh\nexit 101' > /usr/sbin/policy-rc.d +chmod a+x /usr/sbin/policy-rc.d + +# Installing apt packages +echo "The installer is now downloading and installing all required packages." +echo -ne "NOTE: This process may take 10 to 15 minutes to complete, please wait... " +echo +apt-get -y install $software > $LOG +BACK_PID=$! + +# Check if package installation is done, print a spinner +spin_i=1 +while kill -0 $BACK_PID > /dev/null 2>&1; do + printf "\b${spinner:spin_i++%${#spinner}:1}" + sleep 0.5 +done + +# Do a blank echo to get the \n back +echo + +# Check Installation result +wait $BACK_PID +check_result $? "apt-get install failed" + +echo +echo "========================================================================" +echo + +# Install Hestia packages from local folder +if [ -n "$withdebs" ] && [ -d "$withdebs" ]; then + echo "[ * ] Installing local package files..." + echo " - hestia core package" + dpkg -i $withdebs/hestia_*.deb > /dev/null 2>&1 + + if [ -z $(ls $withdebs/hestia-php_*.deb 2> /dev/null) ]; then + echo " - hestia-php backend package (from apt)" + apt-get -y install hestia-php > /dev/null 2>&1 + else + echo " - hestia-php backend package" + dpkg -i $withdebs/hestia-php_*.deb > /dev/null 2>&1 + fi + + if [ -z $(ls $withdebs/hestia-nginx_*.deb 2> /dev/null) ]; then + echo " - hestia-nginx backend package (from apt)" + apt-get -y install hestia-nginx > /dev/null 2>&1 + else + echo " - hestia-nginx backend package" + dpkg -i $withdebs/hestia-nginx_*.deb > /dev/null 2>&1 + fi +fi + +# Restoring autostart policy +rm -f /usr/sbin/policy-rc.d + +#----------------------------------------------------------# +# Configure system # +#----------------------------------------------------------# + +echo "[ * ] Configuring system settings..." + +# Enable SFTP subsystem for SSH +sftp_subsys_enabled=$(grep -iE "^#?.*subsystem.+(sftp )?sftp-server" /etc/ssh/sshd_config) +if [ -n "$sftp_subsys_enabled" ]; then + sed -i -E "s/^#?.*Subsystem.+(sftp )?sftp-server/Subsystem sftp internal-sftp/g" /etc/ssh/sshd_config +fi + +# Reduce SSH login grace time +sed -i "s/[#]LoginGraceTime [[:digit:]]m/LoginGraceTime 1m/g" /etc/ssh/sshd_config + +# Disable SSH suffix broadcast +if [ -z "$(grep "^DebianBanner no" /etc/ssh/sshd_config)" ]; then + sed -i '/^[#]Banner .*/a DebianBanner no' /etc/ssh/sshd_config + if [ -z "$(grep "^DebianBanner no" /etc/ssh/sshd_config)" ]; then + # If first attempt fails just add it + echo '' >> /etc/ssh/sshd_config + echo 'DebianBanner no' >> /etc/ssh/sshd_config + fi +fi + +# Restart SSH daemon +systemctl restart ssh + +# Disable AWStats cron +rm -f /etc/cron.d/awstats + +# Set directory color +if [ -z "$(grep 'LS_COLORS="$LS_COLORS:di=00;33"' /etc/profile)" ]; then + echo 'LS_COLORS="$LS_COLORS:di=00;33"' >> /etc/profile +fi + +# Register /sbin/nologin and /usr/sbin/nologin +if [ -z "$(grep ^/sbin/nologin /etc/shells)" ]; then + echo "/sbin/nologin" >> /etc/shells +fi + +if [ -z "$(grep ^/usr/sbin/nologin /etc/shells)" ]; then + echo "/usr/sbin/nologin" >> /etc/shells +fi + +# Configuring NTP +sed -i 's/#NTP=/NTP=pool.ntp.org/' /etc/systemd/timesyncd.conf +systemctl enable systemd-timesyncd +systemctl start systemd-timesyncd + +# Restrict access to /proc fs +# - Prevent unpriv users from seeing each other running processes +mount -o remount,defaults,hidepid=2 /proc > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Info: Cannot remount /proc (LXC containers require additional perm added to host apparmor profile)" +else + echo "@reboot root sleep 5 && mount -o remount,defaults,hidepid=2 /proc" > /etc/cron.d/hestia-proc +fi + +#----------------------------------------------------------# +# Configure Hestia # +#----------------------------------------------------------# + +echo "[ * ] Configuring Hestia Control Panel..." +# Installing sudo configuration +mkdir -p /etc/sudoers.d +cp -f $HESTIA_INSTALL_DIR/sudo/admin /etc/sudoers.d/ +chmod 440 /etc/sudoers.d/admin + +# Add Hestia global config +if [[ ! -e /etc/hestiacp/hestia.conf ]]; then + mkdir -p /etc/hestiacp + echo -e "# Do not edit this file, will get overwritten on next upgrade, use /etc/hestiacp/local.conf instead\n\nexport HESTIA='/usr/local/hestia'\n\n[[ -f /etc/hestiacp/local.conf ]] && source /etc/hestiacp/local.conf" > /etc/hestiacp/hestia.conf +fi + +# Configuring system env +echo "export HESTIA='$HESTIA'" > /etc/profile.d/hestia.sh +echo 'PATH=$PATH:'$HESTIA'/bin' >> /etc/profile.d/hestia.sh +echo 'export PATH' >> /etc/profile.d/hestia.sh +chmod 755 /etc/profile.d/hestia.sh +source /etc/profile.d/hestia.sh + +# Configuring logrotate for Hestia logs +cp -f $HESTIA_INSTALL_DIR/logrotate/hestia /etc/logrotate.d/hestia + +# Create log path and symbolic link +rm -f /var/log/hestia +mkdir -p /var/log/hestia +ln -s /var/log/hestia $HESTIA/log + +# Building directory tree and creating some blank files for Hestia +mkdir -p $HESTIA/conf $HESTIA/ssl $HESTIA/data/ips \ + $HESTIA/data/queue $HESTIA/data/users $HESTIA/data/firewall \ + $HESTIA/data/sessions +touch $HESTIA/data/queue/backup.pipe $HESTIA/data/queue/disk.pipe \ + $HESTIA/data/queue/webstats.pipe $HESTIA/data/queue/restart.pipe \ + $HESTIA/data/queue/traffic.pipe $HESTIA/data/queue/daily.pipe $HESTIA/log/system.log \ + $HESTIA/log/nginx-error.log $HESTIA/log/auth.log $HESTIA/log/backup.log +chmod 750 $HESTIA/conf $HESTIA/data/users $HESTIA/data/ips $HESTIA/log +chmod -R 750 $HESTIA/data/queue +chmod 660 /var/log/hestia/* +chmod 770 $HESTIA/data/sessions + +# Generating Hestia configuration +rm -f $HESTIA/conf/hestia.conf > /dev/null 2>&1 +touch $HESTIA/conf/hestia.conf +chmod 660 $HESTIA/conf/hestia.conf + +# Write default port value to hestia.conf +# If a custom port is specified it will be set at the end of the installation process. +write_config_value "BACKEND_PORT" "8083" + +# Web stack +if [ "$apache" = 'yes' ]; then + write_config_value "WEB_SYSTEM" "apache2" + write_config_value "WEB_RGROUPS" "www-data" + write_config_value "WEB_PORT" "8080" + write_config_value "WEB_SSL_PORT" "8443" + write_config_value "WEB_SSL" "mod_ssl" + write_config_value "PROXY_SYSTEM" "nginx" + write_config_value "PROXY_PORT" "80" + write_config_value "PROXY_SSL_PORT" "443" + write_config_value "STATS_SYSTEM" "awstats" +fi +if [ "$apache" = 'no' ]; then + write_config_value "WEB_SYSTEM" "nginx" + write_config_value "WEB_PORT" "80" + write_config_value "WEB_SSL_PORT" "443" + write_config_value "WEB_SSL" "openssl" + write_config_value "STATS_SYSTEM" "awstats" + +fi + +if [ "$phpfpm" = 'yes' ]; then + write_config_value "WEB_BACKEND" "php-fpm" +fi + +# Database stack +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + installed_db_types='mysql' +fi + +if [ "$postgresql" = 'yes' ]; then + installed_db_types="$installed_db_types,pgsql" +fi + +if [ -n "$installed_db_types" ]; then + db=$(echo "$installed_db_types" \ + | sed "s/,/\n/g" \ + | sort -r -u \ + | sed "/^$/d" \ + | sed ':a;N;$!ba;s/\n/,/g') + write_config_value "DB_SYSTEM" "$db" +fi + +# FTP stack +if [ "$vsftpd" = 'yes' ]; then + write_config_value "FTP_SYSTEM" "vsftpd" +fi +if [ "$proftpd" = 'yes' ]; then + write_config_value "FTP_SYSTEM" "proftpd" +fi + +# DNS stack +if [ "$named" = 'yes' ]; then + write_config_value "DNS_SYSTEM" "bind9" +fi + +# Mail stack +if [ "$exim" = 'yes' ]; then + write_config_value "MAIL_SYSTEM" "exim4" + if [ "$clamd" = 'yes' ]; then + write_config_value "ANTIVIRUS_SYSTEM" "clamav-daemon" + fi + if [ "$spamd" = 'yes' ]; then + write_config_value "ANTISPAM_SYSTEM" "spamassassin" + fi + if [ "$dovecot" = 'yes' ]; then + write_config_value "IMAP_SYSTEM" "dovecot" + fi + if [ "$sieve" = 'yes' ]; then + write_config_value "SIEVE_SYSTEM" "yes" + fi +fi + +# Cron daemon +write_config_value "CRON_SYSTEM" "cron" + +# Firewall stack +if [ "$iptables" = 'yes' ]; then + write_config_value "FIREWALL_SYSTEM" "iptables" +fi +if [ "$iptables" = 'yes' ] && [ "$fail2ban" = 'yes' ]; then + write_config_value "FIREWALL_EXTENSION" "fail2ban" +fi + +# Disk quota +if [ "$quota" = 'yes' ]; then + write_config_value "DISK_QUOTA" "yes" +else + write_config_value "DISK_QUOTA" "no" +fi + +# Backups +write_config_value "BACKUP_SYSTEM" "local" +write_config_value "BACKUP_GZIP" "4" +write_config_value "BACKUP_MODE" "zstd" + +# Language +write_config_value "LANGUAGE" "$lang" + +# Login in screen +write_config_value "LOGIN_STYLE" "default" + +# Theme +write_config_value "THEME" "dark" + +# Inactive session timeout +write_config_value "INACTIVE_SESSION_TIMEOUT" "60" + +# Version & Release Branch +write_config_value "VERSION" "${HESTIA_INSTALL_VER}" +write_config_value "RELEASE_BRANCH" "release" + +# Email notifications after upgrade +write_config_value "UPGRADE_SEND_EMAIL" "true" +write_config_value "UPGRADE_SEND_EMAIL_LOG" "false" + +# Installing hosting packages +cp -rf $HESTIA_COMMON_DIR/packages $HESTIA/data/ + +# Update nameservers in hosting package +IFS='.' read -r -a domain_elements <<< "$servername" +if [ -n "${domain_elements[-2]}" ] && [ -n "${domain_elements[-1]}" ]; then + serverdomain="${domain_elements[-2]}.${domain_elements[-1]}" + sed -i s/"domain.tld"/"$serverdomain"/g $HESTIA/data/packages/*.pkg +fi + +# Installing templates +cp -rf $HESTIA_INSTALL_DIR/templates $HESTIA/data/ +cp -rf $HESTIA_COMMON_DIR/templates/web/ $HESTIA/data/templates +cp -rf $HESTIA_COMMON_DIR/templates/dns/ $HESTIA/data/templates + +mkdir -p /var/www/html +mkdir -p /var/www/document_errors + +# Install default success page +cp -rf $HESTIA_COMMON_DIR/templates/web/unassigned/index.html /var/www/html/ +cp -rf $HESTIA_COMMON_DIR/templates/web/skel/document_errors/* /var/www/document_errors/ + +# Installing firewall rules +cp -rf $HESTIA_COMMON_DIR/firewall $HESTIA/data/ + +# Installing apis +cp -rf $HESTIA_COMMON_DIR/api $HESTIA/data/ + +# Configuring server hostname +$HESTIA/bin/v-change-sys-hostname $servername > /dev/null 2>&1 + +# Generating SSL certificate +echo "[ * ] Generating default self-signed SSL certificate..." +$HESTIA/bin/v-generate-ssl-cert $(hostname) '' 'US' 'California' \ + 'San Francisco' 'Hestia Control Panel' 'IT' > /tmp/hst.pem + +# Parsing certificate file +crt_end=$(grep -n "END CERTIFICATE-" /tmp/hst.pem | cut -f 1 -d:) +key_start=$(grep -n "BEGIN RSA" /tmp/hst.pem | cut -f 1 -d:) +key_end=$(grep -n "END RSA" /tmp/hst.pem | cut -f 1 -d:) + +# Adding SSL certificate +echo "[ * ] Adding SSL certificate to Hestia Control Panel..." +cd $HESTIA/ssl +sed -n "1,${crt_end}p" /tmp/hst.pem > certificate.crt +sed -n "$key_start,${key_end}p" /tmp/hst.pem > certificate.key +chown root:mail $HESTIA/ssl/* +chmod 660 $HESTIA/ssl/* +rm /tmp/hst.pem + +# Install dhparam.pem +cp -f $HESTIA_INSTALL_DIR/ssl/dhparam.pem /etc/ssl + +# Deleting old admin user +if [ -n "$(grep ^admin: /etc/passwd)" ] && [ "$force" = 'yes' ]; then + chattr -i /home/admin/conf > /dev/null 2>&1 + userdel -f admin > /dev/null 2>&1 + chattr -i /home/admin/conf > /dev/null 2>&1 + mv -f /home/admin $hst_backups/home/ > /dev/null 2>&1 + rm -f /tmp/sess_* > /dev/null 2>&1 +fi +if [ -n "$(grep ^admin: /etc/group)" ] && [ "$force" = 'yes' ]; then + groupdel admin > /dev/null 2>&1 +fi + +# Enable sftp jail +echo "[ * ] Enable SFTP jail..." +$HESTIA/bin/v-add-sys-sftp-jail > /dev/null 2>&1 +check_result $? "can't enable sftp jail" + +# Adding Hestia admin account +$HESTIA/bin/v-add-user admin $vpass $email "system" "System Administrator" +check_result $? "can't create admin user" +$HESTIA/bin/v-change-user-shell admin nologin +$HESTIA/bin/v-change-user-role admin admin +$HESTIA/bin/v-change-user-language admin $lang +$HESTIA/bin/v-change-sys-config-value 'POLICY_SYSTEM_PROTECTED_ADMIN' 'yes' + +locale-gen "en_US.utf8" > /dev/null 2>&1 +#----------------------------------------------------------# +# Configure Nginx # +#----------------------------------------------------------# + +echo "[ * ] Configuring NGINX..." +rm -f /etc/nginx/conf.d/*.conf +cp -f $HESTIA_INSTALL_DIR/nginx/nginx.conf /etc/nginx/ +cp -f $HESTIA_INSTALL_DIR/nginx/status.conf /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/nginx/agents.conf /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/logrotate/nginx /etc/logrotate.d/ +if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/nginx/nginx-ipv6.conf /etc/nginx/nginx.conf + cp -f $HESTIA_INSTALL_DIR/nginx/status-ipv6.conf /etc/nginx/conf.d/status.conf +fi +mkdir -p /etc/nginx/conf.d/domains +mkdir -p /etc/nginx/modules-enabled +mkdir -p /var/log/nginx/domains + +# Update dns servers in nginx.conf +dns_resolver=$(cat /etc/resolv.conf | grep -i '^nameserver' | cut -d ' ' -f2 | tr '\r\n' ' ' | xargs) +for ip in $dns_resolver; do + if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + resolver="$ip $resolver" + fi + if [ "$ipv6" = 'yes' ]; then + if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + resolver="[$ip] $resolver" + fi + fi +done +if [ -n "$resolver" ]; then + if [ "$ipv6" = 'yes' ]; then + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + else + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + fi +fi + +update-rc.d nginx defaults > /dev/null 2>&1 +systemctl start nginx >> $LOG +check_result $? "nginx start failed" + +#----------------------------------------------------------# +# Configure Apache # +#----------------------------------------------------------# + +if [ "$apache" = 'yes' ]; then + echo "[ * ] Configuring Apache Web Server..." + + mkdir -p /etc/apache2/conf.d + mkdir -p /etc/apache2/conf.d/domains + + # Copy configuration files + cp -f $HESTIA_INSTALL_DIR/apache2/apache2.conf /etc/apache2/ + cp -f $HESTIA_INSTALL_DIR/apache2/status.conf /etc/apache2/mods-available/hestia-status.conf + cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load + cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf + fi + # Enable needed modules + a2enmod rewrite > /dev/null 2>&1 + a2enmod suexec > /dev/null 2>&1 + a2enmod ssl > /dev/null 2>&1 + a2enmod actions > /dev/null 2>&1 + a2dismod --quiet status > /dev/null 2>&1 + a2enmod --quiet hestia-status > /dev/null 2>&1 + + if [ "$phpfpm" = 'yes' ]; then + # Disable prefork and php, enable event + a2dismod php$fpm_v > /dev/null 2>&1 + a2dismod mpm_prefork > /dev/null 2>&1 + a2enmod mpm_event > /dev/null 2>&1 + cp -f $HESTIA_INSTALL_DIR/apache2/hestia-event.conf /etc/apache2/conf.d/ + else + a2enmod mpm_itk > /dev/null 2>&1 + fi + + echo "# Powered by hestia" > /etc/apache2/sites-available/default + echo "# Powered by hestia" > /etc/apache2/sites-available/default-ssl + echo "# Powered by hestia" > /etc/apache2/ports.conf + echo -e "/home\npublic_html/cgi-bin" > /etc/apache2/suexec/www-data + touch /var/log/apache2/access.log /var/log/apache2/error.log + mkdir -p /var/log/apache2/domains + chmod a+x /var/log/apache2 + chmod 640 /var/log/apache2/access.log /var/log/apache2/error.log + chmod 751 /var/log/apache2/domains + + # Prevent remote access to server-status page + sed -i '/Allow from all/d' /etc/apache2/mods-available/hestia-status.conf + + update-rc.d apache2 defaults > /dev/null 2>&1 + systemctl start apache2 >> $LOG + check_result $? "apache2 start failed" +else + update-rc.d apache2 disable > /dev/null 2>&1 + systemctl stop apache2 > /dev/null 2>&1 +fi + +#----------------------------------------------------------# +# Configure PHP-FPM # +#----------------------------------------------------------# + +if [ "$phpfpm" = "yes" ]; then + if [ "$multiphp" = 'yes' ]; then + for v in "${multiphp_v[@]}"; do + echo "[ * ] Install PHP $v..." + $HESTIA/bin/v-add-web-php "$v" > /dev/null 2>&1 + done + else + echo "[ * ] Install PHP $fpm_v..." + $HESTIA/bin/v-add-web-php "$fpm_v" > /dev/null 2>&1 + fi + + echo "[ * ] Configuring PHP $fpm_v..." + # Create www.conf for webmail and php(*)admin + cp -f $HESTIA_INSTALL_DIR/php-fpm/www.conf /etc/php/$fpm_v/fpm/pool.d/www.conf + update-rc.d php$fpm_v-fpm defaults > /dev/null 2>&1 + systemctl start php$fpm_v-fpm >> $LOG + check_result $? "php-fpm start failed" + # Set default php version to $fpm_v + update-alternatives --set php /usr/bin/php$fpm_v > /dev/null 2>&1 +fi + +#----------------------------------------------------------# +# Configure PHP # +#----------------------------------------------------------# + +echo "[ * ] Configuring PHP..." +ZONE=$(timedatectl > /dev/null 2>&1 | grep Timezone | awk '{print $2}') +if [ -z "$ZONE" ]; then + ZONE='UTC' +fi +for pconf in $(find /etc/php* -name php.ini); do + sed -i "s/;date.timezone =/date.timezone = $ZONE/g" $pconf + sed -i 's%_open_tag = Off%_open_tag = On%g' $pconf +done + +# Cleanup php session files not changed in the last 7 days (60*24*7 minutes) +echo '#!/bin/sh' > /etc/cron.daily/php-session-cleanup +echo "find -O3 /home/*/tmp/ -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin '+10080' -delete > /dev/null 2>&1" >> /etc/cron.daily/php-session-cleanup +echo "find -O3 $HESTIA/data/sessions/ -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin '+10080' -delete > /dev/null 2>&1" >> /etc/cron.daily/php-session-cleanup +chmod 755 /etc/cron.daily/php-session-cleanup + +#----------------------------------------------------------# +# Configure Vsftpd # +#----------------------------------------------------------# + +if [ "$vsftpd" = 'yes' ]; then + echo "[ * ] Configuring Vsftpd server..." + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf + fi + touch /var/log/vsftpd.log + chown root:adm /var/log/vsftpd.log + chmod 640 /var/log/vsftpd.log + touch /var/log/xferlog + chown root:adm /var/log/xferlog + chmod 640 /var/log/xferlog + update-rc.d vsftpd defaults + systemctl start vsftpd + check_result $? "vsftpd start failed" + +fi + +#----------------------------------------------------------# +# Configure ProFTPD # +#----------------------------------------------------------# + +if [ "$proftpd" = 'yes' ]; then + echo "[ * ] Configuring ProFTPD server..." + echo "127.0.0.1 $servername" >> /etc/hosts + cp -f $HESTIA_INSTALL_DIR/proftpd/proftpd.conf /etc/proftpd/ + cp -f $HESTIA_INSTALL_DIR/proftpd/tls.conf /etc/proftpd/ + + if [ "$release" -eq 11 ]; then + sed -i 's|IdentLookups off|#IdentLookups off|g' /etc/proftpd/proftpd.conf + fi + + update-rc.d proftpd defaults > /dev/null 2>&1 + systemctl start proftpd >> $LOG + check_result $? "proftpd start failed" + + if [ "$release" -eq 11 ]; then + unit_files="$(systemctl list-unit-files | grep proftpd)" + if [[ "$unit_files" =~ "disabled" ]]; then + systemctl enable proftpd + fi + fi +fi + +#----------------------------------------------------------# +# Configure MariaDB / MySQL # +#----------------------------------------------------------# + +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + [ "$mysql" = 'yes' ] && mysql_type="MariaDB" || mysql_type="MySQL" + echo "[ * ] Configuring $mysql_type database server..." + mycnf="my-small.cnf" + if [ $memory -gt 1200000 ]; then + mycnf="my-medium.cnf" + fi + if [ $memory -gt 3900000 ]; then + mycnf="my-large.cnf" + fi + + if [ "$mysql_type" = 'MariaDB' ]; then + # Run mysql_install_db + mysql_install_db >> $LOG + fi + + # Remove symbolic link + rm -f /etc/mysql/my.cnf + # Configuring MariaDB + cp -f $HESTIA_INSTALL_DIR/mysql/$mycnf /etc/mysql/my.cnf + + # Switch MariaDB inclusions to the MySQL + if [ "$mysql_type" = 'MySQL' ]; then + sed -i '/query_cache_size/d' /etc/mysql/my.cnf + sed -i 's|mariadb.conf.d|mysql.conf.d|g' /etc/mysql/my.cnf + fi + + update-rc.d mysql defaults > /dev/null 2>&1 + systemctl start mysql >> $LOG + check_result $? "${mysql_type,,} start failed" + + # Securing MariaDB/MySQL installation + mpass=$(gen_pass) + echo -e "[client]\npassword='$mpass'\n" > /root/.my.cnf + chmod 600 /root/.my.cnf + + # Alter root password + mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$mpass'; FLUSH PRIVILEGES;" + if [ "$mysql_type" = 'MariaDB' ]; then + # Allow mysql access via socket for startup + mysql -e "UPDATE mysql.global_priv SET priv=json_set(priv, '$.password_last_changed', UNIX_TIMESTAMP(), '$.plugin', 'mysql_native_password', '$.authentication_string', 'invalid', '$.auth_or', json_array(json_object(), json_object('plugin', 'unix_socket'))) WHERE User='root';" + # Disable anonymous users + mysql -e "DELETE FROM mysql.global_priv WHERE User='';" + else + mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '$mpass';" + mysql -e "DELETE FROM mysql.user WHERE User='';" + mysql -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');" + fi + # Drop test database + mysql -e "DROP DATABASE IF EXISTS test" + mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'" + # Flush privileges + mysql -e "FLUSH PRIVILEGES;" +fi + +#----------------------------------------------------------# +# Configure phpMyAdmin # +#----------------------------------------------------------# + +# Source upgrade.conf with phpmyadmin versions +# shellcheck source=/usr/local/hestia/install/upgrade/upgrade.conf +source $HESTIA/install/upgrade/upgrade.conf + +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + # Display upgrade information + echo "[ * ] Installing phpMyAdmin version v$pma_v..." + + # Download latest phpmyadmin release + wget --quiet --retry-connrefused https://files.phpmyadmin.net/phpMyAdmin/$pma_v/phpMyAdmin-$pma_v-all-languages.tar.gz + + # Unpack files + tar xzf phpMyAdmin-$pma_v-all-languages.tar.gz + + # Create folders + mkdir -p /usr/share/phpmyadmin + mkdir -p /etc/phpmyadmin + mkdir -p /etc/phpmyadmin/conf.d/ + mkdir /usr/share/phpmyadmin/tmp + + # Configuring Apache2 for PHPMYADMIN + if [ "$apache" = 'yes' ]; then + touch /etc/apache2/conf.d/phpmyadmin.inc + fi + + # Overwrite old files + cp -rf phpMyAdmin-$pma_v-all-languages/* /usr/share/phpmyadmin + + # Create copy of config file + cp -f $HESTIA_INSTALL_DIR/phpmyadmin/config.inc.php /etc/phpmyadmin/ + mkdir -p /var/lib/phpmyadmin/tmp + chmod 770 /var/lib/phpmyadmin/tmp + chown root:www-data /usr/share/phpmyadmin/tmp + + # Set config and log directory + sed -i "s|'configFile' => ROOT_PATH . 'config.inc.php',|'configFile' => '/etc/phpmyadmin/config.inc.php',|g" /usr/share/phpmyadmin/libraries/vendor_config.php + + # Create temporary folder and change permission + chmod 770 /usr/share/phpmyadmin/tmp + chown root:www-data /usr/share/phpmyadmin/tmp + + # Generate blow fish + blowfish=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32) + sed -i "s|%blowfish_secret%|$blowfish|" /etc/phpmyadmin/config.inc.php + + # Clean Up + rm -fr phpMyAdmin-$pma_v-all-languages + rm -f phpMyAdmin-$pma_v-all-languages.tar.gz + + write_config_value "DB_PMA_ALIAS" "phpmyadmin" + $HESTIA/bin/v-change-sys-db-alias 'pma' "phpmyadmin" + + # Special thanks to Pavel Galkin (https://skurudo.ru) + # https://github.com/skurudo/phpmyadmin-fixer + # shellcheck source=/usr/local/hestia/install/deb/phpmyadmin/pma.sh + source $HESTIA_INSTALL_DIR/phpmyadmin/pma.sh > /dev/null 2>&1 + + # limit access to /etc/phpmyadmin/ + chown -R root:www-data /etc/phpmyadmin/ + chmod -R 640 /etc/phpmyadmin/* + chmod 750 /etc/phpmyadmin/conf.d/ +fi + +#----------------------------------------------------------# +# Configure PostgreSQL # +#----------------------------------------------------------# + +if [ "$postgresql" = 'yes' ]; then + echo "[ * ] Configuring PostgreSQL database server..." + ppass=$(gen_pass) + cp -f $HESTIA_INSTALL_DIR/postgresql/pg_hba.conf /etc/postgresql/*/main/ + systemctl restart postgresql + sudo -iu postgres psql -c "ALTER USER postgres WITH PASSWORD '$ppass'" + + # Configuring phpPgAdmin + if [ "$apache" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/pga/phppgadmin.conf /etc/apache2/conf.d/phppgadmin.inc + fi + cp -f $HESTIA_INSTALL_DIR/pga/config.inc.php /etc/phppgadmin/ + + write_config_value "DB_PGA_ALIAS" "phppgadmin" + $HESTIA/bin/v-change-sys-db-alias 'pga' "phppgadmin" +fi + +#----------------------------------------------------------# +# Configure Bind # +#----------------------------------------------------------# + +if [ "$named" = 'yes' ]; then + echo "[ * ] Configuring Bind DNS server..." + cp -f $HESTIA_INSTALL_DIR/bind/named.conf /etc/bind/ + cp -f $HESTIA_INSTALL_DIR/bind/named.conf.options /etc/bind/ + chown root:bind /etc/bind/named.conf + chown root:bind /etc/bind/named.conf.options + chown bind:bind /var/cache/bind + chmod 640 /etc/bind/named.conf + chmod 640 /etc/bind/named.conf.options + aa-complain /usr/sbin/named 2> /dev/null + if [ "$apparmor" = 'yes' ]; then + echo "/home/** rwm," >> /etc/apparmor.d/local/usr.sbin.named 2> /dev/null + systemctl status apparmor > /dev/null 2>&1 + if [ $? -ne 0 ]; then + systemctl restart apparmor + fi + fi + update-rc.d bind9 defaults > /dev/null 2>&1 + systemctl start bind9 + check_result $? "bind9 start failed" + + # Workaround for OpenVZ/Virtuozzo + if [ -e "/proc/vz/veinfo" ] && [ -e "/etc/rc.local" ]; then + sed -i "s/^exit 0/service bind9 restart\nexit 0/" /etc/rc.local + fi +fi + +#----------------------------------------------------------# +# Configure Exim # +#----------------------------------------------------------# + +if [ "$exim" = 'yes' ]; then + echo "[ * ] Configuring Exim mail server..." + gpasswd -a Debian-exim mail > /dev/null 2>&1 + exim_version=$(exim4 --version | head -1 | awk '{print $3}' | cut -f -2 -d .) + if [ "$exim_version" = "4.94" ]; then + cp -f $HESTIA_INSTALL_DIR/exim/exim4.conf.4.94.template /etc/exim4/exim4.conf.template + else + cp -f $HESTIA_INSTALL_DIR/exim/exim4.conf.template /etc/exim4/ + fi + cp -f $HESTIA_INSTALL_DIR/exim/dnsbl.conf /etc/exim4/ + cp -f $HESTIA_INSTALL_DIR/exim/spam-blocks.conf /etc/exim4/ + cp -f $HESTIA_INSTALL_DIR/exim/limit.conf /etc/exim4/ + cp -f $HESTIA_INSTALL_DIR/exim/system.filter /etc/exim4/ + touch /etc/exim4/white-blocks.conf + + if [ "$spamd" = 'yes' ]; then + sed -i "s/#SPAM/SPAM/g" /etc/exim4/exim4.conf.template + fi + if [ "$clamd" = 'yes' ]; then + sed -i "s/#CLAMD/CLAMD/g" /etc/exim4/exim4.conf.template + fi + + chmod 640 /etc/exim4/exim4.conf.template + rm -rf /etc/exim4/domains + mkdir -p /etc/exim4/domains + + rm -f /etc/alternatives/mta + ln -s /usr/sbin/exim4 /etc/alternatives/mta + update-rc.d -f sendmail remove > /dev/null 2>&1 + systemctl stop sendmail > /dev/null 2>&1 + update-rc.d -f postfix remove > /dev/null 2>&1 + systemctl stop postfix > /dev/null 2>&1 + update-rc.d exim4 defaults + systemctl start exim4 + check_result $? "exim4 start failed" +fi + +#----------------------------------------------------------# +# Configure Dovecot # +#----------------------------------------------------------# + +if [ "$dovecot" = 'yes' ]; then + echo "[ * ] Configuring Dovecot POP/IMAP mail server..." + gpasswd -a dovecot mail > /dev/null 2>&1 + cp -rf $HESTIA_COMMON_DIR/dovecot /etc/ + cp -f $HESTIA_INSTALL_DIR/logrotate/dovecot /etc/logrotate.d/ + chown -R root:root /etc/dovecot* + rm -f /etc/dovecot/conf.d/15-mailboxes.conf + + #Alter config for 2.2 + version=$(dovecot --version | cut -f -2 -d .) + if [ "$version" = "2.2" ]; then + echo "[ * ] Downgrade dovecot config to sync with 2.2 settings" + sed -i 's|#ssl_dh_parameters_length = 4096|ssl_dh_parameters_length = 4096|g' /etc/dovecot/conf.d/10-ssl.conf + sed -i 's|ssl_dh = /dev/null 2>&1 + gpasswd -a clamav Debian-exim > /dev/null 2>&1 + cp -f $HESTIA_INSTALL_DIR/clamav/clamd.conf /etc/clamav/ + update-rc.d clamav-daemon defaults + if [ ! -d "/run/clamav" ]; then + mkdir /run/clamav + fi + chown -R clamav:clamav /run/clamav + if [ -e "/lib/systemd/system/clamav-daemon.service" ]; then + exec_pre1='ExecStartPre=-/bin/mkdir -p /run/clamav' + exec_pre2='ExecStartPre=-/bin/chown -R clamav:clamav /run/clamav' + sed -i "s|\[Service\]/|[Service]\n$exec_pre1\n$exec_pre2|g" \ + /lib/systemd/system/clamav-daemon.service + systemctl daemon-reload + fi + echo -ne "[ * ] Installing ClamAV anti-virus definitions... " + /usr/bin/freshclam >> $LOG & + BACK_PID=$! + spin_i=1 + while kill -0 $BACK_PID > /dev/null 2>&1; do + printf "\b${spinner:spin_i++%${#spinner}:1}" + sleep 0.5 + done + echo + systemctl start clamav-daemon + check_result $? "clamav-daemon start failed" +fi + +#----------------------------------------------------------# +# Configure SpamAssassin # +#----------------------------------------------------------# + +if [ "$spamd" = 'yes' ]; then + echo "[ * ] Configuring SpamAssassin..." + update-rc.d spamassassin defaults > /dev/null 2>&1 + sed -i "s/ENABLED=0/ENABLED=1/" /etc/default/spamassassin + systemctl start spamassassin >> $LOG + check_result $? "spamassassin start failed" + unit_files="$(systemctl list-unit-files | grep spamassassin)" + if [[ "$unit_files" =~ "disabled" ]]; then + systemctl enable spamassassin > /dev/null 2>&1 + fi + sed -i "s/#CRON=1/CRON=1/" /etc/default/spamassassin +fi + +#----------------------------------------------------------# +# Configure Fail2Ban # +#----------------------------------------------------------# + +if [ "$fail2ban" = 'yes' ]; then + echo "[ * ] Configuring fail2ban access monitor..." + cp -rf $HESTIA_INSTALL_DIR/fail2ban /etc/ + if [ "$dovecot" = 'no' ]; then + fline=$(cat /etc/fail2ban/jail.local | grep -n dovecot-iptables -A 2) + fline=$(echo "$fline" | grep enabled | tail -n1 | cut -f 1 -d -) + sed -i "${fline}s/true/false/" /etc/fail2ban/jail.local + fi + if [ "$exim" = 'no' ]; then + fline=$(cat /etc/fail2ban/jail.local | grep -n exim-iptables -A 2) + fline=$(echo "$fline" | grep enabled | tail -n1 | cut -f 1 -d -) + sed -i "${fline}s/true/false/" /etc/fail2ban/jail.local + fi + if [ "$vsftpd" = 'yes' ]; then + #Create vsftpd Log File + if [ ! -f "/var/log/vsftpd.log" ]; then + touch /var/log/vsftpd.log + fi + fline=$(cat /etc/fail2ban/jail.local | grep -n vsftpd-iptables -A 2) + fline=$(echo "$fline" | grep enabled | tail -n1 | cut -f 1 -d -) + sed -i "${fline}s/false/true/" /etc/fail2ban/jail.local + fi + if [ ! -e /var/log/auth.log ]; then + # Debian workaround: auth logging was moved to systemd + touch /var/log/auth.log + chmod 640 /var/log/auth.log + chown root:adm /var/log/auth.log + fi + if [ -f /etc/fail2ban/jail.d/defaults-debian.conf ]; then + rm -f /etc/fail2ban/jail.d/defaults-debian.conf + fi + update-rc.d fail2ban defaults + systemctl start fail2ban + check_result $? "fail2ban start failed" +fi + +# Configuring MariaDB/MySQL host +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + $HESTIA/bin/v-add-database-host mysql localhost root $mpass +fi + +# Configuring PostgreSQL host +if [ "$postgresql" = 'yes' ]; then + $HESTIA/bin/v-add-database-host pgsql localhost postgres $ppass +fi + +#----------------------------------------------------------# +# Install Roundcube # +#----------------------------------------------------------# +# Min requirements Dovecot + Exim + Mysql +if ([ "$mysql" == 'yes' ] || [ "$mysqlclassic" == 'yes' ]) && [ "$dovecot" == "yes" ]; then + echo "[ * ] Install Roundcube..." + $HESTIA/bin/v-add-sys-roundcube + write_config_value "WEBMAIL_ALIAS" "webmail" +else + write_config_value "WEBMAIL_ALIAS" "" + write_config_value "WEBMAIL_SYSTEM" "" +fi + +#----------------------------------------------------------# +# Install Sieve # +#----------------------------------------------------------# +# Min requirements Dovecot + Exim + Mysql + Roundcube +if [ "$sieve" = 'yes' ]; then + # Folder paths + RC_INSTALL_DIR="/var/lib/roundcube" + RC_CONFIG_DIR="/etc/roundcube" + + echo "[ * ] Install Sieve..." + + # dovecot.conf install + sed -i "s/namespace/service stats \{\n unix_listener stats-writer \{\n group = mail\n mode = 0660\n user = dovecot\n \}\n\}\n\nnamespace/g" /etc/dovecot/dovecot.conf + + # dovecot conf files + # 10-master.conf + sed -i -E -z "s/ }\n user = dovecot\n}/ \}\n unix_listener auth-master \{\n group = mail\n mode = 0660\n user = dovecot\n \}\n user = dovecot\n\}/g" /etc/dovecot/conf.d/10-master.conf + # 15-lda.conf + sed -i "s/\#mail_plugins = \\\$mail_plugins/mail_plugins = \$mail_plugins quota sieve\n auth_socket_path = \/var\/run\/dovecot\/auth-master/g" /etc/dovecot/conf.d/15-lda.conf + # 20-imap.conf + sed -i "s/mail_plugins = quota imap_quota/mail_plugins = quota imap_quota imap_sieve/g" /etc/dovecot/conf.d/20-imap.conf + + # replace dovecot-sieve config files + cp -f $HESTIA_COMMON_DIR/dovecot/sieve/* /etc/dovecot/conf.d + + echo -e "require [\"fileinto\"];\n# rule:[SPAM]\nif header :contains \"X-Spam-Flag\" \"YES\" {\n fileinto \"INBOX.Spam\";\n}\n" > /etc/dovecot/sieve/default + + # exim4 install + sed -i "s/\stransport = local_delivery/ transport = dovecot_virtual_delivery/" /etc/exim4/exim4.conf.template + sed -i "s/address_pipe:/dovecot_virtual_delivery:\n driver = pipe\n command = \/usr\/lib\/dovecot\/dovecot-lda -e -d \$local_part@\$domain -f \$sender_address -a \$original_local_part@\$original_domain\n delivery_date_add\n envelope_to_add\n return_path_add\n log_output = true\n log_defer_output = true\n user = \${extract{2}{:}{\${lookup{\$local_part}lsearch{\/etc\/exim4\/domains\/\${lookup{\$domain}dsearch{\/etc\/exim4\/domains\/}}\/passwd}}}}\n group = mail\n return_output\n\naddress_pipe:/g" /etc/exim4/exim4.conf.template + + # Permission changes + chown -R dovecot:mail /var/log/dovecot.log + chmod 660 /var/log/dovecot.log + + if [ -d "/var/lib/roundcube" ]; then + # Modify Roundcube config + mkdir -p $RC_CONFIG_DIR/plugins/managesieve + cp -f $HESTIA_COMMON_DIR/roundcube/plugins/config_managesieve.inc.php $RC_CONFIG_DIR/plugins/managesieve/config.inc.php + ln -s $RC_CONFIG_DIR/plugins/managesieve/config.inc.php $RC_INSTALL_DIR/plugins/managesieve/config.inc.php + chown -R root:www-data $RC_CONFIG_DIR/ + chmod 751 -R $RC_CONFIG_DIR + chmod 644 $RC_CONFIG_DIR/*.php + chmod 644 $RC_CONFIG_DIR/plugins/managesieve/config.inc.php + sed -i "s/'archive'/'archive', 'managesieve'/g" $RC_CONFIG_DIR/config.inc.php + fi + # Restart Dovecot and exim4 + systemctl restart dovecot > /dev/null 2>&1 + systemctl restart exim4 > /dev/null 2>&1 +fi + +#----------------------------------------------------------# +# Configure File Manager # +#----------------------------------------------------------# + +echo "[ * ] Configuring File Manager..." +$HESTIA/bin/v-add-sys-filemanager quiet + +#----------------------------------------------------------# +# Configure PHPMailer # +#----------------------------------------------------------# + +echo "[ * ] Configuring PHP dependencies..." +$HESTIA/bin/v-add-sys-dependencies quiet + +#----------------------------------------------------------# +# Configure API # +#----------------------------------------------------------# + +if [ "$api" = "yes" ]; then + # keep legacy api enabled until transition is complete + write_config_value "API" "yes" + write_config_value "API_SYSTEM" "1" + write_config_value "API_ALLOWED_IP" "" +else + write_config_value "API" "no" + write_config_value "API_SYSTEM" "0" + write_config_value "API_ALLOWED_IP" "" + $HESTIA/bin/v-change-sys-api disable +fi + +#----------------------------------------------------------# +# Configure IP # +#----------------------------------------------------------# + +# Configuring system IPs +echo "[ * ] Configuring System IP..." +$HESTIA/bin/v-update-sys-ip > /dev/null 2>&1 + +# Get main IP +ip=$(ip addr | grep 'inet ' | grep global | head -n1 | awk '{print $2}' | cut -f1 -d/) +ipv6=$(ip addr|grep 'inet6 '|grep global|head -n1|awk '{print $2}'|cut -f1 -d/) +local_ip=$ip +local_ipv6=$ipv6 + +# Configuring firewall +if [ "$iptables" = 'yes' ]; then + $HESTIA/bin/v-update-firewall +fi + +# Get public IP +pub_ip=$(curl --ipv4 -s https://ip.hestiacp.com/) +pub_ipv6=$(curl --ipv6 -s https://ip.hestiacp.com/) + +if [ -n "$pub_ip" ] && [ "$pub_ip" != "$ip" ]; then + $HESTIA/bin/v-change-sys-ip-nat $ip $pub_ip > /dev/null 2>&1 + ip=$pub_ip +fi + +# Configuring libapache2-mod-remoteip +if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then + cd /etc/apache2/mods-available + echo "" > remoteip.conf + echo " RemoteIPHeader X-Real-IP" >> remoteip.conf + if [ "$local_ip" != "127.0.0.1" ] && [ "$pub_ip" != "127.0.0.1" ]; then + echo " RemoteIPInternalProxy 127.0.0.1" >> remoteip.conf + fi + if [ -n "$local_ip" ] && [ "$local_ip" != "$pub_ip" ]; then + echo " RemoteIPInternalProxy $local_ip" >> remoteip.conf + fi + if [ -n "$pub_ip" ]; then + echo " RemoteIPInternalProxy $pub_ip" >> remoteip.conf + fi + echo "" >> remoteip.conf + sed -i "s/LogFormat \"%h/LogFormat \"%a/g" /etc/apache2/apache2.conf + a2enmod remoteip >> $LOG + systemctl restart apache2 +fi + +# Adding default domain +$HESTIA/bin/v-add-web-domain admin $servername $ip +check_result $? "can't create $servername domain" + +# Adding cron jobs +export SCHEDULED_RESTART="yes" +command="sudo $HESTIA/bin/v-update-sys-queue restart" +$HESTIA/bin/v-add-cron-job 'admin' '*/2' '*' '*' '*' '*' "$command" +systemctl restart cron + +command="sudo $HESTIA/bin/v-update-sys-queue daily" +$HESTIA/bin/v-add-cron-job 'admin' '10' '00' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue disk" +$HESTIA/bin/v-add-cron-job 'admin' '15' '02' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue traffic" +$HESTIA/bin/v-add-cron-job 'admin' '10' '00' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue webstats" +$HESTIA/bin/v-add-cron-job 'admin' '30' '03' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue backup" +$HESTIA/bin/v-add-cron-job 'admin' '*/5' '*' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-backup-users" +$HESTIA/bin/v-add-cron-job 'admin' '10' '05' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-user-stats" +$HESTIA/bin/v-add-cron-job 'admin' '20' '00' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-rrd" +$HESTIA/bin/v-add-cron-job 'admin' '*/5' '*' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-letsencrypt-ssl" +min=$(gen_pass '012345' '2') +hour=$(gen_pass '1234567' '1') +$HESTIA/bin/v-add-cron-job 'admin' "$min" "$hour" '*' '*' '*' "$command" + +# Enable automatic updates +$HESTIA/bin/v-add-cron-hestia-autoupdate apt + +# Building initital rrd images +$HESTIA/bin/v-update-sys-rrd + +# Enabling file system quota +if [ "$quota" = 'yes' ]; then + $HESTIA/bin/v-add-sys-quota +fi + +# Set backend port +$HESTIA/bin/v-change-sys-port $port > /dev/null 2>&1 + +# Create default configuration files +$HESTIA/bin/v-update-sys-defaults + +# Update remaining packages since repositories have changed +echo -ne "[ * ] Installing remaining software updates..." +apt-get -qq update +apt-get -y upgrade >> $LOG & +BACK_PID=$! +echo + +# Starting Hestia service +update-rc.d hestia defaults +systemctl start hestia +check_result $? "hestia start failed" +chown admin:admin $HESTIA/data/sessions + +# Create backup folder and set correct permission +mkdir -p /backup/ +chmod 755 /backup/ + +# create cronjob to generate ssl +echo "@reboot root sleep 10 && rm /etc/cron.d/hestia-ssl && PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:' && /usr/local/hestia/bin/v-add-letsencrypt-host" > /etc/cron.d/hestia-ssl + +#----------------------------------------------------------# +# Set hestia.conf default values # +#----------------------------------------------------------# + +echo "[ * ] Updating configuration files..." +write_config_value "PHPMYADMIN_KEY" "" +write_config_value "POLICY_USER_VIEW_SUSPENDED" "no" +write_config_value "POLICY_USER_VIEW_LOGS" "yes" +write_config_value "POLICY_USER_EDIT_WEB_TEMPLATES" "true" +write_config_value "POLICY_USER_EDIT_DNS_TEMPLATES" "yes" +write_config_value "POLICY_USER_EDIT_DETAILS" "yes" +write_config_value "POLICY_USER_DELETE_LOGS" "yes" +write_config_value "POLICY_USER_CHANGE_THEME" "yes" +write_config_value "POLICY_SYSTEM_PROTECTED_ADMIN" "no" +write_config_value "POLICY_SYSTEM_PASSWORD_RESET" "yes" +write_config_value "POLICY_SYSTEM_HIDE_SERVICES" "yes" +write_config_value "POLICY_SYSTEM_ENABLE_BACON" "no" +write_config_value "PLUGIN_APP_INSTALLER" "true" +write_config_value "DEBUG_MODE" "no" +write_config_value "ENFORCE_SUBDOMAIN_OWNERSHIP" "yes" +write_config_value "USE_SERVER_SMTP" "false" +write_config_value "SERVER_SMTP_PORT" "" +write_config_value "SERVER_SMTP_HOST" "" +write_config_value "SERVER_SMTP_SECURITY" "" +write_config_value "SERVER_SMTP_USER" "" +write_config_value "SERVER_SMTP_PASSWD" "" +write_config_value "SERVER_SMTP_ADDR" "" +write_config_value "POLICY_CSRF_STRICTNESS" "1" + +# Add /usr/local/hestia/bin/ to path variable +echo 'if [ "${PATH#*/usr/local/hestia/bin*}" = "$PATH" ]; then + . /etc/profile.d/hestia.sh +fi' >> /root/.bashrc + +#----------------------------------------------------------# +# Hestia Access Info # +#----------------------------------------------------------# + +# Comparing hostname and IP +host_ip=$(host $servername | head -n 1 | awk '{print $NF}') +if [ "$host_ip" = "$ip" ]; then + ip="$servername" +fi + +echo -e "\n" +echo "====================================================================" +echo -e "\n" + +# Sending notification to admin email +echo -e "Congratulations! + +You have successfully installed Hestia Control Panel on your server. + +Ready to get started? Log in using the following credentials: + + Admin URL: https://$ip:$port + Username: admin + Password: $displaypass + +Thank you for choosing Hestia Control Panel to power your full stack web server, +we hope that you enjoy using it as much as we do! + +Please feel free to contact us at any time if you have any questions, +or if you encounter any bugs or problems: + +Documentation: https://docs.hestiacp.com/ +Forum: https://forum.hestiacp.com/ +Discord: https://discord.gg/nXRUZch +GitHub: https://www.github.com/hestiacp/hestiacp + +Note: Automatic updates are enabled by default. If you would like to disable them, +please log in and navigate to Server > Updates to turn them off. + +Help support the Hestia Control Panel project by donating via PayPal: +https://www.hestiacp.com/donate + +-- +Sincerely yours, +The Hestia Control Panel development team + +Made with love & pride by the open-source community around the world. +" > $tmpfile + +send_mail="$HESTIA/web/inc/mail-wrapper.php" +cat $tmpfile | $send_mail -s "Hestia Control Panel" $email + +# Congrats +echo +cat $tmpfile +rm -f $tmpfile + +# Add welcome message to notification panel +$HESTIA/bin/v-add-user-notification admin 'Welcome to Hestia Control Panel!' '
You are now ready to begin adding user accounts and domains. For help and assistance, view the documentation or visit our user forum.

Please report any bugs or issues via GitHub.

Have a wonderful day!

The Hestia Control Panel development team' + +# Clean-up +# Sort final configuration file +sort_config_file + +if [ "$interactive" = 'yes' ]; then + echo "[ ! ] IMPORTANT: The system will now reboot to complete the installation process." + read -n 1 -s -r -p "Press any key to continue" + reboot +else + echo "[ ! ] IMPORTANT: You must restart the system before continuing!" +fi +# EOF diff --git a/install/hst-install-ubuntu.sh b/install/hst-install-ubuntu.sh new file mode 100755 index 0000000000..e763da16ce --- /dev/null +++ b/install/hst-install-ubuntu.sh @@ -0,0 +1,2285 @@ +#!/bin/bash + +# ======================================================== # +# +# Hestia Control Panel Installer for Ubuntu +# https://www.hestiacp.com/ +# +# Currently Supported Versions: +# Ubuntu 18.04 LTS, 20.04, 22.04 LTS +# +# ======================================================== # + +#----------------------------------------------------------# +# Variables&Functions # +#----------------------------------------------------------# +export PATH=$PATH:/sbin +export DEBIAN_FRONTEND=noninteractive +RHOST='apt.hestiacp.com' +GPG='gpg.hestiacp.com' +VERSION='ubuntu' +HESTIA='/usr/local/hestia' +LOG="/root/hst_install_backups/hst_install-$(date +%d%m%Y%H%M).log" +memory=$(grep 'MemTotal' /proc/meminfo | tr ' ' '\n' | grep [0-9]) +hst_backups="/root/hst_install_backups/$(date +%d%m%Y%H%M)" +spinner="/-\|" +os='ubuntu' +release="$(lsb_release -s -r)" +codename="$(lsb_release -s -c)" +architecture="$(arch)" +HESTIA_INSTALL_DIR="$HESTIA/install/deb" +HESTIA_COMMON_DIR="$HESTIA/install/common" +VERBOSE='no' + +# Define software versions +HESTIA_INSTALL_VER='1.7.0~alpha' +# Dependencies +multiphp_v=("5.6" "7.0" "7.1" "7.2" "7.3" "7.4" "8.0" "8.1" "8.2") +fpm_v="8.0" +mariadb_v="10.6" + +# Defining software pack for all distros +software="apache2 apache2.2-common apache2-suexec-custom apache2-utils + apparmor-utils awstats bc bind9 bsdmainutils bsdutils clamav-daemon + cron curl dnsutils dovecot-imapd dovecot-pop3d dovecot-sieve dovecot-managesieved + e2fslibs e2fsprogs exim4 exim4-daemon-heavy expect fail2ban flex ftp git idn2 + imagemagick libapache2-mod-fcgid libapache2-mod-php$fpm_v libapache2-mod-rpaf + lsof mc mariadb-client mariadb-common mariadb-server mysql-client mysql-common mysql-server nginx + php$fpm_v php$fpm_v-cgi php$fpm_v-common php$fpm_v-curl + php$fpm_v-mysql php$fpm_v-imap php$fpm_v-ldap php$fpm_v-apcu phppgadmin + php$fpm_v-pgsql php$fpm_v-zip php$fpm_v-bz2 php$fpm_v-cli php$fpm_v-gd + php$fpm_v-imagick php$fpm_v-intl php$fpm_v-mbstring + php$fpm_v-opcache php$fpm_v-pspell php$fpm_v-readline php$fpm_v-xml + postgresql postgresql-contrib proftpd-basic quota rrdtool spamassassin sudo hestia=${HESTIA_INSTALL_VER} + hestia-nginx hestia-php vim-common vsftpd whois unzip zip acl sysstat setpriv rsyslog + ipset libonig5 libzip5 openssh-server lsb-release zstd jq" + +installer_dependencies="apt-transport-https curl dirmngr gnupg wget software-properties-common ca-certificates" + +# Defining help function +help() { + echo "Usage: $0 [OPTIONS] + -a, --apache Install Apache [yes|no] default: yes + -w, --phpfpm Install PHP-FPM [yes|no] default: yes + -o, --multiphp Install Multi-PHP [yes|no] default: no + -v, --vsftpd Install Vsftpd [yes|no] default: yes + -j, --proftpd Install ProFTPD [yes|no] default: no + -k, --named Install Bind [yes|no] default: yes + -m, --mysql Install MariaDB [yes|no] default: yes + -M, --mysql-classic Install MySQL [yes|no] default: no + -g, --postgresql Install PostgreSQL [yes|no] default: no + -x, --exim Install Exim [yes|no] default: yes + -z, --dovecot Install Dovecot [yes|no] default: yes + -Z, --sieve Install Sieve [yes|no] default: no + -c, --clamav Install ClamAV [yes|no] default: yes + -t, --spamassassin Install SpamAssassin [yes|no] default: yes + -i, --iptables Install Iptables [yes|no] default: yes + -b, --fail2ban Install Fail2ban [yes|no] default: yes + -q, --quota Filesystem Quota [yes|no] default: no + -d, --api Activate API [yes|no] default: yes + -r, --port Change Backend Port default: 8083 + -l, --lang Default language default: en + -y, --interactive Interactive install [yes|no] default: yes + -6, --ipv6 Enable IPv6 Support [yes|no] default: no + -s, --hostname Set hostname + -e, --email Set admin email + -p, --password Set admin password + -D, --with-debs Path to Hestia debs + -f, --force Force installation + -h, --help Print this help + + Example: bash $0 -e demo@hestiacp.com -p p4ssw0rd --multiphp yes" + exit 1 +} + +# Defining file download function +download_file() { + wget $1 -q --show-progress --progress=bar:force +} + +# Defining password-gen function +gen_pass() { + matrix=$1 + length=$2 + if [ -z "$matrix" ]; then + matrix="A-Za-z0-9" + fi + if [ -z "$length" ]; then + length=16 + fi + head /dev/urandom | tr -dc $matrix | head -c$length +} + +# Defining return code check function +check_result() { + if [ $1 -ne 0 ]; then + echo "Error: $2" + exit $1 + fi +} + +# Defining function to set default value +set_default_value() { + eval variable=\$$1 + if [ -z "$variable" ]; then + eval $1=$2 + fi + if [ "$variable" != 'yes' ] && [ "$variable" != 'no' ]; then + eval $1=$2 + fi +} + +# Defining function to set default language value +set_default_lang() { + if [ -z "$lang" ]; then + eval lang=$1 + fi + lang_list="ar az bg bn bs ckb cs da de el en es fa fi fr hr hu id it ja ka ko nl no pl pt pt-br ro ru sk sr sv th tr uk ur vi zh-cn zh-tw" + if ! (echo $lang_list | grep -w $lang > /dev/null 2>&1); then + eval lang=$1 + fi +} + +# Define the default backend port +set_default_port() { + if [ -z "$port" ]; then + eval port=$1 + fi +} + +# Write configuration KEY/VALUE pair to $HESTIA/conf/hestia.conf +write_config_value() { + local key="$1" + local value="$2" + echo "$key='$value'" >> $HESTIA/conf/hestia.conf +} + +# Sort configuration file values +# Write final copy to $HESTIA/conf/hestia.conf for active usage +# Duplicate file to $HESTIA/conf/defaults/hestia.conf to restore known good installation values +sort_config_file() { + sort $HESTIA/conf/hestia.conf -o /tmp/updconf + mv $HESTIA/conf/hestia.conf $HESTIA/conf/hestia.conf.bak + mv /tmp/updconf $HESTIA/conf/hestia.conf + rm -f $HESTIA/conf/hestia.conf.bak + if [ ! -d "$HESTIA/conf/defaults/" ]; then + mkdir -p "$HESTIA/conf/defaults/" + fi + cp $HESTIA/conf/hestia.conf $HESTIA/conf/defaults/hestia.conf +} + +# Validate hostname according to RFC1178 +validate_hostname() { + # remove extra . + servername=$(echo "$servername" | sed -e "s/[.]*$//g") + servername=$(echo "$servername" | sed -e "s/^[.]*//") + if [[ $(echo "$servername" | grep -o "\." | wc -l) -gt 1 ]] && [[ ! $servername =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Hostname valid + return 1 + else + # Hostname invalid + return 0 + fi +} + +validate_email() { + if [[ ! "$email" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]]; then + # Email invalid + return 0 + else + # Email valid + return 1 + fi +} + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +# Creating temporary file +tmpfile=$(mktemp -p /tmp) + +# Translating argument to --gnu-long-options +for arg; do + delim="" + case "$arg" in + --apache) args="${args}-a " ;; + --phpfpm) args="${args}-w " ;; + --vsftpd) args="${args}-v " ;; + --proftpd) args="${args}-j " ;; + --named) args="${args}-k " ;; + --mysql) args="${args}-m " ;; + --mysql-classic) args="${args}-M " ;; + --postgresql) args="${args}-g " ;; + --exim) args="${args}-x " ;; + --dovecot) args="${args}-z " ;; + --sieve) args="${args}-Z " ;; + --clamav) args="${args}-c " ;; + --spamassassin) args="${args}-t " ;; + --iptables) args="${args}-i " ;; + --fail2ban) args="${args}-b " ;; + --multiphp) args="${args}-o " ;; + --quota) args="${args}-q " ;; + --port) args="${args}-r " ;; + --lang) args="${args}-l " ;; + --interactive) args="${args}-y " ;; + --api) args="${args}-d " ;; + --hostname) args="${args}-s " ;; + --email) args="${args}-e " ;; + --password) args="${args}-p " ;; + --force) args="${args}-f " ;; + --with-debs) args="${args}-D " ;; + --help) args="${args}-h " ;; + *) + [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} " + ;; + esac +done +eval set -- "$args" + +# Parsing arguments +while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:e:p:D:fh" Option; do + case $Option in + a) apache=$OPTARG ;; # Apache + w) phpfpm=$OPTARG ;; # PHP-FPM + o) multiphp=$OPTARG ;; # Multi-PHP + v) vsftpd=$OPTARG ;; # Vsftpd + j) proftpd=$OPTARG ;; # Proftpd + k) named=$OPTARG ;; # Named + m) mysql=$OPTARG ;; # MariaDB + M) mysqlclassic=$OPTARG ;; # MySQL + g) postgresql=$OPTARG ;; # PostgreSQL + x) exim=$OPTARG ;; # Exim + z) dovecot=$OPTARG ;; # Dovecot + Z) sieve=$OPTARG ;; # Sieve + c) clamd=$OPTARG ;; # ClamAV + t) spamd=$OPTARG ;; # SpamAssassin + i) iptables=$OPTARG ;; # Iptables + b) fail2ban=$OPTARG ;; # Fail2ban + q) quota=$OPTARG ;; # FS Quota + r) port=$OPTARG ;; # Backend Port + l) lang=$OPTARG ;; # Language + d) api=$OPTARG ;; # Activate API + y) interactive=$OPTARG ;; # Interactive install + s) servername=$OPTARG ;; # Hostname + e) email=$OPTARG ;; # Admin email + p) vpass=$OPTARG ;; # Admin password + D) withdebs=$OPTARG ;; # Hestia debs path + f) force='yes' ;; # Force install + h) help ;; # Help + *) help ;; # Print help (default) + esac +done + +# Defining default software stack +set_default_value 'nginx' 'yes' +set_default_value 'apache' 'yes' +set_default_value 'phpfpm' 'yes' +set_default_value 'multiphp' 'no' +set_default_value 'vsftpd' 'yes' +set_default_value 'proftpd' 'no' +set_default_value 'named' 'yes' +set_default_value 'mysql' 'yes' +set_default_value 'mysqlclassic' 'no' +set_default_value 'postgresql' 'no' +set_default_value 'exim' 'yes' +set_default_value 'dovecot' 'yes' +set_default_value 'sieve' 'no' +if [ $memory -lt 1500000 ]; then + set_default_value 'clamd' 'no' + set_default_value 'spamd' 'no' +elif [ $memory -lt 3000000 ]; then + set_default_value 'clamd' 'no' + set_default_value 'spamd' 'yes' +else + set_default_value 'clamd' 'yes' + set_default_value 'spamd' 'yes' +fi +set_default_value 'iptables' 'yes' +set_default_value 'fail2ban' 'yes' +set_default_value 'quota' 'no' +set_default_value 'interactive' 'yes' +set_default_value 'ipv6' 'no' +set_default_value 'api' 'yes' +set_default_port '8083' +set_default_lang 'en' + +# Checking software conflicts +if [ "$proftpd" = 'yes' ]; then + vsftpd='no' +fi +if [ "$exim" = 'no' ]; then + clamd='no' + spamd='no' + dovecot='no' + sieve='no' +fi +if [ "$dovecot" = 'no' ]; then + sieve='no' +fi +if [ "$iptables" = 'no' ]; then + fail2ban='no' +fi +if [ "$apache" = "no" ]; then + phpfpm='yes' +fi +if [ "$mysql" = 'yes' ] && [ "$mysqlclassic" = 'yes' ]; then + mysql='no' +fi + +# Checking root permissions +if [ "x$(id -u)" != 'x0' ]; then + check_result 1 "Script can be run executed only by root" +fi + +if [ -d "/usr/local/hestia" ]; then + check_result 1 "Hestia install detected. Unable to continue" +fi + +# Checking admin user account +if [ -n "$(grep ^admin: /etc/passwd /etc/group)" ] && [ -z "$force" ]; then + echo 'Please remove admin user account before proceeding.' + echo 'If you want to do it automatically run installer with -f option:' + echo -e "Example: bash $0 --force\n" + check_result 1 "User admin exists" +fi + +# Clear the screen once launch permissions have been verified +clear + +# Configure apt to retry downloading on error +if [ ! -f /etc/apt/apt.conf.d/80-retries ]; then + echo "APT::Acquire::Retries \"3\";" > /etc/apt/apt.conf.d/80-retries +fi + +# Welcome message +echo "Welcome to the Hestia Control Panel installer!" +echo +echo "Please wait, the installer is now checking for missing dependencies..." +echo + +# Update apt repository +apt-get -qq update + +# Creating backup directory +mkdir -p "$hst_backups" + +# Pre-install packages +echo "[ * ] Installing dependencies..." +apt-get -y install $installer_dependencies >> $LOG +check_result $? "Package installation failed, check log file for more details." + +# Check repository availability +wget --quiet "https://$GPG/deb_signing.key" -O /dev/null +check_result $? "Unable to connect to the Hestia APT repository" + +# Check installed packages +tmpfile=$(mktemp -p /tmp) +dpkg --get-selections > $tmpfile +conflicts_pkg="exim4 mariadb-server apache2 nginx hestia postfix ufw" + +# Drop postfix from the list if exim should not be installed +if [ "$exim" = 'no' ]; then + conflicts_pkg=$(echo $conflicts_pkg | sed 's/postfix//g' | xargs) +fi + +for pkg in $conflicts_pkg; do + if [ -n "$(grep $pkg $tmpfile)" ]; then + conflicts="$pkg* $conflicts" + fi +done +rm -f $tmpfile +if [ -n "$conflicts" ] && [ -z "$force" ]; then + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + echo 'WARNING: The following packages are already installed' + echo "$conflicts" + echo + echo 'It is highly recommended that you remove them before proceeding.' + echo + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + read -p 'Would you like to remove the conflicting packages? [y/n] ' answer + if [ "$answer" = 'y' ] || [ "$answer" = 'Y' ]; then + apt-get -qq purge $conflicts -y + check_result $? 'apt-get remove failed' + unset $answer + else + check_result 1 "Hestia Control Panel should be installed on a clean server." + fi +fi + +# Check network configuration +if [ -d /etc/netplan ] && [ -z "$force" ]; then + if [ -z "$(ls -A /etc/netplan)" ]; then + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + echo 'WARNING: Your network configuration may not be set up correctly.' + echo 'Details: The netplan configuration directory is empty.' + echo '' + echo 'You may have a network configuration file that was created using' + echo 'systemd-networkd.' + echo '' + echo 'It is strongly recommended to migrate to netplan, which is now the' + echo 'default network configuration system in newer releases of Ubuntu.' + echo '' + echo 'While you can leave your configuration as-is, please note that you' + echo 'will not be able to use additional IPs properly.' + echo '' + echo 'If you wish to continue and force the installation,' + echo 'run this script with -f option:' + echo "Example: bash $0 --force" + echo + echo '!!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!' + echo + check_result 1 "Unable to detect netplan configuration." + fi +fi + +# Validate whether installation script matches release version before continuing with install +if [ -z "$withdebs" ] || [ ! -d "$withdebs" ]; then + release_branch_ver=$(curl -s https://raw.githubusercontent.com/hestiacp/hestiacp/release/src/deb/hestia/control | grep "Version:" | awk '{print $2}') + if [ "$HESTIA_INSTALL_VER" != "$release_branch_ver" ]; then + echo + echo -e "\e[91mInstallation aborted\e[0m" + echo "====================================================================" + echo -e "\e[33mERROR: Install script version does not match package version!\e[0m" + echo -e "\e[33mPlease download the installer from the release branch in order to continue:\e[0m" + echo "" + echo -e "\e[33mhttps://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh\e[0m" + echo "" + echo -e "\e[33mTo test pre-release versions, build the .deb packages and re-run the installer:\e[0m" + echo -e " \e[33m./hst_autocompile.sh \e[1m--hestia branchname no\e[21m\e[0m" + echo -e " \e[33m./hst-install.sh .. \e[1m--with-debs /tmp/hestiacp-src/debs\e[21m\e[0m" + echo "" + check_result 1 "Installation aborted" + fi +fi + +case $architecture in + x86_64) + ARCH="amd64" + ;; + aarch64) + ARCH="arm64" + ;; + *) + echo + echo -e "\e[91mInstallation aborted\e[0m" + echo "====================================================================" + echo -e "\e[33mERROR: $architecture is currently not supported!\e[0m" + echo -e "\e[33mPlease verify the achitecture used is currenlty supported\e[0m" + echo "" + echo -e "\e[33mhttps://github.com/hestiacp/hestiacp/blob/main/README.md\e[0m" + echo "" + check_result 1 "Installation aborted" + ;; +esac +#----------------------------------------------------------# +# Brief Info # +#----------------------------------------------------------# + +install_welcome_message() { + DISPLAY_VER=$(echo $HESTIA_INSTALL_VER | sed "s|~alpha||g" | sed "s|~beta||g") + echo + echo ' _ _ _ _ ____ ____ ' + echo ' | | | | ___ ___| |_(_) __ _ / ___| _ \ ' + echo ' | |_| |/ _ \/ __| __| |/ _` | | | |_) | ' + echo ' | _ | __/\__ \ |_| | (_| | |___| __/ ' + echo ' |_| |_|\___||___/\__|_|\__,_|\____|_| ' + echo " " + echo " Hestia Control Panel " + if [[ "$HESTIA_INSTALL_VER" =~ "beta" ]]; then + echo " BETA RELEASE " + fi + if [[ "$HESTIA_INSTALL_VER" =~ "alpha" ]]; then + echo " DEVELOPMENT SNAPSHOT " + echo " NOT INTENDED FOR PRODUCTION USE " + echo " USE AT YOUR OWN RISK " + fi + echo " ${DISPLAY_VER} " + echo " www.hestiacp.com " + echo + echo "========================================================================" + echo + echo "Thank you for downloading Hestia Control Panel! In a few moments," + echo "we will begin installing the following components on your server:" + echo +} + +# Printing nice ASCII logo +clear +install_welcome_message + +# Web stack +echo ' - NGINX Web / Proxy Server' +if [ "$apache" = 'yes' ]; then + echo ' - Apache Web Server (as backend)' +fi +if [ "$phpfpm" = 'yes' ] && [ "$multiphp" = 'no' ]; then + echo ' - PHP-FPM Application Server' +fi +if [ "$multiphp" = 'yes' ]; then + phpfpm='yes' + echo ' - Multi-PHP Environment' +fi + +# DNS stack +if [ "$named" = 'yes' ]; then + echo ' - Bind DNS Server' +fi + +# Mail stack +if [ "$exim" = 'yes' ]; then + echo -n ' - Exim Mail Server' + if [ "$clamd" = 'yes' ] || [ "$spamd" = 'yes' ]; then + echo -n ' + ' + if [ "$clamd" = 'yes' ]; then + echo -n 'ClamAV ' + fi + if [ "$spamd" = 'yes' ]; then + if [ "$clamd" = 'yes' ]; then + echo -n '+ ' + fi + echo -n 'SpamAssassin' + fi + fi + echo + if [ "$dovecot" = 'yes' ]; then + echo -n ' - Dovecot POP3/IMAP Server' + if [ "$sieve" = 'yes' ]; then + echo -n '+ Sieve' + fi + fi +fi + +echo +# Database stack +if [ "$mysql" = 'yes' ]; then + echo ' - MariaDB Database Server' +fi +if [ "$mysqlclassic" = 'yes' ]; then + echo ' - MySQL8 Database Server' +fi +if [ "$postgresql" = 'yes' ]; then + echo ' - PostgreSQL Database Server' +fi + +# FTP stack +if [ "$vsftpd" = 'yes' ]; then + echo ' - Vsftpd FTP Server' +fi +if [ "$proftpd" = 'yes' ]; then + echo ' - ProFTPD FTP Server' +fi + +# Firewall stack +if [ "$iptables" = 'yes' ]; then + echo -n ' - Firewall (iptables)' +fi +if [ "$iptables" = 'yes' ] && [ "$fail2ban" = 'yes' ]; then + echo -n ' + Fail2Ban Access Monitor' +fi +echo -e "\n" +echo "========================================================================" +echo -e "\n" + +# Asking for confirmation to proceed +if [ "$interactive" = 'yes' ]; then + read -p 'Would you like to continue with the installation? [Y/N]: ' answer + if [ "$answer" != 'y' ] && [ "$answer" != 'Y' ]; then + echo 'Goodbye' + exit 1 + fi +fi + +# Validate Email / Hostname even when interactive = no +# Asking for contact email +if [ -z "$email" ]; then + while validate_email; do + echo -e "\nPlease use a valid emailadress (ex. info@domain.tld)." + read -p 'Please enter admin email address: ' email + done +else + if validate_email; then + echo "Please use a valid emailadress (ex. info@domain.tld)." + exit 1 + fi +fi + +# Asking to set FQDN hostname +if [ -z "$servername" ]; then + # Ask and validate FQDN hostname. + read -p "Please enter FQDN hostname [$(hostname -f)]: " servername + + # Set hostname if it wasn't set + if [ -z "$servername" ]; then + servername=$(hostname -f) + fi + + # Validate Hostname, go to loop if the validation fails. + while validate_hostname; do + echo -e "\nPlease use a valid hostname according to RFC1178 (ex. hostname.domain.tld)." + read -p "Please enter FQDN hostname [$(hostname -f)]: " servername + done +else + # Validate FQDN hostname if it is preset + if validate_hostname; then + echo "Please use a valid hostname according to RFC1178 (ex. hostname.domain.tld)." + exit 1 + fi +fi + +# Generating admin password if it wasn't set +displaypass="The password you chose during installation." +if [ -z "$vpass" ]; then + vpass=$(gen_pass) + displaypass=$vpass +fi + +# Set FQDN if it wasn't set +mask1='(([[:alnum:]](-?[[:alnum:]])*)\.)' +mask2='*[[:alnum:]](-?[[:alnum:]])+\.[[:alnum:]]{2,}' +if ! [[ "$servername" =~ ^${mask1}${mask2}$ ]]; then + if [[ -n "$servername" ]]; then + servername="$servername.example.com" + else + servername="example.com" + fi + echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi +fi + +if [[ -z $(grep -i "$servername" /etc/hosts) ]]; then + echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi +fi + +# Set email if it wasn't set +if [[ -z "$email" ]]; then + email="admin@$servername" +fi + +# Defining backup directory +echo -e "Installation backup directory: $hst_backups" + +# Print Log File Path +echo "Installation log file: $LOG" + +# Print new line +echo + +#----------------------------------------------------------# +# Checking swap # +#----------------------------------------------------------# + +# Checking swap on small instances +if [ -z "$(swapon -s)" ] && [ "$memory" -lt 1000000 ]; then + fallocate -l 1G /swapfile + chmod 600 /swapfile + mkswap /swapfile + swapon /swapfile + echo "/swapfile none swap sw 0 0" >> /etc/fstab +fi + +#----------------------------------------------------------# +# Install repository # +#----------------------------------------------------------# + +# Define apt conf location +apt=/etc/apt/sources.list.d + +# Create new folder if not all-ready exists +mkdir -p /root/.gnupg/ && chmod 700 /root/.gnupg/ + +# Updating system +echo "Adding required repositories to proceed with installation:" +echo + +# Installing Nginx repo + +echo "[ * ] NGINX" +echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://nginx.org/packages/mainline/$VERSION/ $codename nginx" > $apt/nginx.list +curl -s https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-keyring.gpg > /dev/null 2>&1 + +#add-apt-repository does not support it yet see #https://bugs.launchpad.net/ubuntu/+source/software-properties/+bug/1862764 +# Installing sury PHP repo +echo "[ * ] PHP" +LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php > /dev/null 2>&1 + +# Installing sury Apache2 repo +if [ "$apache" = 'yes' ]; then + echo "[ * ] Apache2" + echo "deb http://ppa.launchpad.net/ondrej/apache2/ubuntu $codename main" > $apt/apache2.list +fi + +# Installing MariaDB repo +if [ "$mysql" = 'yes' ]; then + echo "[ * ] MariaDB" + if [ "$release" != '22.04' ]; then + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/mariadb-keyring.gpg] https://dlm.mariadb.com/repo/mariadb-server/$mariadb_v/repo/$VERSION $codename main" > $apt/mariadb.list + else + echo "#deb [arch=$ARCH signed-by=/usr/share/keyrings/mariadb-keyring.gpg] https://dlm.mariadb.com/repo/mariadb-server/$mariadb_v/repo/$VERSION $codename main" > $apt/mariadb.list + fi + curl -s https://mariadb.org/mariadb_release_signing_key.asc | gpg --dearmor | tee /usr/share/keyrings/mariadb-keyring.gpg > /dev/null 2>&1 +fi + +# Installing HestiaCP repo +echo "[ * ] Hestia Control Panel" +echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/hestia-keyring.gpg] https://$RHOST/ $codename main" > $apt/hestia.list +gpg --no-default-keyring --keyring /usr/share/keyrings/hestia-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys A189E93654F0B0E5 > /dev/null 2>&1 + +# Installing PostgreSQL repo +if [ "$postgresql" = 'yes' ]; then + echo "[ * ] PostgreSQL" + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/postgresql-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/ $codename-pgdg main" > $apt/postgresql.list + curl -s https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/postgresql-keyring.gpg > /dev/null 2>&1 +fi + +# Echo for a new line +echo + +# Updating system +echo -ne "Updating currently installed packages, please wait... " +apt-get -qq update +apt-get -y upgrade >> $LOG & +BACK_PID=$! + +# Check if package installation is done, print a spinner +spin_i=1 +while kill -0 $BACK_PID > /dev/null 2>&1; do + printf "\b${spinner:spin_i++%${#spinner}:1}" + sleep 0.5 +done + +# Do a blank echo to get the \n back +echo + +# Check Installation result +wait $BACK_PID +check_result $? 'apt-get upgrade failed' + +#----------------------------------------------------------# +# Backup # +#----------------------------------------------------------# + +# Creating backup directory tree +mkdir -p $hst_backups +cd $hst_backups +mkdir nginx apache2 php vsftpd proftpd bind exim4 dovecot clamd +mkdir spamassassin mysql postgresql hestia + +# Backup nginx configuration +systemctl stop nginx > /dev/null 2>&1 +cp -r /etc/nginx/* $hst_backups/nginx > /dev/null 2>&1 + +# Backup Apache configuration +systemctl stop apache2 > /dev/null 2>&1 +cp -r /etc/apache2/* $hst_backups/apache2 > /dev/null 2>&1 +rm -f /etc/apache2/conf.d/* > /dev/null 2>&1 + +# Backup PHP-FPM configuration +systemctl stop php*-fpm > /dev/null 2>&1 +cp -r /etc/php/* $hst_backups/php/ > /dev/null 2>&1 + +# Backup Bind configuration +systemctl stop bind9 > /dev/null 2>&1 +cp -r /etc/bind/* $hst_backups/bind > /dev/null 2>&1 + +# Backup Vsftpd configuration +systemctl stop vsftpd > /dev/null 2>&1 +cp /etc/vsftpd.conf $hst_backups/vsftpd > /dev/null 2>&1 + +# Backup ProFTPD configuration +systemctl stop proftpd > /dev/null 2>&1 +cp /etc/proftpd/* $hst_backups/proftpd > /dev/null 2>&1 + +# Backup Exim configuration +systemctl stop exim4 > /dev/null 2>&1 +cp -r /etc/exim4/* $hst_backups/exim4 > /dev/null 2>&1 + +# Backup ClamAV configuration +systemctl stop clamav-daemon > /dev/null 2>&1 +cp -r /etc/clamav/* $hst_backups/clamav > /dev/null 2>&1 + +# Backup SpamAssassin configuration +systemctl stop spamassassin > /dev/null 2>&1 +cp -r /etc/spamassassin/* $hst_backups/spamassassin > /dev/null 2>&1 + +# Backup Dovecot configuration +systemctl stop dovecot > /dev/null 2>&1 +cp /etc/dovecot.conf $hst_backups/dovecot > /dev/null 2>&1 +cp -r /etc/dovecot/* $hst_backups/dovecot > /dev/null 2>&1 + +# Backup MySQL/MariaDB configuration and data +systemctl stop mysql > /dev/null 2>&1 +killall -9 mysqld > /dev/null 2>&1 +mv /var/lib/mysql $hst_backups/mysql/mysql_datadir > /dev/null 2>&1 +cp -r /etc/mysql/* $hst_backups/mysql > /dev/null 2>&1 +mv -f /root/.my.cnf $hst_backups/mysql > /dev/null 2>&1 + +# Backup Hestia +systemctl stop hestia > /dev/null 2>&1 +cp -r $HESTIA/* $hst_backups/hestia > /dev/null 2>&1 +apt-get -y purge hestia hestia-nginx hestia-php > /dev/null 2>&1 +rm -rf $HESTIA > /dev/null 2>&1 + +#----------------------------------------------------------# +# Package Includes # +#----------------------------------------------------------# + +if [ "$phpfpm" = 'yes' ]; then + fpm="php$fpm_v php$fpm_v-common php$fpm_v-bcmath php$fpm_v-cli + php$fpm_v-curl php$fpm_v-fpm php$fpm_v-gd php$fpm_v-intl + php$fpm_v-mysql php$fpm_v-soap php$fpm_v-xml php$fpm_v-zip + php$fpm_v-mbstring php$fpm_v-bz2 php$fpm_v-pspell + php$fpm_v-imagick" + software="$software $fpm" +fi + +#----------------------------------------------------------# +# Package Excludes # +#----------------------------------------------------------# + +# Excluding packages +software=$(echo "$software" | sed -e "s/apache2.2-common//") + +if [ "$apache" = 'no' ]; then + software=$(echo "$software" | sed -e "s/apache2 //") + software=$(echo "$software" | sed -e "s/apache2-bin//") + software=$(echo "$software" | sed -e "s/apache2-utils//") + software=$(echo "$software" | sed -e "s/apache2-suexec-custom//") + software=$(echo "$software" | sed -e "s/apache2.2-common//") + software=$(echo "$software" | sed -e "s/libapache2-mod-rpaf//") + software=$(echo "$software" | sed -e "s/libapache2-mod-fcgid//") + software=$(echo "$software" | sed -e "s/libapache2-mod-php$fpm_v//") +fi +if [ "$vsftpd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/vsftpd//") +fi +if [ "$proftpd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/proftpd-basic//") + software=$(echo "$software" | sed -e "s/proftpd-mod-vroot//") +fi +if [ "$named" = 'no' ]; then + software=$(echo "$software" | sed -e "s/bind9//") +fi +if [ "$exim" = 'no' ]; then + software=$(echo "$software" | sed -e "s/exim4 //") + software=$(echo "$software" | sed -e "s/exim4-daemon-heavy//") + software=$(echo "$software" | sed -e "s/dovecot-imapd//") + software=$(echo "$software" | sed -e "s/dovecot-pop3d//") + software=$(echo "$software" | sed -e "s/clamav-daemon//") + software=$(echo "$software" | sed -e "s/spamassassin//") + software=$(echo "$software" | sed -e "s/dovecot-sieve//") + software=$(echo "$software" | sed -e "s/dovecot-managesieved//") +fi +if [ "$clamd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/clamav-daemon//") +fi +if [ "$spamd" = 'no' ]; then + software=$(echo "$software" | sed -e "s/spamassassin//") +fi +if [ "$dovecot" = 'no' ]; then + software=$(echo "$software" | sed -e "s/dovecot-imapd//") + software=$(echo "$software" | sed -e "s/dovecot-pop3d//") +fi +if [ "$sieve" = 'no' ]; then + software=$(echo "$software" | sed -e "s/dovecot-sieve//") + software=$(echo "$software" | sed -e "s/dovecot-managesieved//") +fi +if [ "$mysql" = 'no' ]; then + software=$(echo "$software" | sed -e "s/mariadb-server//") + software=$(echo "$software" | sed -e "s/mariadb-client//") + software=$(echo "$software" | sed -e "s/mariadb-common//") +fi +if [ "$mysqlclassic" = 'no' ]; then + software=$(echo "$software" | sed -e "s/mysql-server//") + software=$(echo "$software" | sed -e "s/mysql-client//") + software=$(echo "$software" | sed -e "s/mysql-common//") +fi +if [ "$mysql" = 'no' ] && [ "$mysqlclassic" = 'no' ]; then + software=$(echo "$software" | sed -e "s/php$fpm_v-mysql//") + if [ "$multiphp" = 'yes' ]; then + for v in "${multiphp_v[@]}"; do + software=$(echo "$software" | sed -e "s/php$v-mysql//") + software=$(echo "$software" | sed -e "s/php$v-bz2//") + done + fi +fi +if [ "$postgresql" = 'no' ]; then + software=$(echo "$software" | sed -e "s/postgresql-contrib//") + software=$(echo "$software" | sed -e "s/postgresql//") + software=$(echo "$software" | sed -e "s/php$fpm_v-pgsql//") + if [ "$multiphp" = 'yes' ]; then + for v in "${multiphp_v[@]}"; do + software=$(echo "$software" | sed -e "s/php$v-pgsql//") + done + fi + software=$(echo "$software" | sed -e "s/phppgadmin//") +fi +if [ "$fail2ban" = 'no' ]; then + software=$(echo "$software" | sed -e "s/fail2ban//") +fi +if [ "$iptables" = 'no' ]; then + software=$(echo "$software" | sed -e "s/ipset//") + software=$(echo "$software" | sed -e "s/fail2ban//") +fi +if [ "$phpfpm" = 'yes' ]; then + software=$(echo "$software" | sed -e "s/php$fpm_v-cgi//") + software=$(echo "$software" | sed -e "s/libapache2-mod-ruid2//") + software=$(echo "$software" | sed -e "s/libapache2-mod-php$fpm_v//") +fi +if [ -d "$withdebs" ]; then + software=$(echo "$software" | sed -e "s/hestia-nginx//") + software=$(echo "$software" | sed -e "s/hestia-php//") + software=$(echo "$software" | sed -e "s/hestia=${HESTIA_INSTALL_VER}//") +fi +if [ "$release" = '18.04' ]; then + software=$(echo "$software" | sed -e "s/libonig5/libonig4/") + software=$(echo "$software" | sed -e "s/libzip5/libzip4/") +fi +if [ "$release" = '20.04' ]; then + software=$(echo "$software" | sed -e "s/setpriv/util-linux/") +fi +if [ "$release" = '22.04' ]; then + software=$(echo "$software" | sed -e "s/setpriv/util-linux/") + software=$(echo "$software" | sed -e "s/libzip5/libzip4/") +fi + +#----------------------------------------------------------# +# Disable Apparmor on LXC # +#----------------------------------------------------------# + +if grep --quiet lxc /proc/1/environ; then + if [ -f /etc/init.d/apparmor ]; then + systemctl stop apparmor > /dev/null 2>&1 + systemctl disable apparmor > /dev/null 2>&1 + fi +fi + +#----------------------------------------------------------# +# Install packages # +#----------------------------------------------------------# + +# Disabling daemon autostart on apt-get install +echo -e '#!/bin/sh\nexit 101' > /usr/sbin/policy-rc.d +chmod a+x /usr/sbin/policy-rc.d + +# Installing apt packages +echo "The installer is now downloading and installing all required packages." +echo -ne "NOTE: This process may take 10 to 15 minutes to complete, please wait... " +echo +apt-get -y install $software > $LOG +BACK_PID=$! + +# Check if package installation is done, print a spinner +spin_i=1 +while kill -0 $BACK_PID > /dev/null 2>&1; do + printf "\b${spinner:spin_i++%${#spinner}:1}" + sleep 0.5 +done + +# Do a blank echo to get the \n back +echo + +# Check Installation result +wait $BACK_PID +check_result $? "apt-get install failed" + +echo +echo "========================================================================" +echo + +# Install Hestia packages from local folder +if [ -n "$withdebs" ] && [ -d "$withdebs" ]; then + echo "[ * ] Installing local package files..." + echo " - hestia core package" + dpkg -i $withdebs/hestia_*.deb > /dev/null 2>&1 + + if [ -z $(ls $withdebs/hestia-php_*.deb 2> /dev/null) ]; then + echo " - hestia-php backend package (from apt)" + apt-get -y install hestia-php > /dev/null 2>&1 + else + echo " - hestia-php backend package" + dpkg -i $withdebs/hestia-php_*.deb > /dev/null 2>&1 + fi + + if [ -z $(ls $withdebs/hestia-nginx_*.deb 2> /dev/null) ]; then + echo " - hestia-nginx backend package (from apt)" + apt-get -y install hestia-nginx > /dev/null 2>&1 + else + echo " - hestia-nginx backend package" + dpkg -i $withdebs/hestia-nginx_*.deb > /dev/null 2>&1 + fi +fi + +# Restoring autostart policy +rm -f /usr/sbin/policy-rc.d + +#----------------------------------------------------------# +# Configure system # +#----------------------------------------------------------# + +echo "[ * ] Configuring system settings..." + +# Enable SFTP subsystem for SSH +sftp_subsys_enabled=$(grep -iE "^#?.*subsystem.+(sftp )?sftp-server" /etc/ssh/sshd_config) +if [ -n "$sftp_subsys_enabled" ]; then + sed -i -E "s/^#?.*Subsystem.+(sftp )?sftp-server/Subsystem sftp internal-sftp/g" /etc/ssh/sshd_config +fi + +# Reduce SSH login grace time +sed -i "s/[#]LoginGraceTime [[:digit:]]m/LoginGraceTime 1m/g" /etc/ssh/sshd_config + +# Disable SSH suffix broadcast +if [ -z "$(grep "^DebianBanner no" /etc/ssh/sshd_config)" ]; then + sed -i '/^[#]Banner .*/a DebianBanner no' /etc/ssh/sshd_config + if [ -z "$(grep "^DebianBanner no" /etc/ssh/sshd_config)" ]; then + # If first attempt fails just add it + echo '' >> /etc/ssh/sshd_config + echo 'DebianBanner no' >> /etc/ssh/sshd_config + fi +fi + +# Restart SSH daemon +systemctl restart ssh + +# Disable AWStats cron +rm -f /etc/cron.d/awstats + +# Set directory color +if [ -z "$(grep 'LS_COLORS="$LS_COLORS:di=00;33"' /etc/profile)" ]; then + echo 'LS_COLORS="$LS_COLORS:di=00;33"' >> /etc/profile +fi + +# Registering /usr/sbin/nologin +if [ -z "$(grep nologin /etc/shells)" ]; then + echo "/usr/sbin/nologin" >> /etc/shells +fi + +# Configuring NTP +sed -i 's/#NTP=/NTP=pool.ntp.org/' /etc/systemd/timesyncd.conf +systemctl enable systemd-timesyncd +systemctl start systemd-timesyncd + +# Check iptables paths and add symlinks when necessary +if [ ! -e "/sbin/iptables" ]; then + if which iptables > /dev/null; then + ln -s "$(which iptables)" /sbin/iptables + elif [ -e "/usr/sbin/iptables" ]; then + ln -s /usr/sbin/iptables /sbin/iptables + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables; then + autoiptables=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables | cut -d '' -f 2) + if [ -x "$autoiptables" ]; then + ln -s "$autoiptables" /sbin/iptables + fi + fi + + if [ "$ipv6" = 'yes' ]; then + if which ip6tables; then + ln -s "$(which ip6tables)" /sbin/ip6tables + elif [ -e "/usr/sbin/ip6tables" ]; then + ln -s /usr/sbin/ip6tables /sbin/ip6tables + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables; then + autoip6tables=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables | cut -d '' -f 2) + if [ -x "$autoip6tables" ]; then + ln -s "$autoip6tables" /sbin/ip6tables + fi + fi + fi +fi + +if [ ! -e "/sbin/iptables-save" ]; then + if which iptables-save > /dev/null; then + ln -s "$(which iptables-save)" /sbin/iptables-save + elif [ -e "/usr/sbin/iptables-save" ]; then + ln -s /usr/sbin/iptables-save /sbin/iptables-save + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-save; then + autoiptables_save=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-save | cut -d '' -f 2) + if [ -x "$autoiptables_save" ]; then + ln -s "$autoiptables_save" /sbin/iptables-save + fi + fi + if [ "$ipv6" = 'yes' ]; then + if which ip6tables-save; then + ln -s "$(which ip6tables-save)" /sbin/ip6tables-save + elif [ -e "/usr/sbin/ip6tables-save" ]; then + ln -s /usr/sbin/ip6tables-save /sbin/ip6tables-save + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-save; then + autoip6tables_save=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-save | cut -d '' -f 2) + if [ -x "$autoip6tables_save" ]; then + ln -s "$autoip6tables_save" /sbin/ip6tables-save + fi + fi + fi +fi + +if [ ! -e "/sbin/iptables-restore" ]; then + if which iptables-restore > /dev/null; then + ln -s "$(which iptables-restore)" /sbin/iptables-restore + elif [ -e "/usr/sbin/iptables-restore" ]; then + ln -s /usr/sbin/iptables-restore /sbin/iptables-restore + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-restore; then + autoiptables_restore=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-restore | cut -d '' -f 2) + if [ -x "$autoiptables_restore" ]; then + ln -s "$autoiptables_restore" /sbin/iptables-restore + fi + fi + if [ "$ipv6" = 'yes' ]; then + if which ip6tables-restore; then + ln -s "$(which ip6tables-restore)" /sbin/ip6tables-restore + elif [ -e "/usr/sbin/ip6tables-restore" ]; then + ln -s /usr/sbin/ip6tables-restore /sbin/ip6tables-restore + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-restore; then + autoip6tables_restore=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-restore | cut -d '' -f 2) + if [ -x "$autoip6tables_restore" ]; then + ln -s "$autoip6tables_restore" /sbin/ip6tables-restore + fi + fi + fi +fi + +# Restrict access to /proc fs +# - Prevent unpriv users from seeing each other running processes +mount -o remount,defaults,hidepid=2 /proc > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Info: Cannot remount /proc (LXC containers require additional perm added to host apparmor profile)" +else + echo "@reboot root sleep 5 && mount -o remount,defaults,hidepid=2 /proc" > /etc/cron.d/hestia-proc +fi + +#----------------------------------------------------------# +# Configure Hestia # +#----------------------------------------------------------# + +echo "[ * ] Configuring Hestia Control Panel..." +# Installing sudo configuration +mkdir -p /etc/sudoers.d +cp -f $HESTIA_INSTALL_DIR/sudo/admin /etc/sudoers.d/ +chmod 440 /etc/sudoers.d/admin + +# Add Hestia global config +if [[ ! -e /etc/hestiacp/hestia.conf ]]; then + mkdir -p /etc/hestiacp + echo -e "# Do not edit this file, will get overwritten on next upgrade, use /etc/hestiacp/local.conf instead\n\nexport HESTIA='/usr/local/hestia'\n\n[[ -f /etc/hestiacp/local.conf ]] && source /etc/hestiacp/local.conf" > /etc/hestiacp/hestia.conf +fi + +# Configuring system env +echo "export HESTIA='$HESTIA'" > /etc/profile.d/hestia.sh +echo 'PATH=$PATH:'$HESTIA'/bin' >> /etc/profile.d/hestia.sh +echo 'export PATH' >> /etc/profile.d/hestia.sh +chmod 755 /etc/profile.d/hestia.sh +source /etc/profile.d/hestia.sh + +# Configuring logrotate for Hestia logs +cp -f $HESTIA_INSTALL_DIR/logrotate/hestia /etc/logrotate.d/hestia + +# Create log path and symbolic link +rm -f /var/log/hestia +mkdir -p /var/log/hestia +ln -s /var/log/hestia $HESTIA/log + +# Building directory tree and creating some blank files for Hestia +mkdir -p $HESTIA/conf $HESTIA/ssl $HESTIA/data/ips \ + $HESTIA/data/queue $HESTIA/data/users $HESTIA/data/firewall \ + $HESTIA/data/sessions +touch $HESTIA/data/queue/backup.pipe $HESTIA/data/queue/disk.pipe \ + $HESTIA/data/queue/webstats.pipe $HESTIA/data/queue/restart.pipe \ + $HESTIA/data/queue/traffic.pipe $HESTIA/data/queue/daily.pipe $HESTIA/log/system.log \ + $HESTIA/log/nginx-error.log $HESTIA/log/auth.log $HESTIA/log/backup.log +chmod 750 $HESTIA/conf $HESTIA/data/users $HESTIA/data/ips $HESTIA/log +chmod -R 750 $HESTIA/data/queue +chmod 660 /var/log/hestia/* +chmod 770 $HESTIA/data/sessions + +# Generating Hestia configuration +rm -f $HESTIA/conf/hestia.conf > /dev/null 2>&1 +touch $HESTIA/conf/hestia.conf +chmod 660 $HESTIA/conf/hestia.conf + +# Write default port value to hestia.conf +# If a custom port is specified it will be set at the end of the installation process. +write_config_value "BACKEND_PORT" "8083" + +# Web stack +if [ "$apache" = 'yes' ]; then + write_config_value "WEB_SYSTEM" "apache2" + write_config_value "WEB_RGROUPS" "www-data" + write_config_value "WEB_PORT" "8080" + write_config_value "WEB_SSL_PORT" "8443" + write_config_value "WEB_SSL" "mod_ssl" + write_config_value "PROXY_SYSTEM" "nginx" + write_config_value "PROXY_PORT" "80" + write_config_value "PROXY_SSL_PORT" "443" + write_config_value "STATS_SYSTEM" "awstats" +fi + +if [ "$apache" = 'no' ]; then + write_config_value "WEB_SYSTEM" "nginx" + write_config_value "WEB_PORT" "80" + write_config_value "WEB_SSL_PORT" "443" + write_config_value "WEB_SSL" "openssl" + write_config_value "STATS_SYSTEM" "awstats" +fi + +if [ "$phpfpm" = 'yes' ] || [ "$multiphp" = 'yes' ]; then + write_config_value "WEB_BACKEND" "php-fpm" +fi + +# Database stack +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + installed_db_types='mysql' +fi + +if [ "$postgresql" = 'yes' ]; then + installed_db_types="$installed_db_types,pgsql" +fi + +if [ -n "$installed_db_types" ]; then + db=$(echo "$installed_db_types" \ + | sed "s/,/\n/g" \ + | sort -r -u \ + | sed "/^$/d" \ + | sed ':a;N;$!ba;s/\n/,/g') + write_config_value "DB_SYSTEM" "$db" +fi + +# FTP stack +if [ "$vsftpd" = 'yes' ]; then + write_config_value "FTP_SYSTEM" "vsftpd" +fi +if [ "$proftpd" = 'yes' ]; then + write_config_value "FTP_SYSTEM" "proftpd" +fi + +# DNS stack +if [ "$named" = 'yes' ]; then + write_config_value "DNS_SYSTEM" "bind9" +fi + +# Mail stack +if [ "$exim" = 'yes' ]; then + write_config_value "MAIL_SYSTEM" "exim4" + if [ "$clamd" = 'yes' ]; then + write_config_value "ANTIVIRUS_SYSTEM" "clamav-daemon" + fi + if [ "$spamd" = 'yes' ]; then + write_config_value "ANTISPAM_SYSTEM" "spamassassin" + fi + if [ "$dovecot" = 'yes' ]; then + write_config_value "IMAP_SYSTEM" "dovecot" + fi + if [ "$sieve" = 'yes' ]; then + write_config_value "SIEVE_SYSTEM" "yes" + fi +fi + +# Cron daemon +write_config_value "CRON_SYSTEM" "cron" + +# Firewall stack +if [ "$iptables" = 'yes' ]; then + write_config_value "FIREWALL_SYSTEM" "iptables" +fi +if [ "$iptables" = 'yes' ] && [ "$fail2ban" = 'yes' ]; then + write_config_value "FIREWALL_EXTENSION" "fail2ban" +fi + +# Disk quota +if [ "$quota" = 'yes' ]; then + write_config_value "DISK_QUOTA" "yes" +else + write_config_value "DISK_QUOTA" "no" +fi + +# Backups +write_config_value "BACKUP_SYSTEM" "local" +write_config_value "BACKUP_GZIP" "4" +write_config_value "BACKUP_MODE" "zstd" + +# Language +write_config_value "LANGUAGE" "$lang" + +# Login in screen +write_config_value "LOGIN_STYLE" "default" + +# Theme +write_config_value "THEME" "dark" + +# Inactive session timeout +write_config_value "INACTIVE_SESSION_TIMEOUT" "60" + +# Version & Release Branch +write_config_value "VERSION" "${HESTIA_INSTALL_VER}" +write_config_value "RELEASE_BRANCH" "release" + +# Email notifications after upgrade +write_config_value "UPGRADE_SEND_EMAIL" "true" +write_config_value "UPGRADE_SEND_EMAIL_LOG" "false" + +# Installing hosting packages +cp -rf $HESTIA_COMMON_DIR/packages $HESTIA/data/ + +# Update nameservers in hosting package +IFS='.' read -r -a domain_elements <<< "$servername" +if [ -n "${domain_elements[-2]}" ] && [ -n "${domain_elements[-1]}" ]; then + serverdomain="${domain_elements[-2]}.${domain_elements[-1]}" + sed -i s/"domain.tld"/"$serverdomain"/g $HESTIA/data/packages/*.pkg +fi + +# Installing templates +cp -rf $HESTIA_INSTALL_DIR/templates $HESTIA/data/ +cp -rf $HESTIA_COMMON_DIR/templates/web/ $HESTIA/data/templates +cp -rf $HESTIA_COMMON_DIR/templates/dns/ $HESTIA/data/templates + +mkdir -p /var/www/html +mkdir -p /var/www/document_errors + +# Install default success page +cp -rf $HESTIA_COMMON_DIR/templates/web/unassigned/index.html /var/www/html/ +cp -rf $HESTIA_COMMON_DIR/templates/web/skel/document_errors/* /var/www/document_errors/ + +# Installing firewall rules +cp -rf $HESTIA_COMMON_DIR/firewall $HESTIA/data/ + +# Installing apis +cp -rf $HESTIA_COMMON_DIR/api $HESTIA/data/ + +# Configuring server hostname +$HESTIA/bin/v-change-sys-hostname $servername > /dev/null 2>&1 + +# Generating SSL certificate +echo "[ * ] Generating default self-signed SSL certificate..." +if [ "$release" = "18.04" ]; then + $HESTIA/bin/v-generate-ssl-cert $(hostname) $email 'US' 'California' \ + 'San Francisco' 'Hestia Control Panel' 'IT' > /tmp/hst.pem +else + $HESTIA/bin/v-generate-ssl-cert $(hostname) '' 'US' 'California' \ + 'San Francisco' 'Hestia Control Panel' 'IT' > /tmp/hst.pem +fi +# Parsing certificate file +crt_end=$(grep -n "END CERTIFICATE-" /tmp/hst.pem | cut -f 1 -d:) +if [ "$release" = "22.04" ]; then + key_start=$(grep -n "BEGIN PRIVATE KEY" /tmp/hst.pem | cut -f 1 -d:) + key_end=$(grep -n "END PRIVATE KEY" /tmp/hst.pem | cut -f 1 -d:) +else + key_start=$(grep -n "BEGIN RSA" /tmp/hst.pem | cut -f 1 -d:) + key_end=$(grep -n "END RSA" /tmp/hst.pem | cut -f 1 -d:) +fi + +# Adding SSL certificate +echo "[ * ] Adding SSL certificate to Hestia Control Panel..." +cd $HESTIA/ssl +sed -n "1,${crt_end}p" /tmp/hst.pem > certificate.crt +sed -n "$key_start,${key_end}p" /tmp/hst.pem > certificate.key +chown root:mail $HESTIA/ssl/* +chmod 660 $HESTIA/ssl/* +rm /tmp/hst.pem + +# Install dhparam.pem +cp -f $HESTIA_INSTALL_DIR/ssl/dhparam.pem /etc/ssl + +# Deleting old admin user +if [ -n "$(grep ^admin: /etc/passwd)" ] && [ "$force" = 'yes' ]; then + chattr -i /home/admin/conf > /dev/null 2>&1 + userdel -f admin > /dev/null 2>&1 + chattr -i /home/admin/conf > /dev/null 2>&1 + mv -f /home/admin $hst_backups/home/ > /dev/null 2>&1 + rm -f /tmp/sess_* > /dev/null 2>&1 +fi +if [ -n "$(grep ^admin: /etc/group)" ] && [ "$force" = 'yes' ]; then + groupdel admin > /dev/null 2>&1 +fi +# Remove sudo "default" sudo permission admin user group should not exists any way +sed -i "s/%admin ALL=(ALL) ALL/#%admin ALL=(ALL) ALL/g" /etc/sudoers + +# Enable sftp jail +echo "[ * ] Enable SFTP jail..." +$HESTIA/bin/v-add-sys-sftp-jail > /dev/null 2>&1 +check_result $? "can't enable sftp jail" + +# Adding Hestia admin account +echo "[ * ] Create admin account..." +$HESTIA/bin/v-add-user admin $vpass $email "system" "System Administrator" +check_result $? "can't create admin user" +$HESTIA/bin/v-change-user-shell admin nologin +$HESTIA/bin/v-change-user-role admin admin +$HESTIA/bin/v-change-user-language admin $lang +$HESTIA/bin/v-change-sys-config-value 'POLICY_SYSTEM_PROTECTED_ADMIN' 'yes' + +locale-gen "en_US.utf8" > /dev/null 2>&1 + +#----------------------------------------------------------# +# Configure Nginx # +#----------------------------------------------------------# + +echo "[ * ] Configuring NGINX..." +rm -f /etc/nginx/conf.d/*.conf +cp -f $HESTIA_INSTALL_DIR/nginx/nginx.conf /etc/nginx/ +cp -f $HESTIA_INSTALL_DIR/nginx/status.conf /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/nginx/agents.conf /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/ +cp -f $HESTIA_INSTALL_DIR/logrotate/nginx /etc/logrotate.d/ +if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/nginx/nginx-ipv6.conf /etc/nginx/nginx.conf + cp -f $HESTIA_INSTALL_DIR/nginx/status-ipv6.conf /etc/nginx/conf.d/status.conf +fi +mkdir -p /etc/nginx/conf.d/domains +mkdir -p /etc/nginx/modules-enabled +mkdir -p /var/log/nginx/domains + +# Update dns servers in nginx.conf +dns_resolver=$(cat /etc/resolv.conf | grep -i '^nameserver' | cut -d ' ' -f2 | tr '\r\n' ' ' | xargs) +for ip in $dns_resolver; do + if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + resolver="$ip $resolver" + fi + if [ "$ipv6" = 'yes' ]; then + if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + resolver="[$ip] $resolver" + fi + fi +done +if [ -n "$resolver" ]; then + if [ "$ipv6" = 'yes' ]; then + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/ + else + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + fi +fi + +update-rc.d nginx defaults > /dev/null 2>&1 +systemctl start nginx >> $LOG +check_result $? "nginx start failed" + +#----------------------------------------------------------# +# Configure Apache # +#----------------------------------------------------------# + +if [ "$apache" = 'yes' ]; then + echo "[ * ] Configuring Apache Web Server..." + + mkdir -p /etc/apache2/conf.d + mkdir -p /etc/apache2/conf.d/domains + + # Copy configuration files + cp -f $HESTIA_INSTALL_DIR/apache2/apache2.conf /etc/apache2/ + cp -f $HESTIA_INSTALL_DIR/apache2/status.conf /etc/apache2/mods-available/hestia-status.conf + cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load + cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ + + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf + fi + + # Enable needed modules + a2enmod rewrite > /dev/null 2>&1 + a2enmod suexec > /dev/null 2>&1 + a2enmod ssl > /dev/null 2>&1 + a2enmod actions > /dev/null 2>&1 + a2dismod --quiet status > /dev/null 2>&1 + a2enmod --quiet hestia-status > /dev/null 2>&1 + + # Enable mod_ruid/mpm_itk or mpm_event + if [ "$phpfpm" = 'yes' ]; then + # Disable prefork and php, enable event + a2dismod php$fpm_v > /dev/null 2>&1 + a2dismod mpm_prefork > /dev/null 2>&1 + a2enmod mpm_event > /dev/null 2>&1 + cp -f $HESTIA_INSTALL_DIR/apache2/hestia-event.conf /etc/apache2/conf.d/ + else + a2enmod ruid2 > /dev/null 2>&1 + fi + + echo "# Powered by hestia" > /etc/apache2/sites-available/default + echo "# Powered by hestia" > /etc/apache2/sites-available/default-ssl + echo "# Powered by hestia" > /etc/apache2/ports.conf + echo -e "/home\npublic_html/cgi-bin" > /etc/apache2/suexec/www-data + touch /var/log/apache2/access.log /var/log/apache2/error.log + mkdir -p /var/log/apache2/domains + chmod a+x /var/log/apache2 + chmod 640 /var/log/apache2/access.log /var/log/apache2/error.log + chmod 751 /var/log/apache2/domains + + # Prevent remote access to server-status page + sed -i '/Allow from all/d' /etc/apache2/mods-available/hestia-status.conf + + update-rc.d apache2 defaults > /dev/null 2>&1 + systemctl start apache2 >> $LOG + check_result $? "apache2 start failed" +else + update-rc.d apache2 disable > /dev/null 2>&1 + systemctl stop apache2 > /dev/null 2>&1 +fi + +#----------------------------------------------------------# +# Configure PHP-FPM # +#----------------------------------------------------------# + +if [ "$phpfpm" = "yes" ]; then + if [ "$multiphp" = 'yes' ]; then + for v in "${multiphp_v[@]}"; do + echo "[ * ] Install PHP $v..." + $HESTIA/bin/v-add-web-php "$v" > /dev/null 2>&1 + done + else + echo "[ * ] Install PHP $fpm_v..." + $HESTIA/bin/v-add-web-php "$fpm_v" > /dev/null 2>&1 + fi + + echo "[ * ] Configuring PHP-FPM $fpm_v..." + # Create www.conf for webmail and php(*)admin + cp -f $HESTIA_INSTALL_DIR/php-fpm/www.conf /etc/php/$fpm_v/fpm/pool.d/www.conf + update-rc.d php$fpm_v-fpm defaults > /dev/null 2>&1 + systemctl start php$fpm_v-fpm >> $LOG + check_result $? "php-fpm start failed" + # Set default php version to $fpm_v + update-alternatives --set php /usr/bin/php$fpm_v > /dev/null 2>&1 +fi + +#----------------------------------------------------------# +# Configure PHP # +#----------------------------------------------------------# + +echo "[ * ] Configuring PHP..." +ZONE=$(timedatectl > /dev/null 2>&1 | grep Timezone | awk '{print $2}') +if [ -z "$ZONE" ]; then + ZONE='UTC' +fi +for pconf in $(find /etc/php* -name php.ini); do + sed -i "s%;date.timezone =%date.timezone = $ZONE%g" $pconf + sed -i 's%_open_tag = Off%_open_tag = On%g' $pconf +done + +# Cleanup php session files not changed in the last 7 days (60*24*7 minutes) +echo '#!/bin/sh' > /etc/cron.daily/php-session-cleanup +echo "find -O3 /home/*/tmp/ -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin '+10080' -delete > /dev/null 2>&1" >> /etc/cron.daily/php-session-cleanup +echo "find -O3 $HESTIA/data/sessions/ -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin '+10080' -delete > /dev/null 2>&1" >> /etc/cron.daily/php-session-cleanup +chmod 755 /etc/cron.daily/php-session-cleanup + +#----------------------------------------------------------# +# Configure Vsftpd # +#----------------------------------------------------------# + +if [ "$vsftpd" = 'yes' ]; then + echo "[ * ] Configuring Vsftpd server..." + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf + fi + touch /var/log/vsftpd.log + chown root:adm /var/log/vsftpd.log + chmod 640 /var/log/vsftpd.log + touch /var/log/xferlog + chown root:adm /var/log/xferlog + chmod 640 /var/log/xferlog + update-rc.d vsftpd defaults + systemctl start vsftpd >> $LOG + check_result $? "vsftpd start failed" +fi + +#----------------------------------------------------------# +# Configure ProFTPD # +#----------------------------------------------------------# + +if [ "$proftpd" = 'yes' ]; then + echo "[ * ] Configuring ProFTPD server..." + echo "127.0.0.1 $servername" >> /etc/hosts + cp -f $HESTIA_INSTALL_DIR/proftpd/proftpd.conf /etc/proftpd/ + cp -f $HESTIA_INSTALL_DIR/proftpd/tls.conf /etc/proftpd/ + if [ "$release" = '22.04' ]; then + sed -i 's|IdentLookups off|#IdentLookups off|g' /etc/proftpd/proftpd.conf + fi + update-rc.d proftpd defaults > /dev/null 2>&1 + systemctl start proftpd >> $LOG + check_result $? "proftpd start failed" + if [ "$release" = '22.04' ]; then + unit_files="$(systemctl list-unit-files | grep proftpd)" + if [[ "$unit_files" =~ "disabled" ]]; then + systemctl enable proftpd + fi + fi +fi + +#----------------------------------------------------------# +# Configure MariaDB / MySQL # +#----------------------------------------------------------# + +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + [ "$mysql" = 'yes' ] && mysql_type="MariaDB" || mysql_type="MySQL" + echo "[ * ] Configuring $mysql_type database server..." + mycnf="my-small.cnf" + if [ $memory -gt 1200000 ]; then + mycnf="my-medium.cnf" + fi + if [ $memory -gt 3900000 ]; then + mycnf="my-large.cnf" + fi + + if [ "$mysql_type" = 'MariaDB' ]; then + # Run mysql_install_db + mysql_install_db >> $LOG + fi + + # Remove symbolic link + rm -f /etc/mysql/my.cnf + # Configuring MariaDB + cp -f $HESTIA_INSTALL_DIR/mysql/$mycnf /etc/mysql/my.cnf + + # Switch MariaDB inclusions to the MySQL + if [ "$mysql_type" = 'MySQL' ]; then + sed -i '/query_cache_size/d' /etc/mysql/my.cnf + sed -i 's|mariadb.conf.d|mysql.conf.d|g' /etc/mysql/my.cnf + fi + + update-rc.d mysql defaults > /dev/null 2>&1 + systemctl start mysql >> $LOG + check_result $? "${mysql_type,,} start failed" + + # Securing MariaDB/MySQL installation + mpass=$(gen_pass) + echo -e "[client]\npassword='$mpass'\n" > /root/.my.cnf + chmod 600 /root/.my.cnf + + # Alter root password + mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$mpass'; FLUSH PRIVILEGES;" + if [ "$mysql_type" = 'MariaDB' ]; then + # Allow mysql access via socket for startup + mysql -e "UPDATE mysql.global_priv SET priv=json_set(priv, '$.password_last_changed', UNIX_TIMESTAMP(), '$.plugin', 'mysql_native_password', '$.authentication_string', 'invalid', '$.auth_or', json_array(json_object(), json_object('plugin', 'unix_socket'))) WHERE User='root';" + # Disable anonymous users + mysql -e "DELETE FROM mysql.global_priv WHERE User='';" + else + mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '$mpass';" + mysql -e "DELETE FROM mysql.user WHERE User='';" + mysql -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');" + fi + # Drop test database + mysql -e "DROP DATABASE IF EXISTS test" + mysql -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'" + # Flush privileges + mysql -e "FLUSH PRIVILEGES;" +fi + +#----------------------------------------------------------# +# Configure phpMyAdmin # +#----------------------------------------------------------# + +# Source upgrade.conf with phpmyadmin versions +# shellcheck source=/usr/local/hestia/install/upgrade/upgrade.conf +source $HESTIA/install/upgrade/upgrade.conf + +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + # Display upgrade information + echo "[ * ] Installing phpMyAdmin version v$pma_v..." + + # Download latest phpmyadmin release + wget --retry-connrefused --quiet https://files.phpmyadmin.net/phpMyAdmin/$pma_v/phpMyAdmin-$pma_v-all-languages.tar.gz + + # Unpack files + tar xzf phpMyAdmin-$pma_v-all-languages.tar.gz + + # Create folders + mkdir -p /usr/share/phpmyadmin + mkdir -p /etc/phpmyadmin + mkdir -p /etc/phpmyadmin/conf.d/ + mkdir /usr/share/phpmyadmin/tmp + + # Configuring Apache2 for PHPMYADMIN + if [ "$apache" = 'yes' ]; then + touch /etc/apache2/conf.d/phpmyadmin.inc + fi + + # Overwrite old files + cp -rf phpMyAdmin-$pma_v-all-languages/* /usr/share/phpmyadmin + + # Create copy of config file + cp -f $HESTIA_INSTALL_DIR/phpmyadmin/config.inc.php /etc/phpmyadmin/ + mkdir -p /var/lib/phpmyadmin/tmp + chmod 770 /var/lib/phpmyadmin/tmp + chown root:www-data /usr/share/phpmyadmin/tmp + + # Set config and log directory + sed -i "s|'configFile' => ROOT_PATH . 'config.inc.php',|'configFile' => '/etc/phpmyadmin/config.inc.php',|g" /usr/share/phpmyadmin/libraries/vendor_config.php + + # Create temporary folder and change permission + chmod 770 /usr/share/phpmyadmin/tmp + chown root:www-data /usr/share/phpmyadmin/tmp + # Generate blow fish + blowfish=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32) + sed -i "s|%blowfish_secret%|$blowfish|" /etc/phpmyadmin/config.inc.php + + # Clean Up + rm -fr phpMyAdmin-$pma_v-all-languages + rm -f phpMyAdmin-$pma_v-all-languages.tar.gz + + write_config_value "DB_PMA_ALIAS" "phpmyadmin" + $HESTIA/bin/v-change-sys-db-alias 'pma' "phpmyadmin" + + # Special thanks to Pavel Galkin (https://skurudo.ru) + # https://github.com/skurudo/phpmyadmin-fixer + # shellcheck source=/usr/local/hestia/install/deb/phpmyadmin/pma.sh + source $HESTIA_INSTALL_DIR/phpmyadmin/pma.sh > /dev/null 2>&1 + + # limit access to /etc/phpmyadmin/ + chown -R root:www-data /etc/phpmyadmin/ + chmod -R 640 /etc/phpmyadmin/* + chmod 750 /etc/phpmyadmin/conf.d/ +fi + +#----------------------------------------------------------# +# Configure PostgreSQL # +#----------------------------------------------------------# + +if [ "$postgresql" = 'yes' ]; then + echo "[ * ] Configuring PostgreSQL database server..." + ppass=$(gen_pass) + cp -f $HESTIA_INSTALL_DIR/postgresql/pg_hba.conf /etc/postgresql/*/main/ + systemctl restart postgresql + sudo -iu postgres psql -c "ALTER USER postgres WITH PASSWORD '$ppass'" > /dev/null 2>&1 + + # Configuring phpPgAdmin + if [ "$apache" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/pga/phppgadmin.conf /etc/apache2/conf.d/phppgadmin.inc + fi + cp -f $HESTIA_INSTALL_DIR/pga/config.inc.php /etc/phppgadmin/ + + write_config_value "DB_PGA_ALIAS" "phppgadmin" + $HESTIA/bin/v-change-sys-db-alias 'pga' "phppgadmin" +fi + +#----------------------------------------------------------# +# Configure Bind # +#----------------------------------------------------------# + +if [ "$named" = 'yes' ]; then + echo "[ * ] Configuring Bind DNS server..." + cp -f $HESTIA_INSTALL_DIR/bind/named.conf /etc/bind/ + cp -f $HESTIA_INSTALL_DIR/bind/named.conf.options /etc/bind/ + chown root:bind /etc/bind/named.conf + chown root:bind /etc/bind/named.conf.options + chown bind:bind /var/cache/bind + chmod 640 /etc/bind/named.conf + chmod 640 /etc/bind/named.conf.options + aa-complain /usr/sbin/named > /dev/null 2>&1 + echo "/home/** rwm," >> /etc/apparmor.d/local/usr.sbin.named 2> /dev/null + if ! grep --quiet lxc /proc/1/environ; then + systemctl status apparmor > /dev/null 2>&1 + if [ $? -ne 0 ]; then + systemctl restart apparmor >> $LOG + fi + fi + if [ "$release" != '18.04' ]; then + update-rc.d named defaults + systemctl start named + else + update-rc.d bind9 defaults + systemctl start bind9 + fi + check_result $? "bind9 start failed" + + # Workaround for OpenVZ/Virtuozzo + if [ -e "/proc/vz/veinfo" ] && [ -e "/etc/rc.local" ]; then + sed -i "s/^exit 0/service bind9 restart\nexit 0/" /etc/rc.local + fi +fi + +#----------------------------------------------------------# +# Configure Exim # +#----------------------------------------------------------# + +if [ "$exim" = 'yes' ]; then + echo "[ * ] Configuring Exim mail server..." + gpasswd -a Debian-exim mail > /dev/null 2>&1 + if [ "$release" = "22.04" ]; then + # Jammyy uses Exim 4.95 instead but config works with Exim4.94 + cp -f $HESTIA_INSTALL_DIR/exim/exim4.conf.4.94.template /etc/exim4/exim4.conf.template + else + cp -f $HESTIA_INSTALL_DIR/exim/exim4.conf.template /etc/exim4/ + fi + cp -f $HESTIA_INSTALL_DIR/exim/dnsbl.conf /etc/exim4/ + cp -f $HESTIA_INSTALL_DIR/exim/spam-blocks.conf /etc/exim4/ + cp -f $HESTIA_INSTALL_DIR/exim/limit.conf /etc/exim4/ + cp -f $HESTIA_INSTALL_DIR/exim/system.filter /etc/exim4/ + touch /etc/exim4/white-blocks.conf + + if [ "$spamd" = 'yes' ]; then + sed -i "s/#SPAM/SPAM/g" /etc/exim4/exim4.conf.template + fi + if [ "$clamd" = 'yes' ]; then + sed -i "s/#CLAMD/CLAMD/g" /etc/exim4/exim4.conf.template + fi + + chmod 640 /etc/exim4/exim4.conf.template + rm -rf /etc/exim4/domains + mkdir -p /etc/exim4/domains + + rm -f /etc/alternatives/mta + ln -s /usr/sbin/exim4 /etc/alternatives/mta + update-rc.d -f sendmail remove > /dev/null 2>&1 + systemctl stop sendmail > /dev/null 2>&1 + update-rc.d -f postfix remove > /dev/null 2>&1 + systemctl stop postfix > /dev/null 2>&1 + + update-rc.d exim4 defaults + systemctl start exim4 >> $LOG + check_result $? "exim4 start failed" +fi + +#----------------------------------------------------------# +# Configure Dovecot # +#----------------------------------------------------------# + +if [ "$dovecot" = 'yes' ]; then + echo "[ * ] Configuring Dovecot POP/IMAP mail server..." + gpasswd -a dovecot mail > /dev/null 2>&1 + cp -rf $HESTIA_COMMON_DIR/dovecot /etc/ + cp -f $HESTIA_INSTALL_DIR/logrotate/dovecot /etc/logrotate.d/ + rm -f /etc/dovecot/conf.d/15-mailboxes.conf + + chown -R root:root /etc/dovecot* + + #Alter config for 2.2 + version=$(dovecot --version | cut -f -2 -d .) + if [ "$version" = "2.2" ]; then + echo "[ * ] Downgrade dovecot config to sync with 2.2 settings" + sed -i 's|#ssl_dh_parameters_length = 4096|ssl_dh_parameters_length = 4096|g' /etc/dovecot/conf.d/10-ssl.conf + sed -i 's|ssl_dh = > $LOG + check_result $? "dovecot start failed" +fi + +#----------------------------------------------------------# +# Configure ClamAV # +#----------------------------------------------------------# + +if [ "$clamd" = 'yes' ]; then + gpasswd -a clamav mail > /dev/null 2>&1 + gpasswd -a clamav Debian-exim > /dev/null 2>&1 + cp -f $HESTIA_INSTALL_DIR/clamav/clamd.conf /etc/clamav/ + update-rc.d clamav-daemon defaults + echo -ne "[ * ] Installing ClamAV anti-virus definitions... " + /usr/bin/freshclam >> $LOG & + BACK_PID=$! + spin_i=1 + while kill -0 $BACK_PID > /dev/null 2>&1; do + printf "\b${spinner:spin_i++%${#spinner}:1}" + sleep 0.5 + done + echo + systemctl start clamav-daemon >> $LOG + check_result $? "clamav-daemon start failed" +fi + +#----------------------------------------------------------# +# Configure SpamAssassin # +#----------------------------------------------------------# + +if [ "$spamd" = 'yes' ]; then + echo "[ * ] Configuring SpamAssassin..." + update-rc.d spamassassin defaults > /dev/null 2>&1 + sed -i "s/ENABLED=0/ENABLED=1/" /etc/default/spamassassin + systemctl start spamassassin >> $LOG + check_result $? "spamassassin start failed" + unit_files="$(systemctl list-unit-files | grep spamassassin)" + if [[ "$unit_files" =~ "disabled" ]]; then + systemctl enable spamassassin > /dev/null 2>&1 + fi + + sed -i "s/#CRON=1/CRON=1/" /etc/default/spamassassin +fi + +#----------------------------------------------------------# +# Configure Fail2Ban # +#----------------------------------------------------------# + +if [ "$fail2ban" = 'yes' ]; then + echo "[ * ] Configuring fail2ban access monitor..." + cp -rf $HESTIA_INSTALL_DIR/fail2ban /etc/ + if [ "$dovecot" = 'no' ]; then + fline=$(cat /etc/fail2ban/jail.local | grep -n dovecot-iptables -A 2) + fline=$(echo "$fline" | grep enabled | tail -n1 | cut -f 1 -d -) + sed -i "${fline}s/true/false/" /etc/fail2ban/jail.local + fi + if [ "$exim" = 'no' ]; then + fline=$(cat /etc/fail2ban/jail.local | grep -n exim-iptables -A 2) + fline=$(echo "$fline" | grep enabled | tail -n1 | cut -f 1 -d -) + sed -i "${fline}s/true/false/" /etc/fail2ban/jail.local + fi + if [ "$vsftpd" = 'yes' ]; then + #Create vsftpd Log File + if [ ! -f "/var/log/vsftpd.log" ]; then + touch /var/log/vsftpd.log + fi + fline=$(cat /etc/fail2ban/jail.local | grep -n vsftpd-iptables -A 2) + fline=$(echo "$fline" | grep enabled | tail -n1 | cut -f 1 -d -) + sed -i "${fline}s/false/true/" /etc/fail2ban/jail.local + fi + if [ -f /etc/fail2ban/jail.d/defaults-debian.conf ]; then + rm -f /etc/fail2ban/jail.d/defaults-debian.conf + fi + + update-rc.d fail2ban defaults + # Ubuntu 22.04 doesn't start F2B by default on boot + update-rc.d fail2ban enable + + systemctl start fail2ban >> $LOG + check_result $? "fail2ban start failed" +fi + +# Configuring MariaDB/MySQL host +if [ "$mysql" = 'yes' ] || [ "$mysqlclassic" = 'yes' ]; then + $HESTIA/bin/v-add-database-host mysql localhost root $mpass +fi + +# Configuring PostgreSQL host +if [ "$postgresql" = 'yes' ]; then + $HESTIA/bin/v-add-database-host pgsql localhost postgres $ppass +fi + +#----------------------------------------------------------# +# Install Roundcube # +#----------------------------------------------------------# +# Min requirements Dovecot + Exim + Mysql +if ([ "$mysql" == 'yes' ] || [ "$mysqlclassic" == 'yes' ]) && [ "$dovecot" == "yes" ]; then + echo "[ * ] Install Roundcube..." + $HESTIA/bin/v-add-sys-roundcube + write_config_value "WEBMAIL_ALIAS" "webmail" +else + write_config_value "WEBMAIL_ALIAS" "" + write_config_value "WEBMAIL_SYSTEM" "" +fi + +#----------------------------------------------------------# +# Install Sieve # +#----------------------------------------------------------# + +# Min requirements Dovecot + Exim + Mysql + Roundcube +if [ "$sieve" = 'yes' ]; then + # Folder paths + RC_INSTALL_DIR="/var/lib/roundcube" + RC_CONFIG_DIR="/etc/roundcube" + + echo "[ * ] Install Sieve..." + + # dovecot.conf install + sed -i "s/namespace/service stats \{\n unix_listener stats-writer \{\n group = mail\n mode = 0660\n user = dovecot\n \}\n\}\n\nnamespace/g" /etc/dovecot/dovecot.conf + + # dovecot conf files + # 10-master.conf + sed -i -E -z "s/ }\n user = dovecot\n}/ \}\n unix_listener auth-master \{\n group = mail\n mode = 0660\n user = dovecot\n \}\n user = dovecot\n\}/g" /etc/dovecot/conf.d/10-master.conf + # 15-lda.conf + sed -i "s/\#mail_plugins = \\\$mail_plugins/mail_plugins = \$mail_plugins quota sieve\n auth_socket_path = \/var\/run\/dovecot\/auth-master/g" /etc/dovecot/conf.d/15-lda.conf + # 20-imap.conf + sed -i "s/mail_plugins = quota imap_quota/mail_plugins = quota imap_quota imap_sieve/g" /etc/dovecot/conf.d/20-imap.conf + + # replace dovecot-sieve config files + cp -f $HESTIA_COMMON_DIR/dovecot/sieve/* /etc/dovecot/conf.d + + # Dovecot default file install + echo -e "require [\"fileinto\"];\n# rule:[SPAM]\nif header :contains \"X-Spam-Flag\" \"YES\" {\n fileinto \"INBOX.Spam\";\n}\n" > /etc/dovecot/sieve/default + + # exim4 install + sed -i "s/\stransport = local_delivery/ transport = dovecot_virtual_delivery/" /etc/exim4/exim4.conf.template + sed -i "s/address_pipe:/dovecot_virtual_delivery:\n driver = pipe\n command = \/usr\/lib\/dovecot\/dovecot-lda -e -d \$local_part@\$domain -f \$sender_address -a \$original_local_part@\$original_domain\n delivery_date_add\n envelope_to_add\n return_path_add\n log_output = true\n log_defer_output = true\n user = \${extract{2}{:}{\${lookup{\$local_part}lsearch{\/etc\/exim4\/domains\/\${lookup{\$domain}dsearch{\/etc\/exim4\/domains\/}}\/passwd}}}}\n group = mail\n return_output\n\naddress_pipe:/g" /etc/exim4/exim4.conf.template + + # Permission changes + chown -R dovecot:mail /var/log/dovecot.log + chmod 660 /var/log/dovecot.log + + if [ -d "/var/lib/roundcube" ]; then + # Modify Roundcube config + mkdir -p $RC_CONFIG_DIR/plugins/managesieve + cp -f $HESTIA_COMMON_DIR/roundcube/plugins/config_managesieve.inc.php $RC_CONFIG_DIR/plugins/managesieve/config.inc.php + ln -s $RC_CONFIG_DIR/plugins/managesieve/config.inc.php $RC_INSTALL_DIR/plugins/managesieve/config.inc.php + chown -R root:www-data $RC_CONFIG_DIR/ + chmod 751 -R $RC_CONFIG_DIR + chmod 644 $RC_CONFIG_DIR/*.php + chmod 644 $RC_CONFIG_DIR/plugins/managesieve/config.inc.php + sed -i "s/'archive'/'archive', 'managesieve'/g" $RC_CONFIG_DIR/config.inc.php + fi + + sed -i "s/'archive'/'archive', 'managesieve'/g" $RC_CONFIG_DIR/config.inc.php + + # Restart Dovecot and exim4 + systemctl restart dovecot > /dev/null 2>&1 + systemctl restart exim4 > /dev/null 2>&1 +fi + +#----------------------------------------------------------# +# Configure API # +#----------------------------------------------------------# + +if [ "$api" = "yes" ]; then + # keep legacy api enabled until transition is complete + write_config_value "API" "yes" + write_config_value "API_SYSTEM" "1" + write_config_value "API_ALLOWED_IP" "" +else + write_config_value "API" "no" + write_config_value "API_SYSTEM" "0" + write_config_value "API_ALLOWED_IP" "" + $HESTIA/bin/v-change-sys-api disable +fi + +#----------------------------------------------------------# +# Configure File Manager # +#----------------------------------------------------------# + +echo "[ * ] Configuring File Manager..." +$HESTIA/bin/v-add-sys-filemanager quiet + +#----------------------------------------------------------# +# Configure PHPMailer # +#----------------------------------------------------------# + +echo "[ * ] Configuring PHP dependencies..." +$HESTIA/bin/v-add-sys-dependencies quiet + +#----------------------------------------------------------# +# Configure IP # +#----------------------------------------------------------# + +# Configuring system IPs +$HESTIA/bin/v-update-sys-ip > /dev/null 2>&1 + +# Get main IP +ip=$(ip addr | grep 'inet ' | grep global | head -n1 | awk '{print $2}' | cut -f1 -d/) +local_ip=$ip + +# Configuring firewall +if [ "$iptables" = 'yes' ]; then + $HESTIA/bin/v-update-firewall +fi + +# Get public IP +echo "[ * ] Configuring System IP..." +pub_ip=$(curl --ipv4 -s https://ip.hestiacp.com/) +pub_ipv6=$(curl --ipv6 -s https://ip.hestiacp.com/) + +if [ -n "$pub_ip" ] && [ "$pub_ip" != "$ip" ]; then + if [ -e /etc/rc.local ]; then + sed -i '/exit 0/d' /etc/rc.local + else + touch /etc/rc.local + fi + + check_rclocal=$(cat /etc/rc.local | grep "#!") + if [ -z "$check_rclocal" ]; then + echo "#!/bin/sh" >> /etc/rc.local + fi + + echo "$HESTIA/bin/v-update-sys-ip" >> /etc/rc.local + echo "exit 0" >> /etc/rc.local + chmod +x /etc/rc.local + systemctl enable rc-local > /dev/null 2>&1 + $HESTIA/bin/v-change-sys-ip-nat $ip $pub_ip > /dev/null 2>&1 + ip=$pub_ip +fi + +# Configuring libapache2-mod-remoteip +if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then + cd /etc/apache2/mods-available + echo "" > remoteip.conf + echo " RemoteIPHeader X-Real-IP" >> remoteip.conf + if [ "$local_ip" != "127.0.0.1" ] && [ "$pub_ip" != "127.0.0.1" ]; then + echo " RemoteIPInternalProxy 127.0.0.1" >> remoteip.conf + fi + if [ -n "$local_ip" ] && [ "$local_ip" != "$pub_ip" ]; then + echo " RemoteIPInternalProxy $local_ip" >> remoteip.conf + fi + if [ -n "$pub_ip" ]; then + echo " RemoteIPInternalProxy $pub_ip" >> remoteip.conf + fi + echo "" >> remoteip.conf + sed -i "s/LogFormat \"%h/LogFormat \"%a/g" /etc/apache2/apache2.conf + a2enmod remoteip >> $LOG + systemctl restart apache2 +fi + +# Adding default domain +$HESTIA/bin/v-add-web-domain admin $servername $ip +check_result $? "can't create $servername domain" + +# Adding cron jobs +export SCHEDULED_RESTART="yes" +command="sudo $HESTIA/bin/v-update-sys-queue restart" +$HESTIA/bin/v-add-cron-job 'admin' '*/2' '*' '*' '*' '*' "$command" +systemctl restart cron + +command="sudo $HESTIA/bin/v-update-sys-queue daily" +$HESTIA/bin/v-add-cron-job 'admin' '10' '00' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue disk" +$HESTIA/bin/v-add-cron-job 'admin' '15' '02' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue traffic" +$HESTIA/bin/v-add-cron-job 'admin' '10' '00' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue webstats" +$HESTIA/bin/v-add-cron-job 'admin' '30' '03' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-queue backup" +$HESTIA/bin/v-add-cron-job 'admin' '*/5' '*' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-backup-users" +$HESTIA/bin/v-add-cron-job 'admin' '10' '05' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-user-stats" +$HESTIA/bin/v-add-cron-job 'admin' '20' '00' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-sys-rrd" +$HESTIA/bin/v-add-cron-job 'admin' '*/5' '*' '*' '*' '*' "$command" +command="sudo $HESTIA/bin/v-update-letsencrypt-ssl" +min=$(gen_pass '012345' '2') +hour=$(gen_pass '1234567' '1') +$HESTIA/bin/v-add-cron-job 'admin' "$min" "$hour" '*' '*' '*' "$command" + +# Enable automatic updates +$HESTIA/bin/v-add-cron-hestia-autoupdate apt + +# Building initital rrd images +$HESTIA/bin/v-update-sys-rrd + +# Enabling file system quota +if [ "$quota" = 'yes' ]; then + $HESTIA/bin/v-add-sys-quota +fi + +# Set backend port +$HESTIA/bin/v-change-sys-port $port > /dev/null 2>&1 + +# Create default configuration files +$HESTIA/bin/v-update-sys-defaults + +# Update remaining packages since repositories have changed +echo -ne "[ * ] Installing remaining software updates..." +apt-get -qq update +apt-get -y upgrade >> $LOG & +BACK_PID=$! +echo + +# Starting Hestia service +update-rc.d hestia defaults +systemctl start hestia +check_result $? "hestia start failed" +chown admin:admin $HESTIA/data/sessions + +# Create backup folder and set correct permission +mkdir -p /backup/ +chmod 755 /backup/ + +# create cronjob to generate ssl +echo "@reboot root sleep 10 && rm /etc/cron.d/hestia-ssl && PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:' && /usr/local/hestia/bin/v-add-letsencrypt-host" > /etc/cron.d/hestia-ssl + +#----------------------------------------------------------# +# Set hestia.conf default values # +#----------------------------------------------------------# + +echo "[ * ] Updating configuration files..." +write_config_value "PHPMYADMIN_KEY" "" +write_config_value "POLICY_USER_VIEW_SUSPENDED" "no" +write_config_value "POLICY_USER_VIEW_LOGS" "yes" +write_config_value "POLICY_USER_EDIT_WEB_TEMPLATES" "true" +write_config_value "POLICY_USER_EDIT_DNS_TEMPLATES" "yes" +write_config_value "POLICY_USER_EDIT_DETAILS" "yes" +write_config_value "POLICY_USER_DELETE_LOGS" "yes" +write_config_value "POLICY_USER_CHANGE_THEME" "yes" +write_config_value "POLICY_SYSTEM_PROTECTED_ADMIN" "no" +write_config_value "POLICY_SYSTEM_PASSWORD_RESET" "yes" +write_config_value "POLICY_SYSTEM_HIDE_SERVICES" "no" +write_config_value "POLICY_SYSTEM_ENABLE_BACON" "no" +write_config_value "PLUGIN_APP_INSTALLER" "true" +write_config_value "DEBUG_MODE" "no" +write_config_value "ENFORCE_SUBDOMAIN_OWNERSHIP" "yes" +write_config_value "USE_SERVER_SMTP" "false" +write_config_value "SERVER_SMTP_PORT" "" +write_config_value "SERVER_SMTP_HOST" "" +write_config_value "SERVER_SMTP_SECURITY" "" +write_config_value "SERVER_SMTP_USER" "" +write_config_value "SERVER_SMTP_PASSWD" "" +write_config_value "SERVER_SMTP_ADDR" "" +write_config_value "POLICY_CSRF_STRICTNESS" "1" + +# Add /usr/local/hestia/bin/ to path variable +echo 'if [ "${PATH#*/usr/local/hestia/bin*}" = "$PATH" ]; then + . /etc/profile.d/hestia.sh +fi' >> /root/.bashrc + +#----------------------------------------------------------# +# Hestia Access Info # +#----------------------------------------------------------# + +# Comparing hostname and IP +host_ip=$(host $servername | head -n 1 | awk '{print $NF}') +if [ "$host_ip" = "$ip" ]; then + ip="$servername" +fi + +echo -e "\n" +echo "====================================================================" +echo -e "\n" + +# Sending notification to admin email +echo -e "Congratulations! + +You have successfully installed Hestia Control Panel on your server. + +Ready to get started? Log in using the following credentials: + + Admin URL: https://$ip:$port + Username: admin + Password: $displaypass + +Thank you for choosing Hestia Control Panel to power your full stack web server, +we hope that you enjoy using it as much as we do! + +Please feel free to contact us at any time if you have any questions, +or if you encounter any bugs or problems: + +Documentation: https://docs.hestiacp.com/ +Forum: https://forum.hestiacp.com/ +Discord: https://discord.gg/nXRUZch +GitHub: https://www.github.com/hestiacp/hestiacp + +Note: Automatic updates are enabled by default. If you would like to disable them, +please log in and navigate to Server > Updates to turn them off. + +Help support the Hestia Control Panel project by donating via PayPal: +https://www.hestiacp.com/donate + +-- +Sincerely yours, +The Hestia Control Panel development team + +Made with love & pride by the open-source community around the world. +" > $tmpfile + +send_mail="$HESTIA/web/inc/mail-wrapper.php" +cat $tmpfile | $send_mail -s "Hestia Control Panel" $email + +# Congrats +echo +cat $tmpfile +rm -f $tmpfile + +# Add welcome message to notification panel +$HESTIA/bin/v-add-user-notification admin 'Welcome to Hestia Control Panel!' '
You are now ready to begin adding user accounts and domains. For help and assistance, view the documentation or visit our user forum.

Please report any bugs or issues via GitHub.

Have a wonderful day!

The Hestia Control Panel development team' + +# Clean-up +# Sort final configuration file +sort_config_file + +if [ "$interactive" = 'yes' ]; then + echo "[ ! ] IMPORTANT: The system will now reboot to complete the installation process." + read -n 1 -s -r -p "Press any key to continue" + reboot +else + echo "[ ! ] IMPORTANT: You must restart the system before continuing!" +fi +# EOF From 775c18a41670fec0c93bb3ba6d68013efe1e9223 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 5 Feb 2023 03:21:44 +0100 Subject: [PATCH 007/223] Tabulators instead of spaces --- install/hst-install-debian.sh | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/install/hst-install-debian.sh b/install/hst-install-debian.sh index c4e04c0f21..37f48be817 100755 --- a/install/hst-install-debian.sh +++ b/install/hst-install-debian.sh @@ -224,7 +224,7 @@ for arg; do --port) args="${args}-r " ;; --lang) args="${args}-l " ;; --interactive) args="${args}-y " ;; - --ipv6) args="${args}-6 " ;; + --ipv6) args="${args}-6 " ;; --api) args="${args}-d " ;; --hostname) args="${args}-s " ;; --email) args="${args}-e " ;; @@ -1334,8 +1334,8 @@ cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/logrotate/nginx /etc/logrotate.d/ if [ "$ipv6" = 'yes' ]; then - cp -f $HESTIA_INSTALL_DIR/nginx/nginx-ipv6.conf /etc/nginx/nginx.conf - cp -f $HESTIA_INSTALL_DIR/nginx/status-ipv6.conf /etc/nginx/conf.d/status.conf + cp -f $HESTIA_INSTALL_DIR/nginx/nginx-ipv6.conf /etc/nginx/nginx.conf + cp -f $HESTIA_INSTALL_DIR/nginx/status-ipv6.conf /etc/nginx/conf.d/status.conf fi mkdir -p /etc/nginx/conf.d/domains mkdir -p /etc/nginx/modules-enabled @@ -1344,23 +1344,23 @@ mkdir -p /var/log/nginx/domains # Update dns servers in nginx.conf dns_resolver=$(cat /etc/resolv.conf | grep -i '^nameserver' | cut -d ' ' -f2 | tr '\r\n' ' ' | xargs) for ip in $dns_resolver; do - if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - resolver="$ip $resolver" - fi - if [ "$ipv6" = 'yes' ]; then - if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then - resolver="[$ip] $resolver" - fi - fi + if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + resolver="$ip $resolver" + fi + if [ "$ipv6" = 'yes' ]; then + if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + resolver="[$ip] $resolver" + fi + fi done if [ -n "$resolver" ]; then - if [ "$ipv6" = 'yes' ]; then - sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf - sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf - else - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf - fi + if [ "$ipv6" = 'yes' ]; then + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + else + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + fi fi update-rc.d nginx defaults > /dev/null 2>&1 @@ -1382,9 +1382,9 @@ if [ "$apache" = 'yes' ]; then cp -f $HESTIA_INSTALL_DIR/apache2/status.conf /etc/apache2/mods-available/hestia-status.conf cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ - if [ "$ipv6" = 'yes' ]; then - cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf - fi + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf + fi # Enable needed modules a2enmod rewrite > /dev/null 2>&1 a2enmod suexec > /dev/null 2>&1 @@ -1476,9 +1476,9 @@ chmod 755 /etc/cron.daily/php-session-cleanup if [ "$vsftpd" = 'yes' ]; then echo "[ * ] Configuring Vsftpd server..." cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ - if [ "$ipv6" = 'yes' ]; then - cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf - fi + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf + fi touch /var/log/vsftpd.log chown root:adm /var/log/vsftpd.log chmod 640 /var/log/vsftpd.log From a681cde8a0299c149f759bc60e367aa22e468af3 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 5 Feb 2023 03:43:13 +0100 Subject: [PATCH 008/223] correct style tabulators for ubuntu install --- install/hst-install-ubuntu.sh | 126 +++++++++++++++++----------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/install/hst-install-ubuntu.sh b/install/hst-install-ubuntu.sh index e763da16ce..644ab1cf03 100755 --- a/install/hst-install-ubuntu.sh +++ b/install/hst-install-ubuntu.sh @@ -647,17 +647,17 @@ if ! [[ "$servername" =~ ^${mask1}${mask2}$ ]]; then else servername="example.com" fi - echo "127.0.0.1 $servername" >> /etc/hosts - if [ "$ipv6" = 'yes' ]; then - echo "::1 $servername" >> /etc/hosts - fi + echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi if [[ -z $(grep -i "$servername" /etc/hosts) ]]; then - echo "127.0.0.1 $servername" >> /etc/hosts - if [ "$ipv6" = 'yes' ]; then - echo "::1 $servername" >> /etc/hosts - fi + echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi # Set email if it wasn't set @@ -1081,18 +1081,18 @@ if [ ! -e "/sbin/iptables" ]; then fi fi - if [ "$ipv6" = 'yes' ]; then - if which ip6tables; then - ln -s "$(which ip6tables)" /sbin/ip6tables - elif [ -e "/usr/sbin/ip6tables" ]; then - ln -s /usr/sbin/ip6tables /sbin/ip6tables - elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables; then - autoip6tables=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables | cut -d '' -f 2) - if [ -x "$autoip6tables" ]; then - ln -s "$autoip6tables" /sbin/ip6tables - fi - fi - fi + if [ "$ipv6" = 'yes' ]; then + if which ip6tables; then + ln -s "$(which ip6tables)" /sbin/ip6tables + elif [ -e "/usr/sbin/ip6tables" ]; then + ln -s /usr/sbin/ip6tables /sbin/ip6tables + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables; then + autoip6tables=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables | cut -d '' -f 2) + if [ -x "$autoip6tables" ]; then + ln -s "$autoip6tables" /sbin/ip6tables + fi + fi + fi fi if [ ! -e "/sbin/iptables-save" ]; then @@ -1106,18 +1106,18 @@ if [ ! -e "/sbin/iptables-save" ]; then ln -s "$autoiptables_save" /sbin/iptables-save fi fi - if [ "$ipv6" = 'yes' ]; then - if which ip6tables-save; then - ln -s "$(which ip6tables-save)" /sbin/ip6tables-save - elif [ -e "/usr/sbin/ip6tables-save" ]; then - ln -s /usr/sbin/ip6tables-save /sbin/ip6tables-save - elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-save; then - autoip6tables_save=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-save | cut -d '' -f 2) - if [ -x "$autoip6tables_save" ]; then - ln -s "$autoip6tables_save" /sbin/ip6tables-save - fi - fi - fi + if [ "$ipv6" = 'yes' ]; then + if which ip6tables-save; then + ln -s "$(which ip6tables-save)" /sbin/ip6tables-save + elif [ -e "/usr/sbin/ip6tables-save" ]; then + ln -s /usr/sbin/ip6tables-save /sbin/ip6tables-save + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-save; then + autoip6tables_save=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-save | cut -d '' -f 2) + if [ -x "$autoip6tables_save" ]; then + ln -s "$autoip6tables_save" /sbin/ip6tables-save + fi + fi + fi fi if [ ! -e "/sbin/iptables-restore" ]; then @@ -1131,18 +1131,18 @@ if [ ! -e "/sbin/iptables-restore" ]; then ln -s "$autoiptables_restore" /sbin/iptables-restore fi fi - if [ "$ipv6" = 'yes' ]; then - if which ip6tables-restore; then - ln -s "$(which ip6tables-restore)" /sbin/ip6tables-restore - elif [ -e "/usr/sbin/ip6tables-restore" ]; then - ln -s /usr/sbin/ip6tables-restore /sbin/ip6tables-restore - elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-restore; then - autoip6tables_restore=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-restore | cut -d '' -f 2) - if [ -x "$autoip6tables_restore" ]; then - ln -s "$autoip6tables_restore" /sbin/ip6tables-restore - fi - fi - fi + if [ "$ipv6" = 'yes' ]; then + if which ip6tables-restore; then + ln -s "$(which ip6tables-restore)" /sbin/ip6tables-restore + elif [ -e "/usr/sbin/ip6tables-restore" ]; then + ln -s /usr/sbin/ip6tables-restore /sbin/ip6tables-restore + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-restore; then + autoip6tables_restore=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-restore | cut -d '' -f 2) + if [ -x "$autoip6tables_restore" ]; then + ln -s "$autoip6tables_restore" /sbin/ip6tables-restore + fi + fi + fi fi # Restrict access to /proc fs @@ -1440,21 +1440,21 @@ dns_resolver=$(cat /etc/resolv.conf | grep -i '^nameserver' | cut -d ' ' -f2 | t for ip in $dns_resolver; do if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then resolver="$ip $resolver" - fi - if [ "$ipv6" = 'yes' ]; then - if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then - resolver="[$ip] $resolver" - fi - fi + fi + if [ "$ipv6" = 'yes' ]; then + if [[ $ip =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + resolver="[$ip] $resolver" + fi + fi done if [ -n "$resolver" ]; then - if [ "$ipv6" = 'yes' ]; then - sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf - sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/ - else - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf - sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf - fi + if [ "$ipv6" = 'yes' ]; then + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 \[2606:4700:4700::1111\] 1.0.0.1 \[2606:4700:4700::1001\]/$resolver/g" /usr/local/hestia/nginx/ + else + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /etc/nginx/nginx.conf + sed -i "s/1.0.0.1 1.1.1.1/$resolver/g" /usr/local/hestia/nginx/conf/nginx.conf + fi fi update-rc.d nginx defaults > /dev/null 2>&1 @@ -1477,9 +1477,9 @@ if [ "$apache" = 'yes' ]; then cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ - if [ "$ipv6" = 'yes' ]; then - cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf - fi + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/apache2/status-ipv6.conf /etc/apache2/mods-available/hestia-status.conf + fi # Enable needed modules a2enmod rewrite > /dev/null 2>&1 @@ -1573,9 +1573,9 @@ chmod 755 /etc/cron.daily/php-session-cleanup if [ "$vsftpd" = 'yes' ]; then echo "[ * ] Configuring Vsftpd server..." cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ - if [ "$ipv6" = 'yes' ]; then - cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf - fi + if [ "$ipv6" = 'yes' ]; then + cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd-ipv6.conf /etc/vsftpd.conf + fi touch /var/log/vsftpd.log chown root:adm /var/log/vsftpd.log chmod 640 /var/log/vsftpd.log From 7aa698b87dfe2eb7a8d5c441532cefd51de79dc5 Mon Sep 17 00:00:00 2001 From: asmcc Date: Sun, 12 Feb 2023 23:24:44 +0100 Subject: [PATCH 009/223] Correct representation of IP addresses in plain and csv mode --- bin/v-list-sys-ip | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/v-list-sys-ip b/bin/v-list-sys-ip index 329cfb4330..50010e7b5a 100755 --- a/bin/v-list-sys-ip +++ b/bin/v-list-sys-ip @@ -59,7 +59,7 @@ shell_list() { # PLAIN list function plain_list() { - echo -ne "$IP\t$OWNER\t$STATUS\t$NAME\t$U_SYS_USERS\t$U_WEB_DOMAINS\t" + echo -ne "$ip\t$OWNER\t$STATUS\t$NAME\t$U_SYS_USERS\t$U_WEB_DOMAINS\t" echo -e "$INTERFACE\t$NETMASK\t$NAT\t$TIME\t$DATE" } @@ -67,7 +67,7 @@ plain_list() { csv_list() { echo -n "IP,OWNER,STATUS,NAME,U_SYS_USERS,U_WEB_DOMAINS,INTERFACE" echo "NETMASK,NAT,TIME,DATE" - echo -n "$IP,$OWNER,$STATUS,$NAME,\"$U_SYS_USERS\",$U_WEB_DOMAINS," + echo -n "$ip,$OWNER,$STATUS,$NAME,\"$U_SYS_USERS\",$U_WEB_DOMAINS," echo "$INTERFACE, $NETMASK,$NAT,$TIME,$DATE" } From 5c9f8321c04a07e35d64d5e9770f22bd7528aa31 Mon Sep 17 00:00:00 2001 From: asmcc Date: Mon, 13 Feb 2023 00:08:16 +0100 Subject: [PATCH 010/223] New shell script to list of parameters for IPV6 adresses --- func/main.sh | 2426 +++++++++++++++++++++++++------------------------- 1 file changed, 1206 insertions(+), 1220 deletions(-) diff --git a/func/main.sh b/func/main.sh index a6dc3a5f0b..0f7d0b53da 100644 --- a/func/main.sh +++ b/func/main.sh @@ -11,11 +11,10 @@ HOMEDIR='/home' BACKUP='/backup' BACKUP_GZIP=9 BACKUP_DISK_LIMIT=95 -BACKUP_LA_LIMIT=$(cat /proc/cpuinfo | grep processor | wc -l) +BACKUP_LA_LIMIT=`cat /proc/cpuinfo | grep processor | wc -l` RRD_STEP=300 BIN=$HESTIA/bin -HESTIA_INSTALL_DIR="$HESTIA/install/deb" -HESTIA_COMMON_DIR="$HESTIA/install/common" +HESTIA_INSTALL_DIR=$HESTIA/install/deb HESTIA_BACKUP="/root/hst_backups/$(date +%d%m%Y%H%M)" USER_DATA=$HESTIA/data/users/$user WEBTPL=$HESTIA/data/templates/web @@ -54,819 +53,811 @@ E_RESTART=20 # Detect operating system detect_os() { - if [ -e "/etc/os-release" ]; then - get_os_type=$(grep "^ID=" /etc/os-release | cut -f 2 -d '=') - if [ "$get_os_type" = "ubuntu" ]; then - if [ -e '/usr/bin/lsb_release' ]; then - OS_VERSION="$(lsb_release -s -r)" - OS_TYPE='Ubuntu' - fi - elif [ "$get_os_type" = "debian" ]; then - OS_TYPE='Debian' - OS_VERSION=$(cat /etc/debian_version | grep -o "[0-9]\{1,2\}" | head -n1) - fi - else - OS_TYPE="Unsupported OS" - OS_VERSION="Unknown" - fi + if [ -e "/etc/os-release" ]; then + get_os_type=$(grep "^ID=" /etc/os-release | cut -f 2 -d '=') + if [ "$get_os_type" = "ubuntu" ]; then + if [ -e '/usr/bin/lsb_release' ]; then + OS_VERSION="$(lsb_release -s -r)" + OS_TYPE='Ubuntu' + fi + elif [ "$get_os_type" = "debian" ]; then + OS_TYPE='Debian' + OS_VERSION=$(cat /etc/debian_version|grep -o "[0-9]\{1,2\}"|head -n1) + fi + else + OS_TYPE="Unsupported OS" + OS_VERSION="Unknown" + fi } # Generate time stamp new_timestamp() { - time_n_date=$(date +'%T %F') - time=$(echo "$time_n_date" | cut -f 1 -d \ ) - date=$(echo "$time_n_date" | cut -f 2 -d \ ) + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" |cut -f 1 -d \ ) + date=$(echo "$time_n_date" |cut -f 2 -d \ ) } # Event string for logger ARGS=("$@") -for ((I = 1; I <= $#; I++)); do - if [[ "$HIDE" != "$I" ]]; then - ARGUMENTS="$ARGUMENTS '${ARGS[${I} - 1]}'" - else - ARGUMENTS="$ARGUMENTS '******'" - fi +for ((I=1; I <= $# ; I++)); do + if [[ "$HIDE" != "$I" ]]; then + ARGUMENTS="$ARGUMENTS '${ARGS[${I}-1]}'" + else + ARGUMENTS="$ARGUMENTS '******'" + fi done # Log event function log_event() { - if [ -z "$time" ]; then - LOG_TIME="$(date +'%F %T') $(basename $0)" - else - LOG_TIME="$date $time $(basename $0)" - fi - if [ "$1" -eq 0 ]; then - echo "$LOG_TIME $2" >> $HESTIA/log/system.log - else - echo "$LOG_TIME $2 [Error $1]" >> $HESTIA/log/error.log - fi + if [ -z "$time" ]; then + LOG_TIME="$(date +'%F %T') $(basename $0)" + else + LOG_TIME="$date $time $(basename $0)" + fi + if [ "$1" -eq 0 ]; then + echo "$LOG_TIME $2" >> $HESTIA/log/system.log + else + echo "$LOG_TIME $2 [Error $1]" >> $HESTIA/log/error.log + fi } # Log user history log_history() { - message=${1//\'/\´} # Avoid single quotes broken the log - evt_level=${2:-$event_level} - log_user=${3-$user} - evt_category=${4:-$event_category} - - # Set default event level and category if not specified - if [ -z "$evt_level" ]; then - evt_level="Info" - fi - if [ -z "$evt_category" ]; then - evt_category="System" - fi - - # Log system events to system log file - if [ "$log_user" = "system" ]; then - log=$HESTIA/data/users/admin/system.log - else - if ! $BIN/v-list-user "$log_user" > /dev/null; then - return $E_NOTEXIST - fi - log=$HESTIA/data/users/$log_user/history.log - fi - touch $log - - # TODO: Improve log pruning and pagination - # - #if [ '1000' -lt "$(wc -l $log |cut -f 1 -d ' ')" ]; then - # tail -n 499 $log > $log.moved - # mv -f $log.moved $log - # chmod 660 $log - #fi - - if [ -z "$date" ]; then - time_n_date=$(date +'%T %F') - time=$(echo "$time_n_date" | cut -f 1 -d \ ) - date=$(echo "$time_n_date" | cut -f 2 -d \ ) - fi - curr_str=$(grep "ID=" $log | cut -f 2 -d \' | sort -n | tail -n1) - id="$((curr_str + 1))" - echo "ID='$id' DATE='$date' TIME='$time' LEVEL='$evt_level' CATEGORY='$evt_category' MESSAGE='$message'" >> $log + message=${1//\'/\´} # Avoid single quotes broken the log + evt_level=${2:-$event_level} + log_user=${3-$user} + evt_category=${4:-$event_category} + + # Set default event level and category if not specified + if [ -z "$evt_level" ]; then + evt_level="Info" + fi + if [ -z "$evt_category" ]; then + evt_category="System" + fi + + # Log system events to system log file + if [ "$log_user" = "system" ]; then + log=$HESTIA/data/users/admin/system.log + else + if ! $BIN/v-list-user "$log_user" >/dev/null; then + return $E_NOTEXIST + fi + log=$HESTIA/data/users/$log_user/history.log + fi + touch $log + + # TODO: Improve log pruning and pagination + # + #if [ '1000' -lt "$(wc -l $log |cut -f 1 -d ' ')" ]; then + # tail -n 499 $log > $log.moved + # mv -f $log.moved $log + # chmod 660 $log + #fi + + if [ -z "$date" ]; then + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" |cut -f 1 -d \ ) + date=$(echo "$time_n_date" |cut -f 2 -d \ ) + fi + curr_str=$(grep "ID=" $log | cut -f 2 -d \' | sort -n | tail -n1) + id="$((curr_str +1))" + echo "ID='$id' DATE='$date' TIME='$time' LEVEL='$evt_level' CATEGORY='$evt_category' MESSAGE='$message'" >> $log } # Result checker check_result() { - if [ $1 -ne 0 ]; then - local err_code="${3:-$1}" - if [[ -n "$CHECK_RESULT_CALLBACK" && "$(type -t "$CHECK_RESULT_CALLBACK")" == 'function' ]]; then - $CHECK_RESULT_CALLBACK "$err_code" "$2" - else - echo "Error: $2" - log_event "$err_code" "$ARGUMENTS" - fi - - exit $err_code - fi + if [ $1 -ne 0 ]; then + local err_code="${3:-$1}" + if [[ -n "$CHECK_RESULT_CALLBACK" && "$(type -t "$CHECK_RESULT_CALLBACK")" == 'function' ]]; then + $CHECK_RESULT_CALLBACK "$err_code" "$2" + else + echo "Error: $2" + log_event "$err_code" "$ARGUMENTS" + fi + + exit $err_code + fi } # Argument list checker check_args() { - if [ "$1" -gt "$2" ]; then - echo "Usage: $(basename $0) $3" - check_result "$E_ARGS" "not enought arguments" > /dev/null - fi + if [ "$1" -gt "$2" ]; then + echo "Usage: $(basename $0) $3" + check_result "$E_ARGS" "not enought arguments" >/dev/null + fi } # Subsystem checker is_system_enabled() { - if [ -z "$1" ] || [ "$1" = no ]; then - check_result "$E_DISABLED" "$2 is not enabled" - fi + if [ -z "$1" ] || [ "$1" = no ]; then + check_result "$E_DISABLED" "$2 is not enabled" + fi } + # User package check is_package_full() { - case "$1" in - WEB_DOMAINS) used=$(wc -l $USER_DATA/web.conf) ;; - WEB_ALIASES) used=$(echo $aliases | tr ',' '\n' | wc -l) ;; - DNS_DOMAINS) used=$(wc -l $USER_DATA/dns.conf) ;; - DNS_RECORDS) used=$(wc -l $USER_DATA/dns/$domain.conf) ;; - MAIL_DOMAINS) used=$(wc -l $USER_DATA/mail.conf) ;; - MAIL_ACCOUNTS) used=$(wc -l $USER_DATA/mail/$domain.conf) ;; - DATABASES) used=$(wc -l $USER_DATA/db.conf) ;; - CRON_JOBS) used=$(wc -l $USER_DATA/cron.conf) ;; - esac - used=$(echo "$used" | cut -f 1 -d \ ) - limit=$(grep "^$1=" $USER_DATA/user.conf | cut -f 2 -d \') - if [ "$1" = WEB_ALIASES ]; then - # Used is always calculated with the new alias added - if [ "$limit" != 'unlimited' ] && [[ "$used" -gt "$limit" ]]; then - check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" - fi - else - if [ "$limit" != 'unlimited' ] && [[ "$used" -ge "$limit" ]]; then - check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" - fi - fi + case "$1" in + WEB_DOMAINS) used=$(wc -l $USER_DATA/web.conf);; + WEB_ALIASES) used=$(echo $aliases |tr ',' '\n' |wc -l);; + DNS_DOMAINS) used=$(wc -l $USER_DATA/dns.conf);; + DNS_RECORDS) used=$(wc -l $USER_DATA/dns/$domain.conf);; + MAIL_DOMAINS) used=$(wc -l $USER_DATA/mail.conf);; + MAIL_ACCOUNTS) used=$(wc -l $USER_DATA/mail/$domain.conf);; + DATABASES) used=$(wc -l $USER_DATA/db.conf);; + CRON_JOBS) used=$(wc -l $USER_DATA/cron.conf);; + esac + used=$(echo "$used"| cut -f 1 -d \ ) + limit=$(grep "^$1=" $USER_DATA/user.conf |cut -f 2 -d \') + if [ "$1" = WEB_ALIASES ]; then + # Used is always calculated with the new alias added + if [ "$limit" != 'unlimited' ] && [[ "$used" -gt "$limit" ]]; then + check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" + fi + else + if [ "$limit" != 'unlimited' ] && [[ "$used" -ge "$limit" ]]; then + check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" + fi + fi } # User owner for reseller plugin get_user_owner() { - if [ -z "$RESELLER_KEY" ]; then - owner='admin' - else - owner=$(grep "^OWNER" $USER_DATA/user.conf | cut -f 2 -d \') - if [ -z "$owner" ]; then - owner='admin' - fi - fi + if [ -z "$RESELLER_KEY" ]; then + owner='admin' + else + owner=$(grep "^OWNER" $USER_DATA/user.conf| cut -f 2 -d \') + if [ -z "$owner" ]; then + owner='admin' + fi + fi } # Random password generator generate_password() { - matrix=$1 - length=$2 - if [ -z "$matrix" ]; then - matrix="A-Za-z0-9" - fi - if [ -z "$length" ]; then - length=16 - fi - head /dev/urandom | tr -dc $matrix | head -c$length + matrix=$1 + length=$2 + if [ -z "$matrix" ]; then + matrix="A-Za-z0-9" + fi + if [ -z "$length" ]; then + length=16 + fi + head /dev/urandom | tr -dc $matrix | head -c$length } # Package existence check is_package_valid() { - if [ -z $1 ]; then - if [ ! -e "$HESTIA/data/packages/$package.pkg" ]; then - check_result "$E_NOTEXIST" "package $package doesn't exist" - fi - else - if [ ! -e "$HESTIA/data/packages/$1.pkg" ]; then - check_result "$E_NOTEXIST" "package $1 doesn't exist" - fi - fi + if [ -z $1 ]; then + if [ ! -e "$HESTIA/data/packages/$package.pkg" ]; then + check_result "$E_NOTEXIST" "package $package doesn't exist" + fi + else + if [ ! -e "$HESTIA/data/packages/$1.pkg" ]; then + check_result "$E_NOTEXIST" "package $1 doesn't exist" + fi + fi } is_package_new() { - if [ -e "$HESTIA/data/packages/$1.pkg" ]; then - echo "Error: package $1 already exists." - log_event "$E_EXISTS" "$ARGUMENTS" - exit "$E_EXISTS" - fi + if [ -e "$HESTIA/data/packages/$1.pkg" ]; then + echo "Error: package $1 already exists." + log_event "$E_EXISTS" "$ARGUMENTS" + exit "$E_EXISTS" + fi } # Validate system type is_type_valid() { - if [ -z "$(echo $1 | grep -w $2)" ]; then - check_result "$E_INVALID" "$2 type is invalid" - fi + if [ -z "$(echo $1 | grep -w $2)" ]; then + check_result "$E_INVALID" "$2 type is invalid" + fi } # Check user backup settings is_backup_enabled() { - BACKUPS=$(grep "^BACKUPS=" $USER_DATA/user.conf | cut -f2 -d \') - if [ -z "$BACKUPS" ] || [[ "$BACKUPS" -le '0' ]]; then - check_result "$E_DISABLED" "user backup is disabled" - fi + BACKUPS=$(grep "^BACKUPS=" $USER_DATA/user.conf | cut -f2 -d \') + if [ -z "$BACKUPS" ] || [[ "$BACKUPS" -le '0' ]]; then + check_result "$E_DISABLED" "user backup is disabled" + fi } # Check user backup settings is_backup_scheduled() { - if [ -e "$HESTIA/data/queue/backup.pipe" ]; then - check_q=$(grep " $user " $HESTIA/data/queue/backup.pipe | grep $1) - if [ -n "$check_q" ]; then - check_result "$E_EXISTS" "$1 is already scheduled" - fi - fi + if [ -e "$HESTIA/data/queue/backup.pipe" ]; then + check_q=$(grep " $user " $HESTIA/data/queue/backup.pipe | grep $1) + if [ -n "$check_q" ]; then + check_result "$E_EXISTS" "$1 is already scheduled" + fi + fi } # Check if object is new is_object_new() { - if [ $2 = 'USER' ]; then - if [ -d "$USER_DATA" ]; then - object="OK" - fi - else - object=$(grep "$2='$3'" $USER_DATA/$1.conf) - fi - if [ -n "$object" ]; then - check_result "$E_EXISTS" "$2=$3 already exists" - fi + if [ $2 = 'USER' ]; then + if [ -d "$USER_DATA" ]; then + object="OK" + fi + else + object=$(grep "$2='$3'" $USER_DATA/$1.conf) + fi + if [ -n "$object" ]; then + check_result "$E_EXISTS" "$2=$3 already exists" + fi } # Check if object is valid is_object_valid() { - if [ $2 = 'USER' ]; then - tstpath="$(readlink -f "$HESTIA/data/users/$3")" - if [ "$(dirname "$tstpath")" != "$(readlink -f "$HESTIA/data/users")" ] || [ ! -d "$HESTIA/data/users/$3" ]; then - check_result "$E_NOTEXIST" "$1 $3 doesn't exist" - fi - elif [ $2 = 'KEY' ]; then - local key="$(basename "$3")" - - if [[ -z "$key" || ${#key} -lt 16 ]] || [[ ! -f "$HESTIA/data/access-keys/${key}" && ! -f "$HESTIA/data/access-keys/$key" ]]; then - check_result "$E_NOTEXIST" "$1 $3 doesn't exist" - fi - else - object=$(grep "$2='$3'" $HESTIA/data/users/$user/$1.conf) - if [ -z "$object" ]; then - arg1=$(basename $1) - arg2=$(echo $2 | tr '[:upper:]' '[:lower:]') - check_result "$E_NOTEXIST" "$arg1 $arg2 $3 doesn't exist" - fi - fi + if [ $2 = 'USER' ]; then + tstpath="$(readlink -f "$HESTIA/data/users/$3")" + if [ "$(dirname "$tstpath")" != "$(readlink -f "$HESTIA/data/users")" ] || [ ! -d "$HESTIA/data/users/$3" ]; then + check_result "$E_NOTEXIST" "$1 $3 doesn't exist" + fi + elif [ $2 = 'KEY' ]; then + local key="$(basename "$3")" + + if [[ -z "$key" || ${#key} -lt 16 ]] || [[ ! -f "$HESTIA/data/access-keys/${key}" && ! -f "$HESTIA/data/access-keys/$key" ]]; then + check_result "$E_NOTEXIST" "$1 $3 doesn't exist" + fi + else + object=$(grep "$2='$3'" $HESTIA/data/users/$user/$1.conf) + if [ -z "$object" ]; then + arg1=$(basename $1) + arg2=$(echo $2 |tr '[:upper:]' '[:lower:]') + check_result "$E_NOTEXIST" "$arg1 $arg2 $3 doesn't exist" + fi + fi } # Check if a object string with key values pairs has the correct format and load it afterwards parse_object_kv_list_non_eval() { - local str - local objkv obj_key obj_val - local OLD_IFS="$IFS" + local str + local objkv obj_key obj_val + local OLD_IFS="$IFS" - str=${@//$'\n'/ } - str=${str//\"/\\\"} - str=${str//$/\\$} - IFS=$'\n' + str=${@//$'\n'/ } + str=${str//\"/\\\"} + str=${str//$/\\$} + IFS=$'\n' - # Extract and loop trough each key-value pair. (Regex test: https://regex101.com/r/eiMufk/5) - for objkv in $(echo "$str" | perl -n -e "while(/\b([a-zA-Z]+[\w]*)='(.*?)'(\s|\$)/g) {print \$1.'='.\$2 . \"\n\" }"); do + # Extract and loop trough each key-value pair. (Regex test: https://regex101.com/r/eiMufk/5) + for objkv in $( echo "$str" | perl -n -e "while(/\b([a-zA-Z]+[\w]*)='(.*?)'(\s|\$)/g) {print \$1.'='.\$2 . \"\n\" }" ); do - if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then - check_result "$E_INVALID" "Invalid key value format [$objkv]" - fi + if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then + check_result "$E_INVALID" "Invalid key value format [$objkv]" + fi - obj_key=${objkv%%=*} # strip everything after first '=' char - obj_val=${objkv#*=} # strip everything before first '=' char - declare -g $obj_key="$obj_val" + obj_key=${objkv%%=*} # strip everything after first '=' char + obj_val=${objkv#*=} # strip everything before first '=' char + declare -g $obj_key="$obj_val" - done - IFS="$OLD_IFS" + done + IFS="$OLD_IFS" } # Check if a object string with key values pairs has the correct format and load it afterwards parse_object_kv_list() { - local str - local objkv - local suboutput - local OLD_IFS="$IFS" + local str + local objkv + local suboutput + local OLD_IFS="$IFS" - str=${@//$'\n'/ } - str=${str//\"/\\\"} - str=${str//$/\\$} - IFS=$'\n' + str=${@//$'\n'/ } + str=${str//\"/\\\"} + str=${str//$/\\$} + IFS=$'\n' - suboutput=$(setpriv --clear-groups --reuid nobody --regid nogroup bash -c "PS4=''; set -xe; eval \"${str}\"" 2>&1) - check_result $? "Invalid object format: ${str}" $E_INVALID + suboutput=$(setpriv --clear-groups --reuid nobody --regid nogroup bash -c "PS4=''; set -xe; eval \"${str}\"" 2>&1) + check_result $? "Invalid object format: ${str}" $E_INVALID - for objkv in $suboutput; do + for objkv in $suboutput; do - if [[ "$objkv" =~ ^'eval ' ]]; then - continue - fi + if [[ "$objkv" =~ ^'eval ' ]]; then + continue + fi - if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then - check_result "$E_INVALID" "Invalid key value format [$objkv]" - fi + if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then + check_result "$E_INVALID" "Invalid key value format [$objkv]" + fi - eval "$objkv" - done - IFS="$OLD_IFS" + eval "$objkv" + done + IFS="$OLD_IFS" } # Check if object is supended is_object_suspended() { - if [ $2 = 'USER' ]; then - spnd=$(cat $USER_DATA/$1.conf | grep "SUSPENDED='yes'") - else - spnd=$(grep "$2='$3'" $USER_DATA/$1.conf | grep "SUSPENDED='yes'") - fi - if [ -z "$spnd" ]; then - check_result "$E_UNSUSPENDED" "$(basename $1) $3 is not suspended" - fi + if [ $2 = 'USER' ]; then + spnd=$(cat $USER_DATA/$1.conf|grep "SUSPENDED='yes'") + else + spnd=$(grep "$2='$3'" $USER_DATA/$1.conf|grep "SUSPENDED='yes'") + fi + if [ -z "$spnd" ]; then + check_result "$E_UNSUSPENDED" "$(basename $1) $3 is not suspended" + fi } # Check if object is unsupended is_object_unsuspended() { - if [ $2 = 'USER' ]; then - spnd=$(cat $USER_DATA/$1.conf | grep "SUSPENDED='yes'") - else - spnd=$(grep "$2='$3'" $USER_DATA/$1.conf | grep "SUSPENDED='yes'") - fi - if [ -n "$spnd" ]; then - check_result "$E_SUSPENDED" "$(basename $1) $3 is suspended" - fi + if [ $2 = 'USER' ]; then + spnd=$(cat $USER_DATA/$1.conf |grep "SUSPENDED='yes'") + else + spnd=$(grep "$2='$3'" $USER_DATA/$1.conf |grep "SUSPENDED='yes'") + fi + if [ -n "$spnd" ]; then + check_result "$E_SUSPENDED" "$(basename $1) $3 is suspended" + fi } # Check if object value is empty is_object_value_empty() { - str=$(grep "$2='$3'" $USER_DATA/$1.conf) - parse_object_kv_list "$str" - eval value=$4 - if [ -n "$value" ] && [ "$value" != 'no' ]; then - check_result "$E_EXISTS" "${4//$/}=$value already exists" - fi + str=$(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list "$str" + eval value=$4 + if [ -n "$value" ] && [ "$value" != 'no' ]; then + check_result "$E_EXISTS" "${4//$}=$value already exists" + fi } # Check if object value is empty is_object_value_exist() { - str=$(grep "$2='$3'" $USER_DATA/$1.conf) - parse_object_kv_list "$str" - eval value=$4 - if [ -z "$value" ] || [ "$value" = 'no' ]; then - check_result "$E_NOTEXIST" "${4//$/}=$value doesn't exist" - fi + str=$(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list "$str" + eval value=$4 + if [ -z "$value" ] || [ "$value" = 'no' ]; then + check_result "$E_NOTEXIST" "${4//$}=$value doesn't exist" + fi } # Check if password is transmitted via file is_password_valid() { - if [[ "$password" =~ ^/tmp/ ]]; then - if ! [[ "$password" == *../* ]]; then - if [ -f "$password" ]; then - password="$(head -n1 $password)" - fi - fi - fi + if [[ "$password" =~ ^/tmp/ ]]; then + if ! [[ "$password" == *../* ]]; then + if [ -f "$password" ]; then + password="$(head -n1 $password)" + fi + fi + fi } # Check if hash is transmitted via file is_hash_valid() { - if [[ "$hash" =~ ^/tmp/ ]]; then - if ! [[ "$hash" == *../* ]]; then - if [ -f "$hash" ]; then - hash="$(head -n1 $hash)" - fi - fi - fi + if [[ "$hash" =~ ^/tmp/ ]]; then + if ! [[ "$hash" == *../* ]]; then + if [ -f "$hash" ]; then + hash="$(head -n1 $hash)" + fi + fi + fi } # Check if directory is a symlink is_dir_symlink() { - if [[ -L "$1" ]]; then - check_result "$E_FORBIDEN" "$1 directory is a symlink" - fi + if [[ -L "$1" ]]; then + check_result "$E_FORBIDEN" "$1 directory is a symlink" + fi } # Get object value get_object_value() { - object=$(grep "$2='$3'" $USER_DATA/$1.conf) - parse_object_kv_list "$object" - eval echo $4 + object=$(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list "$object" + eval echo $4 } get_object_values() { - parse_object_kv_list $(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list $(grep "$2='$3'" $USER_DATA/$1.conf) } # Update object value update_object_value() { - row=$(grep -nF "$2='$3'" $USER_DATA/$1.conf) - lnr=$(echo $row | cut -f 1 -d ':') - object=$(echo $row | sed "s/^$lnr://") - parse_object_kv_list "$object" - eval old="$4" - old=$(echo "$old" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') - new=$(echo "$5" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') - sed -i "$lnr s/${4//$/}='${old//\*/\\*}'/${4//$/}='${new//\*/\\*}'/g" \ - $USER_DATA/$1.conf + row=$(grep -nF "$2='$3'" $USER_DATA/$1.conf) + lnr=$(echo $row | cut -f 1 -d ':') + object=$(echo $row | sed "s/^$lnr://") + parse_object_kv_list "$object" + eval old="$4" + old=$(echo "$old" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + new=$(echo "$5" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + sed -i "$lnr s/${4//$/}='${old//\*/\\*}'/${4//$/}='${new//\*/\\*}'/g" \ + $USER_DATA/$1.conf } # Add object key add_object_key() { - row=$(grep -n "$2='$3'" $USER_DATA/$1.conf) - lnr=$(echo $row | cut -f 1 -d ':') - object=$(echo $row | sed "s/^$lnr://") - if [ -z "$(echo $object | grep $4=)" ]; then - eval old="$4" - sed -i "$lnr s/$5='/$4='' $5='/" $USER_DATA/$1.conf - fi + row=$(grep -n "$2='$3'" $USER_DATA/$1.conf) + lnr=$(echo $row | cut -f 1 -d ':') + object=$(echo $row | sed "s/^$lnr://") + if [ -z "$(echo $object |grep $4=)" ]; then + eval old="$4" + sed -i "$lnr s/$5='/$4='' $5='/" $USER_DATA/$1.conf + fi } # Search objects search_objects() { - OLD_IFS="$IFS" - IFS=$'\n' - if [ -f $USER_DATA/$1.conf ]; then - for line in $(grep $2=\'$3\' $USER_DATA/$1.conf); do - parse_object_kv_list "$line" - eval echo \$$4 - done - fi - IFS="$OLD_IFS" + OLD_IFS="$IFS" + IFS=$'\n' + if [ -f $USER_DATA/$1.conf ]; then + for line in $(grep $2=\'$3\' $USER_DATA/$1.conf); do + parse_object_kv_list "$line" + eval echo \$$4 + done + fi + IFS="$OLD_IFS" } # Get user value get_user_value() { - grep "^${1//$/}=" $USER_DATA/user.conf | head -1 | awk -F "'" '{print $2}' + grep "^${1//$/}=" $USER_DATA/user.conf | head -1 | awk -F "'" '{print $2}' } # Update user value in user.conf update_user_value() { - key="${2//$/}" - lnr=$(grep -n "^$key='" $HESTIA/data/users/$1/user.conf | cut -f 1 -d ':') - if [ -n "$lnr" ]; then - sed -i "$lnr d" $HESTIA/data/users/$1/user.conf - sed -i "$lnr i\\$key='${3}'" $HESTIA/data/users/$1/user.conf - fi + key="${2//$}" + lnr=$(grep -n "^$key='" $HESTIA/data/users/$1/user.conf |cut -f 1 -d ':') + if [ -n "$lnr" ]; then + sed -i "$lnr d" $HESTIA/data/users/$1/user.conf + sed -i "$lnr i\\$key='${3}'" $HESTIA/data/users/$1/user.conf + fi } # Increase user counter increase_user_value() { - key="${2//$/}" - factor="${3-1}" - conf="$HESTIA/data/users/$1/user.conf" - old=$(grep "$key=" $conf | cut -f 2 -d \') - if [ -z "$old" ]; then - old=0 - fi - new=$((old + factor)) - sed -i "s/$key='$old'/$key='$new'/g" $conf + key="${2//$}" + factor="${3-1}" + conf="$HESTIA/data/users/$1/user.conf" + old=$(grep "$key=" $conf | cut -f 2 -d \') + if [ -z "$old" ]; then + old=0 + fi + new=$((old + factor)) + sed -i "s/$key='$old'/$key='$new'/g" $conf } # Decrease user counter decrease_user_value() { - key="${2//$/}" - factor="${3-1}" - conf="$HESTIA/data/users/$1/user.conf" - old=$(grep "$key=" $conf | cut -f 2 -d \') - if [ -z "$old" ]; then - old=0 - fi - if [ "$old" -le 1 ]; then - new=0 - else - new=$((old - factor)) - fi - if [ "$new" -lt 0 ]; then - new=0 - fi - sed -i "s/$key='$old'/$key='$new'/g" $conf + key="${2//$}" + factor="${3-1}" + conf="$HESTIA/data/users/$1/user.conf" + old=$(grep "$key=" $conf | cut -f 2 -d \') + if [ -z "$old" ]; then + old=0 + fi + if [ "$old" -le 1 ]; then + new=0 + else + new=$((old - factor)) + fi + if [ "$new" -lt 0 ]; then + new=0 + fi + sed -i "s/$key='$old'/$key='$new'/g" $conf } # Notify user send_notice() { - topic=$1 - notice=$2 - - if [ "$notify" = 'yes' ]; then - touch $USER_DATA/notifications.conf - chmod 660 $USER_DATA/notifications.conf - - time_n_date=$(date +'%T %F') - time=$(echo "$time_n_date" | cut -f 1 -d \ ) - date=$(echo "$time_n_date" | cut -f 2 -d \ ) - - nid=$(grep "NID=" $USER_DATA/notifications.conf | cut -f 2 -d \') - nid=$(echo "$nid" | sort -n | tail -n1) - if [ -n "$nid" ]; then - nid="$((nid + 1))" - else - nid=1 - fi - - str="NID='$nid' TOPIC='$topic' NOTICE='$notice' TYPE='$type'" - str="$str ACK='no' TIME='$time' DATE='$date'" - - echo "$str" >> $USER_DATA/notifications.conf - - if [ -z "$(grep NOTIFICATIONS $USER_DATA/user.conf)" ]; then - sed -i "s/^TIME/NOTIFICATIONS='yes'\nTIME/g" $USER_DATA/user.conf - else - update_user_value "$user" '$NOTIFICATIONS' "yes" - fi - fi + topic=$1 + notice=$2 + + if [ "$notify" = 'yes' ]; then + touch $USER_DATA/notifications.conf + chmod 660 $USER_DATA/notifications.conf + + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" |cut -f 1 -d \ ) + date=$(echo "$time_n_date" |cut -f 2 -d \ ) + + nid=$(grep "NID=" $USER_DATA/notifications.conf |cut -f 2 -d \') + nid=$(echo "$nid" |sort -n |tail -n1) + if [ -n "$nid" ]; then + nid="$((nid +1))" + else + nid=1 + fi + + str="NID='$nid' TOPIC='$topic' NOTICE='$notice' TYPE='$type'" + str="$str ACK='no' TIME='$time' DATE='$date'" + + echo "$str" >> $USER_DATA/notifications.conf + + if [ -z "$(grep NOTIFICATIONS $USER_DATA/user.conf)" ]; then + sed -i "s/^TIME/NOTIFICATIONS='yes'\nTIME/g" $USER_DATA/user.conf + else + update_user_value "$user" '$NOTIFICATIONS' "yes" + fi + fi } # Recalculate U_DISK value recalc_user_disk_usage() { - u_usage=0 - if [ -f "$USER_DATA/web.conf" ]; then - usage=0 - dusage=$(grep 'U_DISK=' $USER_DATA/web.conf \ - | awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') - for disk_usage in $dusage; do - usage=$((usage + disk_usage)) - done - d=$(grep "U_DISK_WEB='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK_WEB='$d'/U_DISK_WEB='$usage'/g" $USER_DATA/user.conf - u_usage=$((u_usage + usage)) - fi - - if [ -f "$USER_DATA/mail.conf" ]; then - usage=0 - dusage=$(grep 'U_DISK=' $USER_DATA/mail.conf \ - | awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') - for disk_usage in $dusage; do - usage=$((usage + disk_usage)) - done - d=$(grep "U_DISK_MAIL='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK_MAIL='$d'/U_DISK_MAIL='$usage'/g" $USER_DATA/user.conf - u_usage=$((u_usage + usage)) - fi - - if [ -f "$USER_DATA/db.conf" ]; then - usage=0 - dusage=$(grep 'U_DISK=' $USER_DATA/db.conf \ - | awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') - for disk_usage in $dusage; do - usage=$((usage + disk_usage)) - done - d=$(grep "U_DISK_DB='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK_DB='$d'/U_DISK_DB='$usage'/g" $USER_DATA/user.conf - u_usage=$((u_usage + usage)) - fi - usage=$(grep 'U_DISK_DIRS=' $USER_DATA/user.conf | cut -f 2 -d "'") - u_usage=$((u_usage + usage)) - old=$(grep "U_DISK='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK='$old'/U_DISK='$u_usage'/g" $USER_DATA/user.conf + u_usage=0 + if [ -f "$USER_DATA/web.conf" ]; then + usage=0 + dusage=$(grep 'U_DISK=' $USER_DATA/web.conf |\ + awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') + for disk_usage in $dusage; do + usage=$((usage + disk_usage)) + done + d=$(grep "U_DISK_WEB='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK_WEB='$d'/U_DISK_WEB='$usage'/g" $USER_DATA/user.conf + u_usage=$((u_usage + usage)) + fi + + if [ -f "$USER_DATA/mail.conf" ]; then + usage=0 + dusage=$(grep 'U_DISK=' $USER_DATA/mail.conf |\ + awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') + for disk_usage in $dusage; do + usage=$((usage + disk_usage)) + done + d=$(grep "U_DISK_MAIL='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK_MAIL='$d'/U_DISK_MAIL='$usage'/g" $USER_DATA/user.conf + u_usage=$((u_usage + usage)) + fi + + if [ -f "$USER_DATA/db.conf" ]; then + usage=0 + dusage=$(grep 'U_DISK=' $USER_DATA/db.conf |\ + awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') + for disk_usage in $dusage; do + usage=$((usage + disk_usage)) + done + d=$(grep "U_DISK_DB='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK_DB='$d'/U_DISK_DB='$usage'/g" $USER_DATA/user.conf + u_usage=$((u_usage + usage)) + fi + usage=$(grep 'U_DISK_DIRS=' $USER_DATA/user.conf | cut -f 2 -d "'") + u_usage=$((u_usage + usage)) + old=$(grep "U_DISK='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK='$old'/U_DISK='$u_usage'/g" $USER_DATA/user.conf } # Recalculate U_BANDWIDTH value recalc_user_bandwidth_usage() { - usage=0 - bandwidth_usage=$(grep 'U_BANDWIDTH=' $USER_DATA/web.conf \ - | awk -F "U_BANDWIDTH='" '{print $2}' | cut -f 1 -d \') - for bandwidth in $bandwidth_usage; do - usage=$((usage + bandwidth)) - done - old=$(grep "U_BANDWIDTH='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_BANDWIDTH='$old'/U_BANDWIDTH='$usage'/g" $USER_DATA/user.conf + usage=0 + bandwidth_usage=$(grep 'U_BANDWIDTH=' $USER_DATA/web.conf |\ + awk -F "U_BANDWIDTH='" '{print $2}'|cut -f 1 -d \') + for bandwidth in $bandwidth_usage; do + usage=$((usage + bandwidth)) + done + old=$(grep "U_BANDWIDTH='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_BANDWIDTH='$old'/U_BANDWIDTH='$usage'/g" $USER_DATA/user.conf } # Get next cron job id get_next_cronjob() { - if [ -z "$job" ]; then - curr_str=$(grep "JOB=" $USER_DATA/cron.conf | cut -f 2 -d \' \ - | sort -n | tail -n1) - job="$((curr_str + 1))" - fi + if [ -z "$job" ]; then + curr_str=$(grep "JOB=" $USER_DATA/cron.conf|cut -f 2 -d \'|\ + sort -n|tail -n1) + job="$((curr_str +1))" + fi } # Sort cron jobs by id sort_cron_jobs() { - cat $USER_DATA/cron.conf | sort -n -k 2 -t \' > $USER_DATA/cron.tmp - mv -f $USER_DATA/cron.tmp $USER_DATA/cron.conf + cat $USER_DATA/cron.conf |sort -n -k 2 -t \' > $USER_DATA/cron.tmp + mv -f $USER_DATA/cron.tmp $USER_DATA/cron.conf } # Sync cronjobs with system cron sync_cron_jobs() { - source_conf "$USER_DATA/user.conf" - if [ -e "/var/spool/cron/crontabs" ]; then - crontab="/var/spool/cron/crontabs/$user" - else - crontab="/var/spool/cron/$user" - fi - - # remove file if exists - if [ -e "$crontab" ]; then - rm -f $crontab - fi - - # touch new crontab file - touch $crontab - - if [ "$CRON_REPORTS" = 'yes' ]; then - echo "MAILTO=$CONTACT" > $crontab - echo 'CONTENT_TYPE="text/plain; charset=utf-8"' >> $crontab - else - echo 'MAILTO=""' > $crontab - fi - - while read line; do - parse_object_kv_list "$line" - if [ "$SUSPENDED" = 'no' ]; then - echo "$MIN $HOUR $DAY $MONTH $WDAY $CMD" \ - | sed -e "s/%quote%/'/g" -e "s/%dots%/:/g" \ - >> $crontab - fi - done < $USER_DATA/cron.conf - chown $user:$user $crontab - chmod 600 $crontab + source_conf "$USER_DATA/user.conf" + if [ -e "/var/spool/cron/crontabs" ]; then + crontab="/var/spool/cron/crontabs/$user" + else + crontab="/var/spool/cron/$user" + fi + + # remove file if exists + if [ -e "$crontab" ]; then + rm -f $crontab + fi + + # touch new crontab file + touch $crontab + + if [ "$CRON_REPORTS" = 'yes' ]; then + echo "MAILTO=$CONTACT" > $crontab + echo 'CONTENT_TYPE="text/plain; charset=utf-8"' >> $crontab + else + echo 'MAILTO=""' > $crontab + fi + + while read line; do + parse_object_kv_list "$line" + if [ "$SUSPENDED" = 'no' ]; then + echo "$MIN $HOUR $DAY $MONTH $WDAY $CMD" |\ + sed -e "s/%quote%/'/g" -e "s/%dots%/:/g" \ + >> $crontab + fi + done < $USER_DATA/cron.conf + chown $user:$user $crontab + chmod 600 $crontab } # User format validator is_user_format_valid() { - if [ ${#1} -eq 1 ]; then - if ! [[ "$1" =~ ^^[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - else - if [ -n "$3" ]; then - maxlenght=$(($3 - 2)) - if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,$maxlenght}[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - else - if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,28}[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - fi - fi - if [ "$1" != "${1//[^[:ascii:]]/}" ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if [ ${#1} -eq 1 ]; then + if ! [[ "$1" =~ ^^[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + else + if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,28}[[:alnum:]]$ ]] + then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + fi } # Domain format validator is_domain_format_valid() { - object_name=${2-domain} - exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|_|/|\|\"|'|;|%|\`| ]" - if [[ $1 =~ $exclude ]] || [[ $1 =~ ^[0-9]+$ ]] || [[ $1 =~ \.\. ]] || [[ $1 =~ $(printf '\t') ]] || [[ "$1" = "www" ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - is_no_new_line_format "$1" + object_name=${2-domain} + exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|_|/|\|\"|'|;|%|\`| ]" + if [[ $1 =~ $exclude ]] || [[ $1 =~ ^[0-9]+$ ]] || [[ $1 =~ \.\. ]] || [[ $1 =~ $(printf '\t') ]] || [[ "$1" = "www" ]]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + is_no_new_line_format "$1"; } # Alias forman validator is_alias_format_valid() { - for object in ${1//,/ }; do - exclude="[!|@|#|$|^|&|(|)|+|=|{|}|:|<|>|?|_|/|\|\"|'|;|%|\`| ]" - if [[ "$object" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid alias format :: $object" - fi - if [[ "$object" =~ [*] ]] && ! [[ "$object" =~ ^[*]\..* ]]; then - check_result "$E_INVALID" "invalid alias format :: $object" - fi - done + for object in ${1//,/ }; do + exclude="[!|@|#|$|^|&|(|)|+|=|{|}|:|<|>|?|_|/|\|\"|'|;|%|\`| ]" + if [[ "$object" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid alias format :: $object" + fi + if [[ "$object" =~ [*] ]] && ! [[ "$object" =~ ^[*]\..* ]]; then + check_result "$E_INVALID" "invalid alias format :: $object" + fi + done } # IP format validator is_ip_format_valid() { - object_name=${2-ip} - ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' - ip_clean=$(echo "${1%/*}") - if ! [[ $ip_clean =~ ^$ip_regex\.$ip_regex\.$ip_regex\.$ip_regex$ ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - if [ $1 != "$ip_clean" ]; then - ip_cidr="$ip_clean/" - ip_cidr=$(echo "${1#$ip_cidr}") - if [[ "$ip_cidr" -gt 32 ]] || [[ "$ip_cidr" =~ [:alnum:] ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - fi + object_name=${2-ip} + ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' + ip_clean=$(echo "${1%/*}") + if ! [[ $ip_clean =~ ^$ip_regex\.$ip_regex\.$ip_regex\.$ip_regex$ ]]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + if [ $1 != "$ip_clean" ]; then + ip_cidr="$ip_clean/" + ip_cidr=$(echo "${1#$ip_cidr}") + if [[ "$ip_cidr" -gt 32 ]] || [[ "$ip_cidr" =~ [:alnum:] ]]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + fi } # IPv6 format validator is_ipv6_format_valid() { - object_name=${2-ip6} - ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' - t_ip=$(echo $1 | awk -F / '{print $1}') - t_cidr=$(echo $1 | awk -F / '{print $2}') - valid_cidr=1 - - WORD="[0-9A-Fa-f]\{1,4\}" - # flat address, no compressed words - FLAT="^${WORD}\(:${WORD}\)\{7\}$" - - COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" - COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" - COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" - COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" - COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" - COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" - # trailing :: edge case, includes case of only :: (all 0's) - EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" - # leading :: edge case - EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" - - echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" - if [ $? -ne 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - - if [ -n "$(echo $1 | grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 - fi - fi - if [ "$valid_cidr" -eq 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi + object_name=${2-ipv6} + ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' + t_ip=$(echo $1 |awk -F / '{print $1}') + t_cidr=$(echo $1 |awk -F / '{print $2}') + valid_cidr=1 + + WORD="[0-9A-Fa-f]\{1,4\}" + # flat address, no compressed words + FLAT="^${WORD}\(:${WORD}\)\{7\}$" + + COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" + COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" + COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" + COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" + COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" + COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" + # trailing :: edge case, includes case of only :: (all 0's) + EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" + # leading :: edge case + EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" + + echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" + if [ $? -ne 0 ]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + + if [ -n "$(echo $1|grep '/')" ]; then + if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then + valid_cidr=0 + fi + if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then + valid_cidr=0 + fi + fi + if [ "$valid_cidr" -eq 0 ]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi } is_ip46_format_valid() { - t_ip=$(echo $1 | awk -F / '{print $1}') - t_cidr=$(echo $1 | awk -F / '{print $2}') - valid_octets=0 - valid_cidr=1 - for octet in ${t_ip//./ }; do - if [[ $octet =~ ^[0-9]{1,3}$ ]] && [[ $octet -le 255 ]]; then - ((++valid_octets)) - fi - done - - if [ -n "$(echo $1 | grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 32 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 - fi - fi - if [ "$valid_octets" -lt 4 ] || [ "$valid_cidr" -eq 0 ]; then - #Check IPV6 - ipv6_valid="" - WORD="[0-9A-Fa-f]\{1,4\}" - # flat address, no compressed words - FLAT="^${WORD}\(:${WORD}\)\{7\}$" - - COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" - COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" - COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" - COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" - COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" - COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" - # trailing :: edge case, includes case of only :: (all 0's) - EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" - # leading :: edge case - EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" - - echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" - if [ $? -ne 0 ]; then - ipv6_valid="INVALID" - fi - - if [ -n "$(echo $1 | grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 - fi - fi - - if [ -n "$ipv6_valid" ] || [ "$valid_cidr" -eq 0 ]; then - check_result "$E_INVALID" "invalid IP format :: $1" - fi - fi + t_ip=$(echo $1 |awk -F / '{print $1}') + t_cidr=$(echo $1 |awk -F / '{print $2}') + valid_octets=0 + valid_cidr=1 + for octet in ${t_ip//./ }; do + if [[ $octet =~ ^[0-9]{1,3}$ ]] && [[ $octet -le 255 ]]; then + ((++valid_octets)) + fi + done + + if [ -n "$(echo $1|grep '/')" ]; then + if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 32 ]]; then + valid_cidr=0 + fi + if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then + valid_cidr=0 + fi + fi + if [ "$valid_octets" -lt 4 ] || [ "$valid_cidr" -eq 0 ]; then + #Check IPV6 + ipv6_valid="" + WORD="[0-9A-Fa-f]\{1,4\}" + # flat address, no compressed words + FLAT="^${WORD}\(:${WORD}\)\{7\}$" + + COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" + COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" + COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" + COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" + COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" + COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" + # trailing :: edge case, includes case of only :: (all 0's) + EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" + # leading :: edge case + EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" + + echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" + if [ $? -ne 0 ]; then + ipv6_valid="INVALID" + fi + + if [ -n "$(echo $1|grep '/')" ]; then + if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then + valid_cidr=0 + fi + if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then + valid_cidr=0 + fi + fi + + if [ -n "$ipv6_valid" ] || [ "$valid_cidr" -eq 0 ]; then + check_result "$E_INVALID" "invalid IP format :: $1" + fi + fi } # Proxy extention format validator is_extention_format_valid() { - exclude="[!|#|$|^|&|(|)|+|=|{|}|:|@|<|>|?|/|\|\"|'|;|%|\`| ]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid proxy extention format :: $1" - fi - is_no_new_line_format "$1" + exclude="[!|#|$|^|&|(|)|+|=|{|}|:|@|<|>|?|/|\|\"|'|;|%|\`| ]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid proxy extention format :: $1" + fi + is_no_new_line_format "$1"; } # Number format validator is_number_format_valid() { - object_name=${2-number} - if ! [[ "$1" =~ ^[0-9]+$ ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi + object_name=${2-number} + if ! [[ "$1" =~ ^[0-9]+$ ]] ; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi } # Autoreply format validator is_autoreply_format_valid() { - if [ 10240 -le ${#1} ]; then - check_result "$E_INVALID" "invalid autoreply format :: $1" - fi + if [ 10240 -le ${#1} ]; then + check_result "$E_INVALID" "invalid autoreply format :: $1" + fi } # Boolean format validator is_boolean_format_valid() { - if [ "$1" != 'yes' ] && [ "$1" != 'no' ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if [ "$1" != 'yes' ] && [ "$1" != 'no' ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Refresh IPset format validator @@ -878,661 +869,656 @@ is_refresh_ipset_format_valid() { # Common format validator is_common_format_valid() { - exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`| ]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [ 400 -le ${#1} ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ "$1" =~ @ ]] && [ ${#1} -gt 1 ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $1 =~ \* ]]; then - if [[ "$(echo $1 | grep -o '\*\.' | wc -l)" -eq 0 ]] && [[ $1 != '*' ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - fi - if [[ $(echo -n "$1" | tail -c 1) =~ [^a-zA-Z0-9_*@.] ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | grep -c '\.\.') -gt 0 ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | head -c 1) =~ [^a-zA-Z0-9_*@] ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | grep -c '\-\-') -gt 0 ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | grep -c '\_\_') -gt 0 ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1" + exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`| ]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [ 400 -le ${#1} ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ "$1" =~ @ ]] && [ ${#1} -gt 1 ] ; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $1 =~ \* ]]; then + if [[ "$(echo $1 | grep -o '\*\.' |wc -l)" -eq 0 ]] && [[ $1 != '*' ]] ; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + fi + if [[ $(echo -n "$1" | tail -c 1) =~ [^a-zA-Z0-9_*@] ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | grep -c '\.\.') -gt 0 ]];then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | head -c 1) =~ [^a-zA-Z0-9_*@] ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | grep -c '\-\-') -gt 0 ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | grep -c '\_\_') -gt 0 ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1"; } is_no_new_line_format() { - test=$(echo "$1" | head -n1) - if [[ "$test" != "$1" ]]; then - check_result "$E_INVALID" "invalid value :: $1" - fi + test=$(echo "$1" | head -n1 ); + if [[ "$test" != "$1" ]]; then + check_result "$E_INVALID" "invalid value :: $1" + fi } is_string_format_valid() { - exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1" + exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1"; } # Database format validator is_database_format_valid() { - exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" - if [[ "$1" =~ $exclude ]] || [ 64 -le ${#1} ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1" + exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" + if [[ "$1" =~ $exclude ]] || [ 64 -le ${#1} ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1"; } # Date format validator is_date_format_valid() { - if ! [[ "$1" =~ ^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$ ]]; then - check_result "$E_INVALID" "invalid date format :: $1" - fi + if ! [[ "$1" =~ ^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$ ]]; then + check_result "$E_INVALID" "invalid date format :: $1" + fi } # Database user validator is_dbuser_format_valid() { - exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" - if [ 33 -le ${#1} ]; then - check_result "$E_INVALID" "mysql username can be up to 32 characters long" - fi - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1" + exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" + if [ 33 -le ${#1} ]; then + check_result "$E_INVALID" "mysql username can be up to 32 characters long" + fi + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1" } # DNS record type validator is_dns_type_format_valid() { - known_dnstype='A,AAAA,NS,CNAME,MX,TXT,SRV,DNSKEY,KEY,IPSECKEY,PTR,SPF,TLSA,CAA,DS' - if [ -z "$(echo $known_dnstype | grep -w $1)" ]; then - check_result "$E_INVALID" "invalid dns record type format :: $1" - fi + known_dnstype='A,AAAA,NS,CNAME,MX,TXT,SRV,DNSKEY,KEY,IPSECKEY,PTR,SPF,TLSA,CAA' + if [ -z "$(echo $known_dnstype |grep -w $1)" ]; then + check_result "$E_INVALID" "invalid dns record type format :: $1" + fi } # DNS record validator is_dns_record_format_valid() { - if [ "$rtype" = 'A' ]; then - is_ip_format_valid "$1" - fi - if [ "$rtype" = 'NS' ]; then - is_domain_format_valid "${1::-1}" 'ns_record' - fi - if [ "$rtype" = 'MX' ]; then - is_domain_format_valid "${1::-1}" 'mx_record' - is_int_format_valid "$priority" 'priority_record' - fi - is_no_new_line_format "$1" + if [ "$rtype" = 'A' ]; then + is_ip_format_valid "$1" + fi + if [ "$rtype" = 'NS' ]; then + is_domain_format_valid "${1::-1}" 'ns_record' + fi + if [ "$rtype" = 'MX' ]; then + is_domain_format_valid "${1::-1}" 'mx_record' + is_int_format_valid "$priority" 'priority_record' + fi + is_no_new_line_format "$1"; } # Email format validator is_email_format_valid() { - if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]]; then - if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.(xn--)[[:alnum:]]{2,63}$ ]]; then - check_result "$E_INVALID" "invalid email format :: $1" - fi - fi + if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]] ; then + if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.(xn--)[[:alnum:]]{2,63}$ ]] ; then + check_result "$E_INVALID" "invalid email format :: $1" + fi + fi } # Firewall action validator is_fw_action_format_valid() { - if [ "$1" != "ACCEPT" ] && [ "$1" != 'DROP' ]; then - check_result "$E_INVALID" "invalid action format :: $1" - fi + if [ "$1" != "ACCEPT" ] && [ "$1" != 'DROP' ] ; then + check_result "$E_INVALID" "invalid action format :: $1" + fi } # Firewall protocol validator is_fw_protocol_format_valid() { - if [ "$1" != "ICMP" ] && [ "$1" != 'UDP' ] && [ "$1" != 'TCP' ]; then - check_result "$E_INVALID" "invalid protocol format :: $1" - fi + if [ "$1" != "ICMP" ] && [ "$1" != 'UDP' ] && [ "$1" != 'TCP' ] ; then + check_result "$E_INVALID" "invalid protocol format :: $1" + fi } # Firewall port validator is_fw_port_format_valid() { - if [ "${#1}" -eq 1 ]; then - if ! [[ "$1" =~ [0-9] ]]; then - check_result "$E_INVALID" "invalid port format :: $1" - fi - else - if ! [[ "$1" =~ ^[0-9][-|,|:|0-9]{0,30}[0-9]$ ]]; then - check_result "$E_INVALID" "invalid port format :: $1" - fi - fi + if [ "${#1}" -eq 1 ]; then + if ! [[ "$1" =~ [0-9] ]]; then + check_result "$E_INVALID" "invalid port format :: $1" + fi + else + if ! [[ "$1" =~ ^[0-9][-|,|:|0-9]{0,30}[0-9]$ ]] + then + check_result "$E_INVALID" "invalid port format :: $1" + fi + fi } # Integer validator is_int_format_valid() { - if ! [[ "$1" =~ ^[0-9]+$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[0-9]+$ ]] ; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Interface validator is_interface_format_valid() { - netdevices=$(cat /proc/net/dev | grep : | cut -f 1 -d : | tr -d ' ') - if [ -z $(echo "$netdevices" | grep -x $1) ]; then - check_result "$E_INVALID" "invalid interface format :: $1" - fi + netdevices=$(cat /proc/net/dev |grep : |cut -f 1 -d : |tr -d ' ') + if [ -z $(echo "$netdevices" |grep -x $1) ]; then + check_result "$E_INVALID" "invalid interface format :: $1" + fi } # IP status validator is_ip_status_format_valid() { - if [ -z "$(echo shared,dedicated | grep -w $1)" ]; then - check_result "$E_INVALID" "invalid status format :: $1" - fi + if [ -z "$(echo shared,dedicated | grep -w $1 )" ]; then + check_result "$E_INVALID" "invalid status format :: $1" + fi } # Cron validator is_cron_format_valid() { - limit=59 - check_format='' - if [ "$2" = 'hour' ]; then - limit=23 - fi - - if [ "$2" = 'day' ]; then - limit=31 - fi - if [ "$2" = 'month' ]; then - limit=12 - fi - if [ "$2" = 'wday' ]; then - limit=7 - fi - if [ "$1" = '*' ]; then - check_format='ok' - fi - if [[ "$1" =~ ^[\*]+[/]+[0-9] ]]; then - if [ "$(echo $1 | cut -f 2 -d /)" -lt $limit ]; then - check_format='ok' - fi - fi - if [[ "$1" =~ ^[0-9][-|,|0-9]{0,70}[\/][0-9]$ ]]; then - check_format='ok' - crn_values=${1//,/ } - crn_values=${crn_values//-/ } - crn_values=${crn_values//\// } - for crn_vl in $crn_values; do - if [ "$crn_vl" -gt $limit ]; then - check_format='invalid' - fi - done - fi - crn_values=$(echo $1 | tr "," " " | tr "-" " ") - for crn_vl in $crn_values; do - if [[ "$crn_vl" =~ ^[0-9]+$ ]] && [ "$crn_vl" -le $limit ]; then - check_format='ok' - fi - done - if [ "$check_format" != 'ok' ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + limit=59 + check_format='' + if [ "$2" = 'hour' ]; then + limit=23 + fi + + if [ "$2" = 'day' ]; then + limit=31 + fi + if [ "$2" = 'month' ]; then + limit=12 + fi + if [ "$2" = 'wday' ]; then + limit=7 + fi + if [ "$1" = '*' ]; then + check_format='ok' + fi + if [[ "$1" =~ ^[\*]+[/]+[0-9] ]]; then + if [ "$(echo $1 |cut -f 2 -d /)" -lt $limit ]; then + check_format='ok' + fi + fi + if [[ "$1" =~ ^[0-9][-|,|0-9]{0,70}[\/][0-9]$ ]]; then + check_format='ok' + crn_values=${1//,/ } + crn_values=${crn_values//-/ } + crn_values=${crn_values//\// } + for crn_vl in $crn_values; do + if [ "$crn_vl" -gt $limit ]; then + check_format='invalid' + fi + done + fi + crn_values=$(echo $1 |tr "," " " | tr "-" " ") + for crn_vl in $crn_values + do + if [[ "$crn_vl" =~ ^[0-9]+$ ]] && [ "$crn_vl" -le $limit ]; then + check_format='ok' + fi + done + if [ "$check_format" != 'ok' ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } -is_object_name_format_valid() { - if ! [[ "$1" =~ ^[-|\ |\.|_[:alnum:]]{0,50}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi -} # Name validator is_name_format_valid() { - exclude="['|\"|<|>]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "Invalid $2 contains qoutes (\" or ') :: $1" - fi - is_no_new_line_format "$1" + if ! [[ "$1" =~ ^[-|\ |\.|_[:alnum:]]{0,50}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Object validator is_object_format_valid() { - if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Role validator -is_role_valid() { - if ! [[ "$1" =~ ^admin$|^user$|^dns-cluster$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi +is_role_valid (){ + if ! [[ "$1" =~ ^admin$|^user$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Password validator is_password_format_valid() { - if [ "${#1}" -lt '6' ]; then - check_result "$E_INVALID" "invalid password format :: $1" - fi + if [ "${#1}" -lt '6' ]; then + check_result "$E_INVALID" "invalid password format :: $1" + fi } # Missing function - # Before: validate_format_shell # After: is_format_valid_shell is_format_valid_shell() { - if [ -z "$(grep -w $1 /etc/shells)" ]; then - echo "Error: shell $1 is not valid" - log_event "$E_INVALID" "$EVENT" - exit $E_INVALID - fi + if [ -z "$(grep -w $1 /etc/shells)" ]; then + echo "Error: shell $1 is not valid" + log_event "$E_INVALID" "$EVENT" + exit $E_INVALID + fi } # Service name validator is_service_format_valid() { - if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } is_hash_format_valid() { - if ! [[ "$1" =~ ^[[:alnum:]|\:|\=|_|-]{1,80}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[[:alnum:]|\:|\=|_|-]{1,80}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Format validation controller is_format_valid() { - for arg_name in $*; do - eval arg=\$$arg_name - if [ -n "$arg" ]; then - case $arg_name in - access_key_id) is_access_key_id_format_valid "$arg" "$arg_name" ;; - account) is_user_format_valid "$arg" "$arg_name" '64' ;; - action) is_fw_action_format_valid "$arg" ;; - active) is_boolean_format_valid "$arg" 'active' ;; - aliases) is_alias_format_valid "$arg" ;; - alias) is_alias_format_valid "$arg" ;; - antispam) is_boolean_format_valid "$arg" 'antispam' ;; - antivirus) is_boolean_format_valid "$arg" 'antivirus' ;; - autoreply) is_autoreply_format_valid "$arg" ;; - backup) is_object_format_valid "$arg" 'backup' ;; - charset) is_object_format_valid "$arg" "$arg_name" ;; - charsets) is_common_format_valid "$arg" 'charsets' ;; - chain) is_object_format_valid "$arg" 'chain' ;; - comment) is_object_format_valid "$arg" 'comment' ;; - database) is_database_format_valid "$arg" 'database' ;; - day) is_cron_format_valid "$arg" $arg_name ;; - dbpass) is_password_format_valid "$arg" ;; - dbuser) is_dbuser_format_valid "$arg" 'dbuser' ;; - dkim) is_boolean_format_valid "$arg" 'dkim' ;; - dkim_size) is_int_format_valid "$arg" ;; - domain) is_domain_format_valid "$arg" ;; - dom_alias) is_alias_format_valid "$arg" ;; - dvalue) is_dns_record_format_valid "$arg" ;; - email) is_email_format_valid "$arg" ;; - email_forward) is_email_format_valid "$arg" ;; - exp) is_date_format_valid "$arg" ;; - extentions) is_common_format_valid "$arg" 'extentions' ;; - format) is_type_valid 'plain json shell csv' "$arg" ;; - ftp_password) is_password_format_valid "$arg" ;; - ftp_user) is_user_format_valid "$arg" "$arg_name" ;; - hash) is_hash_format_valid "$arg" "$arg_name" ;; - host) is_object_format_valid "$arg" "$arg_name" ;; - hour) is_cron_format_valid "$arg" $arg_name ;; - id) is_int_format_valid "$arg" 'id' ;; - iface) is_interface_format_valid "$arg" ;; - ip) is_ip_format_valid "$arg" ;; - ipv6) is_ipv6_format_valid "$arg" ;; - ip46) is_ip46_format_valid "$arg" ;; - ip_name) is_domain_format_valid "$arg" 'IP name' ;; - ip_status) is_ip_status_format_valid "$arg" ;; - job) is_int_format_valid "$arg" 'job' ;; - key) is_common_format_valid "$arg" "$arg_name" ;; - malias) is_user_format_valid "$arg" "$arg_name" '64' ;; - max_db) is_int_format_valid "$arg" 'max db' ;; - min) is_cron_format_valid "$arg" $arg_name ;; - month) is_cron_format_valid "$arg" $arg_name ;; - name) is_name_format_valid "$arg" "name" ;; - nat_ip) is_ip_format_valid "$arg" ;; - netmask) is_ip_format_valid "$arg" 'netmask' ;; - newid) is_int_format_valid "$arg" 'id' ;; - ns1) is_domain_format_valid "$arg" 'ns1' ;; - ns2) is_domain_format_valid "$arg" 'ns2' ;; - ns3) is_domain_format_valid "$arg" 'ns3' ;; - ns4) is_domain_format_valid "$arg" 'ns4' ;; - ns5) is_domain_format_valid "$arg" 'ns5' ;; - ns6) is_domain_format_valid "$arg" 'ns6' ;; - ns7) is_domain_format_valid "$arg" 'ns7' ;; - ns8) is_domain_format_valid "$arg" 'ns8' ;; - object) is_object_name_format_valid "$arg" 'object' ;; - package) is_object_format_valid "$arg" "$arg_name" ;; - password) is_password_format_valid "$arg" ;; - port) is_int_format_valid "$arg" 'port' ;; - port_ext) is_fw_port_format_valid "$arg" ;; - protocol) is_fw_protocol_format_valid "$arg" ;; - proxy_ext) is_extention_format_valid "$arg" ;; - quota) is_int_format_valid "$arg" 'quota' ;; - rate) is_int_format_valid "$arg" 'rate' ;; - record) is_common_format_valid "$arg" 'record' ;; - reject) is_boolean_format_valid "$arg" 'reject' ;; - restart) is_restart_format_valid "$arg" 'restart' ;; - role) is_role_valid "$arg" 'role' ;; - rtype) is_dns_type_format_valid "$arg" ;; - rule) is_int_format_valid "$arg" "rule id" ;; - service) is_service_format_valid "$arg" "$arg_name" ;; - secret_access_key) is_secret_access_key_format_valid "$arg" "$arg_name" ;; - soa) is_domain_format_valid "$arg" 'SOA' ;; - #missing command: is_format_valid_shell - shell) is_format_valid_shell "$arg" ;; - ssl_dir) is_folder_exists "$arg" "$arg_name" ;; - stats_pass) is_password_format_valid "$arg" ;; - stats_user) is_user_format_valid "$arg" "$arg_name" ;; - template) is_object_format_valid "$arg" "$arg_name" ;; - theme) is_common_format_valid "$arg" "$arg_name" ;; - ttl) is_int_format_valid "$arg" 'ttl' ;; - user) is_user_format_valid "$arg" $arg_name ;; - wday) is_cron_format_valid "$arg" $arg_name ;; - value) is_common_format_valid "$arg" $arg_name ;; - esac - fi - done -} - -is_folder_exists() { - if [ ! -d "$1" ]; then - check_result "$E_NOTEXIST" "folder $1 does not exist" - fi -} - -is_command_valid_format() { - if [[ ! "$1" =~ ^v-[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "Invalid command format" - fi - if [[ -n $(echo "$1" | grep -e '\-\-') ]]; then - check_result "$E_INVALID" "Invalid command format" - fi + for arg_name in $*; do + eval arg=\$$arg_name + if [ -n "$arg" ]; then + case $arg_name in + access_key_id) is_access_key_id_format_valid "$arg" "$arg_name";; + account) is_user_format_valid "$arg" "$arg_name";; + action) is_fw_action_format_valid "$arg";; + active) is_boolean_format_valid "$arg" 'active' ;; + aliases) is_alias_format_valid "$arg" ;; + alias) is_alias_format_valid "$arg" ;; + antispam) is_boolean_format_valid "$arg" 'antispam' ;; + antivirus) is_boolean_format_valid "$arg" 'antivirus' ;; + autoreply) is_autoreply_format_valid "$arg" ;; + backup) is_object_format_valid "$arg" 'backup' ;; + charset) is_object_format_valid "$arg" "$arg_name" ;; + charsets) is_common_format_valid "$arg" 'charsets' ;; + chain) is_object_format_valid "$arg" 'chain' ;; + comment) is_object_format_valid "$arg" 'comment' ;; + database) is_database_format_valid "$arg" 'database';; + day) is_cron_format_valid "$arg" $arg_name ;; + dbpass) is_password_format_valid "$arg" ;; + dbuser) is_dbuser_format_valid "$arg" 'dbuser';; + dkim) is_boolean_format_valid "$arg" 'dkim' ;; + dkim_size) is_int_format_valid "$arg" ;; + domain) is_domain_format_valid "$arg" ;; + dom_alias) is_alias_format_valid "$arg" ;; + dvalue) is_dns_record_format_valid "$arg";; + email) is_email_format_valid "$arg" ;; + email_forward) is_email_format_valid "$arg" ;; + exp) is_date_format_valid "$arg" ;; + extentions) is_common_format_valid "$arg" 'extentions' ;; + format) is_type_valid 'plain json shell csv' "$arg" ;; + ftp_password) is_password_format_valid "$arg" ;; + ftp_user) is_user_format_valid "$arg" "$arg_name" ;; + hash) is_hash_format_valid "$arg" "$arg_name" ;; + host) is_object_format_valid "$arg" "$arg_name" ;; + hour) is_cron_format_valid "$arg" $arg_name ;; + id) is_int_format_valid "$arg" 'id' ;; + iface) is_interface_format_valid "$arg" ;; + ip) is_ip_format_valid "$arg" ;; + ipv6) is_ipv6_format_valid "$arg" ;; + ip46) is_ip46_format_valid "$arg" ;; + ip_name) is_domain_format_valid "$arg" 'IP name';; + ip_status) is_ip_status_format_valid "$arg" ;; + job) is_int_format_valid "$arg" 'job' ;; + key) is_common_format_valid "$arg" "$arg_name" ;; + malias) is_user_format_valid "$arg" "$arg_name" ;; + max_db) is_int_format_valid "$arg" 'max db';; + min) is_cron_format_valid "$arg" $arg_name ;; + month) is_cron_format_valid "$arg" $arg_name ;; + name) is_name_format_valid "$arg" "name" ;; + nat_ip) is_ip_format_valid "$arg" ;; + netmask) is_ip_format_valid "$arg" 'netmask' ;; + newid) is_int_format_valid "$arg" 'id' ;; + ns1) is_domain_format_valid "$arg" 'ns1' ;; + ns2) is_domain_format_valid "$arg" 'ns2' ;; + ns3) is_domain_format_valid "$arg" 'ns3' ;; + ns4) is_domain_format_valid "$arg" 'ns4' ;; + ns5) is_domain_format_valid "$arg" 'ns5' ;; + ns6) is_domain_format_valid "$arg" 'ns6' ;; + ns7) is_domain_format_valid "$arg" 'ns7' ;; + ns8) is_domain_format_valid "$arg" 'ns8' ;; + object) is_name_format_valid "$arg" 'object';; + package) is_object_format_valid "$arg" "$arg_name" ;; + password) is_password_format_valid "$arg" ;; + port) is_int_format_valid "$arg" 'port' ;; + port_ext) is_fw_port_format_valid "$arg";; + protocol) is_fw_protocol_format_valid "$arg" ;; + proxy_ext) is_extention_format_valid "$arg" ;; + quota) is_int_format_valid "$arg" 'quota' ;; + rate) is_int_format_valid "$arg" 'rate' ;; + record) is_common_format_valid "$arg" 'record';; + reject) is_boolean_format_valid "$arg" 'reject' ;; + restart) is_restart_format_valid "$arg" 'restart' ;; + role) is_role_valid "$arg" 'role' ;; + rtype) is_dns_type_format_valid "$arg" ;; + rule) is_int_format_valid "$arg" "rule id" ;; + service) is_service_format_valid "$arg" "$arg_name" ;; + secret_access_key) is_secret_access_key_format_valid "$arg" "$arg_name" ;; + soa) is_domain_format_valid "$arg" 'SOA' ;; + #missing command: is_format_valid_shell + shell) is_format_valid_shell "$arg" ;; + ssl_dir) is_folder_exists "$arg" "$arg_name" ;; + stats_pass) is_password_format_valid "$arg" ;; + stats_user) is_user_format_valid "$arg" "$arg_name" ;; + template) is_object_format_valid "$arg" "$arg_name" ;; + theme) is_common_format_valid "$arg" "$arg_name" ;; + ttl) is_int_format_valid "$arg" 'ttl';; + user) is_user_format_valid "$arg" $arg_name;; + wday) is_cron_format_valid "$arg" $arg_name ;; + value) is_common_format_valid "$arg" $arg_name;; + esac + fi + done +} + +is_folder_exists () { + if [ ! -d "$1" ]; then + check_result "$E_NOTEXIST" "folder $1 does not exist" + fi +} + +is_command_valid_format () { + if [[ ! "$1" =~ ^v-[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "Invalid command format" + fi + if [[ -n $( echo "$1" | grep -e '\-\-' ) ]]; then + check_result "$E_INVALID" "Invalid command format" + fi } # Check access_key_id name # Don't work with legacy key format is_access_key_id_format_valid() { - local hash="$1" + local hash="$1" - # ACCESS_KEY_ID format validation - if ! [[ "$hash" =~ ^[[:alnum:]]{20}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $hash" - fi + # ACCESS_KEY_ID format validation + if ! [[ "$hash" =~ ^[[:alnum:]]{20}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $hash" + fi } # SECRET_ACCESS_KEY format validation is_secret_access_key_format_valid() { - local hash="$1" + local hash="$1" - if ! [[ "$hash" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then - check_result "$E_INVALID" "invalid $2 format" - fi + if ! [[ "$hash" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then + check_result "$E_INVALID" "invalid $2 format" + fi } # Checks if the secret belongs to the access key check_access_key_secret() { - local access_key_id="$(basename "$1")" - local secret_access_key=$2 - local -n key_user=$3 + local access_key_id="$(basename "$1")" + local secret_access_key=$2 + local -n key_user=$3 - if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then - check_result "$E_PASSWORD" "Access key $access_key_id doesn't exist" - fi + if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then + check_result "$E_PASSWORD" "Access key $access_key_id doesn't exist" + fi - if [[ -z "$secret_access_key" ]]; then - check_result "$E_PASSWORD" "Secret key not provided for key $access_key_id" - elif ! [[ "$secret_access_key" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then - check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" - else - SECRET_ACCESS_KEY="" - source_conf "$HESTIA/data/access-keys/${access_key_id}" - - if [[ -z "$SECRET_ACCESS_KEY" || "$SECRET_ACCESS_KEY" != "$secret_access_key" ]]; then - check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" - fi - fi + if [[ -z "$secret_access_key" ]]; then + check_result "$E_PASSWORD" "Secret key not provided for key $access_key_id" + elif ! [[ "$secret_access_key" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then + check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" + else + SECRET_ACCESS_KEY="" + source_conf "$HESTIA/data/access-keys/${access_key_id}"; + + if [[ -z "$SECRET_ACCESS_KEY" || "$SECRET_ACCESS_KEY" != "$secret_access_key" ]]; then + check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" + fi + fi - key_user="$USER" + key_user="$USER" } # Checks if the key belongs to the user check_access_key_user() { - local access_key_id="$(basename "$1")" - local user=$2 + local access_key_id="$(basename "$1")" + local user=$2 - if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then - check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" - fi + if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then + check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" + fi - if [[ -z "$user" ]]; then - check_result "$E_FORBIDEN" "User not provided" - else - USER="" - source_conf "$HESTIA/data/access-keys/${access_key_id}" + if [[ -z "$user" ]]; then + check_result "$E_FORBIDEN" "User not provided" + else + USER="" + source_conf "$HESTIA/data/access-keys/${access_key_id}"; - if [[ -z "$USER" || "$USER" != "$user" ]]; then - check_result "$E_FORBIDEN" "key $access_key_id does not belong to the user $user" - fi - fi + if [[ -z "$USER" || "$USER" != "$user" ]]; then + check_result "$E_FORBIDEN" "key $access_key_id does not belong to the user $user" + fi + fi } # Checks if the key is allowed to run the command check_access_key_cmd() { - local access_key_id="$(basename "$1")" - local cmd=$2 - local -n user_arg_position=$3 - - if [[ "$DEBUG_MODE" = "true" ]]; then - new_timestamp - echo "[$date:$time] $1 $2" >> /var/log/hestia/api.log - fi - if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then - check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" - fi - - if [[ -z "$cmd" ]]; then - check_result "$E_FORBIDEN" "Command not provided" - elif [[ "$cmd" = 'v-make-tmp-file' ]]; then - USER="" PERMISSIONS="" - source_conf "${HESTIA}/data/access-keys/${access_key_id}" - local allowed_commands - if [[ -n "$PERMISSIONS" ]]; then - allowed_commands="$(get_apis_commands "$PERMISSIONS")" - if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - user_arg_position="0" - elif [[ ! -e "$BIN/$cmd" ]]; then - check_result "$E_FORBIDEN" "Command $cmd not found" - else - USER="" PERMISSIONS="" - source_conf "${HESTIA}/data/access-keys/${access_key_id}" - - local allowed_commands - if [[ -n "$PERMISSIONS" ]]; then - allowed_commands="$(get_apis_commands "$PERMISSIONS")" - if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - - if [[ "$USER" == "admin" ]]; then - # Admin can run commands for any user - user_arg_position="0" - else - user_arg_position="$(search_command_arg_position "$hst_command" "USER")" - if ! [[ "$user_arg_position" =~ ^[0-9]+$ ]]; then - check_result "$E_FORBIDEN" "Command $hst_command not found" - fi - fi - fi + local access_key_id="$(basename "$1")" + local cmd=$2 + local -n user_arg_position=$3 + + if [[ "$DEBUG_MODE" = "true" ]]; then + new_timestamp + echo "[$date:$time] $1 $2" >> /var/log/hestia/api.log + fi + if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then + check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" + fi + + if [[ -z "$cmd" ]]; then + check_result "$E_FORBIDEN" "Command not provided" + elif [[ "$cmd" = 'v-make-tmp-file' ]]; then + USER="" PERMISSIONS="" + source_conf "${HESTIA}/data/access-keys/${access_key_id}" + local allowed_commands + if [[ -n "$PERMISSIONS" ]]; then + allowed_commands="$(get_apis_commands "$PERMISSIONS")" + if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + user_arg_position="0" + elif [[ ! -e "$BIN/$cmd" ]]; then + check_result "$E_FORBIDEN" "Command $cmd not found" + else + USER="" PERMISSIONS="" + source_conf "${HESTIA}/data/access-keys/${access_key_id}" + + local allowed_commands + if [[ -n "$PERMISSIONS" ]]; then + allowed_commands="$(get_apis_commands "$PERMISSIONS")" + if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + + if [[ "$USER" == "admin" ]]; then + # Admin can run commands for any user + user_arg_position="0" + else + user_arg_position="$(search_command_arg_position "$hst_command" "USER")" + if ! [[ "$user_arg_position" =~ ^[0-9]+$ ]]; then + check_result "$E_FORBIDEN" "Command $hst_command not found" + fi + fi + fi } # Domain argument formatting format_domain() { - if [[ "$domain" = *[![:ascii:]]* ]]; then - if [[ "$domain" =~ [[:upper:]] ]]; then - domain=$(echo "$domain" | sed 's/[[:upper:]].*/\L&/') - fi - else - if [[ "$domain" =~ [[:upper:]] ]]; then - domain=$(echo "$domain" | tr '[:upper:]' '[:lower:]') - fi - fi - if [[ "$domain" =~ ^www\..* ]]; then - domain=$(echo "$domain" | sed -e "s/^www.//") - fi - if [[ "$domain" =~ .*\.$ ]]; then - domain=$(echo "$domain" | sed -e "s/[.]*$//g") - fi - if [[ "$domain" =~ ^\. ]]; then - domain=$(echo "$domain" | sed -e "s/^[.]*//") - fi + if [[ "$domain" = *[![:ascii:]]* ]]; then + if [[ "$domain" =~ [[:upper:]] ]]; then + domain=$(echo "$domain" |sed 's/[[:upper:]].*/\L&/') + fi + else + if [[ "$domain" =~ [[:upper:]] ]]; then + domain=$(echo "$domain" |tr '[:upper:]' '[:lower:]') + fi + fi + if [[ "$domain" =~ ^www\..* ]]; then + domain=$(echo "$domain" |sed -e "s/^www.//") + fi + if [[ "$domain" =~ .*\.$ ]]; then + domain=$(echo "$domain" |sed -e "s/[.]*$//g") + fi + if [[ "$domain" =~ ^\. ]]; then + domain=$(echo "$domain" |sed -e "s/^[.]*//") + fi } format_domain_idn() { - if [ -z "$domain_idn" ]; then - domain_idn=$domain - fi - if [[ "$domain_idn" = *[![:ascii:]]* ]]; then - domain_idn=$(idn2 --quiet $domain_idn) - fi + if [ -z "$domain_idn" ]; then + domain_idn=$domain + fi + if [[ "$domain_idn" = *[![:ascii:]]* ]]; then + domain_idn=$(idn2 --quiet $domain_idn) + fi } format_aliases() { - if [ -n "$aliases" ] && [ "$aliases" != 'none' ]; then - aliases=$(echo $aliases | tr '[:upper:]' '[:lower:]' | tr ',' '\n') - aliases=$(echo "$aliases" | sed -e "s/\.$//" | sort -u) - aliases=$(echo "$aliases" | tr -s '.') - aliases=$(echo "$aliases" | sed -e "s/[.]*$//g") - aliases=$(echo "$aliases" | sed -e "s/^[.]*//") - aliases=$(echo "$aliases" | sed -e "/^$/d") - aliases=$(echo "$aliases" | tr '\n' ',' | sed -e "s/,$//") - fi + if [ -n "$aliases" ] && [ "$aliases" != 'none' ]; then + aliases=$(echo $aliases |tr '[:upper:]' '[:lower:]' |tr ',' '\n') + aliases=$(echo "$aliases" |sed -e "s/\.$//" |sort -u) + aliases=$(echo "$aliases" |tr -s '.') + aliases=$(echo "$aliases" |sed -e "s/[.]*$//g") + aliases=$(echo "$aliases" |sed -e "s/^[.]*//") + aliases=$(echo "$aliases" |sed -e "/^$/d") + aliases=$(echo "$aliases" |tr '\n' ',' |sed -e "s/,$//") + fi } is_restart_format_valid() { - if [ "$1" != 'yes' ] && [ "$1" != 'no' ] && [ "$1" != 'ssl' ] && [ "$1" != 'reload' ] && [ "$1" != 'updatessl' ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if [ "$1" != 'yes' ] && [ "$1" != 'no' ] && [ "$1" != 'ssl' ] && [ "$1" != 'reload' ] && [ "$1" != 'updatessl' ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } check_backup_conditions() { - # Checking load average - la=$(cat /proc/loadavg | cut -f 1 -d ' ' | cut -f 1 -d '.') - # i=0 - while [ "$la" -ge "$BACKUP_LA_LIMIT" ]; do - echo -e "$(date "+%F %T") Load Average $la" - sleep 60 - la=$(cat /proc/loadavg | cut -f 1 -d ' ' | cut -f 1 -d '.') - done + # Checking load average + la=$(cat /proc/loadavg |cut -f 1 -d ' ' |cut -f 1 -d '.') + # i=0 + while [ "$la" -ge "$BACKUP_LA_LIMIT" ]; do + echo -e "$(date "+%F %T") Load Average $la" + sleep 60 + la=$(cat /proc/loadavg |cut -f 1 -d ' ' |cut -f 1 -d '.') + done } # Define download function download_file() { - local url=$1 - local destination=$2 - local force=$3 - - # Default destination is the curent working directory - local dstopt="" - - if [ -n "$(echo "$url" | grep -E "\.(gz|gzip|bz2|zip|xz)$")" ]; then - # When an archive file is downloaded it will be first saved localy - dstopt="--directory-prefix=$ARCHIVE_DIR" - local is_archive="true" - local filename="${url##*/}" - if [ -z "$filename" ]; then - echo >&2 "[!] No filename was found in url, exiting ($url)" - exit 1 - fi - if [ -n "$force" ] && [ -f "$ARCHIVE_DIR/$filename" ]; then - rm -f $ARCHIVE_DIR/$filename - fi - elif [ -n "$destination" ]; then - # Plain files will be written to specified location - dstopt="-O $destination" - fi - # check for corrupted archive - if [ -f "$ARCHIVE_DIR/$filename" ] && [ "$is_archive" = "true" ]; then - tar -tzf "$ARCHIVE_DIR/$filename" > /dev/null 2>&1 - if [ $? -ne 0 ]; then - echo >&2 "[!] Archive $ARCHIVE_DIR/$filename is corrupted, redownloading" - rm -f $ARCHIVE_DIR/$filename - fi - fi - - if [ ! -f "$ARCHIVE_DIR/$filename" ]; then - wget $url -q $dstopt --show-progress --progress=bar:force --limit-rate=3m - fi - - if [ -n "$destination" ] && [ "$is_archive" = "true" ]; then - if [ "$destination" = "-" ]; then - cat "$ARCHIVE_DIR/$filename" - elif [ -d "$(dirname $destination)" ]; then - cp "$ARCHIVE_DIR/$filename" "$destination" - fi - fi + local url=$1 + local destination=$2 + local force=$3 + + # Default destination is the curent working directory + local dstopt="" + + if [ -n "$(echo "$url" | grep -E "\.(gz|gzip|bz2|zip|xz)$")" ]; then + # When an archive file is downloaded it will be first saved localy + dstopt="--directory-prefix=$ARCHIVE_DIR" + local is_archive="true" + local filename="${url##*/}" + if [ -z "$filename" ]; then + >&2 echo "[!] No filename was found in url, exiting ($url)" + exit 1 + fi + if [ -n "$force" ] && [ -f "$ARCHIVE_DIR/$filename" ]; then + rm -f $ARCHIVE_DIR/$filename + fi + elif [ -n "$destination" ]; then + # Plain files will be written to specified location + dstopt="-O $destination" + fi + # check for corrupted archive + if [ -f "$ARCHIVE_DIR/$filename" ] && [ "$is_archive" = "true" ]; then + tar -tzf "$ARCHIVE_DIR/$filename" > /dev/null 2>&1 + if [ $? -ne 0 ]; then + >&2 echo "[!] Archive $ARCHIVE_DIR/$filename is corrupted, redownloading" + rm -f $ARCHIVE_DIR/$filename + fi + fi + + if [ ! -f "$ARCHIVE_DIR/$filename" ]; then + wget $url -q $dstopt --show-progress --progress=bar:force --limit-rate=3m + fi + + if [ -n "$destination" ] && [ "$is_archive" = "true" ]; then + if [ "$destination" = "-" ]; then + cat "$ARCHIVE_DIR/$filename" + elif [ -d "$(dirname $destination)" ]; then + cp "$ARCHIVE_DIR/$filename" "$destination" + fi + fi } check_hestia_demo_mode() { - demo_mode=$(grep DEMO_MODE /usr/local/hestia/conf/hestia.conf | cut -d '=' -f2 | sed "s|'||g") - if [ -n "$demo_mode" ] && [ "$demo_mode" = "yes" ]; then - echo "ERROR: Unable to perform operation due to security restrictions that are in place." - exit 1 - fi + demo_mode=$(grep DEMO_MODE /usr/local/hestia/conf/hestia.conf | cut -d '=' -f2 | sed "s|'||g") + if [ -n "$demo_mode" ] && [ "$demo_mode" = "yes" ]; then + echo "ERROR: Unable to perform operation due to security restrictions that are in place." + exit 1 + fi } multiphp_count() { - $BIN/v-list-sys-php plain | wc -l + $BIN/v-list-sys-php plain | wc -l } multiphp_versions() { - local -a php_versions_list - local php_ver - if [ "$(multiphp_count)" -gt 0 ]; then - for php_ver in $($BIN/v-list-sys-php plain); do - [ ! -d "/etc/php/$php_ver/fpm/pool.d/" ] && continue - php_versions_list+=($php_ver) - done - echo "${php_versions_list[@]}" - fi + local -a php_versions_list; + local php_ver; + if [ "$(multiphp_count)" -gt 0 ] ; then + for php_ver in $( $BIN/v-list-sys-php plain); do + [ ! -d "/etc/php/$php_ver/fpm/pool.d/" ] && continue + php_versions_list+=($php_ver) + done + echo "${php_versions_list[@]}" + fi } multiphp_default_version() { - # Get system wide default php version (set by update-alternatives) - local sys_phpversion=$(php -r "echo substr(phpversion(),0,3);") - - # Check if the system php also has php-fpm enabled, otherwise return - # the most recent php version which does have it installed. - if [ ! -d "/etc/php/$sys_phpversion/fpm/pool.d/" ]; then - local all_versions="$(multiphp_versions)" - if [ -n "$all_versions" ]; then - sys_phpversion="${all_versions##*\ }" - fi - fi + # Get system wide default php version (set by update-alternatives) + local sys_phpversion=$(php -r "echo substr(phpversion(),0,3);") + + # Check if the system php also has php-fpm enabled, otherwise return + # the most recent php version which does have it installed. + if [ ! -d "/etc/php/$sys_phpversion/fpm/pool.d/" ]; then + local all_versions="$(multiphp_versions)" + if [ -n "$all_versions" ]; then + sys_phpversion="${all_versions##*\ }"; + fi + fi - echo "$sys_phpversion" + echo "$sys_phpversion" } -is_hestia_package() { - check=false - for pkg in $1; do - if [ "$pkg" == "$2" ]; then - check="true" - fi - done - if [ "$check" != "true" ]; then - check_result $E_INVALID "$2 package is not controlled by hestiacp" - fi +is_hestia_package(){ + check=false; + for pkg in $1; do + if [ "$pkg" == "$2" ]; then + check="true"; + fi + done + if [ "$check" != "true" ]; then + check_result $E_INVALID "$2 package is not controlled by hestiacp" + fi } # Run arbitrary cli commands with dropped privileges @@ -1540,136 +1526,136 @@ is_hestia_package() { # Input: # - $user : Vaild hestia user user_exec() { - is_object_valid 'user' 'USER' "$user" + is_object_valid 'user' 'USER' "$user" - local user_groups=$(id -G "$user") - user_groups=${user_groups//\ /,} + local user_groups=$(id -G "$user") + user_groups=${user_groups//\ /,} - setpriv --groups "$user_groups" --reuid "$user" --regid "$user" -- "${@}" + setpriv --groups "$user_groups" --reuid "$user" --regid "$user" -- "${@}" } # Simple chmod wrapper that skips symlink files after glob expand no_symlink_chmod() { - local filemode=$1 - shift + local filemode=$1; shift; - for i in "$@"; do - [[ -L ${i} ]] && continue + for i in "$@"; do + [[ -L ${i} ]] && continue - chmod "${filemode}" "${i}" - done + chmod "${filemode}" "${i}" + done } -source_conf() { - while IFS='= ' read -r lhs rhs; do - if [[ ! $lhs =~ ^\ *# && -n $lhs ]]; then - rhs="${rhs%%^\#*}" # Del in line right comments - rhs="${rhs%%*( )}" # Del trailing spaces - rhs="${rhs%\'*}" # Del opening string quotes - rhs="${rhs#\'*}" # Del closing string quotes - declare -g $lhs="$rhs" - fi - done < $1 +source_conf(){ + while IFS='= ' read -r lhs rhs + do + if [[ ! $lhs =~ ^\ *# && -n $lhs ]]; then + rhs="${rhs%%^\#*}" # Del in line right comments + rhs="${rhs%%*( )}" # Del trailing spaces + rhs="${rhs%\'*}" # Del opening string quotes + rhs="${rhs#\'*}" # Del closing string quotes + declare -g $lhs="$rhs" + fi + done < $1 } format_no_quotes() { - exclude="['|\"]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "Invalid $2 contains qoutes (\" or ') :: $1" - fi - is_no_new_line_format "$1" + exclude="['|\"]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "Invalid $2 contains qoutes (\" or ') :: $1" + fi + is_no_new_line_format "$1" } -is_username_format_valid() { - if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]]; then - is_string_format_valid "$1" "$2" - fi +is_username_format_valid(){ + if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]] ; then + is_string_format_valid "$1" "$2" + fi } -change_sys_value() { - check_ckey=$(grep "^$1='" "$HESTIA/conf/hestia.conf") - if [ -z "$check_ckey" ]; then - echo "$1='$2'" >> "$HESTIA/conf/hestia.conf" - else - sed -i "s|^$1=.*|$1='$2'|g" "$HESTIA/conf/hestia.conf" - fi +change_sys_value(){ + check_ckey=$(grep "^$1='" "$HESTIA/conf/hestia.conf") + if [ -z "$check_ckey" ]; then + echo "$1='$2'" >> "$HESTIA/conf/hestia.conf" + else + sed -i "s|^$1=.*|$1='$2'|g" "$HESTIA/conf/hestia.conf" + fi } # Checks the format of APIs that will be allowed for the key is_key_permissions_format_valid() { - local permissions="$1" - local user="$2" + local permissions="$1" + local user="$2" - if [[ "$user" != "admin" && -z "$permissions" ]]; then - check_result "$E_INVALID" "Non-admin users need a permission list" - fi + if [[ "$user" != "admin" && -z "$permissions" ]]; then + check_result "$E_INVALID" "Non-admin users need a permission list" + fi - while IFS=',' read -ra permissions_arr; do - for permission in "${permissions_arr[@]}"; do - permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" + while IFS=',' read -ra permissions_arr; do + for permission in "${permissions_arr[@]}"; do + permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" - # if [[ -z "$(echo "$permission" | grep -E "^v-")" ]]; then - if [[ ! -e "$HESTIA/data/api/$permission" ]]; then - check_result "$E_NOTEXIST" "API $permission doesn't exist" - fi +# if [[ -z "$(echo "$permission" | grep -E "^v-")" ]]; then + if [[ ! -e "$HESTIA/data/api/$permission" ]]; then + check_result "$E_NOTEXIST" "API $permission doesn't exist" + fi - source_conf "$HESTIA/data/api/$permission" - if [ "$ROLE" = "admin" ] && [ "$user" != "admin" ]; then - check_result "$E_INVALID" "Only the admin can run this API" - fi - # elif [[ ! -e "$BIN/$permission" ]]; then - # check_result "$E_NOTEXIST" "Command $permission doesn't exist" - # fi - done - done <<< "$permissions" + source_conf "$HESTIA/data/api/$permission"; + if [ "$ROLE" = "admin" ] && [ "$user" != "admin" ]; then + check_result "$E_INVALID" "Only the admin can run this API" + fi +# elif [[ ! -e "$BIN/$permission" ]]; then +# check_result "$E_NOTEXIST" "Command $permission doesn't exist" +# fi + done + done <<<"$permissions" } # Remove whitespaces, and bin path from commands cleanup_key_permissions() { - local permissions="$1" + local permissions="$1" - local final quote - while IFS=',' read -ra permissions_arr; do - for permission in "${permissions_arr[@]}"; do - permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" + local final quote + while IFS=',' read -ra permissions_arr; do + for permission in "${permissions_arr[@]}"; do + permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" - # Avoid duplicate items - if [[ -z "$(echo ",${final}," | grep ",${permission},")" ]]; then - final+="${quote}${permission}" - quote=',' - fi - done - done <<< "$permissions" + # Avoid duplicate items + if [[ -z "$(echo ",${final}," | grep ",${permission},")" ]]; then + final+="${quote}${permission}" + quote=',' + fi + done + done <<<"$permissions" - echo "$final" + echo "$final" } # Extract all allowed commands from a permission list get_apis_commands() { - local permissions="$1" + local permissions="$1" - local allowed_commands quote commands_to_add - while IFS=',' read -ra permissions_arr; do - for permission in "${permissions_arr[@]}"; do - permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" + local allowed_commands quote commands_to_add + while IFS=',' read -ra permissions_arr; do + for permission in "${permissions_arr[@]}"; do + permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" - commands_to_add="" - # if [[ -n "$(echo "$permission" | grep -E "^v-")" ]]; then - # commands_to_add="$permission" - # el - if [[ -e "$HESTIA/data/api/$permission" ]]; then - source_conf "$HESTIA/data/api/$permission" - commands_to_add="$COMMANDS" - fi + commands_to_add="" +# if [[ -n "$(echo "$permission" | grep -E "^v-")" ]]; then +# commands_to_add="$permission" +# el + if [[ -e "$HESTIA/data/api/$permission" ]]; then + source_conf "$HESTIA/data/api/$permission"; + commands_to_add="$COMMANDS" + fi - if [[ -n "$commands_to_add" ]]; then - allowed_commands+="${quote}${commands_to_add}" - quote=',' - fi - done - done <<< "$permissions" + if [[ -n "$commands_to_add" ]]; then + allowed_commands+="${quote}${commands_to_add}" + quote=',' + fi + done + done <<<"$permissions" - cleanup_key_permissions "$allowed_commands" + cleanup_key_permissions "$allowed_commands" } # Get the position of an argument by name in a hestia command using the command's documentation comment. @@ -1678,28 +1664,28 @@ get_apis_commands() { # * 0: It doesn't have the argument; # * 1-9: The position of the argument in the command. search_command_arg_position() { - local hst_command="$(basename "$1")" - local arg_name="$2" - - local command_path="$BIN/$hst_command" - if [[ -z "$hst_command" || ! -e "$command_path" ]]; then - echo "-1" - return - fi - - local position=0 - local count=0 - local command_options="$(sed -En 's/^# options: (.+)/\1/p' "$command_path")" - while IFS=' ' read -ra options_arr; do - for option in "${options_arr[@]}"; do - count=$((count + 1)) - - option_name="$(echo " $option " | sed -E 's/^(\s|\[)*|(\s|\])*$//g')" - if [[ "${option_name^^}" == "$arg_name" ]]; then - position=$count - fi - done - done <<< "$command_options" - - echo "$position" + local hst_command="$(basename "$1")" + local arg_name="$2" + + local command_path="$BIN/$hst_command" + if [[ -z "$hst_command" || ! -e "$command_path" ]]; then + echo "-1" + return + fi + + local position=0 + local count=0 + local command_options="$(sed -En 's/^# options: (.+)/\1/p' "$command_path")" + while IFS=' ' read -ra options_arr; do + for option in "${options_arr[@]}"; do + count=$((count+1)) + + option_name="$(echo " $option " | sed -E 's/^(\s|\[)*|(\s|\])*$//g')" + if [[ "${option_name^^}" == "$arg_name" ]]; then + position=$count + fi + done + done <<<"$command_options" + + echo "$position" } From 8cf89237c7905cca3b78c677ce6fb862155428f9 Mon Sep 17 00:00:00 2001 From: asmcc Date: Mon, 13 Feb 2023 00:10:24 +0100 Subject: [PATCH 011/223] New shell script to list of parameters for IPV6 adresses --- bin/v-list-sys-ipv6 | 103 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100755 bin/v-list-sys-ipv6 diff --git a/bin/v-list-sys-ipv6 b/bin/v-list-sys-ipv6 new file mode 100755 index 0000000000..bfdad7cae1 --- /dev/null +++ b/bin/v-list-sys-ipv6 @@ -0,0 +1,103 @@ +#!/bin/bash +# info: list system ip +# options: IP [FORMAT] +# +# example: v-list-sys-ip 116.203.78.202 +# +# This function for getting the list of system ip parameters. + +#----------------------------------------------------------# +# Variables & Functions # +#----------------------------------------------------------# + +# Argument definition +ipv6=$1 +format=${2-shell} + +# 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 +# shellcheck source=/usr/local/hestia/func/ipv6.sh +source $HESTIA/func/ipv6.sh +# load config file +source_conf "$HESTIA/conf/hestia.conf" + +# JSON list function +json_list() { + echo '{' + echo ' "'$ipv6'": { + "OWNER": "'$OWNER'", + "STATUS": "'$STATUS'", + "NAME": "'$NAME'", + "U_SYS_USERS": "'$U_SYS_USERS'", + "U_WEB_DOMAINS": "'$U_WEB_DOMAINS'", + "INTERFACE": "'$INTERFACE'", + "NETMASK": "'$NETMASK'", + "NAT": "'$NAT'", + "TIME": "'$TIME'", + "DATE": "'$DATE'" + }' + echo '}' +} + +# SHELL list function +shell_list() { + echo "IP: $ipv6" + echo "NETMASK: $NETMASK" + echo "INTERFACE: $INTERFACE" + echo "NAT: $NAT" + echo "OWNER: $OWNER" + echo "STATUS: $STATUS" + echo "NAME: $NAME" + echo "USERS: $U_SYS_USERS" + echo "DOMAINS: $U_WEB_DOMAINS" + echo "TIME: $TIME" + echo "DATE: $DATE" +} + +# PLAIN list function +plain_list() { + echo -ne "$ipv6\t$OWNER\t$STATUS\t$NAME\t$U_SYS_USERS\t$U_WEB_DOMAINS\t" + echo -e "$INTERFACE\t$NETMASK\t$NAT\t$TIME\t$DATE" +} + +# CSV list function +csv_list() { + echo -n "IP,OWNER,STATUS,NAME,U_SYS_USERS,U_WEB_DOMAINS,INTERFACE" + echo "NETMASK,NAT,TIME,DATE" + echo -n "$ipv6,$OWNER,$STATUS,$NAME,\"$U_SYS_USERS\",$U_WEB_DOMAINS," + echo "$INTERFACE, $NETMASK,$NAT,$TIME,$DATE" +} + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'IP [FORMAT]' +is_format_valid 'ipv6' +if [ ! -e "$HESTIA/data/ips/$ipv6" ]; then + check_result $E_NOTEXIST "ip $ipv6 doesn't exist" +fi + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Parsing ip +source_conf "$HESTIA/data/ips/$ipv6" + +# Listing data +case $format in + json) json_list ;; + plain) plain_list ;; + csv) csv_list ;; + shell) shell_list ;; +esac + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +exit From de25000dd26b79936a328a106a864faaee143bb6 Mon Sep 17 00:00:00 2001 From: asmcc Date: Mon, 13 Feb 2023 00:16:41 +0100 Subject: [PATCH 012/223] Add execution permission for some IPV6 shell scripts under bin --- bin/v-add-firewall-ipv6-rule | 0 bin/v-add-sys-ipv6 | 0 bin/v-change-dns-domain-ipv6 | 0 bin/v-change-firewall-ipv6-rule | 0 bin/v-change-sys-ipv6-name | 0 bin/v-change-sys-ipv6-owner | 0 bin/v-change-sys-ipv6-status | 0 bin/v-change-web-domain-ipv6 | 0 bin/v-delete-firewall-ipv6-rule | 0 bin/v-delete-sys-ipv6 | 0 bin/v-list-firewall-ipv6 | 0 bin/v-list-firewall-ipv6-rule | 0 bin/v-suspend-firewall-ipv6-rule | 0 13 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/v-add-firewall-ipv6-rule mode change 100644 => 100755 bin/v-add-sys-ipv6 mode change 100644 => 100755 bin/v-change-dns-domain-ipv6 mode change 100644 => 100755 bin/v-change-firewall-ipv6-rule mode change 100644 => 100755 bin/v-change-sys-ipv6-name mode change 100644 => 100755 bin/v-change-sys-ipv6-owner mode change 100644 => 100755 bin/v-change-sys-ipv6-status mode change 100644 => 100755 bin/v-change-web-domain-ipv6 mode change 100644 => 100755 bin/v-delete-firewall-ipv6-rule mode change 100644 => 100755 bin/v-delete-sys-ipv6 mode change 100644 => 100755 bin/v-list-firewall-ipv6 mode change 100644 => 100755 bin/v-list-firewall-ipv6-rule mode change 100644 => 100755 bin/v-suspend-firewall-ipv6-rule diff --git a/bin/v-add-firewall-ipv6-rule b/bin/v-add-firewall-ipv6-rule old mode 100644 new mode 100755 diff --git a/bin/v-add-sys-ipv6 b/bin/v-add-sys-ipv6 old mode 100644 new mode 100755 diff --git a/bin/v-change-dns-domain-ipv6 b/bin/v-change-dns-domain-ipv6 old mode 100644 new mode 100755 diff --git a/bin/v-change-firewall-ipv6-rule b/bin/v-change-firewall-ipv6-rule old mode 100644 new mode 100755 diff --git a/bin/v-change-sys-ipv6-name b/bin/v-change-sys-ipv6-name old mode 100644 new mode 100755 diff --git a/bin/v-change-sys-ipv6-owner b/bin/v-change-sys-ipv6-owner old mode 100644 new mode 100755 diff --git a/bin/v-change-sys-ipv6-status b/bin/v-change-sys-ipv6-status old mode 100644 new mode 100755 diff --git a/bin/v-change-web-domain-ipv6 b/bin/v-change-web-domain-ipv6 old mode 100644 new mode 100755 diff --git a/bin/v-delete-firewall-ipv6-rule b/bin/v-delete-firewall-ipv6-rule old mode 100644 new mode 100755 diff --git a/bin/v-delete-sys-ipv6 b/bin/v-delete-sys-ipv6 old mode 100644 new mode 100755 diff --git a/bin/v-list-firewall-ipv6 b/bin/v-list-firewall-ipv6 old mode 100644 new mode 100755 diff --git a/bin/v-list-firewall-ipv6-rule b/bin/v-list-firewall-ipv6-rule old mode 100644 new mode 100755 diff --git a/bin/v-suspend-firewall-ipv6-rule b/bin/v-suspend-firewall-ipv6-rule old mode 100644 new mode 100755 From 0c250ef4dceada8a446d12db4a707fb5a735860b Mon Sep 17 00:00:00 2001 From: asmcc <40981206+asmcc@users.noreply.github.com> Date: Mon, 13 Feb 2023 00:32:56 +0100 Subject: [PATCH 013/223] Revert "New shell script to list of parameters for IPV6 adresses" This reverts commit 5c9f8321c04a07e35d64d5e9770f22bd7528aa31. --- func/main.sh | 2426 +++++++++++++++++++++++++------------------------- 1 file changed, 1220 insertions(+), 1206 deletions(-) diff --git a/func/main.sh b/func/main.sh index 0f7d0b53da..a6dc3a5f0b 100644 --- a/func/main.sh +++ b/func/main.sh @@ -11,10 +11,11 @@ HOMEDIR='/home' BACKUP='/backup' BACKUP_GZIP=9 BACKUP_DISK_LIMIT=95 -BACKUP_LA_LIMIT=`cat /proc/cpuinfo | grep processor | wc -l` +BACKUP_LA_LIMIT=$(cat /proc/cpuinfo | grep processor | wc -l) RRD_STEP=300 BIN=$HESTIA/bin -HESTIA_INSTALL_DIR=$HESTIA/install/deb +HESTIA_INSTALL_DIR="$HESTIA/install/deb" +HESTIA_COMMON_DIR="$HESTIA/install/common" HESTIA_BACKUP="/root/hst_backups/$(date +%d%m%Y%H%M)" USER_DATA=$HESTIA/data/users/$user WEBTPL=$HESTIA/data/templates/web @@ -53,811 +54,819 @@ E_RESTART=20 # Detect operating system detect_os() { - if [ -e "/etc/os-release" ]; then - get_os_type=$(grep "^ID=" /etc/os-release | cut -f 2 -d '=') - if [ "$get_os_type" = "ubuntu" ]; then - if [ -e '/usr/bin/lsb_release' ]; then - OS_VERSION="$(lsb_release -s -r)" - OS_TYPE='Ubuntu' - fi - elif [ "$get_os_type" = "debian" ]; then - OS_TYPE='Debian' - OS_VERSION=$(cat /etc/debian_version|grep -o "[0-9]\{1,2\}"|head -n1) - fi - else - OS_TYPE="Unsupported OS" - OS_VERSION="Unknown" - fi + if [ -e "/etc/os-release" ]; then + get_os_type=$(grep "^ID=" /etc/os-release | cut -f 2 -d '=') + if [ "$get_os_type" = "ubuntu" ]; then + if [ -e '/usr/bin/lsb_release' ]; then + OS_VERSION="$(lsb_release -s -r)" + OS_TYPE='Ubuntu' + fi + elif [ "$get_os_type" = "debian" ]; then + OS_TYPE='Debian' + OS_VERSION=$(cat /etc/debian_version | grep -o "[0-9]\{1,2\}" | head -n1) + fi + else + OS_TYPE="Unsupported OS" + OS_VERSION="Unknown" + fi } # Generate time stamp new_timestamp() { - time_n_date=$(date +'%T %F') - time=$(echo "$time_n_date" |cut -f 1 -d \ ) - date=$(echo "$time_n_date" |cut -f 2 -d \ ) + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" | cut -f 1 -d \ ) + date=$(echo "$time_n_date" | cut -f 2 -d \ ) } # Event string for logger ARGS=("$@") -for ((I=1; I <= $# ; I++)); do - if [[ "$HIDE" != "$I" ]]; then - ARGUMENTS="$ARGUMENTS '${ARGS[${I}-1]}'" - else - ARGUMENTS="$ARGUMENTS '******'" - fi +for ((I = 1; I <= $#; I++)); do + if [[ "$HIDE" != "$I" ]]; then + ARGUMENTS="$ARGUMENTS '${ARGS[${I} - 1]}'" + else + ARGUMENTS="$ARGUMENTS '******'" + fi done # Log event function log_event() { - if [ -z "$time" ]; then - LOG_TIME="$(date +'%F %T') $(basename $0)" - else - LOG_TIME="$date $time $(basename $0)" - fi - if [ "$1" -eq 0 ]; then - echo "$LOG_TIME $2" >> $HESTIA/log/system.log - else - echo "$LOG_TIME $2 [Error $1]" >> $HESTIA/log/error.log - fi + if [ -z "$time" ]; then + LOG_TIME="$(date +'%F %T') $(basename $0)" + else + LOG_TIME="$date $time $(basename $0)" + fi + if [ "$1" -eq 0 ]; then + echo "$LOG_TIME $2" >> $HESTIA/log/system.log + else + echo "$LOG_TIME $2 [Error $1]" >> $HESTIA/log/error.log + fi } # Log user history log_history() { - message=${1//\'/\´} # Avoid single quotes broken the log - evt_level=${2:-$event_level} - log_user=${3-$user} - evt_category=${4:-$event_category} - - # Set default event level and category if not specified - if [ -z "$evt_level" ]; then - evt_level="Info" - fi - if [ -z "$evt_category" ]; then - evt_category="System" - fi - - # Log system events to system log file - if [ "$log_user" = "system" ]; then - log=$HESTIA/data/users/admin/system.log - else - if ! $BIN/v-list-user "$log_user" >/dev/null; then - return $E_NOTEXIST - fi - log=$HESTIA/data/users/$log_user/history.log - fi - touch $log - - # TODO: Improve log pruning and pagination - # - #if [ '1000' -lt "$(wc -l $log |cut -f 1 -d ' ')" ]; then - # tail -n 499 $log > $log.moved - # mv -f $log.moved $log - # chmod 660 $log - #fi - - if [ -z "$date" ]; then - time_n_date=$(date +'%T %F') - time=$(echo "$time_n_date" |cut -f 1 -d \ ) - date=$(echo "$time_n_date" |cut -f 2 -d \ ) - fi - curr_str=$(grep "ID=" $log | cut -f 2 -d \' | sort -n | tail -n1) - id="$((curr_str +1))" - echo "ID='$id' DATE='$date' TIME='$time' LEVEL='$evt_level' CATEGORY='$evt_category' MESSAGE='$message'" >> $log + message=${1//\'/\´} # Avoid single quotes broken the log + evt_level=${2:-$event_level} + log_user=${3-$user} + evt_category=${4:-$event_category} + + # Set default event level and category if not specified + if [ -z "$evt_level" ]; then + evt_level="Info" + fi + if [ -z "$evt_category" ]; then + evt_category="System" + fi + + # Log system events to system log file + if [ "$log_user" = "system" ]; then + log=$HESTIA/data/users/admin/system.log + else + if ! $BIN/v-list-user "$log_user" > /dev/null; then + return $E_NOTEXIST + fi + log=$HESTIA/data/users/$log_user/history.log + fi + touch $log + + # TODO: Improve log pruning and pagination + # + #if [ '1000' -lt "$(wc -l $log |cut -f 1 -d ' ')" ]; then + # tail -n 499 $log > $log.moved + # mv -f $log.moved $log + # chmod 660 $log + #fi + + if [ -z "$date" ]; then + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" | cut -f 1 -d \ ) + date=$(echo "$time_n_date" | cut -f 2 -d \ ) + fi + curr_str=$(grep "ID=" $log | cut -f 2 -d \' | sort -n | tail -n1) + id="$((curr_str + 1))" + echo "ID='$id' DATE='$date' TIME='$time' LEVEL='$evt_level' CATEGORY='$evt_category' MESSAGE='$message'" >> $log } # Result checker check_result() { - if [ $1 -ne 0 ]; then - local err_code="${3:-$1}" - if [[ -n "$CHECK_RESULT_CALLBACK" && "$(type -t "$CHECK_RESULT_CALLBACK")" == 'function' ]]; then - $CHECK_RESULT_CALLBACK "$err_code" "$2" - else - echo "Error: $2" - log_event "$err_code" "$ARGUMENTS" - fi - - exit $err_code - fi + if [ $1 -ne 0 ]; then + local err_code="${3:-$1}" + if [[ -n "$CHECK_RESULT_CALLBACK" && "$(type -t "$CHECK_RESULT_CALLBACK")" == 'function' ]]; then + $CHECK_RESULT_CALLBACK "$err_code" "$2" + else + echo "Error: $2" + log_event "$err_code" "$ARGUMENTS" + fi + + exit $err_code + fi } # Argument list checker check_args() { - if [ "$1" -gt "$2" ]; then - echo "Usage: $(basename $0) $3" - check_result "$E_ARGS" "not enought arguments" >/dev/null - fi + if [ "$1" -gt "$2" ]; then + echo "Usage: $(basename $0) $3" + check_result "$E_ARGS" "not enought arguments" > /dev/null + fi } # Subsystem checker is_system_enabled() { - if [ -z "$1" ] || [ "$1" = no ]; then - check_result "$E_DISABLED" "$2 is not enabled" - fi + if [ -z "$1" ] || [ "$1" = no ]; then + check_result "$E_DISABLED" "$2 is not enabled" + fi } - # User package check is_package_full() { - case "$1" in - WEB_DOMAINS) used=$(wc -l $USER_DATA/web.conf);; - WEB_ALIASES) used=$(echo $aliases |tr ',' '\n' |wc -l);; - DNS_DOMAINS) used=$(wc -l $USER_DATA/dns.conf);; - DNS_RECORDS) used=$(wc -l $USER_DATA/dns/$domain.conf);; - MAIL_DOMAINS) used=$(wc -l $USER_DATA/mail.conf);; - MAIL_ACCOUNTS) used=$(wc -l $USER_DATA/mail/$domain.conf);; - DATABASES) used=$(wc -l $USER_DATA/db.conf);; - CRON_JOBS) used=$(wc -l $USER_DATA/cron.conf);; - esac - used=$(echo "$used"| cut -f 1 -d \ ) - limit=$(grep "^$1=" $USER_DATA/user.conf |cut -f 2 -d \') - if [ "$1" = WEB_ALIASES ]; then - # Used is always calculated with the new alias added - if [ "$limit" != 'unlimited' ] && [[ "$used" -gt "$limit" ]]; then - check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" - fi - else - if [ "$limit" != 'unlimited' ] && [[ "$used" -ge "$limit" ]]; then - check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" - fi - fi + case "$1" in + WEB_DOMAINS) used=$(wc -l $USER_DATA/web.conf) ;; + WEB_ALIASES) used=$(echo $aliases | tr ',' '\n' | wc -l) ;; + DNS_DOMAINS) used=$(wc -l $USER_DATA/dns.conf) ;; + DNS_RECORDS) used=$(wc -l $USER_DATA/dns/$domain.conf) ;; + MAIL_DOMAINS) used=$(wc -l $USER_DATA/mail.conf) ;; + MAIL_ACCOUNTS) used=$(wc -l $USER_DATA/mail/$domain.conf) ;; + DATABASES) used=$(wc -l $USER_DATA/db.conf) ;; + CRON_JOBS) used=$(wc -l $USER_DATA/cron.conf) ;; + esac + used=$(echo "$used" | cut -f 1 -d \ ) + limit=$(grep "^$1=" $USER_DATA/user.conf | cut -f 2 -d \') + if [ "$1" = WEB_ALIASES ]; then + # Used is always calculated with the new alias added + if [ "$limit" != 'unlimited' ] && [[ "$used" -gt "$limit" ]]; then + check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" + fi + else + if [ "$limit" != 'unlimited' ] && [[ "$used" -ge "$limit" ]]; then + check_result "$E_LIMIT" "$1 limit is reached :: upgrade user package" + fi + fi } # User owner for reseller plugin get_user_owner() { - if [ -z "$RESELLER_KEY" ]; then - owner='admin' - else - owner=$(grep "^OWNER" $USER_DATA/user.conf| cut -f 2 -d \') - if [ -z "$owner" ]; then - owner='admin' - fi - fi + if [ -z "$RESELLER_KEY" ]; then + owner='admin' + else + owner=$(grep "^OWNER" $USER_DATA/user.conf | cut -f 2 -d \') + if [ -z "$owner" ]; then + owner='admin' + fi + fi } # Random password generator generate_password() { - matrix=$1 - length=$2 - if [ -z "$matrix" ]; then - matrix="A-Za-z0-9" - fi - if [ -z "$length" ]; then - length=16 - fi - head /dev/urandom | tr -dc $matrix | head -c$length + matrix=$1 + length=$2 + if [ -z "$matrix" ]; then + matrix="A-Za-z0-9" + fi + if [ -z "$length" ]; then + length=16 + fi + head /dev/urandom | tr -dc $matrix | head -c$length } # Package existence check is_package_valid() { - if [ -z $1 ]; then - if [ ! -e "$HESTIA/data/packages/$package.pkg" ]; then - check_result "$E_NOTEXIST" "package $package doesn't exist" - fi - else - if [ ! -e "$HESTIA/data/packages/$1.pkg" ]; then - check_result "$E_NOTEXIST" "package $1 doesn't exist" - fi - fi + if [ -z $1 ]; then + if [ ! -e "$HESTIA/data/packages/$package.pkg" ]; then + check_result "$E_NOTEXIST" "package $package doesn't exist" + fi + else + if [ ! -e "$HESTIA/data/packages/$1.pkg" ]; then + check_result "$E_NOTEXIST" "package $1 doesn't exist" + fi + fi } is_package_new() { - if [ -e "$HESTIA/data/packages/$1.pkg" ]; then - echo "Error: package $1 already exists." - log_event "$E_EXISTS" "$ARGUMENTS" - exit "$E_EXISTS" - fi + if [ -e "$HESTIA/data/packages/$1.pkg" ]; then + echo "Error: package $1 already exists." + log_event "$E_EXISTS" "$ARGUMENTS" + exit "$E_EXISTS" + fi } # Validate system type is_type_valid() { - if [ -z "$(echo $1 | grep -w $2)" ]; then - check_result "$E_INVALID" "$2 type is invalid" - fi + if [ -z "$(echo $1 | grep -w $2)" ]; then + check_result "$E_INVALID" "$2 type is invalid" + fi } # Check user backup settings is_backup_enabled() { - BACKUPS=$(grep "^BACKUPS=" $USER_DATA/user.conf | cut -f2 -d \') - if [ -z "$BACKUPS" ] || [[ "$BACKUPS" -le '0' ]]; then - check_result "$E_DISABLED" "user backup is disabled" - fi + BACKUPS=$(grep "^BACKUPS=" $USER_DATA/user.conf | cut -f2 -d \') + if [ -z "$BACKUPS" ] || [[ "$BACKUPS" -le '0' ]]; then + check_result "$E_DISABLED" "user backup is disabled" + fi } # Check user backup settings is_backup_scheduled() { - if [ -e "$HESTIA/data/queue/backup.pipe" ]; then - check_q=$(grep " $user " $HESTIA/data/queue/backup.pipe | grep $1) - if [ -n "$check_q" ]; then - check_result "$E_EXISTS" "$1 is already scheduled" - fi - fi + if [ -e "$HESTIA/data/queue/backup.pipe" ]; then + check_q=$(grep " $user " $HESTIA/data/queue/backup.pipe | grep $1) + if [ -n "$check_q" ]; then + check_result "$E_EXISTS" "$1 is already scheduled" + fi + fi } # Check if object is new is_object_new() { - if [ $2 = 'USER' ]; then - if [ -d "$USER_DATA" ]; then - object="OK" - fi - else - object=$(grep "$2='$3'" $USER_DATA/$1.conf) - fi - if [ -n "$object" ]; then - check_result "$E_EXISTS" "$2=$3 already exists" - fi + if [ $2 = 'USER' ]; then + if [ -d "$USER_DATA" ]; then + object="OK" + fi + else + object=$(grep "$2='$3'" $USER_DATA/$1.conf) + fi + if [ -n "$object" ]; then + check_result "$E_EXISTS" "$2=$3 already exists" + fi } # Check if object is valid is_object_valid() { - if [ $2 = 'USER' ]; then - tstpath="$(readlink -f "$HESTIA/data/users/$3")" - if [ "$(dirname "$tstpath")" != "$(readlink -f "$HESTIA/data/users")" ] || [ ! -d "$HESTIA/data/users/$3" ]; then - check_result "$E_NOTEXIST" "$1 $3 doesn't exist" - fi - elif [ $2 = 'KEY' ]; then - local key="$(basename "$3")" - - if [[ -z "$key" || ${#key} -lt 16 ]] || [[ ! -f "$HESTIA/data/access-keys/${key}" && ! -f "$HESTIA/data/access-keys/$key" ]]; then - check_result "$E_NOTEXIST" "$1 $3 doesn't exist" - fi - else - object=$(grep "$2='$3'" $HESTIA/data/users/$user/$1.conf) - if [ -z "$object" ]; then - arg1=$(basename $1) - arg2=$(echo $2 |tr '[:upper:]' '[:lower:]') - check_result "$E_NOTEXIST" "$arg1 $arg2 $3 doesn't exist" - fi - fi + if [ $2 = 'USER' ]; then + tstpath="$(readlink -f "$HESTIA/data/users/$3")" + if [ "$(dirname "$tstpath")" != "$(readlink -f "$HESTIA/data/users")" ] || [ ! -d "$HESTIA/data/users/$3" ]; then + check_result "$E_NOTEXIST" "$1 $3 doesn't exist" + fi + elif [ $2 = 'KEY' ]; then + local key="$(basename "$3")" + + if [[ -z "$key" || ${#key} -lt 16 ]] || [[ ! -f "$HESTIA/data/access-keys/${key}" && ! -f "$HESTIA/data/access-keys/$key" ]]; then + check_result "$E_NOTEXIST" "$1 $3 doesn't exist" + fi + else + object=$(grep "$2='$3'" $HESTIA/data/users/$user/$1.conf) + if [ -z "$object" ]; then + arg1=$(basename $1) + arg2=$(echo $2 | tr '[:upper:]' '[:lower:]') + check_result "$E_NOTEXIST" "$arg1 $arg2 $3 doesn't exist" + fi + fi } # Check if a object string with key values pairs has the correct format and load it afterwards parse_object_kv_list_non_eval() { - local str - local objkv obj_key obj_val - local OLD_IFS="$IFS" + local str + local objkv obj_key obj_val + local OLD_IFS="$IFS" - str=${@//$'\n'/ } - str=${str//\"/\\\"} - str=${str//$/\\$} - IFS=$'\n' + str=${@//$'\n'/ } + str=${str//\"/\\\"} + str=${str//$/\\$} + IFS=$'\n' - # Extract and loop trough each key-value pair. (Regex test: https://regex101.com/r/eiMufk/5) - for objkv in $( echo "$str" | perl -n -e "while(/\b([a-zA-Z]+[\w]*)='(.*?)'(\s|\$)/g) {print \$1.'='.\$2 . \"\n\" }" ); do + # Extract and loop trough each key-value pair. (Regex test: https://regex101.com/r/eiMufk/5) + for objkv in $(echo "$str" | perl -n -e "while(/\b([a-zA-Z]+[\w]*)='(.*?)'(\s|\$)/g) {print \$1.'='.\$2 . \"\n\" }"); do - if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then - check_result "$E_INVALID" "Invalid key value format [$objkv]" - fi + if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then + check_result "$E_INVALID" "Invalid key value format [$objkv]" + fi - obj_key=${objkv%%=*} # strip everything after first '=' char - obj_val=${objkv#*=} # strip everything before first '=' char - declare -g $obj_key="$obj_val" + obj_key=${objkv%%=*} # strip everything after first '=' char + obj_val=${objkv#*=} # strip everything before first '=' char + declare -g $obj_key="$obj_val" - done - IFS="$OLD_IFS" + done + IFS="$OLD_IFS" } # Check if a object string with key values pairs has the correct format and load it afterwards parse_object_kv_list() { - local str - local objkv - local suboutput - local OLD_IFS="$IFS" + local str + local objkv + local suboutput + local OLD_IFS="$IFS" - str=${@//$'\n'/ } - str=${str//\"/\\\"} - str=${str//$/\\$} - IFS=$'\n' + str=${@//$'\n'/ } + str=${str//\"/\\\"} + str=${str//$/\\$} + IFS=$'\n' - suboutput=$(setpriv --clear-groups --reuid nobody --regid nogroup bash -c "PS4=''; set -xe; eval \"${str}\"" 2>&1) - check_result $? "Invalid object format: ${str}" $E_INVALID + suboutput=$(setpriv --clear-groups --reuid nobody --regid nogroup bash -c "PS4=''; set -xe; eval \"${str}\"" 2>&1) + check_result $? "Invalid object format: ${str}" $E_INVALID - for objkv in $suboutput; do + for objkv in $suboutput; do - if [[ "$objkv" =~ ^'eval ' ]]; then - continue - fi + if [[ "$objkv" =~ ^'eval ' ]]; then + continue + fi - if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then - check_result "$E_INVALID" "Invalid key value format [$objkv]" - fi + if ! [[ "$objkv" =~ ^([[:alnum:]][_[:alnum:]]{0,64}[[:alnum:]])=(\'?[^\']+?\'?)?$ ]]; then + check_result "$E_INVALID" "Invalid key value format [$objkv]" + fi - eval "$objkv" - done - IFS="$OLD_IFS" + eval "$objkv" + done + IFS="$OLD_IFS" } # Check if object is supended is_object_suspended() { - if [ $2 = 'USER' ]; then - spnd=$(cat $USER_DATA/$1.conf|grep "SUSPENDED='yes'") - else - spnd=$(grep "$2='$3'" $USER_DATA/$1.conf|grep "SUSPENDED='yes'") - fi - if [ -z "$spnd" ]; then - check_result "$E_UNSUSPENDED" "$(basename $1) $3 is not suspended" - fi + if [ $2 = 'USER' ]; then + spnd=$(cat $USER_DATA/$1.conf | grep "SUSPENDED='yes'") + else + spnd=$(grep "$2='$3'" $USER_DATA/$1.conf | grep "SUSPENDED='yes'") + fi + if [ -z "$spnd" ]; then + check_result "$E_UNSUSPENDED" "$(basename $1) $3 is not suspended" + fi } # Check if object is unsupended is_object_unsuspended() { - if [ $2 = 'USER' ]; then - spnd=$(cat $USER_DATA/$1.conf |grep "SUSPENDED='yes'") - else - spnd=$(grep "$2='$3'" $USER_DATA/$1.conf |grep "SUSPENDED='yes'") - fi - if [ -n "$spnd" ]; then - check_result "$E_SUSPENDED" "$(basename $1) $3 is suspended" - fi + if [ $2 = 'USER' ]; then + spnd=$(cat $USER_DATA/$1.conf | grep "SUSPENDED='yes'") + else + spnd=$(grep "$2='$3'" $USER_DATA/$1.conf | grep "SUSPENDED='yes'") + fi + if [ -n "$spnd" ]; then + check_result "$E_SUSPENDED" "$(basename $1) $3 is suspended" + fi } # Check if object value is empty is_object_value_empty() { - str=$(grep "$2='$3'" $USER_DATA/$1.conf) - parse_object_kv_list "$str" - eval value=$4 - if [ -n "$value" ] && [ "$value" != 'no' ]; then - check_result "$E_EXISTS" "${4//$}=$value already exists" - fi + str=$(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list "$str" + eval value=$4 + if [ -n "$value" ] && [ "$value" != 'no' ]; then + check_result "$E_EXISTS" "${4//$/}=$value already exists" + fi } # Check if object value is empty is_object_value_exist() { - str=$(grep "$2='$3'" $USER_DATA/$1.conf) - parse_object_kv_list "$str" - eval value=$4 - if [ -z "$value" ] || [ "$value" = 'no' ]; then - check_result "$E_NOTEXIST" "${4//$}=$value doesn't exist" - fi + str=$(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list "$str" + eval value=$4 + if [ -z "$value" ] || [ "$value" = 'no' ]; then + check_result "$E_NOTEXIST" "${4//$/}=$value doesn't exist" + fi } # Check if password is transmitted via file is_password_valid() { - if [[ "$password" =~ ^/tmp/ ]]; then - if ! [[ "$password" == *../* ]]; then - if [ -f "$password" ]; then - password="$(head -n1 $password)" - fi - fi - fi + if [[ "$password" =~ ^/tmp/ ]]; then + if ! [[ "$password" == *../* ]]; then + if [ -f "$password" ]; then + password="$(head -n1 $password)" + fi + fi + fi } # Check if hash is transmitted via file is_hash_valid() { - if [[ "$hash" =~ ^/tmp/ ]]; then - if ! [[ "$hash" == *../* ]]; then - if [ -f "$hash" ]; then - hash="$(head -n1 $hash)" - fi - fi - fi + if [[ "$hash" =~ ^/tmp/ ]]; then + if ! [[ "$hash" == *../* ]]; then + if [ -f "$hash" ]; then + hash="$(head -n1 $hash)" + fi + fi + fi } # Check if directory is a symlink is_dir_symlink() { - if [[ -L "$1" ]]; then - check_result "$E_FORBIDEN" "$1 directory is a symlink" - fi + if [[ -L "$1" ]]; then + check_result "$E_FORBIDEN" "$1 directory is a symlink" + fi } # Get object value get_object_value() { - object=$(grep "$2='$3'" $USER_DATA/$1.conf) - parse_object_kv_list "$object" - eval echo $4 + object=$(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list "$object" + eval echo $4 } get_object_values() { - parse_object_kv_list $(grep "$2='$3'" $USER_DATA/$1.conf) + parse_object_kv_list $(grep "$2='$3'" $USER_DATA/$1.conf) } # Update object value update_object_value() { - row=$(grep -nF "$2='$3'" $USER_DATA/$1.conf) - lnr=$(echo $row | cut -f 1 -d ':') - object=$(echo $row | sed "s/^$lnr://") - parse_object_kv_list "$object" - eval old="$4" - old=$(echo "$old" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') - new=$(echo "$5" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') - sed -i "$lnr s/${4//$/}='${old//\*/\\*}'/${4//$/}='${new//\*/\\*}'/g" \ - $USER_DATA/$1.conf + row=$(grep -nF "$2='$3'" $USER_DATA/$1.conf) + lnr=$(echo $row | cut -f 1 -d ':') + object=$(echo $row | sed "s/^$lnr://") + parse_object_kv_list "$object" + eval old="$4" + old=$(echo "$old" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + new=$(echo "$5" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + sed -i "$lnr s/${4//$/}='${old//\*/\\*}'/${4//$/}='${new//\*/\\*}'/g" \ + $USER_DATA/$1.conf } # Add object key add_object_key() { - row=$(grep -n "$2='$3'" $USER_DATA/$1.conf) - lnr=$(echo $row | cut -f 1 -d ':') - object=$(echo $row | sed "s/^$lnr://") - if [ -z "$(echo $object |grep $4=)" ]; then - eval old="$4" - sed -i "$lnr s/$5='/$4='' $5='/" $USER_DATA/$1.conf - fi + row=$(grep -n "$2='$3'" $USER_DATA/$1.conf) + lnr=$(echo $row | cut -f 1 -d ':') + object=$(echo $row | sed "s/^$lnr://") + if [ -z "$(echo $object | grep $4=)" ]; then + eval old="$4" + sed -i "$lnr s/$5='/$4='' $5='/" $USER_DATA/$1.conf + fi } # Search objects search_objects() { - OLD_IFS="$IFS" - IFS=$'\n' - if [ -f $USER_DATA/$1.conf ]; then - for line in $(grep $2=\'$3\' $USER_DATA/$1.conf); do - parse_object_kv_list "$line" - eval echo \$$4 - done - fi - IFS="$OLD_IFS" + OLD_IFS="$IFS" + IFS=$'\n' + if [ -f $USER_DATA/$1.conf ]; then + for line in $(grep $2=\'$3\' $USER_DATA/$1.conf); do + parse_object_kv_list "$line" + eval echo \$$4 + done + fi + IFS="$OLD_IFS" } # Get user value get_user_value() { - grep "^${1//$/}=" $USER_DATA/user.conf | head -1 | awk -F "'" '{print $2}' + grep "^${1//$/}=" $USER_DATA/user.conf | head -1 | awk -F "'" '{print $2}' } # Update user value in user.conf update_user_value() { - key="${2//$}" - lnr=$(grep -n "^$key='" $HESTIA/data/users/$1/user.conf |cut -f 1 -d ':') - if [ -n "$lnr" ]; then - sed -i "$lnr d" $HESTIA/data/users/$1/user.conf - sed -i "$lnr i\\$key='${3}'" $HESTIA/data/users/$1/user.conf - fi + key="${2//$/}" + lnr=$(grep -n "^$key='" $HESTIA/data/users/$1/user.conf | cut -f 1 -d ':') + if [ -n "$lnr" ]; then + sed -i "$lnr d" $HESTIA/data/users/$1/user.conf + sed -i "$lnr i\\$key='${3}'" $HESTIA/data/users/$1/user.conf + fi } # Increase user counter increase_user_value() { - key="${2//$}" - factor="${3-1}" - conf="$HESTIA/data/users/$1/user.conf" - old=$(grep "$key=" $conf | cut -f 2 -d \') - if [ -z "$old" ]; then - old=0 - fi - new=$((old + factor)) - sed -i "s/$key='$old'/$key='$new'/g" $conf + key="${2//$/}" + factor="${3-1}" + conf="$HESTIA/data/users/$1/user.conf" + old=$(grep "$key=" $conf | cut -f 2 -d \') + if [ -z "$old" ]; then + old=0 + fi + new=$((old + factor)) + sed -i "s/$key='$old'/$key='$new'/g" $conf } # Decrease user counter decrease_user_value() { - key="${2//$}" - factor="${3-1}" - conf="$HESTIA/data/users/$1/user.conf" - old=$(grep "$key=" $conf | cut -f 2 -d \') - if [ -z "$old" ]; then - old=0 - fi - if [ "$old" -le 1 ]; then - new=0 - else - new=$((old - factor)) - fi - if [ "$new" -lt 0 ]; then - new=0 - fi - sed -i "s/$key='$old'/$key='$new'/g" $conf + key="${2//$/}" + factor="${3-1}" + conf="$HESTIA/data/users/$1/user.conf" + old=$(grep "$key=" $conf | cut -f 2 -d \') + if [ -z "$old" ]; then + old=0 + fi + if [ "$old" -le 1 ]; then + new=0 + else + new=$((old - factor)) + fi + if [ "$new" -lt 0 ]; then + new=0 + fi + sed -i "s/$key='$old'/$key='$new'/g" $conf } # Notify user send_notice() { - topic=$1 - notice=$2 - - if [ "$notify" = 'yes' ]; then - touch $USER_DATA/notifications.conf - chmod 660 $USER_DATA/notifications.conf - - time_n_date=$(date +'%T %F') - time=$(echo "$time_n_date" |cut -f 1 -d \ ) - date=$(echo "$time_n_date" |cut -f 2 -d \ ) - - nid=$(grep "NID=" $USER_DATA/notifications.conf |cut -f 2 -d \') - nid=$(echo "$nid" |sort -n |tail -n1) - if [ -n "$nid" ]; then - nid="$((nid +1))" - else - nid=1 - fi - - str="NID='$nid' TOPIC='$topic' NOTICE='$notice' TYPE='$type'" - str="$str ACK='no' TIME='$time' DATE='$date'" - - echo "$str" >> $USER_DATA/notifications.conf - - if [ -z "$(grep NOTIFICATIONS $USER_DATA/user.conf)" ]; then - sed -i "s/^TIME/NOTIFICATIONS='yes'\nTIME/g" $USER_DATA/user.conf - else - update_user_value "$user" '$NOTIFICATIONS' "yes" - fi - fi + topic=$1 + notice=$2 + + if [ "$notify" = 'yes' ]; then + touch $USER_DATA/notifications.conf + chmod 660 $USER_DATA/notifications.conf + + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" | cut -f 1 -d \ ) + date=$(echo "$time_n_date" | cut -f 2 -d \ ) + + nid=$(grep "NID=" $USER_DATA/notifications.conf | cut -f 2 -d \') + nid=$(echo "$nid" | sort -n | tail -n1) + if [ -n "$nid" ]; then + nid="$((nid + 1))" + else + nid=1 + fi + + str="NID='$nid' TOPIC='$topic' NOTICE='$notice' TYPE='$type'" + str="$str ACK='no' TIME='$time' DATE='$date'" + + echo "$str" >> $USER_DATA/notifications.conf + + if [ -z "$(grep NOTIFICATIONS $USER_DATA/user.conf)" ]; then + sed -i "s/^TIME/NOTIFICATIONS='yes'\nTIME/g" $USER_DATA/user.conf + else + update_user_value "$user" '$NOTIFICATIONS' "yes" + fi + fi } # Recalculate U_DISK value recalc_user_disk_usage() { - u_usage=0 - if [ -f "$USER_DATA/web.conf" ]; then - usage=0 - dusage=$(grep 'U_DISK=' $USER_DATA/web.conf |\ - awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') - for disk_usage in $dusage; do - usage=$((usage + disk_usage)) - done - d=$(grep "U_DISK_WEB='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK_WEB='$d'/U_DISK_WEB='$usage'/g" $USER_DATA/user.conf - u_usage=$((u_usage + usage)) - fi - - if [ -f "$USER_DATA/mail.conf" ]; then - usage=0 - dusage=$(grep 'U_DISK=' $USER_DATA/mail.conf |\ - awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') - for disk_usage in $dusage; do - usage=$((usage + disk_usage)) - done - d=$(grep "U_DISK_MAIL='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK_MAIL='$d'/U_DISK_MAIL='$usage'/g" $USER_DATA/user.conf - u_usage=$((u_usage + usage)) - fi - - if [ -f "$USER_DATA/db.conf" ]; then - usage=0 - dusage=$(grep 'U_DISK=' $USER_DATA/db.conf |\ - awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') - for disk_usage in $dusage; do - usage=$((usage + disk_usage)) - done - d=$(grep "U_DISK_DB='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK_DB='$d'/U_DISK_DB='$usage'/g" $USER_DATA/user.conf - u_usage=$((u_usage + usage)) - fi - usage=$(grep 'U_DISK_DIRS=' $USER_DATA/user.conf | cut -f 2 -d "'") - u_usage=$((u_usage + usage)) - old=$(grep "U_DISK='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_DISK='$old'/U_DISK='$u_usage'/g" $USER_DATA/user.conf + u_usage=0 + if [ -f "$USER_DATA/web.conf" ]; then + usage=0 + dusage=$(grep 'U_DISK=' $USER_DATA/web.conf \ + | awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') + for disk_usage in $dusage; do + usage=$((usage + disk_usage)) + done + d=$(grep "U_DISK_WEB='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK_WEB='$d'/U_DISK_WEB='$usage'/g" $USER_DATA/user.conf + u_usage=$((u_usage + usage)) + fi + + if [ -f "$USER_DATA/mail.conf" ]; then + usage=0 + dusage=$(grep 'U_DISK=' $USER_DATA/mail.conf \ + | awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') + for disk_usage in $dusage; do + usage=$((usage + disk_usage)) + done + d=$(grep "U_DISK_MAIL='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK_MAIL='$d'/U_DISK_MAIL='$usage'/g" $USER_DATA/user.conf + u_usage=$((u_usage + usage)) + fi + + if [ -f "$USER_DATA/db.conf" ]; then + usage=0 + dusage=$(grep 'U_DISK=' $USER_DATA/db.conf \ + | awk -F "U_DISK='" '{print $2}' | cut -f 1 -d \') + for disk_usage in $dusage; do + usage=$((usage + disk_usage)) + done + d=$(grep "U_DISK_DB='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK_DB='$d'/U_DISK_DB='$usage'/g" $USER_DATA/user.conf + u_usage=$((u_usage + usage)) + fi + usage=$(grep 'U_DISK_DIRS=' $USER_DATA/user.conf | cut -f 2 -d "'") + u_usage=$((u_usage + usage)) + old=$(grep "U_DISK='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_DISK='$old'/U_DISK='$u_usage'/g" $USER_DATA/user.conf } # Recalculate U_BANDWIDTH value recalc_user_bandwidth_usage() { - usage=0 - bandwidth_usage=$(grep 'U_BANDWIDTH=' $USER_DATA/web.conf |\ - awk -F "U_BANDWIDTH='" '{print $2}'|cut -f 1 -d \') - for bandwidth in $bandwidth_usage; do - usage=$((usage + bandwidth)) - done - old=$(grep "U_BANDWIDTH='" $USER_DATA/user.conf | cut -f 2 -d \') - sed -i "s/U_BANDWIDTH='$old'/U_BANDWIDTH='$usage'/g" $USER_DATA/user.conf + usage=0 + bandwidth_usage=$(grep 'U_BANDWIDTH=' $USER_DATA/web.conf \ + | awk -F "U_BANDWIDTH='" '{print $2}' | cut -f 1 -d \') + for bandwidth in $bandwidth_usage; do + usage=$((usage + bandwidth)) + done + old=$(grep "U_BANDWIDTH='" $USER_DATA/user.conf | cut -f 2 -d \') + sed -i "s/U_BANDWIDTH='$old'/U_BANDWIDTH='$usage'/g" $USER_DATA/user.conf } # Get next cron job id get_next_cronjob() { - if [ -z "$job" ]; then - curr_str=$(grep "JOB=" $USER_DATA/cron.conf|cut -f 2 -d \'|\ - sort -n|tail -n1) - job="$((curr_str +1))" - fi + if [ -z "$job" ]; then + curr_str=$(grep "JOB=" $USER_DATA/cron.conf | cut -f 2 -d \' \ + | sort -n | tail -n1) + job="$((curr_str + 1))" + fi } # Sort cron jobs by id sort_cron_jobs() { - cat $USER_DATA/cron.conf |sort -n -k 2 -t \' > $USER_DATA/cron.tmp - mv -f $USER_DATA/cron.tmp $USER_DATA/cron.conf + cat $USER_DATA/cron.conf | sort -n -k 2 -t \' > $USER_DATA/cron.tmp + mv -f $USER_DATA/cron.tmp $USER_DATA/cron.conf } # Sync cronjobs with system cron sync_cron_jobs() { - source_conf "$USER_DATA/user.conf" - if [ -e "/var/spool/cron/crontabs" ]; then - crontab="/var/spool/cron/crontabs/$user" - else - crontab="/var/spool/cron/$user" - fi - - # remove file if exists - if [ -e "$crontab" ]; then - rm -f $crontab - fi - - # touch new crontab file - touch $crontab - - if [ "$CRON_REPORTS" = 'yes' ]; then - echo "MAILTO=$CONTACT" > $crontab - echo 'CONTENT_TYPE="text/plain; charset=utf-8"' >> $crontab - else - echo 'MAILTO=""' > $crontab - fi - - while read line; do - parse_object_kv_list "$line" - if [ "$SUSPENDED" = 'no' ]; then - echo "$MIN $HOUR $DAY $MONTH $WDAY $CMD" |\ - sed -e "s/%quote%/'/g" -e "s/%dots%/:/g" \ - >> $crontab - fi - done < $USER_DATA/cron.conf - chown $user:$user $crontab - chmod 600 $crontab + source_conf "$USER_DATA/user.conf" + if [ -e "/var/spool/cron/crontabs" ]; then + crontab="/var/spool/cron/crontabs/$user" + else + crontab="/var/spool/cron/$user" + fi + + # remove file if exists + if [ -e "$crontab" ]; then + rm -f $crontab + fi + + # touch new crontab file + touch $crontab + + if [ "$CRON_REPORTS" = 'yes' ]; then + echo "MAILTO=$CONTACT" > $crontab + echo 'CONTENT_TYPE="text/plain; charset=utf-8"' >> $crontab + else + echo 'MAILTO=""' > $crontab + fi + + while read line; do + parse_object_kv_list "$line" + if [ "$SUSPENDED" = 'no' ]; then + echo "$MIN $HOUR $DAY $MONTH $WDAY $CMD" \ + | sed -e "s/%quote%/'/g" -e "s/%dots%/:/g" \ + >> $crontab + fi + done < $USER_DATA/cron.conf + chown $user:$user $crontab + chmod 600 $crontab } # User format validator is_user_format_valid() { - if [ ${#1} -eq 1 ]; then - if ! [[ "$1" =~ ^^[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - else - if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,28}[[:alnum:]]$ ]] - then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - fi + if [ ${#1} -eq 1 ]; then + if ! [[ "$1" =~ ^^[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + else + if [ -n "$3" ]; then + maxlenght=$(($3 - 2)) + if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,$maxlenght}[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + else + if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,28}[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + fi + fi + if [ "$1" != "${1//[^[:ascii:]]/}" ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Domain format validator is_domain_format_valid() { - object_name=${2-domain} - exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|_|/|\|\"|'|;|%|\`| ]" - if [[ $1 =~ $exclude ]] || [[ $1 =~ ^[0-9]+$ ]] || [[ $1 =~ \.\. ]] || [[ $1 =~ $(printf '\t') ]] || [[ "$1" = "www" ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - is_no_new_line_format "$1"; + object_name=${2-domain} + exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|_|/|\|\"|'|;|%|\`| ]" + if [[ $1 =~ $exclude ]] || [[ $1 =~ ^[0-9]+$ ]] || [[ $1 =~ \.\. ]] || [[ $1 =~ $(printf '\t') ]] || [[ "$1" = "www" ]]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + is_no_new_line_format "$1" } # Alias forman validator is_alias_format_valid() { - for object in ${1//,/ }; do - exclude="[!|@|#|$|^|&|(|)|+|=|{|}|:|<|>|?|_|/|\|\"|'|;|%|\`| ]" - if [[ "$object" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid alias format :: $object" - fi - if [[ "$object" =~ [*] ]] && ! [[ "$object" =~ ^[*]\..* ]]; then - check_result "$E_INVALID" "invalid alias format :: $object" - fi - done + for object in ${1//,/ }; do + exclude="[!|@|#|$|^|&|(|)|+|=|{|}|:|<|>|?|_|/|\|\"|'|;|%|\`| ]" + if [[ "$object" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid alias format :: $object" + fi + if [[ "$object" =~ [*] ]] && ! [[ "$object" =~ ^[*]\..* ]]; then + check_result "$E_INVALID" "invalid alias format :: $object" + fi + done } # IP format validator is_ip_format_valid() { - object_name=${2-ip} - ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' - ip_clean=$(echo "${1%/*}") - if ! [[ $ip_clean =~ ^$ip_regex\.$ip_regex\.$ip_regex\.$ip_regex$ ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - if [ $1 != "$ip_clean" ]; then - ip_cidr="$ip_clean/" - ip_cidr=$(echo "${1#$ip_cidr}") - if [[ "$ip_cidr" -gt 32 ]] || [[ "$ip_cidr" =~ [:alnum:] ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - fi + object_name=${2-ip} + ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' + ip_clean=$(echo "${1%/*}") + if ! [[ $ip_clean =~ ^$ip_regex\.$ip_regex\.$ip_regex\.$ip_regex$ ]]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + if [ $1 != "$ip_clean" ]; then + ip_cidr="$ip_clean/" + ip_cidr=$(echo "${1#$ip_cidr}") + if [[ "$ip_cidr" -gt 32 ]] || [[ "$ip_cidr" =~ [:alnum:] ]]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + fi } # IPv6 format validator is_ipv6_format_valid() { - object_name=${2-ipv6} - ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' - t_ip=$(echo $1 |awk -F / '{print $1}') - t_cidr=$(echo $1 |awk -F / '{print $2}') - valid_cidr=1 - - WORD="[0-9A-Fa-f]\{1,4\}" - # flat address, no compressed words - FLAT="^${WORD}\(:${WORD}\)\{7\}$" - - COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" - COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" - COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" - COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" - COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" - COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" - # trailing :: edge case, includes case of only :: (all 0's) - EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" - # leading :: edge case - EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" - - echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" - if [ $? -ne 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - - if [ -n "$(echo $1|grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 - fi - fi - if [ "$valid_cidr" -eq 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi + object_name=${2-ip6} + ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' + t_ip=$(echo $1 | awk -F / '{print $1}') + t_cidr=$(echo $1 | awk -F / '{print $2}') + valid_cidr=1 + + WORD="[0-9A-Fa-f]\{1,4\}" + # flat address, no compressed words + FLAT="^${WORD}\(:${WORD}\)\{7\}$" + + COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" + COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" + COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" + COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" + COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" + COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" + # trailing :: edge case, includes case of only :: (all 0's) + EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" + # leading :: edge case + EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" + + echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" + if [ $? -ne 0 ]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi + + if [ -n "$(echo $1 | grep '/')" ]; then + if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then + valid_cidr=0 + fi + if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then + valid_cidr=0 + fi + fi + if [ "$valid_cidr" -eq 0 ]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi } is_ip46_format_valid() { - t_ip=$(echo $1 |awk -F / '{print $1}') - t_cidr=$(echo $1 |awk -F / '{print $2}') - valid_octets=0 - valid_cidr=1 - for octet in ${t_ip//./ }; do - if [[ $octet =~ ^[0-9]{1,3}$ ]] && [[ $octet -le 255 ]]; then - ((++valid_octets)) - fi - done - - if [ -n "$(echo $1|grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 32 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 - fi - fi - if [ "$valid_octets" -lt 4 ] || [ "$valid_cidr" -eq 0 ]; then - #Check IPV6 - ipv6_valid="" - WORD="[0-9A-Fa-f]\{1,4\}" - # flat address, no compressed words - FLAT="^${WORD}\(:${WORD}\)\{7\}$" - - COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" - COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" - COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" - COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" - COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" - COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" - # trailing :: edge case, includes case of only :: (all 0's) - EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" - # leading :: edge case - EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" - - echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" - if [ $? -ne 0 ]; then - ipv6_valid="INVALID" - fi - - if [ -n "$(echo $1|grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 - fi - fi - - if [ -n "$ipv6_valid" ] || [ "$valid_cidr" -eq 0 ]; then - check_result "$E_INVALID" "invalid IP format :: $1" - fi - fi + t_ip=$(echo $1 | awk -F / '{print $1}') + t_cidr=$(echo $1 | awk -F / '{print $2}') + valid_octets=0 + valid_cidr=1 + for octet in ${t_ip//./ }; do + if [[ $octet =~ ^[0-9]{1,3}$ ]] && [[ $octet -le 255 ]]; then + ((++valid_octets)) + fi + done + + if [ -n "$(echo $1 | grep '/')" ]; then + if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 32 ]]; then + valid_cidr=0 + fi + if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then + valid_cidr=0 + fi + fi + if [ "$valid_octets" -lt 4 ] || [ "$valid_cidr" -eq 0 ]; then + #Check IPV6 + ipv6_valid="" + WORD="[0-9A-Fa-f]\{1,4\}" + # flat address, no compressed words + FLAT="^${WORD}\(:${WORD}\)\{7\}$" + + COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" + COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" + COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" + COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" + COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" + COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" + # trailing :: edge case, includes case of only :: (all 0's) + EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" + # leading :: edge case + EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" + + echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" + if [ $? -ne 0 ]; then + ipv6_valid="INVALID" + fi + + if [ -n "$(echo $1 | grep '/')" ]; then + if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then + valid_cidr=0 + fi + if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then + valid_cidr=0 + fi + fi + + if [ -n "$ipv6_valid" ] || [ "$valid_cidr" -eq 0 ]; then + check_result "$E_INVALID" "invalid IP format :: $1" + fi + fi } # Proxy extention format validator is_extention_format_valid() { - exclude="[!|#|$|^|&|(|)|+|=|{|}|:|@|<|>|?|/|\|\"|'|;|%|\`| ]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid proxy extention format :: $1" - fi - is_no_new_line_format "$1"; + exclude="[!|#|$|^|&|(|)|+|=|{|}|:|@|<|>|?|/|\|\"|'|;|%|\`| ]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid proxy extention format :: $1" + fi + is_no_new_line_format "$1" } # Number format validator is_number_format_valid() { - object_name=${2-number} - if ! [[ "$1" =~ ^[0-9]+$ ]] ; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi + object_name=${2-number} + if ! [[ "$1" =~ ^[0-9]+$ ]]; then + check_result "$E_INVALID" "invalid $object_name format :: $1" + fi } # Autoreply format validator is_autoreply_format_valid() { - if [ 10240 -le ${#1} ]; then - check_result "$E_INVALID" "invalid autoreply format :: $1" - fi + if [ 10240 -le ${#1} ]; then + check_result "$E_INVALID" "invalid autoreply format :: $1" + fi } # Boolean format validator is_boolean_format_valid() { - if [ "$1" != 'yes' ] && [ "$1" != 'no' ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if [ "$1" != 'yes' ] && [ "$1" != 'no' ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Refresh IPset format validator @@ -869,656 +878,661 @@ is_refresh_ipset_format_valid() { # Common format validator is_common_format_valid() { - exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`| ]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [ 400 -le ${#1} ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ "$1" =~ @ ]] && [ ${#1} -gt 1 ] ; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $1 =~ \* ]]; then - if [[ "$(echo $1 | grep -o '\*\.' |wc -l)" -eq 0 ]] && [[ $1 != '*' ]] ; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - fi - if [[ $(echo -n "$1" | tail -c 1) =~ [^a-zA-Z0-9_*@] ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | grep -c '\.\.') -gt 0 ]];then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | head -c 1) =~ [^a-zA-Z0-9_*@] ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | grep -c '\-\-') -gt 0 ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - if [[ $(echo -n "$1" | grep -c '\_\_') -gt 0 ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1"; + exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`| ]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [ 400 -le ${#1} ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ "$1" =~ @ ]] && [ ${#1} -gt 1 ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $1 =~ \* ]]; then + if [[ "$(echo $1 | grep -o '\*\.' | wc -l)" -eq 0 ]] && [[ $1 != '*' ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + fi + if [[ $(echo -n "$1" | tail -c 1) =~ [^a-zA-Z0-9_*@.] ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | grep -c '\.\.') -gt 0 ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | head -c 1) =~ [^a-zA-Z0-9_*@] ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | grep -c '\-\-') -gt 0 ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + if [[ $(echo -n "$1" | grep -c '\_\_') -gt 0 ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1" } is_no_new_line_format() { - test=$(echo "$1" | head -n1 ); - if [[ "$test" != "$1" ]]; then - check_result "$E_INVALID" "invalid value :: $1" - fi + test=$(echo "$1" | head -n1) + if [[ "$test" != "$1" ]]; then + check_result "$E_INVALID" "invalid value :: $1" + fi } is_string_format_valid() { - exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1"; + exclude="[!|#|$|^|&|(|)|+|=|{|}|:|<|>|?|/|\|\"|'|;|%|\`]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1" } # Database format validator is_database_format_valid() { - exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" - if [[ "$1" =~ $exclude ]] || [ 64 -le ${#1} ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1"; + exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" + if [[ "$1" =~ $exclude ]] || [ 64 -le ${#1} ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1" } # Date format validator is_date_format_valid() { - if ! [[ "$1" =~ ^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$ ]]; then - check_result "$E_INVALID" "invalid date format :: $1" - fi + if ! [[ "$1" =~ ^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$ ]]; then + check_result "$E_INVALID" "invalid date format :: $1" + fi } # Database user validator is_dbuser_format_valid() { - exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" - if [ 33 -le ${#1} ]; then - check_result "$E_INVALID" "mysql username can be up to 32 characters long" - fi - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi - is_no_new_line_format "$1" + exclude="[!|@|#|$|^|&|*|(|)|+|=|{|}|:|,|<|>|?|/|\|\"|'|;|%|\`| ]" + if [ 33 -le ${#1} ]; then + check_result "$E_INVALID" "mysql username can be up to 32 characters long" + fi + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi + is_no_new_line_format "$1" } # DNS record type validator is_dns_type_format_valid() { - known_dnstype='A,AAAA,NS,CNAME,MX,TXT,SRV,DNSKEY,KEY,IPSECKEY,PTR,SPF,TLSA,CAA' - if [ -z "$(echo $known_dnstype |grep -w $1)" ]; then - check_result "$E_INVALID" "invalid dns record type format :: $1" - fi + known_dnstype='A,AAAA,NS,CNAME,MX,TXT,SRV,DNSKEY,KEY,IPSECKEY,PTR,SPF,TLSA,CAA,DS' + if [ -z "$(echo $known_dnstype | grep -w $1)" ]; then + check_result "$E_INVALID" "invalid dns record type format :: $1" + fi } # DNS record validator is_dns_record_format_valid() { - if [ "$rtype" = 'A' ]; then - is_ip_format_valid "$1" - fi - if [ "$rtype" = 'NS' ]; then - is_domain_format_valid "${1::-1}" 'ns_record' - fi - if [ "$rtype" = 'MX' ]; then - is_domain_format_valid "${1::-1}" 'mx_record' - is_int_format_valid "$priority" 'priority_record' - fi - is_no_new_line_format "$1"; + if [ "$rtype" = 'A' ]; then + is_ip_format_valid "$1" + fi + if [ "$rtype" = 'NS' ]; then + is_domain_format_valid "${1::-1}" 'ns_record' + fi + if [ "$rtype" = 'MX' ]; then + is_domain_format_valid "${1::-1}" 'mx_record' + is_int_format_valid "$priority" 'priority_record' + fi + is_no_new_line_format "$1" } # Email format validator is_email_format_valid() { - if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]] ; then - if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.(xn--)[[:alnum:]]{2,63}$ ]] ; then - check_result "$E_INVALID" "invalid email format :: $1" - fi - fi + if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]]; then + if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.(xn--)[[:alnum:]]{2,63}$ ]]; then + check_result "$E_INVALID" "invalid email format :: $1" + fi + fi } # Firewall action validator is_fw_action_format_valid() { - if [ "$1" != "ACCEPT" ] && [ "$1" != 'DROP' ] ; then - check_result "$E_INVALID" "invalid action format :: $1" - fi + if [ "$1" != "ACCEPT" ] && [ "$1" != 'DROP' ]; then + check_result "$E_INVALID" "invalid action format :: $1" + fi } # Firewall protocol validator is_fw_protocol_format_valid() { - if [ "$1" != "ICMP" ] && [ "$1" != 'UDP' ] && [ "$1" != 'TCP' ] ; then - check_result "$E_INVALID" "invalid protocol format :: $1" - fi + if [ "$1" != "ICMP" ] && [ "$1" != 'UDP' ] && [ "$1" != 'TCP' ]; then + check_result "$E_INVALID" "invalid protocol format :: $1" + fi } # Firewall port validator is_fw_port_format_valid() { - if [ "${#1}" -eq 1 ]; then - if ! [[ "$1" =~ [0-9] ]]; then - check_result "$E_INVALID" "invalid port format :: $1" - fi - else - if ! [[ "$1" =~ ^[0-9][-|,|:|0-9]{0,30}[0-9]$ ]] - then - check_result "$E_INVALID" "invalid port format :: $1" - fi - fi + if [ "${#1}" -eq 1 ]; then + if ! [[ "$1" =~ [0-9] ]]; then + check_result "$E_INVALID" "invalid port format :: $1" + fi + else + if ! [[ "$1" =~ ^[0-9][-|,|:|0-9]{0,30}[0-9]$ ]]; then + check_result "$E_INVALID" "invalid port format :: $1" + fi + fi } # Integer validator is_int_format_valid() { - if ! [[ "$1" =~ ^[0-9]+$ ]] ; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[0-9]+$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Interface validator is_interface_format_valid() { - netdevices=$(cat /proc/net/dev |grep : |cut -f 1 -d : |tr -d ' ') - if [ -z $(echo "$netdevices" |grep -x $1) ]; then - check_result "$E_INVALID" "invalid interface format :: $1" - fi + netdevices=$(cat /proc/net/dev | grep : | cut -f 1 -d : | tr -d ' ') + if [ -z $(echo "$netdevices" | grep -x $1) ]; then + check_result "$E_INVALID" "invalid interface format :: $1" + fi } # IP status validator is_ip_status_format_valid() { - if [ -z "$(echo shared,dedicated | grep -w $1 )" ]; then - check_result "$E_INVALID" "invalid status format :: $1" - fi + if [ -z "$(echo shared,dedicated | grep -w $1)" ]; then + check_result "$E_INVALID" "invalid status format :: $1" + fi } # Cron validator is_cron_format_valid() { - limit=59 - check_format='' - if [ "$2" = 'hour' ]; then - limit=23 - fi - - if [ "$2" = 'day' ]; then - limit=31 - fi - if [ "$2" = 'month' ]; then - limit=12 - fi - if [ "$2" = 'wday' ]; then - limit=7 - fi - if [ "$1" = '*' ]; then - check_format='ok' - fi - if [[ "$1" =~ ^[\*]+[/]+[0-9] ]]; then - if [ "$(echo $1 |cut -f 2 -d /)" -lt $limit ]; then - check_format='ok' - fi - fi - if [[ "$1" =~ ^[0-9][-|,|0-9]{0,70}[\/][0-9]$ ]]; then - check_format='ok' - crn_values=${1//,/ } - crn_values=${crn_values//-/ } - crn_values=${crn_values//\// } - for crn_vl in $crn_values; do - if [ "$crn_vl" -gt $limit ]; then - check_format='invalid' - fi - done - fi - crn_values=$(echo $1 |tr "," " " | tr "-" " ") - for crn_vl in $crn_values - do - if [[ "$crn_vl" =~ ^[0-9]+$ ]] && [ "$crn_vl" -le $limit ]; then - check_format='ok' - fi - done - if [ "$check_format" != 'ok' ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + limit=59 + check_format='' + if [ "$2" = 'hour' ]; then + limit=23 + fi + + if [ "$2" = 'day' ]; then + limit=31 + fi + if [ "$2" = 'month' ]; then + limit=12 + fi + if [ "$2" = 'wday' ]; then + limit=7 + fi + if [ "$1" = '*' ]; then + check_format='ok' + fi + if [[ "$1" =~ ^[\*]+[/]+[0-9] ]]; then + if [ "$(echo $1 | cut -f 2 -d /)" -lt $limit ]; then + check_format='ok' + fi + fi + if [[ "$1" =~ ^[0-9][-|,|0-9]{0,70}[\/][0-9]$ ]]; then + check_format='ok' + crn_values=${1//,/ } + crn_values=${crn_values//-/ } + crn_values=${crn_values//\// } + for crn_vl in $crn_values; do + if [ "$crn_vl" -gt $limit ]; then + check_format='invalid' + fi + done + fi + crn_values=$(echo $1 | tr "," " " | tr "-" " ") + for crn_vl in $crn_values; do + if [[ "$crn_vl" =~ ^[0-9]+$ ]] && [ "$crn_vl" -le $limit ]; then + check_format='ok' + fi + done + if [ "$check_format" != 'ok' ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } +is_object_name_format_valid() { + if ! [[ "$1" =~ ^[-|\ |\.|_[:alnum:]]{0,50}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi +} # Name validator is_name_format_valid() { - if ! [[ "$1" =~ ^[-|\ |\.|_[:alnum:]]{0,50}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + exclude="['|\"|<|>]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "Invalid $2 contains qoutes (\" or ') :: $1" + fi + is_no_new_line_format "$1" } # Object validator is_object_format_valid() { - if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Role validator -is_role_valid (){ - if ! [[ "$1" =~ ^admin$|^user$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi +is_role_valid() { + if ! [[ "$1" =~ ^admin$|^user$|^dns-cluster$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Password validator is_password_format_valid() { - if [ "${#1}" -lt '6' ]; then - check_result "$E_INVALID" "invalid password format :: $1" - fi + if [ "${#1}" -lt '6' ]; then + check_result "$E_INVALID" "invalid password format :: $1" + fi } # Missing function - # Before: validate_format_shell # After: is_format_valid_shell is_format_valid_shell() { - if [ -z "$(grep -w $1 /etc/shells)" ]; then - echo "Error: shell $1 is not valid" - log_event "$E_INVALID" "$EVENT" - exit $E_INVALID - fi + if [ -z "$(grep -w $1 /etc/shells)" ]; then + echo "Error: shell $1 is not valid" + log_event "$E_INVALID" "$EVENT" + exit $E_INVALID + fi } # Service name validator is_service_format_valid() { - if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[[:alnum:]][-|\.|_[:alnum:]]{0,64}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } is_hash_format_valid() { - if ! [[ "$1" =~ ^[[:alnum:]|\:|\=|_|-]{1,80}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if ! [[ "$1" =~ ^[[:alnum:]|\:|\=|_|-]{1,80}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } # Format validation controller is_format_valid() { - for arg_name in $*; do - eval arg=\$$arg_name - if [ -n "$arg" ]; then - case $arg_name in - access_key_id) is_access_key_id_format_valid "$arg" "$arg_name";; - account) is_user_format_valid "$arg" "$arg_name";; - action) is_fw_action_format_valid "$arg";; - active) is_boolean_format_valid "$arg" 'active' ;; - aliases) is_alias_format_valid "$arg" ;; - alias) is_alias_format_valid "$arg" ;; - antispam) is_boolean_format_valid "$arg" 'antispam' ;; - antivirus) is_boolean_format_valid "$arg" 'antivirus' ;; - autoreply) is_autoreply_format_valid "$arg" ;; - backup) is_object_format_valid "$arg" 'backup' ;; - charset) is_object_format_valid "$arg" "$arg_name" ;; - charsets) is_common_format_valid "$arg" 'charsets' ;; - chain) is_object_format_valid "$arg" 'chain' ;; - comment) is_object_format_valid "$arg" 'comment' ;; - database) is_database_format_valid "$arg" 'database';; - day) is_cron_format_valid "$arg" $arg_name ;; - dbpass) is_password_format_valid "$arg" ;; - dbuser) is_dbuser_format_valid "$arg" 'dbuser';; - dkim) is_boolean_format_valid "$arg" 'dkim' ;; - dkim_size) is_int_format_valid "$arg" ;; - domain) is_domain_format_valid "$arg" ;; - dom_alias) is_alias_format_valid "$arg" ;; - dvalue) is_dns_record_format_valid "$arg";; - email) is_email_format_valid "$arg" ;; - email_forward) is_email_format_valid "$arg" ;; - exp) is_date_format_valid "$arg" ;; - extentions) is_common_format_valid "$arg" 'extentions' ;; - format) is_type_valid 'plain json shell csv' "$arg" ;; - ftp_password) is_password_format_valid "$arg" ;; - ftp_user) is_user_format_valid "$arg" "$arg_name" ;; - hash) is_hash_format_valid "$arg" "$arg_name" ;; - host) is_object_format_valid "$arg" "$arg_name" ;; - hour) is_cron_format_valid "$arg" $arg_name ;; - id) is_int_format_valid "$arg" 'id' ;; - iface) is_interface_format_valid "$arg" ;; - ip) is_ip_format_valid "$arg" ;; - ipv6) is_ipv6_format_valid "$arg" ;; - ip46) is_ip46_format_valid "$arg" ;; - ip_name) is_domain_format_valid "$arg" 'IP name';; - ip_status) is_ip_status_format_valid "$arg" ;; - job) is_int_format_valid "$arg" 'job' ;; - key) is_common_format_valid "$arg" "$arg_name" ;; - malias) is_user_format_valid "$arg" "$arg_name" ;; - max_db) is_int_format_valid "$arg" 'max db';; - min) is_cron_format_valid "$arg" $arg_name ;; - month) is_cron_format_valid "$arg" $arg_name ;; - name) is_name_format_valid "$arg" "name" ;; - nat_ip) is_ip_format_valid "$arg" ;; - netmask) is_ip_format_valid "$arg" 'netmask' ;; - newid) is_int_format_valid "$arg" 'id' ;; - ns1) is_domain_format_valid "$arg" 'ns1' ;; - ns2) is_domain_format_valid "$arg" 'ns2' ;; - ns3) is_domain_format_valid "$arg" 'ns3' ;; - ns4) is_domain_format_valid "$arg" 'ns4' ;; - ns5) is_domain_format_valid "$arg" 'ns5' ;; - ns6) is_domain_format_valid "$arg" 'ns6' ;; - ns7) is_domain_format_valid "$arg" 'ns7' ;; - ns8) is_domain_format_valid "$arg" 'ns8' ;; - object) is_name_format_valid "$arg" 'object';; - package) is_object_format_valid "$arg" "$arg_name" ;; - password) is_password_format_valid "$arg" ;; - port) is_int_format_valid "$arg" 'port' ;; - port_ext) is_fw_port_format_valid "$arg";; - protocol) is_fw_protocol_format_valid "$arg" ;; - proxy_ext) is_extention_format_valid "$arg" ;; - quota) is_int_format_valid "$arg" 'quota' ;; - rate) is_int_format_valid "$arg" 'rate' ;; - record) is_common_format_valid "$arg" 'record';; - reject) is_boolean_format_valid "$arg" 'reject' ;; - restart) is_restart_format_valid "$arg" 'restart' ;; - role) is_role_valid "$arg" 'role' ;; - rtype) is_dns_type_format_valid "$arg" ;; - rule) is_int_format_valid "$arg" "rule id" ;; - service) is_service_format_valid "$arg" "$arg_name" ;; - secret_access_key) is_secret_access_key_format_valid "$arg" "$arg_name" ;; - soa) is_domain_format_valid "$arg" 'SOA' ;; - #missing command: is_format_valid_shell - shell) is_format_valid_shell "$arg" ;; - ssl_dir) is_folder_exists "$arg" "$arg_name" ;; - stats_pass) is_password_format_valid "$arg" ;; - stats_user) is_user_format_valid "$arg" "$arg_name" ;; - template) is_object_format_valid "$arg" "$arg_name" ;; - theme) is_common_format_valid "$arg" "$arg_name" ;; - ttl) is_int_format_valid "$arg" 'ttl';; - user) is_user_format_valid "$arg" $arg_name;; - wday) is_cron_format_valid "$arg" $arg_name ;; - value) is_common_format_valid "$arg" $arg_name;; - esac - fi - done -} - -is_folder_exists () { - if [ ! -d "$1" ]; then - check_result "$E_NOTEXIST" "folder $1 does not exist" - fi -} - -is_command_valid_format () { - if [[ ! "$1" =~ ^v-[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then - check_result "$E_INVALID" "Invalid command format" - fi - if [[ -n $( echo "$1" | grep -e '\-\-' ) ]]; then - check_result "$E_INVALID" "Invalid command format" - fi + for arg_name in $*; do + eval arg=\$$arg_name + if [ -n "$arg" ]; then + case $arg_name in + access_key_id) is_access_key_id_format_valid "$arg" "$arg_name" ;; + account) is_user_format_valid "$arg" "$arg_name" '64' ;; + action) is_fw_action_format_valid "$arg" ;; + active) is_boolean_format_valid "$arg" 'active' ;; + aliases) is_alias_format_valid "$arg" ;; + alias) is_alias_format_valid "$arg" ;; + antispam) is_boolean_format_valid "$arg" 'antispam' ;; + antivirus) is_boolean_format_valid "$arg" 'antivirus' ;; + autoreply) is_autoreply_format_valid "$arg" ;; + backup) is_object_format_valid "$arg" 'backup' ;; + charset) is_object_format_valid "$arg" "$arg_name" ;; + charsets) is_common_format_valid "$arg" 'charsets' ;; + chain) is_object_format_valid "$arg" 'chain' ;; + comment) is_object_format_valid "$arg" 'comment' ;; + database) is_database_format_valid "$arg" 'database' ;; + day) is_cron_format_valid "$arg" $arg_name ;; + dbpass) is_password_format_valid "$arg" ;; + dbuser) is_dbuser_format_valid "$arg" 'dbuser' ;; + dkim) is_boolean_format_valid "$arg" 'dkim' ;; + dkim_size) is_int_format_valid "$arg" ;; + domain) is_domain_format_valid "$arg" ;; + dom_alias) is_alias_format_valid "$arg" ;; + dvalue) is_dns_record_format_valid "$arg" ;; + email) is_email_format_valid "$arg" ;; + email_forward) is_email_format_valid "$arg" ;; + exp) is_date_format_valid "$arg" ;; + extentions) is_common_format_valid "$arg" 'extentions' ;; + format) is_type_valid 'plain json shell csv' "$arg" ;; + ftp_password) is_password_format_valid "$arg" ;; + ftp_user) is_user_format_valid "$arg" "$arg_name" ;; + hash) is_hash_format_valid "$arg" "$arg_name" ;; + host) is_object_format_valid "$arg" "$arg_name" ;; + hour) is_cron_format_valid "$arg" $arg_name ;; + id) is_int_format_valid "$arg" 'id' ;; + iface) is_interface_format_valid "$arg" ;; + ip) is_ip_format_valid "$arg" ;; + ipv6) is_ipv6_format_valid "$arg" ;; + ip46) is_ip46_format_valid "$arg" ;; + ip_name) is_domain_format_valid "$arg" 'IP name' ;; + ip_status) is_ip_status_format_valid "$arg" ;; + job) is_int_format_valid "$arg" 'job' ;; + key) is_common_format_valid "$arg" "$arg_name" ;; + malias) is_user_format_valid "$arg" "$arg_name" '64' ;; + max_db) is_int_format_valid "$arg" 'max db' ;; + min) is_cron_format_valid "$arg" $arg_name ;; + month) is_cron_format_valid "$arg" $arg_name ;; + name) is_name_format_valid "$arg" "name" ;; + nat_ip) is_ip_format_valid "$arg" ;; + netmask) is_ip_format_valid "$arg" 'netmask' ;; + newid) is_int_format_valid "$arg" 'id' ;; + ns1) is_domain_format_valid "$arg" 'ns1' ;; + ns2) is_domain_format_valid "$arg" 'ns2' ;; + ns3) is_domain_format_valid "$arg" 'ns3' ;; + ns4) is_domain_format_valid "$arg" 'ns4' ;; + ns5) is_domain_format_valid "$arg" 'ns5' ;; + ns6) is_domain_format_valid "$arg" 'ns6' ;; + ns7) is_domain_format_valid "$arg" 'ns7' ;; + ns8) is_domain_format_valid "$arg" 'ns8' ;; + object) is_object_name_format_valid "$arg" 'object' ;; + package) is_object_format_valid "$arg" "$arg_name" ;; + password) is_password_format_valid "$arg" ;; + port) is_int_format_valid "$arg" 'port' ;; + port_ext) is_fw_port_format_valid "$arg" ;; + protocol) is_fw_protocol_format_valid "$arg" ;; + proxy_ext) is_extention_format_valid "$arg" ;; + quota) is_int_format_valid "$arg" 'quota' ;; + rate) is_int_format_valid "$arg" 'rate' ;; + record) is_common_format_valid "$arg" 'record' ;; + reject) is_boolean_format_valid "$arg" 'reject' ;; + restart) is_restart_format_valid "$arg" 'restart' ;; + role) is_role_valid "$arg" 'role' ;; + rtype) is_dns_type_format_valid "$arg" ;; + rule) is_int_format_valid "$arg" "rule id" ;; + service) is_service_format_valid "$arg" "$arg_name" ;; + secret_access_key) is_secret_access_key_format_valid "$arg" "$arg_name" ;; + soa) is_domain_format_valid "$arg" 'SOA' ;; + #missing command: is_format_valid_shell + shell) is_format_valid_shell "$arg" ;; + ssl_dir) is_folder_exists "$arg" "$arg_name" ;; + stats_pass) is_password_format_valid "$arg" ;; + stats_user) is_user_format_valid "$arg" "$arg_name" ;; + template) is_object_format_valid "$arg" "$arg_name" ;; + theme) is_common_format_valid "$arg" "$arg_name" ;; + ttl) is_int_format_valid "$arg" 'ttl' ;; + user) is_user_format_valid "$arg" $arg_name ;; + wday) is_cron_format_valid "$arg" $arg_name ;; + value) is_common_format_valid "$arg" $arg_name ;; + esac + fi + done +} + +is_folder_exists() { + if [ ! -d "$1" ]; then + check_result "$E_NOTEXIST" "folder $1 does not exist" + fi +} + +is_command_valid_format() { + if [[ ! "$1" =~ ^v-[[:alnum:]][-|\.|_[:alnum:]]{0,64}[[:alnum:]]$ ]]; then + check_result "$E_INVALID" "Invalid command format" + fi + if [[ -n $(echo "$1" | grep -e '\-\-') ]]; then + check_result "$E_INVALID" "Invalid command format" + fi } # Check access_key_id name # Don't work with legacy key format is_access_key_id_format_valid() { - local hash="$1" + local hash="$1" - # ACCESS_KEY_ID format validation - if ! [[ "$hash" =~ ^[[:alnum:]]{20}$ ]]; then - check_result "$E_INVALID" "invalid $2 format :: $hash" - fi + # ACCESS_KEY_ID format validation + if ! [[ "$hash" =~ ^[[:alnum:]]{20}$ ]]; then + check_result "$E_INVALID" "invalid $2 format :: $hash" + fi } # SECRET_ACCESS_KEY format validation is_secret_access_key_format_valid() { - local hash="$1" + local hash="$1" - if ! [[ "$hash" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then - check_result "$E_INVALID" "invalid $2 format" - fi + if ! [[ "$hash" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then + check_result "$E_INVALID" "invalid $2 format" + fi } # Checks if the secret belongs to the access key check_access_key_secret() { - local access_key_id="$(basename "$1")" - local secret_access_key=$2 - local -n key_user=$3 + local access_key_id="$(basename "$1")" + local secret_access_key=$2 + local -n key_user=$3 - if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then - check_result "$E_PASSWORD" "Access key $access_key_id doesn't exist" - fi - - if [[ -z "$secret_access_key" ]]; then - check_result "$E_PASSWORD" "Secret key not provided for key $access_key_id" - elif ! [[ "$secret_access_key" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then - check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" - else - SECRET_ACCESS_KEY="" - source_conf "$HESTIA/data/access-keys/${access_key_id}"; + if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then + check_result "$E_PASSWORD" "Access key $access_key_id doesn't exist" + fi - if [[ -z "$SECRET_ACCESS_KEY" || "$SECRET_ACCESS_KEY" != "$secret_access_key" ]]; then - check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" - fi - fi + if [[ -z "$secret_access_key" ]]; then + check_result "$E_PASSWORD" "Secret key not provided for key $access_key_id" + elif ! [[ "$secret_access_key" =~ ^[[:alnum:]|_|\.|\+|/|\^|~|=|%|\-]{40}$ ]]; then + check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" + else + SECRET_ACCESS_KEY="" + source_conf "$HESTIA/data/access-keys/${access_key_id}" + + if [[ -z "$SECRET_ACCESS_KEY" || "$SECRET_ACCESS_KEY" != "$secret_access_key" ]]; then + check_result "$E_PASSWORD" "Invalid secret key for key $access_key_id" + fi + fi - key_user="$USER" + key_user="$USER" } # Checks if the key belongs to the user check_access_key_user() { - local access_key_id="$(basename "$1")" - local user=$2 + local access_key_id="$(basename "$1")" + local user=$2 - if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then - check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" - fi + if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then + check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" + fi - if [[ -z "$user" ]]; then - check_result "$E_FORBIDEN" "User not provided" - else - USER="" - source_conf "$HESTIA/data/access-keys/${access_key_id}"; + if [[ -z "$user" ]]; then + check_result "$E_FORBIDEN" "User not provided" + else + USER="" + source_conf "$HESTIA/data/access-keys/${access_key_id}" - if [[ -z "$USER" || "$USER" != "$user" ]]; then - check_result "$E_FORBIDEN" "key $access_key_id does not belong to the user $user" - fi - fi + if [[ -z "$USER" || "$USER" != "$user" ]]; then + check_result "$E_FORBIDEN" "key $access_key_id does not belong to the user $user" + fi + fi } # Checks if the key is allowed to run the command check_access_key_cmd() { - local access_key_id="$(basename "$1")" - local cmd=$2 - local -n user_arg_position=$3 - - if [[ "$DEBUG_MODE" = "true" ]]; then - new_timestamp - echo "[$date:$time] $1 $2" >> /var/log/hestia/api.log - fi - if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then - check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" - fi - - if [[ -z "$cmd" ]]; then - check_result "$E_FORBIDEN" "Command not provided" - elif [[ "$cmd" = 'v-make-tmp-file' ]]; then - USER="" PERMISSIONS="" - source_conf "${HESTIA}/data/access-keys/${access_key_id}" - local allowed_commands - if [[ -n "$PERMISSIONS" ]]; then - allowed_commands="$(get_apis_commands "$PERMISSIONS")" - if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - user_arg_position="0" - elif [[ ! -e "$BIN/$cmd" ]]; then - check_result "$E_FORBIDEN" "Command $cmd not found" - else - USER="" PERMISSIONS="" - source_conf "${HESTIA}/data/access-keys/${access_key_id}" - - local allowed_commands - if [[ -n "$PERMISSIONS" ]]; then - allowed_commands="$(get_apis_commands "$PERMISSIONS")" - if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then - check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" - fi - - if [[ "$USER" == "admin" ]]; then - # Admin can run commands for any user - user_arg_position="0" - else - user_arg_position="$(search_command_arg_position "$hst_command" "USER")" - if ! [[ "$user_arg_position" =~ ^[0-9]+$ ]]; then - check_result "$E_FORBIDEN" "Command $hst_command not found" - fi - fi - fi + local access_key_id="$(basename "$1")" + local cmd=$2 + local -n user_arg_position=$3 + + if [[ "$DEBUG_MODE" = "true" ]]; then + new_timestamp + echo "[$date:$time] $1 $2" >> /var/log/hestia/api.log + fi + if [[ -z "$access_key_id" || ! -f "$HESTIA/data/access-keys/${access_key_id}" ]]; then + check_result "$E_FORBIDEN" "Access key $access_key_id doesn't exist" + fi + + if [[ -z "$cmd" ]]; then + check_result "$E_FORBIDEN" "Command not provided" + elif [[ "$cmd" = 'v-make-tmp-file' ]]; then + USER="" PERMISSIONS="" + source_conf "${HESTIA}/data/access-keys/${access_key_id}" + local allowed_commands + if [[ -n "$PERMISSIONS" ]]; then + allowed_commands="$(get_apis_commands "$PERMISSIONS")" + if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + user_arg_position="0" + elif [[ ! -e "$BIN/$cmd" ]]; then + check_result "$E_FORBIDEN" "Command $cmd not found" + else + USER="" PERMISSIONS="" + source_conf "${HESTIA}/data/access-keys/${access_key_id}" + + local allowed_commands + if [[ -n "$PERMISSIONS" ]]; then + allowed_commands="$(get_apis_commands "$PERMISSIONS")" + if [[ -z "$(echo ",${allowed_commands}," | grep ",${hst_command},")" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + elif [[ -z "$PERMISSIONS" && "$USER" != "admin" ]]; then + check_result "$E_FORBIDEN" "Key $access_key_id don't have permission to run the command $hst_command" + fi + + if [[ "$USER" == "admin" ]]; then + # Admin can run commands for any user + user_arg_position="0" + else + user_arg_position="$(search_command_arg_position "$hst_command" "USER")" + if ! [[ "$user_arg_position" =~ ^[0-9]+$ ]]; then + check_result "$E_FORBIDEN" "Command $hst_command not found" + fi + fi + fi } # Domain argument formatting format_domain() { - if [[ "$domain" = *[![:ascii:]]* ]]; then - if [[ "$domain" =~ [[:upper:]] ]]; then - domain=$(echo "$domain" |sed 's/[[:upper:]].*/\L&/') - fi - else - if [[ "$domain" =~ [[:upper:]] ]]; then - domain=$(echo "$domain" |tr '[:upper:]' '[:lower:]') - fi - fi - if [[ "$domain" =~ ^www\..* ]]; then - domain=$(echo "$domain" |sed -e "s/^www.//") - fi - if [[ "$domain" =~ .*\.$ ]]; then - domain=$(echo "$domain" |sed -e "s/[.]*$//g") - fi - if [[ "$domain" =~ ^\. ]]; then - domain=$(echo "$domain" |sed -e "s/^[.]*//") - fi + if [[ "$domain" = *[![:ascii:]]* ]]; then + if [[ "$domain" =~ [[:upper:]] ]]; then + domain=$(echo "$domain" | sed 's/[[:upper:]].*/\L&/') + fi + else + if [[ "$domain" =~ [[:upper:]] ]]; then + domain=$(echo "$domain" | tr '[:upper:]' '[:lower:]') + fi + fi + if [[ "$domain" =~ ^www\..* ]]; then + domain=$(echo "$domain" | sed -e "s/^www.//") + fi + if [[ "$domain" =~ .*\.$ ]]; then + domain=$(echo "$domain" | sed -e "s/[.]*$//g") + fi + if [[ "$domain" =~ ^\. ]]; then + domain=$(echo "$domain" | sed -e "s/^[.]*//") + fi } format_domain_idn() { - if [ -z "$domain_idn" ]; then - domain_idn=$domain - fi - if [[ "$domain_idn" = *[![:ascii:]]* ]]; then - domain_idn=$(idn2 --quiet $domain_idn) - fi + if [ -z "$domain_idn" ]; then + domain_idn=$domain + fi + if [[ "$domain_idn" = *[![:ascii:]]* ]]; then + domain_idn=$(idn2 --quiet $domain_idn) + fi } format_aliases() { - if [ -n "$aliases" ] && [ "$aliases" != 'none' ]; then - aliases=$(echo $aliases |tr '[:upper:]' '[:lower:]' |tr ',' '\n') - aliases=$(echo "$aliases" |sed -e "s/\.$//" |sort -u) - aliases=$(echo "$aliases" |tr -s '.') - aliases=$(echo "$aliases" |sed -e "s/[.]*$//g") - aliases=$(echo "$aliases" |sed -e "s/^[.]*//") - aliases=$(echo "$aliases" |sed -e "/^$/d") - aliases=$(echo "$aliases" |tr '\n' ',' |sed -e "s/,$//") - fi + if [ -n "$aliases" ] && [ "$aliases" != 'none' ]; then + aliases=$(echo $aliases | tr '[:upper:]' '[:lower:]' | tr ',' '\n') + aliases=$(echo "$aliases" | sed -e "s/\.$//" | sort -u) + aliases=$(echo "$aliases" | tr -s '.') + aliases=$(echo "$aliases" | sed -e "s/[.]*$//g") + aliases=$(echo "$aliases" | sed -e "s/^[.]*//") + aliases=$(echo "$aliases" | sed -e "/^$/d") + aliases=$(echo "$aliases" | tr '\n' ',' | sed -e "s/,$//") + fi } is_restart_format_valid() { - if [ "$1" != 'yes' ] && [ "$1" != 'no' ] && [ "$1" != 'ssl' ] && [ "$1" != 'reload' ] && [ "$1" != 'updatessl' ]; then - check_result "$E_INVALID" "invalid $2 format :: $1" - fi + if [ "$1" != 'yes' ] && [ "$1" != 'no' ] && [ "$1" != 'ssl' ] && [ "$1" != 'reload' ] && [ "$1" != 'updatessl' ]; then + check_result "$E_INVALID" "invalid $2 format :: $1" + fi } check_backup_conditions() { - # Checking load average - la=$(cat /proc/loadavg |cut -f 1 -d ' ' |cut -f 1 -d '.') - # i=0 - while [ "$la" -ge "$BACKUP_LA_LIMIT" ]; do - echo -e "$(date "+%F %T") Load Average $la" - sleep 60 - la=$(cat /proc/loadavg |cut -f 1 -d ' ' |cut -f 1 -d '.') - done + # Checking load average + la=$(cat /proc/loadavg | cut -f 1 -d ' ' | cut -f 1 -d '.') + # i=0 + while [ "$la" -ge "$BACKUP_LA_LIMIT" ]; do + echo -e "$(date "+%F %T") Load Average $la" + sleep 60 + la=$(cat /proc/loadavg | cut -f 1 -d ' ' | cut -f 1 -d '.') + done } # Define download function download_file() { - local url=$1 - local destination=$2 - local force=$3 - - # Default destination is the curent working directory - local dstopt="" - - if [ -n "$(echo "$url" | grep -E "\.(gz|gzip|bz2|zip|xz)$")" ]; then - # When an archive file is downloaded it will be first saved localy - dstopt="--directory-prefix=$ARCHIVE_DIR" - local is_archive="true" - local filename="${url##*/}" - if [ -z "$filename" ]; then - >&2 echo "[!] No filename was found in url, exiting ($url)" - exit 1 - fi - if [ -n "$force" ] && [ -f "$ARCHIVE_DIR/$filename" ]; then - rm -f $ARCHIVE_DIR/$filename - fi - elif [ -n "$destination" ]; then - # Plain files will be written to specified location - dstopt="-O $destination" - fi - # check for corrupted archive - if [ -f "$ARCHIVE_DIR/$filename" ] && [ "$is_archive" = "true" ]; then - tar -tzf "$ARCHIVE_DIR/$filename" > /dev/null 2>&1 - if [ $? -ne 0 ]; then - >&2 echo "[!] Archive $ARCHIVE_DIR/$filename is corrupted, redownloading" - rm -f $ARCHIVE_DIR/$filename - fi - fi - - if [ ! -f "$ARCHIVE_DIR/$filename" ]; then - wget $url -q $dstopt --show-progress --progress=bar:force --limit-rate=3m - fi - - if [ -n "$destination" ] && [ "$is_archive" = "true" ]; then - if [ "$destination" = "-" ]; then - cat "$ARCHIVE_DIR/$filename" - elif [ -d "$(dirname $destination)" ]; then - cp "$ARCHIVE_DIR/$filename" "$destination" - fi - fi + local url=$1 + local destination=$2 + local force=$3 + + # Default destination is the curent working directory + local dstopt="" + + if [ -n "$(echo "$url" | grep -E "\.(gz|gzip|bz2|zip|xz)$")" ]; then + # When an archive file is downloaded it will be first saved localy + dstopt="--directory-prefix=$ARCHIVE_DIR" + local is_archive="true" + local filename="${url##*/}" + if [ -z "$filename" ]; then + echo >&2 "[!] No filename was found in url, exiting ($url)" + exit 1 + fi + if [ -n "$force" ] && [ -f "$ARCHIVE_DIR/$filename" ]; then + rm -f $ARCHIVE_DIR/$filename + fi + elif [ -n "$destination" ]; then + # Plain files will be written to specified location + dstopt="-O $destination" + fi + # check for corrupted archive + if [ -f "$ARCHIVE_DIR/$filename" ] && [ "$is_archive" = "true" ]; then + tar -tzf "$ARCHIVE_DIR/$filename" > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo >&2 "[!] Archive $ARCHIVE_DIR/$filename is corrupted, redownloading" + rm -f $ARCHIVE_DIR/$filename + fi + fi + + if [ ! -f "$ARCHIVE_DIR/$filename" ]; then + wget $url -q $dstopt --show-progress --progress=bar:force --limit-rate=3m + fi + + if [ -n "$destination" ] && [ "$is_archive" = "true" ]; then + if [ "$destination" = "-" ]; then + cat "$ARCHIVE_DIR/$filename" + elif [ -d "$(dirname $destination)" ]; then + cp "$ARCHIVE_DIR/$filename" "$destination" + fi + fi } check_hestia_demo_mode() { - demo_mode=$(grep DEMO_MODE /usr/local/hestia/conf/hestia.conf | cut -d '=' -f2 | sed "s|'||g") - if [ -n "$demo_mode" ] && [ "$demo_mode" = "yes" ]; then - echo "ERROR: Unable to perform operation due to security restrictions that are in place." - exit 1 - fi + demo_mode=$(grep DEMO_MODE /usr/local/hestia/conf/hestia.conf | cut -d '=' -f2 | sed "s|'||g") + if [ -n "$demo_mode" ] && [ "$demo_mode" = "yes" ]; then + echo "ERROR: Unable to perform operation due to security restrictions that are in place." + exit 1 + fi } multiphp_count() { - $BIN/v-list-sys-php plain | wc -l + $BIN/v-list-sys-php plain | wc -l } multiphp_versions() { - local -a php_versions_list; - local php_ver; - if [ "$(multiphp_count)" -gt 0 ] ; then - for php_ver in $( $BIN/v-list-sys-php plain); do - [ ! -d "/etc/php/$php_ver/fpm/pool.d/" ] && continue - php_versions_list+=($php_ver) - done - echo "${php_versions_list[@]}" - fi + local -a php_versions_list + local php_ver + if [ "$(multiphp_count)" -gt 0 ]; then + for php_ver in $($BIN/v-list-sys-php plain); do + [ ! -d "/etc/php/$php_ver/fpm/pool.d/" ] && continue + php_versions_list+=($php_ver) + done + echo "${php_versions_list[@]}" + fi } multiphp_default_version() { - # Get system wide default php version (set by update-alternatives) - local sys_phpversion=$(php -r "echo substr(phpversion(),0,3);") - - # Check if the system php also has php-fpm enabled, otherwise return - # the most recent php version which does have it installed. - if [ ! -d "/etc/php/$sys_phpversion/fpm/pool.d/" ]; then - local all_versions="$(multiphp_versions)" - if [ -n "$all_versions" ]; then - sys_phpversion="${all_versions##*\ }"; - fi - fi + # Get system wide default php version (set by update-alternatives) + local sys_phpversion=$(php -r "echo substr(phpversion(),0,3);") + + # Check if the system php also has php-fpm enabled, otherwise return + # the most recent php version which does have it installed. + if [ ! -d "/etc/php/$sys_phpversion/fpm/pool.d/" ]; then + local all_versions="$(multiphp_versions)" + if [ -n "$all_versions" ]; then + sys_phpversion="${all_versions##*\ }" + fi + fi - echo "$sys_phpversion" + echo "$sys_phpversion" } -is_hestia_package(){ - check=false; - for pkg in $1; do - if [ "$pkg" == "$2" ]; then - check="true"; - fi - done - if [ "$check" != "true" ]; then - check_result $E_INVALID "$2 package is not controlled by hestiacp" - fi +is_hestia_package() { + check=false + for pkg in $1; do + if [ "$pkg" == "$2" ]; then + check="true" + fi + done + if [ "$check" != "true" ]; then + check_result $E_INVALID "$2 package is not controlled by hestiacp" + fi } # Run arbitrary cli commands with dropped privileges @@ -1526,136 +1540,136 @@ is_hestia_package(){ # Input: # - $user : Vaild hestia user user_exec() { - is_object_valid 'user' 'USER' "$user" + is_object_valid 'user' 'USER' "$user" - local user_groups=$(id -G "$user") - user_groups=${user_groups//\ /,} + local user_groups=$(id -G "$user") + user_groups=${user_groups//\ /,} - setpriv --groups "$user_groups" --reuid "$user" --regid "$user" -- "${@}" + setpriv --groups "$user_groups" --reuid "$user" --regid "$user" -- "${@}" } # Simple chmod wrapper that skips symlink files after glob expand no_symlink_chmod() { - local filemode=$1; shift; + local filemode=$1 + shift - for i in "$@"; do - [[ -L ${i} ]] && continue + for i in "$@"; do + [[ -L ${i} ]] && continue - chmod "${filemode}" "${i}" - done + chmod "${filemode}" "${i}" + done } -source_conf(){ - while IFS='= ' read -r lhs rhs - do - if [[ ! $lhs =~ ^\ *# && -n $lhs ]]; then - rhs="${rhs%%^\#*}" # Del in line right comments - rhs="${rhs%%*( )}" # Del trailing spaces - rhs="${rhs%\'*}" # Del opening string quotes - rhs="${rhs#\'*}" # Del closing string quotes - declare -g $lhs="$rhs" - fi - done < $1 +source_conf() { + while IFS='= ' read -r lhs rhs; do + if [[ ! $lhs =~ ^\ *# && -n $lhs ]]; then + rhs="${rhs%%^\#*}" # Del in line right comments + rhs="${rhs%%*( )}" # Del trailing spaces + rhs="${rhs%\'*}" # Del opening string quotes + rhs="${rhs#\'*}" # Del closing string quotes + declare -g $lhs="$rhs" + fi + done < $1 } format_no_quotes() { - exclude="['|\"]" - if [[ "$1" =~ $exclude ]]; then - check_result "$E_INVALID" "Invalid $2 contains qoutes (\" or ') :: $1" - fi - is_no_new_line_format "$1" + exclude="['|\"]" + if [[ "$1" =~ $exclude ]]; then + check_result "$E_INVALID" "Invalid $2 contains qoutes (\" or ') :: $1" + fi + is_no_new_line_format "$1" } -is_username_format_valid(){ - if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]] ; then - is_string_format_valid "$1" "$2" - fi +is_username_format_valid() { + if [[ ! "$1" =~ ^[A-Za-z0-9._%+-]+@[[:alnum:].-]+\.[A-Za-z]{2,63}$ ]]; then + is_string_format_valid "$1" "$2" + fi } -change_sys_value(){ - check_ckey=$(grep "^$1='" "$HESTIA/conf/hestia.conf") - if [ -z "$check_ckey" ]; then - echo "$1='$2'" >> "$HESTIA/conf/hestia.conf" - else - sed -i "s|^$1=.*|$1='$2'|g" "$HESTIA/conf/hestia.conf" - fi +change_sys_value() { + check_ckey=$(grep "^$1='" "$HESTIA/conf/hestia.conf") + if [ -z "$check_ckey" ]; then + echo "$1='$2'" >> "$HESTIA/conf/hestia.conf" + else + sed -i "s|^$1=.*|$1='$2'|g" "$HESTIA/conf/hestia.conf" + fi } # Checks the format of APIs that will be allowed for the key is_key_permissions_format_valid() { - local permissions="$1" - local user="$2" + local permissions="$1" + local user="$2" - if [[ "$user" != "admin" && -z "$permissions" ]]; then - check_result "$E_INVALID" "Non-admin users need a permission list" - fi + if [[ "$user" != "admin" && -z "$permissions" ]]; then + check_result "$E_INVALID" "Non-admin users need a permission list" + fi - while IFS=',' read -ra permissions_arr; do - for permission in "${permissions_arr[@]}"; do - permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" + while IFS=',' read -ra permissions_arr; do + for permission in "${permissions_arr[@]}"; do + permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" -# if [[ -z "$(echo "$permission" | grep -E "^v-")" ]]; then - if [[ ! -e "$HESTIA/data/api/$permission" ]]; then - check_result "$E_NOTEXIST" "API $permission doesn't exist" - fi + # if [[ -z "$(echo "$permission" | grep -E "^v-")" ]]; then + if [[ ! -e "$HESTIA/data/api/$permission" ]]; then + check_result "$E_NOTEXIST" "API $permission doesn't exist" + fi - source_conf "$HESTIA/data/api/$permission"; - if [ "$ROLE" = "admin" ] && [ "$user" != "admin" ]; then - check_result "$E_INVALID" "Only the admin can run this API" - fi -# elif [[ ! -e "$BIN/$permission" ]]; then -# check_result "$E_NOTEXIST" "Command $permission doesn't exist" -# fi - done - done <<<"$permissions" + source_conf "$HESTIA/data/api/$permission" + if [ "$ROLE" = "admin" ] && [ "$user" != "admin" ]; then + check_result "$E_INVALID" "Only the admin can run this API" + fi + # elif [[ ! -e "$BIN/$permission" ]]; then + # check_result "$E_NOTEXIST" "Command $permission doesn't exist" + # fi + done + done <<< "$permissions" } # Remove whitespaces, and bin path from commands cleanup_key_permissions() { - local permissions="$1" + local permissions="$1" - local final quote - while IFS=',' read -ra permissions_arr; do - for permission in "${permissions_arr[@]}"; do - permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" + local final quote + while IFS=',' read -ra permissions_arr; do + for permission in "${permissions_arr[@]}"; do + permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" - # Avoid duplicate items - if [[ -z "$(echo ",${final}," | grep ",${permission},")" ]]; then - final+="${quote}${permission}" - quote=',' - fi - done - done <<<"$permissions" + # Avoid duplicate items + if [[ -z "$(echo ",${final}," | grep ",${permission},")" ]]; then + final+="${quote}${permission}" + quote=',' + fi + done + done <<< "$permissions" - echo "$final" + echo "$final" } # Extract all allowed commands from a permission list get_apis_commands() { - local permissions="$1" + local permissions="$1" - local allowed_commands quote commands_to_add - while IFS=',' read -ra permissions_arr; do - for permission in "${permissions_arr[@]}"; do - permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" + local allowed_commands quote commands_to_add + while IFS=',' read -ra permissions_arr; do + for permission in "${permissions_arr[@]}"; do + permission="$(basename "$permission" | sed -E "s/^\s*|\s*$//g")" - commands_to_add="" -# if [[ -n "$(echo "$permission" | grep -E "^v-")" ]]; then -# commands_to_add="$permission" -# el - if [[ -e "$HESTIA/data/api/$permission" ]]; then - source_conf "$HESTIA/data/api/$permission"; - commands_to_add="$COMMANDS" - fi + commands_to_add="" + # if [[ -n "$(echo "$permission" | grep -E "^v-")" ]]; then + # commands_to_add="$permission" + # el + if [[ -e "$HESTIA/data/api/$permission" ]]; then + source_conf "$HESTIA/data/api/$permission" + commands_to_add="$COMMANDS" + fi - if [[ -n "$commands_to_add" ]]; then - allowed_commands+="${quote}${commands_to_add}" - quote=',' - fi - done - done <<<"$permissions" + if [[ -n "$commands_to_add" ]]; then + allowed_commands+="${quote}${commands_to_add}" + quote=',' + fi + done + done <<< "$permissions" - cleanup_key_permissions "$allowed_commands" + cleanup_key_permissions "$allowed_commands" } # Get the position of an argument by name in a hestia command using the command's documentation comment. @@ -1664,28 +1678,28 @@ get_apis_commands() { # * 0: It doesn't have the argument; # * 1-9: The position of the argument in the command. search_command_arg_position() { - local hst_command="$(basename "$1")" - local arg_name="$2" - - local command_path="$BIN/$hst_command" - if [[ -z "$hst_command" || ! -e "$command_path" ]]; then - echo "-1" - return - fi - - local position=0 - local count=0 - local command_options="$(sed -En 's/^# options: (.+)/\1/p' "$command_path")" - while IFS=' ' read -ra options_arr; do - for option in "${options_arr[@]}"; do - count=$((count+1)) - - option_name="$(echo " $option " | sed -E 's/^(\s|\[)*|(\s|\])*$//g')" - if [[ "${option_name^^}" == "$arg_name" ]]; then - position=$count - fi - done - done <<<"$command_options" - - echo "$position" + local hst_command="$(basename "$1")" + local arg_name="$2" + + local command_path="$BIN/$hst_command" + if [[ -z "$hst_command" || ! -e "$command_path" ]]; then + echo "-1" + return + fi + + local position=0 + local count=0 + local command_options="$(sed -En 's/^# options: (.+)/\1/p' "$command_path")" + while IFS=' ' read -ra options_arr; do + for option in "${options_arr[@]}"; do + count=$((count + 1)) + + option_name="$(echo " $option " | sed -E 's/^(\s|\[)*|(\s|\])*$//g')" + if [[ "${option_name^^}" == "$arg_name" ]]; then + position=$count + fi + done + done <<< "$command_options" + + echo "$position" } From e806cc868792d4230f20a26f53026ba7c01cc94a Mon Sep 17 00:00:00 2001 From: asmcc Date: Mon, 13 Feb 2023 00:37:18 +0100 Subject: [PATCH 014/223] Adaptation func/main.sh for correct working with IPV6 addresses --- func/main.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/func/main.sh b/func/main.sh index a6dc3a5f0b..20335867a5 100644 --- a/func/main.sh +++ b/func/main.sh @@ -742,7 +742,7 @@ is_ip_format_valid() { # IPv6 format validator is_ipv6_format_valid() { - object_name=${2-ip6} + object_name=${2-ipv6} ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' t_ip=$(echo $1 | awk -F / '{print $1}') t_cidr=$(echo $1 | awk -F / '{print $2}') From 361d277fbff65810524dd634d3dfffdbfcad65ab Mon Sep 17 00:00:00 2001 From: asmcc Date: Mon, 13 Feb 2023 00:42:32 +0100 Subject: [PATCH 015/223] Adaptation func/main.sh for correct working with IPV6 addresses --- func/main.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/func/main.sh b/func/main.sh index a6dc3a5f0b..20335867a5 100644 --- a/func/main.sh +++ b/func/main.sh @@ -742,7 +742,7 @@ is_ip_format_valid() { # IPv6 format validator is_ipv6_format_valid() { - object_name=${2-ip6} + object_name=${2-ipv6} ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' t_ip=$(echo $1 | awk -F / '{print $1}') t_cidr=$(echo $1 | awk -F / '{print $2}') From e08e3c79484751faf5e226300cfc9d469faf8b31 Mon Sep 17 00:00:00 2001 From: asmcc Date: Tue, 14 Feb 2023 23:19:31 +0100 Subject: [PATCH 016/223] Revised edition of shell scripts for addition and delete of a system ipv6 interface bin/v-add-sys-ipv6: revised bin/v-delete-sys-ipv6: revised func/main.sh: small changes revised and tested with debian based hestia --- bin/v-add-sys-ipv6 | 132 ++++++++++++++++++++++++++++-------------- bin/v-delete-sys-ipv6 | 86 ++++++++++++++++----------- func/main.sh | 76 ++++++++++++------------ 3 files changed, 179 insertions(+), 115 deletions(-) diff --git a/bin/v-add-sys-ipv6 b/bin/v-add-sys-ipv6 index 66a98b66f8..7ec2dd840a 100755 --- a/bin/v-add-sys-ipv6 +++ b/bin/v-add-sys-ipv6 @@ -1,76 +1,104 @@ #!/bin/bash -# info: add system ip address -# options: IPV6 NETMASK [INTERFACE] [USER] [IP_STATUS] [IP_NAME] +# info: add system ipv6 address +# options: IPV6 [PREFIXLEN] [INTERFACE] [USER] [IP_STATUS] [IP_NAME] # -# The function adds ipv6 address into a system. It also creates rc scripts. You +# example: v-add-sys-ipv6 1234:55:66::1 /64 +# +# This function adds ipv6 address into a system. It also creates rc scripts. You # can specify ipv6 name which will be used as root domain for temporary aliases. # For example, if you set a1.myhosting.com as name, each new domain created on # this ipv6 will automatically receive alias $domain.a1.myhosting.com. Of course # you must have wildcard record *.a1.myhosting.com pointed to ipv6. This feature # is very handy when customer wants to test domain before dns migration. - #----------------------------------------------------------# -# Variable&Function # +# Variables & Functions # #----------------------------------------------------------# +# Get interface name +iface=$(/bin/ip token | awk -F 'dev ' '{print $2}') + # Argument definition ipv6=${1// /} -netmask=$2 -interface="${3-eth0}" +prefix_length="${2-/64}" +iface="${3-$iface}" user="${4-admin}" ip_status="${5-shared}" ip_name=$6 # 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 +# shellcheck source=/usr/local/hestia/func/ipv6.sh source $HESTIA/func/ipv6.sh +# shellcheck source=/usr/local/hestia/func/domain.sh source $HESTIA/func/domain.sh -source $HESTIA/conf/hestia.conf - +# load config file +source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# -check_args '2' "$#" 'IPV6 NETMASK [INTERFACE] [USER] [STATUS] [NAME]' -is_format_valid 'ipv6' 'netmaskv6' 'interface' 'user' 'ip_status' +check_args '1' "$#" 'IPV6 [PREFIXLEN] [INTERFACE] [USER] [STATUS] [NAME]' +is_format_valid 'ipv6' 'prefix_length' 'iface' 'user' 'ip_status' is_ipv6_free is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" -if [ ! -z "$ip_name" ] ; then +if [ -n "$ip_name" ] ; then is_format_valid 'ip_name' fi +if [ "$user" != "admin" ]; then + ip_status="dedicated" +fi + +# Perform verification if read-only mode is enabled +check_hestia_demo_mode #----------------------------------------------------------# # Action # #----------------------------------------------------------# -iface=$(get_ipv6_iface) -#cidr=$(convert_netmaskv6 $netmask) -cidr=$netmask sys_ip_check=$(/sbin/ip -6 addr | grep "$ipv6") if [ -z "$sys_ip_check" ]; then # Adding sys ip - /sbin/ip addr add $ipv6/$cidr dev $interface - - # Adding RHEL/CentOS/Fedora startup script - if [ -e "/etc/redhat-release" ]; then - sys_ip="# Added by hestia" - sys_ip="$sys_ip\nIPV6INIT=yes" - sys_ip="$sys_ip\nIPV6ADDR=$ipv6/$cidr" - sys_ip="$sys_ip\nIPV6_DEFAULTGW=$interface" - sys_ip="$sys_ip\nIPV6_AUTOCONF=no" - #sys_ip="$sys_ip\nIPV6ADDR_SECONDARIES=""" - echo -e $sys_ip > /etc/sysconfig/network-scripts/ifcfg-$interface + /sbin/ip addr add $ipv6$prefix_length dev ${iface%:*} label ${iface} + + + # Check if netplan is in use and generate configuration file + if [ ! -z $(which netplan) ]; then + if [ ! -z "$(netplan generate --mapping "$iface" | grep networkd)" ]; then + netplan=1 + else + netplan=0 + fi + else + netplan=0 fi - - # Adding Debian/Ubuntu startup script - if [ -e "/etc/debian_version" ]; then - sys_ip="\n# Added by hestia" - sys_ip="$sys_ip\niface $interface inet6 static" - sys_ip="$sys_ip\naddress $ipv6" - sys_ip="$sys_ip\nnetmask $cidr" + + if [ "$netplan" == "1" ]; then + if [ -f "/etc/netplan/60-hestia.yaml" ]; then + sys_ip=" - $ipv6$prefix_length" + else + sys_ip="# Added by hestia, please do not edit the file manually!" + sys_ip="$sys_ip\nnetwork:" + sys_ip="$sys_ip\n version: 2" + sys_ip="$sys_ip\n renderer: networkd" + sys_ip="$sys_ip\n ethernets:" + sys_ip="$sys_ip\n $iface:" + sys_ip="$sys_ip\n addresses:" + sys_ip="$sys_ip\n - $ipv6$prefix_length" + fi + IFS='%' + echo -e $sys_ip >> /etc/netplan/60-hestia.yaml + unset IFS + else + sys_ip="\n# Added by Hestia Control Panel" + sys_ip="$sys_ip\nauto ${iface}" + sys_ip="$sys_ip\niface ${iface} inet6 static" + sys_ip="$sys_ip\naddress $ipv6$prefix_length" echo -e $sys_ip >> /etc/network/interfaces fi fi @@ -86,8 +114,8 @@ STATUS='$ip_status' NAME='$ip_name' U_SYS_USERS='' U_WEB_DOMAINS='0' -INTERFACE='$interface' -NETMASK='$netmask' +INTERFACE='$iface' +NETMASK='$prefix_length' NAT='' TIME='$time' DATE='$date' @@ -104,28 +132,45 @@ if [ ! -z "$WEB_SYSTEM" ]; then echo "NameVirtualHost [$ipv6]:$WEB_PORT" > $web_conf fi echo "Listen [$ipv6]:$WEB_PORT" >> $web_conf + cat $HESTIA_INSTALL_DIR/apache2/unassigned.conf >> $web_conf + sed -i 's/directIP/'$ipv6'/g' $web_conf + sed -i 's/directPORT/'$WEB_PORT'/g' $web_conf + + elif [ "$WEB_SYSTEM" = 'nginx' ]; then + cp -f $HESTIA_INSTALL_DIR/nginx/unassigned.inc $web_conf + sed -i 's/directIP/'$ipv6'/g' $web_conf fi if [ "$WEB_SSL" = 'mod_ssl' ]; then if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then - echo "NameVirtualHost [$ipv6]:$WEB_SSL_PORT" >> $web_conf + sed -i "1s/^/NameVirtualHost $ipv6:$WEB_SSL_PORT\n/" $web_conf fi - echo "Listen [$ipv6]:$WEB_SSL_PORT" >> $web_conf + sed -i "1s/^/Listen $ipv6:$WEB_SSL_PORT\n/" $web_conf + sed -i 's/directSSLPORT/'$WEB_SSL_PORT'/g' $web_conf fi fi # Proxy support -if [ ! -z "$PROXY_SYSTEM" ]; then +if [ -n "$PROXY_SYSTEM" ]; then cat $WEBTPL/$PROXY_SYSTEM/proxy_ip.tpl |\ sed -e "s/%ip%/[$ipv6]/g" \ -e "s/%web_port%/$WEB_PORT/g" \ -e "s/%proxy_port%/$PROXY_PORT/g" \ + -e "s/%proxy_ssl_port%/$PROXY_SSL_PORT/g" \ > /etc/$PROXY_SYSTEM/conf.d/$ipv6.conf + # mod_extract_forwarded + fw_conf="/etc/$WEB_SYSTEM/conf.d/mod_extract_forwarded.conf" + if [ -e "$fw_conf" ]; then + ips=$(grep 'MEFaccept ' $fw_conf | grep -v '#' | head -n1) + sed -i "s/$ips/$ips $ipv6/g" $fw_conf + fi + # mod_rpaf rpaf_conf="/etc/$WEB_SYSTEM/mods-enabled/rpaf.conf" if [ -e "$rpaf_conf" ]; then rpaf_str=$(grep RPAFproxy_ips $rpaf_conf) + [ -z "$rpaf_str" ] && sed -i 's||RPAFproxy_ips\n|' $rpaf_conf && rpaf_str='RPAFproxy_ips' rpaf_str="$rpaf_str $ipv6" sed -i "s/.*RPAFproxy_ips.*/$rpaf_str/" $rpaf_conf fi @@ -139,7 +184,6 @@ if [ ! -z "$PROXY_SYSTEM" ]; then fi fi - #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# @@ -148,7 +192,7 @@ fi increase_user_value "$user" '$IPV6_OWNED' if [ "$user" = 'admin' ]; then if [ "$ip_status" = 'shared' ]; then - for user in $(ls $HESTIA/data/users); do + for hestia_user in $($HESTIA/bin/v-list-sys-users plain); do increase_user_value "$user" '$IPV6_AVAIL' done else @@ -164,18 +208,18 @@ $BIN/v-restart-web check_result $? "Web restart failed" >/dev/null # Restarting proxy server -if [ ! -z "$PROXY_SYSTEM" ]; then +if [ -n "$PROXY_SYSTEM" ]; then $BIN/v-restart-proxy check_result $? "Proxy restart failed" >/dev/null fi # Restarting firewall -if [ ! -z "$FIREWALL_SYSTEM" ]; then +if [ -n "$FIREWALL_SYSTEM" ]; then $BIN/v-update-firewall fi # Logging -log_history "added system ipv6 address $ipv6" '' 'admin' +$BIN/v-log-action "system" "Info" "Network" "Added new IPV6 address to the system (IPV6: $ipv6)." log_event "$OK" "$ARGUMENTS" -exit \ No newline at end of file +exit diff --git a/bin/v-delete-sys-ipv6 b/bin/v-delete-sys-ipv6 index 972fad0980..54c338c666 100755 --- a/bin/v-delete-sys-ipv6 +++ b/bin/v-delete-sys-ipv6 @@ -2,23 +2,29 @@ # info: delete system ipv6 # options: IPV6 # -# The function for deleting a system ip. It does not allow to delete first ip -# on interface and do not allow to delete ip which is used by a web domain. - +# example: v-delete-sys-ipv6 1234:5678::1 +# +# This function for deleting a system ipv6. It does not allow +# to delete ip which is used by a web domain. #----------------------------------------------------------# -# Variable&Function # +# Variables & Functions # #----------------------------------------------------------# # Argument definition ipv6=$1 # 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 +# shellcheck source=/usr/local/hestia/func/ipv6.sh source $HESTIA/func/ipv6.sh +# shellcheck source=/usr/local/hestia/func/domain.sh source $HESTIA/func/domain.sh -source $HESTIA/conf/hestia.conf - +# load config file +source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# # Verifications # @@ -26,30 +32,31 @@ source $HESTIA/conf/hestia.conf check_args '1' "$#" 'IPV6' is_format_valid 'ipv6' -is_ip_valid "$ipv6" -is_ip_key_empty '$U_WEB_DOMAINS' -is_ip_key_empty '$U_SYS_USERS' +is_ipv6_valid "$ipv6" +is_ipv6_key_empty '$U_WEB_DOMAINS' +is_ipv6_key_empty '$U_SYS_USERS' +# Perform verification if read-only mode is enabled +check_hestia_demo_mode #----------------------------------------------------------# # Action # #----------------------------------------------------------# # Import ip variables -source $HESTIA/data/ips/$ipv6 -cidr=$(convert_netmaskv6 $NETMASK) +source "$HESTIA/data/ips/$ipv6" # Checking main ip on the interface -interface=$(/sbin/ip -6 addr | grep "$ipv6/$cidr" | awk '{print $NF}') -if [ ! -z "$interface" ] && [ -z "$(echo $interface |cut -s -f2 -d :)" ]; then +interface=$(/sbin/ip -6 addr | grep "$ipv6${NETMASK}" | awk '{print $NF}') +if [ $(/sbin/ip -6 addr | grep -c global) -le 1 ]; then echo "Error: can't delete main IP address" log_event "$E_FORBIDEN" "$ARGUMENTS" - exit $E_FORBIDEN + exit "$E_FORBIDEN" fi # Deleting system ip -if [ ! -z "$interface" ]; then - /sbin/ip -6 addr del $ip/$cidr dev $INTERFACE +if [ -n "$interface" ]; then + /sbin/ip -6 addr del $ipv6${NETMASK} dev ${INTERFACE%:*} if [ "$?" -ne 0 ]; then echo "Error: can't delete system ip" log_event "$E_FORBIDEN" "$ARGUMENTS" @@ -63,11 +70,16 @@ if [ -e "/etc/sysconfig/network-scripts/ifcfg-$interface" ]; then fi # Deleting startup conf on Debian/Ubuntu -if [ -e "/etc/network/interfaces" ]; then - ip_str=$(grep -n $ip$ /etc/network/interfaces |cut -f1 -d:) - if [ ! -z "$ip_str" ]; then - first_str=$((ip_str - 3)) - last_str=$((ip_str + 1)) +if [ -f "/etc/netplan/60-hestia.yaml" ]; then + sed -i "/$ipv6/d" /etc/netplan/60-hestia.yaml + if ! grep -q '-' /etc/netplan/60-hestia.yaml; then + rm /etc/netplan/60-hestia.yaml + fi +elif [ -e "/etc/network/interfaces" ]; then + ip_str=$(grep -n $ipv6 /etc/network/interfaces |cut -f1 -d:) + if [ -n "$ip_str" ]; then + first_str=$((ip_str - 4)) + last_str=$((ip_str + 1)) sed -i "$first_str,$last_str d" /etc/network/interfaces fi fi @@ -76,12 +88,12 @@ fi rm -f $HESTIA/data/ips/$ipv6 # Deleting web config -if [ ! -z "$WEB_SYSTEM" ]; then +if [ -n "$WEB_SYSTEM" ]; then rm -f /etc/$WEB_SYSTEM/conf.d/$ipv6.conf fi # Deleting proxy config -if [ ! -z "$PROXY_SYSTEM" ]; then +if [ -n "$PROXY_SYSTEM" ]; then rm -f /etc/$PROXY_SYSTEM/conf.d/$ipv6.conf # mod_extract_forwarded @@ -95,33 +107,37 @@ if [ ! -z "$PROXY_SYSTEM" ]; then # mod_rpaf rpaf_conf="/etc/$WEB_SYSTEM/mods-enabled/rpaf.conf" if [ -e "$rpaf_conf" ]; then - ips=$(grep RPAFproxy_ips $rpaf_conf) - new_ips=$(echo "$rpaf_str" | sed "s/$ipv6//") - sed -i "s/$ips/$new_ips/g" $rpaf_conf + ips=$(grep RPAFproxy_ips "$rpaf_conf") + new_ips=$(echo "$ips" | sed "s/$ipv6//") + sed -i "s/$ips/$new_ips/g" "$rpaf_conf" + + # Remove RPAFproxy_ips line when ip list is empty + [ "$(grep RPAFproxy_ips $rpaf_conf | sed 's/^[[:space:]]*//g')" = "RPAFproxy_ips" ] && sed -i "/RPAFproxy_ips/d" $rpaf_conf fi #mod_remoteip remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf" if [ -e "$remoteip_conf" ]; then - sed -i "s/RemoteIPInternalProxy $ipv6//g" $remoteip_conf + sed -i "/RemoteIPInternalProxy $ipv6\$/d" "$remoteip_conf" fi fi - #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# # Updating user conf -if [ ! -z "$OWNER" ]; then +if [ -n "$OWNER" ]; then decrease_user_value "$OWNER" '$IPV6_OWNED' fi if [ "$OWNER" = 'admin' ]; then if [ "$STATUS" = 'shared' ]; then - for user in $(ls $HESTIA/data/users/); do - decrease_user_value "$user" '$IPV6_AVAIL' + for hestia_user in $($HESTIA/bin/v-list-sys-users plain); do + decrease_user_value "$hestia_user" '$IPV6_AVAIL' done + else + decrease_user_value "$OWNER" '$IPV6_AVAIL' fi else decrease_user_value "$OWNER" '$IPV6_AVAIL' @@ -132,18 +148,18 @@ $BIN/v-restart-web check_result $? "Web restart failed" >/dev/null # Restarting proxy server -if [ ! -z "$PROXY_SYSTEM" ]; then +if [ -n "$PROXY_SYSTEM" ]; then $BIN/v-restart-proxy check_result $? "Proxy restart failed" >/dev/null fi # Restarting firewall -if [ ! -z "$FIREWALL_SYSTEM" ]; then +if [ -n "$FIREWALL_SYSTEM" ]; then $BIN/v-update-firewall fi # Logging -log_history "deleted system ip address $ip" +$BIN/v-log-action "system" "Info" "System" "IPV6 address deleted (IPV6: $ipv6)." log_event "$OK" "$ARGUMENTS" -exit \ No newline at end of file +exit diff --git a/func/main.sh b/func/main.sh index 20335867a5..90a90d3723 100644 --- a/func/main.sh +++ b/func/main.sh @@ -710,7 +710,7 @@ is_domain_format_valid() { is_no_new_line_format "$1" } -# Alias forman validator +# Alias format validator is_alias_format_valid() { for object in ${1//,/ }; do exclude="[!|@|#|$|^|&|(|)|+|=|{|}|:|<|>|?|_|/|\|\"|'|;|%|\`| ]" @@ -742,43 +742,46 @@ is_ip_format_valid() { # IPv6 format validator is_ipv6_format_valid() { - object_name=${2-ipv6} - ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' - t_ip=$(echo $1 | awk -F / '{print $1}') - t_cidr=$(echo $1 | awk -F / '{print $2}') - valid_cidr=1 - - WORD="[0-9A-Fa-f]\{1,4\}" - # flat address, no compressed words - FLAT="^${WORD}\(:${WORD}\)\{7\}$" - - COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" - COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" - COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" - COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" - COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" - COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" - # trailing :: edge case, includes case of only :: (all 0's) - EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" - # leading :: edge case - EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" - - echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" - if [ $? -ne 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi + object_name=${2-ipv6} + t_ipv6=$(echo $1 |awk -F / '{print $1}') + t_prefixlen=$(echo $1 |awk -F / '{print $2}') + valid_prefixlen=1 + + if [[ "$object_name" != "prefix_length" ]] || [[ -n "$t_ipv6" ]]; then + # check for ipv6 address format excluding "prefix length only" mode + WORD="[0-9A-Fa-f]\{1,4\}" + # flat address, no compressed words + FLAT="^${WORD}\(:${WORD}\)\{7\}$" - if [ -n "$(echo $1 | grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 + COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" + COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" + COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" + COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" + COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" + COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" + # trailing :: edge case, includes case of only :: (all 0's) + EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" + # leading :: edge case + EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" + echo $t_ipv6 | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" + if [ $? -ne 0 ]; then + check_result "$E_INVALID" "invalid ipv6 address format :: $1" fi fi - if [ "$valid_cidr" -eq 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi + + # ipv6 prefix length checks + if [ -n "$(echo $1|grep '/')" ]; then + # introducing slash as prefix length attribute detected + if [[ "$t_prefixlen" -lt 0 ]] || [[ "$t_prefixlen" -gt 128 ]]; then + valid_prefixlen=0 + fi + if ! [[ "$t_prefixlen" =~ ^[0-9]+$ ]]; then + valid_prefixlen=0 + fi + fi + if [ "$valid_prefixlen" -eq 0 ]; then + check_result "$E_INVALID" "invalid ipv6 prefix length format :: $1" + fi } is_ip46_format_valid() { @@ -1023,7 +1026,7 @@ is_int_format_valid() { # Interface validator is_interface_format_valid() { netdevices=$(cat /proc/net/dev | grep : | cut -f 1 -d : | tr -d ' ') - if [ -z $(echo "$netdevices" | grep -x $1) ]; then + if [ -z $(echo "$netdevices" | grep -x ${1%:*}) ]; then check_result "$E_INVALID" "invalid interface format :: $1" fi } @@ -1209,6 +1212,7 @@ is_format_valid() { password) is_password_format_valid "$arg" ;; port) is_int_format_valid "$arg" 'port' ;; port_ext) is_fw_port_format_valid "$arg" ;; + prefix_length) is_ipv6_format_valid "$arg" 'prefix_length' ;; protocol) is_fw_protocol_format_valid "$arg" ;; proxy_ext) is_extention_format_valid "$arg" ;; quota) is_int_format_valid "$arg" 'quota' ;; From 942a3134e7d78626d7b85a028a031aad2648a765 Mon Sep 17 00:00:00 2001 From: asmcc Date: Thu, 16 Feb 2023 23:31:15 +0100 Subject: [PATCH 017/223] IPV6: add/edit/delete IPV6 address User web interface. PHP files and a template --- web/add/ip/index.php | 8 +++++++- web/delete/ip/index.php | 7 ++++++- web/edit/ip/index.php | 15 ++++++++++----- web/templates/pages/edit_ip.php | 6 ++++++ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/web/add/ip/index.php b/web/add/ip/index.php index 674557f7f8..0180ee9ada 100644 --- a/web/add/ip/index.php +++ b/web/add/ip/index.php @@ -50,6 +50,12 @@ $v_interface = quoteshellarg($_POST["v_interface"]); $v_owner = quoteshellarg($_POST["v_owner"]); $v_shared = $_POST["v_shared"]; + if (filter_var($_POST["v_ip"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $v_ipv6_suffix='v6'; + } else { + $v_ipv6_suffix=''; + + } // Check shared checkmark if ($v_shared == "on") { @@ -63,7 +69,7 @@ if (empty($_SESSION["error_msg"])) { exec( HESTIA_CMD . - "v-add-sys-ip " . + "v-add-sys-ip".$v_ipv6_suffix." " . $v_ip . " " . $v_netmask . diff --git a/web/delete/ip/index.php b/web/delete/ip/index.php index b25115807a..dd45ccc171 100644 --- a/web/delete/ip/index.php +++ b/web/delete/ip/index.php @@ -10,7 +10,12 @@ if ($_SESSION["userContext"] === "admin") { if (!empty($_GET["ip"])) { $v_ip = quoteshellarg($_GET["ip"]); - exec(HESTIA_CMD . "v-delete-sys-ip " . $v_ip, $output, $return_var); + if (filter_var($_GET["ip"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $v_ipv6_suffix='v6'; + } else { + $v_ipv6_suffix=''; + } + exec(HESTIA_CMD . "v-delete-sys-ip".$v_ipv6_suffix." " . $v_ip, $output, $return_var); } check_return_code($return_var, $output); unset($output); diff --git a/web/edit/ip/index.php b/web/edit/ip/index.php index ff1a0dda4d..2fc48bc906 100644 --- a/web/edit/ip/index.php +++ b/web/edit/ip/index.php @@ -21,7 +21,12 @@ // List ip $v_ip = quoteshellarg($_GET["ip"]); -exec(HESTIA_CMD . "v-list-sys-ip " . $v_ip . " 'json'", $output, $return_var); +if (filter_var($_GET['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $v_ipv6_suffix='v6'; +} else { + $v_ipv6_suffix=''; +} +exec(HESTIA_CMD . "v-list-sys-ip".$v_ipv6_suffix." " . $v_ip . " 'json'", $output, $return_var); check_return_code_redirect($return_var, $output, "/list/ip"); $data = json_decode(implode("", $output), true); unset($output); @@ -55,13 +60,13 @@ // Change Status if ($v_ipstatus == "shared" && empty($_POST["v_shared"]) && empty($_SESSION["error_msg"])) { - exec(HESTIA_CMD . "v-change-sys-ip-status " . $v_ip . " 'dedicated'", $output, $return_var); + exec(HESTIA_CMD . "v-change-sys-ip".$v_ipv6_suffix."-status " . $v_ip . " 'dedicated'", $output, $return_var); check_return_code($return_var, $output); unset($output); $v_dedicated = "yes"; } if ($v_ipstatus == "dedicated" && !empty($_POST["v_shared"]) && empty($_SESSION["error_msg"])) { - exec(HESTIA_CMD . "v-change-sys-ip-status " . $v_ip . " 'shared'", $output, $return_var); + exec(HESTIA_CMD . "v-change-sys-ip".$v_ipv6_suffix."-status " . $v_ip . " 'shared'", $output, $return_var); check_return_code($return_var, $output); unset($output); unset($v_dedicated); @@ -70,7 +75,7 @@ // Change owner if ($v_owner != $_POST["v_owner"] && empty($_SESSION["error_msg"])) { $v_owner = quoteshellarg($_POST["v_owner"]); - exec(HESTIA_CMD . "v-change-sys-ip-owner " . $v_ip . " " . $v_owner, $output, $return_var); + exec(HESTIA_CMD . "v-change-sys-ip".$v_ipv6_suffix."-owner " . $v_ip . " " . $v_owner, $output, $return_var); check_return_code($return_var, $output); $v_owner = $_POST["v_owner"]; unset($output); @@ -79,7 +84,7 @@ // Change associated domain if ($v_name != $_POST["v_name"] && empty($_SESSION["error_msg"])) { $v_name = quoteshellarg($_POST["v_name"]); - exec(HESTIA_CMD . "v-change-sys-ip-name " . $v_ip . " " . $v_name, $output, $return_var); + exec(HESTIA_CMD . "v-change-sys-ip".$v_ipv6_suffix."-name " . $v_ip . " " . $v_name, $output, $return_var); check_return_code($return_var, $output); unset($output); } diff --git a/web/templates/pages/edit_ip.php b/web/templates/pages/edit_ip.php index 9b0db92a73..d64f35e075 100644 --- a/web/templates/pages/edit_ip.php +++ b/web/templates/pages/edit_ip.php @@ -70,12 +70,18 @@ "> +
">
+ From 0a7d40e81c53c6cef1c70a3f314e92973cef598c Mon Sep 17 00:00:00 2001 From: asmcc Date: Fri, 17 Feb 2023 20:41:00 +0100 Subject: [PATCH 018/223] IPV6: Add the ipv6 option to debian install script --- install/hst-install-debian.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/hst-install-debian.sh b/install/hst-install-debian.sh index 37f48be817..a4b1e0025e 100755 --- a/install/hst-install-debian.sh +++ b/install/hst-install-debian.sh @@ -264,6 +264,7 @@ while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:e:p:D:fh" Option; do l) lang=$OPTARG ;; # Language d) api=$OPTARG ;; # Activate API y) interactive=$OPTARG ;; # Interactive install + 6) ipv6=$OPTARG ;; # IPv6 s) servername=$OPTARG ;; # Hostname e) email=$OPTARG ;; # Admin email p) vpass=$OPTARG ;; # Admin password From c809bbea8cdc73f718eb77f8cfe2b0fd22c56cdb Mon Sep 17 00:00:00 2001 From: asmcc Date: Fri, 17 Feb 2023 21:06:30 +0100 Subject: [PATCH 019/223] IPV6: Change release to 1.8.0~alpha --- install/hst-install-debian.sh | 2 +- install/hst-install-ubuntu.sh | 2 +- package.json | 2 +- src/deb/hestia/control | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/install/hst-install-debian.sh b/install/hst-install-debian.sh index a4b1e0025e..649877875a 100755 --- a/install/hst-install-debian.sh +++ b/install/hst-install-debian.sh @@ -32,7 +32,7 @@ HESTIA_COMMON_DIR="$HESTIA/install/common" VERBOSE='no' # Define software versions -HESTIA_INSTALL_VER='1.7.0~alpha' +HESTIA_INSTALL_VER='1.8.0~alpha' # Dependencies multiphp_v=("5.6" "7.0" "7.1" "7.2" "7.3" "7.4" "8.0" "8.1" "8.2") fpm_v="8.0" diff --git a/install/hst-install-ubuntu.sh b/install/hst-install-ubuntu.sh index 514e4dfb20..21bb01a502 100755 --- a/install/hst-install-ubuntu.sh +++ b/install/hst-install-ubuntu.sh @@ -32,7 +32,7 @@ HESTIA_COMMON_DIR="$HESTIA/install/common" VERBOSE='no' # Define software versions -HESTIA_INSTALL_VER='1.7.0~alpha' +HESTIA_INSTALL_VER='1.8.0~alpha' # Dependencies multiphp_v=("5.6" "7.0" "7.1" "7.2" "7.3" "7.4" "8.0" "8.1" "8.2") fpm_v="8.0" diff --git a/package.json b/package.json index 682ea5b3c1..05b2ffc79b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hestia", "private": true, - "version": "1.7.0-alpha", + "version": "1.8.0-alpha", "description": "An open-source Linux web server control panel.", "repository": "https://github.com/hestiacp/hestiacp", "license": "GPL-3.0-or-later", diff --git a/src/deb/hestia/control b/src/deb/hestia/control index 2ba1d2b7ce..fa401a59ec 100644 --- a/src/deb/hestia/control +++ b/src/deb/hestia/control @@ -1,7 +1,7 @@ Source: hestia Package: hestia Priority: optional -Version: 1.7.0~alpha +Version: 1.8.0~alpha Section: admin Maintainer: HestiaCP Homepage: https://www.hestiacp.com From ae4096d41d7333eed9e32ce4c888642a276f2d6a Mon Sep 17 00:00:00 2001 From: root Date: Sat, 18 Feb 2023 23:21:50 +0000 Subject: [PATCH 020/223] IPV6: Small corrections in apache template and debian install --- install/deb/apache2/status-ipv6.conf | 2 +- install/hst-install-debian.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/deb/apache2/status-ipv6.conf b/install/deb/apache2/status-ipv6.conf index e9e8cc32ba..eda3502e10 100644 --- a/install/deb/apache2/status-ipv6.conf +++ b/install/deb/apache2/status-ipv6.conf @@ -5,5 +5,5 @@ Listen [::1]:8081 Order deny,allow Deny from all Allow from 127.0.0.1 - Allow from [::1] + Allow from ::1 diff --git a/install/hst-install-debian.sh b/install/hst-install-debian.sh index 649877875a..5a8be4eef9 100755 --- a/install/hst-install-debian.sh +++ b/install/hst-install-debian.sh @@ -241,7 +241,7 @@ done eval set -- "$args" # Parsing arguments -while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:e:p:D:fh" Option; do +while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:6:s:e:p:D:fh" Option; do case $Option in a) apache=$OPTARG ;; # Apache w) phpfpm=$OPTARG ;; # PHP-FPM From f265e855536062b6093895fbccf04b5e5f96ec42 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 21 Feb 2023 00:55:45 +0000 Subject: [PATCH 021/223] IPV6: Universal script to list user IPs bin/v-list-user-ips Rewriting and adaptation to ipv6 Addition of new field VERSION (4/6) --- bin/v-list-user-ips | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/bin/v-list-user-ips b/bin/v-list-user-ips index a1647eb2fb..61badc70fe 100755 --- a/bin/v-list-user-ips +++ b/bin/v-list-user-ips @@ -22,18 +22,28 @@ source $HESTIA/func/main.sh # load config file source_conf "$HESTIA/conf/hestia.conf" +# Read and filter IP config file +read_ip_config() { + VERSION="" + source_conf "$HESTIA/data/ips/$1" + if [ -z "$VERSION" ]; then + VERSION=4 + fi +} + # JSON list function json_list() { echo '{' ip_count=$(echo "$ips" | wc -l) i=1 for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" + read_ip_config ${IP} echo -n ' "'$IP'": { "OWNER": "'$OWNER'", "STATUS": "'$STATUS'", "NAME": "'$NAME'", - "NAT": "'$NAT'" + "NAT": "'$NAT'", + "VERSION": "'$VERSION'" }' if [ "$i" -lt "$ip_count" ]; then echo ',' @@ -47,25 +57,26 @@ json_list() { # SHELL list function shell_list() { - echo "IP NAT OWNER STATUS NAME" - echo "-- --- ----- ------ ---" + echo "IP NAT OWNER STATUS NAME VER" + echo "-- --- ----- ------ ---- ---" for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" + read_ip_config ${IP} if [ -z "$NAT" ]; then NAT='no' fi if [ -z "$NAME" ]; then NAME='no' fi - echo "$IP $NAT $OWNER $STATUS $NAME" + echo "$IP $NAT $OWNER $STATUS $NAME $VERSION" done } # PLAIN list function plain_list() { for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" - echo -e "$IP\t$OWNER\t$STATUS\t$NAME\t$NAT" + VERSION="" + read_ip_config ${IP} + echo -e "$IP\t$OWNER\t$STATUS\t$NAME\t$NAT\t$VERSION" done } @@ -73,8 +84,8 @@ plain_list() { csv_list() { echo "IP,OWNER,STATUS,NAME,NAT" for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" - echo "$IP,$OWNER,$STATUS,$NAME,$NAT" + read_ip_config ${IP} + echo "$IP,$OWNER,$STATUS,$NAME,$NAT,$VERSION" done } @@ -92,11 +103,8 @@ is_object_valid 'user' 'USER' "$user" # Defining fileds to select owner='admin' -owner_ips=$(grep -A 1 -H "OWNER='$owner'" $HESTIA/data/ips/*) -owner_ips=$(echo "$owner_ips" | grep "STATUS='shared'") -owner_ips=$(echo "$owner_ips" | cut -f 7 -d / | cut -f 1 -d -) -user_ips=$(grep -H "OWNER='$user'" $HESTIA/data/ips/*) -user_ips=$(echo "$user_ips" | cut -f 7 -d / | cut -f 1 -d :) +owner_ips="$(grep -A 1 -H "OWNER='$owner'" $HESTIA/data/ips/* | grep "STATUS='shared'" | sed 's/.*\/\([^\/*]\)/\1/;s/-STATUS.*//')" +user_ips="$(grep -l "OWNER='$user'" $HESTIA/data/ips/* | sed 's/.*\/\([^\/*]\)/\1/')" ips=$(echo -e "$user_ips\n$owner_ips" | sort -u | sed "/^$/d") fields='$IP $OWNER $STATUS $NAME $NAT' From 10f6fd1bd286110138b4a45821094b5979df09bb Mon Sep 17 00:00:00 2001 From: root Date: Tue, 21 Feb 2023 02:11:21 +0000 Subject: [PATCH 022/223] IPV6: Edit IPV6 User Web Interface --- web/edit/web/index.php | 80 +++++++++++++++++++++++++++++++- web/templates/pages/edit_web.php | 24 ++++++++-- 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/web/edit/web/index.php b/web/edit/web/index.php index a2d98577f8..104405ff5e 100644 --- a/web/edit/web/index.php +++ b/web/edit/web/index.php @@ -39,6 +39,7 @@ // Parse domain $v_ip = $data[$v_domain]["IP"]; +$v_ipv6 = $data[$v_domain]["IP6"]; $v_template = $data[$v_domain]["TPL"]; $v_aliases = str_replace(",", "\n", $data[$v_domain]["ALIAS"]); $valiases = explode(",", $data[$v_domain]["ALIAS"]); @@ -193,6 +194,7 @@ // Change web domain IP $v_newip = ""; + $v_newipv6 = ""; $v_newip_public = ""; if (!empty($_POST["v_ip"])) { @@ -200,6 +202,10 @@ $v_newip_public = empty($ips[$v_newip]["NAT"]) ? $v_newip : $ips[$v_newip]["NAT"]; } + if (!empty($_POST["v_ipv6"])) { + $v_newipv6 = $_POST["v_ipv6"]; + } + if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { exec( HESTIA_CMD . @@ -219,6 +225,25 @@ unset($output); } + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { + exec( + HESTIA_CMD . + "v-change-web-domain-ipv6 " . + $user . + " " . + quoteshellarg($v_domain) . + " " . + quoteshellarg($_POST["v_ipv6"]) . + " 'no'", + $output, + $return_var, + ); + check_return_code($return_var, $output); + $restart_web = "yes"; + $restart_proxy = "yes"; + unset($output); + } + // Change dns domain IP if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { exec( @@ -246,6 +271,32 @@ } } + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { + exec( + HESTIA_CMD . "v-list-dns-domain " . $user . " " . quoteshellarg($v_domain) . " json", + $output, + $return_var, + ); + unset($output); + if ($return_var == 0) { + exec( + HESTIA_CMD . + "v-change-dns-domain-ipv6 " . + $user . + " " . + quoteshellarg($v_domain) . + " " . + quoteshellarg($v_newipv6) . + " 'no'", + $output, + $return_var, + ); + check_return_code($return_var, $output); + unset($output); + $restart_dns = "yes"; + } + } + // Change dns ip for each alias if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { foreach ($valiases as $v_alias) { @@ -274,8 +325,35 @@ } } + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { + foreach ($valiases as $v_alias) { + exec( + HESTIA_CMD . "v-list-dns-domain " . $user . " " . quoteshellarg($v_alias) . " json", + $output, + $return_var, + ); + unset($output); + if ($return_var == 0) { + exec( + HESTIA_CMD . + "v-change-dns-domain-ipv6 " . + $user . + " " . + quoteshellarg($v_alias) . + " " . + quoteshellarg($v_newipv6), + $output, + $return_var, + ); + check_return_code($return_var, $output); + unset($output); + $restart_dns = "yes"; + } + } + } + // Change mail domain IP - if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { + if ((($v_ip != $_POST["v_ip"]) || ($v_ipv6 != $_POST["v_ipv6"])) && empty($_SESSION["error_msg"])) { exec( HESTIA_CMD . "v-list-mail-domain " . $user . " " . quoteshellarg($v_domain) . " json", $output, diff --git a/web/templates/pages/edit_web.php b/web/templates/pages/edit_web.php index 0151197b13..32f09d8269 100644 --- a/web/templates/pages/edit_web.php +++ b/web/templates/pages/edit_web.php @@ -66,13 +66,29 @@ class=""
- + +
+
+ + From a035412c8ea7e553687593d426f5c3463824b594 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 21 Feb 2023 22:43:21 +0000 Subject: [PATCH 023/223] IPV6: Address brakes for apache and nginx IP configs --- bin/v-add-sys-ipv6 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/v-add-sys-ipv6 b/bin/v-add-sys-ipv6 index 7ec2dd840a..d78548aaa0 100755 --- a/bin/v-add-sys-ipv6 +++ b/bin/v-add-sys-ipv6 @@ -133,19 +133,20 @@ if [ ! -z "$WEB_SYSTEM" ]; then fi echo "Listen [$ipv6]:$WEB_PORT" >> $web_conf cat $HESTIA_INSTALL_DIR/apache2/unassigned.conf >> $web_conf + sed -i 's/\(VirtualHost \)directIP/\1['$ipv6']/g' $web_conf sed -i 's/directIP/'$ipv6'/g' $web_conf sed -i 's/directPORT/'$WEB_PORT'/g' $web_conf elif [ "$WEB_SYSTEM" = 'nginx' ]; then cp -f $HESTIA_INSTALL_DIR/nginx/unassigned.inc $web_conf - sed -i 's/directIP/'$ipv6'/g' $web_conf + sed -i 's/directIP/['$ipv6']/g' $web_conf fi if [ "$WEB_SSL" = 'mod_ssl' ]; then if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then - sed -i "1s/^/NameVirtualHost $ipv6:$WEB_SSL_PORT\n/" $web_conf + sed -i "1s/^/NameVirtualHost [$ipv6]:$WEB_SSL_PORT\n/" $web_conf fi - sed -i "1s/^/Listen $ipv6:$WEB_SSL_PORT\n/" $web_conf + sed -i "1s/^/Listen [$ipv6]:$WEB_SSL_PORT\n/" $web_conf sed -i 's/directSSLPORT/'$WEB_SSL_PORT'/g' $web_conf fi fi From 4a9a3b44a08638b2147e60e1dfa3c2de53d243dd Mon Sep 17 00:00:00 2001 From: root Date: Wed, 22 Feb 2023 22:46:55 +0000 Subject: [PATCH 024/223] IPV6: Resolving of timing issues in v-add-sys-ip/ipv6 v-add-sys-ip: New VERSION='4' variable --- bin/v-add-sys-ip | 4 +++- bin/v-add-sys-ipv6 | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/v-add-sys-ip b/bin/v-add-sys-ip index 86e9cfb6c5..834320be4f 100755 --- a/bin/v-add-sys-ip +++ b/bin/v-add-sys-ip @@ -73,6 +73,7 @@ if [ -z "$sys_ip_check" ]; then # Adding sys ip /sbin/ip addr add $ip/$cidr dev $iface \ broadcast $broadcast label $iface + sleep 2 # wait to avoid issues with apache and nginx port binding # Check if netplan is in use and generate configuration file if [ ! -z $(which netplan) ]; then @@ -126,7 +127,8 @@ INTERFACE='$iface' NETMASK='$netmask' NAT='$nat_ip' TIME='$time' -DATE='$date'" > $HESTIA/data/ips/$ip +DATE='$date' +VERSION='4'" > $HESTIA/data/ips/$ip chmod 660 $HESTIA/data/ips/$ip # WEB support diff --git a/bin/v-add-sys-ipv6 b/bin/v-add-sys-ipv6 index d78548aaa0..18931ec202 100755 --- a/bin/v-add-sys-ipv6 +++ b/bin/v-add-sys-ipv6 @@ -65,7 +65,7 @@ sys_ip_check=$(/sbin/ip -6 addr | grep "$ipv6") if [ -z "$sys_ip_check" ]; then # Adding sys ip /sbin/ip addr add $ipv6$prefix_length dev ${iface%:*} label ${iface} - + sleep 2 # wait to avoid issues with apache and nginx port binding # Check if netplan is in use and generate configuration file if [ ! -z $(which netplan) ]; then From b1a47e98b5c53b24efdd3af7fa98d64f4f45c4d1 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 23 Feb 2023 00:28:33 +0000 Subject: [PATCH 025/223] IPV6: Change web domain bash script bin/v-change-web-domain-ipv6 Rewriting of wrong version --- bin/v-change-web-domain-ipv6 | 131 +++++++++++++++-------------------- 1 file changed, 57 insertions(+), 74 deletions(-) diff --git a/bin/v-change-web-domain-ipv6 b/bin/v-change-web-domain-ipv6 index 13e8092c15..94200136d8 100755 --- a/bin/v-change-web-domain-ipv6 +++ b/bin/v-change-web-domain-ipv6 @@ -1,12 +1,13 @@ #!/bin/bash -# info: change dns domain ip address -# options: USER DOMAIN IPV6 +# info: change web domain ipv6 +# options: USER DOMAIN DOMAIN [RESTART] # -# The function for changing the main ipv6 of DNS zone. - +# example: v-change-web-domain-ipv6 admin example.com 1111:2222:3333::1 yes +# +# This function is used for changing domain ipv6 #----------------------------------------------------------# -# Variable&Function # +# Variables & Functions # #----------------------------------------------------------# # Argument definition @@ -17,104 +18,86 @@ ipv6=$3 restart=$4 # 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 -source $HESTIA/func/ipv6.sh +# shellcheck source=/usr/local/hestia/func/domain.sh source $HESTIA/func/domain.sh -source $HESTIA/conf/hestia.conf +# shellcheck source=/usr/local/hestia/func/ip.sh +source $HESTIA/func/ipv6.sh +# load config file +source_conf "$HESTIA/conf/hestia.conf" # Additional argument formatting format_domain format_domain_idn +# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ? #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# -check_args '3' "$#" 'USER DOMAIN IPV6' -is_format_valid 'user' 'domain' -is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' +check_args '3' "$#" 'USER DOMAIN IPV6 [RESTART]' +is_format_valid 'user' 'domain' 'ipv6' +is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" -is_object_valid 'dns' 'DOMAIN' "$domain" -is_object_unsuspended 'dns' 'DOMAIN' "$domain" +is_object_valid 'web' 'DOMAIN' "$domain" +is_object_unsuspended 'web' 'DOMAIN' "$domain" +is_ipv6_valid "$ipv6" "$user" -if [ "$ipv6" != "no" ]; then - is_format_valid 'ipv6' -fi -if [ "$ipv6" != "no" ]; then - is_ipv6_valid "$ipv6" "$user" -else - ipv6='' -fi - -# Get old ip -get_domain_values 'dns' -if [ -z @"$ipv6" ] && [ -z "$IP" ]; then - check_result $E_INVALID "IP or IPv6 is required" -fi +# Perform verification if read-only mode is enabled +check_hestia_demo_mode #----------------------------------------------------------# # Action # #----------------------------------------------------------# -old=$IP6 - -if [ -z "$old" ]; then - #Create new - # Generating timestamp - time_n_date=$(date +'%T %F') - time=$(echo "$time_n_date" |cut -f 1 -d \ ) - date=$(echo "$time_n_date" |cut -f 2 -d \ ) - ip="" - add_dns_config_records -else - if [ ! -z "$ipv6" ]; then - # Changing records - sed -i "s/$old/$ipv6/g" $USER_DATA/dns/$domain.conf - else - #Delete configs - ipv6="" - ip=$IP - remove_dns_config_records - fi -fi - -# Changing ip -update_object_value 'dns' 'DOMAIN' "$domain" '$IP6' "$ipv6" +# Preparing variables for vhost replace +get_domain_values 'web' +old=$(get_real_ip "$IP6") +new=$(get_real_ip "$ipv6") -#update counters -records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f1 -d ' ')" -update_object_value 'dns' 'DOMAIN' "$domain" '$RECORDS' "$records" -records=$(wc -l $USER_DATA/dns/*.conf | cut -f 1 -d ' ') -update_user_value "$user" '$U_DNS_RECORDS' "$records" - -# Updating zone -if [[ "$DNS_SYSTEM" =~ named|bind ]]; then - update_domain_serial - update_domain_zone +# Replacing vhost +replace_web_config "$WEB_SYSTEM" "$TPL.tpl" +if [ "$SSL" = 'yes' ]; then + replace_web_config "$WEB_SYSTEM" "$TPL.stpl" fi -# Updating dns-cluster queue -if [ ! -z "$DNS_CLUSTER" ]; then - # Check for first sync - dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe) - if [ -z "$dlock" ]; then - cmd="$BIN/v-add-remote-dns-domain $user $domain domain yes" - echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe - fi +# Replacing proxy vhost +if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then + replace_web_config "$PROXY_SYSTEM" "$PROXY.tpl" + if [ "$SSL" = 'yes' ]; then + replace_web_config "$PROXY_SYSTEM" "$PROXY.stpl" + fi fi +# Check for webmail +if [ -n "$IMAP_SYSTEM" ]; then + $BIN/v-rebuild-mail-domain "$user" "$domain" +fi #----------------------------------------------------------# -# HESTIA # +# Hestia # #----------------------------------------------------------# -# Restarting named -$BIN/v-restart-dns $restart -check_result $? "DNS restart failed" >/dev/null +# Update config +update_object_value 'web' 'DOMAIN' "$domain" '$IP6' "$3" + +# Update counters +increase_ipv6_value "$new" +decrease_ipv6_value "$old" + +# Restart web server +$BIN/v-restart-web "$restart" +check_result $? "WEB restart failed" > /dev/null + +$BIN/v-restart-proxy "$restart" +check_result $? "Proxy restart failed" > /dev/null # Logging -log_history "changed dns ipv6 for $domain to $ipv6" +$BIN/v-log-action "$user" "Info" "Web" "Web domain IPV6 address changed (IPV6: $3, Domain: $domain)." log_event "$OK" "$ARGUMENTS" -exit \ No newline at end of file +exit From 26ba44063f9157fee340d2387a27b0473614295d Mon Sep 17 00:00:00 2001 From: root Date: Thu, 23 Feb 2023 22:04:36 +0000 Subject: [PATCH 026/223] IPV6: Edit DNS/Web Domain Adaptation of bash and PHP scripts --- bin/v-change-dns-domain-ipv6 | 64 ++++++++++++++++++-------------- web/edit/dns/index.php | 23 +++++++++++- web/templates/pages/edit_dns.php | 28 ++++++++++++-- web/templates/pages/edit_web.php | 2 + 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/bin/v-change-dns-domain-ipv6 b/bin/v-change-dns-domain-ipv6 index 13e8092c15..cfef5ebe86 100755 --- a/bin/v-change-dns-domain-ipv6 +++ b/bin/v-change-dns-domain-ipv6 @@ -1,12 +1,13 @@ #!/bin/bash # info: change dns domain ip address -# options: USER DOMAIN IPV6 +# options: USER DOMAIN IPV6 [RESTART] # -# The function for changing the main ipv6 of DNS zone. - +# example: v-change-dns-domain-ipv6 admin domain.com 1111:2222::1 +# +# This function for changing the main ipv6 of DNS zone. #----------------------------------------------------------# -# Variable&Function # +# Variables & Functions # #----------------------------------------------------------# # Argument definition @@ -17,27 +18,41 @@ ipv6=$3 restart=$4 # 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 -source $HESTIA/func/ipv6.sh +# shellcheck source=/usr/local/hestia/func/domain.sh source $HESTIA/func/domain.sh -source $HESTIA/conf/hestia.conf +# shellcheck source=/usr/local/hestia/func/rebuild.sh +source $HESTIA/func/rebuild.sh +# load config file +source_conf "$HESTIA/conf/hestia.conf" # Additional argument formatting format_domain format_domain_idn +# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ? #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# -check_args '3' "$#" 'USER DOMAIN IPV6' -is_format_valid 'user' 'domain' +check_args '3' "$#" 'USER DOMAIN IPV6 [RESTART]' +is_format_valid 'user' 'domain' 'ipv6' is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" is_object_valid 'dns' 'DOMAIN' "$domain" is_object_unsuspended 'dns' 'DOMAIN' "$domain" +# Perform verification if read-only mode is enabled +check_hestia_demo_mode + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + if [ "$ipv6" != "no" ]; then is_format_valid 'ipv6' fi @@ -52,11 +67,6 @@ get_domain_values 'dns' if [ -z @"$ipv6" ] && [ -z "$IP" ]; then check_result $E_INVALID "IP or IPv6 is required" fi - -#----------------------------------------------------------# -# Action # -#----------------------------------------------------------# - old=$IP6 if [ -z "$old" ]; then @@ -90,31 +100,29 @@ update_user_value "$user" '$U_DNS_RECORDS' "$records" # Updating zone if [[ "$DNS_SYSTEM" =~ named|bind ]]; then - update_domain_serial - update_domain_zone + rebuild_dns_domain_conf fi # Updating dns-cluster queue -if [ ! -z "$DNS_CLUSTER" ]; then - # Check for first sync - dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe) - if [ -z "$dlock" ]; then - cmd="$BIN/v-add-remote-dns-domain $user $domain domain yes" - echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe - fi +if [ "$DNS_CLUSTER" = "yes" ]; then + # Check for first sync + dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe) + if [ -z "$dlock" ]; then + cmd="$BIN/v-add-remote-dns-domain $user $domain domain yes" + echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe + fi fi - #----------------------------------------------------------# -# HESTIA # +# Hestia # #----------------------------------------------------------# # Restarting named -$BIN/v-restart-dns $restart -check_result $? "DNS restart failed" >/dev/null +$BIN/v-restart-dns "$restart" +check_result $? "DNS restart failed" > /dev/null # Logging -log_history "changed dns ipv6 for $domain to $ipv6" +$BIN/v-log-action "$user" "Info" "DNS" "IPV6 address for DNS domain changed (IPV6: $ipv6, Domain: $domain)." log_event "$OK" "$ARGUMENTS" -exit \ No newline at end of file +exit diff --git a/web/edit/dns/index.php b/web/edit/dns/index.php index 38afe6eb98..7bc4e6f4f4 100644 --- a/web/edit/dns/index.php +++ b/web/edit/dns/index.php @@ -40,6 +40,7 @@ $v_username = $user; $v_domain = $_GET["domain"]; $v_ip = $data[$v_domain]["IP"]; + $v_ipv6 = $data[$v_domain]["IP6"]; $v_template = $data[$v_domain]["TPL"]; $v_ttl = $data[$v_domain]["TTL"]; $v_dnssec = $data[$v_domain]["DNSSEC"]; @@ -98,7 +99,7 @@ // Check token verify_csrf($_POST); - // Change domain IP + // Change domain IPV4 if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { $v_ip = quoteshellarg($_POST["v_ip"]); exec( @@ -118,6 +119,26 @@ unset($output); } + // Change domain IPV6 + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { + $v_ipv6 = quoteshellarg($_POST["v_ipv6"]); + exec( + HESTIA_CMD . + "v-change-dns-domain-ipv6 " . + $user . + " " . + $v_domain . + " " . + $v_ipv6 . + " 'no'", + $output, + $return_var, + ); + check_return_code($return_var, $output); + $restart_dns = "yes"; + unset($output); + } + // Change domain template if ($v_template != $_POST["v_template"] && empty($_SESSION["error_msg"])) { $v_template = quoteshellarg($_POST["v_template"]); diff --git a/web/templates/pages/edit_dns.php b/web/templates/pages/edit_dns.php index e1f36cc104..4af22095c4 100644 --- a/web/templates/pages/edit_dns.php +++ b/web/templates/pages/edit_dns.php @@ -30,21 +30,41 @@ ">
- +
">
+
+ +
+ + "> +
+