]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.dnsdist/test_DynBlocks.py
Merge pull request #13770 from Assumeru/require-tsig
[thirdparty/pdns.git] / regression-tests.dnsdist / test_DynBlocks.py
CommitLineData
d354e773 1#!/usr/bin/env python
87b0577d 2import base64
aa306c70 3import socket
d354e773
RG
4import time
5import dns
8c87daac
RG
6from dnsdisttests import DNSDistTest
7from dnsdistDynBlockTests import DynBlocksTest, waitForMaintenanceToRun, _maintenanceWaitTime
d354e773 8
8c87daac 9class TestDynBlockQPS(DynBlocksTest):
dc2fd93a 10
dc2fd93a 11 _config_template = """
dc2fd93a 12 function maintenance()
8c87daac 13 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
dc2fd93a 14 end
dc2fd93a 15 newServer{address="127.0.0.1:%s"}
8c87daac
RG
16 webserver("127.0.0.1:%s")
17 setWebserverConfig({password="%s", apiKey="%s"})
dc2fd93a 18 """
8c87daac 19 _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
dc2fd93a 20
8c87daac 21 def testDynBlocksQRate(self):
dc2fd93a 22 """
8c87daac 23 Dyn Blocks: QRate
dc2fd93a 24 """
8c87daac
RG
25 name = 'qrate.dynblocks.tests.powerdns.com.'
26 self.doTestQRate(name)
dc2fd93a 27
8c87daac 28class TestDynBlockQPSRefused(DynBlocksTest):
94c38f24 29
94c38f24 30 _config_template = """
94c38f24 31 function maintenance()
8c87daac 32 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d)
94c38f24 33 end
8c87daac 34 setDynBlocksAction(DNSAction.Refused)
94c38f24
RG
35 newServer{address="127.0.0.1:%s"}
36 """
37
8c87daac 38 def testDynBlocksQRate(self):
94c38f24 39 """
8c87daac 40 Dyn Blocks: QRate refused
94c38f24 41 """
8c87daac
RG
42 name = 'qraterefused.dynblocks.tests.powerdns.com.'
43 self.doTestQRateRCode(name, dns.rcode.REFUSED)
94c38f24 44
8c87daac 45class TestDynBlockQPSActionRefused(DynBlocksTest):
dc2fd93a 46
dc2fd93a 47 _config_template = """
dc2fd93a 48 function maintenance()
8c87daac 49 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Refused)
dc2fd93a 50 end
8c87daac 51 setDynBlocksAction(DNSAction.Drop)
dc2fd93a
RG
52 newServer{address="127.0.0.1:%s"}
53 """
54
8c87daac 55 def testDynBlocksQRate(self):
dc2fd93a 56 """
8c87daac 57 Dyn Blocks: QRate refused (action)
dc2fd93a 58 """
8c87daac
RG
59 name = 'qrateactionrefused.dynblocks.tests.powerdns.com.'
60 self.doTestQRateRCode(name, dns.rcode.REFUSED)
dc2fd93a 61
8c87daac 62class TestDynBlockQPSActionNXD(DynBlocksTest):
dc2fd93a 63
dc2fd93a 64 _config_template = """
dc2fd93a 65 function maintenance()
8c87daac 66 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Nxdomain)
dc2fd93a 67 end
8c87daac 68 setDynBlocksAction(DNSAction.Drop)
dc2fd93a
RG
69 newServer{address="127.0.0.1:%s"}
70 """
71
8c87daac 72 def testDynBlocksQRate(self):
dc2fd93a 73 """
8c87daac 74 Dyn Blocks: QRate NXD (action)
dc2fd93a 75 """
8c87daac
RG
76 name = 'qrateactionnxd.dynblocks.tests.powerdns.com.'
77 self.doTestQRateRCode(name, dns.rcode.NXDOMAIN)
b718792f 78
8c87daac 79class TestDynBlockQPSActionTruncated(DNSDistTest):
b718792f
RG
80
81 _dynBlockQPS = 10
82 _dynBlockPeriod = 2
8c87daac
RG
83 # this needs to be greater than maintenanceWaitTime
84 _dynBlockDuration = _maintenanceWaitTime + 1
477c86a0 85 _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
b718792f 86 _config_template = """
b718792f 87 function maintenance()
8c87daac 88 addDynBlocks(exceedQRate(%d, %d), "Exceeded query rate", %d, DNSAction.Truncate)
b718792f 89 end
8c87daac 90 setDynBlocksAction(DNSAction.Drop)
b718792f
RG
91 newServer{address="127.0.0.1:%s"}
92 """
93
8c87daac 94 def testDynBlocksQRate(self):
b718792f 95 """
8c87daac 96 Dyn Blocks: QRate truncated (action)
b718792f 97 """
8c87daac 98 name = 'qrateactiontruncated.dynblocks.tests.powerdns.com.'
b718792f 99 query = dns.message.make_query(name, 'A', 'IN')
8c87daac
RG
100 # dnsdist sets RA = RD for TC responses
101 query.flags &= ~dns.flags.RD
b718792f
RG
102 response = dns.message.make_response(query)
103 rrset = dns.rrset.from_text(name,
104 60,
105 dns.rdataclass.IN,
106 dns.rdatatype.A,
107 '192.0.2.1')
5a2f3287 108 response.answer.append(rrset)
8c87daac
RG
109 truncatedResponse = dns.message.make_response(query)
110 truncatedResponse.flags |= dns.flags.TC
5a2f3287
RG
111
112 allowed = 0
113 sent = 0
114 for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
115 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
116 sent = sent + 1
117 if receivedQuery:
118 receivedQuery.id = query.id
4bfebc93 119 self.assertEqual(query, receivedQuery)
8c87daac 120 self.assertEqual(receivedResponse, response)
5a2f3287
RG
121 allowed = allowed + 1
122 else:
8c87daac 123 self.assertEqual(receivedResponse, truncatedResponse)
5a2f3287
RG
124 # the query has not reached the responder,
125 # let's clear the response queue
126 self.clearToResponderQueue()
127
8c87daac
RG
128 # we might be already truncated, but we should have been able to send
129 # at least self._dynBlockQPS queries
130 self.assertGreaterEqual(allowed, self._dynBlockQPS)
5a2f3287 131
8c87daac
RG
132 if allowed == sent:
133 waitForMaintenanceToRun()
5a2f3287 134
8c87daac
RG
135 # we should now be 'truncated' for up to self._dynBlockDuration + self._dynBlockPeriod
136 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
137 self.assertEqual(receivedResponse, truncatedResponse)
b718792f 138
8c87daac
RG
139 # check over TCP, which should not be truncated
140 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
b718792f 141
8c87daac
RG
142 receivedQuery.id = query.id
143 self.assertEqual(query, receivedQuery)
144 self.assertEqual(receivedResponse, response)
b718792f 145
8c87daac
RG
146 # wait until we are not blocked anymore
147 time.sleep(self._dynBlockDuration + self._dynBlockPeriod)
b718792f 148
8c87daac 149 # this one should succeed
b718792f
RG
150 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
151 receivedQuery.id = query.id
4bfebc93 152 self.assertEqual(query, receivedQuery)
8c87daac 153 self.assertEqual(response, receivedResponse)
477c86a0
RG
154
155 allowed = 0
156 sent = 0
8c87daac 157 # again, over TCP this time, we should never get truncated!
477c86a0 158 for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
8c87daac 159 (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
477c86a0 160 sent = sent + 1
8c87daac
RG
161 receivedQuery.id = query.id
162 self.assertEqual(query, receivedQuery)
163 self.assertEqual(receivedResponse, response)
164 receivedQuery.id = query.id
165 allowed = allowed + 1
477c86a0 166
477c86a0
RG
167 self.assertEqual(allowed, sent)
168
8c87daac 169class TestDynBlockAllowlist(DynBlocksTest):
1d3ba133 170
1d3ba133 171 _config_template = """
8c87daac 172 allowlisted = false
1d3ba133 173 function maintenance()
8c87daac
RG
174 toBlock = exceedQRate(%d, %d)
175 for addr, count in pairs(toBlock) do
176 if tostring(addr) == "127.0.0.1" then
177 allowlisted = true
178 toBlock[addr] = nil
179 end
180 end
181 addDynBlocks(toBlock, "Exceeded query rate", %d)
182 end
183
184 function spoofrule(dq)
185 if (allowlisted)
186 then
187 return DNSAction.Spoof, "192.0.2.42"
188 else
189 return DNSAction.None, ""
190 end
1d3ba133 191 end
8c87daac 192 addAction("allowlisted-test.dynblocks.tests.powerdns.com.", LuaAction(spoofrule))
1d3ba133
RG
193
194 newServer{address="127.0.0.1:%s"}
1d3ba133
RG
195 """
196
8c87daac 197 def testAllowlisted(self):
1d3ba133 198 """
8c87daac 199 Dyn Blocks: Allowlisted from the dynamic blocks
1d3ba133 200 """
8c87daac 201 name = 'allowlisted.dynblocks.tests.powerdns.com.'
1d3ba133
RG
202 query = dns.message.make_query(name, 'A', 'IN')
203 response = dns.message.make_response(query)
204 rrset = dns.rrset.from_text(name,
205 60,
206 dns.rdataclass.IN,
207 dns.rdatatype.A,
208 '192.0.2.1')
209 response.answer.append(rrset)
210
211 allowed = 0
212 sent = 0
8c87daac 213 for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
1d3ba133
RG
214 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
215 sent = sent + 1
216 if receivedQuery:
217 receivedQuery.id = query.id
4bfebc93
CH
218 self.assertEqual(query, receivedQuery)
219 self.assertEqual(response, receivedResponse)
1d3ba133
RG
220 allowed = allowed + 1
221 else:
222 # the query has not reached the responder,
223 # let's clear the response queue
224 self.clearToResponderQueue()
225
8c87daac 226 # we should not have been blocked
1d3ba133
RG
227 self.assertEqual(allowed, sent)
228
8c87daac 229 waitForMaintenanceToRun()
1d3ba133 230
8c87daac 231 # we should still not be blocked
1d3ba133
RG
232 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
233 receivedQuery.id = query.id
4bfebc93
CH
234 self.assertEqual(query, receivedQuery)
235 self.assertEqual(receivedResponse, receivedResponse)
1d3ba133 236
8c87daac
RG
237 # check that we would have been blocked without the allowlisting
238 name = 'allowlisted-test.dynblocks.tests.powerdns.com.'
aa306c70 239 query = dns.message.make_query(name, 'A', 'IN')
8c87daac
RG
240 # dnsdist set RA = RD for spoofed responses
241 query.flags &= ~dns.flags.RD
242 expectedResponse = dns.message.make_response(query)
aa306c70
RG
243 rrset = dns.rrset.from_text(name,
244 60,
245 dns.rdataclass.IN,
246 dns.rdatatype.A,
8c87daac
RG
247 '192.0.2.42')
248 expectedResponse.answer.append(rrset)
aa306c70 249 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
8c87daac 250 self.assertEqual(receivedResponse, expectedResponse)