]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
test-extra: add test for XFR over QUIC
authorDaniel Salzman <daniel.salzman@nic.cz>
Wed, 19 Apr 2023 17:28:27 +0000 (19:28 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Tue, 23 May 2023 08:42:01 +0000 (10:42 +0200)
tests-extra/tests/quic/xfr/test.py [new file with mode: 0644]
tests-extra/tools/dnstest/server.py
tests-extra/tools/dnstest/test.py

diff --git a/tests-extra/tests/quic/xfr/test.py b/tests-extra/tests/quic/xfr/test.py
new file mode 100644 (file)
index 0000000..f06330f
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+'''Test of zone transfers over QUIC.'''
+
+from dnstest.test import Test
+from dnstest.utils import *
+import random
+
+t = Test(quic=True, tsig=True) # TSIG needed to skip weaker ACL rules
+
+master = t.server("knot")
+slave = t.server("knot")
+rnd_zones = t.zone_rnd(1, records=50) + \
+            t.zone_rnd(1, records=500) + \
+            t.zone_rnd(1, records=1000)
+zones = t.zone(".") + rnd_zones
+
+t.link(zones, master, slave)
+
+for z in rnd_zones:
+    master.dnssec(z).enable = True
+
+MSG_DENIED_NOTIFY = "ACL, denied, action notify"
+MSG_DENIED_TRANSFER = "ACL, denied, action transfer"
+MSG_RMT_NOTAUTH = "server responded with error 'NOTAUTH'"
+MSG_RMT_BADCERT = "failed (unknown certificate key)"
+MSG_TSIG_ERROR = "failed (failed to verify TSIG)"
+
+def check_error(server, msg):
+    for i in range(10):
+        if server.log_search(msg):
+            return
+        t.sleep(1)
+    detail_log("Failed log expected")
+    set_err("MISSING ERROR LOG")
+
+def upd_check_zones(master, slave, zones, prev_serials):
+    for z in rnd_zones:
+        master.random_ddns(z, allow_empty=False)
+    serials = slave.zones_wait(zones, prev_serials)
+    t.xfr_diff(master, slave, zones, prev_serials)
+    return serials
+
+try:
+    t.start()
+except Failed as e:
+    stderr = t.out_dir + "/" + str(e).split("'")[1] + "/stderr"
+    with open(stderr) as fstderr:
+        if "QUIC" in fstderr.readline():
+            raise Skip("QUIC support not compiled in")
+    raise e
+
+# Check initial AXFR without cert-key-based authentication
+serials = master.zones_wait(zones)
+slave.zones_wait(zones, serials, equal=True, greater=False)
+if slave.log_search(MSG_TSIG_ERROR):
+    set_err("INCOMPLETE TRANSFER")
+t.xfr_diff(master, slave, zones)
+
+# Check master not authenticated due to bad cert-key
+master.cert_key = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY="
+slave.gen_confile()
+slave.reload()
+master.ctl("zone-notify")
+check_error(master, MSG_RMT_NOTAUTH)
+check_error(slave, MSG_DENIED_NOTIFY)
+slave.ctl("zone-retransfer")
+check_error(slave, MSG_RMT_BADCERT)
+
+# Check IXFR with cert-key-based authenticated master
+master.fill_cert_key()
+slave.gen_confile()
+slave.reload()
+serials = upd_check_zones(master, slave, rnd_zones, serials)
+
+# Check slave not authenticated due to bad cert-key
+slave.cert_key = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY="
+master.gen_confile()
+master.reload()
+master.ctl("zone-notify")
+check_error(master, MSG_RMT_BADCERT)
+slave.ctl("zone-retransfer")
+check_error(slave, MSG_RMT_NOTAUTH)
+check_error(master, MSG_DENIED_TRANSFER)
+
+# Check IXFR with cert-key-based authenticated slave
+slave.fill_cert_key()
+master.gen_confile()
+master.reload()
+serials = upd_check_zones(master, slave, rnd_zones, serials)
+
+t.end()
index d1e87d33eeecb2a05a2a2366f961a379b6cb7be7..2b2569b1fdeca5ca05b530627462b4aeb405c862 100644 (file)
@@ -158,6 +158,8 @@ class Server(object):
         self.addr = None
         self.addr_extra = list()
         self.port = 53 # Needed for keymgr when port not yet generated
+        self.quic_port = None
+        self.cert_key = str()
         self.udp_workers = None
         self.bg_workers = None
         self.fixed_port = False
@@ -517,6 +519,17 @@ class Server(object):
         f.write(self.get_config())
         f.close()
 
+    def fill_cert_key(self):
+        try:
+            out = check_output([self.control_bin] + self.ctl_params + ["status", "cert-key"],
+                               stderr=open(self.dir + "/call.err", mode="a"))
+            key = out.rstrip().decode('ascii')
+            if key != "-":
+                self.cert_key = key
+        except CalledProcessError as e:
+            raise Failed("Can't get certificate key, server='%s', ret='%i'" %
+                         (self.name, e.returncode))
+
     def dig(self, rname, rtype, rclass="IN", udp=None, serial=None, timeout=None,
             tries=3, flags="", bufsize=None, edns=None, nsid=False, dnssec=False,
             log_no_sep=False, tsig=None, addr=None, source=None):
@@ -1295,6 +1308,8 @@ class Knot(Server):
             s.item_str("listen", "%s" % self.addr)
         else:
             s.item_str("listen", "%s@%s" % (self.addr, self.port))
+        if self.quic_port:
+            s.item_str("listen-quic", "%s@%s" % (self.addr, self.quic_port))
         if self.udp_workers:
             s.item_str("udp-workers", self.udp_workers)
         if self.bg_workers:
@@ -1348,10 +1363,16 @@ class Knot(Server):
                         s.begin("remote")
                         have_remote = True
                     s.id_item("id", master.name)
-                    if master.addr.startswith("/"):
-                        s.item_str("address", "%s" % master.addr)
+                    if master.quic_port:
+                        s.item_str("address", "%s@%s" % (master.addr, master.quic_port))
+                        s.item_str("quic", "on")
+                        if master.cert_key:
+                            s.item_str("cert-key", master.cert_key)
                     else:
-                        s.item_str("address", "%s@%s" % (master.addr, master.port))
+                        if master.addr.startswith("/"):
+                            s.item_str("address", "%s" % master.addr)
+                        else:
+                            s.item_str("address", "%s@%s" % (master.addr, master.port))
                     if self.tsig:
                         s.item_str("key", self.tsig.name)
                     if master.no_xfr_edns:
@@ -1363,10 +1384,16 @@ class Knot(Server):
                         s.begin("remote")
                         have_remote = True
                     s.id_item("id", slave.name)
-                    if slave.addr.startswith("/"):
-                        s.item_str("address", "%s" % slave.addr)
+                    if slave.quic_port:
+                        s.item_str("address", "%s@%s" % (slave.addr, slave.quic_port))
+                        s.item_str("quic", "on")
+                        if slave.cert_key:
+                            s.item_str("cert-key", slave.cert_key)
                     else:
-                        s.item_str("address", "%s@%s" % (slave.addr, slave.port))
+                        if slave.addr.startswith("/"):
+                            s.item_str("address", "%s" % slave.addr)
+                        else:
+                            s.item_str("address", "%s@%s" % (slave.addr, slave.port))
                     if self.tsig:
                         s.item_str("key", self.tsig.name)
                     servers.add(slave.name)
@@ -1404,6 +1431,8 @@ class Knot(Server):
                         s.item_str("address", master.addr)
                     if master.tsig:
                         s.item_str("key", master.tsig.name)
+                    if master.cert_key:
+                        s.item_str("cert-key", master.cert_key)
                     s.item("action", "notify")
                     servers.add(master.name)
             for slave in z.slaves:
@@ -1416,6 +1445,8 @@ class Knot(Server):
                     s.item_str("address", slave.addr)
                 if slave.tsig:
                     s.item_str("key", slave.tsig.name)
+                if slave.cert_key:
+                    s.item_str("cert-key", slave.cert_key)
                 s.item("action", "[transfer, update]")
                 servers.add(slave.name)
         s.end()
index 0a06d55e4f5ed3fdb726411bdb9c4ce43323e94f..8fcf6355adb425feec643faba070274796c2cb89 100644 (file)
@@ -31,13 +31,14 @@ class Test(object):
     rel_time = time.time()
     start_time = 0
 
-    def __init__(self, address=None, tsig=None, stress=True):
+    def __init__(self, address=None, tsig=None, stress=True, quic=False):
         if not os.path.exists(Context().out_dir):
             raise Exception("Output directory doesn't exist")
 
         self.out_dir = Context().out_dir
         self.data_dir = Context().test_dir + "/data/"
         self.zones_dir = self.out_dir + "/zones/"
+        self.quic = quic
 
         if address == 4 or address == 6:
             self.addr = Test.LOCAL_ADDR[address]
@@ -196,6 +197,7 @@ class Test(object):
 
             server.port = self._gen_port()
             server.ctlport = self._gen_port()
+            server.quic_port = self._gen_port() if self.quic else None
 
         for server in self.servers:
             server.gen_confile()