Beispiel #1
0
def update_dns(config, record, sa_version):
    "Update the DNS record"
    try:
        domain = config.get('domain_name', 'sa.baruwa.com.')
        dns_key = config.get('domain_key')
        dns_ip = config.get('domain_ip', '127.0.0.1')
        keyring = tsigkeyring.from_text({domain: dns_key})
        transaction = update.Update(domain,
                                    keyring=keyring,
                                    keyalgorithm=tsig.HMAC_SHA512)
        txtrecord = '%s.%s' % (sa_version, domain)
        transaction.replace(txtrecord, 120, 'txt', record)
        query.tcp(transaction, dns_ip)
        return True
    except DNSException, msg:
        raise SaChannelUpdateDNSError(msg)
Beispiel #2
0
def return_response_tcp(name, server):

    name_message, server_addr = query_data(name, server, "A")

    response = query.tcp(name_message, server_addr, 2.0)

    return response
Beispiel #3
0
    def add_record(self, new_ip, ttl=300):
        """
        Adds an A record for the current instance.
        """
        o_update = update.Update(DOMAIN_NAME,
                                 keyring=KEYRING,
                                 keyalgorithm=KEYALGORITHM)
        o_update.add(self.hostname, ttl, 'A', new_ip)

        try:
            query.tcp(o_update, NAMESERVER)
        except Exception as e:
            logger.error("Attempt to add A record {} to {} failed.".format(
                new_ip, self.hostname))
            logger.debug(e)
            raise
        logger.info("A record {} added to {}.".format(new_ip, self.hostname))
Beispiel #4
0
def update_dns(config, record, sa_version):
    "Update the DNS record"
    try:
        domain = config.get('domain_name', 'sa.baruwa.com.')
        dns_key = config.get('domain_key')
        dns_ip = config.get('domain_ip', '127.0.0.1')
        keyring = tsigkeyring.from_text({domain: dns_key})
        transaction = update.Update(
            domain,
            keyring=keyring,
            keyalgorithm=tsig.HMAC_SHA512)
        txtrecord = '%s.%s' % (sa_version, domain)
        transaction.replace(txtrecord, 120, 'txt', record)
        query.tcp(transaction, dns_ip)
        return True
    except DNSException, msg:
        raise SaChannelUpdateDNSError(msg)
Beispiel #5
0
def create_challenge_responses_in_dns(zones, fqdn_challenges):
    """
    Create the expected challenge response in dns

    @param zones:           dict of zones, where each zone has a list of fqdns
                            as values
    @type zones:            dict()
    @param fqdn_challenges: dict of zones, containing challenge response
                            (key) of zone
    @type fqdn_challenges:  dict()
    @rtype:                 None
    @exceptions             Can''t parse ddns key or
                            DNS update failed for zone {} with rcode: {}
    """

    if Misc.LE_ZONE_UPDATE_METHOD == 'zone_file':

        for zone in zones.keys():
            dest = str(Pathes.zone_file_root / zone /
                       Pathes.zone_file_include_name)
            lines = []
            for fqdn in zones[zone]:
                sld('fqdn: {}'.format(fqdn))
                lines.append(
                    str('_acme-challenge.{}.  IN TXT  \"{}\"\n'.format(
                        fqdn, fqdn_challenges[fqdn].key)))
            sli('Writing RRs: {}'.format(lines))
            with open(dest, 'w') as file:
                file.writelines(lines)
                ##os.chmod(file.fileno(), Pathes.zone_tlsa_inc_mode)
                ##os.chown(file.fileno(), pathes.zone_tlsa_inc_uid, pathes.zone_tlsa_inc_gid)
            updateZoneCache(zone)
        updateSOAofUpdatedZones()

    elif Misc.LE_ZONE_UPDATE_METHOD == 'ddns':

        txt_datatape = rdatatype.from_text('TXT')
        for zone in zones.keys():
            the_update = ddns_update(zone)
            for fqdn in zones[zone]:
                the_update.delete('_acme-challenge.{}.'.format(fqdn),
                                  txt_datatape)
                the_update.add('_acme-challenge.{}.'.format(fqdn), 60,
                               txt_datatape, fqdn_challenges[fqdn].key)
                sld('DNS update of RR: {}'.format(
                    '_acme-challenge.{}.  60 TXT  \"{}\"'.format(
                        fqdn, fqdn_challenges[fqdn].key)))
            response = dns_query.tcp(the_update, '127.0.0.1', timeout=10)
            sld('DNS update delete/add returned response: {}'.format(response))
            rc = response.rcode()
            if rc != 0:
                sle('DNS delete failed for zone {} with rcode: {}:\n{}'.format(
                    zone, rc.to_text(rc), rc))
                raise Exception(
                    'DNS update failed for zone {} with rcode: {}'.format(
                        zone, rc.to_text(rc)))
Beispiel #6
0
    def _query_ns(self):
        domain = dns.name.from_text(self.domain)
        if not domain.is_absolute():
            domain = domain.concatenate(dns.name.root)
        request = dns.message.make_query(domain, dns.rdatatype.ANY)

        res = query.tcp(
            request, where='127.0.0.1',
            port=self.conf.port)
        print [str(a) for a in res.answer]
Beispiel #7
0
    def replace_records(self, new_ip, ttl=300):
        """
        Replaces all existing A records for the current instance with a single
        new one.
        """
        o_update = update.Update(DOMAIN_NAME,
                                 keyring=KEYRING,
                                 keyalgorithm=KEYALGORITHM)
        o_update.replace(self.hostname, ttl, 'A', new_ip)

        try:
            query.tcp(o_update, NAMESERVER)
        except Exception as e:
            logger.error("Attempt to replace A records for {} failed.".format(
                self.hostname))
            logger.debug(e)
            raise SystemExit
        logger.info("All A records for {} replaced with {}.".format(
            self.hostname, new_ip))
Beispiel #8
0
 def query(self, message, verify=True):
     try:
         if self.type == "tcp":
             response = query.tcp(message, self.host, port=self.port)
         elif self.type == "udp":
             response = query.udp(message, self.host, port=self.port)
         if self.verify(message):
             return response
     except:
         return None
Beispiel #9
0
def resolve_name(name):
    _log.debug("DNS resolve for %s", name)
    if libnet.is_ipaddr(name):
        name = reversename.from_address(name)
    msg = message.make_query(name, "ANY")
    resp = query.tcp(msg, _config["nameserver"])
    dnslines = []
    for rr in resp.answer:
        rrtype = rdatatype.to_text(rr.rdtype)
        if rrtype in _config["dns_rr_types"]:
            if rrtype != "TXT":
                dnslines += ["{} {} {}".format(name, rrtype, str(r) if rrtype != "TXT" else str(r).tolower()) for r in rr]
    dnslines.sort()
    result = "\n".join(dnslines)
    return result
Beispiel #10
0
def delete_TLSA(cert_meta: Certificate) -> None:
    """
    Delete all TLSA RRs per fqdn of all altnames either in flatfile (make include file empty) or in dyn dns
    :param cert_meta:
    :return:
    """

    if Pathes.tlsa_dns_master == '':  # DNS master on local host

        if Misc.LE_ZONE_UPDATE_METHOD == 'zone_file':

            for (zone, fqdn) in cert_meta.zone_and_FQDN_from_altnames():
                filename = fqdn + '.tlsa'
                dest = str(Pathes.zone_file_root / zone / filename)

                #just open for write without writing, which makes file empty
                with open(dest, 'w') as fd:
                    sli('Truncating {}'.format(dest))
                updateZoneCache(zone)

        elif Misc.LE_ZONE_UPDATE_METHOD == 'ddns':

            zones = {}
            for (zone, fqdn) in cert_meta.zone_and_FQDN_from_altnames():
                if zone in zones:
                    if fqdn not in zones[zone]: zones[zone].append(fqdn)
                else:
                    zones[zone] = [fqdn]
            for zone in zones:
                the_update = ddns_update(zone)
                for fqdn in zones[zone]:
                    for prefix in cert_meta.tlsaprefixes.keys():
                        tag = str(prefix.format(fqdn)).split(maxsplit=1)[0]
                        sld('Deleting TLSA with tag {} an fqdn {} in zone {}'.
                            format(tag, fqdn, zone))
                        the_update.delete(tag)
                response = dns_query.tcp(the_update, '127.0.0.1', timeout=10)
                rc = response.rcode()
                if rc != 0:
                    sle('DNS update failed for zone {} with rcode: {}:\n{}'.
                        format(zone, response.rcode.to_text(rc),
                               response.rcode))
                    raise Exception(
                        'DNS update failed for zone {} with rcode: {}'.format(
                            zone, response.rcode.to_text(rc)))
Beispiel #11
0
def delete_challenge_responses_in_dns(zones):
    """
    Delete the challenge response in dns, created by
                            create_challenge_responses_in_dns()

    @param zones:           dict of zones, where each zone has a list of fqdns
                            as values
    @type zones:            dict()
    @rtype:                 None
    @exceptions             Can''t parse ddns key or
                            DNS update failed for zone {} with rcode: {}
    """

    if Misc.LE_ZONE_UPDATE_METHOD == 'zone_file':

        for zone in zones.keys():
            dest = str(Pathes.zone_file_root / zone /
                       Pathes.zone_file_include_name)
            with open(dest, 'w') as file:
                file.writelines(('', ))
            updateZoneCache(zone)
        updateSOAofUpdatedZones()

    elif Misc.LE_ZONE_UPDATE_METHOD == 'ddns':

        txt_datatape = rdatatype.from_text('TXT')
        for zone in zones.keys():
            the_update = ddns_update(zone)
            for fqdn in zones[zone]:
                the_update.delete('_acme-challenge.{}.'.format(fqdn),
                                  txt_datatape)
            response = dns_query.tcp(the_update, '127.0.0.1', timeout=10)
            sld('DNS update delete/add returned response: {}'.format(response))
            rc = response.rcode()
            if rc != 0:
                sle('DNS update failed for zone {} with rcode: {}:\n{}'.format(
                    zone, rc.to_text(rc), rc))
                raise Exception(
                    'DNS update failed for zone {} with rcode: {}'.format(
                        zone, rc.to_text(rc)))
Beispiel #12
0
 def check_dns(domain, ns):
     ns_s = filter(len, map(lambda s: s.strip(),
                            ns.replace('"', '').split(';')))
     if len(ns_s) < 1:
         return {}
     mess = dns.message.make_query(dns_name.from_text(domain),
                                   dns.rdatatype.SOA)
     result = {}
     for ns in ns_s:
         try:
             name_s = dns_name.from_text(ns.split()[0]).to_text()
             answer = query.tcp(mess, name_s, timeout=2)
             if len(answer.authority):
                 result[ns] = True
             else:
                 rr = answer.answer[0][0]
                 if rr.rdtype == dns.rdatatype.SOA:
                     result[ns] = True
                 else:
                     result[ns] = False
         except:
             result[ns] = False
     return result
Beispiel #13
0
def dnsck_query(
    dns_server: str,
    dns_query: str,
    record_type: str,
    iterations: int,
    tcp: bool = False,
    nosleep: bool = False,
) -> int:
    """Perform a DNS query for a set number of iterations.

    Args:
        dns_server (str): IP address of server.
        dns_query (str): Query to lookup.
        record_type (str): Record type.
        iterations (int): Number of iterations.
        tcp (bool): Use TCP for query.
        nosleep (bool): Disable sleep.

    Returns:
        int: Number of errors.

    """
    result_code_dict: DefaultDict[str, int] = defaultdict(int)
    query_times = []  # type: List[float]
    record_number = 0  # type: int
    response_errors = 0  # type: int
    iteration_count = 0  # type: int

    try:
        make_dns_query = message.make_query(dns_query,
                                            record_type.upper(),
                                            use_edns=True)
    except rdatatype.UnknownRdatatype:
        print("Unknown record type, try again.")
        sys.exit(1)
    print(
        f"Performing {iterations} queries to server {dns_server} for domain {dns_query}",
        f"with record type {record_type.upper()}.\n",
    )

    try:
        for iteration in range(iterations):
            print(f"[Query {iteration + 1} of {iterations}]")
            try:
                if tcp:
                    dns_response = query.tcp(make_dns_query,
                                             dns_server,
                                             timeout=10)
                else:
                    dns_response = query.udp(make_dns_query,
                                             dns_server,
                                             timeout=10)
                if dns_response.answer:
                    for answer in dns_response.answer:
                        print(answer)
                        record_number = len(answer)
                else:
                    print("No records returned.")
                elapsed_time = dns_response.time * 1000  # type: float
                if elapsed_time < 500:
                    result_code = rcode.to_text(
                        dns_response.rcode())  # type: str
                    result_code_dict[result_code] += 1
                    iteration_count += 1
                else:
                    result_code = "Degraded"
                    result_code_dict[result_code] += 1
                    iteration_count += 1
                    response_errors += 1
            except exception.Timeout:
                print("Query timeout.")
                result_code = "Timeout"
                result_code_dict[result_code] += 1
                elapsed_time = 10000
                iteration_count += 1
                response_errors += 1
            if not nosleep:
                time.sleep(1)
            query_times.append(elapsed_time)
            print(f"Records returned: {record_number}")
            print(f"Response time: {elapsed_time:.2f} ms")
            print(f"Response status: {result_code}\n")
    except KeyboardInterrupt:
        print("Program terminating...")

    print("Response status breakdown:")
    for query_rcode, count in result_code_dict.items():
        print(f"{count} {query_rcode}")
    print(
        f"\nSummary: Performed {iteration_count} queries to server {dns_server}",
        f"for domain {dns_query} with record type {record_type.upper()}.",
        f"\nResponse errors: {response_errors / iteration_count * 100:.2f}%",
    )
    print(
        f"Average response time: {sum(query_times) / len(query_times):.2f} ms\n"
    )

    return response_errors
Beispiel #14
0
def distribute_tlsa_rrs(cert_meta: Certificate,
                        hashes: Union[Tuple[str], List[str]]) -> None:
    """
    Distribute TLSA RR.
    Puts TLSA RR fqdn into DNS zone, by dynamic dns or editing zone file and updating zone cache.
    If cert has altnames, one set of TLSA RRs is inserted per altname and per TLSA prefix.
    :param cert_meta:
    :param hashes: list of hashes, may include active and prepublishes hashes for all algos
    :return:
    """

    if len(cert_meta.tlsaprefixes) == 0: return

    sli('Distributing TLSA RRs for DANE.')

    if Pathes.tlsa_dns_master == '':  # DNS master on local host

        if Misc.LE_ZONE_UPDATE_METHOD == 'zone_file':

            for (zone, fqdn) in zone_and_FQDN_from_altnames(cert_meta):
                filename = fqdn + '.tlsa'
                dest = str(Pathes.zone_file_root / zone / filename)
                sli('{} => {}'.format(filename, dest))
                tlsa_lines = []
                for prefix in cert_meta.tlsaprefixes.keys():
                    for hash in hashes:
                        tlsa_lines.append(
                            str(prefix.format(fqdn) + ' ' + hash + '\n'))
                with open(dest, 'w') as fd:
                    fd.writelines(tlsa_lines)
                updateZoneCache(zone)

        elif Misc.LE_ZONE_UPDATE_METHOD == 'ddns':

            tlsa_datatype = rdatatype.from_text('TLSA')
            zones = {}
            for (zone, fqdn) in cert_meta.zone_and_FQDN_from_altnames():
                if zone in zones:
                    if fqdn not in zones[zone]: zones[zone].append(fqdn)
                else:
                    zones[zone] = [fqdn]
            for zone in zones:
                the_update = ddns_update(zone)
                for fqdn in zones[zone]:
                    for prefix in cert_meta.tlsaprefixes.keys():
                        pf_with_fqdn = str(prefix.format(fqdn))
                        fields = pf_with_fqdn.split(maxsplit=4)
                        sld('Deleting possible old TLSAs: {}'.format(
                            fields[0]))
                        the_update.delete(fields[0], tlsa_datatype)

                        for hash in hashes:
                            sld('Adding TLSA: {} {} {} {}'.format(
                                fields[0], int(fields[1]), fields[3],
                                fields[4] + ' ' + hash))
                            the_update.add(fields[0], int(fields[1]),
                                           fields[3], fields[4] + ' ' + hash)

                response = dns_query.tcp(the_update, '127.0.0.1', timeout=10)
                rc = response.rcode()
                if rc != 0:
                    sle('DNS update failed for zone {} with rcode: {}:\n{}'.
                        format(zone, response.rcode.to_text(rc),
                               response.rcode))
                    raise Exception(
                        'DNS update add failed for zone {} with rcode: {}'.
                        format(zone, response.rcode.to_text(rc)))

    else:  # remote DNS master ( **INCOMPLETE**)
        sle('Remote DNS master server is currently not supported. Must be on same host as this script.'
            )
        exit(1)
        with ssh_connection(Pathes.tlsa_dns_master) as client:
            with client.open_sftp() as sftp:
                chdir(str(Pathes.work_tlsa))
                p = Path('.')
                sftp.chdir(str(Pathes.zone_file_root))

                for child_dir in p.iterdir():
                    for child in child_dir.iterdir():
                        sli('{} => {}:{}'.format(child, Pathes.tlsa_dns_master,
                                                 child))
                        fat = sftp.put(str(child), str(child), confirm=True)
                        sld('size={}, uid={}, gid={}, mtime={}'.format(
                            fat.st_size, fat.st_uid, fat.st_gid, fat.st_mtime))