mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-28 04:34:54 -04:00
Rewrite nsec3 system test to pytest (1/4)
This converts all the nsec3 system test cases prior to reconfiguring the
name server. There are two main classes, one that tests the zone is
correctly signed with NSEC, the other with NSEC3.
Two extra tests for nsec3-dynamic-update-inline.kasp and
nsec3-change.kasp are also rewritten. For the former, we need to
change the 'nsupdate' definition to be able to set the expected RCODE.
(cherry picked from commit e81cc1520a)
This commit is contained in:
parent
bb9451c73f
commit
e8457b1358
3 changed files with 450 additions and 156 deletions
|
|
@ -154,7 +154,9 @@ class NamedInstance:
|
|||
|
||||
return response
|
||||
|
||||
def nsupdate(self, update_msg: dns.message.Message):
|
||||
def nsupdate(
|
||||
self, update_msg: dns.message.Message, expected_rcode=dns.rcode.NOERROR
|
||||
):
|
||||
"""
|
||||
Issue a dynamic update to a server's zone.
|
||||
"""
|
||||
|
|
@ -168,12 +170,14 @@ class NamedInstance:
|
|||
self.ip,
|
||||
self.ports.dns,
|
||||
timeout=3,
|
||||
expected_rcode=dns.rcode.NOERROR,
|
||||
expected_rcode=expected_rcode,
|
||||
)
|
||||
except dns.exception.Timeout as exc:
|
||||
msg = f"update timeout for {zone}"
|
||||
raise dns.exception.Timeout(msg) from exc
|
||||
debug(f"update of zone {zone} to server {self.ip} successful")
|
||||
debug(
|
||||
f"update of zone {zone} to server {self.ip} finished with {expected_rcode}"
|
||||
)
|
||||
return response
|
||||
|
||||
def watch_log_from_start(
|
||||
|
|
|
|||
|
|
@ -235,159 +235,6 @@ key_clear "KEY2"
|
|||
key_clear "KEY3"
|
||||
key_clear "KEY4"
|
||||
|
||||
# Zone: nsec-to-nsec3.kasp.
|
||||
set_zone_policy "nsec-to-nsec3.kasp" "nsec" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec
|
||||
|
||||
if [ $RSASHA1_SUPPORTED = 1 ]; then
|
||||
# Zone: rsasha1-to-nsec3.kasp.
|
||||
set_zone_policy "rsasha1-to-nsec3.kasp" "rsasha1" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_rsasha1_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec
|
||||
|
||||
# Zone: rsasha1-to-nsec3-wait.kasp.
|
||||
set_zone_policy "rsasha1-to-nsec3-wait.kasp" "rsasha1" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_rsasha1_values "KEY1"
|
||||
set_key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec
|
||||
|
||||
# Zone: nsec3-to-rsasha1.kasp.
|
||||
set_zone_policy "nsec3-to-rsasha1.kasp" "nsec3" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_rsasha1_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-to-rsasha1-ds.kasp.
|
||||
set_zone_policy "nsec3-to-rsasha1-ds.kasp" "nsec3" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_rsasha1_values "KEY1"
|
||||
set_key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
fi
|
||||
|
||||
# Zone: nsec3.kasp.
|
||||
set_zone_policy "nsec3.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-dynamic.kasp.
|
||||
set_zone_policy "nsec3-dynamic.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-change.kasp.
|
||||
set_zone_policy "nsec3-change.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Test that NSEC3PARAM TTL is equal to SOA MINIMUM.
|
||||
n=$((n + 1))
|
||||
echo_i "check TTL of NSEC3PARAM in zone $ZONE is equal to SOA MINIMUM ($n)"
|
||||
ret=0
|
||||
dig_with_opts +noquestion "@${SERVER}" "$ZONE" NSEC3PARAM >"dig.out.test$n" || ret=1
|
||||
grep "${ZONE}\..*3600.*IN.*NSEC3PARAM" "dig.out.test$n" >/dev/null || ret=1
|
||||
test "$ret" -eq 0 || echo_i "failed"
|
||||
status=$((status + ret))
|
||||
|
||||
# Update SOA MINIMUM.
|
||||
cp "${DIR}/template2.db.in" "${DIR}/${ZONE}.db"
|
||||
rndccmd $SERVER reload $ZONE >rndc.reload.test$n.$ZONE || log_error "failed to call rndc reload $ZONE"
|
||||
_wait_for_new_soa() {
|
||||
dig_with_opts +noquestion "@${SERVER}" "$ZONE" SOA >"dig.out.soa.test$n" || return 1
|
||||
grep "${ZONE}\..*IN.*SOA.*mname1..*..*20.*20.*.1814400.*900" "dig.out.soa.test$n" >/dev/null || return 1
|
||||
}
|
||||
retry_quiet 10 _wait_for_new_soa || log_error "failed to update SOA record in zone $ZONE"
|
||||
|
||||
# Zone: nsec3-dynamic-change.kasp.
|
||||
set_zone_policy "nsec3-dynamic-change.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-dynamic-to-inline.kasp.
|
||||
set_zone_policy "nsec3-dynamic-to-inline.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-inline-to-dynamic.kasp.
|
||||
set_zone_policy "nsec3-inline-to-dynamic.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-to-nsec.kasp.
|
||||
set_zone_policy "nsec3-to-nsec.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-to-optout.kasp.
|
||||
set_zone_policy "nsec3-to-optout.kasp" "nsec3" 1 3600
|
||||
set_nsec3param "0" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-from-optout.kasp.
|
||||
set_zone_policy "nsec3-from-optout.kasp" "optout" 1 3600
|
||||
set_nsec3param "1" "0"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-other.kasp.
|
||||
set_zone_policy "nsec3-other.kasp" "nsec3-other" 1 3600
|
||||
set_nsec3param "1" "8"
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-xfr-inline.kasp.
|
||||
# This is a secondary zone, where the primary is signed with NSEC3 but
|
||||
# the dnssec-policy dictates NSEC.
|
||||
set_zone_policy "nsec3-xfr-inline.kasp" "nsec" 1 3600
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec
|
||||
|
||||
# Zone: nsec3-dynamic-update-inline.kasp.
|
||||
set_zone_policy "nsec3-dynamic-update-inline.kasp" "nsec" 1 3600
|
||||
set_key_default_values "KEY1"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "dynamic update dnssec-policy zone ${ZONE} with NSEC3 ($n)"
|
||||
ret=0
|
||||
$NSUPDATE >update.out.$ZONE.test$n 2>&1 <<END || ret=1
|
||||
server 10.53.0.3 ${PORT}
|
||||
zone ${ZONE}.
|
||||
update add 04O18462RI5903H8RDVL0QDT5B528DUJ.${ZONE}. 3600 NSEC3 0 0 0 408A4B2D412A4E95 1JMDDPMTFF8QQLIOINSIG4CR9OTICAOC A RRSIG
|
||||
send
|
||||
END
|
||||
wait_for_log 10 "updating zone '${ZONE}/IN': update failed: explicit NSEC3 updates are not allowed in secure zones (REFUSED)" ns3/named.run || ret=1
|
||||
check_nsec
|
||||
|
||||
# Reconfig named.
|
||||
ret=0
|
||||
echo_i "reconfig dnssec-policy to trigger nsec3 rollovers"
|
||||
|
|
|
|||
443
bin/tests/system/nsec3/tests_nsec3_initial.py
Normal file
443
bin/tests/system/nsec3/tests_nsec3_initial.py
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
# 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.
|
||||
|
||||
import shutil
|
||||
import os
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
import dns
|
||||
import dns.update
|
||||
import pytest
|
||||
|
||||
pytest.importorskip("dns", minversion="2.0.0")
|
||||
import isctest
|
||||
import isctest.mark
|
||||
from isctest.vars.algorithms import RSASHA1
|
||||
|
||||
pytestmark = pytest.mark.extra_artifacts(
|
||||
[
|
||||
"*.axfr",
|
||||
"*.created",
|
||||
"dig.out.*",
|
||||
"rndc.reload.*",
|
||||
"rndc.signing.*",
|
||||
"update.out.*",
|
||||
"verify.out.*",
|
||||
"ns*/dsset-**",
|
||||
"ns*/K*",
|
||||
"ns*/settime.out.*",
|
||||
"ns*/*.db",
|
||||
"ns*/*.jbk",
|
||||
"ns*/*.jnl",
|
||||
"ns*/*.signed",
|
||||
"ns*/keygen.out.*",
|
||||
"ns3/named-fips.conf",
|
||||
]
|
||||
)
|
||||
|
||||
ALGORITHM = os.environ["DEFAULT_ALGORITHM_NUMBER"]
|
||||
SIZE = os.environ["DEFAULT_BITS"]
|
||||
|
||||
default_config = {
|
||||
"dnskey-ttl": timedelta(hours=1),
|
||||
"ds-ttl": timedelta(days=1),
|
||||
"key-directory": "{keydir}",
|
||||
"max-zone-ttl": timedelta(days=1),
|
||||
"parent-propagation-delay": timedelta(hours=1),
|
||||
"publish-safety": timedelta(hours=1),
|
||||
"retire-safety": timedelta(hours=1),
|
||||
"signatures-refresh": timedelta(days=5),
|
||||
"signatures-validity": timedelta(days=14),
|
||||
"zone-propagation-delay": timedelta(minutes=5),
|
||||
}
|
||||
|
||||
|
||||
def check_auth_nsec(response):
|
||||
rrs = []
|
||||
for rrset in response.authority:
|
||||
if rrset.match(dns.rdataclass.IN, dns.rdatatype.NSEC, dns.rdatatype.NONE):
|
||||
rrs.append(rrset)
|
||||
assert not rrset.match(
|
||||
dns.rdataclass.IN, dns.rdatatype.NSEC3, dns.rdatatype.NONE
|
||||
)
|
||||
assert len(rrs) != 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec-to-nsec3.kasp",
|
||||
"policy": "nsec",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec-to-nsec3.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "rsasha1-to-nsec3.kasp",
|
||||
"policy": "rsasha1",
|
||||
"key-properties": [
|
||||
f"csk 0 {RSASHA1.number} 2048 goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="rsasha1-to-nsec3.kasp",
|
||||
marks=isctest.mark.with_algorithm("RSASHA1"),
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "rsasha1-to-nsec3-wait.kasp",
|
||||
"policy": "rsasha1",
|
||||
"key-properties": [
|
||||
f"csk 0 {RSASHA1.number} 2048 goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent",
|
||||
],
|
||||
},
|
||||
id="rsasha1-to-nsec3-wait.kasp",
|
||||
marks=isctest.mark.with_algorithm("RSASHA1"),
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
# This is a secondary zone, where the primary is signed with
|
||||
# NSEC3 but the dnssec-policy dictates NSEC.
|
||||
"zone": "nsec3-xfr-inline.kasp",
|
||||
"policy": "nsec",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
"external-keys": [
|
||||
f"csk 0 {ALGORITHM} {SIZE}",
|
||||
],
|
||||
"external-keydir": "ns2",
|
||||
},
|
||||
id="nsec3-xfr-inline.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-dynamic-update-inline.kasp",
|
||||
"policy": "nsec",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-dynamic-update-inline.kasp",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_nsec_case(ns3, params):
|
||||
# Get test parameters.
|
||||
zone = params["zone"]
|
||||
fqdn = f"{zone}."
|
||||
policy = params["policy"]
|
||||
keydir = ns3.identifier
|
||||
config = default_config
|
||||
ttl = int(config["dnskey-ttl"].total_seconds())
|
||||
expected = isctest.kasp.policy_to_properties(ttl=ttl, keys=params["key-properties"])
|
||||
|
||||
# Test case.
|
||||
isctest.log.info(f"check nsec case zone {zone} policy {policy}")
|
||||
|
||||
# First make sure the zone is properly signed.
|
||||
isctest.kasp.wait_keymgr_done(ns3, zone)
|
||||
|
||||
# Key files.
|
||||
keys = isctest.kasp.keydir_to_keylist(zone, keydir)
|
||||
if "external-keys" in params:
|
||||
expected2 = isctest.kasp.policy_to_properties(ttl, keys=params["external-keys"])
|
||||
for ek in expected2:
|
||||
ek.private = False # noqa
|
||||
ek.legacy = True # noqa
|
||||
expected = expected + expected2
|
||||
assert "external-keydir" in params
|
||||
extkeys = isctest.kasp.keydir_to_keylist(zone, params["external-keydir"])
|
||||
keys = keys + extkeys
|
||||
|
||||
isctest.kasp.check_keys(zone, keys, expected)
|
||||
isctest.kasp.check_dnssec_verify(ns3, zone)
|
||||
isctest.kasp.check_apex(ns3, zone, keys, [])
|
||||
|
||||
query = isctest.query.create(fqdn, dns.rdatatype.NSEC3PARAM)
|
||||
response = isctest.query.tcp(query, ns3.ip)
|
||||
assert response.rcode() == dns.rcode.NOERROR
|
||||
assert len(response.answer) == 0
|
||||
check_auth_nsec(response)
|
||||
|
||||
query = isctest.query.create(f"nosuchname.{fqdn}", dns.rdatatype.A)
|
||||
response = isctest.query.tcp(query, ns3.ip)
|
||||
assert response.rcode() == dns.rcode.NXDOMAIN
|
||||
check_auth_nsec(response)
|
||||
|
||||
# Extra test for nsec3-dynamic-update-inline.kasp.
|
||||
if zone == "nsec3-dynamic-update-inline.kasp":
|
||||
isctest.log.info(f"dynamic update dnssec-policy zone {zone} with NSEC3")
|
||||
update_msg = dns.update.UpdateMessage(zone)
|
||||
update_msg.add(
|
||||
f"04O18462RI5903H8RDVL0QDT5B528DUJ.{zone}.",
|
||||
3600,
|
||||
"NSEC3",
|
||||
"0 0 0 408A4B2D412A4E95 1JMDDPMTFF8QQLIOINSIG4CR9OTICAOC A RRSIG",
|
||||
)
|
||||
|
||||
with ns3.watch_log_from_here() as watcher:
|
||||
ns3.nsupdate(update_msg, expected_rcode=dns.rcode.REFUSED)
|
||||
watcher.wait_for_line(
|
||||
f"updating zone '{zone}/IN': update failed: explicit NSEC3 updates are not allowed in secure zones (REFUSED)"
|
||||
)
|
||||
|
||||
|
||||
def wait_for_soa_update(server, fqdn):
|
||||
verified = False
|
||||
match = f"20 20 1814400 900"
|
||||
|
||||
for _ in range(5):
|
||||
query = isctest.query.create(fqdn, dns.rdatatype.SOA)
|
||||
response = isctest.query.tcp(query, server.ip, server.ports.dns, timeout=3)
|
||||
for rrset in response.answer:
|
||||
if match in rrset.to_text():
|
||||
verified = True
|
||||
|
||||
if verified:
|
||||
break
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
return verified
|
||||
|
||||
|
||||
def check_nsec3param(response, match, saltlen):
|
||||
rrs = []
|
||||
|
||||
for rrset in response.answer:
|
||||
if rrset.match(dns.rdataclass.IN, dns.rdatatype.NSEC3PARAM, dns.rdatatype.NONE):
|
||||
assert match in rrset.to_text()
|
||||
if saltlen == 0:
|
||||
assert f"{match} -" in rrset.to_text()
|
||||
else:
|
||||
assert not f"{match} -" in rrset.to_text()
|
||||
|
||||
rrs.append(rrset)
|
||||
else:
|
||||
assert rrset.match(
|
||||
dns.rdataclass.IN, dns.rdatatype.RRSIG, dns.rdatatype.NSEC3PARAM
|
||||
)
|
||||
|
||||
assert len(rrs) != 0
|
||||
|
||||
|
||||
def check_auth_nsec3(response, iterations=0, optout=0, saltlen=0):
|
||||
match = f"IN NSEC3 1 {optout} {iterations}"
|
||||
rrs = []
|
||||
|
||||
for rrset in response.authority:
|
||||
if rrset.match(dns.rdataclass.IN, dns.rdatatype.NSEC3, dns.rdatatype.NONE):
|
||||
assert match in rrset.to_text()
|
||||
if saltlen == 0:
|
||||
assert f"{match} -" in rrset.to_text()
|
||||
else:
|
||||
assert not f"{match} -" in rrset.to_text()
|
||||
|
||||
rrs.append(rrset)
|
||||
assert not rrset.match(
|
||||
dns.rdataclass.IN, dns.rdatatype.NSEC, dns.rdatatype.NONE
|
||||
)
|
||||
|
||||
assert len(rrs) != 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-to-rsasha1.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-to-rsasha1.kasp",
|
||||
marks=isctest.mark.with_algorithm("RSASHA1"),
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-to-rsasha1-ds.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:omnipresent krrsig:omnipresent zrrsig:omnipresent ds:omnipresent",
|
||||
],
|
||||
},
|
||||
id="nsec3-to-rsasha1-ds.kasp",
|
||||
marks=isctest.mark.with_algorithm("RSASHA1"),
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-dynamic.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-dynamic.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-change.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-change.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-dynamic-change.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-dynamic-change.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-dynamic-to-inline.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-dynamic-to-inline.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-inline-to-dynamic.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-inline-to-dynamic.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-to-nsec.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-to-nsec.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-to-optout.kasp",
|
||||
"policy": "nsec3",
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-to-optout.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-from-optout.kasp",
|
||||
"policy": "optout",
|
||||
"nsec3param": {
|
||||
"optout": 1,
|
||||
"salt-length": 0,
|
||||
},
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-from-optout.kasp",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"zone": "nsec3-other.kasp",
|
||||
"policy": "nsec3-other",
|
||||
"nsec3param": {
|
||||
"optout": 1,
|
||||
"salt-length": 8,
|
||||
},
|
||||
"key-properties": [
|
||||
f"csk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:rumoured krrsig:rumoured zrrsig:rumoured ds:hidden",
|
||||
],
|
||||
},
|
||||
id="nsec3-other.kasp",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_nsec3_case(ns3, params):
|
||||
# Get test parameters.
|
||||
zone = params["zone"]
|
||||
fqdn = f"{zone}."
|
||||
policy = params["policy"]
|
||||
keydir = ns3.identifier
|
||||
config = default_config
|
||||
ttl = int(config["dnskey-ttl"].total_seconds())
|
||||
expected = isctest.kasp.policy_to_properties(ttl=ttl, keys=params["key-properties"])
|
||||
|
||||
iterations = 0
|
||||
optout = 0
|
||||
saltlen = 0
|
||||
if "nsec3param" in params:
|
||||
optout = params["nsec3param"].get("optout", 0)
|
||||
saltlen = params["nsec3param"].get("salt-length", 0)
|
||||
|
||||
match = f"{fqdn} 3600 IN NSEC3PARAM 1 0 {iterations}"
|
||||
|
||||
# Test case.
|
||||
isctest.log.info(f"check nsec3 case zone {zone} policy {policy}")
|
||||
|
||||
# First make sure the zone is properly signed.
|
||||
isctest.kasp.wait_keymgr_done(ns3, zone)
|
||||
|
||||
keys = isctest.kasp.keydir_to_keylist(zone, keydir)
|
||||
isctest.kasp.check_keys(zone, keys, expected)
|
||||
isctest.kasp.check_dnssec_verify(ns3, zone)
|
||||
isctest.kasp.check_apex(ns3, zone, keys, [])
|
||||
|
||||
query = isctest.query.create(fqdn, dns.rdatatype.NSEC3PARAM)
|
||||
response = isctest.query.tcp(query, ns3.ip)
|
||||
assert response.rcode() == dns.rcode.NOERROR
|
||||
|
||||
check_nsec3param(response, match, saltlen)
|
||||
|
||||
query = isctest.query.create(f"nosuchname.{fqdn}", dns.rdatatype.A)
|
||||
response = isctest.query.tcp(query, ns3.ip)
|
||||
assert response.rcode() == dns.rcode.NXDOMAIN
|
||||
check_auth_nsec3(response, iterations, optout, saltlen)
|
||||
|
||||
# Extra test for nsec3-change.kasp.
|
||||
if zone == "nsec3-change.kasp":
|
||||
|
||||
shutil.copyfile(
|
||||
f"{ns3.identifier}/template2.db.in", f"{ns3.identifier}/{zone}.db"
|
||||
)
|
||||
ns3.rndc(f"reload {zone}")
|
||||
|
||||
wait_for_soa_update(ns3, fqdn)
|
||||
# After reconfig, the NSEC3PARAM TTL should match the new SOA MINIMUM.
|
||||
Loading…
Reference in a new issue