
Il faudra sans doute voir un moyen pour récupérer les informations pour les enregistrement TLSA depuis la base ldap.
613 lines
25 KiB
Python
Executable file
613 lines
25 KiB
Python
Executable file
#! /usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
""" Génération de la configuration pour bind9
|
|
|
|
Copyright (C) Valentin Samir
|
|
Licence : GPLv3
|
|
"""
|
|
|
|
import sys
|
|
import ssl
|
|
import time
|
|
import base64
|
|
import hashlib
|
|
import binascii
|
|
import netaddr
|
|
sys.path.append('/usr/scripts/')
|
|
|
|
import lc_ldap.shortcuts
|
|
import affich_tools
|
|
from gestion.gen_confs import gen_config
|
|
from socket import gethostname
|
|
from gestion import config
|
|
import config.dns
|
|
|
|
disclamer = """//**************************************************************//
|
|
// 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. Il peut être propagé via bcfg2. //
|
|
// //
|
|
// NE PAS EDITER //
|
|
// //
|
|
//**************************************************************//
|
|
"""
|
|
|
|
def short_name(fullhostname):
|
|
return fullhostname.split(".")[0]
|
|
|
|
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 TLSA(ResourceRecord):
|
|
def __init__(self, name, port, proto, cert, certtype, reftype, compat=True, ttl=None):
|
|
"""
|
|
name: nom du domaine du certificat
|
|
port: port où écoute le service utilisant le certificat
|
|
proto: udp ou tcp
|
|
cert: le certificat au format pem (selector est donc toujours à 0)
|
|
certtype: type d'enregistrement 0 = CA pinning, 1 = cert pinning, 2 = self trusted CA, 3 = self trusted cert
|
|
reftype: 0 = plain cert, 1 = sha256, 2 = sha512
|
|
compat: on génère un enregistement compris même par les serveurs dns n'implémentant pas TLSA
|
|
"""
|
|
selector = 0
|
|
if cert is None and proto == 'tcp' and name[-1] == '.':
|
|
try:
|
|
cert = ssl.get_server_certificate((name[:-1], port), ca_certs='/etc/ssl/certs/ca-certificates.crt')
|
|
except Exception as e:
|
|
raise ValueError("Unable de retrieve cert dynamically: %s" % e)
|
|
elif cert is None:
|
|
raise ValueError("cert can only be retrive if proto is tcp and name fqdn")
|
|
dercert = ssl.PEM_cert_to_DER_cert(cert)
|
|
if not dercert:
|
|
raise ValueError("Impossible de convertir le certificat au format DER %s %s %s\n%s" % (name, port, proto, cert))
|
|
certhex = TLSA.hashCert(reftype, dercert)
|
|
if compat:
|
|
super(TLSA, self).__init__(
|
|
'TYPE52',
|
|
'_%s._%s%s' % (port, proto, '.' + name if name else ''),
|
|
"\# %s 0%s0%s0%s%s" % (len(certhex)/2 +3, certtype, selector, reftype, certhex),
|
|
ttl
|
|
)
|
|
else:
|
|
super(TLSA, self).__init__(
|
|
'TLSA',
|
|
'_%s._%s%s' % (port, proto, '.' + name if name else ''),
|
|
"%s %s %s %s"% (certtype, selector, reftype, certhex),
|
|
ttl
|
|
)
|
|
|
|
@staticmethod
|
|
def hashCert(reftype, certblob):
|
|
"""
|
|
certblob: un certificat au format DER
|
|
"""
|
|
if reftype == 0:
|
|
return binascii.b2a_hex(certblob).upper()
|
|
elif reftype == 1:
|
|
hashobj = hashlib.sha256()
|
|
hashobj.update(certblob)
|
|
elif reftype == 2:
|
|
hashobj = hashlib.sha512()
|
|
hashobj.update(certblob)
|
|
else:
|
|
raise ValueError("reftype sould be 0 1 or 2, not %s" % reftype)
|
|
return hashobj.hexdigest().upper()
|
|
|
|
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 DS(ResourceRecord):
|
|
def __init__(self, name, value, ttl=None):
|
|
super(DS, self).__init__('DS', 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, priority, weight, port, target, ttl=None):
|
|
super(SRV, self).__init__('SRV', '_%s._%s' % (service, proto), '%s\t%s\t%s\t%s' % (priority, weight, port, target), ttl)
|
|
class NAPTR(ResourceRecord):
|
|
def __init__(self, name, 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, zone_name):
|
|
self._rrlist=[]
|
|
self.zone_name = zone_name
|
|
|
|
|
|
def __repr__(self):
|
|
return "<%s %s>" % (self.__class__.__name__, self.zone_name)
|
|
def __str__(self):
|
|
ret="%s\n$ORIGIN %s.\n$TTL %s\n" % (disclamer.replace('//', ';'), 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)
|
|
else:
|
|
raise ValueError("You can only add ResourceRecords to a Zone")
|
|
def extend(self, rr_list):
|
|
for rr in rr_list:
|
|
self.add(rr)
|
|
|
|
def write(self, path):
|
|
with open(path, 'w') as f:
|
|
f.write("%s" % self)
|
|
|
|
|
|
|
|
class ZoneClone(ZoneBase):
|
|
def __init__(self, zone_name, zone_clone, soa):
|
|
super(ZoneClone, self).__init__(zone_name)
|
|
self.zone_clone = zone_clone
|
|
self.ttl = zone_clone.ttl
|
|
|
|
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." % self.zone_name, rr._value))
|
|
|
|
|
|
class Zone(ZoneBase):
|
|
def __init__(self, zone_name, ttl, soa, ns_list, ipv6=True, ipv4=True, other_zones=[]):
|
|
super(Zone, self).__init__(zone_name)
|
|
self.ttl = ttl
|
|
self.ipv4 = ipv4
|
|
self.ipv6 = ipv6
|
|
self.other_zones = other_zones
|
|
self.subzones = [z for z in self.other_zones if z != self.zone_name and z.endswith(self.zone_name)]
|
|
|
|
self.add(soa)
|
|
for ns in ns_list:
|
|
self.add(NS('@', '%s.' % ns))
|
|
|
|
def name_in_subzone(self, hostname):
|
|
for zone in self.subzones:
|
|
if str(hostname).endswith(".%s" % zone):
|
|
return True
|
|
return False
|
|
|
|
def get_name(self, hostname):
|
|
# le hostname fini bien par la zone courante, et il n'appartient pas à une sous-zone
|
|
if str(hostname) == self.zone_name or str(hostname).endswith(".%s" % self.zone_name) and not self.name_in_subzone(hostname):
|
|
ret=str(hostname)[0:- len(self.zone_name) -1]
|
|
if ret == "":
|
|
return "@"
|
|
else:
|
|
return ret
|
|
else:
|
|
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', []):
|
|
if len(machine['dnsIpv6'])<1 or machine['dnsIpv6'][0].value:
|
|
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', []):
|
|
if str(alias) in self.other_zones and str(alias) != self.zone_name:
|
|
continue
|
|
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]))
|
|
|
|
|
|
class ZoneReverse(Zone):
|
|
def __init__(self, net, ttl, soa, ns_list):
|
|
if len(ZoneReverse.network_to_arpanets(net))!=1:
|
|
raise ValueError("%s n'est pas un réseau valide pour une zone de reverse dns" % net)
|
|
self.net = net
|
|
zone_name = ZoneReverse.reverse(net)[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)
|
|
|
|
@staticmethod
|
|
def reverse(net, ip=None):
|
|
"""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 si elle est fournie, n'importe
|
|
quoi sinon."""
|
|
n = netaddr.IPNetwork(net)
|
|
a = netaddr.IPAddress(ip if ip else n.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)
|
|
elif n.prefixlen == 24:
|
|
return ('.'.join(rev_dns_a[1:]), 1)
|
|
else:
|
|
raise ValueError("Bad network %s" % n)
|
|
elif n.version == 6:
|
|
return ('.'.join(rev_dns_a[(128-n.prefixlen)/4:]), (128-n.prefixlen)/4)
|
|
|
|
|
|
@staticmethod
|
|
def network_to_arpanets(nets):
|
|
"""
|
|
retourne une liste de reseaux ne contenant que
|
|
des préfixes de taille 32, 24, 16 ou 8 en ipv4
|
|
et laisse inchangé les réseaux ipv6.
|
|
"""
|
|
if not isinstance(nets, list):
|
|
nets = [nets]
|
|
subnets = []
|
|
for net in nets:
|
|
if not isinstance(net, netaddr.IPNetwork):
|
|
net = netaddr.IPNetwork(net)
|
|
if net.version == 4:
|
|
if net.prefixlen > 24:
|
|
subnets.extend(net.subnet(32))
|
|
elif net.prefixlen > 16:
|
|
subnets.extend(net.subnet(24))
|
|
elif net.prefixlen > 8:
|
|
subnets.extend(net.subnet(16))
|
|
else:
|
|
subnets.extend(net.subnet(8))
|
|
elif net.version == 6:
|
|
subnets.append(net)
|
|
return subnets
|
|
|
|
|
|
def add_machine(self, machine):
|
|
if machine['host']:
|
|
if self.ipv4:
|
|
attr = 'ipHostNumber'
|
|
elif self.ipv6:
|
|
attr = 'ip6HostNumber'
|
|
else:
|
|
raise ValueError("A reverse zone should be ipv6 or ipv6")
|
|
for ip in machine[attr]:
|
|
try:
|
|
zone, length = ZoneReverse.reverse(self.net, str(ip))
|
|
nom = '.'.join(ip.value.reverse_dns.split('.')[:length])
|
|
if zone != self.zone_name:
|
|
continue
|
|
if attr != 'ip6HostNumber' or len(machine['dnsIpv6'])<1 or machine['dnsIpv6'][0].value: # Hack pour envoyer le reverse vers l'adresse .v6 dans le cas où dnsIpv6 = False
|
|
self.add(PTR(nom, '%s.' % machine['host'][0]))
|
|
else:
|
|
rev_nom, rev_zone = str(machine['host'][0]).split('.', 1)
|
|
self.add(PTR(nom, '%s.v6.%s.' % (rev_nom, rev_zone)))
|
|
except AssertionError:
|
|
pass
|
|
|
|
|
|
class dns(gen_config) :
|
|
######################################PARTIE DE CONFIGURATION
|
|
|
|
### Fichiers à écrire
|
|
# Répertoire d'écriture des fichiers de zone
|
|
DNS_DIR = '/etc/bind/generated/' # Avec un / à la fin
|
|
DNSSEC_DIR = '/etc/bind/signed/' # Avec un / à la fin
|
|
# Fichier de définition des zones pour le maître
|
|
DNS_CONF = DNS_DIR + 'zones_crans'
|
|
|
|
# Fichier de définition des zones pour les esclaves géré par BCfg2
|
|
DNS_CONF_BCFG2 = "/var/lib/bcfg2/Cfg/etc/bind/generated/zones_crans/zones_crans"
|
|
|
|
### Liste DNS
|
|
# Le premier doit être le maitre
|
|
|
|
### Liste des délégations de zone
|
|
# Pour les demandes de ces zones, le DNS dira d'aller voir les serveurs listés ici
|
|
# Pour les noms des serveurs on met le nom sans point à la fin
|
|
# { nom_de_zone : [ ns1, ns2, ...]
|
|
DELEG = {}
|
|
|
|
### Serveurs de mail
|
|
# format : [ priorité serveur , .... ]
|
|
MXs = [
|
|
MX('@',10, 'redisdead.crans.org.'),
|
|
MX('@',20, 'ovh.crans.org.'),
|
|
MX('@',25, 'freebox.crans.org.'),
|
|
]
|
|
SRVs = {
|
|
'crans.org': [
|
|
SRV('jabber', 'tcp', 5, 0, 5269, 'xmpp'),
|
|
SRV('xmpp-server', 'tcp', 5, 0, 5269, 'xmpp'),
|
|
SRV('xmpp-client', 'tcp', 5, 0, 5222, 'xmpp'),
|
|
SRV('sip', 'udp', 5, 0, 5060, 'asterisk'),
|
|
SRV('sip', 'tcp', 5, 0, 5060, 'asterisk'),
|
|
SRV('sips', 'tcp', 5, 0, 5061, 'asterisk'),
|
|
]
|
|
}
|
|
NATPRs = {
|
|
'crans.org' : [
|
|
NAPTR('@', 5, 100, "S", "SIPS+D2T", "", '_sips._tcp.crans.org.', ttl=86400),
|
|
NAPTR('@', 10, 100, "S", "SIP+D2U", "", '_sip._udp.crans.org.', ttl=86400),
|
|
NAPTR('@', 15, 100, "S", "SIP+D2T", "", '_sip._tcp.crans.org.', ttl=86400),
|
|
]
|
|
}
|
|
|
|
# DS à publier dans zone parentes : { parent : [ zone. TTL IN DS key_id algo_id 1 hash ] }
|
|
# ex : { 'crans.eu' : ['wifi.crans.eu. 86400 IN DS 33131 8 1 3B573B0E2712D8A8B1B0C3'] }
|
|
# /!\ Il faut faire attention au rollback des keys, il faudrait faire quelque chose d'automatique avec opendnssec
|
|
DSs = {
|
|
'crans.org': [
|
|
DS('adm','565 8 2 498f6cd5bcf291aae4129700a7569fa6e9a86821185bd655f0b9efc6a3bf547e'),
|
|
DS('ferme','35156 8 2 b63a1443b3d7434429e879e046bc8ba89056cdcb4b9c3566853e64fd521895b8'),
|
|
DS('wifi','41320 8 2 024799c1d53f1e827f03d17bc96709b85ee1c05d77eb0ebeadcfbe207ee776a4'),
|
|
DS('tv','30910 8 2 3317f684081867ab94402804fbb3cd187e29655cc7f34cb92c938183fe0b71f5'),
|
|
],
|
|
}
|
|
|
|
|
|
EXTRAS = {
|
|
'crans.org' : [
|
|
TLSA('cas.crans.org.', 443, 'tcp', None, 3, 2),
|
|
TLSA('wiki.crans.org.', 443, 'tcp', None, 3, 2),
|
|
TLSA('perso.crans.org.', 443, 'tcp', None, 3, 2),
|
|
TLSA('intranet.crans.org.', 443, 'tcp', None, 3, 2),
|
|
TLSA('webmail.crans.org.', 443, 'tcp', None, 3, 2),
|
|
TLSA('horde.crans.org.', 443, 'tcp', None, 3, 2),
|
|
TLSA('roundcube.crans.org.', 443, 'tcp', None, 3, 2),
|
|
TLSA('sogo.crans.org.', 443, 'tcp', None, 3, 2),
|
|
]
|
|
}
|
|
|
|
hostname = short_name(gethostname())
|
|
serial = int(time.time()) + 1000000000
|
|
TTL = 3600
|
|
|
|
if hostname == short_name(config.dns.DNSs[0]):
|
|
restart_cmd = '/usr/sbin/ods-signer sign --all && /etc/init.d/bind9 reload'
|
|
else:
|
|
restart_cmd = '/etc/init.d/bind9 reload'
|
|
|
|
|
|
def gen_soa(self, ns_list, serial, ttl):
|
|
return SOA(ns_list[0], 'root.crans.org', serial, 21600, 3600, 1209600, ttl)
|
|
|
|
|
|
def populate_zones(self, zones, machines):
|
|
self.anim.iter=len(zones.values())
|
|
for zone in zones.values():
|
|
zone.extend(self.MXs)
|
|
for rr_type in [self.SRVs, self.NATPRs, self.DSs, self.EXTRAS]:
|
|
if zone.zone_name in rr_type.keys():
|
|
zone.extend(rr_type[zone.zone_name])
|
|
for m in machines:
|
|
zone.add_machine(m)
|
|
self.anim.cycle()
|
|
return zones
|
|
|
|
def gen_zones_ldap(self, ttl, ns_list, serial, zones={}, zones_ldap=config.dns.zones_ldap):
|
|
for zone in zones_ldap:
|
|
zones[zone]=Zone(zone, ttl, self.gen_soa(ns_list, serial, ttl), ns_list, other_zones=config.dns.zones_direct)
|
|
return zones
|
|
|
|
def gen_zones_reverse(self, ttl, ns_list, serial, zones={},
|
|
zones_reverse_v4=config.dns.zones_reverse, zones_reverse_v6=config.dns.zones_reverse_v6):
|
|
for net in ZoneReverse.network_to_arpanets(zones_reverse_v4 + zones_reverse_v6):
|
|
zones[str(net)]=ZoneReverse(str(net), ttl, self.gen_soa(ns_list, serial, ttl), ns_list)
|
|
return zones
|
|
|
|
def gen_zones_clone(self, ttl, ns_list, serial, zones={}):
|
|
for zone_clone, zones_alias in config.dns.zone_alias.items():
|
|
for zone in zones_alias:
|
|
zones[zone]=ZoneClone(zone, zones[zone_clone], self.gen_soa(ns_list, serial, ttl))
|
|
for rr_type in [self.SRVs, self.NATPRs, self.DSs]:
|
|
if zones[zone].zone_name in rr_type.keys():
|
|
zones[zone].extend(rr_type[zones[zone].zone_name])
|
|
return zones
|
|
|
|
|
|
def gen_zones(self, ttl, serial, ns_list, populate=True):
|
|
zones = {}
|
|
self.gen_zones_ldap(ttl, ns_list, serial, zones)
|
|
self.gen_zones_reverse(ttl, ns_list, serial, zones)
|
|
|
|
if populate:
|
|
conn = lc_ldap.shortcuts.lc_ldap_admin()
|
|
machines = conn.search(u"mid=*", sizelimit=10000)
|
|
machines.extend(conn.machinesMulticast())
|
|
self.populate_zones(zones, machines)
|
|
|
|
# Doit être fait après populate_zones lorsque l'on a l'intention d'écrire les fichiers de zone
|
|
# En effet, la génération de la zone clone dépend du contenue de la zone originale
|
|
self.gen_zones_clone(ttl, ns_list, serial, zones)
|
|
return zones
|
|
|
|
|
|
def gen_tv(self, populate=True):
|
|
self.anim = affich_tools.anim('\tgénération de la zone tv')
|
|
zones = {}
|
|
serial = self.serial
|
|
self.gen_zones_reverse(self.TTL, config.dns.DNSs, serial, zones, zones_reverse_v4=config.NETs['multicast'], zones_reverse_v6=[])
|
|
self.gen_zones_ldap(self.TTL, config.dns.DNSs, serial, zones, zones_ldap=[config.dns.zone_tv])
|
|
|
|
if populate:
|
|
conn = lc_ldap.shortcuts.lc_ldap_admin()
|
|
machines=conn.machinesMulticast()
|
|
machines.extend(conn.search(u'(|(host=%s)(host=*.%s)(hostAlias=%s)(hostAlias=*.%s))' % ((config.dns.zone_tv,)*4)))
|
|
self.populate_zones(zones, machines)
|
|
|
|
for zone in zones.values():
|
|
zone.write(self.DNS_DIR + 'db.' + zone.zone_name)
|
|
|
|
self.anim.reinit()
|
|
print affich_tools.OK
|
|
return zones
|
|
|
|
def gen_master(self):
|
|
# Syntaxe utilisée dans le fichier DNS_CONF pour définir une zone sur le maître
|
|
zone_template="""
|
|
zone "%(zone_name)s" {
|
|
type master;
|
|
file "%(zone_path)s";
|
|
};
|
|
"""
|
|
zones = self.gen_zones(self.TTL, self.serial, config.dns.DNSs)
|
|
with open(self.DNS_CONF, 'w') as f:
|
|
f.write(disclamer)
|
|
for zone in zones.values():
|
|
zone.write(self.DNS_DIR + 'db.' + zone.zone_name)
|
|
if zone.zone_name in config.dns.zones_dnssec:
|
|
zone_path = self.DNSSEC_DIR + 'db.' + zone.zone_name
|
|
else:
|
|
zone_path = self.DNS_DIR + 'db.' + zone.zone_name
|
|
f.write(zone_template % {'zone_name' : zone.zone_name, 'zone_path' : zone_path})
|
|
|
|
def gen_slave(self):
|
|
zone_template="""
|
|
zone "%(zone_name)s" {
|
|
type slave;
|
|
file "%(zone_path)s";
|
|
masters { %(master_ip)s; };
|
|
};
|
|
"""
|
|
zones = self.gen_zones(self.TTL, self.serial, config.dns.DNSs, populate=False)
|
|
with open(self.DNS_CONF_BCFG2, 'w') as f:
|
|
f.write(disclamer)
|
|
for zone in zones.values():
|
|
if zone.zone_name in config.dns.zones_dnssec:
|
|
zone_path = self.DNSSEC_DIR + 'db.' + zone.zone_name
|
|
else:
|
|
zone_path = self.DNS_DIR + 'db.' + zone.zone_name
|
|
f.write(zone_template % {'zone_name' : zone.zone_name, 'zone_path' : zone_path, 'master_ip' : config.dns.master})
|
|
|
|
def _gen(self):
|
|
self.gen_master()
|
|
|
|
def __str__(self):
|
|
return "DNS"
|
|
|
|
|
|
if __name__ == '__main__' :
|
|
hostname = short_name(gethostname())
|
|
if hostname == short_name(config.bcfg2_main):
|
|
print "Reconfiguration du fichier de BCfg2 pour configurer le bind d'un serveur en esclave (pensez à lancer bcfg2 sur les esclaves)."
|
|
c = dns()
|
|
c.gen_slave()
|
|
elif hostname == short_name(config.dns.DNSs[0]):
|
|
print "Serveur maître :"
|
|
c = dns()
|
|
zones = c.gen_tv()
|
|
import subprocess
|
|
for zone in zones.values():
|
|
if zone.zone_name in config.dns.zones_dnssec:
|
|
args=("/usr/sbin/ods-signer sign %s" % zone.zone_name).split()
|
|
p=subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
|
ret=p.communicate()
|
|
print ret[0].strip()
|
|
if ret[1].strip():
|
|
print ret[1].strip()
|
|
print "Ce serveur est également serveur maitre pour les autres zones dns, mais leur reconfiguration se fait par generate."
|
|
elif hostname in map(lambda fullhostname : short_name(fullhostname),config.dns.DNSs[1:]):
|
|
print "Ce serveur est esclave! Lancez ce script sur %s, puis lancez bcfg2 ici" % bcfg2_main
|
|
else:
|
|
print "Ce serveur ne correspond à rien pour la configuration DNS."
|
|
|