Add xfr quota starvation system test

Add a starvation test that tries to starve the XFR quota with
unautorized requests.

(cherry picked from commit 53135592b7ff6c272b6577b2e7747258628442e3)
This commit is contained in:
Alessio Podda 2026-04-13 15:55:38 +02:00 committed by Michał Kępień
parent 9e5a52e6fa
commit 1ba67b6055
No known key found for this signature in database
5 changed files with 133 additions and 0 deletions

View file

@ -22,6 +22,8 @@ options {
recursion no;
dnssec-validation no;
notify yes;
transfers-out 3;
};
key rndc_key {

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion no;
dnssec-validation no;
transfers-out 1;
allow-transfer { 10.53.0.2; };
};
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "." {
type primary;
file "root.db";
};
zone "quota." {
type primary;
file "quota.db";
};

View file

@ -0,0 +1,22 @@
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
;
; SPDX-License-Identifier: MPL-2.0
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, you can obtain one at https://mozilla.org/MPL/2.0/.
;
; See the COPYRIGHT file distributed with this work for additional
; information regarding copyright ownership.
$TTL 300
@ IN SOA ns1.quota. hostmaster.quota. (
1 ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
600 ; minimum
)
IN NS ns1.quota.
ns1 IN A 10.53.0.3
www IN A 10.0.0.1

View file

@ -0,0 +1,21 @@
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
;
; SPDX-License-Identifier: MPL-2.0
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, you can obtain one at https://mozilla.org/MPL/2.0/.
;
; See the COPYRIGHT file distributed with this work for additional
; information regarding copyright ownership.
$TTL 300
. IN SOA ns.root. hostmaster.root. (
1 ; serial
3600 ; refresh
1800 ; retry
604800 ; expire
600 ; minimum
)
. NS a.root-servers.nil.
a.root-servers.nil. A 10.53.0.3

View file

@ -10,6 +10,7 @@
# information regarding copyright ownership.
import glob
import multiprocessing
import os
import re
from re import compile as Re
@ -18,6 +19,8 @@ import signal
import time
import dns.message
import dns.query
import dns.zone
import pytest
import isctest
@ -59,6 +62,9 @@ def test_xferquota(named_port, servers):
matching_line_count += 1
return matching_line_count == 300
# The primary has 'transfers-out 3;', while the secondary has
# 'transfers-in 5; transfer-per-ns 5;'. This will allow all the zones
# to be eventually transferred, hitting the quotas now and then.
isctest.run.retry_with_timeout(check_line_count, timeout=360)
axfr_msg = isctest.query.create("zone000099.example.", "AXFR")
@ -79,3 +85,39 @@ def test_xferquota(named_port, servers):
with servers["ns2"].watch_log_from_start(timeout=30) as watcher:
watcher.wait_for_line(pattern)
query_and_compare(a_msg)
def _flood_unauthorized_axfrs(port, duration):
"""Child process: send unauthorized AXFR requests for `duration` seconds."""
deadline = time.monotonic() + duration
while time.monotonic() < deadline:
try:
msg = dns.message.make_query("quota.", "AXFR")
dns.query.tcp(msg, "10.53.0.3", port=port, timeout=2, source="10.53.0.1")
except Exception: # pylint: disable=broad-exception-caught
pass
def test_xfrquota_unauthorized_no_starve(named_port):
"""Unauthorized AXFR clients must not consume XFR-out quota (GL #3859).
ns3 is configured with transfers-out 1 and allow-transfer { 10.53.0.2; }.
We flood AXFR requests from unauthorized source processes (10.53.0.1) and
verify that an authorized client (10.53.0.2) can still transfer.
"""
with multiprocessing.Pool(10) as pool:
pool.starmap_async(_flood_unauthorized_axfrs, [(named_port, 5)] * 10)
# Give the flood a moment to saturate
time.sleep(1)
# Try an authorized AXFR from 10.53.0.2 multiple times to increase
# the chance of hitting the race window where quota is consumed.
zone = dns.zone.Zone("quota.")
dns.query.inbound_xfr(
"10.53.0.3",
zone,
port=named_port,
timeout=10,
source="10.53.0.2",
)