Skip to content

Instantly share code, notes, and snippets.

@siavash119
Last active December 3, 2024 00:46
Show Gist options
  • Save siavash119/49ba9921fa860735eaa8c7cdbdc39ed6 to your computer and use it in GitHub Desktop.
Save siavash119/49ba9921fa860735eaa8c7cdbdc39ed6 to your computer and use it in GitHub Desktop.
firehol sets to nftables
#!/bin/bash
trust="{1.1.1.1/32, 1.0.0.1/32, 8.8.8.8/32, 114.114.114.114/32, 114.114.115.115/32}"
declare -A sets
#sets["firehol2"]="https://iplists.firehol.org/files/firehol_level2.netset"
sets["blocklist_net_ua"]="https://iplists.firehol.org/files/blocklist_net_ua.ipset"
sets["firehol3"]="https://iplists.firehol.org/files/firehol_level3.netset"
table_name="sink"
rule_file="/etc/nft/rules.d/10_firehol_rules.nft"
function mytrap() {
echo "deleting netset files"
for key in ${!sets[@]}; do
rm "/tmp/${key}_IPs.netset"
done
}
trap "mytrap" INT TERM EXIT
echo "downloading ip sets"
for key in ${!sets[@]}; do
echo ${key} ${sets[${key}]}
curl --retry-all-errors --retry 5 -s "${sets[${key}]}" | sed -r -e '/^#/d' -e '/^\s*$/d' -e 's/^/\t\t\t/' -e '$!s/$/,/' > "/tmp/${key}_IPs.netset" &
done
wait
sync
declare -A ips
for key in ${!sets[@]}; do
ips[${key}]=`cat /tmp/"${key}_IPs.netset"`
if [[ ${ips[${key}]} == "" ]]; then
echo "error downloading ${key} set. exiting"
exit 1
fi
done
echo "writing nftables script to $rule_file"
touch "$rule_file"
rm "$rule_file"
#header
cat <<HERE > "$rule_file"
#!/sbin/nft -f
table inet $table_name
delete table inet $table_name
table inet $table_name {
counter blackhole { }
HERE
#set elements
for key in ${!sets[@]}; do
cat <<HERE >> "$rule_file"
set ${key} {
type ipv4_addr; flags constant, interval;
elements = {
${ips["${key}"]}
}
}
HERE
done
#chain
cat <<HERE >> "$rule_file"
chain ${table_name}_chain {
type filter hook input priority -300; policy accept;
ip saddr $trust accept
ip daddr $trust accept
HERE
for key in ${!sets[@]}; do
cat <<HERE >> "$rule_file"
#ip saddr @${key} counter name "blackhole" log prefix "nftables ${key} source dropped:" drop
#ip daddr @${key} counter name "blackhole" log prefix "nftables ${key} destination dropped:" drop
ip saddr @${key} counter name "blackhole" drop
ip daddr @${key} counter name "blackhole" drop
HERE
done
cat <<HERE >> "$rule_file"
accept
}
}
HERE
chmod u+x "$rule_file"
/sbin/nft -c -f "$rule_file" > /dev/null 2>&1
exit $?
@williamdes
Copy link

williamdes commented Dec 2, 2024

Thank you for your version, here is my improved version of the script

#!/bin/bash

# Bash is needed for arrays

set -eu

buildFile() {
rule_file=$1
familly=$2

echo "Building IPv$familly file: $rule_file"

rule_type="ip"
set_type="ip"
table_type="ip"

if [ "$familly" -eq 6 ]; then
    set_type="ip6"
    rule_type="ip6"
    table_type="ip6"
fi

interfaces_rule_in=""
interfaces_rule_out=""
if [ -n "${interfaces:-}" ]; then
    interfaces_rule_in="iifname $interfaces"
    interfaces_rule_out="oifname $interfaces"
fi

#header
cat <<HERE > "$rule_file"
#!/sbin/nft -f

# Flush it first
table ${set_type} ${table_prefix}-v${familly}
delete table ${set_type} ${table_prefix}-v${familly}

table ${table_type} ${table_prefix}-v${familly} {

HERE

#set elements
for key in ${!sets[@]}; do
    set +e
    if [ "$familly" -eq 6 ]; then
        # Include IPv6 addresses only, add tabs and line endings
        set_ips="$(echo "${ips["${key}"]}" | grep -F ":" | sed -r -e 's/^/\t\t\t/' -e '$!s/$/,/')"
	set_ips_code=$?
    else
        # Remove IPv6 addresses, sort, add tabs and line endings
        set_ips="$(echo "${ips["${key}"]}" | grep -v -F "#" | grep -v -F ":" | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 | sed -r -e 's/^/\t\t\t/' -e '$!s/$/,/')"
	set_ips_code=$?
    fi
    set -e

    if [ -z "${set_ips:-}" ]; then
        echo "No IPv${familly} found in ${key}" > /dev/stderr
cat <<HERE >> "$rule_file"
	set ${table_prefix}-${key}-v${familly}-${table_suffix} {
		type ipv${familly}_addr
		flags constant, interval
		auto-merge
	}
HERE
    else
cat <<HERE >> "$rule_file"
        set ${table_prefix}-${key}-v${familly}-${table_suffix} {
                type ipv${familly}_addr
                flags constant, interval
                auto-merge
                elements = {
$set_ips
                }

        }
HERE
fi

done

#input chain
cat <<HERE >> "$rule_file"
	chain ${table_prefix}-v${familly}-chain-input {
		type filter hook input priority ${filter_priority}; policy accept;
HERE

for key in ${!sets[@]}; do
cat <<HERE >> "$rule_file"
		${rule_type} ${interfaces_rule_out} saddr @${table_prefix}-${key}-v${familly}-${table_suffix} counter drop comment "Drop from IPS @${key}"
		${rule_type} ${interfaces_rule_in} daddr @${table_prefix}-${key}-v${familly}-${table_suffix} counter drop comment "Drop to IPS @${key}"
HERE
done

#end chain
cat <<HERE >> "$rule_file"
	}
HERE

#footer
cat <<HERE >> "$rule_file"
}
HERE

echo "Built IPv$familly file: $rule_file"
chmod u+x "$rule_file"
echo "Checking file: $rule_file"
set +e
nft -c -f "$rule_file" > /dev/null 2>&1
RESULT_CODE=$?
set -e
if [ $RESULT_CODE -eq 0 ]; then
    echo "Checked file (PASS): $rule_file"
else
    echo "Checked file (ERR): $rule_file with exit code $RESULT_CODE" > /dev/stderr
    nft -c -f "$rule_file"
    exit $RESULT_CODE
fi

}

downloadFiles() {
    echo "downloading ip sets..."
    for key in ${!sets[@]}; do
        echo "downloading ${key}: ${sets[${key}]}"
        # Remove comments, remove whitespace lines
        ips["${key}"]="$(curl --user-agent "$USER_AGENT" --location --retry-all-errors --retry 5 -s "${sets[${key}]}" | sed -r -e '/^#/d' -e '/^\s*$/d')"
    done
    echo "done downloading ip sets."
}

loadChain() {
    rule_file=$1
    echo "Loading: $rule_file"
    nft -f $rule_file
}

# Script variables starts here

table_prefix="company-intranet"
table_suffix="blacklists"

# The interfaces to protect, empty to protect all. But some IP lists can also ban your private traffic !
interfaces="{ ens18, ens19 }"

#    NF_IP_PRI_CONNTRACK_DEFRAG (-400): priority of defragmentation
#    NF_IP_PRI_RAW (-300): traditional priority of the raw table placed before connection tracking operation
#    NF_IP_PRI_SELINUX_FIRST (-225): SELinux operations
#    NF_IP_PRI_CONNTRACK (-200): Connection tracking operations
#    NF_IP_PRI_MANGLE (-150): mangle operation
#    NF_IP_PRI_NAT_DST (-100): destination NAT
#    NF_IP_PRI_FILTER (0): filtering operation, the filter table
#    NF_IP_PRI_SECURITY (50): Place of security table where secmark can be set for example
#    NF_IP_PRI_NAT_SRC (100): source NAT
#    NF_IP_PRI_SELINUX_LAST (225): SELinux at packet exit
#    NF_IP_PRI_CONNTRACK_HELPER (300): connection tracking at exit

# CrowdSec uses: filter -10
filter_priority="-300"

declare -A sets
sets["firehol1"]="https://iplists.firehol.org/files/firehol_level1.netset"

declare -A ips

# You can put your email in case someone finds that your script makes too much requests
USER_AGENT="Firewall protection script"

# Script running starts here

downloadFiles

buildFile "/etc/nftables/collections/ipv4.conf" 4
buildFile "/etc/nftables/collections/ipv6.conf" 6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment