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
|