Browse Source

Refactor and fixes for handling system IP/Interfaces (#3605)

* Refactor and fixes for handling system IP/Interfaces

* Minor updates
myrevery 2 years ago
parent
commit
55813004b9

+ 51 - 52
bin/v-add-sys-ip

@@ -1,31 +1,37 @@
 #!/bin/bash
-# info: add system ip address
+# info: add system IP address
 # options: IP NETMASK [INTERFACE] [USER] [IP_STATUS] [IP_NAME] [NAT_IP]
 #
-# example: v-add-sys-ip 216.239.32.21 255.255.255.0
+# example: v-add-sys-ip 203.0.113.1 255.255.255.0
 #
-# This function adds ip address into a system. It also creates rc scripts. You
-# can specify ip name which will be used as root domain for temporary aliases.
+# This function adds IP address into a system. It also creates rc scripts. You
+# can specify IP 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 ip will automatically receive alias $domain.a1.myhosting.com. Of course
-# you must have wildcard record *.a1.myhosting.com pointed to ip. This feature
+# this IP will automatically receive alias $domain.a1.myhosting.com. Of course
+# you must have wildcard record *.a1.myhosting.com pointed to IP. This feature
 # is very handy when customer wants to test domain before dns migration.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
+# Argument definition
+ip="${1// /}"
+netmask="$2"
+
 # Get interface name
-iface=$(/bin/ip token | awk -F 'dev ' '{print $2}')
+# First try to detect which interface the IP address resides on
+iface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')"
+# If that fails, detect the default interface as a fallback
+if [ -z "$iface" ]; then
+	iface="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')"
+fi
 
-# Argument definition
-ip=${1// /}
-netmask=$2
 iface="${3-$iface}"
 user="${4-admin}"
 ip_status="${5-shared}"
-ip_name=$6
-nat_ip=$7
+ip_name="$6"
+nat_ip="$7"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -65,31 +71,26 @@ check_hestia_demo_mode
 #                       Action                             #
 #----------------------------------------------------------#
 
-cidr=$(convert_netmask $netmask)
-broadcast=$(get_broadcast $ip $netmask)
+cidr="$(convert_netmask "$netmask")"
+broadcast="$(get_broadcast "$ip" "$netmask")"
 
-sys_ip_check=$(/sbin/ip addr | grep "$ip")
+sys_ip_check="$(ip addr | grep -w "$ip")"
 if [ -z "$sys_ip_check" ]; then
-	# Adding sys ip
-	/sbin/ip addr add $ip/$cidr dev $iface \
-		broadcast $broadcast label $iface
+	# Adding system IP
+	ip addr add "$ip/$cidr" dev "$iface" broadcast "$broadcast" 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
+	if [ -n "$(netplan generate --mapping "$iface" 2> /dev/null | grep networkd)" ]; then
+		netplan="true"
 	else
-		netplan=0
+		netplan="false"
 	fi
 
-	if [ "$netplan" == "1" ]; then
+	if [ "$netplan" = "true" ]; then
 		if [ -f "/etc/netplan/60-hestia.yaml" ]; then
 			sys_ip="        - $ip/$cidr"
 		else
-			sys_ip="# Added by hestia, please do not edit the file manually!"
+			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"
@@ -99,7 +100,7 @@ if [ -z "$sys_ip_check" ]; then
 			sys_ip="$sys_ip\n        - $ip/$cidr"
 		fi
 		IFS='%'
-		echo -e $sys_ip >> /etc/netplan/60-hestia.yaml
+		echo -e "$sys_ip" >> /etc/netplan/60-hestia.yaml
 		unset IFS
 	else
 		sys_ip="\n# Added by Hestia Control Panel"
@@ -112,11 +113,9 @@ if [ -z "$sys_ip_check" ]; then
 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 \ )
+new_timestamp
 
-# Adding hestia ip
+# Adding Hestia IP
 echo "OWNER='$user'
 STATUS='$ip_status'
 NAME='$ip_name'
@@ -130,30 +129,30 @@ DATE='$date'" > $HESTIA/data/ips/$ip
 chmod 660 $HESTIA/data/ips/$ip
 
 # WEB support
-if [ ! -z "$WEB_SYSTEM" ]; then
+if [ -n "$WEB_SYSTEM" ]; then
 	web_conf="/etc/$WEB_SYSTEM/conf.d/$ip.conf"
-	rm -f $web_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 $ip:$WEB_PORT" > $web_conf
+			echo "NameVirtualHost $ip:$WEB_PORT" > "$web_conf"
 		fi
-		echo "Listen $ip:$WEB_PORT" >> $web_conf
-		cat $HESTIA_INSTALL_DIR/apache2/unassigned.conf >> $web_conf
-		sed -i 's/directIP/'$ip'/g' $web_conf
-		sed -i 's/directPORT/'$WEB_PORT'/g' $web_conf
+		echo "Listen $ip:$WEB_PORT" >> "$web_conf"
+		cat $HESTIA_INSTALL_DIR/apache2/unassigned.conf >> "$web_conf"
+		sed -i 's/directIP/'$ip'/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/'$ip'/g' $web_conf
+		cp -f $HESTIA_INSTALL_DIR/nginx/unassigned.inc "$web_conf"
+		sed -i 's/directIP/'$ip'/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 $ip:$WEB_SSL_PORT\n/" $web_conf
+			sed -i "1s/^/NameVirtualHost $ip:$WEB_SSL_PORT\n/" "$web_conf"
 		fi
-		sed -i "1s/^/Listen $ip:$WEB_SSL_PORT\n/" $web_conf
-		sed -i 's/directSSLPORT/'$WEB_SSL_PORT'/g' $web_conf
+		sed -i "1s/^/Listen $ip:$WEB_SSL_PORT\n/" "$web_conf"
+		sed -i 's/directSSLPORT/'$WEB_SSL_PORT'/g' "$web_conf"
 	fi
 fi
 
@@ -169,24 +168,24 @@ if [ -n "$PROXY_SYSTEM" ]; then
 	# 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 $ip/g" $fw_conf
+		ips=$(grep 'MEFaccept ' "$fw_conf" | grep -v '#' | head -n1)
+		sed -i "s/$ips/$ips $ip/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|</IfModule>|RPAFproxy_ips\n</IfModule>|' $rpaf_conf && rpaf_str='RPAFproxy_ips'
+		rpaf_str="$(grep RPAFproxy_ips "$rpaf_conf")"
+		[ -z "$rpaf_str" ] && sed -i 's|</IfModule>|RPAFproxy_ips\n</IfModule>|' "$rpaf_conf" && rpaf_str='RPAFproxy_ips'
 		rpaf_str="$rpaf_str $ip"
-		sed -i "s/.*RPAFproxy_ips.*/$rpaf_str/" $rpaf_conf
+		sed -i "s/.*RPAFproxy_ips.*/$rpaf_str/" "$rpaf_conf"
 	fi
 
-	#mod_remoteip
+	# mod_remoteip
 	remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf"
 	if [ -e "$remoteip_conf" ]; then
-		if [ $(grep -ic "$ip" $remoteip_conf) -eq 0 ]; then
-			sed -i "s/<\/IfModule>/RemoteIPInternalProxy $ip\n<\/IfModule>/g" $remoteip_conf
+		if [ "$(grep -ic "$ip" "$remoteip_conf")" -eq "0" ]; then
+			sed -i "s/<\/IfModule>/RemoteIPInternalProxy $ip\n<\/IfModule>/g" "$remoteip_conf"
 		fi
 	fi
 fi

+ 6 - 6
bin/v-change-sys-ip-name

@@ -1,18 +1,18 @@
 #!/bin/bash
-# info: change ip name
+# info: change IP name
 # options: IP NAME
 #
-# example: v-change-sys-ip-name 80.122.52.70 acme.com
+# example: v-change-sys-ip-name 203.0.113.1 acme.com
 #
-# This function for changing dns domain associated with ip.
+# This function for changing dns domain associated with IP.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
-ip_name=$2
+ip="$1"
+ip_name="$2"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -40,7 +40,7 @@ check_hestia_demo_mode
 #                       Action                             #
 #----------------------------------------------------------#
 
-# Changing ip name
+# Changing IP name
 update_ip_value '$NAME' "$ip_name"
 
 #----------------------------------------------------------#

+ 23 - 23
bin/v-change-sys-ip-nat

@@ -1,19 +1,19 @@
 #!/bin/bash
-# info: change ip nat address
+# info: change NAT IP address
 # options: IP NAT_IP [RESTART]
 #
-# example: v-change-sys-ip-nat 185.209.50.140 10.110.104.205
+# example: v-change-sys-ip-nat 10.0.0.1 203.0.113.1
 #
-# This function for changing nat ip associated with ip.
+# This function for changing NAT IP associated with IP.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
-nat_ip=$2
-restart=$3
+ip="$1"
+nat_ip="$2"
+restart="$3"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -45,13 +45,13 @@ check_hestia_demo_mode
 if [ -z "$(grep NAT= $HESTIA/data/ips/$ip)" ]; then
 	sed -i "s/^TIME/NAT='$nat_ip'\nTIME/g" $HESTIA/data/ips/$ip
 	old=''
-	new=$nat_ip
+	new="$nat_ip"
 else
-	old=$(get_ip_value '$NAT')
-	new=$nat_ip
+	old="$(get_ip_value '$NAT')"
+	new="$nat_ip"
 	sed -i "s/NAT=.*/NAT='$new'/" $HESTIA/data/ips/$ip
 	if [ -z "$nat_ip" ]; then
-		new=$ip
+		new="$ip"
 	fi
 fi
 
@@ -59,7 +59,7 @@ fi
 if [ -n "$old" ] && [ -n "$WEB_SYSTEM" ]; then
 	for user in $($BIN/v-list-sys-users plain); do
 		sed -i "s/$old/$new/" $HESTIA/data/users/$user/web.conf
-		$BIN/v-rebuild-web-domains $user no
+		$BIN/v-rebuild-web-domains "$user" no
 	done
 	$BIN/v-restart-dns "$restart"
 fi
@@ -67,37 +67,37 @@ fi
 # Updating DNS configs
 if [ -n "$old" ] && [ -n "$DNS_SYSTEM" ]; then
 	for user in $($BIN/v-list-sys-users plain); do
-		sed -i "s/$old/$new/" $HESTIA/data/users/$user/dns.conf
-		if ls $HESTIA/data/users/$user/dns/*.conf 1> /dev/null 2>&1; then
+		sed -i "s/$old/$new/" "$HESTIA/data/users/$user/dns.conf"
+		if ls $HESTIA/data/users/$user/dns/*.conf > /dev/null 2>&1; then
 			sed -i "s/$old/$new/" $HESTIA/data/users/$user/dns/*.conf
 		fi
-		$BIN/v-rebuild-dns-domains $user no
+		$BIN/v-rebuild-dns-domains "$user" no
 	done
 	$BIN/v-restart-dns "$restart"
 fi
 
 # Updating FTP
 if [ -n "$old" ] && [ -n "$FTP_SYSTEM" ]; then
-	conf=$(find /etc -name $FTP_SYSTEM.conf)
-	if [ -e "$conf" ]; then
-		sed -i "s/$old/$new/g" $conf
+	ftp_conf="$(find /etc -maxdepth 2 -name "$FTP_SYSTEM.conf")"
+	if [ -e "$ftp_conf" ]; then
+		sed -i "s/$old/$new/g" "$ftp_conf"
 		if [ "$FTP_SYSTEM" = 'vsftpd' ]; then
-			check_pasv=$(grep pasv_address $conf)
+			check_pasv="$(grep pasv_address "$ftp_conf")"
 			if [ -z "$check_pasv" ] && [ -n "$nat_ip" ]; then
-				echo "pasv_address=$nat_ip" >> $conf
+				echo "pasv_address=$nat_ip" >> "$ftp_conf"
 			fi
 			if [ -n "$check_pasv" ] && [ -z "$nat_ip" ]; then
-				sed -i "/pasv_address/d" $conf
+				sed -i "/pasv_address/d" "$ftp_conf"
 			fi
 			if [ -n "$check_pasv" ] && [ -n "$nat_ip" ]; then
-				sed -i "s/pasv_address=.*/pasv_address='$nat_ip'/g" $conf
+				sed -i "s/pasv_address=.*/pasv_address='$nat_ip'/g" "$ftp_conf"
 			fi
 		fi
 	fi
 	if [ "$FTP_SYSTEM" = 'proftpd' ]; then
-        	conf="/etc/$FTP_SYSTEM/conf.d/external_ip.conf"
+        	ext_ip_conf="/etc/$FTP_SYSTEM/conf.d/external_ip.conf"
         	content="MasqueradeAddress ${nat_ip}"
-        	echo "$content" > $conf
+        	echo "$content" > "$ext_ip_conf"
     	fi
 	$BIN/v-restart-ftp "$restart"
 fi

+ 7 - 7
bin/v-change-sys-ip-owner

@@ -1,18 +1,18 @@
 #!/bin/bash
-# info: change ip owner
+# info: change IP owner
 # options: IP USER
 #
-# example: v-change-sys-ip-owner 91.198.136.14 admin
+# example: v-change-sys-ip-owner 203.0.113.1 admin
 #
-# This function of changing ip address ownership.
+# This function of changing IP address ownership.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
-user=$2
+ip="$1"
+user="$2"
 
 # shellcheck source=/etc/hestiacp/hestia.conf
 source /etc/hestiacp/hestia.conf
@@ -44,7 +44,7 @@ check_hestia_demo_mode
 #                       Action                             #
 #----------------------------------------------------------#
 
-# Changing ip owner
+# Changing IP owner
 ip_owner=$(get_ip_value '$OWNER')
 if [ "$ip_owner" != "$user" ]; then
 	update_ip_value '$OWNER' "$user"
@@ -79,7 +79,7 @@ fi
 
 # Set status to dedicated if owner is not admin
 ip_status="$(get_ip_value '$STATUS')"
-if [ "$user" != 'admin' ] && [ "$ip_status" == 'shared' ]; then
+if [ "$user" != 'admin' ] && [ "$ip_status" = 'shared' ]; then
 	$BIN/v-change-sys-ip-status "$ip" 'dedicated'
 fi
 

+ 8 - 8
bin/v-change-sys-ip-status

@@ -1,18 +1,18 @@
 #!/bin/bash
-# info: change ip status
+# info: change IP status
 # options: IP IP_STATUS
 #
-# example: v-change-sys-ip-status 91.198.136.14 yourstatus
+# example: v-change-sys-ip-status 203.0.113.1 yourstatus
 #
-# This function of changing an ip address's status.
+# This function of changing an IP address's status.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
-ip_status=$2
+ip="$1"
+ip_status="$2"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -38,9 +38,9 @@ web_domains=$(get_ip_value '$U_WEB_DOMAINS')
 sys_user=$(get_ip_value '$U_SYS_USERS')
 ip_owner=$(get_ip_value '$OWNER')
 if [ "$web_domains" -ne '0' ] && [ "$sys_user" != "$ip_owner" ]; then
-	check_result "$E_INUSE" "ip $ip is used"
+	check_result "$E_INUSE" "IP $ip is used"
 fi
-if [ "$ip_owner" != "admin" ] && [ "$ip_status" == "shared" ]; then
+if [ "$ip_owner" != "admin" ] && [ "$ip_status" = "shared" ]; then
 	$BIN/v-change-sys-ip-owner "$ip" "admin"
 fi
 
@@ -51,7 +51,7 @@ check_hestia_demo_mode
 #                       Action                             #
 #----------------------------------------------------------#
 
-# Changing ip name
+# Changing IP status
 update_ip_value '$STATUS' "$ip_status"
 
 #----------------------------------------------------------#

+ 33 - 29
bin/v-delete-sys-ip

@@ -1,18 +1,18 @@
 #!/bin/bash
-# info: delete system ip
+# info: delete system IP
 # options: IP
 #
-# example: v-delete-sys-ip 212.42.76.210
+# example: v-delete-sys-ip 203.0.113.1
 #
-# This 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.
+# This 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.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
+ip="$1"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -43,30 +43,34 @@ check_hestia_demo_mode
 #                       Action                             #
 #----------------------------------------------------------#
 
-# Import ip variables
-source $HESTIA/data/ips/$ip
-cidr=$(convert_netmask "$NETMASK")
-main_ip=$(hostname -i)
+# Import IP variables
+source "$HESTIA/data/ips/$ip"
+cidr="$(convert_netmask "$NETMASK")"
 
-# Checking main ip on the interface
-interface=$(/sbin/ip addr | grep "$ip/$cidr" | awk '{print $NF}')
-if [ -n "$interface" ] && [ "$ip" = "$main_ip" ]; then
-	echo "Error: can't delete main IP address"
+# Get primary IP
+default_nic="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')"
+primary_ipv4="$(ip -4 -d -j addr show "$default_nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end' | head -n1)"
+
+# Checking primary IP on the interface
+interface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')"
+if [ -n "$interface" ] && [ "$ip" = "$primary_ipv4" ]; then
+	echo "Error: can't delete primary IP address"
 	log_event "$E_FORBIDEN" "$ARGUMENTS"
 	exit "$E_FORBIDEN"
 fi
 
-# Deleting system ip
+# Deleting system IP
 if [ -n "$interface" ]; then
-	/sbin/ip addr del "$ip/$cidr" dev "$INTERFACE"
-	if [ "$?" -ne 0 ]; then
-		echo "Error: can't delete system ip"
+	ip addr del "$ip/$cidr" dev "$interface" 2> /dev/null
+	if [ "$?" -ne "0" ]; then
+		echo "Error: can't delete system IP address"
 		log_event "$E_FORBIDEN" "$ARGUMENTS"
-		exit $E_FORBIDEN
+		exit "$E_FORBIDEN"
 	fi
 fi
 
 # Deleting startup conf on RHEL/CentOS/Fedora
+# Need RHEL experts to fix me
 if [ -e "/etc/sysconfig/network-scripts/ifcfg-$interface" ]; then
 	rm -f /etc/sysconfig/network-scripts/ifcfg-$interface
 fi
@@ -78,15 +82,15 @@ if [ -f "/etc/netplan/60-hestia.yaml" ]; then
 		rm /etc/netplan/60-hestia.yaml
 	fi
 elif [ -e "/etc/network/interfaces" ]; then
-	ip_str=$(grep -n $ip$ /etc/network/interfaces | cut -f1 -d:)
+	ip_str="$(grep -n "$ip$" /etc/network/interfaces | cut -f1 -d:)"
 	if [ -n "$ip_str" ]; then
-		first_str=$((ip_str - 3))
-		last_str=$((ip_str + 1))
+		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
+# Deleting Hestia IP
 rm -f $HESTIA/data/ips/$ip
 
 # Deleting web config
@@ -101,23 +105,23 @@ if [ -n "$PROXY_SYSTEM" ]; then
 	# 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/$ip//")
-		sed -i "s/$ips/$new_ips/g" $fw_conf
+		ips="$(grep 'MEFaccept 127.0.0.1' "$fw_conf")"
+		new_ips="$(echo "$ips" | sed "s/$ip//")"
+		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 "$ips" | sed "s/ $ip//")
+		ips="$(grep RPAFproxy_ips "$rpaf_conf")"
+		new_ips="$(echo "$ips" | sed "s/ $ip//")"
 		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
+		[ "$(grep RPAFproxy_ips "$rpaf_conf" | sed 's/^[[:space:]]*//g')" = "RPAFproxy_ips" ] && sed -i "/RPAFproxy_ips/d" "$rpaf_conf"
 	fi
 
-	#mod_remoteip
+	# mod_remoteip
 	remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf"
 	if [ -e "$remoteip_conf" ]; then
 		sed -i "/RemoteIPInternalProxy $ip\$/d" "$remoteip_conf"

+ 8 - 7
bin/v-list-sys-interfaces

@@ -11,7 +11,7 @@
 #----------------------------------------------------------#
 
 # Argument definition
-format=${1-shell}
+format="${1-shell}"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -23,10 +23,10 @@ source_conf "$HESTIA/conf/hestia.conf"
 
 # JSON list function
 json_list() {
-	objects=$(echo "$interfaces" | wc -l)
+	objects=$(echo "$physical_nics" | wc -l)
 	i=1
 	echo '['
-	for interface in $interfaces; do
+	for interface in $physical_nics; do
 		echo -n '    "'$interface'"'
 		if [ "$i" -lt "$objects" ]; then
 			echo ','
@@ -42,14 +42,14 @@ json_list() {
 shell_list() {
 	echo "INTERFACE"
 	echo "---------"
-	for interface in $interfaces; do
+	for interface in $physical_nics; do
 		echo "$interface"
 	done
 }
 
 # PLAIN list function
 plain_list() {
-	for interface in $interfaces; do
+	for interface in $physical_nics; do
 		echo "$interface"
 	done
 }
@@ -57,7 +57,7 @@ plain_list() {
 # CSV list function
 csv_list() {
 	echo "INTERFACE"
-	for interface in $interfaces; do
+	for interface in $physical_nics; do
 		echo "$interface"
 	done
 }
@@ -67,7 +67,8 @@ csv_list() {
 #----------------------------------------------------------#
 
 # Defining interface list
-interfaces=$(cat /proc/net/dev | grep : | cut -f 1 -d : | tr -d ' ' | grep -v lo)
+# Detect "physical" NICs only (virtual NICs created by Docker, WireGuard etc. are excluded)
+physical_nics="$(ip -d -j link show | jq -r '.[] | if .link_type == "loopback" // .linkinfo.info_kind then empty else .ifname end')"
 
 # Listing data
 case $format in

+ 7 - 7
bin/v-list-sys-ip

@@ -1,18 +1,18 @@
 #!/bin/bash
-# info: list system ip
+# info: list system IP
 # options: IP [FORMAT]
 #
-# example: v-list-sys-ip 116.203.78.202
+# example: v-list-sys-ip 203.0.113.1
 #
-# This function for getting the list of system ip parameters.
+# This function for getting the list of system IP parameters.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
-format=${2-shell}
+ip="$1"
+format="${2-shell}"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -78,14 +78,14 @@ csv_list() {
 check_args '1' "$#" 'IP [FORMAT]'
 is_format_valid 'ip'
 if [ ! -e "$HESTIA/data/ips/$ip" ]; then
-	check_result $E_NOTEXIST "ip $ip doesn't exist"
+	check_result $E_NOTEXIST "IP $ip doesn't exist"
 fi
 
 #----------------------------------------------------------#
 #                       Action                             #
 #----------------------------------------------------------#
 
-# Parsing ip
+# Parsing IP
 source_conf "$HESTIA/data/ips/$ip"
 
 # Listing data

+ 4 - 4
bin/v-list-sys-ips

@@ -1,17 +1,17 @@
 #!/bin/bash
-# info: list system ips
+# info: list system IPs
 # options: [FORMAT]
 #
 # example: v-list-sys-ips
 #
-# This function for obtaining the list of system ip addresses.
+# This function for obtaining the list of system IP addresses.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-format=${1-shell}
+format="${1-shell}"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -26,7 +26,7 @@ source_conf "$HESTIA/conf/hestia.conf"
 # JSON list function
 json_list() {
 	echo '{'
-	ip_count=$(ls $HESTIA/data/ips/ | wc -l)
+	ip_count="$(ls $HESTIA/data/ips/ | wc -l)"
 	i=1
 	while read IP; do
 		source_conf "$HESTIA/data/ips/$IP"

+ 11 - 11
bin/v-list-user-ips

@@ -1,18 +1,18 @@
 #!/bin/bash
-# info: list user ips
+# info: list user IPs
 # options: USER [FORMAT]
 #
 # example: v-list-user-ips admin
 #
-# This function for obtaining the list of available ip addresses.
+# This function for obtaining the list of available IP addresses.
 
 #----------------------------------------------------------#
 #                Variables & Functions                     #
 #----------------------------------------------------------#
 
 # Argument definition
-user=$1
-format=${2-shell}
+user="$1"
+format="${2-shell}"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -25,7 +25,7 @@ source_conf "$HESTIA/conf/hestia.conf"
 # JSON list function
 json_list() {
 	echo '{'
-	ip_count=$(echo "$ips" | wc -l)
+	ip_count="$(echo "$ips" | wc -l)"
 	i=1
 	for IP in $ips; do
 		source_conf "$HESTIA/data/ips/$IP"
@@ -92,12 +92,12 @@ 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 :)
-ips=$(echo -e "$user_ips\n$owner_ips" | sort -u | sed "/^$/d")
+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 :)"
+ips="$(echo -e "$user_ips\n$owner_ips" | sort -u | sed "/^$/d")"
 fields='$IP $OWNER $STATUS $NAME $NAT'
 
 # Listing data

+ 70 - 42
bin/v-update-sys-ip

@@ -1,12 +1,12 @@
 #!/bin/bash
-# info: update system ip
+# info: update system IP
 # options: NONE
 #
 # example: v-update-sys-ip
 #          # Intended for internal usage
 #
-# This function scans configured ip in the system and register them with hestia
-# internal database. This call is intended for use on vps servers, where ip is
+# This function scans configured IP in the system and register them with Hestia
+# internal database. This call is intended for use on vps servers, where IP is
 # set by hypervisor.
 
 #----------------------------------------------------------#
@@ -34,110 +34,138 @@ check_hestia_demo_mode
 #                       Action                             #
 #----------------------------------------------------------#
 
-# Listing system ip addresses
-ips=$(/sbin/ip addr | grep 'inet ' | grep global | awk '{print $2}' | cut -f1 -d/)
-v_ips=$(ls $HESTIA/data/ips/)
-ip_num=$(echo "$ips" | wc -l)
-v_ip_num=$(echo "$v_ips" | wc -l)
+# Listing system IP addresses
+# Detect "physical" NICs only (virtual NICs created by Docker, WireGuard etc. are excluded)
+physical_nics="$(ip -d -j link show | jq -r '.[] | if .link_type == "loopback" // .linkinfo.info_kind then empty else .ifname end')"
+
+for nic in $physical_nics; do
+	nic_ipv4s="$(ip -4 -d -j addr show "$nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end')"
+	if [ -z "$ips" ]; then
+		ips="$nic_ipv4s"
+	else
+		ips="$ips\n$nic_ipv4s"
+	fi
+done
+
+v_ips="$(ls $HESTIA/data/ips/)"
+ip_num="$(echo "$ips" | wc -l)"
+v_ip_num="$(echo "$v_ips" | wc -l)"
 
 # Checking primary IP change
-if [[ "$ip_num" -eq '1' ]] && [[ "$v_ip_num" -eq 1 ]]; then
-	if [ "$ips" != "$v_ips" ]; then
-		new=$ips
-		old=$v_ips
+if [ "$ip_num" -eq "1" ] && [ "$v_ip_num" -eq "1" ]; then
+	if [ -n "$v_ips" ] && [ "$ips" != "$v_ips" ]; then
+		new_ip="$ips"
+		old_ip="$v_ips"
 	fi
 fi
 
 # Updating configs
-if [ ! -z "$new" ]; then
-	mv $HESTIA/data/ips/$old $HESTIA/data/ips/$new
+if [ -n "$old_ip" ]; then
+	mv $HESTIA/data/ips/$old_ip $HESTIA/data/ips/$new_ip
+
+	# Generating timestamp
+	new_timestamp
+
+	# Updating IP's values
+	ip="$new_ip"
+	interface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')"
+	prefixlen="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[].addr_info[] | if .local == $IP then .prefixlen else empty end')"
+	netmask="$(convert_cidr "$prefixlen")"
+	update_ip_value '$INTERFACE' "$interface"
+	update_ip_value '$NETMASK' "$netmask"
+	update_ip_value '$TIME' "$time"
+	update_ip_value '$DATE' "$date"
 
 	# Updating PROXY
 	if [ -n "$PROXY_SYSTEM" ]; then
 		cd /etc/$PROXY_SYSTEM/conf.d
-		if [ -e "$old.conf" ]; then
-			mv $old.conf $new.conf
-			sed -i "s/$old/$new/g" $new.conf
+		if [ -e "$old_ip.conf" ]; then
+			mv $old_ip.conf $new_ip.conf
+			sed -i "s/$old_ip/$new_ip/g" $new_ip.conf
 		fi
 	fi
 
 	# Updating WEB
 	if [ -n "$WEB_SYSTEM" ]; then
 		cd /etc/$WEB_SYSTEM/conf.d
-		if [ -e "$old.conf" ]; then
-			mv $old.conf $new.conf
-			sed -i "s/$old/$new/g" $new.conf
+		if [ -e "$old_ip.conf" ]; then
+			mv $old_ip.conf $new_ip.conf
+			sed -i "s/$old_ip/$new_ip/g" $new_ip.conf
 		fi
 		for user in $($BIN/v-list-sys-users plain); do
-			sed -i "s/$old/$new/g" $HESTIA/data/users/$user/web.conf
+			sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/users/$user/web.conf
 			$BIN/v-rebuild-web-domains "$user" no
 		done
 
 		if [ -e "/etc/apache2/mods-available/remoteip.conf" ]; then
-			sed -i "s/$old/$new/g" /etc/apache2/mods-available/remoteip.conf
+			sed -i "s/$old_ip/$new_ip/g" /etc/apache2/mods-available/remoteip.conf
 		fi
 
 		if [ -e "/etc/apache2/mods-enabled/rpaf.conf" ]; then
-			sed -i "s/$old/$new/g" /etc/apache2/mods-enabled/rpaf.conf
+			sed -i "s/$old_ip/$new_ip/g" /etc/apache2/mods-enabled/rpaf.conf
 		fi
 
 		$BIN/v-restart-proxy
 		$BIN/v-restart-web
 	fi
 
+	# Updating MAIL
 	if [ -n "$IMAP_SYSTEM" ]; then
 		for user in $($BIN/v-list-sys-users plain); do
 			$BIN/v-rebuild-mail-domains "$user" no
 		done
+		$BIN/v-restart-mail
 	fi
 
 	# Updating DNS
-	if [ ! -z "$DNS_SYSTEM" ]; then
+	if [ -n "$DNS_SYSTEM" ]; then
 		for user in $($BIN/v-list-sys-users plain); do
-			sed -i "s/$old/$new/g" $HESTIA/data/users/$user/dns.conf
-			sed -i "s/$old/$new/g" $HESTIA/data/users/$user/dns/*.conf
+			sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/users/$user/dns.conf
+			sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/users/$user/dns/*.conf
 			$BIN/v-rebuild-dns-domains "$user" no
 		done
 		$BIN/v-restart-dns
 	fi
 
 	# Updating FTP
-	if [ ! -z "$FTP_SYSTEM" ] && [ "$FTP_SYSTEM" = 'vsftpd' ]; then
-		conf=$(find /etc/ -maxdepth 2 -name $FTP_SYSTEM.conf)
-		if [ ! -z "$conf" ]; then
-			sed -i "s/$old/$new/g" $conf
+	if [ -n "$FTP_SYSTEM" ] && [ "$FTP_SYSTEM" = 'vsftpd' ]; then
+		ftp_conf="$(find /etc/ -maxdepth 2 -name $FTP_SYSTEM.conf)"
+		if [ -n "$ftp_conf" ]; then
+			sed -i "s/$old_ip/$new_ip/g" "$ftp_conf"
 			$BIN/v-restart-ftp
 		fi
 	fi
 
 	# Updating firewall
-	if [ ! -z "$FIREWALL_SYSTEM" ]; then
-		sed -i "s/$old/$new/g" $HESTIA/data/firewall/*.conf
+	if [ -n "$FIREWALL_SYSTEM" ]; then
+		sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/firewall/*.conf
 		$BIN/v-update-firewall
 	fi
 fi
 
 # Adding system IP
 for ip in $ips; do
-	check_ip=$(/sbin/ip addr list | grep "$ip")
+	check_ip="$(ip addr list | grep -w "$ip")"
 	if [ ! -e "$HESTIA/data/ips/$ip" ] && [ -n "$check_ip" ]; then
-		interface=$(/sbin/ip addr | grep $ip | awk '{print $NF}' | uniq)
-		interface=$(echo "$interface" | cut -f 1 -d : | head -n 1)
-		netmask=$(/sbin/ip addr | grep $ip | cut -f 2 -d / | cut -f 1 -d \ )
-		netmask=$(convert_cidr $netmask)
+		interface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')"
+		prefixlen="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[].addr_info[] | if .local == $IP then .prefixlen else empty end')"
+		netmask="$(convert_cidr "$prefixlen")"
 		$BIN/v-add-sys-ip "$ip" "$netmask" "$interface"
 	fi
 done
 
 # Updating NAT
-pub_ip=$(curl --ipv4 -s https://ip.hestiacp.com/)
-if [ ! -e "$HESTIA/data/ips/$pub_ip" ]; then
-	if [ -z "$(grep -R "$pub_ip" $HESTIA/data/ips/)" ]; then
-		ip=$(ls -t $HESTIA/data/ips/ | head -n1)
-		$BIN/v-change-sys-ip-nat "$ip" "$pub_ip"
+pub_ipv4="$(curl -fsLm5 --retry 2 --ipv4 https://ip.hestiacp.com/)"
+if [ ! -e "$HESTIA/data/ips/$pub_ipv4" ]; then
+	if [ -z "$(grep -R "$pub_ipv4" $HESTIA/data/ips/)" ]; then
+		ip="$(ls -t $HESTIA/data/ips/ | head -n1)"
+		$BIN/v-change-sys-ip-nat "$ip" "$pub_ipv4"
 	fi
 fi
 
+# Updating IP usage counters
+$BIN/v-update-sys-ip-counters
+
 #----------------------------------------------------------#
 #                       Hestia                             #
 #----------------------------------------------------------#

+ 5 - 6
bin/v-update-sys-ip-counters

@@ -11,7 +11,7 @@
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
+ip="$1"
 
 # Includes
 # shellcheck source=/etc/hestiacp/hestia.conf
@@ -42,7 +42,7 @@ check_hestia_demo_mode
 
 # Creating user_list
 if [ -z "$ip" ]; then
-	ip_list=$(ls $HESTIA/data/ips)
+	ip_list="$(ls $HESTIA/data/ips/)"
 else
 	ip_list="$ip"
 fi
@@ -51,10 +51,9 @@ fi
 for ip in $ip_list; do
 
 	# Calculate usage
-	ip_usage=$(grep -H $ip $HESTIA/data/users/*/web.conf)
-	web_domains=$(echo "$ip_usage" | sed '/^$/d' | wc -l)
-	sys_users=$(echo "$ip_usage" | cut -f7 -d/ | sort -u \
-		| tr '\n' ',' | sed "s/,$//g")
+	ip_usage="$(grep -H "$ip" $HESTIA/data/users/*/web.conf)"
+	web_domains="$(echo "$ip_usage" | sed '/^$/d' | wc -l)"
+	sys_users="$(echo "$ip_usage" | cut -f7 -d/ | sort -u | tr '\n' ',' | sed "s/,$//g")"
 
 	# Update counters
 	update_ip_value '$U_WEB_DOMAINS' "$web_domains"

+ 4 - 3
func/main.sh

@@ -1025,15 +1025,16 @@ 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
+	# Detect "physical" NICs only (virtual NICs created by Docker, WireGuard etc. are excluded)
+	nic_names="$(ip -d -j link show | jq -r '.[] | if .link_type == "loopback" // .linkinfo.info_kind then empty else .ifname, if .altnames then .altnames[] else empty end end')"
+	if [ -z "$(echo "$nic_names" | 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
+	if [ -z "$(echo shared,dedicated | grep -w "$1")" ]; then
 		check_result "$E_INVALID" "invalid status format :: $1"
 	fi
 }

+ 17 - 12
install/hst-install-debian.sh

@@ -2009,9 +2009,14 @@ curl -s https://rclone.org/install.sh | bash > /dev/null 2>&1
 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/)
-local_ip=$ip
+# Get primary IP
+default_nic="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')"
+# IPv4
+primary_ipv4="$(ip -4 -d -j addr show "$default_nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end' | head -n1)"
+# IPv6
+#primary_ipv6="$(ip -6 -d -j addr show "$default_nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end' | head -n1)"
+ip="$primary_ipv4"
+local_ip="$primary_ipv4"
 
 # Configuring firewall
 if [ "$iptables" = 'yes' ]; then
@@ -2019,10 +2024,10 @@ if [ "$iptables" = 'yes' ]; then
 fi
 
 # Get public IP
-pub_ip=$(curl --ipv4 -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
+pub_ipv4="$(curl -fsLm5 --retry 2 --ipv4 https://ip.hestiacp.com/)"
+if [ -n "$pub_ipv4" ] && [ "$pub_ipv4" != "$ip" ]; then
+	$HESTIA/bin/v-change-sys-ip-nat "$ip" "$pub_ipv4" > /dev/null 2>&1
+	ip="$pub_ipv4"
 fi
 
 # Configuring libapache2-mod-remoteip
@@ -2030,14 +2035,14 @@ if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then
 	cd /etc/apache2/mods-available
 	echo "<IfModule mod_remoteip.c>" > remoteip.conf
 	echo "  RemoteIPHeader X-Real-IP" >> remoteip.conf
-	if [ "$local_ip" != "127.0.0.1" ] && [ "$pub_ip" != "127.0.0.1" ]; then
+	if [ "$local_ip" != "127.0.0.1" ] && [ "$pub_ipv4" != "127.0.0.1" ]; then
 		echo "  RemoteIPInternalProxy 127.0.0.1" >> remoteip.conf
 	fi
-	if [ -n "$local_ip" ] && [ "$local_ip" != "$pub_ip" ]; then
+	if [ -n "$local_ip" ] && [ "$local_ip" != "$pub_ipv4" ]; then
 		echo "  RemoteIPInternalProxy $local_ip" >> remoteip.conf
 	fi
-	if [ -n "$pub_ip" ]; then
-		echo "  RemoteIPInternalProxy $pub_ip" >> remoteip.conf
+	if [ -n "$pub_ipv4" ]; then
+		echo "  RemoteIPInternalProxy $pub_ipv4" >> remoteip.conf
 	fi
 	echo "</IfModule>" >> remoteip.conf
 	sed -i "s/LogFormat \"%h/LogFormat \"%a/g" /etc/apache2/apache2.conf
@@ -2046,7 +2051,7 @@ if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then
 fi
 
 # Adding default domain
-$HESTIA/bin/v-add-web-domain admin $servername $ip
+$HESTIA/bin/v-add-web-domain admin "$servername" "$ip"
 check_result $? "can't create $servername domain"
 
 # Adding cron jobs

+ 17 - 12
install/hst-install-ubuntu.sh

@@ -2033,9 +2033,14 @@ curl -s https://rclone.org/install.sh | bash > /dev/null 2>&1
 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/)
-local_ip=$ip
+# Get primary IP
+default_nic="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')"
+# IPv4
+primary_ipv4="$(ip -4 -d -j addr show "$default_nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end' | head -n1)"
+# IPv6
+#primary_ipv6="$(ip -6 -d -j addr show "$default_nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end' | head -n1)"
+ip="$primary_ipv4"
+local_ip="$primary_ipv4"
 
 # Configuring firewall
 if [ "$iptables" = 'yes' ]; then
@@ -2043,8 +2048,8 @@ if [ "$iptables" = 'yes' ]; then
 fi
 
 # Get public IP
-pub_ip=$(curl --ipv4 -s https://ip.hestiacp.com/)
-if [ -n "$pub_ip" ] && [ "$pub_ip" != "$ip" ]; then
+pub_ipv4="$(curl -fsLm5 --retry 2 --ipv4 https://ip.hestiacp.com/)"
+if [ -n "$pub_ipv4" ] && [ "$pub_ipv4" != "$ip" ]; then
 	if [ -e /etc/rc.local ]; then
 		sed -i '/exit 0/d' /etc/rc.local
 	else
@@ -2060,8 +2065,8 @@ if [ -n "$pub_ip" ] && [ "$pub_ip" != "$ip" ]; then
 	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
+	$HESTIA/bin/v-change-sys-ip-nat "$ip" "$pub_ipv4" > /dev/null 2>&1
+	ip="$pub_ipv4"
 fi
 
 # Configuring libapache2-mod-remoteip
@@ -2069,14 +2074,14 @@ if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then
 	cd /etc/apache2/mods-available
 	echo "<IfModule mod_remoteip.c>" > remoteip.conf
 	echo "  RemoteIPHeader X-Real-IP" >> remoteip.conf
-	if [ "$local_ip" != "127.0.0.1" ] && [ "$pub_ip" != "127.0.0.1" ]; then
+	if [ "$local_ip" != "127.0.0.1" ] && [ "$pub_ipv4" != "127.0.0.1" ]; then
 		echo "  RemoteIPInternalProxy 127.0.0.1" >> remoteip.conf
 	fi
-	if [ -n "$local_ip" ] && [ "$local_ip" != "$pub_ip" ]; then
+	if [ -n "$local_ip" ] && [ "$local_ip" != "$pub_ipv4" ]; then
 		echo "  RemoteIPInternalProxy $local_ip" >> remoteip.conf
 	fi
-	if [ -n "$pub_ip" ]; then
-		echo "  RemoteIPInternalProxy $pub_ip" >> remoteip.conf
+	if [ -n "$pub_ipv4" ]; then
+		echo "  RemoteIPInternalProxy $pub_ipv4" >> remoteip.conf
 	fi
 	echo "</IfModule>" >> remoteip.conf
 	sed -i "s/LogFormat \"%h/LogFormat \"%a/g" /etc/apache2/apache2.conf
@@ -2085,7 +2090,7 @@ if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then
 fi
 
 # Adding default domain
-$HESTIA/bin/v-add-web-domain admin $servername $ip
+$HESTIA/bin/v-add-web-domain admin "$servername" "$ip"
 check_result $? "can't create $servername domain"
 
 # Adding cron jobs

+ 18 - 8
src/deb/nginx/hestia

@@ -32,17 +32,27 @@ set -e
 . /etc/profile.d/hestia.sh
 
 adapt_nginx_config() {
-	ipv4_string="$(ip -4 addr show scope global | sed -ne '/^[ \t]*inet[6]*/p')"
-	ipv6_string="$(ip -6 addr show scope global | sed -ne '/^[ \t]*inet[6]*/p')"
-	if [ -n "$ipv4_string" ]; then
-		sed -i 's/#IPV4\([ \t]*listen[ \t]*[0-9]\{1,5\}.*\)/\1/' ${NGINX_CONF}
+	# Detect "physical" NICs only (virtual NICs created by Docker, WireGuard etc. are excluded)
+	physical_nics="$(ip -d -j link show | jq -r '.[] | if .link_type == "loopback" // .linkinfo.info_kind then empty else .ifname end')"
+
+	for nic in $physical_nics; do
+		if [ -z "$ipv4_scope_global" ]; then
+			ipv4_scope_global="$(ip -4 -d -j addr show "$nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end')"
+		fi
+		if [ -z "$ipv6_scope_global" ]; then
+			ipv6_scope_global="$(ip -6 -d -j addr show "$nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end')"
+		fi
+	done
+
+	if [ -n "$ipv4_scope_global" ]; then
+		sed -i 's/#IPV4\([ \t]*listen[ \t]*[0-9]\{1,5\}.*\)/\1/' "$NGINX_CONF"
 	else
-		sed -i 's/^\([ \t]*listen[ \t]*[0-9]\{1,5\}.*\)/#IPV4\1/' ${NGINX_CONF}
+		sed -i 's/^\([ \t]*listen[ \t]*[0-9]\{1,5\}.*\)/#IPV4\1/' "$NGINX_CONF"
 	fi
-	if [ -n "$ipv6_string" ]; then
-		sed -i 's/#IPV6\([ \t]*listen[ \t]*\[\:\:\]\:[0-9]\{1,5\}.*\)/\1/' ${NGINX_CONF}
+	if [ -n "$ipv6_scope_global" ]; then
+		sed -i 's/#IPV6\([ \t]*listen[ \t]*\[\:\:\]\:[0-9]\{1,5\}.*\)/\1/' "$NGINX_CONF"
 	else
-		sed -i 's/^\([ \t]*listen[ \t]*\[\:\:\]\:[0-9]\{1,5\}.*\)/#IPV6\1/' ${NGINX_CONF}
+		sed -i 's/^\([ \t]*listen[ \t]*\[\:\:\]\:[0-9]\{1,5\}.*\)/#IPV6\1/' "$NGINX_CONF"
 	fi
 }
 

+ 2 - 2
src/deb/nginx/nginx.conf

@@ -93,8 +93,8 @@ http {
 
 	# Vhost
 	server {
-#IPV4		listen				8083 ssl;
-#IPV6		listen				[::]:8083 ssl;
+#IPV4		listen              8083 ssl;
+#IPV6		listen              [::]:8083 ssl;
 		server_name         _;
 		root                /usr/local/hestia/web;
 		# Fix error "The plain HTTP request was sent to HTTPS port"