]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.dnsdist/test_DynBlocksGroup.py
Merge pull request #13917 from Habbie/auth-4.9.0-docs-secpoll
[thirdparty/pdns.git] / regression-tests.dnsdist / test_DynBlocksGroup.py
CommitLineData
8c87daac
RG
1#!/usr/bin/env python
2import base64
3import socket
4import time
5import dns
6from dnsdisttests import DNSDistTest
7from dnsdistDynBlockTests import DynBlocksTest, waitForMaintenanceToRun, _maintenanceWaitTime
8
9class TestDynBlockGroupQPS(DynBlocksTest):
10
11 _config_template = """
12 local dbr = dynBlockRulesGroup()
13 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
14
15 function maintenance()
16 dbr:apply()
17 end
18 newServer{address="127.0.0.1:%s"}
19 webserver("127.0.0.1:%s")
20 setWebserverConfig({password="%s", apiKey="%s"})
21 """
22 _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
23
24 def testDynBlocksQRate(self):
25 """
26 Dyn Blocks (Group): QRate
27 """
28 name = 'qrate.group.dynblocks.tests.powerdns.com.'
29 self.doTestQRate(name)
30
31class TestDynBlockGroupQPSRefused(DynBlocksTest):
32
33 _config_template = """
34 local dbr = dynBlockRulesGroup()
35 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
36
37 function maintenance()
38 dbr:apply()
39 end
40 setDynBlocksAction(DNSAction.Refused)
41 newServer{address="127.0.0.1:%s"}
42 """
43
44 def testDynBlocksQRate(self):
45 """
46 Dyn Blocks (Group): QRate refused
47 """
48 name = 'qraterefused.group.dynblocks.tests.powerdns.com.'
49 self.doTestQRateRCode(name, dns.rcode.REFUSED)
50
51class TestDynBlockGroupQPSActionRefused(DynBlocksTest):
52
53 _config_template = """
54 local dbr = dynBlockRulesGroup()
55 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Refused)
56
57 function maintenance()
58 dbr:apply()
59 end
60 setDynBlocksAction(DNSAction.Drop)
61 newServer{address="127.0.0.1:%s"}
62 """
63
64 def testDynBlocksQRate(self):
65 """
66 Dyn Blocks (group): QRate refused (action)
67 """
68 name = 'qrateactionrefused.group.dynblocks.tests.powerdns.com.'
69 self.doTestQRateRCode(name, dns.rcode.REFUSED)
70
71class TestDynBlockGroupExcluded(DynBlocksTest):
72
73 _config_template = """
74 local dbr = dynBlockRulesGroup()
75 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
76 dbr:excludeRange("127.0.0.1/32")
77
78 function maintenance()
79 dbr:apply()
80 end
81
82 newServer{address="127.0.0.1:%s"}
83 """
84
85 def testExcluded(self):
86 """
87 Dyn Blocks (group) : Excluded from the dynamic block rules
88 """
89 name = 'excluded.group.dynblocks.tests.powerdns.com.'
90 query = dns.message.make_query(name, 'A', 'IN')
91 response = dns.message.make_response(query)
92 rrset = dns.rrset.from_text(name,
93 60,
94 dns.rdataclass.IN,
95 dns.rdatatype.A,
96 '192.0.2.1')
97 response.answer.append(rrset)
98
99 allowed = 0
100 sent = 0
101 for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
102 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
103 sent = sent + 1
104 if receivedQuery:
105 receivedQuery.id = query.id
106 self.assertEqual(query, receivedQuery)
107 self.assertEqual(response, receivedResponse)
108 allowed = allowed + 1
109 else:
110 # the query has not reached the responder,
111 # let's clear the response queue
112 self.clearToResponderQueue()
113
114 # we should not have been blocked
115 self.assertEqual(allowed, sent)
116
117 waitForMaintenanceToRun()
118
119 # we should still not be blocked
120 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
121 receivedQuery.id = query.id
122 self.assertEqual(query, receivedQuery)
123 self.assertEqual(receivedResponse, receivedResponse)
124
125class TestDynBlockGroupExcludedViaNMG(DynBlocksTest):
126
127 _config_template = """
128 local nmg = newNMG()
129 nmg:addMask("127.0.0.1/32")
130
131 local dbr = dynBlockRulesGroup()
132 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d)
133 dbr:excludeRange(nmg)
134
135 function maintenance()
136 dbr:apply()
137 end
138
139 newServer{address="127.0.0.1:%s"}
140 """
141
142 def testExcluded(self):
143 """
144 Dyn Blocks (group) : Excluded (via NMG) from the dynamic block rules
145 """
146 name = 'excluded-nmg.group.dynblocks.tests.powerdns.com.'
147 query = dns.message.make_query(name, 'A', 'IN')
148 response = dns.message.make_response(query)
149 rrset = dns.rrset.from_text(name,
150 60,
151 dns.rdataclass.IN,
152 dns.rdatatype.A,
153 '192.0.2.1')
154 response.answer.append(rrset)
155
156 allowed = 0
157 sent = 0
158 for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
159 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
160 sent = sent + 1
161 if receivedQuery:
162 receivedQuery.id = query.id
163 self.assertEqual(query, receivedQuery)
164 self.assertEqual(response, receivedResponse)
165 allowed = allowed + 1
166 else:
167 # the query has not reached the responder,
168 # let's clear the response queue
169 self.clearToResponderQueue()
170
171 # we should not have been blocked
172 self.assertEqual(allowed, sent)
173
174 waitForMaintenanceToRun()
175
176 # we should still not be blocked
177 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
178 receivedQuery.id = query.id
179 self.assertEqual(query, receivedQuery)
180 self.assertEqual(receivedResponse, receivedResponse)
181
182class TestDynBlockGroupNoOp(DynBlocksTest):
183
184 _config_template = """
185 local dbr = dynBlockRulesGroup()
186 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.NoOp)
187
188 function maintenance()
189 dbr:apply()
190 end
191
192 newServer{address="127.0.0.1:%s"}
193 webserver("127.0.0.1:%s")
194 setWebserverConfig({password="%s", apiKey="%s"})
195 """
196 _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
197
198 def testNoOp(self):
199 """
200 Dyn Blocks (group) : NoOp
201 """
202 name = 'noop.group.dynblocks.tests.powerdns.com.'
203 query = dns.message.make_query(name, 'A', 'IN')
204 response = dns.message.make_response(query)
205 rrset = dns.rrset.from_text(name,
206 60,
207 dns.rdataclass.IN,
208 dns.rdatatype.A,
209 '192.0.2.1')
210 response.answer.append(rrset)
211
212 allowed = 0
213 sent = 0
214 for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
215 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
216 sent = sent + 1
217 if receivedQuery:
218 receivedQuery.id = query.id
219 self.assertEqual(query, receivedQuery)
220 self.assertEqual(response, receivedResponse)
221 allowed = allowed + 1
222 else:
223 # the query has not reached the responder,
224 # let's clear the response queue
225 self.clearToResponderQueue()
226
227 # a dynamic rule should have been inserted, but the queries should still go on
228 self.assertEqual(allowed, sent)
229
230 waitForMaintenanceToRun()
231
232 # the rule should still be present, but the queries pass through anyway
233 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
234 receivedQuery.id = query.id
235 self.assertEqual(query, receivedQuery)
236 self.assertEqual(receivedResponse, receivedResponse)
237
238 # check that the rule has been inserted
239 self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, 0, sent)
240
241class TestDynBlockGroupWarning(DynBlocksTest):
242
243 _dynBlockWarningQPS = 5
244 _dynBlockQPS = 20
245 _config_template = """
246 local dbr = dynBlockRulesGroup()
247 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Drop, %d)
248
249 function maintenance()
250 dbr:apply()
251 end
252
253 newServer{address="127.0.0.1:%s"}
254 webserver("127.0.0.1:%s")
255 setWebserverConfig({password="%s", apiKey="%s"})
256 """
257 _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_dynBlockWarningQPS', '_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed']
258
259 def testWarning(self):
260 """
261 Dyn Blocks (group) : Warning
262 """
263 name = 'warning.group.dynblocks.tests.powerdns.com.'
264 query = dns.message.make_query(name, 'A', 'IN')
265 response = dns.message.make_response(query)
266 rrset = dns.rrset.from_text(name,
267 60,
268 dns.rdataclass.IN,
269 dns.rdatatype.A,
270 '192.0.2.1')
271 response.answer.append(rrset)
272
273 allowed = 0
274 sent = 0
275 for _ in range((self._dynBlockWarningQPS * self._dynBlockPeriod) + 1):
276 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
277 sent = sent + 1
278 if receivedQuery:
279 receivedQuery.id = query.id
280 self.assertEqual(query, receivedQuery)
281 self.assertEqual(response, receivedResponse)
282 allowed = allowed + 1
283 else:
284 # the query has not reached the responder,
285 # let's clear the response queue
286 self.clearToResponderQueue()
287
288 # a dynamic rule should have been inserted, but the queries should
289 # still go on because we are still at warning level
290 self.assertEqual(allowed, sent)
291
292 waitForMaintenanceToRun()
293
294 # the rule should still be present, but the queries pass through anyway
295 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
296 receivedQuery.id = query.id
297 self.assertEqual(query, receivedQuery)
298 self.assertEqual(receivedResponse, receivedResponse)
299
300 # check that the rule has been inserted
301 self.doTestDynBlockViaAPI('127.0.0.1/32', 'Exceeded query rate', 1, self._dynBlockDuration, 0, sent)
302
303 self.doTestQRate(name)
304
305class TestDynBlockGroupPort(DNSDistTest):
306
307 _dynBlockQPS = 20
308 _dynBlockPeriod = 2
309 # this needs to be greater than maintenanceWaitTime
310 _dynBlockDuration = _maintenanceWaitTime + 1
311 _config_template = """
312 local dbr = dynBlockRulesGroup()
313 dbr:setQueryRate(%d, %d, "Exceeded query rate", %d, DNSAction.Drop)
314 -- take the exact port into account
315 dbr:setMasks(32, 128, 16)
316
317 function maintenance()
318 dbr:apply()
319 end
320 newServer{address="127.0.0.1:%d"}
321 """
322 _config_params = ['_dynBlockQPS', '_dynBlockPeriod', '_dynBlockDuration', '_testServerPort']
323
324 def testPort(self):
325 """
326 Dyn Blocks (group): Exact port matching
327 """
328 name = 'port.group.dynblocks.tests.powerdns.com.'
329 query = dns.message.make_query(name, 'A', 'IN')
330 response = dns.message.make_response(query)
331 rrset = dns.rrset.from_text(name,
332 60,
333 dns.rdataclass.IN,
334 dns.rdatatype.A,
335 '192.0.2.1')
336 response.answer.append(rrset)
337
338 allowed = 0
339 sent = 0
340 for _ in range((self._dynBlockQPS * self._dynBlockPeriod) + 1):
341 (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
342 sent = sent + 1
343 if receivedQuery:
344 receivedQuery.id = query.id
345 self.assertEqual(query, receivedQuery)
346 self.assertEqual(response, receivedResponse)
347 allowed = allowed + 1
348 else:
349 # the query has not reached the responder,
350 # let's clear the response queue
351 self.clearToResponderQueue()
352
353 # we might be already blocked, but we should have been able to send
354 # at least self._dynBlockQPS queries
355 self.assertGreaterEqual(allowed, self._dynBlockQPS)
356
357 if allowed == sent:
358 waitForMaintenanceToRun()
359
360 # we should now be dropped for up to self._dynBlockDuration + self._dynBlockPeriod
361 (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
362 self.assertEqual(receivedResponse, None)
363
364 # use a new socket, so a new port
365 self._toResponderQueue.put(response, True, 1.0)
366 newsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
367 newsock.settimeout(1.0)
368 newsock.connect(("127.0.0.1", self._dnsDistPort))
369 newsock.send(query.to_wire())
370 receivedResponse = newsock.recv(4096)
371 if receivedResponse:
372 receivedResponse = dns.message.from_wire(receivedResponse)
373 receivedQuery = self._fromResponderQueue.get(True, 1.0)
374 receivedQuery.id = query.id
375 self.assertEqual(query, receivedQuery)
376 self.assertEqual(response, receivedResponse)