test.py 9.06 KB
Newer Older
1 2 3 4 5 6 7 8
#!/usr/bin/env python3

"""
Check of automatic algorithm rollover scenario.
"""

import collections
import os
9
import random
10 11 12 13 14 15 16 17 18
import shutil
import datetime
import subprocess
from subprocess import check_call

from dnstest.utils import *
from dnstest.keys import Keymgr
from dnstest.test import Test

19 20 21 22
DOUBLE_DS = random.choice([True, False])
if DOUBLE_DS:
    check_log("DOUBLE DS ENABLED")

23 24 25 26 27 28 29 30
def pregenerate_key(server, zone, alg):
    class a_class_with_name:
        def __init__(self, name):
            self.name = name

    server.gen_key(a_class_with_name("notexisting.zone."), ksk=True, alg=alg,
                   addtopolicy=zone[0].name)

31
# check zone if keys are present and used for signing
32
def check_zone(server, zone, slave, dnskeys, dnskey_rrsigs, cdnskeys, soa_rrsigs, msg):
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
    qdnskeys = server.dig("example.com", "DNSKEY", bufsize=4096)
    found_dnskeys = qdnskeys.count("DNSKEY")

    qdnskeyrrsig = server.dig("example.com", "DNSKEY", dnssec=True, bufsize=4096)
    found_rrsigs = qdnskeyrrsig.count("RRSIG")

    qcdnskey = server.dig("example.com", "CDNSKEY", bufsize=4096)
    found_cdnskeys = qcdnskey.count("CDNSKEY")

    qsoa = server.dig("example.com", "SOA", dnssec=True, bufsize=4096)
    found_soa_rrsigs = qsoa.count("RRSIG")

    check_log("DNSKEYs: %d (expected %d)" % (found_dnskeys, dnskeys));
    check_log("RRSIGs: %d (expected %d)" % (found_soa_rrsigs, soa_rrsigs));
    check_log("DNSKEY-RRSIGs: %d (expected %d)" % (found_rrsigs, dnskey_rrsigs));
    check_log("CDNSKEYs: %d (expected %d)" % (found_cdnskeys, cdnskeys));

    if found_dnskeys != dnskeys:
        set_err("BAD DNSKEY COUNT: " + msg)
        detail_log("!DNSKEYs not published and activated as expected: " + msg)

    if found_soa_rrsigs != soa_rrsigs:
        set_err("BAD RRSIG COUNT: " + msg)
        detail_log("!RRSIGs not published and activated as expected: " + msg)

    if found_rrsigs != dnskey_rrsigs:
        set_err("BAD DNSKEY RRSIG COUNT: " + msg)
        detail_log("!RRSIGs not published and activated as expected: " + msg)

    if found_cdnskeys != cdnskeys:
        set_err("BAD CDNSKEY COUNT: " + msg)
        detail_log("!CDNSKEYs not published and activated as expected: " + msg)

    detail_log(SEP)

    # Valgrind delay breaks the timing!
    if not server.valgrind:
70 71
        t.xfr_diff(server, slave, zone)

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
        server.zone_backup(zone, flush=True)
        server.zone_verify(zone)

def wait_for_rrsig_count(t, server, rrtype, rrsig_count, timeout):
    rtime = 0
    while True:
        qdnskeyrrsig = server.dig("example.com", rrtype, dnssec=True, bufsize=4096)
        found_rrsigs = qdnskeyrrsig.count("RRSIG")
        if found_rrsigs == rrsig_count:
            break
        rtime = rtime + 1
        t.sleep(1)
        if rtime > timeout:
            break

87
def wait_for_count(t, server, rrtype, count, timeout):
88 89
    rtime = 0
    while True:
90 91 92
        q = server.dig("example.com", rrtype, dnssec=True, bufsize=4096)
        found = q.count(rrtype)
        if found == count:
93 94 95 96 97 98
            break
        rtime = rtime + 1
        t.sleep(1)
        if rtime > timeout:
            break

99 100 101 102 103 104
def wait_after_submission(t, server):
    if DOUBLE_DS:
        wait_for_count(t, server, "CDNSKEY", 1, 10)
    else:
        t.sleep(4)

105 106
def watch_alg_rollover(t, server, zone, slave, before_keys, after_keys, desc, set_alg, set_stss, submission_cb):
    check_zone(server, zone, slave, before_keys, 1, 1, 1, desc + ": initial keys")
107 108 109 110 111 112 113

    server.dnssec(zone).single_type_signing = set_stss
    server.dnssec(zone).alg = set_alg
    server.gen_confile()
    server.reload()

    wait_for_rrsig_count(t, server, "SOA", 2, 20)
114
    check_zone(server, zone, slave, before_keys, 1 if after_keys > 1 else 2, 1, 2, desc + ": pre active")
115

116
    wait_for_count(t, server, "DNSKEY", before_keys + after_keys, 20)
117
    check_zone(server, zone, slave, before_keys + after_keys, 2, 1, 2, desc + ": both algorithms active")
118 119 120 121 122 123 124

    # wait for any change in CDS records
    CDS1 = str(server.dig(ZONE, "CDS").resp.answer[0].to_rdataset())
    t.sleep(3)
    while CDS1 == str(server.dig(ZONE, "CDS").resp.answer[0].to_rdataset()):
      t.sleep(1)

125
    cdnskeys = 2 if DOUBLE_DS else 1
126
    check_zone(server, zone, slave, before_keys + after_keys, 2, cdnskeys, 2, desc + ": new KSK ready")
127 128

    submission_cb()
129
    wait_after_submission(t, server)
130
    check_zone(server, zone, slave, before_keys + after_keys, 2, 1, 2, desc + ": both still active")
131

132
    wait_for_count(t, server, "DNSKEY", after_keys, 20)
133
    check_zone(server, zone, slave, after_keys, 1 if before_keys > 1 else 2, 1, 2, desc + ": post active")
134 135

    wait_for_rrsig_count(t, server, "SOA", 1, 20)
136
    check_zone(server, zone, slave, after_keys, 1, 1, 1, desc + ": old alg removed")
137

138 139
def watch_ksk_rollover(t, server, zone, slave, before_keys, after_keys, total_keys, desc, set_stss, set_ksk_lifetime, submission_cb):
    check_zone(server, zone, slave, before_keys, 1, 1, 1, desc + ": initial keys")
140 141 142 143 144 145 146
    orig_ksk_lifetime = server.dnssec(zone).ksk_lifetime

    server.dnssec(zone).single_type_signing = set_stss
    server.dnssec(zone).ksk_lifetime = set_ksk_lifetime if set_ksk_lifetime > 0 else orig_ksk_lifetime
    server.gen_confile()
    server.reload()

147
    wait_for_count(t, server, "DNSKEY", total_keys, 20)
148 149

    t.sleep(3)
150
    check_zone(server, zone, slave, total_keys, 1, 1, 1, desc + ": published new")
151 152

    wait_for_rrsig_count(t, server, "DNSKEY", 2, 20)
153
    cdnskeys = 2 if DOUBLE_DS else 1
154
    check_zone(server, zone, slave, total_keys, 2, cdnskeys, 1 if before_keys > 1 and after_keys > 1 else 2, desc + ": new KSK ready")
155 156 157 158 159

    server.dnssec(zone).ksk_lifetime = orig_ksk_lifetime
    server.gen_confile()
    server.reload()

160
    t.sleep(server.dnssec(zone).propagation_delay + 1) # check that Knot does wait for the submittion to succeed
161
    submission_cb()
162
    wait_after_submission(t, server)
163
    if before_keys < 2 or after_keys > 1:
164
        check_zone(server, zone, slave, total_keys, 2, 1, 1 if before_keys > 1 else 2, desc + ": both still active")
165
    # else skip the test as we have no control on KSK and ZSK retiring asynchronously
166 167 168

    wait_for_rrsig_count(t, server, "DNSKEY", 1, 20)
    if before_keys < 2 or after_keys > 1:
169
        check_zone(server, zone, slave, total_keys, 1, 1, 1, desc + ": old key retired")
170 171
    # else skip the test as we have no control on KSK and ZSK retiring asynchronously

172
    wait_for_count(t, server, "DNSKEY", after_keys, 20)
173
    check_zone(server, zone, slave, after_keys, 1, 1, 1, desc + ": old key removed")
174 175 176 177 178 179 180

t = Test()

parent = t.server("knot")
parent_zone = t.zone("com.", storage=".")
t.link(parent_zone, parent)

181 182
parent.dnssec(parent_zone).enable = True

183
child = t.server("knot")
184
slave = t.server("knot")
185
child_zone = t.zone("example.com.", storage=".")
186
t.link(child_zone, child, slave, ixfr=True)
187 188 189 190 191

def cds_submission():
    cds = child.dig(ZONE, "CDS")
    cds_rdata = cds.resp.answer[0].to_rdataset()[0].to_text()
    up = parent.update(parent_zone)
192
    up.add(ZONE, 7, "DS", cds_rdata)
193 194 195 196 197 198
    if DOUBLE_DS:
        try:
            cds_rdata = cds.resp.answer[0].to_rdataset()[1].to_text()
            up.add(ZONE, 7, "DS", cds_rdata)
        except:
            pass
199 200 201 202 203 204
    up.send("NOERROR")

child.zonefile_sync = 24 * 60 * 60

child.dnssec(child_zone).enable = True
child.dnssec(child_zone).manual = False
205
child.dnssec(child_zone).alg = "ECDSAP384SHA384"
206 207 208 209 210 211
child.dnssec(child_zone).dnskey_ttl = 2
child.dnssec(child_zone).zsk_lifetime = 99999
child.dnssec(child_zone).ksk_lifetime = 300 # this can be possibly left also infinity
child.dnssec(child_zone).propagation_delay = 11
child.dnssec(child_zone).ksk_sbm_check = [ parent ]
child.dnssec(child_zone).ksk_sbm_check_interval = 2
212
child.dnssec(child_zone).ksk_shared = True
213
child.dnssec(child_zone).cds_publish = "always"
214 215
if DOUBLE_DS:
    child.dnssec(child_zone).cds_publish = "double-ds"
216 217 218 219 220 221 222

# parameters
ZONE = "example.com."

t.start()
child.zone_wait(child_zone)

223 224 225
cds_submission()
t.sleep(5)

226
pregenerate_key(child, child_zone, "ECDSAP256SHA256")
227
watch_alg_rollover(t, child, child_zone, slave, 2, 1, "KZSK to CSK alg", "ECDSAP256SHA256", True, cds_submission)
228

229
pregenerate_key(child, child_zone, "ECDSAP256SHA256")
230
watch_ksk_rollover(t, child, child_zone, slave, 1, 1, 2, "CSK rollover", True, 27, cds_submission)
231

232
pregenerate_key(child, child_zone, "ECDSAP256SHA256")
233
watch_ksk_rollover(t, child, child_zone, slave, 1, 2, 3, "CSK to KZSK", False, 0, cds_submission)
234

235
pregenerate_key(child, child_zone, "ECDSAP256SHA256")
236
watch_ksk_rollover(t, child, child_zone, slave, 2, 2, 3, "KSK rollover", False, 27, cds_submission)
237

238
pregenerate_key(child, child_zone, "ECDSAP256SHA256")
239
watch_ksk_rollover(t, child, child_zone, slave, 2, 1, 3, "KZSK to CSK", True, 0, cds_submission)
240

241
pregenerate_key(child, child_zone, "ECDSAP384SHA384")
242
watch_alg_rollover(t, child, child_zone, slave, 1, 1, "CSK to CSK alg", "ECDSAP384SHA384", True, cds_submission)
243

244
pregenerate_key(child, child_zone, "ECDSAP256SHA256")
245
watch_alg_rollover(t, child, child_zone, slave, 1, 2, "CSK to KZSK alg", "ECDSAP256SHA256", False, cds_submission)
246

247
pregenerate_key(child, child_zone, "ECDSAP384SHA384")
248
watch_alg_rollover(t, child, child_zone, slave, 2, 2, "KZSK alg", "ECDSAP384SHA384", False, cds_submission)
249

250
t.end()