diff --git a/gestion/gen_confs/bind2.py b/gestion/gen_confs/bind2.py new file mode 100644 index 00000000..e1e6b337 --- /dev/null +++ b/gestion/gen_confs/bind2.py @@ -0,0 +1,245 @@ +#! /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]))