mirror of https://github.com/fail2ban/fail2ban
Merge python-3.12--asyncore: python 3.12 support, see gh-3487
commit
4e326cb5cb
|
@ -22,7 +22,7 @@ jobs:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', pypy3.10]
|
python-version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12', pypy3.10]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
steps:
|
steps:
|
||||||
|
@ -59,6 +59,13 @@ jobs:
|
||||||
python -m pip install systemd-python || echo 'systemd not available'
|
python -m pip install systemd-python || echo 'systemd not available'
|
||||||
#readline if available as module:
|
#readline if available as module:
|
||||||
python -c 'import readline' 2> /dev/null || python -m pip install readline || echo 'readline not available'
|
python -c 'import readline' 2> /dev/null || python -m pip install readline || echo 'readline not available'
|
||||||
|
# asyncore/asynchat:
|
||||||
|
if dpkg --compare-versions "$F2B_PYV" ge 3.12; then
|
||||||
|
#sudo apt-get -y install python${F2B_PY/2/}-setuptools || echo 'setuptools not unavailable'
|
||||||
|
python -m pip install setuptools || echo "can't install setuptools"
|
||||||
|
python -m pip install pyasynchat || echo "can't install pyasynchat";
|
||||||
|
python -m pip install pyasyncore || echo "can't install pyasyncore";
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Before scripts
|
- name: Before scripts
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -284,7 +284,7 @@ def splitwords(s):
|
||||||
"""
|
"""
|
||||||
if not s:
|
if not s:
|
||||||
return []
|
return []
|
||||||
return list(filter(bool, [v.strip() for v in re.split('[\s,]+', s)]))
|
return list(filter(bool, [v.strip() for v in re.split(r'[\s,]+', s)]))
|
||||||
|
|
||||||
def _merge_dicts(x, y):
|
def _merge_dicts(x, y):
|
||||||
"""Helper to merge dicts.
|
"""Helper to merge dicts.
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import smtpd
|
|
||||||
import threading
|
import threading
|
||||||
import unittest
|
import unittest
|
||||||
import re
|
import re
|
||||||
|
@ -29,134 +28,139 @@ else:
|
||||||
import imp
|
import imp
|
||||||
|
|
||||||
from ..dummyjail import DummyJail
|
from ..dummyjail import DummyJail
|
||||||
|
|
||||||
from ..utils import CONFIG_DIR, asyncserver, Utils, uni_decode
|
from ..utils import CONFIG_DIR, asyncserver, Utils, uni_decode
|
||||||
|
|
||||||
class TestSMTPServer(smtpd.SMTPServer):
|
try:
|
||||||
|
import smtpd
|
||||||
|
|
||||||
def __init__(self, *args):
|
class TestSMTPServer(smtpd.SMTPServer):
|
||||||
smtpd.SMTPServer.__init__(self, *args)
|
|
||||||
self.ready = False
|
|
||||||
|
|
||||||
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
|
def __init__(self, *args):
|
||||||
self.peer = peer
|
smtpd.SMTPServer.__init__(self, *args)
|
||||||
self.mailfrom = mailfrom
|
self.ready = False
|
||||||
self.rcpttos = rcpttos
|
|
||||||
self.org_data = data
|
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
|
||||||
# replace new line (with tab or space) for possible mime translations (word wrap),
|
self.peer = peer
|
||||||
self.data = re.sub(r"\n[\t ]", " ", uni_decode(data))
|
self.mailfrom = mailfrom
|
||||||
self.ready = True
|
self.rcpttos = rcpttos
|
||||||
|
self.org_data = data
|
||||||
|
# replace new line (with tab or space) for possible mime translations (word wrap),
|
||||||
|
self.data = re.sub(r"\n[\t ]", " ", uni_decode(data))
|
||||||
|
self.ready = True
|
||||||
|
|
||||||
|
|
||||||
class SMTPActionTest(unittest.TestCase):
|
class SMTPActionTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Call before every test case."""
|
"""Call before every test case."""
|
||||||
unittest.F2B.SkipIfCfgMissing(action='smtp.py')
|
unittest.F2B.SkipIfCfgMissing(action='smtp.py')
|
||||||
super(SMTPActionTest, self).setUp()
|
super(SMTPActionTest, self).setUp()
|
||||||
self.jail = DummyJail()
|
self.jail = DummyJail()
|
||||||
pythonModule = os.path.join(CONFIG_DIR, "action.d", "smtp.py")
|
pythonModule = os.path.join(CONFIG_DIR, "action.d", "smtp.py")
|
||||||
pythonModuleName = os.path.basename(pythonModule.rstrip(".py"))
|
pythonModuleName = os.path.basename(pythonModule.rstrip(".py"))
|
||||||
if sys.version_info >= (3, 3):
|
if sys.version_info >= (3, 3):
|
||||||
customActionModule = importlib.machinery.SourceFileLoader(
|
customActionModule = importlib.machinery.SourceFileLoader(
|
||||||
pythonModuleName, pythonModule).load_module()
|
pythonModuleName, pythonModule).load_module()
|
||||||
else:
|
else:
|
||||||
customActionModule = imp.load_source(
|
customActionModule = imp.load_source(
|
||||||
pythonModuleName, pythonModule)
|
pythonModuleName, pythonModule)
|
||||||
|
|
||||||
self.smtpd = TestSMTPServer(("localhost", 0), None)
|
self.smtpd = TestSMTPServer(("localhost", 0), None)
|
||||||
port = self.smtpd.socket.getsockname()[1]
|
port = self.smtpd.socket.getsockname()[1]
|
||||||
|
|
||||||
self.action = customActionModule.Action(
|
self.action = customActionModule.Action(
|
||||||
self.jail, "test", host="localhost:%i" % port)
|
self.jail, "test", host="localhost:%i" % port)
|
||||||
|
|
||||||
## because of bug in loop (see loop in asyncserver.py) use it's loop instead of asyncore.loop:
|
## because of bug in loop (see loop in asyncserver.py) use it's loop instead of asyncore.loop:
|
||||||
self._active = True
|
self._active = True
|
||||||
self._loop_thread = threading.Thread(
|
self._loop_thread = threading.Thread(
|
||||||
target=asyncserver.loop, kwargs={'active': lambda: self._active})
|
target=asyncserver.loop, kwargs={'active': lambda: self._active})
|
||||||
self._loop_thread.daemon = True
|
self._loop_thread.daemon = True
|
||||||
self._loop_thread.start()
|
self._loop_thread.start()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Call after every test case."""
|
"""Call after every test case."""
|
||||||
self.smtpd.close()
|
self.smtpd.close()
|
||||||
self._active = False
|
self._active = False
|
||||||
self._loop_thread.join()
|
self._loop_thread.join()
|
||||||
super(SMTPActionTest, self).tearDown()
|
super(SMTPActionTest, self).tearDown()
|
||||||
|
|
||||||
def _exec_and_wait(self, doaction, timeout=3, short=False):
|
def _exec_and_wait(self, doaction, timeout=3, short=False):
|
||||||
if short: timeout /= 25
|
if short: timeout /= 25
|
||||||
self.smtpd.ready = False
|
self.smtpd.ready = False
|
||||||
doaction()
|
doaction()
|
||||||
Utils.wait_for(lambda: self.smtpd.ready, timeout)
|
Utils.wait_for(lambda: self.smtpd.ready, timeout)
|
||||||
|
|
||||||
def testStart(self):
|
def testStart(self):
|
||||||
self._exec_and_wait(self.action.start)
|
self._exec_and_wait(self.action.start)
|
||||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
"Subject: [Fail2Ban] %s: started" % self.jail.name
|
"Subject: [Fail2Ban] %s: started" % self.jail.name
|
||||||
in self.smtpd.data)
|
in self.smtpd.data)
|
||||||
|
|
||||||
def testStop(self):
|
def testStop(self):
|
||||||
self._exec_and_wait(self.action.stop)
|
self._exec_and_wait(self.action.stop)
|
||||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
"Subject: [Fail2Ban] %s: stopped" %
|
"Subject: [Fail2Ban] %s: stopped" %
|
||||||
self.jail.name in self.smtpd.data)
|
self.jail.name in self.smtpd.data)
|
||||||
|
|
||||||
def _testBan(self, restored=False):
|
def _testBan(self, restored=False):
|
||||||
aInfo = {
|
aInfo = {
|
||||||
'ip': "127.0.0.2",
|
'ip': "127.0.0.2",
|
||||||
'failures': 3,
|
'failures': 3,
|
||||||
'matches': "Test fail 1\n",
|
'matches': "Test fail 1\n",
|
||||||
'ipjailmatches': "Test fail 1\nTest Fail2\n",
|
'ipjailmatches': "Test fail 1\nTest Fail2\n",
|
||||||
'ipmatches': "Test fail 1\nTest Fail2\nTest Fail3\n",
|
'ipmatches': "Test fail 1\nTest Fail2\nTest Fail3\n",
|
||||||
}
|
}
|
||||||
if restored:
|
if restored:
|
||||||
aInfo['restored'] = 1
|
aInfo['restored'] = 1
|
||||||
|
|
||||||
self._exec_and_wait(lambda: self.action.ban(aInfo), short=restored)
|
self._exec_and_wait(lambda: self.action.ban(aInfo), short=restored)
|
||||||
if restored: # no mail, should raises attribute error:
|
if restored: # no mail, should raises attribute error:
|
||||||
self.assertRaises(AttributeError, lambda: self.smtpd.mailfrom)
|
self.assertRaises(AttributeError, lambda: self.smtpd.mailfrom)
|
||||||
return
|
return
|
||||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||||
subject = "Subject: [Fail2Ban] %s: banned %s" % (
|
subject = "Subject: [Fail2Ban] %s: banned %s" % (
|
||||||
self.jail.name, aInfo['ip'])
|
self.jail.name, aInfo['ip'])
|
||||||
self.assertIn(subject, self.smtpd.data)
|
self.assertIn(subject, self.smtpd.data)
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"%i attempts" % aInfo['failures'], self.smtpd.data)
|
"%i attempts" % aInfo['failures'], self.smtpd.data)
|
||||||
|
|
||||||
self.action.matches = "matches"
|
self.action.matches = "matches"
|
||||||
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
||||||
self.assertIn(aInfo['matches'], self.smtpd.data)
|
self.assertIn(aInfo['matches'], self.smtpd.data)
|
||||||
|
|
||||||
self.action.matches = "ipjailmatches"
|
self.action.matches = "ipjailmatches"
|
||||||
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
||||||
self.assertIn(aInfo['ipjailmatches'], self.smtpd.data)
|
self.assertIn(aInfo['ipjailmatches'], self.smtpd.data)
|
||||||
|
|
||||||
self.action.matches = "ipmatches"
|
self.action.matches = "ipmatches"
|
||||||
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
self._exec_and_wait(lambda: self.action.ban(aInfo))
|
||||||
self.assertIn(aInfo['ipmatches'], self.smtpd.data)
|
self.assertIn(aInfo['ipmatches'], self.smtpd.data)
|
||||||
|
|
||||||
def testBan(self):
|
def testBan(self):
|
||||||
self._testBan()
|
self._testBan()
|
||||||
|
|
||||||
def testNOPByRestored(self):
|
def testNOPByRestored(self):
|
||||||
self._testBan(restored=True)
|
self._testBan(restored=True)
|
||||||
|
|
||||||
def testOptions(self):
|
def testOptions(self):
|
||||||
self._exec_and_wait(self.action.start)
|
self._exec_and_wait(self.action.start)
|
||||||
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
self.assertEqual(self.smtpd.mailfrom, "fail2ban")
|
||||||
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
self.assertEqual(self.smtpd.rcpttos, ["root"])
|
||||||
|
|
||||||
self.action.fromname = "Test"
|
self.action.fromname = "Test"
|
||||||
self.action.fromaddr = "test@example.com"
|
self.action.fromaddr = "test@example.com"
|
||||||
self.action.toaddr = "test@example.com, test2@example.com"
|
self.action.toaddr = "test@example.com, test2@example.com"
|
||||||
self._exec_and_wait(self.action.start)
|
self._exec_and_wait(self.action.start)
|
||||||
self.assertEqual(self.smtpd.mailfrom, "test@example.com")
|
self.assertEqual(self.smtpd.mailfrom, "test@example.com")
|
||||||
self.assertTrue("From: %s <%s>" %
|
self.assertTrue("From: %s <%s>" %
|
||||||
(self.action.fromname, self.action.fromaddr) in self.smtpd.data)
|
(self.action.fromname, self.action.fromaddr) in self.smtpd.data)
|
||||||
self.assertEqual(set(self.smtpd.rcpttos), set(["test@example.com", "test2@example.com"]))
|
self.assertEqual(set(self.smtpd.rcpttos), set(["test@example.com", "test2@example.com"]))
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
print("I: Skipping smtp tests: %s" % e)
|
||||||
|
|
|
@ -221,7 +221,7 @@ class Fail2banRegexTest(LogCaptureTestCase):
|
||||||
self.pruneLog()
|
self.pruneLog()
|
||||||
self.assertTrue(_test_exec(
|
self.assertTrue(_test_exec(
|
||||||
"-d", "^Epoch",
|
"-d", "^Epoch",
|
||||||
"1490349000 test failed.dns.ch", "^\s*test <F-ID>\S+</F-ID>"
|
"1490349000 test failed.dns.ch", r"^\s*test <F-ID>\S+</F-ID>"
|
||||||
))
|
))
|
||||||
self.assertLogged('Lines: 1 lines, 0 ignored, 1 matched, 0 missed', all=True)
|
self.assertLogged('Lines: 1 lines, 0 ignored, 1 matched, 0 missed', all=True)
|
||||||
self.assertNotLogged('Unable to find a corresponding IP address')
|
self.assertNotLogged('Unable to find a corresponding IP address')
|
||||||
|
@ -229,7 +229,7 @@ class Fail2banRegexTest(LogCaptureTestCase):
|
||||||
self.pruneLog()
|
self.pruneLog()
|
||||||
self.assertTrue(_test_exec(
|
self.assertTrue(_test_exec(
|
||||||
"-d", "^Epoch", "-o", "id",
|
"-d", "^Epoch", "-o", "id",
|
||||||
"1490349000 test this/is/some/path/32", "^\s*test <F-ID>\S+</F-ID>"
|
"1490349000 test this/is/some/path/32", r"^\s*test <F-ID>\S+</F-ID>"
|
||||||
))
|
))
|
||||||
self.assertLogged('this/is/some/path/32', all=True)
|
self.assertLogged('this/is/some/path/32', all=True)
|
||||||
|
|
||||||
|
@ -439,23 +439,23 @@ class Fail2banRegexTest(LogCaptureTestCase):
|
||||||
# with different ID/IP from failregex (ID/User from first, IP from second message):
|
# with different ID/IP from failregex (ID/User from first, IP from second message):
|
||||||
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
||||||
flt+'[failregex="'
|
flt+'[failregex="'
|
||||||
'^'+prefix+'<F-ID>User <F-USER>\S+</F-USER></F-ID> not allowed\n'
|
'^'+prefix+r'<F-ID>User <F-USER>\S+</F-USER></F-ID> not allowed'+'\n'
|
||||||
'^'+prefix+'Received disconnect from <ADDR>'
|
'^'+prefix+r'Received disconnect from <ADDR>'
|
||||||
'"]'))
|
'"]'))
|
||||||
self.assertLogged('ID:"User root" | IP:192.0.2.76 | U:root')
|
self.assertLogged('ID:"User root" | IP:192.0.2.76 | U:root')
|
||||||
self.pruneLog()
|
self.pruneLog()
|
||||||
# with different ID/IP from failregex (User from first, ID and IP from second message):
|
# with different ID/IP from failregex (User from first, ID and IP from second message):
|
||||||
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
self.assertTrue(_test('-o', 'ID:"<fid>" | IP:<ip> | U:<F-USER>', log,
|
||||||
flt+'[failregex="'
|
flt+'[failregex="'
|
||||||
'^'+prefix+'User <F-USER>\S+</F-USER> not allowed\n'
|
'^'+prefix+r'User <F-USER>\S+</F-USER> not allowed'+'\n'
|
||||||
'^'+prefix+'Received disconnect from <F-ID><ADDR> port \d+</F-ID>'
|
'^'+prefix+r'Received disconnect from <F-ID><ADDR> port \d+</F-ID>'
|
||||||
'"]'))
|
'"]'))
|
||||||
self.assertLogged('ID:"192.0.2.76 port 58846" | IP:192.0.2.76 | U:root')
|
self.assertLogged('ID:"192.0.2.76 port 58846" | IP:192.0.2.76 | U:root')
|
||||||
self.pruneLog()
|
self.pruneLog()
|
||||||
# first with sshd and prefregex:
|
# first with sshd and prefregex:
|
||||||
_test_variants()
|
_test_variants()
|
||||||
# the same without prefregex and MLFID directly in failregex (no merge with prefregex groups):
|
# the same without prefregex and MLFID directly in failregex (no merge with prefregex groups):
|
||||||
_test_variants('common', prefix="\s*\S+ sshd\[<F-MLFID>\d+</F-MLFID>\]:\s+")
|
_test_variants('common', prefix=r"\s*\S+ sshd\[<F-MLFID>\d+</F-MLFID>\]:\s+")
|
||||||
|
|
||||||
def testNoDateTime(self):
|
def testNoDateTime(self):
|
||||||
# datepattern doesn't match:
|
# datepattern doesn't match:
|
||||||
|
@ -541,7 +541,7 @@ class Fail2banRegexTest(LogCaptureTestCase):
|
||||||
'svc[2] connect started 192.0.2.4\n'
|
'svc[2] connect started 192.0.2.4\n'
|
||||||
'svc[2] connect authorized 192.0.2.4\n'
|
'svc[2] connect authorized 192.0.2.4\n'
|
||||||
'svc[2] connect finished 192.0.2.4\n',
|
'svc[2] connect finished 192.0.2.4\n',
|
||||||
'common[prefregex="^svc\[<F-MLFID>\d+</F-MLFID>\] connect <F-CONTENT>.+</F-CONTENT>$"'
|
r'common[prefregex="^svc\[<F-MLFID>\d+</F-MLFID>\] connect <F-CONTENT>.+</F-CONTENT>$"'
|
||||||
', failregex="'
|
', failregex="'
|
||||||
'^started\n'
|
'^started\n'
|
||||||
'^<F-NOFAIL><F-MLFFORGET>finished</F-MLFFORGET></F-NOFAIL> <ADDR>\n'
|
'^<F-NOFAIL><F-MLFFORGET>finished</F-MLFFORGET></F-NOFAIL> <ADDR>\n'
|
||||||
|
|
|
@ -1372,12 +1372,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
|
||||||
"`{ nft flush set inet f2b-table addr6-set-j-w-nft-mp 2> /dev/null; } || ",
|
"`{ nft flush set inet f2b-table addr6-set-j-w-nft-mp 2> /dev/null; } || ",
|
||||||
),
|
),
|
||||||
'stop': (
|
'stop': (
|
||||||
"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-mp\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-mp\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
||||||
"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
||||||
"`nft delete set inet f2b-table addr-set-j-w-nft-mp`",
|
r"`nft delete set inet f2b-table addr-set-j-w-nft-mp`",
|
||||||
"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-mp\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-mp\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
||||||
"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
||||||
"`nft delete set inet f2b-table addr6-set-j-w-nft-mp`",
|
r"`nft delete set inet f2b-table addr6-set-j-w-nft-mp`",
|
||||||
),
|
),
|
||||||
'ip4-check': (
|
'ip4-check': (
|
||||||
r"`nft list chain inet f2b-table f2b-chain | grep -q '@addr-set-j-w-nft-mp[ \t]'`",
|
r"`nft list chain inet f2b-table f2b-chain | grep -q '@addr-set-j-w-nft-mp[ \t]'`",
|
||||||
|
@ -1418,12 +1418,12 @@ class ServerConfigReaderTests(LogCaptureTestCase):
|
||||||
"`{ nft flush set inet f2b-table addr6-set-j-w-nft-ap 2> /dev/null; } || ",
|
"`{ nft flush set inet f2b-table addr6-set-j-w-nft-ap 2> /dev/null; } || ",
|
||||||
),
|
),
|
||||||
'stop': (
|
'stop': (
|
||||||
"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-ap\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr-set-j-w-nft-ap\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
||||||
"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
||||||
"`nft delete set inet f2b-table addr-set-j-w-nft-ap`",
|
r"`nft delete set inet f2b-table addr-set-j-w-nft-ap`",
|
||||||
"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-ap\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
r"`{ nft -a list chain inet f2b-table f2b-chain | grep -oP '@addr6-set-j-w-nft-ap\s+.*\s+\Khandle\s+(\d+)$'; } | while read -r hdl; do`",
|
||||||
"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
r"`nft delete rule inet f2b-table f2b-chain $hdl; done`",
|
||||||
"`nft delete set inet f2b-table addr6-set-j-w-nft-ap`",
|
r"`nft delete set inet f2b-table addr6-set-j-w-nft-ap`",
|
||||||
),
|
),
|
||||||
'ip4-check': (
|
'ip4-check': (
|
||||||
r"""`nft list chain inet f2b-table f2b-chain | grep -q '@addr-set-j-w-nft-ap[ \t]'`""",
|
r"""`nft list chain inet f2b-table f2b-chain | grep -q '@addr-set-j-w-nft-ap[ \t]'`""",
|
||||||
|
|
Loading…
Reference in New Issue