Skip to content

Commit 00f42c2

Browse files
committed
active response script for nftables
Does the same as firewall-drop.sh and firewalld-drop.sh but for nftables (default in Debian 10+). Needs a bit more steps to setup but does a very good job. Documentation should be updated to contain maybe an example for the nftables configuration. Tested on Debian 11 with IPv4 and IPv6 adresses.
1 parent 9df55e1 commit 00f42c2

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

active-response/nftables-drop.sh

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#!/bin/sh
2+
# Adds an IP to a nftables set
3+
# Requirements: Linux with nftables
4+
# Expect: srcip
5+
# Author: Daniel B. Cid (iptables)
6+
# Author: cgzones
7+
# Author: ChristianBeer
8+
# Last modified: Dec 26, 2021
9+
10+
# You need to create a set and a rule that uses it in order for packets to be dropped
11+
# make sure the drop rules are checked before the accept rules
12+
# Example:
13+
# nft add set inet filter ossec_ar4 { type ipv4_addr\; comment \"ossec active response\" \; }
14+
# nft add set inet filter ossec_ar6 { type ipv6_addr\; comment \"ossec active response\" \; }
15+
# nft add rule inet filter input ip saddr @ossec_ar4 drop
16+
# nft add rule inet filter input ip6 saddr @ossec_ar6 drop
17+
18+
19+
UNAME=`uname`
20+
ECHO="/bin/echo"
21+
GREP="/bin/grep"
22+
NFTCMD="/usr/sbin/nft"
23+
RULE=""
24+
ARG1=""
25+
# protocol family used in nftables configuration
26+
FAMILYIPV4="inet" # use "ip" when you have separate tables for ipv4 and ipv6
27+
FAMILYIPV6="inet" # use "ip6" when you have separate tables for ipv4 and ipv6
28+
# nftables table name
29+
TABLEIPV4="filter"
30+
TABLEIPV6="filter"
31+
# Name of the sets where offending IPs should be added
32+
SETIPV4="ossec_ar4"
33+
SETIPV6="ossec_ar6"
34+
35+
RULEID=""
36+
ACTION=$1
37+
USER=$2
38+
IP=$3
39+
PWD=`pwd`
40+
LOCK="${PWD}/nft-drop"
41+
LOCK_PID="${PWD}/nft-drop/pid"
42+
43+
44+
LOCAL=`dirname $0`;
45+
cd $LOCAL
46+
cd ../
47+
filename=$(basename "$0")
48+
49+
LOG_FILE="${PWD}/../logs/active-responses.log"
50+
51+
echo "`date` $0 $1 $2 $3 $4 $5" >> ${LOG_FILE}
52+
53+
54+
# Checking for an IP
55+
if [ "x${IP}" = "x" ]; then
56+
echo "$0: <action> <username> <ip>"
57+
exit 1;
58+
fi
59+
60+
case "${IP}" in
61+
*:* ) RULE="element ${FAMILYIPV6} ${TABLEIPV6} ${SETIPV6} { ${IP} }";;
62+
*.* ) RULE="element ${FAMILYIPV4} ${TABLEIPV4} ${SETIPV4} { ${IP} }";;
63+
* ) echo "`date` Unable to run active response (invalid IP: '${IP}')." >> ${LOG_FILE} && exit 1;;
64+
esac
65+
66+
# This number should be more than enough (even if a hundred
67+
# instances of this script is ran together). If you have
68+
# a really loaded env, you can increase it to 75 or 100.
69+
MAX_ITERATION="50"
70+
71+
# Lock function
72+
lock()
73+
{
74+
i=0;
75+
# Providing a lock.
76+
while [ 1 ]; do
77+
mkdir ${LOCK} > /dev/null 2>&1
78+
MSL=$?
79+
if [ "${MSL}" = "0" ]; then
80+
# Lock acquired (setting the pid)
81+
echo "$$" > ${LOCK_PID}
82+
return;
83+
fi
84+
85+
# Getting currently/saved PID locking the file
86+
C_PID=`cat ${LOCK_PID} 2>/dev/null`
87+
if [ "x" = "x${S_PID}" ]; then
88+
S_PID=${C_PID}
89+
fi
90+
91+
# Breaking out of the loop after X attempts
92+
if [ "x${C_PID}" = "x${S_PID}" ]; then
93+
i=`expr $i + 1`;
94+
fi
95+
96+
sleep $i;
97+
98+
i=`expr $i + 1`;
99+
100+
# So i increments 2 by 2 if the pid does not change.
101+
# If the pid keeps changing, we will increments one
102+
# by one and fail after MAX_ITERACTION
103+
104+
if [ "$i" = "${MAX_ITERATION}" ]; then
105+
kill="false"
106+
for pid in `pgrep -f "${filename}"`; do
107+
if [ "x${pid}" = "x${C_PID}" ]; then
108+
# Unlocking and exiting
109+
kill -9 ${C_PID}
110+
echo "`date` Killed process ${C_PID} holding lock." >> ${LOG_FILE}
111+
kill="true"
112+
unlock;
113+
i=0;
114+
S_PID="";
115+
break;
116+
fi
117+
done
118+
119+
if [ "x${kill}" = "xfalse" ]; then
120+
echo "`date` Unable kill process ${C_PID} holding lock." >> ${LOG_FILE}
121+
# Unlocking and exiting
122+
unlock;
123+
exit 1;
124+
fi
125+
fi
126+
done
127+
}
128+
129+
# Unlock function
130+
unlock()
131+
{
132+
rm -rf ${LOCK}
133+
}
134+
135+
136+
137+
# Blocking IP
138+
if [ "x${ACTION}" != "xadd" -a "x${ACTION}" != "xdelete" ]; then
139+
echo "$0: invalid action: ${ACTION}"
140+
exit 1;
141+
fi
142+
143+
144+
145+
# We should run on linux
146+
if [ "X${UNAME}" = "XLinux" ]; then
147+
if [ "x${ACTION}" = "xadd" ]; then
148+
ARG1="add"
149+
else
150+
ARG1="delete"
151+
fi
152+
153+
# Checking if nft is present
154+
if [ ! -x ${NFTCMD} ]; then
155+
NFTCMD="/usr"${NFTCMD}
156+
if [ ! -x ${NFTCMD} ]; then
157+
echo "$0: can not find nft"
158+
exit 1;
159+
fi
160+
fi
161+
162+
# Executing and exiting
163+
COUNT=0;
164+
lock;
165+
while [ 1 ]; do
166+
${NFTCMD} ${ARG1} ${RULE} >/dev/null
167+
RES=$?
168+
if [ $RES = 0 ]; then
169+
break;
170+
else
171+
COUNT=`expr $COUNT + 1`;
172+
echo "`date` Unable to run (nft returning $RES): $COUNT - $0 $1 $2 $3 $4 $5" >> ${LOG_FILE}
173+
sleep $COUNT;
174+
175+
if [ $COUNT -gt 4 ]; then
176+
break;
177+
fi
178+
fi
179+
done
180+
unlock;
181+
182+
exit 0;
183+
else
184+
exit 0;
185+
fi

0 commit comments

Comments
 (0)