def add_zonemd(zone, zonemd_algorithm='sha384', zonemd_ttl=None): """ Add a ZONEMD record to a zone. This also removes any existing ZONEMD records in the zone. The ZONEMD record will be at the zone apex, and have an all-zero digest. If the TTL is not specified, then the TTL of the SOA record is used. @var zone: The zone object to update. @type zone: dns.zone.Zone @var zonemd_algorithm: The name of the algorithm to use, "sha384", or the number of the algorithm to use. @type zonemd_algorithm: str @var zonemd_ttl: The TTL to use for the ZONEMD record, or None to get this from the zone SOA. @type zonemd_ttl: int @rtype: dns.rdataset.Rdataset @raises ZoneDigestUnknownAlgorithm: zonemd_algorithm is unknown Returns the placeholder ZONEMD record added, as a ZONEMD object. """ if zonemd_algorithm in ('sha384', 1): algorithm = 1 else: msg = 'Unknown digest ' + zonemd_algorithm raise ZoneDigestUnknownAlgorithm(msg) empty_digest = _EMPTY_DIGEST_BY_ALGORITHM[algorithm] # Remove any existing ZONEMD from the zone. # Also find the first name, which will be the zone name. for name in zone: zone.delete_rdataset(name, ZONEMD_RTYPE) zone_name = min(zone.keys()) # Get the zone name. zone_name = min(zone.keys()) # Get the SOA. soa_rdataset = zone.get_rdataset(zone_name, dns.rdatatype.SOA) soa = soa_rdataset.items[0] # Get the TTL to use for our placeholder ZONEMD. if zonemd_ttl is None: zonemd_ttl = soa_rdataset.ttl # Build placeholder ZONEMD and add to the zone. placeholder = dns.rdataset.Rdataset(dns.rdataclass.IN, ZONEMD_RTYPE) placeholder.update_ttl(zonemd_ttl) placeholder_rdata = ZONEMD(dns.rdataclass.IN, soa.serial, algorithm, empty_digest) placeholder.add(placeholder_rdata) zone.replace_rdataset(zone_name, placeholder) return placeholder_rdata
def remove_dnssec_from_zone(zone, only_remove_sigs): for record in list(zone.iterate_rdatas()): zone.delete_rdataset(record[0], dns.rdatatype.RRSIG, covers=record[2].rdtype) zone.delete_rdataset(record[0], dns.rdatatype.NSEC) zone.delete_rdataset(record[0], dns.rdatatype.NSEC3) if (not only_remove_sigs): zone.delete_rdataset(record[0], dns.rdatatype.NSEC3PARAM) zone.delete_rdataset(record[0], dns.rdatatype.DNSKEY)
def delete(hostname=None, UUID=None): if mongo.db.users.find_one({'token': UUID}): zonefile = '/etc/bind/db.example.com' zone = dns.zone.from_file(zonefile, os.path.basename(zonefile)) zone.delete_rdataset(hostname, dns.rdatatype.A) # restart bind for changes to take effect subprocess.call(["sudo", "rndc", "reload"]) zone.to_file(zonefile) mongo.db.users.update({'username': session['username']}, {'$pull': { 'fqdns': hostname }}) return {"message": "A-record removed hostname {}".format(hostname)} else: return {"message": "not authorized"}
def read_zone(self, zone_name, filter_dnssec=True): """ Use dnspython to read in a Zone from the DNS server Returns a Zone Instance based on the read in data. NOTE: This is not from the DB! """ xfr_generator = dns.query.xfr(self.server, zone_name, port=self.port) try: zone = dns.zone.from_xfr(xfr_generator) except dns.exception.FormError as exc: zone = None finally: del xfr_generator # Unlink exception chaining if (not zone): raise NoSuchZoneOnServerError(zone_name, self.server_name, self.port) # Filter out dnssec if requested. dnssec_types = settings['dnssec_filter'].split() dnssec_rdtypes = [dns.rdatatype.from_text(x) for x in dnssec_types] nsec3param_rdtype = dns.rdatatype.from_text(RRTYPE_NSEC3PARAM) dnskey_rdtype = dns.rdatatype.from_text(RRTYPE_DNSKEY) # Need to find items to delete before deleting them, or # else zone data structure is corrupted. rr_delete_list = [] dnskey_flag = False nsec3param_flag = False for rdata in zone.iterate_rdatas(): if not dnskey_flag: dnskey_flag = (rdata[2].rdtype == dnskey_rdtype) if not nsec3param_flag: nsec3param_flag = (rdata[2].rdtype == nsec3param_rdtype) if rdata[2].rdtype in dnssec_rdtypes: rr_delete_list.append((rdata[0], rdata[2].rdtype, rdata[2].covers(),)) # Finally delete all unwanted records if filter_dnssec: for (name, rdtype, covers) in rr_delete_list: zone.delete_rdataset(name, rdtype, covers) # Finally, an unclutered zone without DNSSEC return (zone, dnskey_flag, nsec3param_flag)
def test_zonemd_no_digest(self): zone = dns.zone.from_text(self.simple_example, origin='example') zone.delete_rdataset(dns.name.empty, 'ZONEMD') with self.assertRaises(dns.zone.NoDigest): zone.verify_digest()
def _check_zones(zone_list, add_rrsets, delete_rrsets): """Apply changes to zones and run named-checkzone. This function always returns None. Its only effect is to raise an exception if the updates cause named-checkzone to fail. This function will check to ensure that deleted records existed previously, and added records did not exist. This duplicates the checks built into the DNS UPDATE message, but this is necessary because relying only on the DNS UPDATE constraints could mean that updates to one zone get processed but updates to another get rejected. We want to try as hard as possible to make this all-or-nothing. """ zones = {} # Acquire a copy of each zone. for zone in zone_list: axfr = dns.query.xfr(jinx_global_settings['DNS_NAMESERVER'], zone, relativize=False, port=int(jinx_global_settings['DNS_NAMESERVER_PORT'])) zones[zone] = dns.zone.from_xfr(axfr, relativize=False) # Apply the updates. for rrset in delete_rrsets: zone = zones[_get_zone(rrset.name, zone_list)] new = _rrset_to_rdataset(rrset) existing = zone.get_rdataset(rrset.name, rrset.rdtype) # Are we deleting or modifying? If an RRset with this name is in the # add group, then it's actually a modification. if rrset.name in [r.name for r in add_rrsets]: action = "modify" else: action = "delete" if existing is None: raise JinxInvalidStateError("You are attempting to %s %s, but this record does not exist in DNS." % (action, _format_rrset_name(rrset))) elif not _rdatasets_are_equal(new, existing): raise JinxInvalidStateError("You are attempting to %s %s, but its value in DNS has changed since you constructed your update." % (action, _format_rrset_name(rrset))) zone.delete_rdataset(rrset.name, rrset.rdtype) for rrset in add_rrsets: zone = zones[_get_zone(rrset.name, zone_list)] if zone.get_rdataset(rrset.name, rrset.rdtype) is not None: raise JinxInvalidStateError("You are attempting to add %s, but this record already exists in DNS." % _format_rrset_name(rrset)) rdataset = _rrset_to_rdataset(rrset) zone.replace_rdataset(rrset.name, rdataset) for name, zone in zones.iteritems(): zone_file = tempfile.NamedTemporaryFile() zone.to_file(zone_file, sorted=False, relativize=False) zone_file.flush() # Just flush, but don't close, the temp file, because Python temp files # are deleted once they're closed. try: checkzone = subprocess.Popen("/usr/sbin/named-checkzone -k fail -m fail -n fail -S ignore %s %s" % (name, zone_file.name), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) checkzone.wait() if checkzone.returncode != 0: raise JinxInvalidRequestError("zone %s failed zone check:\n%s" % (name, checkzone.stdout.read())) except OSError, exception: # Perhaps I should wrap this in a JinxAPIServerError, but really, # if I just let it go up, the middleware will do that for me. raise