Sfoglia il codice sorgente

Merge branch 'improve-validation-of-ips' into 1.9.0-release

Jaap Marcus 1 anno fa
parent
commit
d34837432a
6 ha cambiato i file con 78 aggiunte e 141 eliminazioni
  1. 10 10
      bin/v-add-firewall-ban
  2. 7 7
      bin/v-add-firewall-rule
  3. 7 7
      bin/v-change-firewall-rule
  4. 11 11
      bin/v-delete-firewall-ban
  5. 38 102
      func/main.sh
  6. 5 4
      web/login/index.php

+ 10 - 10
bin/v-add-firewall-ban

@@ -1,6 +1,6 @@
 #!/bin/bash
 # info: add firewall blocking rule
-# options: IP CHAIN
+# options: IPV4_CIDR CHAIN
 #
 # example: v-add-firewall-ban 37.120.129.20 MAIL
 #
@@ -11,7 +11,7 @@
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
+ipv4_cidr=$1
 chain=$(echo $2 | tr '[:lower:]' '[:upper:]')
 
 # Defining absolute path for iptables and modprobe
@@ -31,8 +31,8 @@ source_conf "$HESTIA/conf/hestia.conf"
 #                    Verifications                         #
 #----------------------------------------------------------#
 
-check_args '2' "$#" 'IP CHAIN'
-is_format_valid 'ip' 'chain'
+check_args '2' "$#" 'IPV4_CIDR CHAIN'
+is_format_valid 'ipv4_cidr' 'chain'
 is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
 
 # Perform verification if read-only mode is enabled
@@ -46,20 +46,20 @@ check_hestia_demo_mode
 heal_iptables_links
 
 # Checking server ip
-if [ -e "$HESTIA/data/ips/$ip" ] || [ "$ip" = '127.0.0.1' ]; then
+if [ -e "$HESTIA/data/ips/$ipv4_cidr" ] || [ "$ipv4_cidr" = '127.0.0.1' ]; then
 	exit
 fi
 
 # Checking ip exclusions
 excludes="$HESTIA/data/firewall/excludes.conf"
-check_excludes=$(grep "^$ip$" $excludes 2> /dev/null)
+check_excludes=$(grep "^$ipv4_cidr$" $excludes 2> /dev/null)
 if [ -n "$check_excludes" ]; then
 	exit
 fi
 
 # Checking ip in banlist
 conf="$HESTIA/data/firewall/banlist.conf"
-check_ip=$(grep "IP='$ip' CHAIN='$chain'" $conf 2> /dev/null)
+check_ip=$(grep "IP='$ipv4_cidr' CHAIN='$chain'" $conf 2> /dev/null)
 if [ -n "$check_ip" ]; then
 	exit
 fi
@@ -73,8 +73,8 @@ time=$(echo "$time_n_date" | cut -f 1 -d \ )
 date=$(echo "$time_n_date" | cut -f 2 -d \ )
 
 # Adding ip to banlist
-echo "IP='$ip' CHAIN='$chain' TIME='$time' DATE='$date'" >> $conf
-$iptables -I fail2ban-$chain 1 -s $ip \
+echo "IP='$ipv4_cidr' CHAIN='$chain' TIME='$time' DATE='$date'" >> $conf
+$iptables -I fail2ban-$chain 1 -s $ipv4_cidr \
 	-j REJECT --reject-with icmp-port-unreachable 2> /dev/null
 
 # Changing permissions
@@ -85,7 +85,7 @@ chmod 660 $conf
 #----------------------------------------------------------#
 
 # Logging
-$BIN/v-log-action "system" "Warning" "Firewall" "Banned IP address $ip."
+$BIN/v-log-action "system" "Warning" "Firewall" "Banned IP address $ipv4_cidr."
 log_event "$OK" "$ARGUMENTS"
 
 exit

+ 7 - 7
bin/v-add-firewall-rule

@@ -1,6 +1,6 @@
 #!/bin/bash
 # info: add firewall rule
-# options: ACTION IP PORT [PROTOCOL] [COMMENT] [RULE]
+# options: ACTION IPV4_CIDR PORT [PROTOCOL] [COMMENT] [RULE]
 #
 # example: v-add-firewall-rule DROP 185.137.111.77 25
 #
@@ -12,7 +12,7 @@
 
 # Argument definition
 action=$(echo $1 | tr '[:lower:]' '[:upper:]')
-ip=$2
+ipv4_cidr=$2
 port_ext=$3
 protocol=${4-TCP}
 protocol=$(echo $protocol | tr '[:lower:]' '[:upper:]')
@@ -47,7 +47,7 @@ sort_fw_rules() {
 #                    Verifications                         #
 #----------------------------------------------------------#
 
-check_args '3' "$#" 'ACTION IP PORT [PROTOCOL] [COMMENT] [RULE]'
+check_args '3' "$#" 'ACTION IPV4_CIDR PORT [PROTOCOL] [COMMENT] [RULE]'
 is_format_valid 'action' 'protocol' 'port_ext'
 is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
 get_next_fw_rule
@@ -56,12 +56,12 @@ is_object_new '../../../data/firewall/rules' 'RULE' "$rule"
 if [ -n "$comment" ]; then
 	is_format_valid 'comment'
 fi
-if [[ "$ip" =~ ^ipset: ]]; then
-	ipset_name="${ip#ipset:}"
+if [[ "$ipv4_cidr" =~ ^ipset: ]]; then
+	ipset_name="${ipv4_cidr#ipset:}"
 	$BIN/v-list-firewall-ipset plain | grep "^$ipset_name\s" > /dev/null
 	check_result $? 'ipset object not found' "$E_NOTEXIST"
 else
-	is_format_valid 'ip'
+	is_format_valid 'ipv4_cidr'
 fi
 
 # Perform verification if read-only mode is enabled
@@ -78,7 +78,7 @@ date=$(echo "$time_n_date" | cut -f 2 -d \ )
 
 # Concatenating rule
 str="RULE='$rule' ACTION='$action' PROTOCOL='$protocol' PORT='$port_ext'"
-str="$str IP='$ip' COMMENT='$comment' SUSPENDED='no'"
+str="$str IP='$ipv4_cidr' COMMENT='$comment' SUSPENDED='no'"
 str="$str TIME='$time' DATE='$date'"
 
 # Adding to config

+ 7 - 7
bin/v-change-firewall-rule

@@ -1,6 +1,6 @@
 #!/bin/bash
 # info: change firewall rule
-# options: RULE ACTION IP PORT [PROTOCOL] [COMMENT]
+# options: RULE ACTION IPV4_CIDR PORT [PROTOCOL] [COMMENT]
 #
 # example: v-change-firewall-rule 3 ACCEPT 5.188.123.17 443
 #
@@ -14,7 +14,7 @@
 # Argument definition
 rule=$1
 action=$(echo $2 | tr '[:lower:]' '[:upper:]')
-ip=$3
+ipv4_cidr=$3
 port_ext=$4
 protocol=${5-TCP}
 protocol=$(echo $protocol | tr '[:lower:]' '[:upper:]')
@@ -40,7 +40,7 @@ sort_fw_rules() {
 #                    Verifications                         #
 #----------------------------------------------------------#
 
-check_args '4' "$#" 'RULE ACTION IP PORT [PROTOCOL] [COMMENT]'
+check_args '4' "$#" 'RULE ACTION IPV4_CIDR PORT [PROTOCOL] [COMMENT]'
 is_format_valid 'rule' 'action' 'protocol' 'port_ext'
 if [ ! -z "$comment" ]; then
 	is_format_valid 'comment'
@@ -48,12 +48,12 @@ fi
 is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
 is_object_valid '../../../data/firewall/rules' 'RULE' "$rule"
 
-if [[ "$ip" =~ ^ipset: ]]; then
-	ipset_name="${ip#ipset:}"
+if [[ "$ipv4_cidr" =~ ^ipset: ]]; then
+	ipset_name="${ipv4_cidr#ipset:}"
 	$BIN/v-list-firewall-ipset plain | grep "^$ipset_name\s" > /dev/null
 	check_result $? 'ipset object not found' "$E_NOTEXIST"
 else
-	is_format_valid 'ip'
+	is_format_valid 'ipv4_cidr'
 fi
 
 # Perform verification if read-only mode is enabled
@@ -70,7 +70,7 @@ date=$(echo "$time_n_date" | cut -f 2 -d \ )
 
 # Concatenating firewall rule
 str="RULE='$rule' ACTION='$action' PROTOCOL='$protocol' PORT='$port_ext'"
-str="$str IP='$ip' COMMENT='$comment' SUSPENDED='no'"
+str="$str IP='$ipv4_cidr' COMMENT='$comment' SUSPENDED='no'"
 str="$str TIME='$time' DATE='$date'"
 
 # Deleting old rule

+ 11 - 11
bin/v-delete-firewall-ban

@@ -1,6 +1,6 @@
 #!/bin/bash
 # info: delete firewall blocking rule
-# options: IP CHAIN
+# options: IPV4_CIDR CHAIN
 #
 # example: v-delete-firewall-ban 198.11.130.250 MAIL
 #
@@ -11,7 +11,7 @@
 #----------------------------------------------------------#
 
 # Argument definition
-ip=$1
+ipv4_cidr=$1
 chain=$(echo $2 | tr '[:lower:]' '[:upper:]')
 
 # Defining absolute path for iptables and modprobe
@@ -31,8 +31,8 @@ source_conf "$HESTIA/conf/hestia.conf"
 #                    Verifications                         #
 #----------------------------------------------------------#
 
-check_args '2' "$#" 'IP CHAIN'
-is_format_valid 'ip' 'chain'
+check_args '2' "$#" 'IPV4_CIDR CHAIN'
+is_format_valid 'ipv4_cidr' 'chain'
 is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
 
 # Perform verification if read-only mode is enabled
@@ -47,30 +47,30 @@ heal_iptables_links
 
 conf="$HESTIA/data/firewall/banlist.conf"
 if [ "$chain" == "ALL" ]; then
-	check_ip=$(grep "IP='$ip' CHAIN='*'" $conf)
+	check_ip=$(grep "IP='$ipv4_cidr' CHAIN='*'" $conf)
 	if [ -z "$check_ip" ]; then
 		exit
 	fi
-	grep "IP='$ip' CHAIN='*'" $conf | while read -r line; do
+	grep "IP='$ipv4_cidr' CHAIN='*'" $conf | while read -r line; do
 		parse_object_kv_list $line
 
 		# Deleting ip from banlist
 		sip=$(echo "$IP" | sed "s|/|\\\/|g")
 		sed -i "/IP='$sip' CHAIN='$CHAIN'/d" $conf
-		b=$($iptables -L fail2ban-$CHAIN --line-number -n | grep -w $ip | awk '{print $1}')
+		b=$($iptables -L fail2ban-$CHAIN --line-number -n | grep -w $ipv4_cidr | awk '{print $1}')
 		$iptables -D fail2ban-$CHAIN $b 2> /dev/null
 	done
 else
 	# Checking ip in banlist
-	check_ip=$(grep "IP='$ip' CHAIN='$chain'" $conf 2> /dev/null)
+	check_ip=$(grep "IP='$ipv4_cidr' CHAIN='$chain'" $conf 2> /dev/null)
 	if [ -z "$check_ip" ]; then
 		exit
 	fi
 
 	# Deleting ip from banlist
-	sip=$(echo "$ip" | sed "s|/|\\\/|g")
+	sip=$(echo "$ipv4_cidr" | sed "s|/|\\\/|g")
 	sed -i "/IP='$sip' CHAIN='$chain'/d" $conf
-	b=$($iptables -L fail2ban-$chain --line-number -n | grep -w $ip | awk '{print $1}')
+	b=$($iptables -L fail2ban-$chain --line-number -n | grep -w $ipv4_cidr | awk '{print $1}')
 	$iptables -D fail2ban-$chain $b 2> /dev/null
 fi
 
@@ -82,7 +82,7 @@ chmod 660 $conf
 #----------------------------------------------------------#
 
 # Logging
-$BIN/v-log-action "system" "Info" "Firewall" "Removed IP from ban list (IP: $ip, Service: $chain)."
+$BIN/v-log-action "system" "Info" "Firewall" "Removed IP from ban list (IP: $ipv4_cidr, Service: $chain)."
 log_event "$OK" "$ARGUMENTS"
 
 exit

+ 38 - 102
func/main.sh

@@ -791,117 +791,51 @@ is_alias_format_valid() {
 
 # 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}
+    valid=$($HESTIA_PHP -r '$ip="$argv[1]"; echo (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? 0 : 1);' $1);
+    if [ "$valid" -ne 0 ]; then
+        check_result "$E_INVALID" "invalid $object_name :: $1"
+    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-ipv6}
+    valid=$($HESTIA_PHP -r '$ip="$argv[1]"; echo (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 0 : 1);' $1);
+    if [ "$valid" -ne 0 ]; then
+        check_result "$E_INVALID" "invalid $object_name :: $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
+    valid=$($HESTIA_PHP -r '$ip="$argv[1]"; echo (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) ? 0 : 1);' $1);
+    if [ "$valid" -ne 0 ]; then
+        check_result "$E_INVALID" "invalid IP format :: $1"
+    fi
+}
 
-	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
+is_ipv4_cidr_format_valid() {
+    object_name=${2-ip}
+    valid=$($HESTIA_PHP -r '$cidr="$argv[1]"; list($ip, $netmask) = [...explode("/", $cidr), 32]; echo ((filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && $netmask <= 32) ? 0 : 1);' $1);
+    if [ "$valid" -ne 0 ]; then
+        check_result "$E_INVALID" "invalid $object_name :: $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
+is_ipv6_cidr_format_valid() {
+    object_name=${2-ipv6}
+    valid=$($HESTIA_PHP -r '$cidr="$argv[1]"; list($ip, $netmask) = [...explode("/", $cidr), 128]; echo ((filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && $netmask <= 128) ? 0 : 1);' $1);
+    if [ "$valid" -ne 0 ]; then
+        check_result "$E_INVALID" "invalid $object_name :: $1"
+    fi
+}
 
-		if [ -n "$ipv6_valid" ] || [ "$valid_cidr" -eq 0 ]; then
-			check_result "$E_INVALID" "invalid IP format :: $1"
-		fi
-	fi
+is_netmask_format_valid() {
+    object_name=${2-netmask}
+    valid=$($HESTIA_PHP -r '$netmask="$argv[1]"; echo (preg_match("/^(128|192|224|240|248|252|254|255)\.(0|128|192|224|240|248|252|254|255)\.(0|128|192|224|240|248|252|254|255)\.(0|128|192|224|240|248|252|254|255)/", $netmask) ? 0 : 1);' $1);
+    if [ "$valid" -ne 0 ]; then
+        check_result "$E_INVALID" "invalid $object_name :: $1"
+    fi
 }
 
 # Proxy extention format validator
@@ -1295,6 +1229,8 @@ is_format_valid() {
 				ip) is_ip_format_valid "$arg" ;;
 				ipv6) is_ipv6_format_valid "$arg" ;;
 				ip46) is_ip46_format_valid "$arg" ;;
+				ipv4_cidr) is_ipv4_cidr_format_valid "$arg" ;;
+				ipv6_cidr) is_ipv6_cidr_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' ;;
@@ -1305,7 +1241,7 @@ is_format_valid() {
 				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' ;;
+				netmask) is_netmask_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' ;;

+ 5 - 4
web/login/index.php

@@ -115,10 +115,11 @@ function authenticate_user($user, $password, $twofa = "") {
 		$v_user = quoteshellarg($user);
 		$ip = $_SERVER["REMOTE_ADDR"];
 		$user_agent = $_SERVER["HTTP_USER_AGENT"];
-		if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
-			if (!empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
-				$ip = $_SERVER["HTTP_CF_CONNECTING_IP"];
-			}
+
+		if (!empty($_SERVER["HTTP_CF_CONNECTING_IP"]) &&
+			filter_var($_SERVER["HTTP_CF_CONNECTING_IP"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)
+		) {
+			$ip = $_SERVER["HTTP_CF_CONNECTING_IP"];
 		}
 
 		// Handling IPv4-mapped IPv6 address