Commit 49992167 authored by Petr Špaček's avatar Petr Špaček

resolver: Simulate successful RFC 5011 KSK roll-over in 2017

Directory rfc5011 contains test generator which can be used to
re-generate the test from scratch. This will get handy when we decide to
generate more RFC 5011 test cases like that.
parent f976a9b4
Pipeline #13459 failed with stage
in 2 minutes and 25 seconds
This diff is collapsed.
Start with `genkeyszones.sh` and generate DNSSEC keys + signed versions of `unsigned.db`.
Then use `dns2rpl.py` to run Knot DNS server with signed zone
and to generate RPL file from server's answers.
See comments in script headers to further details.
#!/usr/bin/python3
"""
Generate RFC 5011 test simulating succesfull KSK roll-over in 2017.
Depedencies: Knot DNS server + Deckard library.
Environment: Set PYTHONPATH variable so "import pydnstest" will use module from Deckard.
Input: Root zone files, presumably created by genkeyszones.sh.
Output: RPL file for Deckard on standard output.
"""
import copy
import datetime
import os.path
import subprocess
import time
import dns.resolver
import pydnstest.scenario
def store_answer(qname, qtype, template):
answ = dns.resolver.query(qname, qtype, raise_on_no_answer=False)
entr = copy.copy(template)
entr.message = answ.response
return entr
def resolver_init():
"""
Configure dns.resolver to ask ::1@5353 with EDNS0 DO set.
"""
dns.resolver.reset_default_resolver()
dns.resolver.default_resolver.use_edns(0, dns.flags.DO, 4096)
dns.resolver.default_resolver.nameservers = ['::1']
dns.resolver.default_resolver.nameserver_ports = {'::1': 5353}
dns.resolver.default_resolver.flags = 0
def get_templates():
"""
Return empty objects for RANGE and ENTRY suitable as object templates.
"""
empty_case, empty_config = pydnstest.scenario.parse_file(os.path.realpath('empty.rpl'))
rng = copy.copy(empty_case.ranges[0])
entry = copy.copy(rng.stored[0])
entry.adjust_fields = ['copy_id']
entry.match_fields = ['opcode', 'question']
rng.addresses = {'198.41.0.4', '2001:503:ba3e::2:30'}
rng.stored = []
return rng, entry
def generate_range(filename, rng_templ, entry_templ):
"""
Run Knot DNS server with specified zone file and generate RANGE object.
"""
assert filename.startswith('20')
assert filename.endswith('.db')
try:
os.unlink('root.db')
except FileNotFoundError:
pass
os.link(filename, 'root.db')
# run server
knotd = subprocess.Popen(['/usr/local/sbin/knotd', '-c', 'knot.root.conf'])
time.sleep(0.1) # give kresd time to start so we do not wait for first timeout
# query data
rng = copy.copy(rng_templ)
rng.stored = []
rng.stored.append(store_answer('.', 'SOA', entry_templ))
rng.stored.append(store_answer('.', 'DNSKEY', entry_templ))
rng.stored.append(store_answer('.', 'NS', entry_templ))
rng.stored.append(store_answer('rootns.', 'NS', entry_templ))
rng.stored.append(store_answer('rootns.', 'A', entry_templ))
rng.stored.append(store_answer('rootns.', 'AAAA', entry_templ))
rng.stored.append(store_answer('test.', 'TXT', entry_templ))
rng.a = int(filename[:-len('.db')])
# kill server
knotd.kill()
return rng
def generate_step_query(tcurr, id_prefix):
out = '; {0}'.format(tcurr.isoformat())
out += '''
STEP {0}000000 QUERY
ENTRY_BEGIN
REPLY RD AD
SECTION QUESTION
test. IN TXT
ENTRY_END
'''.format(id_prefix)
return out
def generate_step_check(id_prefix):
return '''STEP {0}000001 CHECK_ANSWER
ENTRY_BEGIN
REPLY QR RD RA AD
MATCH opcode rcode flags question answer
SECTION QUESTION
test. IN TXT
SECTION ANSWER
test. IN TXT "it works"
ENTRY_END
'''.format(id_prefix)
def generate_step_elapse(tstep, id_prefix):
out = '; move time by {0}\n'.format(tstep)
out += '''STEP {0}000099 TIME_PASSES ELAPSE {1}\n\n'''.format(
id_prefix, int(tstep.total_seconds()))
return out
resolver_init()
rng_templ, entry_templ = get_templates()
ranges = []
# transform data in zones files into RANGEs
files = os.listdir()
files.sort()
for fn in files:
if not fn.endswith('.db') or not fn.startswith('20'):
continue
ranges.append(generate_range(fn, rng_templ, entry_templ))
# connect ranges
for i in range(1, len(ranges)):
ranges[i - 1].b = ranges[i].a - 1
ranges[-1].b = 99999999999999
# steps
steps = []
tstart = datetime.datetime(year=2017, month=7, day=1)
tend = datetime.datetime(year=2017, month=12, day=31, hour=23, minute=59, second=59)
tstep = datetime.timedelta(days=1)
tcurr = tstart
while tcurr < tend:
id_prefix = tcurr.strftime('%Y%m%d')
steps.append(generate_step_query(tcurr, id_prefix))
steps.append(generate_step_check(id_prefix))
steps.append(generate_step_elapse(tstep, id_prefix))
tcurr += tstep
# generate output
with open('keys/ds') as dsfile:
ta = dsfile.read().strip()
# constant RPL file header
print("""stub-addr: 2001:503:ba3e::2:30
trust-anchor: {ta}
val-override-date: 20170701000000
query-minimization: off
CONFIG_END
SCENARIO_BEGIN Simulation of successfull RFC 5011 KSK roll-over during 2017
""".format(ta=ta))
for rng in ranges:
print(rng)
for step in steps:
print(step)
# constant RPL file footer
print('''
SCENARIO_END
''')
stub-addr: 127.0.0.10
CONFIG_END
SCENARIO_BEGIN empty replies
RANGE_BEGIN 0 100
ADDRESS 127.0.0.10
ENTRY_BEGIN
MATCH subdomain
ADJUST copy_id copy_query
SECTION QUESTION
. IN A
ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
ENTRY_END
SCENARIO_END
#!/usr/bin/bash
# First, generate DNSSEC keys with timers set to simulate 2017 KSK roll-over.
# Second, fake system time to pretend that we are at the beginning on time slots
# used during 2017 and sign our fake root zone.
# Depends on libfaketime + dnssec-keygen and dnssec-signzone from BIND 9.11.
# Output: Bunch of DNSSEC keys + several versions of signed root zone.
set -o nounset -o errexit -o xtrace
GEN="dnssec-keygen -r /dev/urandom -K keys/ -a RSASHA256 -b 2048 -L 21d"
function sign {
OUTFILE="$(echo "$1" | sed 's/[- :]//g').db"
TZ=UTC \
LD_PRELOAD="/usr/lib64/faketime/libfaketimeMT.so.1" \
FAKETIME="$1" \
dnssec-signzone \
-r /dev/urandom \
-K keys/ \
-o . \
-S \
-T 21d \
-s now \
-e +14d \
-X +21d \
-O full \
-f "${OUTFILE}" \
unsigned.db
# DS for the very first KSK
test ! -f keys/ds && dnssec-dsfromkey -2 -f "${OUTFILE}" . > keys/ds || : initial DS RR already exists
}
rm -f 20*.db
rm -f keys/K*
rm -f keys/ds
mkdir -p keys/
# old KSK
${GEN} -f KSK -P 20100715000000 -A 20100715000000 -I 20171011000000 -R 20180111000000 -D 20180322000000 .
# new KSK
${GEN} -f KSK -P 20170711000000 -A 20171011000000 .
# ZSK before roll-over: 2017-Q2
${GEN} -P 20170320000000 -A 20170401000000 -I 20170701000000 -D 20170711000000 .
# ZSK-q1: 2017-Q3
${GEN} -P 20170621000000 -A 20170701000000 -I 20171001000000 -D 20171011000000 .
# ZSK-q2: 2017-Q4
${GEN} -P 20170919000000 -A 20171001000000 -I 20180101000000 -D 20180111000000 .
# ZSK-q3: 2018-Q1
${GEN} -P 20171220000000 -A 20180101000000 -I 20180401000000 -D 20180411000000 .
# ZSK: 2018-Q2
${GEN} -P 20180322000000 -A 20180401000000 .
# hopefully slots according to
# https://www.icann.org/en/system/files/files/ksk-rollover-operational-implementation-plan-22jul16-en.pdf
# https://data.iana.org/ksk-ceremony/29/KC29_Script_Annotated.pdf
sign "2017-07-01 00:00:00" # 2017 Q3 slot 1
sign "2017-07-11 00:00:00" # 2017 Q3 slot 2
sign "2017-07-21 00:00:00" # 2017 Q3 slot 3
sign "2017-07-31 00:00:00" # 2017 Q3 slot 4
sign "2017-08-10 00:00:00" # 2017 Q3 slot 5
sign "2017-08-20 00:00:00" # 2017 Q3 slot 6
sign "2017-08-30 00:00:00" # 2017 Q3 slot 7
sign "2017-09-09 00:00:00" # 2017 Q3 slot 8
sign "2017-09-19 00:00:00" # 2017 Q3 slot 9
sign "2017-10-01 00:00:00" # 2017 Q4 slot 1
sign "2017-10-11 00:00:00" # 2017 Q4 slot 2
sign "2017-10-21 00:00:00" # 2017 Q4 slot 3
sign "2017-10-31 00:00:00" # 2017 Q4 slot 4
sign "2017-11-10 00:00:00" # 2017 Q4 slot 5
sign "2017-11-20 00:00:00" # 2017 Q4 slot 6
sign "2017-11-30 00:00:00" # 2017 Q4 slot 7
sign "2017-12-10 00:00:00" # 2017 Q4 slot 8
sign "2017-12-20 00:00:00" # 2017 Q4 slot 9
# 2018-01-01 00:00:00 # 2018 Q1 slot 1
# 2018-01-11 00:00:00 # 2018 Q1 slot 2
# 2018-01-21 00:00:00 # 2018 Q1 slot 3
# 2018-01-31 00:00:00 # 2018 Q1 slot 4
# 2018-02-10 00:00:00 # 2018 Q1 slot 5
# 2018-02-20 00:00:00 # 2018 Q1 slot 6
# 2018-03-02 00:00:00 # 2018 Q1 slot 7
# 2018-03-12 00:00:00 # 2018 Q1 slot 8
# 2018-03-22 00:00:00 # 2018 Q1 slot 9
#
# Minimal configuration file for Knot DNS server used by dns2rpl.py.
#
server:
# Listen on all configured IPv4 interfaces.
listen: 0.0.0.0@5353
# Listen on all configured IPv6 interfaces.
listen: ::@5353
# User for running the server.
# user: knot:knot
# Log info and more serious events to syslog.
log:
- target: stderr
any: debug
template:
# - id: default
# storage: "/usr/local/var/lib/knot"
zone:
# Master zone.
- domain: .
storage: "."
file: "root.db"
. 86400 IN SOA rootns. you.test. 2017071100 1800 900 604800 86400
. 518400 IN NS rootns.
rootns. 518400 IN A 198.41.0.4
rootns. 518400 IN AAAA 2001:503:ba3e::2:30
test. 1 IN TXT "it works"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment