From: Otto Moerbeek Date: Thu, 8 May 2025 12:05:39 +0000 (+0200) Subject: Run all ECS tests also in hardened mode and add a specific hardened mode test X-Git-Tag: rec-5.1.6^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=31f8514d70c5b35aa19259af2c6018cc58d39ce9;p=thirdparty%2Fpdns.git Run all ECS tests also in hardened mode and add a specific hardened mode test --- diff --git a/regression-tests.recursor-dnssec/test_ECS.py b/regression-tests.recursor-dnssec/test_ECS.py index e3934e3ab6..dde1dfe8ea 100644 --- a/regression-tests.recursor-dnssec/test_ECS.py +++ b/regression-tests.recursor-dnssec/test_ECS.py @@ -36,7 +36,6 @@ ecs-add-for=0.0.0.0/0 def sendECSQuery(self, query, expected, expectedFirstTTL=None, scopeZeroResponse=None): res = self.sendUDPQuery(query) - self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) # this will break if you are not looking for the first RR, sorry! @@ -141,7 +140,106 @@ forward-zones=ecs-echo.example=%s.21 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) -class testIncomingNoECS(ECSTest): +class NoECSHardenedTest(NoECSTest): + _confdir = 'NoECSHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list= +forward-zones=ecs-echo.example=%s.21 + """ % (os.environ['PREFIX']) + +class NoECSInAnswerTest(ECSTest): + _confdir = 'NoECSInAnswer' + + _wsPort = 8042 + _wsTimeout = 2 + _wsPassword = 'secretpassword' + _apiKey = 'secretapikey' + _config_template = """edns-subnet-allow-list=xecs-echo.example +forward-zones=xecs-echo.example=%s.21 +webserver=yes +webserver-port=%d +webserver-address=127.0.0.1 +webserver-password=%s +webserver-allow-from=127.0.0.1 +api-key=%s + """ % (os.environ['PREFIX'], _wsPort, _wsPassword, _apiKey) + + def test1SendECS(self): + expected = dns.rrset.from_text('x'+ nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X') + ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) + query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) + self.sendECSQuery(query, expected) + self.checkMetrics({ + 'ecs-missing': 0 + }) + + def test2NoECS(self): + expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X') + query = dns.message.make_query('x' + nameECS, 'TXT') + self.sendECSQuery(query, expected) + self.checkMetrics({ + 'ecs-missing': 0 + }) + + def test3RequireNoECS(self): + expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X') + + ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0) + query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) + self.sendECSQuery(query, expected) + self.checkMetrics({ + 'ecs-missing': 0 + }) + +class NoECSInAnswerHardenedTest(NoECSInAnswerTest): + _confdir = 'NoECSInAnswerHardened' + + _wsPort = 8042 + _wsTimeout = 2 + _wsPassword = 'secretpassword' + _apiKey = 'secretapikey' + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=xecs-echo.example +forward-zones=xecs-echo.example=%s.21 +webserver=yes +webserver-port=%d +webserver-address=127.0.0.1 +webserver-password=%s +webserver-allow-from=127.0.0.1 +api-key=%s + """ % (os.environ['PREFIX'], _wsPort, _wsPassword, _apiKey) + + # All test below have ecs-missing count to be 1, as they result is a no ecs scoped answer in the cache + def test1SendECS(self): + expected = dns.rrset.from_text('x'+ nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X') + ecso = clientsubnetoption.ClientSubnetOption('192.0.2.1', 32) + query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) + self.sendECSQuery(query, expected) + self.checkMetrics({ + 'ecs-missing': 1 + }) + + def test2NoECS(self): + expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X') + query = dns.message.make_query('x' + nameECS, 'TXT') + self.sendECSQuery(query, expected) + self.checkMetrics({ + 'ecs-missing': 1 + }) + + def test3RequireNoECS(self): + expected = dns.rrset.from_text('x' + nameECS, ttlECS, dns.rdataclass.IN, 'TXT', 'X') + ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0) + query = dns.message.make_query('x' + nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) + self.sendECSQuery(query, expected) + self.checkMetrics({ + 'ecs-missing': 1 + }) + +class IncomingNoECSTest(ECSTest): _confdir = 'IncomingNoECS' _config_template = """edns-subnet-allow-list= @@ -169,7 +267,17 @@ forward-zones=ecs-echo.example=%s.21 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected, scopeZeroResponse=True) -class testECSByName(ECSTest): +class IncomingNoECSHardenedTest(IncomingNoECSTest): + _confdir = 'IncomingNoECSHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list= +use-incoming-edns-subnet=yes +forward-zones=ecs-echo.example=%s.21 + """ % (os.environ['PREFIX']) + +class ECSByNameTest(ECSTest): _confdir = 'ECSByName' _config_template = """edns-subnet-allow-list=ecs-echo.example. @@ -234,8 +342,20 @@ ecs-ipv6-cache-bits=128 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) -class testECSByNameSmaller(ECSTest): - _confdir = 'ECSByNameLarger' +class ECSByNameLargerHardenedTest(ECSByNameLargerTest): + _confdir = 'ECSByNameLargerHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=ecs-echo.example. +ecs-ipv4-bits=32 +forward-zones=ecs-echo.example=%s.21 +ecs-ipv4-cache-bits=32 +ecs-ipv6-cache-bits=128 + """ % (os.environ['PREFIX']) + +class ECSByNameSmallerTest(ECSTest): + _confdir = 'ECSByNameSmaller' _config_template = """edns-subnet-allow-list=ecs-echo.example. ecs-ipv4-bits=16 @@ -261,8 +381,18 @@ forward-zones=ecs-echo.example=%s.21 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) -class testIncomingECSByName(ECSTest): - _confdir = 'ECSIncomingByName' +class ECSByNameSmallerHardenedTest(ECSByNameSmallerTest): + _confdir = 'ECSByNameSmallerHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=ecs-echo.example. +ecs-ipv4-bits=16 +forward-zones=ecs-echo.example=%s.21 + """ % (os.environ['PREFIX']) + +class IncomingECSByNameTest(ECSTest): + _confdir = 'IncomingECSByName' _config_template = """edns-subnet-allow-list=ecs-echo.example. use-incoming-edns-subnet=yes @@ -301,8 +431,21 @@ ecs-ipv6-cache-bits=128 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected, ttlECS) -class testIncomingECSByNameLarger(ECSTest): - _confdir = 'ECSIncomingByNameLarger' +class IncomingECSByNameHardenedTest(IncomingECSByNameTest): + _confdir = 'IncomingECSByNameHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=ecs-echo.example. +use-incoming-edns-subnet=yes +forward-zones=ecs-echo.example=%s.21 +ecs-scope-zero-address=2001:db8::42 +ecs-ipv4-cache-bits=32 +ecs-ipv6-cache-bits=128 + """ % (os.environ['PREFIX']) + +class IncomingECSByNameLargerTest(ECSTest): + _confdir = 'IncomingECSByNameLarger' _config_template = """edns-subnet-allow-list=ecs-echo.example. use-incoming-edns-subnet=yes @@ -333,8 +476,22 @@ ecs-ipv6-cache-bits=128 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected, ttlECS) -class testIncomingECSByNameSmaller(ECSTest): - _confdir = 'ECSIncomingByNameSmaller' +class IncomingECSByNameLargerHardenedTest(IncomingECSByNameLargerTest): + _confdir = 'IncomingECSByNameLargerHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=ecs-echo.example. +use-incoming-edns-subnet=yes +ecs-ipv4-bits=32 +forward-zones=ecs-echo.example=%s.21 +ecs-scope-zero-address=192.168.0.1 +ecs-ipv4-cache-bits=32 +ecs-ipv6-cache-bits=128 + """ % (os.environ['PREFIX']) + +class IncomingECSByNameSmallerTest(ECSTest): + _confdir = 'IncomingECSByNameSmaller' _config_template = """edns-subnet-allow-list=ecs-echo.example. use-incoming-edns-subnet=yes @@ -363,6 +520,20 @@ ecs-ipv6-cache-bits=128 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected, ttlECS) +class IncomingECSByNameSmallerHardenedTest(IncomingECSByNameSmallerTest): + _confdir = 'IncomingECSByNameSmallerHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=ecs-echo.example. +use-incoming-edns-subnet=yes +ecs-ipv4-bits=16 +forward-zones=ecs-echo.example=%s.21 +ecs-scope-zero-address=192.168.0.1 +ecs-ipv4-cache-bits=32 +ecs-ipv6-cache-bits=128 + """ % (os.environ['PREFIX']) + @unittest.skipIf(not have_ipv6(), "No IPv6") class testIncomingECSByNameV6(ECSTest): _confdir = 'ECSIncomingByNameV6' @@ -422,7 +593,22 @@ forward-zones=ecs-echo.example=%s.21 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) -class testECSByIP(ECSTest): +@unittest.skipIf(not have_ipv6(), "No IPv6") +class IncomingECSByNameV6HardenedTest(IncomingECSByNameV6Test): + _confdir = 'IncomingECSByNameV6Hardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=ecs-echo.example. +use-incoming-edns-subnet=yes +ecs-ipv6-bits=128 +ecs-ipv4-cache-bits=32 +ecs-ipv6-cache-bits=128 +query-local-address=::1 +forward-zones=ecs-echo.example=[::1]:53000 + """ + +class ECSByIPTest(ECSTest): _confdir = 'ECSByIP' _config_template = """edns-subnet-allow-list=%s.21 @@ -448,8 +634,17 @@ forward-zones=ecs-echo.example=%s.21 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) -class testIncomingECSByIP(ECSTest): - _confdir = 'ECSIncomingByIP' +class ECSByIPHardenedTest(ECSByIPTest): + _confdir = 'ECSByIPHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=%s.21 +forward-zones=ecs-echo.example=%s.21 + """ % (os.environ['PREFIX'], os.environ['PREFIX']) + +class IncomingECSByIPTest(ECSTest): + _confdir = 'IncomingECSByIP' _config_template = """edns-subnet-allow-list=%s.21 use-incoming-edns-subnet=yes @@ -488,7 +683,20 @@ ecs-ipv6-cache-bits=128 self.sendECSQuery(query, expected) -class testECSIPMismatch(ECSTest): +class IncomingECSByIPHardenedTest(IncomingECSByIPTest): + _confdir = 'IncomingECSByIPHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=%s.21 +use-incoming-edns-subnet=yes +forward-zones=ecs-echo.example=%s.21 +ecs-scope-zero-address=::1 +ecs-ipv4-cache-bits=32 +ecs-ipv6-cache-bits=128 + """ % (os.environ['PREFIX'], os.environ['PREFIX']) + +class ECSIPMismatchTest(ECSTest): _confdir = 'ECSIPMismatch' _config_template = """edns-subnet-allow-list=192.0.2.1 @@ -514,8 +722,17 @@ forward-zones=ecs-echo.example=%s.21 query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512) self.sendECSQuery(query, expected) -class testECSWithProxyProtocoldRecursorTest(ECSTest): - _confdir = 'ECSWithProxyProtocol' +class ECSIPMismatchHardenedTest(ECSIPMismatchTest): + _confdir = 'ECSIPMismatchHardened' + + _config_template = """ +edns-subnet-harden=yes +edns-subnet-allow-list=192.0.2.1 +forward-zones=ecs-echo.example=%s.21 + """ % (os.environ['PREFIX']) + +class ECSWithProxyProtocolRecursorTest(ECSTest): + _confdir = 'ECSWithProxyProtocolRecursor' _config_template = """ ecs-add-for=2001:db8::1/128 edns-subnet-allow-list=ecs-echo.example. @@ -535,7 +752,18 @@ class testECSWithProxyProtocoldRecursorTest(ECSTest): self.assertRcodeEqual(res, dns.rcode.NOERROR) self.assertRRsetInAnswer(res, expected) -class testTooLargeToAddZeroScope(RecursorTest): +class ECSWithProxyProtocolRecursorHardenedTest(ECSWithProxyProtocolRecursorTest): + _confdir = 'ECSWithProxyProtocolRecursorHardened' + _config_template = """ + edns-subnet-harden=yes + ecs-add-for=2001:db8::1/128 + edns-subnet-allow-list=ecs-echo.example. + forward-zones=ecs-echo.example=%s.21 + proxy-protocol-from=127.0.0.1/32 + allow-from=2001:db8::1/128 +""" % (os.environ['PREFIX']) + +class TooLargeToAddZeroScopeTest(RecursorTest): _confdir = 'TooLargeToAddZeroScope' _config_template = """ @@ -637,6 +865,10 @@ class UDPECSResponder(DatagramProtocol): additional = dns.rrset.from_text('ns1.ecs-echo.example.', 15, dns.rdataclass.IN, 'A', os.environ['PREFIX'] + '.21') response.additional.append(additional) + elif request.question[0].name == dns.name.from_text('x' + nameECS): + answer = dns.rrset.from_text(request.question[0].name, ttlECS, dns.rdataclass.IN, 'TXT', 'X') + response.answer.append(answer) + if ecso: response.use_edns(options = [ecso])