1 # Copyright (C) Internet Systems Consortium, Inc. ("ISC") 2 # 3 # SPDX-License-Identifier: MPL-2.0 4 # 5 # This Source Code Form is subject to the terms of the Mozilla Public 6 # License, v. 2.0. If a copy of the MPL was not distributed with this 7 # file, you can obtain one at https://mozilla.org/MPL/2.0/. 8 # 9 # See the COPYRIGHT file distributed with this work for additional 10 # information regarding copyright ownership. 11 12 # pylint: disable=redefined-outer-name,unused-import 13 14 import os 15 import shutil 16 17 from datetime import timedelta 18 19 import dns.update 20 import pytest 21 22 pytest.importorskip("dns", minversion="2.0.0") 23 import isctest 24 import isctest.mark 25 from isctest.vars.algorithms import RSASHA256 26 from nsec3.common import ( 27 pytestmark, 28 check_auth_nsec3, 29 check_nsec3param, 30 ) 31 32 DNSKEY_TTL = int(timedelta(hours=1).total_seconds()) 33 ZSK_LIFETIME = int(timedelta(days=90).total_seconds()) 34 35 # include the following zones when rendering named configs 36 ZONES = { 37 "retransfer.kasp", 38 } 39 40 41 def bootstrap(): 42 return { 43 "zones": ZONES, 44 } 45 46 47 def perform_nsec3_tests(server, params): 48 # Get test parameters. 49 zone = params["zone"] 50 fqdn = f"{zone}." 51 policy = params["policy"] 52 keydir = server.identifier 53 minimum = params.get("soa-minimum", 3600) 54 expected = isctest.kasp.policy_to_properties( 55 ttl=DNSKEY_TTL, keys=params["key-properties"] 56 ) 57 58 iterations = 0 59 optout = 0 60 saltlen = 0 61 62 match = f"{fqdn} {minimum} IN NSEC3PARAM 1 0 {iterations}" 63 64 # Test case. 65 isctest.log.info(f"check nsec3 case zone {zone} policy {policy}") 66 67 # First make sure the zone is properly signed. 68 isctest.kasp.wait_keymgr_done(server, zone) 69 70 keys = isctest.kasp.keydir_to_keylist(zone, keydir) 71 ksks = [k for k in keys if k.is_ksk()] 72 zsks = [k for k in keys if k.is_zsk()] 73 isctest.kasp.check_keys(zone, keys, expected) 74 isctest.kasp.check_dnssec_verify(server, zone) 75 isctest.kasp.check_apex(server, zone, ksks, zsks) 76 77 query = isctest.query.create(fqdn, dns.rdatatype.NSEC3PARAM) 78 response = isctest.query.tcp(query, server.ip, server.ports.dns, timeout=3) 79 assert response.rcode() == dns.rcode.NOERROR 80 81 salt = check_nsec3param(response, match, saltlen) 82 83 query = isctest.query.create(f"nosuchname.{fqdn}", dns.rdatatype.A) 84 response = isctest.query.tcp(query, server.ip, server.ports.dns, timeout=3) 85 assert response.rcode() == dns.rcode.NXDOMAIN 86 check_auth_nsec3(response, iterations, optout, salt) 87 88 return salt 89 90 91 def test_nsec3_retransfer(servers, templates): 92 ns2 = servers["ns2"] 93 ns3 = servers["ns3"] 94 95 params = { 96 "zone": "retransfer.kasp", 97 "policy": "nsec3rsa256", 98 "key-properties": [ 99 f"ksk 0 {RSASHA256.number} 2048 goal:omnipresent dnskey:rumoured krrsig:rumoured ds:hidden", 100 f"zsk {ZSK_LIFETIME} {RSASHA256.number} 2048 goal:omnipresent dnskey:rumoured zrrsig:rumoured", 101 ], 102 } 103 104 zone = params["zone"] 105 salt = perform_nsec3_tests(ns3, params) 106 107 # Stop primary. 108 ns2.stop() 109 110 # Update the zone. 111 serial = 10 112 templates.render(f"{ns2.identifier}/{zone}.db", {"serial": serial}) 113 114 with ns2.watch_log_from_here() as watcher: 115 ns2.start(["--noclean", "--restart", "--port", os.environ["PORT"]]) 116 watcher.wait_for_line("all zones loaded") 117 118 # Test NSEC3 and NSEC3PARAM is the same after retransfer. 119 isctest.log.info(f"check zone {zone} after retransfer has salt {salt}") 120 prevsalt = salt 121 122 # Retransfer zone, NSEC3 should stay the same. 123 with ns3.watch_log_from_here() as watcher: 124 ns3.rndc(f"retransfer {zone}") 125 # When sending notifies, the zone should be up to date. 126 watcher.wait_for_line(f"zone {zone}/IN (signed): sending notify to 10.53.0.4") 127 128 salt = perform_nsec3_tests(ns3, params) 129 assert prevsalt == salt 130