#! /usr/bin/env python # -*- coding: utf-8 -*- import sys import base64 import hashlib import netaddr sys.path.append('/usr/scripts/') from gestion import config class ResourceRecord(object): def __init__(self, type, name, value, ttl=None): self._type=type self._name=name self._value=value self._ttl=ttl def __str__(self): if self._ttl: return "%s\t%s\tIN\t%s\t%s" % (self._name, self._ttl, self._type, self._value) else: return "%s\tIN\t%s\t%s" % (self._name, self._type, self._value) def __repr__(self): return str(self) class SOA(ResourceRecord): def __init__(self, master, email, serial, refresh, retry, expire, ttl): super(SOA, self).__init__('SOA', '', '%s. %s. (\n %s ; numero de serie\n %s ; refresh (s)\n %s ; retry (s)\n %s ; expire (s)\n %s ; TTL (s)\n )' % (master, email, serial, refresh, retry, expire, ttl)) class A(ResourceRecord): def __init__(self, name, value, ttl=None): super(A, self).__init__('A', name, value, ttl) class PTR(ResourceRecord): def __init__(self, name, value, ttl=None): super(PTR, self).__init__('PTR', name, value, ttl) class AAAA(ResourceRecord): def __init__(self, name, value, ttl=None): super(AAAA, self).__init__('AAAA', name, value, ttl) class TXT(ResourceRecord): def __init__(self, name, value, ttl=None): super(TXT, self).__init__('TXT', name, value, ttl) class CNAME(ResourceRecord): def __init__(self, name, value, ttl=None): super(CNAME, self).__init__('CNAME', name, value, ttl) class DNAME(ResourceRecord): def __init__(self, name, value, ttl=None): super(DNAME, self).__init__('DNAME', name, value, ttl) class MX(ResourceRecord): def __init__(self, name, priority, value, ttl=None): super(MX, self).__init__('MX', name, '%s\t%s' % (priority, value), ttl) class NS(ResourceRecord): def __init__(self, name, value, ttl=None): super(NS, self).__init__('NS', name, value, ttl) class SPF(ResourceRecord): def __init__(self, name, value, ttl=None): super(SPF, self).__init__('SPF', name, value, ttl) class SRV(ResourceRecord): def __init__(self, service, proto, name, priority, weight, port, target, ttl=None): super(SRV, self).__init__('SRV', '_%s._%s.%s' % (service, proto, name), '%s\t%s\t%s\t%s' % (priority, weight, port, target), ttl) class NAPTR(ResourceRecord): def __init__(self, order, preference, flag, service, replace_regexpr, value, ttl=None): super(NAPTR, self).__init__('NAPTR', name, '%s\t%s\t"%s"\t"%s"\t"%s"\t%s' % (order, preference, flag, service, replace_regexpr, value), ttl) class SSHFP(ResourceRecord): def __init__(self, name, hash, algo, key, ttl=None): if not hash in config.sshfp_hash.keys(): raise ValueError('Hash %s invalid, valid hash are %s' % (hash, ', '.join(config.sshfp_host.keys()))) if not algo in config.sshfp_algo.keys(): raise ValueError('Algo %s unknown, valid values are %s' % (algo, ', '.join(config.sshfp_algo.keys()))) super(SSHFP, self).__init__('SSHFP', name, '%s\t%s\t%s' % (config.sshfp_algo[algo][0], config.sshfp_hash[hash], getattr(hashlib, hash)(base64.b64decode(key)).hexdigest()), ttl) class ZoneBase(object): def __init__(self): self._rrlist=[] def __str__(self): ret=""";*********************************************************** ; Ce fichier est genere par les scripts de gen_confs ; Les donnees proviennent de la base LDAP et de la conf ; presente au debut du script. ; ; NE PAS EDITER ; ;*********************************************************** $ORIGIN %s. $TTL %s """ % (self.zone_name, self.ttl) for rr in self._rrlist: ret+="%s\n" % rr return ret def add(self, rr): if isinstance(rr, ResourceRecord): self._rrlist.append(rr) class ZoneClone(ZoneBase): def __init__(self, zone_name, zone_clone, soa): super(ZoneClone, self).__init__() self.zone_name = zone_name self.zone_clone = zone_clone self.add(soa) self.add(DNAME('', "%s." % self.zone_clone.zone_name)) for rr in self.zone_clone._rrlist[1:]: if rr._name in ['', '@']: self.add(rr) if rr._name in ["%s." % self.zone_clone.zone_name]: self.add(ResourceRecord(rr._type, "%s." % "%s.", rr._value)) class Zone(ZoneBase): def __init__(self, zone_name, ttl, soa, ns_list, ipv6=True, ipv4=True): super(Zone, self).__init__() self.zone_name = zone_name self.ttl = ttl self.ipv4=ipv4 self.ipv6=ipv6 self.add(soa) for ns in ns_list: self.add(NS('@', '%s.' % ns)) def get_name(self, hostname): if str(hostname).endswith(self.zone_name): ret=str(hostname)[0:- len(self.zone_name) -1] if ret == "": return "@" else: return ret else: print "%s not in zone %s" % (hostname, self.zone_name) return None def add_delegation(zone, server): zone = self.het_name(zone) if zone: self.add(NS('@', '%s.' % server)) def add_a_record(self, nom, machine): if self.ipv4: for ip in machine.get('ipHostNumber', []): self.add(A(nom, ip)) if self.ipv6: if nom == '@': self.add(A("v4", ip)) else: self.add(A("%s.v4" % nom, ip)) def add_aaaa_record(self, nom, machine): if self.ipv6: for ip in machine.get('ip6HostNumber', []): self.add(AAAA(nom, ip)) if self.ipv4: if nom == '@': self.add(AAAA("v6", ip)) else: self.add(AAAA("%s.v6" % nom, ip)) def add_sshfp_record(self, nom, machine): for sshkey in machine.get('sshFingerprint', []): algo_txt, key = str(sshkey).split()[:2] algo=config.sshfs_ralgo[algo_txt][1] for hash in config.sshfp_hash.keys(): self.add(SSHFP(nom, hash, algo, key)) if self.ipv4 and self.ipv6: if nom == '@': self.add(SSHFP("v4", hash, algo, key)) self.add(SSHFP("v6", hash, algo, key)) else: self.add(SSHFP("%s.v4" % nom, hash, algo, key)) self.add(SSHFP("%s.v6" % nom, hash, algo, key)) def add_machine(self, machine): for host in machine['host']: nom=self.get_name(host) if nom is None: continue self.add_a_record(nom, machine) self.add_aaaa_record(nom, machine) self.add_sshfp_record(nom, machine) if machine['host']: for alias in machine.get('hostAlias', []): alias = self.get_name(alias) if alias is None: continue to_nom, to_zone = str(machine['host'][0]).split('.', 1) if alias in ['@', '%s.' % self.zone_name]: self.add_a_record(alias, machine) self.add_aaaa_record(alias, machine) self.add_sshfp_record(alias, machine) elif to_zone == self.zone_name: self.add(CNAME(alias, "%s" % to_nom)) if self.ipv4 and self.ipv6: self.add(CNAME("%s.v4" % alias, "%s.v4" % to_nom)) self.add(CNAME("%s.v6" % alias, "%s.v6" % to_nom)) else: self.add(CNAME(alias, "%s." % machine['host'][0])) def reverse(net, ip): """Renvoie la zone DNS inverse correspondant au réseau et à l'adresse donnés, ainsi que le nombre d'éléments de l'ip a mettre dans le fichier de zone.""" n = netaddr.IPNetwork(net) a = netaddr.IPAddress(ip) rev_dns_a = a.reverse_dns.split('.')[:-1] assert a in n if n.version == 4: if n.prefixlen == 8: return ('.'.join(rev_dns_a[3:]), 3) elif n.prefixlen == 16: return ('.'.join(rev_dns_a[2:]), 2) else: return ('.'.join(rev_dns_a[1:]), 1) elif n.version == 6: return ('.'.join(rev_dns_a[(128-n.prefixlen)/4:]), (128-n.prefixlen)/4) class ZoneReverse(Zone): def __init__(self, net, ttl, soa, ns_list): self.net = net zone_name = reverse(net, net.split('/')[0])[0] if '.' in net: ipv6=False ipv4=True elif ':' in net: ipv6=True ipv4=False else: raise ValueError("net should be an ipv4 ou ipv6 network") super(ZoneReverse, self).__init__(zone_name, ttl, soa, ns_list, ipv6=ipv6, ipv4=ipv4) def add_machine(self, machine): if machine['host']: if self.ipv4: for ip in machine['ipHostNumber']: zone, length = reverse(self.net, str(ip)) nom = '.'.join(ip.value.reverse_dns.split('.')[:length]) if zone != self.zone_name: continue self.add(PTR(nom, '%s.' % machine['host'][0]))