You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							206 lines
						
					
					
						
							7.4 KiB
						
					
					
				
			
		
		
	
	
							206 lines
						
					
					
						
							7.4 KiB
						
					
					
				#!/bin/bash
 | 
						|
# info: add firewall ipset
 | 
						|
# options: NAME [SOURCE] [IPVERSION] [AUTOUPDATE] [REFRESH]
 | 
						|
#
 | 
						|
# example: v-add-firewall-ipset country-nl "https://raw.githubusercontent.com/ipverse/rir-ip/master/country/nl/ipv4-aggregated.txt"
 | 
						|
#
 | 
						|
# This function adds new ipset to system firewall
 | 
						|
 | 
						|
#----------------------------------------------------------#
 | 
						|
#                Variables & Functions                     #
 | 
						|
#----------------------------------------------------------#
 | 
						|
 | 
						|
ip_name=${1}
 | 
						|
data_source=${2}
 | 
						|
ip_version=${3:-v4}
 | 
						|
autoupdate=${4:-yes}
 | 
						|
refresh=${5:-no}
 | 
						|
 | 
						|
# Includes
 | 
						|
# shellcheck source=/etc/hestiacp/hestia.conf
 | 
						|
source /etc/hestiacp/hestia.conf
 | 
						|
# shellcheck source=/usr/local/hestia/func/main.sh
 | 
						|
source $HESTIA/func/main.sh
 | 
						|
# load config file
 | 
						|
source_conf "$HESTIA/conf/hestia.conf"
 | 
						|
 | 
						|
#----------------------------------------------------------#
 | 
						|
#                    Verifications                         #
 | 
						|
#----------------------------------------------------------#
 | 
						|
 | 
						|
check_args '1' "$#" 'NAME [SOURCE] [IPVERSION] [AUTOUPDATE] [FORCE]'
 | 
						|
is_format_valid 'ip_name'
 | 
						|
is_boolean_format_valid "$autoupdate" 'Automatically update IP list (yes/no)'
 | 
						|
is_boolean_format_valid "$refresh" 'Refresh IP list (yes/no)'
 | 
						|
is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
 | 
						|
 | 
						|
# Perform verification if read-only mode is enabled
 | 
						|
check_hestia_demo_mode
 | 
						|
 | 
						|
# Define variables for ipset configuration
 | 
						|
ipset_hstobject='../../data/firewall/ipset'
 | 
						|
IPSET_BIN="$(command -v ipset)"
 | 
						|
IPSET_PATH="$HESTIA/data/firewall/ipset"
 | 
						|
 | 
						|
# Ensure ipset is installed
 | 
						|
if [ -z "$IPSET_BIN" ]; then
 | 
						|
	if [ -f '/etc/redhat-release' ]; then
 | 
						|
		dnf install -q -y ipset > /dev/null
 | 
						|
	else
 | 
						|
		apt-get --quiet --yes install ipset > /dev/null
 | 
						|
	fi
 | 
						|
	check_result $? "Installing IPset package"
 | 
						|
 | 
						|
	IPSET_BIN="$(which ipset)"
 | 
						|
	check_result $? "IPset binary not found"
 | 
						|
fi
 | 
						|
 | 
						|
# Ensure ipset configuration path and master file exist before attempting to parse
 | 
						|
mkdir -p "$IPSET_PATH"
 | 
						|
if [ ! -f "$HESTIA/data/firewall/ipset.conf" ]; then
 | 
						|
	touch $HESTIA/data/firewall/ipset.conf
 | 
						|
fi
 | 
						|
 | 
						|
if [ -z "$data_source" ]; then
 | 
						|
	if [ ! -f "${IPSET_PATH}.conf" ] || [[ ! $(grep "LISTNAME='$ip_name'" "${IPSET_PATH}.conf") ]]; then
 | 
						|
		check_args '2' "$#" 'NAME SOURCE [IPVERSION] [AUTOUPDATE] [FORCE]'
 | 
						|
	fi
 | 
						|
 | 
						|
	data_source="$(get_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$SOURCE')"
 | 
						|
	ip_version="$(get_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$IP_VERSION')"
 | 
						|
else
 | 
						|
	is_object_new "$ipset_hstobject" 'LISTNAME' "$ip_name"
 | 
						|
fi
 | 
						|
 | 
						|
if [ "$ip_version" != "v4" ] && [ "$ip_version" != "v6" ]; then
 | 
						|
	check_result "$E_INVALID" "invalid ip version, valid: (v4|v6)"
 | 
						|
fi
 | 
						|
 | 
						|
if ! echo "$data_source" | egrep -q '^(https?|script|file):'; then
 | 
						|
	check_result "$E_INVALID" "invalid ipset source, valid: (http[s]://|script:|file:)"
 | 
						|
fi
 | 
						|
 | 
						|
IPSET_FILE="${ip_name}.${ip_version}"
 | 
						|
IPSET_MIN_SIZE=10
 | 
						|
 | 
						|
#----------------------------------------------------------#
 | 
						|
#                       Action                             #
 | 
						|
#----------------------------------------------------------#
 | 
						|
 | 
						|
# Generate ip lists file if missing or required refresh
 | 
						|
if [ ! -f "${IPSET_PATH}/${IPSET_FILE}.iplist" ] || [ "$refresh" = "yes" ]; then
 | 
						|
 | 
						|
	iplist_tempfile=$(mktemp)
 | 
						|
 | 
						|
	if [[ "$data_source" =~ ^https?:// ]]; then
 | 
						|
 | 
						|
		wget --tries=3 --timeout=15 --read-timeout=15 --waitretry=3 --no-dns-cache --quiet "$data_source" -O "$iplist_tempfile"
 | 
						|
		check_result $? "Downloading ip list"
 | 
						|
 | 
						|
		# Advanced: execute script with the same basename for aditional pre-processing
 | 
						|
		# ex:
 | 
						|
		if [ -x "${IPSET_PATH}/${IPSET_FILE}.sh" ]; then
 | 
						|
			if [ -e '/etc/redhat-release' ]; then
 | 
						|
				preprocess_output="$(cat "$iplist_tempfile" | setpriv --clear-groups --reuid nobody --regid nobody -- ${IPSET_PATH}/${IPSET_FILE}.sh "$ip_name" "$iplist_tempfile")"
 | 
						|
			else
 | 
						|
				preprocess_output="$(cat "$iplist_tempfile" | setpriv --clear-groups --reuid nobody --regid nogroup -- ${IPSET_PATH}/${IPSET_FILE}.sh "$ip_name" "$iplist_tempfile")"
 | 
						|
			fi
 | 
						|
			check_result $? "Preprocessing script failed (${IPSET_FILE}.sh)"
 | 
						|
			[[ "$preprocess_output" ]] && echo "$preprocess_output" > "$iplist_tempfile"
 | 
						|
		fi
 | 
						|
 | 
						|
	elif [[ "$data_source" =~ ^script:/ ]]; then
 | 
						|
 | 
						|
		# Generate the ip list file trough a external script
 | 
						|
		# ex: compiling a ip list from multiple sources on demand
 | 
						|
		if [ -x "${data_source#script:}" ]; then
 | 
						|
			if [ -e '/etc/redhat-release' ]; then
 | 
						|
				setpriv --clear-groups --reuid nobody --regid nobody -- ${data_source#script:} "$ip_name" > "$iplist_tempfile"
 | 
						|
			else
 | 
						|
				setpriv --clear-groups --reuid nobody --regid nogroup -- ${data_source#script:} "$ip_name" > "$iplist_tempfile"
 | 
						|
			fi
 | 
						|
			check_result $? "Running custom ip list update script"
 | 
						|
 | 
						|
		fi
 | 
						|
 | 
						|
	elif [[ "$data_source" =~ ^file:/ ]]; then
 | 
						|
 | 
						|
		# Use a external ip-list file managed by other apps
 | 
						|
		# ex: Using a ip list that is continuously updated
 | 
						|
		[ -f "${data_source#file:}" ] && cp -f "${data_source#file:}" "$iplist_tempfile"
 | 
						|
 | 
						|
	fi
 | 
						|
 | 
						|
	# Cleanup ip list
 | 
						|
	sed -r -i -e 's/[;#].*$//' -e 's/[ \t]*$//' -e '/^$/d' "$iplist_tempfile"
 | 
						|
	if [[ $ip_version == 'v4' ]]; then
 | 
						|
		sed -i -r -n -e '/^((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])/p' "$iplist_tempfile"
 | 
						|
	elif [[ $ip_version == 'v6' ]]; then
 | 
						|
		sed -i -r -n -e '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}/p' "$iplist_tempfile"
 | 
						|
	fi
 | 
						|
 | 
						|
	# Validate iplist file size
 | 
						|
	iplist_size=$(sed -r -e '/^#|^$/d' "$iplist_tempfile" | wc -l)
 | 
						|
	[[ "$iplist_size" -le "$IPSET_MIN_SIZE" ]] && check_result "$E_INVALID" "IP list file too small (<${IPSET_MIN_SIZE}), ignoring"
 | 
						|
	mv -f "$iplist_tempfile" "${IPSET_PATH}/${IPSET_FILE}.iplist"
 | 
						|
 | 
						|
fi
 | 
						|
 | 
						|
# Load ipset in kernel
 | 
						|
inet_ver="inet"
 | 
						|
[ "$ip_version" == "v6" ] && inet_ver="inet6"
 | 
						|
 | 
						|
$IPSET_BIN -quiet create -exist "$ip_name" hash:net family $inet_ver
 | 
						|
$IPSET_BIN -quiet destroy "${ip_name}-tmp"
 | 
						|
$IPSET_BIN create "${ip_name}-tmp" -exist hash:net family $inet_ver maxelem 1048576
 | 
						|
$IPSET_BIN flush "${ip_name}-tmp"
 | 
						|
 | 
						|
sed -rn -e '/^#|^$/d' -e "s/^(.*)/add ${ip_name}-tmp \\1/p" "${IPSET_PATH}/${IPSET_FILE}.iplist" | $IPSET_BIN -quiet restore
 | 
						|
check_result $? "Populating ipset table"
 | 
						|
 | 
						|
$IPSET_BIN swap "${ip_name}-tmp" "${ip_name}"
 | 
						|
$IPSET_BIN -quiet destroy "${ip_name}-tmp"
 | 
						|
 | 
						|
# 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 \ )
 | 
						|
 | 
						|
if [ ! -f "${IPSET_PATH}.conf" ] || [ -z "$(get_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$LISTNAME')" ]; then
 | 
						|
 | 
						|
	# Concatenating rule
 | 
						|
	str="LISTNAME='$ip_name' IP_VERSION='$ip_version' SOURCE='$data_source'"
 | 
						|
	str="$str AUTOUPDATE='$autoupdate' SUSPENDED='no'"
 | 
						|
	str="$str TIME='$time' DATE='$date'"
 | 
						|
	echo "$str" >> $HESTIA/data/firewall/ipset.conf
 | 
						|
	log_type="added"
 | 
						|
 | 
						|
elif [ "$refresh" = "yes" ]; then
 | 
						|
 | 
						|
	# Update iplist last regen time
 | 
						|
	update_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$TIME' "$time"
 | 
						|
	update_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$DATE' "$date"
 | 
						|
	log_type="refreshed"
 | 
						|
 | 
						|
fi
 | 
						|
 | 
						|
# Changing permissions
 | 
						|
chmod 660 $HESTIA/data/firewall/ipset.conf
 | 
						|
chmod 660 "${IPSET_PATH}/${IPSET_FILE}.iplist"
 | 
						|
 | 
						|
# Install ipset daily cron updater
 | 
						|
if ! grep --silent --no-messages "v-update-firewall-ipset" $HESTIA/data/queue/daily.pipe; then
 | 
						|
	cmd="$BIN/v-update-firewall-ipset yes"
 | 
						|
	echo "$cmd" >> $HESTIA/data/queue/daily.pipe
 | 
						|
fi
 | 
						|
 | 
						|
#----------------------------------------------------------#
 | 
						|
#                       Hestia                             #
 | 
						|
#----------------------------------------------------------#
 | 
						|
 | 
						|
# Logging
 | 
						|
$BIN/v-log-action "system" "Info" "Firewall" "IPset IP list ${log_type:-loaded} (Name: $ip_name, IP version: $ip_version, Autoupdate: $autoupdate)."
 | 
						|
log_event "$OK" "$ARGUMENTS"
 | 
						|
 | 
						|
exit
 |