Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
This commit is contained in:
+319
-123
@@ -1,13 +1,23 @@
|
||||
#!/bin/bash
|
||||
################################################################################
|
||||
# Script Name: iptables-blocklists.sh
|
||||
# Version: 1.0
|
||||
# Version: 1.04
|
||||
# Description: Per-feed iptables threat intelligence blocking with ipset
|
||||
# Author: Phil Connor
|
||||
# Contact: contact@mylinux.work
|
||||
# Website: https://mylinux.work
|
||||
# License: MIT
|
||||
################################################################################
|
||||
# Changelog:
|
||||
# v1.04 - Fix IPv6 parser: reject timestamps (e.g. 14:34:21) misidentified
|
||||
# as IPv6 addresses by requiring hex letters, 3+ groups, or ::
|
||||
# v1.03 - Fix remove-feed/disable-feed: delete iptables rules before destroying
|
||||
# ipsets. Remove dangerous ipset destruction of disabled feeds during
|
||||
# update. Always swap ipsets even when feed is empty (clears stale
|
||||
# blocks). Use -I (insert) for IPv6 rules instead of -A (append).
|
||||
# Fix show-stats to count both IPv4+IPv6 blocks with cached journal.
|
||||
# Add curl requirement check. Fix ipset member counting.
|
||||
################################################################################
|
||||
# Don't use 'set -e' - causes issues with ipset error handling
|
||||
|
||||
CONFIG_DIR="/etc/iptables-threats"
|
||||
@@ -45,6 +55,7 @@ COMMANDS:
|
||||
whitelist-add IP Add IP/CIDR to whitelist
|
||||
whitelist-init Initialize whitelist with RFC1918/Docker networks
|
||||
whitelist-list Show all whitelisted IPs
|
||||
clean-cache Remove cache files for disabled feeds
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
@@ -77,7 +88,53 @@ EOF
|
||||
}
|
||||
|
||||
log_message() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
local msg
|
||||
msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
echo "$msg"
|
||||
echo "$msg" >> "$LOG_FILE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Iterate over enabled feeds in $FEEDS_CONFIG, calling the provided callback
|
||||
# function with arguments: name url type description
|
||||
# Usage: for_each_enabled_feed my_callback_function
|
||||
for_each_enabled_feed() {
|
||||
local callback="$1"
|
||||
[ -f "$FEEDS_CONFIG" ] || return 0
|
||||
|
||||
local enabled name url type description
|
||||
while IFS='|' read -r enabled name url type description; do
|
||||
[[ "$enabled" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$enabled" ]] && continue
|
||||
[ "$enabled" != "1" ] && continue
|
||||
"$callback" "$name" "$url" "$type" "$description"
|
||||
done < "$FEEDS_CONFIG"
|
||||
}
|
||||
|
||||
# Iterate over ALL feeds (enabled + disabled), calling the provided callback
|
||||
# function with arguments: enabled name url type description
|
||||
for_each_feed() {
|
||||
local callback="$1"
|
||||
[ -f "$FEEDS_CONFIG" ] || return 0
|
||||
|
||||
local enabled name url type description
|
||||
while IFS='|' read -r enabled name url type description; do
|
||||
[[ "$enabled" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$enabled" ]] && continue
|
||||
"$callback" "$enabled" "$name" "$url" "$type" "$description"
|
||||
done < "$FEEDS_CONFIG"
|
||||
}
|
||||
|
||||
validate_feed_name() {
|
||||
local name="$1"
|
||||
if [ -z "$name" ]; then
|
||||
echo "ERROR: Feed name cannot be empty"; return 1
|
||||
fi
|
||||
if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
||||
echo "ERROR: Feed name '$name' contains invalid characters (only a-z, 0-9, _, - allowed)"; return 1
|
||||
fi
|
||||
if [ "${#name}" -gt 20 ]; then
|
||||
echo "ERROR: Feed name '$name' too long (max 20 chars, ipset name limit)"; return 1
|
||||
fi
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
@@ -89,7 +146,7 @@ parse_args() {
|
||||
--no-auto-update) ENABLE_AUTO_UPDATE=false; shift ;;
|
||||
--no-ipv6) ENABLE_IPV6=false; shift ;;
|
||||
--update-interval) UPDATE_INTERVAL="$2"; shift 2 ;;
|
||||
install|update|apply-rules|test-rules|list-feeds|show-stats|whitelist-init|whitelist-list) COMMAND="$1"; shift ;;
|
||||
install|update|apply-rules|test-rules|list-feeds|show-stats|whitelist-init|whitelist-list|clean-cache) COMMAND="$1"; shift ;;
|
||||
add-feed) COMMAND="add-feed"; FEED_NAME="$2"; FEED_URL="$3"; shift 3 ;;
|
||||
remove-feed|enable-feed|disable-feed) COMMAND="$1"; FEED_NAME="$2"; shift 2 ;;
|
||||
whitelist-add) COMMAND="whitelist-add"; WHITELIST_IP="$2"; shift 2 ;;
|
||||
@@ -115,6 +172,10 @@ check_requirements() {
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
command -v curl >/dev/null 2>&1 || { echo "ERROR: curl required"; exit 1; }
|
||||
|
||||
ensure_ipsets_exist
|
||||
}
|
||||
|
||||
create_directory_structure() {
|
||||
@@ -137,16 +198,29 @@ cleanup_old_backups() {
|
||||
}
|
||||
|
||||
initialize_feeds_config() {
|
||||
[ -f "$FEEDS_CONFIG" ] && return
|
||||
|
||||
local has_feeds
|
||||
has_feeds=$(grep -c '^[01]|' "$FEEDS_CONFIG" 2>/dev/null || true)
|
||||
|
||||
if [ -f "$FEEDS_CONFIG" ] && [ "$has_feeds" -gt 0 ]; then
|
||||
log_message "Feeds configuration already exists with $has_feeds feeds"
|
||||
return
|
||||
fi
|
||||
|
||||
log_message "Creating feeds configuration..."
|
||||
|
||||
[ -f "$FEEDS_CONFIG" ] && mv "$FEEDS_CONFIG" "${FEEDS_CONFIG}.old-$(date +%Y%m%d-%H%M%S)"
|
||||
|
||||
cat > "$FEEDS_CONFIG" <<'EOF'
|
||||
# Threat Intelligence Feeds Configuration
|
||||
# Format: ENABLED|NAME|URL|TYPE|DESCRIPTION
|
||||
#
|
||||
# ENABLED: 1 (enabled) or 0 (disabled)
|
||||
# NAME: Unique feed identifier
|
||||
# URL: Feed URL
|
||||
# TYPE: Format type (plain, cidr, commented, custom)
|
||||
# URL: Feed URL (http/https) or local file (file:///path/to/file)
|
||||
# TYPE: Format type (plain, cidr, commented)
|
||||
# plain - One IP/CIDR per line, no comments
|
||||
# cidr - IP/CIDR with optional inline comments/fields
|
||||
# commented - Lines starting with # or ; are ignored, IPs extracted
|
||||
# DESCRIPTION: Feed description
|
||||
|
||||
1|cinsarmy|http://cinsscore.com/list/ci-badguys.txt|plain|CINS Army Malicious IPs
|
||||
@@ -181,95 +255,144 @@ EOF
|
||||
chmod 600 "$FEEDS_CONFIG"
|
||||
}
|
||||
|
||||
_ensure_feed_ipset() {
|
||||
local name="$1"
|
||||
|
||||
ipset list "${IPSET_PREFIX}-${name}" >/dev/null 2>&1 || \
|
||||
ipset create "${IPSET_PREFIX}-${name}" hash:net family inet hashsize 4096 maxelem 200000 2>/dev/null || true
|
||||
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
ipset list "${IPSET_PREFIX}-${name}-v6" >/dev/null 2>&1 || \
|
||||
ipset create "${IPSET_PREFIX}-${name}-v6" hash:net family inet6 hashsize 4096 maxelem 200000 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_ipsets_exist() {
|
||||
if [ -f /etc/sysconfig/ipset ]; then
|
||||
ipset restore -f /etc/sysconfig/ipset 2>/dev/null || true
|
||||
elif [ -f /etc/iptables/ipsets ]; then
|
||||
ipset restore -f /etc/iptables/ipsets 2>/dev/null || true
|
||||
fi
|
||||
|
||||
ipset list "$WHITELIST_IPSET" >/dev/null 2>&1 || \
|
||||
ipset create "$WHITELIST_IPSET" hash:net family inet hashsize 1024 maxelem 10000 2>/dev/null || true
|
||||
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
ipset list "$WHITELIST_IPSET_V6" >/dev/null 2>&1 || \
|
||||
ipset create "$WHITELIST_IPSET_V6" hash:net family inet6 hashsize 1024 maxelem 10000 2>/dev/null || true
|
||||
fi
|
||||
|
||||
for_each_enabled_feed _ensure_feed_ipset
|
||||
}
|
||||
|
||||
setup_ipsets() {
|
||||
log_message "Setting up per-feed ipsets..."
|
||||
|
||||
# Whitelist
|
||||
|
||||
if ! ipset list "$WHITELIST_IPSET" >/dev/null 2>&1; then
|
||||
ipset create "$WHITELIST_IPSET" hash:net family inet hashsize 1024 maxelem 10000
|
||||
ipset add "$WHITELIST_IPSET" 127.0.0.1 2>/dev/null || true
|
||||
fi
|
||||
|
||||
|
||||
if [ "$ENABLE_IPV6" = true ] && ! ipset list "$WHITELIST_IPSET_V6" >/dev/null 2>&1; then
|
||||
ipset create "$WHITELIST_IPSET_V6" hash:net family inet6 hashsize 1024 maxelem 10000
|
||||
ipset add "$WHITELIST_IPSET_V6" ::1 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Create ipset per feed
|
||||
while IFS='|' read -r enabled name url type description; do
|
||||
[[ "$enabled" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$enabled" ]] && continue
|
||||
[ "$enabled" != "1" ] && continue
|
||||
|
||||
if ! ipset list "${IPSET_PREFIX}-${name}" >/dev/null 2>&1; then
|
||||
ipset create "${IPSET_PREFIX}-${name}" hash:net family inet hashsize 4096 maxelem 200000
|
||||
fi
|
||||
|
||||
if [ "$ENABLE_IPV6" = true ] && ! ipset list "${IPSET_PREFIX}-${name}-v6" >/dev/null 2>&1; then
|
||||
ipset create "${IPSET_PREFIX}-${name}-v6" hash:net family inet6 hashsize 4096 maxelem 200000
|
||||
fi
|
||||
done < "$FEEDS_CONFIG"
|
||||
|
||||
for_each_enabled_feed _ensure_feed_ipset
|
||||
}
|
||||
|
||||
download_feed() {
|
||||
curl -f -s -m 30 -L "$1" -o "$2" 2>/dev/null
|
||||
local url="$1" output="$2"
|
||||
|
||||
# Local file support: file:///path or file://path
|
||||
if [[ "$url" == file://* ]]; then
|
||||
local local_path="${url#file://}"
|
||||
if [ ! -f "$local_path" ]; then
|
||||
log_message " Local file not found: $local_path"
|
||||
return 1
|
||||
fi
|
||||
cp "$local_path" "$output" 2>/dev/null || return 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
local http_code
|
||||
http_code=$(curl -f -s -m 60 --connect-timeout 10 -L \
|
||||
-A "iptables-threat-feeds-per-feed/1.0" \
|
||||
-w "%{http_code}" -o "$output" "$url" 2>/dev/null) || true
|
||||
|
||||
if [ ! -s "$output" ]; then
|
||||
log_message " Download failed for $url (HTTP $http_code, empty response)"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
parse_feed() {
|
||||
local file="$1" type="$2" out_v4="$3" out_v6="$4"
|
||||
true > "$out_v4"
|
||||
true > "$out_v6"
|
||||
|
||||
|
||||
: > "$out_v4"
|
||||
: > "$out_v6"
|
||||
|
||||
local cleaned
|
||||
cleaned=$(mktemp)
|
||||
tr -d '\r' < "$file" > "$cleaned"
|
||||
|
||||
# IPv6 filter: require either a hex letter [a-fA-F], or 3+ colon-separated
|
||||
# groups, or ::. This excludes timestamps like 14:34:21 which only have
|
||||
# digits and exactly two colon-separated groups.
|
||||
local v6_filter='([a-fA-F]|:.*:.*:|::)'
|
||||
|
||||
case "$type" in
|
||||
plain)
|
||||
grep -E '^[0-9.]+(/[0-9]+)?$' "$file" >> "$out_v4" 2>/dev/null || true
|
||||
[ "$ENABLE_IPV6" = true ] && grep -E '^[0-9a-fA-F:]+(/[0-9]+)?$' "$file" | grep ':' >> "$out_v6" 2>/dev/null || true
|
||||
grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?$' "$cleaned" >> "$out_v4" 2>/dev/null || true
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
grep -E '^[0-9a-fA-F:]+(/[0-9]+)?$' "$cleaned" | grep ':' \
|
||||
| grep -E "$v6_filter" >> "$out_v6" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
cidr)
|
||||
grep -E '^[0-9.]+' "$file" | cut -d' ' -f1 | cut -d'#' -f1 | grep -v '^$' >> "$out_v4" 2>/dev/null || true
|
||||
[ "$ENABLE_IPV6" = true ] && grep -E '^[0-9a-fA-F:]+' "$file" | grep ':' | cut -d' ' -f1 | cut -d'#' -f1 >> "$out_v6" 2>/dev/null || true
|
||||
grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?' "$cleaned" \
|
||||
| grep -v '^$' >> "$out_v4" 2>/dev/null || true
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
grep -oE '^[0-9a-fA-F:]+(/[0-9]+)?' "$cleaned" \
|
||||
| grep ':' | grep -E "$v6_filter" | grep -v '^$' >> "$out_v6" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
commented)
|
||||
grep -v -E '^[#;]|^$' "$file" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?' >> "$out_v4" 2>/dev/null || true
|
||||
[ "$ENABLE_IPV6" = true ] && grep -v -E '^[#;]|^$' "$file" | grep -oE '[0-9a-fA-F:]+(/[0-9]+)?' | grep -E '^[0-9a-fA-F]{1,4}:[0-9a-fA-F:]+' >> "$out_v6" 2>/dev/null || true
|
||||
grep -v -E '^[#;]|^$' "$cleaned" \
|
||||
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?' >> "$out_v4" 2>/dev/null || true
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
grep -v -E '^[#;]|^$' "$cleaned" \
|
||||
| grep -oE '[0-9a-fA-F:]+(/[0-9]+)?' \
|
||||
| grep -E '^[0-9a-fA-F]{1,4}:[0-9a-fA-F:]+' \
|
||||
| grep -E "$v6_filter" >> "$out_v6" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f "$cleaned"
|
||||
}
|
||||
|
||||
update_feeds() {
|
||||
log_message "Starting per-feed update (FAST ipset restore mode)..."
|
||||
|
||||
# Auto-cleanup cache and ipsets for disabled feeds
|
||||
local enabled_feeds=$(grep '^1|' "$FEEDS_CONFIG" 2>/dev/null | cut -d'|' -f2)
|
||||
local enabled_feeds
|
||||
enabled_feeds=$(grep '^1|' "$FEEDS_CONFIG" 2>/dev/null | cut -d'|' -f2)
|
||||
local cleaned_cache=0
|
||||
local cleaned_ipsets=0
|
||||
|
||||
# Clean cache files
|
||||
for cache_file in "$CACHE_DIR"/*.raw "$CACHE_DIR"/*-v4.parsed "$CACHE_DIR"/*-v6.parsed "$CACHE_DIR"/*-v4.restore "$CACHE_DIR"/*-v6.restore; do
|
||||
[ -f "$cache_file" ] || continue
|
||||
local bn=$(basename "$cache_file")
|
||||
local bn
|
||||
bn=$(basename "$cache_file")
|
||||
local fn="${bn%%.raw}"; fn="${fn%%-v4.parsed}"; fn="${fn%%-v6.parsed}"; fn="${fn%%-v4.restore}"; fn="${fn%%-v6.restore}"
|
||||
if ! echo "$enabled_feeds" | grep -q "^${fn}$"; then
|
||||
rm -f "$cache_file" && cleaned_cache=$((cleaned_cache + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# Clean ipsets for disabled feeds
|
||||
while IFS='|' read -r enabled name url type description; do
|
||||
[[ "$enabled" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$enabled" ]] && continue
|
||||
[ "$enabled" = "1" ] && continue
|
||||
|
||||
if ipset list "${IPSET_PREFIX}-${name}" >/dev/null 2>&1; then
|
||||
ipset destroy "${IPSET_PREFIX}-${name}" 2>/dev/null && cleaned_ipsets=$((cleaned_ipsets + 1))
|
||||
fi
|
||||
if ipset list "${IPSET_PREFIX}-${name}-v6" >/dev/null 2>&1; then
|
||||
ipset destroy "${IPSET_PREFIX}-${name}-v6" 2>/dev/null && cleaned_ipsets=$((cleaned_ipsets + 1))
|
||||
fi
|
||||
done < "$FEEDS_CONFIG"
|
||||
|
||||
[ "$cleaned_cache" -gt 0 ] && log_message " Cleaned $cleaned_cache stale cache files"
|
||||
[ "$cleaned_ipsets" -gt 0 ] && log_message " Destroyed $cleaned_ipsets stale ipsets"
|
||||
|
||||
local total=0 failed=0
|
||||
|
||||
@@ -290,36 +413,34 @@ update_feeds() {
|
||||
c4=$(wc -l < "$v4" 2>/dev/null || echo 0)
|
||||
[ "$ENABLE_IPV6" = true ] && c6=$(wc -l < "$v6" 2>/dev/null || echo 0)
|
||||
|
||||
# FAST IPv4: Use ipset restore
|
||||
if [ "$c4" -gt 0 ]; then
|
||||
# Ensure target ipset exists for swap
|
||||
if ! ipset list "${IPSET_PREFIX}-${name}" >/dev/null 2>&1; then
|
||||
ipset create "${IPSET_PREFIX}-${name}" hash:net family inet hashsize 4096 maxelem 200000
|
||||
fi
|
||||
|
||||
{
|
||||
echo "create ${IPSET_PREFIX}-${name}-tmp hash:net family inet hashsize 4096 maxelem 200000"
|
||||
echo "flush ${IPSET_PREFIX}-${name}-tmp"
|
||||
while IFS= read -r ip; do
|
||||
[ -z "$ip" ] && continue
|
||||
echo "add ${IPSET_PREFIX}-${name}-tmp $ip"
|
||||
done < "$v4"
|
||||
echo "swap ${IPSET_PREFIX}-${name} ${IPSET_PREFIX}-${name}-tmp"
|
||||
echo "destroy ${IPSET_PREFIX}-${name}-tmp"
|
||||
} > "$CACHE_DIR/${name}-v4.restore"
|
||||
|
||||
ipset restore < "$CACHE_DIR/${name}-v4.restore" 2>/dev/null || {
|
||||
log_message " ⚠ Batch load failed for $name IPv4, using fallback"
|
||||
ipset flush "${IPSET_PREFIX}-${name}" 2>/dev/null || true
|
||||
while IFS= read -r ip; do
|
||||
[ -z "$ip" ] && continue
|
||||
ipset add "${IPSET_PREFIX}-${name}" "$ip" 2>/dev/null || true
|
||||
done < "$v4"
|
||||
}
|
||||
# FAST IPv4: Use ipset restore (always swap, even if empty, to clear stale entries)
|
||||
# Ensure target ipset exists for swap
|
||||
if ! ipset list "${IPSET_PREFIX}-${name}" >/dev/null 2>&1; then
|
||||
ipset create "${IPSET_PREFIX}-${name}" hash:net family inet hashsize 4096 maxelem 200000
|
||||
fi
|
||||
|
||||
# FAST IPv6: Use ipset restore
|
||||
if [ "$ENABLE_IPV6" = true ] && [ "$c6" -gt 0 ]; then
|
||||
{
|
||||
echo "create ${IPSET_PREFIX}-${name}-tmp hash:net family inet hashsize 4096 maxelem 200000"
|
||||
echo "flush ${IPSET_PREFIX}-${name}-tmp"
|
||||
while IFS= read -r ip; do
|
||||
[ -z "$ip" ] && continue
|
||||
echo "add ${IPSET_PREFIX}-${name}-tmp $ip"
|
||||
done < "$v4"
|
||||
echo "swap ${IPSET_PREFIX}-${name} ${IPSET_PREFIX}-${name}-tmp"
|
||||
echo "destroy ${IPSET_PREFIX}-${name}-tmp"
|
||||
} > "$CACHE_DIR/${name}-v4.restore"
|
||||
|
||||
ipset restore < "$CACHE_DIR/${name}-v4.restore" 2>/dev/null || {
|
||||
log_message " ⚠ Batch load failed for $name IPv4, using fallback"
|
||||
ipset flush "${IPSET_PREFIX}-${name}" 2>/dev/null || true
|
||||
while IFS= read -r ip; do
|
||||
[ -z "$ip" ] && continue
|
||||
ipset add "${IPSET_PREFIX}-${name}" "$ip" 2>/dev/null || true
|
||||
done < "$v4"
|
||||
}
|
||||
|
||||
# FAST IPv6: Use ipset restore (always swap, even if empty, to clear stale entries)
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
# Ensure target ipset exists for swap
|
||||
if ! ipset list "${IPSET_PREFIX}-${name}-v6" >/dev/null 2>&1; then
|
||||
ipset create "${IPSET_PREFIX}-${name}-v6" hash:net family inet6 hashsize 4096 maxelem 200000
|
||||
@@ -391,6 +512,7 @@ apply_iptables_rules() {
|
||||
|
||||
# Add per-feed rules
|
||||
local line=2
|
||||
local v6_line=2
|
||||
while IFS='|' read -r enabled name url type description; do
|
||||
[[ "$enabled" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$enabled" ]] && continue
|
||||
@@ -404,8 +526,10 @@ apply_iptables_rules() {
|
||||
|
||||
# IPv6
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
ip6tables -A INPUT -m set --match-set "${IPSET_PREFIX}-${name}-v6" src -m limit --limit 5/min -j LOG --log-prefix "[THREAT-v6:${name}] "
|
||||
ip6tables -A INPUT -m set --match-set "${IPSET_PREFIX}-${name}-v6" src -j DROP
|
||||
ip6tables -I INPUT $v6_line -m set --match-set "${IPSET_PREFIX}-${name}-v6" src -m limit --limit 5/min -j LOG --log-prefix "[THREAT-v6:${name}] "
|
||||
v6_line=$((v6_line + 1))
|
||||
ip6tables -I INPUT $v6_line -m set --match-set "${IPSET_PREFIX}-${name}-v6" src -j DROP
|
||||
v6_line=$((v6_line + 1))
|
||||
fi
|
||||
done < "$FEEDS_CONFIG"
|
||||
|
||||
@@ -429,9 +553,11 @@ apply_iptables_rules() {
|
||||
|
||||
setup_iptables_persistence() {
|
||||
log_message "Setting up iptables persistence..."
|
||||
|
||||
# Create systemd service for iptables restore
|
||||
cat > /etc/systemd/system/iptables-restore.service <<'EOF'
|
||||
|
||||
local script_path
|
||||
script_path=$(readlink -f "$0")
|
||||
|
||||
cat > /etc/systemd/system/iptables-restore.service <<EOF
|
||||
[Unit]
|
||||
Description=Restore iptables rules
|
||||
Before=network-pre.target
|
||||
@@ -440,14 +566,14 @@ Wants=network-pre.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/bin/bash -c 'ipset restore -f /etc/sysconfig/ipset 2>/dev/null || ipset restore -f /etc/iptables/ipsets 2>/dev/null || true'
|
||||
ExecStart=/bin/bash -c 'ipset restore -f /etc/sysconfig/ipset 2>/dev/null || ipset restore -f /etc/iptables/ipsets 2>/dev/null || true; ${script_path} _ensure-ipsets 2>/dev/null || true'
|
||||
ExecStart=/bin/bash -c 'iptables-restore /etc/sysconfig/iptables 2>/dev/null || iptables-restore /etc/iptables/rules.v4 2>/dev/null || true'
|
||||
ExecStart=/bin/bash -c 'ip6tables-restore /etc/sysconfig/ip6tables 2>/dev/null || ip6tables-restore /etc/iptables/rules.v6 2>/dev/null || true'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable iptables-restore.service 2>/dev/null || true
|
||||
log_message "✓ iptables persistence configured"
|
||||
@@ -456,7 +582,8 @@ EOF
|
||||
setup_auto_update() {
|
||||
[ "$ENABLE_AUTO_UPDATE" = false ] && return
|
||||
|
||||
local script=$(readlink -f "$0")
|
||||
local script
|
||||
script=$(readlink -f "$0")
|
||||
|
||||
cat > /etc/systemd/system/iptables-threat-feeds-update.service <<EOF
|
||||
[Unit]
|
||||
@@ -485,6 +612,9 @@ EOF
|
||||
}
|
||||
|
||||
cmd_show_stats() {
|
||||
local journal_1h
|
||||
journal_1h=$(journalctl -k --since "1 hour ago" 2>/dev/null | grep '\[THREAT' || true)
|
||||
|
||||
echo "Per-Feed Blocking Statistics"
|
||||
printf "%-25s %10s %10s %12s\n" "FEED" "IPv4" "IPv6" "BLOCKS(1h)"
|
||||
echo "-------------------------------------------------------------------"
|
||||
@@ -494,35 +624,45 @@ cmd_show_stats() {
|
||||
[[ -z "$enabled" ]] && continue
|
||||
[ "$enabled" != "1" ] && continue
|
||||
|
||||
local v4 v6=0 blocks
|
||||
v4=$(ipset list "${IPSET_PREFIX}-${name}" 2>/dev/null | grep -c '^[0-9.]' || echo 0)
|
||||
[ "$ENABLE_IPV6" = true ] && v6=$(ipset list "${IPSET_PREFIX}-${name}-v6" 2>/dev/null | grep -c '^[0-9a-fA-F:]' || echo 0)
|
||||
blocks=$(journalctl -k --since "1 hour ago" 2>/dev/null | grep -c "\[THREAT:${name}\]" || echo 0)
|
||||
local v4 v6=0 blocks_v4=0 blocks_v6=0
|
||||
v4=$(ipset list "${IPSET_PREFIX}-${name}" 2>/dev/null | sed -n '/^Members:$/,$p' | tail -n +2 | wc -l)
|
||||
[ "$ENABLE_IPV6" = true ] && v6=$(ipset list "${IPSET_PREFIX}-${name}-v6" 2>/dev/null | sed -n '/^Members:$/,$p' | tail -n +2 | wc -l)
|
||||
|
||||
printf "%-25s %10s %10s %12s\n" "$name" "$v4" "$v6" "$blocks"
|
||||
if [ -n "$journal_1h" ]; then
|
||||
blocks_v4=$(printf '%s' "$journal_1h" | grep -c "\[THREAT:${name}\]" 2>/dev/null || true)
|
||||
blocks_v6=$(printf '%s' "$journal_1h" | grep -c "\[THREAT-v6:${name}\]" 2>/dev/null || true)
|
||||
fi
|
||||
local blocks=$(( ${blocks_v4:-0} + ${blocks_v6:-0} ))
|
||||
|
||||
printf "%-25s %10s %10s %12s\n" "$name" "${v4:-0}" "${v6:-0}" "$blocks"
|
||||
done < "$FEEDS_CONFIG"
|
||||
}
|
||||
|
||||
_print_feed() {
|
||||
local enabled="$1" name="$2" _url="$3" _type="$4" description="$5"
|
||||
printf "%-10s %-25s %s\n" "$([ "$enabled" = "1" ] && echo "ENABLED" || echo "DISABLED")" "$name" "$description"
|
||||
}
|
||||
|
||||
cmd_list_feeds() {
|
||||
printf "%-10s %-25s %s\n" "STATUS" "NAME" "DESC"
|
||||
while IFS='|' read -r enabled name url type description; do
|
||||
[[ "$enabled" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$enabled" ]] && continue
|
||||
printf "%-10s %-25s %s\n" "$([ "$enabled" = "1" ] && echo "ENABLED" || echo "DISABLED")" "$name" "$description"
|
||||
done < "$FEEDS_CONFIG"
|
||||
for_each_feed _print_feed
|
||||
}
|
||||
|
||||
cmd_whitelist_add() {
|
||||
[ -z "$WHITELIST_IP" ] && { echo "Usage: $0 whitelist-add <IP|CIDR>"; exit 1; }
|
||||
|
||||
if echo "$WHITELIST_IP" | grep -q ':'; then
|
||||
ipset add "$WHITELIST_IPSET_V6" "$WHITELIST_IP" 2>/dev/null && \
|
||||
log_message "✓ Added to IPv6 whitelist: $WHITELIST_IP" || \
|
||||
{ echo "Failed to add $WHITELIST_IP"; exit 1; }
|
||||
if [[ "$WHITELIST_IP" == *:* ]]; then
|
||||
if ipset add "$WHITELIST_IPSET_V6" "$WHITELIST_IP" 2>/dev/null; then
|
||||
log_message "✓ Added to IPv6 whitelist: $WHITELIST_IP"
|
||||
else
|
||||
echo "Failed to add $WHITELIST_IP"; exit 1
|
||||
fi
|
||||
else
|
||||
ipset add "$WHITELIST_IPSET" "$WHITELIST_IP" 2>/dev/null && \
|
||||
log_message "✓ Added to IPv4 whitelist: $WHITELIST_IP" || \
|
||||
{ echo "Failed to add $WHITELIST_IP"; exit 1; }
|
||||
if ipset add "$WHITELIST_IPSET" "$WHITELIST_IP" 2>/dev/null; then
|
||||
log_message "✓ Added to IPv4 whitelist: $WHITELIST_IP"
|
||||
else
|
||||
echo "Failed to add $WHITELIST_IP"; exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
ipset save > /etc/sysconfig/ipset 2>/dev/null || ipset save > /etc/iptables/ipsets 2>/dev/null || true
|
||||
@@ -586,6 +726,7 @@ cmd_whitelist_list() {
|
||||
|
||||
cmd_add_feed() {
|
||||
[ -z "$FEED_NAME" ] || [ -z "$FEED_URL" ] && { echo "Usage: $0 add-feed <NAME> <URL>"; exit 1; }
|
||||
validate_feed_name "$FEED_NAME" || exit 1
|
||||
grep -q "^[01]|${FEED_NAME}|" "$FEEDS_CONFIG" 2>/dev/null && { echo "Feed exists"; exit 1; }
|
||||
echo "1|${FEED_NAME}|${FEED_URL}|plain|Custom: ${FEED_NAME}" >> "$FEEDS_CONFIG"
|
||||
log_message "✓ Added feed: $FEED_NAME"
|
||||
@@ -593,15 +734,23 @@ cmd_add_feed() {
|
||||
|
||||
cmd_remove_feed() {
|
||||
[ -z "$FEED_NAME" ] && { echo "Usage: $0 remove-feed <NAME>"; exit 1; }
|
||||
sed -i "/|${FEED_NAME}|/d" "$FEEDS_CONFIG"
|
||||
|
||||
# Remove ipsets and rules
|
||||
|
||||
# Remove rules for this feed first (while ipsets still exist)
|
||||
iptables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}" src -m limit --limit 5/min -j LOG --log-prefix "[THREAT:${FEED_NAME}] " 2>/dev/null || true
|
||||
iptables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}" src -j DROP 2>/dev/null || true
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
ip6tables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}-v6" src -m limit --limit 5/min -j LOG --log-prefix "[THREAT-v6:${FEED_NAME}] " 2>/dev/null || true
|
||||
ip6tables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}-v6" src -j DROP 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Now safe to destroy ipsets
|
||||
ipset destroy "${IPSET_PREFIX}-${FEED_NAME}" 2>/dev/null || true
|
||||
ipset destroy "${IPSET_PREFIX}-${FEED_NAME}-v6" 2>/dev/null || true
|
||||
|
||||
|
||||
# Remove from config last
|
||||
sed -i "/|${FEED_NAME}|/d" "$FEEDS_CONFIG"
|
||||
|
||||
log_message "✓ Removed feed: $FEED_NAME"
|
||||
log_message "Reapplying rules..."
|
||||
apply_iptables_rules
|
||||
}
|
||||
|
||||
cmd_enable_feed() {
|
||||
@@ -622,15 +771,23 @@ cmd_enable_feed() {
|
||||
|
||||
cmd_disable_feed() {
|
||||
[ -z "$FEED_NAME" ] && { echo "Usage: $0 disable-feed <NAME>"; exit 1; }
|
||||
sed -i "s/^1|${FEED_NAME}|/0|${FEED_NAME}|/" "$FEEDS_CONFIG"
|
||||
|
||||
# Destroy ipsets to clear metrics
|
||||
|
||||
# Remove rules for this feed first (while ipsets still exist)
|
||||
iptables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}" src -m limit --limit 5/min -j LOG --log-prefix "[THREAT:${FEED_NAME}] " 2>/dev/null || true
|
||||
iptables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}" src -j DROP 2>/dev/null || true
|
||||
if [ "$ENABLE_IPV6" = true ]; then
|
||||
ip6tables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}-v6" src -m limit --limit 5/min -j LOG --log-prefix "[THREAT-v6:${FEED_NAME}] " 2>/dev/null || true
|
||||
ip6tables -D INPUT -m set --match-set "${IPSET_PREFIX}-${FEED_NAME}-v6" src -j DROP 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Now safe to destroy ipsets
|
||||
ipset destroy "${IPSET_PREFIX}-${FEED_NAME}" 2>/dev/null || true
|
||||
ipset destroy "${IPSET_PREFIX}-${FEED_NAME}-v6" 2>/dev/null || true
|
||||
|
||||
|
||||
# Mark disabled in config
|
||||
sed -i "s/^1|${FEED_NAME}|/0|${FEED_NAME}|/" "$FEEDS_CONFIG"
|
||||
|
||||
log_message "✓ Disabled: $FEED_NAME"
|
||||
log_message "Reapplying rules..."
|
||||
apply_iptables_rules
|
||||
}
|
||||
|
||||
cmd_install() {
|
||||
@@ -704,19 +861,20 @@ cmd_test_rules() {
|
||||
echo "IPv6 rules that would be created:"
|
||||
echo " 1. Whitelist bypass: -I INPUT 1 -m set --match-set $WHITELIST_IPSET_V6 src -j ACCEPT"
|
||||
|
||||
local v6_count=0
|
||||
local v6_line=2
|
||||
while IFS='|' read -r enabled name url type description; do
|
||||
[[ "$enabled" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$enabled" ]] && continue
|
||||
[ "$enabled" != "1" ] && continue
|
||||
|
||||
v6_count=$((v6_count + 1))
|
||||
echo " $((v6_count * 2)). [${name}] LOG: -A INPUT -m set --match-set ${IPSET_PREFIX}-${name}-v6 src -j LOG"
|
||||
echo " $((v6_count * 2 + 1)). [${name}] DROP: -A INPUT -m set --match-set ${IPSET_PREFIX}-${name}-v6 src -j DROP"
|
||||
echo " $v6_line. [${name}] LOG: -I INPUT $v6_line -m set --match-set ${IPSET_PREFIX}-${name}-v6 src -j LOG"
|
||||
v6_line=$((v6_line + 1))
|
||||
echo " $v6_line. [${name}] DROP: -I INPUT $v6_line -m set --match-set ${IPSET_PREFIX}-${name}-v6 src -j DROP"
|
||||
v6_line=$((v6_line + 1))
|
||||
done < "$FEEDS_CONFIG"
|
||||
|
||||
echo ""
|
||||
echo "Total IPv6 rules: $((v6_count * 2 + 1))"
|
||||
echo "Total IPv6 rules: $((v6_line - 1))"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -726,7 +884,44 @@ cmd_test_rules() {
|
||||
echo "=========================================="
|
||||
}
|
||||
|
||||
cmd_clean_cache() {
|
||||
log_message "Cleaning cache for disabled feeds..."
|
||||
|
||||
local removed=0
|
||||
local kept=0
|
||||
|
||||
local enabled_feeds
|
||||
enabled_feeds=$(grep '^1|' "$FEEDS_CONFIG" 2>/dev/null | cut -d'|' -f2)
|
||||
|
||||
for cache_file in "$CACHE_DIR"/*.raw "$CACHE_DIR"/*-v4.parsed "$CACHE_DIR"/*-v6.parsed "$CACHE_DIR"/*-v4.restore "$CACHE_DIR"/*-v6.restore; do
|
||||
[ -f "$cache_file" ] || continue
|
||||
|
||||
local bn feed_name
|
||||
bn=$(basename "$cache_file")
|
||||
feed_name="${bn%%.raw}"
|
||||
feed_name="${feed_name%%-v4.parsed}"
|
||||
feed_name="${feed_name%%-v6.parsed}"
|
||||
feed_name="${feed_name%%-v4.restore}"
|
||||
feed_name="${feed_name%%-v6.restore}"
|
||||
|
||||
if ! grep -q "^${feed_name}$" <<< "$enabled_feeds"; then
|
||||
rm -f "$cache_file"
|
||||
removed=$((removed + 1))
|
||||
else
|
||||
kept=$((kept + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
log_message "Removed $removed cache files, kept $kept active feeds"
|
||||
}
|
||||
|
||||
main() {
|
||||
# Internal command used by iptables-restore.service at boot
|
||||
if [ "${1:-}" = "_ensure-ipsets" ]; then
|
||||
ensure_ipsets_exist
|
||||
exit 0
|
||||
fi
|
||||
|
||||
parse_args "$@"
|
||||
case "$COMMAND" in
|
||||
install) cmd_install ;;
|
||||
@@ -751,6 +946,7 @@ main() {
|
||||
whitelist-add) cmd_whitelist_add ;;
|
||||
whitelist-init) cmd_whitelist_init ;;
|
||||
whitelist-list) cmd_whitelist_list ;;
|
||||
clean-cache) cmd_clean_cache ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user