Backport the whole NTA test suite

The 9.20 branch was missing the new NTA test suite.  Backport it.
This commit is contained in:
Aram Sargsyan 2026-02-24 17:24:38 +00:00 committed by Ondřej Surý
parent 567c170b0c
commit 22c3861df4
15 changed files with 959 additions and 0 deletions

View file

@ -0,0 +1,36 @@
/*
* 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.
*/
// NS1
options {
query-source address 10.53.0.1;
notify-source 10.53.0.1;
transfer-source 10.53.0.1;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.1; };
listen-on-v6 { none; };
recursion no;
notify yes;
dnssec-validation yes;
/* test that we can turn off trust-anchor-telemetry */
trust-anchor-telemetry no;
};
zone "." {
type primary;
file "root.db.signed";
};
include "trusted.conf";

View file

@ -0,0 +1,24 @@
; 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 gson.nominum.com. a.root.servers.nil. (
2000042100 ; serial
600 ; refresh
600 ; retry
1200 ; expire
600 ; minimum
)
. NS a.root-servers.nil.
a.root-servers.nil. A 10.53.0.1
example. NS ns2.example.
ns2.example. A 10.53.0.2

View file

@ -0,0 +1,37 @@
#!/bin/sh -e
# 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.
# shellcheck source=conf.sh
. ../../conf.sh
set -e
zone=.
infile=root.db.in
zonefile=root.db
(cd ../ns2 && $SHELL sign.sh)
cp "../ns2/dsset-example." .
ksk=$("$KEYGEN" -q -fk -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile"
"$SIGNER" -g -o "$zone" "$zonefile" >/dev/null 2>&1
# Configure the resolving server with a static key.
keyfile_to_static_ds "$ksk" >trusted.conf
cp trusted.conf ../ns4/trusted.conf
cp trusted.conf ../ns9/trusted.conf

View file

@ -0,0 +1,23 @@
; 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 30 ; 5 minutes
@ IN SOA mname1. . (
2000042407 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
30 ; minimum (1 hour)
)
NS ns2
ns2 A 10.53.0.2
www A 10.0.0.1

View file

@ -0,0 +1,35 @@
; 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 ; 5 minutes
@ IN SOA mname1. . (
2000042407 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
NS ns2
NS ns3
ns2 A 10.53.0.2
ns3 A 10.53.0.3
; A secure subdomain
secure NS ns3.secure
ns3.secure A 10.53.0.3
; A secure subdomain we're going to inject bogus data into
bogus NS ns.bogus
ns.bogus A 10.53.0.3
; A subdomain with a corrupt DS
badds NS ns.badds
ns.badds A 10.53.0.3

View file

@ -0,0 +1,50 @@
/*
* 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.
*/
// NS2
options {
query-source address 10.53.0.2;
notify-source 10.53.0.2;
transfer-source 10.53.0.2;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on-v6 { none; };
allow-transfer { any; };
recursion no;
notify yes;
notify-delay 1;
dnssec-validation no;
minimal-responses no;
};
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "example" {
type primary;
file "example.db.signed";
allow-update { any; };
};
zone "corp" {
type primary;
file "corp.db";
};

View file

@ -0,0 +1,38 @@
#!/bin/sh -e
# 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.
# shellcheck source=conf.sh
. ../../conf.sh
set -e
# Sign child zones (served by ns3).
(cd ../ns3 && $SHELL sign.sh)
# The "example." zone.
zone=example.
infile=example.db.in
zonefile=example.db
# Get the DS records for the "example." zone.
for subdomain in bogus badds secure; do
cp "../ns3/dsset-$subdomain.example." .
done
# Sign the "example." zone.
keyname1=$("$KEYGEN" -q -a "$ALTERNATIVE_ALGORITHM" -b "$ALTERNATIVE_BITS" -f KSK "$zone")
keyname2=$("$KEYGEN" -q -a "$ALTERNATIVE_ALGORITHM" -b "$ALTERNATIVE_BITS" "$zone")
cat "$infile" "$keyname1.key" "$keyname2.key" >"$zonefile"
"$SIGNER" -g -o "$zone" -k "$keyname1" "$zonefile" "$keyname2" >/dev/null 2>&1

View file

@ -0,0 +1,27 @@
; 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 ; 5 minutes
@ IN SOA mname1. . (
2000042407 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
NS ns
ns A 10.53.0.3
a A 10.0.0.1
b A 10.0.0.2
c A 10.0.0.3
d A 10.0.0.4
z A 10.0.0.26

View file

@ -0,0 +1,62 @@
/*
* 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.
*/
// NS3
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; };
allow-transfer { any; };
recursion no;
notify yes;
dnssec-validation no;
minimal-responses no;
};
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "example" {
type secondary;
primaries { 10.53.0.2; };
file "example.bk";
};
zone "secure.example" {
type primary;
file "secure.example.db.signed";
allow-update { any; };
};
zone "bogus.example" {
type primary;
file "bogus.example.db.signed";
allow-update { any; };
};
zone "badds.example" {
type primary;
file "badds.example.db.signed";
allow-update { any; };
};

View file

@ -0,0 +1,30 @@
; 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 ; 5 minutes
@ IN SOA mname1. . (
2000042407 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
NS ns3
ns3 A 10.53.0.3
a A 10.0.0.1
b A 10.0.0.2
c A 10.0.0.3
d A 10.0.0.4
e A 10.0.0.5
f A 10.0.0.6
g A 10.0.0.7
z A 10.0.0.26

View file

@ -0,0 +1,62 @@
#!/bin/sh -e
# 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.
# shellcheck source=conf.sh
. ../../conf.sh
set -e
# a validly signed zone
zone=secure.example.
infile=secure.example.db.in
zonefile=secure.example.db
keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
cat "$infile" "$keyname.key" >"$zonefile"
"$SIGNER" -z -D -o "$zone" "$zonefile" >/dev/null
cat "$zonefile" "$zonefile".signed >"$zonefile".tmp
mv "$zonefile".tmp "$zonefile".signed
# a zone that we'll add bogus data to
zone=bogus.example.
infile=bogus.example.db.in
zonefile=bogus.example.db
keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
cat "$infile" "$keyname.key" >"$zonefile"
"$SIGNER" -z -o "$zone" "$zonefile" >/dev/null
{
echo "a.bogus.example. A 10.0.0.22"
echo "b.bogus.example. A 10.0.0.23"
echo "c.bogus.example. A 10.0.0.23"
} >>bogus.example.db.signed
#
# A zone with a bad DS in the parent
# (sourced from bogus.example.db.in)
#
zone=badds.example.
infile=bogus.example.db.in
zonefile=badds.example.db
keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone")
cat "$infile" "$keyname.key" >"$zonefile"
"$SIGNER" -P -o "$zone" "$zonefile" >/dev/null
sed -e 's/bogus/badds/g' <dsset-bogus.example. >dsset-badds.example.

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
// NS4
options {
query-source address 10.53.0.4;
notify-source 10.53.0.4;
transfer-source 10.53.0.4;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.4; };
listen-on-v6 { none; };
recursion yes;
minimal-responses no;
nta-lifetime 12s;
nta-recheck 9s;
validate-except { corp; };
dnssec-validation yes;
};
include "trusted.conf";
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "../../_common/root.hint";
};
zone "corp" {
type static-stub;
server-addresses { 10.53.0.2; };
};

View file

@ -0,0 +1,40 @@
/*
* 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.
*/
// NS9
options {
query-source address 10.53.0.9;
notify-source 10.53.0.9;
transfer-source 10.53.0.9;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.9; };
listen-on-v6 { none; };
recursion yes;
dnssec-validation yes;
forward only;
forwarders { 10.53.0.4; };
servfail-ttl 0;
};
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.9 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
include "trusted.conf";

View file

@ -0,0 +1,22 @@
#!/bin/sh -e
# 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.
# shellcheck source=conf.sh
. ../conf.sh
set -e
(
cd ns1
$SHELL sign.sh
)

View file

@ -0,0 +1,420 @@
# 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.
from re import compile as Re
import os
import time
import isctest
def active(blob):
return len([x for x in blob.splitlines() if " expiry" in x])
# global start-time variable
# pylint: disable=global-statement
START = 0
def test_initial():
m = isctest.query.create("a.bogus.example.", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.servfail(res)
m = isctest.query.create("badds.example.", "SOA")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.servfail(res)
m = isctest.query.create("a.secure.example.", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.adflag(res)
def test_nta_validate_except(servers):
ns4 = servers["ns4"]
response = ns4.rndc("secroots -")
assert Re("^corp: permanent") in response.out
# check insecure local domain works with validate-except
m = isctest.query.create("www.corp", "NS")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
def test_nta_bogus_lifetimes(servers):
ns4 = servers["ns4"]
# no nta lifetime specified:
response = ns4.rndc("nta -l '' foo", raise_on_exception=False)
assert "'nta' failed: bad ttl" in response.err
# bad nta lifetime:
response = ns4.rndc("nta -l garbage foo", raise_on_exception=False)
assert "'nta' failed: bad ttl" in response.err
# excessive nta lifetime:
response = ns4.rndc("nta -l 7d1h foo", raise_on_exception=False)
assert "'nta' failed: out of range" in response.err
def test_nta_install(servers):
global START
ns4 = servers["ns4"]
ns4.rndc("nta -f -l 20s bogus.example")
ns4.rndc("nta badds.example")
# NTAs should persist after reconfig
ns4.reconfigure()
response = ns4.rndc("nta -d")
assert len(response.out.splitlines()) == 3
ns4.rndc("nta secure.example")
ns4.rndc("nta fakenode.secure.example")
with ns4.watch_log_from_here() as watcher:
ns4.rndc("reload")
watcher.wait_for_line("all zones loaded")
response = ns4.rndc("nta -d")
assert len(response.out.splitlines()) == 5
START = time.time()
def test_nta_behavior(servers):
assert START, "test_nta_behavior must be run as part of the full NTA test"
m = isctest.query.create("a.bogus.example.", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
m = isctest.query.create("badds.example.", "SOA")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
m = isctest.query.create("a.secure.example.", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
m = isctest.query.create("a.fakenode.secure.example.", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noadflag(res)
ns4 = servers["ns4"]
response = ns4.rndc("secroots -")
assert Re("^bogus.example: expiry") in response.out
assert Re("^badds.example: expiry") in response.out
assert Re("^secure.example: expiry") in response.out
assert Re("^fakenode.secure.example: expiry") in response.out
# secure.example and badds.example used the default nta-duration
# (configured as 12s in ns4/named1.conf), but the nta recheck interval
# is configured to 9s, so at t=10 the NTAs for secure.example and
# fakenode.secure.example should both be lifted, while badds.example
# should still be going.
delay = START + 10 - time.time()
if delay > 0:
time.sleep(delay)
m = isctest.query.create("b.secure.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.adflag(res)
m = isctest.query.create("b.fakenode.secure.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.nxdomain(res)
isctest.check.adflag(res)
m = isctest.query.create("badds.example.", "SOA")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
# bogus.example was set to expire in 20s, so at t=13
# it should still be NTA'd, but badds.example used the default
# lifetime of 12s, so it should revert to SERVFAIL now.
delay = START + 13 - time.time()
if delay > 0:
time.sleep(delay)
response = ns4.rndc("nta -d")
assert active(response.out) <= 2
response = ns4.rndc("secroots -")
assert Re("bogus.example: expiry") in response.out
assert Re("badds.example: expiry") not in response.out
m = isctest.query.create("b.bogus.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
m = isctest.query.create("a.badds.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.servfail(res)
isctest.check.noadflag(res)
m = isctest.query.create("c.secure.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.adflag(res)
# at t=21, all the NTAs should have expired.
delay = START + 21 - time.time()
if delay > 0:
time.sleep(delay)
response = ns4.rndc("nta -d")
assert active(response.out) == 0
m = isctest.query.create("d.secure.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.adflag(res)
m = isctest.query.create("c.bogus.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.servfail(res)
isctest.check.noadflag(res)
def test_nta_removals(servers):
ns4 = servers["ns4"]
ns4.rndc("nta badds.example")
response = ns4.rndc("nta -d")
assert Re("^badds.example/_default: expiry") in response.out
m = isctest.query.create("a.badds.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
response = ns4.rndc("nta -remove badds.example")
assert "Negative trust anchor removed: badds.example" in response.out
response = ns4.rndc("nta -d")
assert Re("^badds.example/_default: expiry") not in response.out
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.servfail(res)
isctest.check.noadflag(res)
# remove non-existent NTA three times
ns4.rndc("nta -r foo")
ns4.rndc("nta -remove foo")
response = ns4.rndc("nta -r foo")
assert "not found" in response.out
def test_nta_restarts(servers):
global START
assert START, "test_nta_restarts must be run as part of the full NTA test"
# test NTA persistence across restarts
ns4 = servers["ns4"]
response = ns4.rndc("nta -d")
assert active(response.out) == 0
START = time.time()
ns4.rndc("nta -f -l 30s bogus.example")
ns4.rndc("nta -f -l 10s badds.example")
response = ns4.rndc("nta -d")
assert active(response.out) == 2
# stop the server
ns4.stop()
# wait 14s before restarting. badds.example's NTA (lifetime=10s) should
# have expired, and bogus.example should still be running.
delay = START + 14 - time.time()
if delay > 0:
time.sleep(delay)
ns4.start(["--noclean", "--restart", "--port", os.environ["PORT"]])
response = ns4.rndc("nta -d")
assert active(response.out) == 1
assert Re("^bogus.example/_default: expiry") in response.out
m = isctest.query.create("a.badds.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.servfail(res)
m = isctest.query.create("a.bogus.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
ns4.rndc("nta -r bogus.example")
def test_nta_regular(servers):
global START
assert START, "test_nta_regular must be run as part of the full NTA test"
# check "regular" attribute in NTA file
ns4 = servers["ns4"]
response = ns4.rndc("nta -d")
assert active(response.out) == 0
# secure.example validates with AD=1
m = isctest.query.create("a.secure.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.adflag(res)
# stop the server, update _default.nta, restart
ns4.stop()
now = time.localtime()
future = str(now.tm_year + 20) + "0101010000"
with open("ns4/_default.nta", "w", encoding="utf-8") as f:
f.write(f"secure.example. regular {future}")
ns4.start(["--noclean", "--restart", "--port", os.environ["PORT"]])
# NTA active; secure.example. should now return an AD=0 answer.
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
# nta-recheck is configured as 9s, so at t=12 the NTA for
# secure.example. should be lifted as it is not a "forced" NTA.
START = time.mktime(now)
delay = START + 12 - time.time()
if delay > 0:
time.sleep(delay)
response = ns4.rndc("nta -d")
assert active(response.out) == 0
# NTA lifted; secure.example. flush the cache to trigger a new query,
# and it should now return an AD=1 answer.
ns4.rndc("flushtree secure.example")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.adflag(res)
def test_nta_forced(servers):
global START
assert START, "test_nta_regular must be run as part of the full NTA test"
# check "forced" attribute in NTA file
ns4 = servers["ns4"]
# just to be certain, clean up any existing NTA first
ns4.rndc("nta -r secure.example")
response = ns4.rndc("nta -d")
assert active(response.out) == 0
# secure.example validates with AD=1
m = isctest.query.create("a.secure.example", "A")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.adflag(res)
# stop the server, update _default.nta, restart
ns4.stop()
now = time.localtime()
future = str(now.tm_year + 20) + "0101010000"
with open("ns4/_default.nta", "w", encoding="utf-8") as f:
f.write(f"secure.example. forced {future}")
ns4.start(["--noclean", "--restart", "--port", os.environ["PORT"]])
# NTA active; secure.example. should now return an AD=0 answer
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
# nta-recheck is configured as 9s. at t=12 the NTA for
# secure.example. should NOT be lifted as it is "forced".
START = time.mktime(now)
delay = START + 12 - time.time()
if delay > 0:
time.sleep(delay)
# NTA lifted; secure.example. should still return an AD=0 answer
ns4.rndc("flushtree secure.example")
res = isctest.query.tcp(m, "10.53.0.4")
isctest.check.noerror(res)
isctest.check.noadflag(res)
def test_nta_clamping(servers):
ns4 = servers["ns4"]
# clean up any existing NTA
ns4.rndc("nta -r secure.example")
# stop the server, update _default.nta, restart
ns4.stop()
now = time.localtime()
future = str(now.tm_year + 20) + "0101010000"
with open("ns4/_default.nta", "w", encoding="utf-8") as f:
f.write(f"secure.example. forced {future}")
ns4.start(["--noclean", "--restart", "--port", os.environ["PORT"]])
# check that NTA lifetime read from file is clamped to 1 week.
response = ns4.rndc("nta -d")
assert active(response.out) == 1
nta = next((s for s in response.out.splitlines() if " expiry" in s), None)
assert nta is not None
nta = nta.split(" ")
expiry = f"{nta[2]} {nta[3]}"
then = time.mktime(time.strptime(expiry, "%d-%b-%Y %H:%M:%S.000"))
nextweek = time.mktime(now) + (86400 * 7)
# normally there's no more than a few seconds difference between the
# clamped expiration date and the calculated date for next week,
# but add a 3600 second fudge factor to allow for daylight savings
# changes.
assert abs(nextweek - then < 3610)
# remove the NTA
ns4.rndc("nta -r secure.example")
def test_nta_forward(servers):
ns9 = servers["ns9"]
m = isctest.query.create("badds.example", "SOA")
res = isctest.query.tcp(m, "10.53.0.9")
isctest.check.servfail(res)
isctest.check.empty_answer(res)
isctest.check.noadflag(res)
# add NTA and expect resolution to succeed
ns9.rndc("nta badds.example")
res = isctest.query.tcp(m, "10.53.0.9")
isctest.check.noerror(res)
isctest.check.rr_count_eq(res.answer, 2)
isctest.check.noadflag(res)
# remove NTA and expect resolution to fail again
ns9.rndc("nta -remove badds.example")
res = isctest.query.tcp(m, "10.53.0.9")
isctest.check.servfail(res)
isctest.check.empty_answer(res)
isctest.check.noadflag(res)