mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-23 02:28:55 -04:00
Flushing the name when NTA expires causes problems for the ongoing resolving process. Do not flush the name from the cache. Instead, the resolver should do the flushing (this is planned to be merged next).
420 lines
13 KiB
Python
420 lines
13 KiB
Python
# 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)
|