diff options
Diffstat (limited to 'modules/ta_update/ta_update.test.integr/rfc5011')
8 files changed, 472 insertions, 0 deletions
diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/README b/modules/ta_update/ta_update.test.integr/rfc5011/README new file mode 100644 index 0000000..9b71987 --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/README @@ -0,0 +1,13 @@ +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. + +Generate RFC5011 test: +`dns2rpl.py`. +`./genkeyszones.sh` + +Generate unmanaged keys tests: +`./genkeyszones.sh <--unmanaged_key-presens|--unmanagedkey-missing|--unmanagedkey-revoke>` +`VARIANT="unmanaged_key" ./dns2rpl.py` + +See comments in script headers to further details. diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/dns2rpl.py b/modules/ta_update/ta_update.test.integr/rfc5011/dns2rpl.py new file mode 100755 index 0000000..317d671 --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/dns2rpl.py @@ -0,0 +1,222 @@ +#!/usr/bin/python3 +""" +Generate RFC 5011 test simulating successful KSK roll-over in 2017. + +Dependencies: 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 + +try: + VARIANT = os.environ["VARIANT"] +except KeyError: + VARIANT = "" + +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, _ = 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(['knotd', '-c', 'knot.root.conf', '-s', '/tmp/knot-dns2rpl.sock']) + 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_nocheck(id_prefix): + return '''STEP {0}000001 CHECK_ANSWER +ENTRY_BEGIN +REPLY QR RD RA AD +MATCH opcode qname question +SECTION QUESTION +test. IN TXT +SECTION ANSWER +test. IN TXT "it works" +ENTRY_END +'''.format(id_prefix) + +def generate_step_finish_msg(id_prefix): + return '''STEP {0}000001 CHECK_ANSWER +ENTRY_BEGIN +REPLY QR RD RA AA NXDOMAIN +MATCH opcode rcode flags question answer +SECTION QUESTION +test. IN TXT +SECTION AUTHORITY +test. 10800 IN SOA test. nobody.invalid. 1 3600 1200 604800 10800 +SECTION ADDITIONAL +explanation.invalid. 10800 IN TXT "check last answer" +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 + + +def main(): + resolver_init() + rng_templ, entry_templ = get_templates() + ranges = [] + check_last_msg = False + + # 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) + if VARIANT == "unmanaged_key": + tend = datetime.datetime(year=2017, month=7, day=21, hour=23, minute=59, second=59) + check_last_msg = True + else: + 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)) + if (check_last_msg is True and tcurr + tstep > tend): + steps.append(generate_step_finish_msg(id_prefix)) + elif VARIANT == "unmanaged_key": + steps.append(generate_step_nocheck(id_prefix)) + else: + 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: + tas = dsfile.read().strip() + + # constant RPL file header + print("stub-addr: 2001:503:ba3e::2:30") + for ta in tas.split('\n'): + print ("trust-anchor: " + ta) + print("""val-override-date: 20170701000000 +query-minimization: off +CONFIG_END + +SCENARIO_BEGIN Simulation of successful 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 + ''') + + +if __name__ == '__main__': + main() diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/empty.rpl b/modules/ta_update/ta_update.test.integr/rfc5011/empty.rpl new file mode 100644 index 0000000..295d5a5 --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/empty.rpl @@ -0,0 +1,20 @@ +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 diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/genkeyszones.sh b/modules/ta_update/ta_update.test.integr/rfc5011/genkeyszones.sh new file mode 100755 index 0000000..4a65469 --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/genkeyszones.sh @@ -0,0 +1,174 @@ +#!/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 -K keys/ -a RSASHA256 -b 2048 -L 21d" + +function usage { + echo -e "Usage: $0 <option>\n\n\ +Option:\n\ +\t--help\t\t\tShow this help. +\t--rollover\t\tGenerate files for rollover test.\n\ +\t--unmanagedkey-present\tGenerate files for present new unmanaged key.\n\ +\t--unmanagedkey-missing\tGenerate files for missing unmanaged key.\n\ +\t--unmanagedkey-revoke\tGenerate files for revoked unmanaged key." +} + +function sign () { + OUTFILE="$(echo "$1" | sed 's/[- :]//g').db" + TZ=UTC \ + LD_PRELOAD="/usr/lib64/faketime/libfaketimeMT.so.1" \ + FAKETIME="$1" \ + dnssec-signzone \ + -K keys/ \ + -o . \ + -S \ + -T 21d \ + -s now \ + -e +14d \ + -X +21d \ + -O full \ + -f "${OUTFILE}" \ + "$2" + + # DS for the very first KSK + test ! -f keys/ds && dnssec-dsfromkey -2 -f "${OUTFILE}" . > keys/ds || : initial DS RR already exists +} + +function test_rollover { + # 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 +} + +function test_unmanagedkey_present { + # old KSK + ${GEN} -f KSK -P 20100715000000 -A 20100715000000 -I 20171011000000 -R 20180111000000 -D 20180322000000 . + # new KSK + ${GEN} -f KSK -P 20170711000000 -A 20171011000000 . + + # ZSKs + ${GEN} -P 20170621000000 -A 20170701000000 -I 20171001000000 -D 20171011000000 . + ${GEN} -P 20170919000000 -A 20171001000000 -I 20180101000000 -D 20180111000000 . + + sign "2017-07-01 00:00:00" unsigned_ok.db + sign "2017-07-11 00:00:00" unsigned_ok.db # present key is seen 10 days + sign "2017-07-21 00:00:00" unsigned_check.db # last edited message for check result from deckard +} + +function test_unmanagedkey_revoke { + # old KSK + ${GEN} -f KSK -P 20100715000000 -A 20100715000000 -I 20171011000000 -R 20180111000000 -D 20180322000000 . + # revoked KSK + ${GEN} -f KSK -P 20100715000000 -A 20100715000000 -I 20171011000000 -R 20170710000000 -D 20180322000000 . + + # ZSKs + ${GEN} -P 20170621000000 -A 20170701000000 -I 20171001000000 -D 20171011000000 . + ${GEN} -P 20170919000000 -A 20171001000000 -I 20180101000000 -D 20180111000000 . + + sign "2017-07-01 00:00:00" unsigned_ok.db + sign "2017-07-11 00:00:00" unsigned_ok.db # revoke key is seen 10 days + sign "2017-07-21 00:00:00" unsigned_check.db # last edited message for check result from deckard +} + +function test_unmanagedkey_missing { + # old KSK + ${GEN} -f KSK -P 20100715000000 -A 20100715000000 -I 20171011000000 -R 20180111000000 -D 20180322000000 . + # missing KSK + ${GEN} -f KSK -P 20100715000000 -A 20100715000000 -I 20171011000000 -R 20180111000000 -D 20170710000000 . + + # ZSKs + ${GEN} -P 20170621000000 -A 20170701000000 -I 20171001000000 -D 20171011000000 . + ${GEN} -P 20170919000000 -A 20171001000000 -I 20180101000000 -D 20180111000000 . + + sign "2017-07-01 00:00:00" unsigned_ok.db + sign "2017-07-11 00:00:00" unsigned_ok.db # missing key is seen 10 days + sign "2017-07-21 00:00:00" unsigned_check.db # last edited message for check result from deckard +} + +if [ $# -ne 1 ]; then + usage + exit 0 +fi + +rm -f 20*.db +rm -f keys/K* +rm -f keys/ds +mkdir -p keys/ + +case $1 in + --rollover) + test_rollover + ;; + --unmanagedkey-present) + test_unmanagedkey_present + #test_rollover + ;; + --unmanagedkey-revoke) + test_unmanagedkey_revoke + ;; + --unmanagedkey-missing) + test_unmanagedkey_missing + ;; + --help|-h) + usage + ;; + *) + echo -e "Unknown option !\n\n" + usage + ;; +esac diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/knot.root.conf b/modules/ta_update/ta_update.test.integr/rfc5011/knot.root.conf new file mode 100644 index 0000000..5e20747 --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/knot.root.conf @@ -0,0 +1,26 @@ +# +# 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" + diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/pydnstest b/modules/ta_update/ta_update.test.integr/rfc5011/pydnstest new file mode 120000 index 0000000..331fa73 --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/pydnstest @@ -0,0 +1 @@ +../../../../tests/integration/deckard/pydnstest
\ No newline at end of file diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/unsigned_check.db b/modules/ta_update/ta_update.test.integr/rfc5011/unsigned_check.db new file mode 100644 index 0000000..cf03621 --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/unsigned_check.db @@ -0,0 +1,8 @@ +. 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 "check and change answer" diff --git a/modules/ta_update/ta_update.test.integr/rfc5011/unsigned_ok.db b/modules/ta_update/ta_update.test.integr/rfc5011/unsigned_ok.db new file mode 100644 index 0000000..b837acd --- /dev/null +++ b/modules/ta_update/ta_update.test.integr/rfc5011/unsigned_ok.db @@ -0,0 +1,8 @@ +. 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" |