Home | History | Annotate | Line # | Download | only in ans1
      1 #!/usr/bin/python3
      2 
      3 # Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      4 #
      5 # SPDX-License-Identifier: MPL-2.0
      6 
      7 from dataclasses import dataclass
      8 from pathlib import Path
      9 
     10 import json
     11 
     12 from cryptography.hazmat.primitives import serialization
     13 
     14 import dns.dnssec
     15 import dns.message
     16 import dns.name
     17 import dns.rdata
     18 import dns.rdataclass
     19 import dns.rdatatype
     20 import dns.rrset
     21 
     22 TTL = 300
     23 
     24 
     25 @dataclass(frozen=True)
     26 class Key:
     27     zone: dns.name.Name
     28     private_key: object
     29     dnskey: dns.rdata.Rdata
     30     ds: dns.rdata.Rdata
     31 
     32 
     33 def name(text: str) -> dns.name.Name:
     34     return dns.name.from_text(text)
     35 
     36 
     37 def load_keys() -> dict[str, Key]:
     38     path = Path(".") / "keys.json"
     39     with path.open(encoding="utf-8") as keys_file:
     40         raw_keys = json.load(keys_file)
     41 
     42     keys = {}
     43     for zone, raw_key in raw_keys.items():
     44         private_key = serialization.load_pem_private_key(
     45             raw_key["private_pem"].encode("ascii"),
     46             password=None,
     47         )
     48         dnskey = dns.rdata.from_text(
     49             dns.rdataclass.IN, dns.rdatatype.DNSKEY, raw_key["dnskey"]
     50         )
     51         ds = dns.rdata.from_text(dns.rdataclass.IN, dns.rdatatype.DS, raw_key["ds"])
     52         keys[zone] = Key(name(zone), private_key, dnskey, ds)
     53 
     54     return keys
     55 
     56 
     57 def rrset(owner: str, rdtype: dns.rdatatype.RdataType, *rdatas: str) -> dns.rrset.RRset:
     58     return dns.rrset.from_text(owner, TTL, dns.rdataclass.IN, rdtype, *rdatas)
     59 
     60 
     61 def rrset_from_rdata(owner: str, rdata: dns.rdata.Rdata) -> dns.rrset.RRset:
     62     return dns.rrset.from_rdata(owner, TTL, rdata)
     63 
     64 
     65 def add_signed(
     66     section: list[dns.rrset.RRset], covered: dns.rrset.RRset, signer: Key
     67 ) -> None:
     68     rrsig = dns.dnssec.sign(
     69         covered,
     70         signer.private_key,
     71         signer.zone,
     72         signer.dnskey,
     73         lifetime=86400,
     74         verify=True,
     75     )
     76     section.append(covered)
     77     section.append(dns.rrset.from_rdata(covered.name, covered.ttl, rrsig))
     78 
     79 
     80 def soa_rrset(zone) -> dns.rrset.RRset:
     81     return rrset(
     82         zone,
     83         dns.rdatatype.SOA,
     84         f"ns.{zone} hostmaster.{zone} 1 3600 600 86400 300",
     85     )
     86 
     87 
     88 def add_dnskey(response: dns.message.Message, zone: str, key: Key) -> None:
     89     add_signed(response.answer, rrset_from_rdata(zone, key.dnskey), key)
     90 
     91 
     92 def wildcard_rrsig(owner: str, a: str, key: Key) -> dns.rrset.RRset:
     93     wildcard = rrset(key.zone, dns.rdatatype.A, a)
     94     rrsig = dns.dnssec.sign(
     95         wildcard,
     96         key.private_key,
     97         key.zone,
     98         key.dnskey,
     99         lifetime=86400,
    100         verify=True,
    101     )
    102     return dns.rrset.from_rdata(name(owner), wildcard.ttl, rrsig)
    103