bind.py 24.6 KB
Newer Older
bernat's avatar
bernat committed
1
#! /usr/bin/env python
2
# -*- coding: utf-8 -*-
3

4
""" Génération de la configuration pour bind9
bernat's avatar
bernat committed
5

6
Copyright (C) Frédéric Pauget
bernat's avatar
bernat committed
7
8
Licence : GPLv2
"""
9
import time, sys, re, hashlib, base64, os
10
sys.path.append('/usr/scripts/gestion')
11
from socket import gethostname
bernat's avatar
bernat committed
12
from gen_confs import gen_config
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
13
14

import config
15
import config.dns
16
from iptools import AddrInNet, AddrInNets
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
17
18
19
import ip6tools

import netaddr
20
import ldap_crans
bernat's avatar
bernat committed
21

22
23
24
def short_name(fullhostname):
    return fullhostname.split(".")[0]

25
26
27
28


def netv4_to_arpa(net):
    addr, prefixlen = net.split('/')
29
30
    if prefixlen == '8':
        return ["%s.in-addr.arpa" % addr.split('.')[0]]
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    if prefixlen == '16':
        return ["%s.in-addr.arpa" % '.'.join(reversed(addr.split('.')[0:2]))]
    zones=[]
    n = map(int,net.split('/')[0].split('.')[:3])
    while 1 :
        try:
            innet = AddrInNet("%d.%d.%d.1" % tuple(n),net)
        except ValueError:
            break
        else:
            if not innet:
                break
            else :
                n.reverse()
                zones.append("%d.%d.%d.in-addr.arpa" % tuple(n))
                n.reverse()
        n[2] += 1
    return zones
    
def netv6_to_arpa(net):
    n = netaddr.IPNetwork(net)
    network_reverse = netaddr.IPAddress(n.first).reverse_dns
    zone = network_reverse.split('.')[(128-n.prefixlen)/4:-1]
    return '.'.join(zone)


bernat's avatar
bernat committed
57
class dns(gen_config) :
58
    """
59
60
61
    Génération des fichiers de configuration de bind9 :
        * fichier DNS_CONF qui contient les définitions de zone conformément
à zone_template. Ce fichier doit être inclus à partir de la config statique
bernat's avatar
bernat committed
62
de bind
63
64
65
        * les fichiers de zones, ce sont eux qui contiennent les données du
dns, ils ont appellés par le fichier DNS_CONF et sont générés dans DNS_DIR
Leur entète est générée à partir de zone_entete.
bernat's avatar
bernat committed
66

67
68
    Les fichiers générés placent bind comme autoritaire sur les noms de
zones_direct et les adresses de zones_reverse. Les données proviennent de
bernat's avatar
bernat committed
69
70
71
la base LDAP
    """
    ######################################PARTIE DE CONFIGURATION
72

73
74
75
76
77
    ### 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
78
79
    DNS_CONF = DNS_DIR + 'zones_crans'

80
    # Fichier de définition des zones pour les esclaves géré par BCfg2
81
    DNS_CONF_BCFG2 = "/var/lib/bcfg2/Cfg/etc/bind/generated/zones_crans/zones_crans"
82

83
84
    ### Sur quelles zones on a autorité ?
    ## En cas de modification de ces zones penser à regéner le fichier de
85
    ## zone des esclaves (sur le serveur principal de bcfg2 : python /usr/scripts/gestion/gen_confs/bind.py puis lancer bcfg2 sur les miroirs)
86
    # Résolution directe
87
    zones_direct = config.dns.zones_direct
88
    # Zones signée par opendnssec sur le serveur maitre
89
    zones_dnssec = config.dns.zones_dnssec
90
    # Zones alias pour les enregistrement A AAAA CNAME TXT et SSHFP
91
    zone_alias = config.dns.zone_alias
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
92
    zones_v4_to_v6 = {
93
94
        'wifi.crans.eu': 'wifi.v6.crans.eu',
        'crans.eu': 'v6.crans.eu',
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
95
96
97
98
99
        'crans.org': 'v6.crans.org',
        'wifi.crans.org': 'wifi.v6.crans.org',
        'adm.crans.org': 'adm.v6.crans.org',
        'ferme.crans.org': 'ferme.v6.crans.org',
        }
100
    # Résolution inverse
101
    zones_reverse = config.dns.zones_reverse
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
102
    zones_v6_to_net = {
103
        'crans.eu': config.prefix["fil"][0],
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
104
105
106
107
        'crans.org': config.prefix["fil"][0],
        'wifi.crans.org': config.prefix["wifi"][0],
        'adm.crans.org': config.prefix["adm"][0],
        'ferme.crans.org': config.prefix["fil"][0],
108
        # Hack pour générer un fichier de zone vide
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
109
110
        '##HACK##': config.prefix["subnet"][0],
        }
bernat's avatar
bernat committed
111
    ### Liste DNS
112
    # Le premier doit être le  maitre
113
    DNSs = ['sable.crans.org', 'freebox.crans.org', 'ovh.crans.org']
114
115
    DNSs_private = []
    ip_master_DNS = config.dns.master
116
117
    
    zone_multicast = 'tv.crans.org'
118

119
120
    ### Liste des délégations de zone
    # Pour les demandes de ces zones, le DNS dira d'aller voir les serveurs listés ici
121
    # Pour les noms des serveurs on met l'IP sans point ou le nom avec un point
122
    DELEG = { 
123
      zone_multicast : [ 'sable.crans.org.' , 'mdr.crans.org.', 'freebox.crans.org.', 'ovh.crans.org.'] ,
124
    }
125

bernat's avatar
bernat committed
126
    ### Serveurs de mail
127
    # format : [ priorité serveur , .... ]
128
    MXs = ['10 redisdead.crans.org', '20 ovh.crans.org', '25 freebox.crans.org']
129
130
    SRVs = [
            '_jabber._tcp.crans.org.         86400 IN SRV 5 0 5269 xmpp.crans.org.',
131
            '_xmpp-server._tcp.crans.org.    86400 IN SRV 5 0 5269 xmpp.crans.org.',
132
133
134
135
136
            '_xmpp-client._tcp.crans.org.    86400 IN SRV 5 0 5222 xmpp.crans.org.',
            '_sip._udp.crans.org.            86400 IN SRV 5 0 5060 asterisk.crans.org.',
            '_sip._tcp.crans.org.            86400 IN SRV 5 0 5060 asterisk.crans.org.',
            '_sips._tcp.crans.org.           86400 IN SRV 5 0 5061 asterisk.crans.org.',
           ]
137
    
138
    # DS à publier dans zone parentes : { parent : [ zone. TTL IN DS key_id algo_id 1 hash ] }
139
140
    # 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
141
142
143
144
    DS = {
        'crans.eu': [
            'wifi.crans.eu.  3600    IN      DS      49739 8 2 de49579462a8f439c9b1853c2a06d337ae4e5ffbd41c21449d4c7112794254ed',
            'v6.crans.eu.    3600    IN      DS      81 8 2 36fa8d4782ecdbc25f0455e2c14aea899b86cf497ce78c7d90d1cf85647f5190',
145
146
147
148
149
150
151
152
153
          ],
        'v6.crans.eu' : [
            'wifi.v6.crans.eu.	3600	IN	DS	1799 8 2 52a40a7dfb3e9c88aee032c21c59be756c8d3de29149c408ed8b699d83e30032',
         ],
        'crans.org': [
            'v6.crans.org.       3600    IN      DS      23641 8 2 3fff97a2581f0f2f49257b4914d5badf8ccb0a49c5a6f4cbf2f520b97de332d0',
            'adm.crans.org.      3600    IN      DS      565 8 2 498f6cd5bcf291aae4129700a7569fa6e9a86821185bd655f0b9efc6a3bf547e',
            'ferme.crans.org.    3600    IN      DS      35156 8 2 b63a1443b3d7434429e879e046bc8ba89056cdcb4b9c3566853e64fd521895b8',
            'wifi.crans.org.     3600    IN      DS      41320 8 2 024799c1d53f1e827f03d17bc96709b85ee1c05d77eb0ebeadcfbe207ee776a4',
154
            'tv.crans.org.       3600    IN      DS      30910 8 2 3317f684081867ab94402804fbb3cd187e29655cc7f34cb92c938183fe0b71f5',
155
156
157
158
159
160
          ],
        'v6.crans.org' : [
            'adm.v6.crans.org.   3600    IN      DS      1711 8 2 f154eeb8eb346d2ca5cffb3f9cc464a17c0c4d69ee425b4fe44eaed7f5dd253b',
            'ferme.v6.crans.org. 3600    IN      DS      44434 8 2 fb87cb4216599cb6574add543078a9e48d0e50438483386585a9960557434ab0',
            'wifi.v6.crans.org.  3600    IN      DS      59539 8 2 dbe86f2f2e92d6a27bd1436f03ec1588f2948a2aa02124de0383be801cced85e',
         ]
161
    }
162
        
163

164
    ### Entète des fichiers de zone
bernat's avatar
bernat committed
165
166
    zone_entete="""
$ORIGIN %(zone)s.
167
$TTL 3600
168
@\tIN\tSOA %(serveur_autoritaire)s. root.crans.org. (
bernat's avatar
bernat committed
169
    %(serial)i ; numero de serie
170
171
172
    21600 ; refresh (s)
    3600 ; retry (s)
    1209600 ; expire (s)
173
    3600 ; TTL (s)
bernat's avatar
bernat committed
174
175
    )
"""
176

177
    # Syntaxe utilisée dans le fichier DNS_CONF pour définir une zone sur le maître
bernat's avatar
bernat committed
178
179
180
181
182
183
    zone_template="""
zone "%(NOM_zone)s" {
    type master;
    file "%(FICHIER_zone)s";
};
"""
184
    # Syntaxe utilisée dans le fichier DNS_CONF_BCFG2 pour définir une zone sur un esclave
185
186
187
188
    zone_template_slave="""
zone "%(NOM_zone)s" {
    type slave;
    file "%(FICHIER_zone)s";
189
    masters { %(ip_master_DNS)s; };
190
191
192
};
"""

193
194
    ### Verbosité
    # Si =2, ralera (chaine warnings) si machines hors zone trouvée
195
    # Si =1, comme ci-dessus, mais ne ralera pas pour freebox
196
    # Si =0, ralera seulement contre les machines ne pouvant être classées
bernat's avatar
bernat committed
197
    verbose = 1
198

199
200
201
202
203
204
    hostname = short_name(gethostname())
    if hostname ==  short_name(DNSs[0]):
        restart_cmd = '/usr/sbin/ods-signer sign --all && /etc/init.d/bind9 reload'
    else:
        restart_cmd = '/etc/init.d/bind9 reload'

bernat's avatar
bernat committed
205
    ######################################FIN PARTIE DE CONFIGURATION
206

bernat's avatar
bernat committed
207
    def __str__(self) :
bernat's avatar
bernat committed
208
        return "DNS"
209

Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
210
    def reverse(self, net, ip):
211
212
        """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
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
213
214
215
216
217
218
219
220
221
222
223
224
        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 == 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)
225
            
226
227
228
229
230
    def gen_tv(self):
        serial = time.time() + 1000000000
        zone_reverse=netv4_to_arpa(config.NETs['multicast'][0])[0]
        sap=open('/tmp/chaines_recup_sap.txt').readlines()
         
231
        DNS='; DNS de la zone par ordre de priorité\n'
232
233
234
235
236
237
238
239
240
        for d in self.DELEG[self.zone_multicast] :
            DNS += '@\tIN\tNS %s\n' % d
        DNS += '\n'
        
        lignes_d = '\n'
        lignes_r = '\n'
        lignes_d +='@\tIN\tA\t%s\n' % '138.231.136.243'
        for line in sap:
            [nom,ip]=line.split(':')
241
242
243
            nom=re.sub('TNT([0-9]*) ','',nom) # on enlève les TNT## des noms
            nom=re.sub(' +([^ ])','-\g<1>',nom) # on remplaces les espaces intérieur par un tiret
            nom=re.sub('[ .():,"\'+<>]','',nom) # on enlève tous les caractères illégaux
244
245
246
247
248
249
250
            nom=nom.lower()
            try:
                [ip1,ip2,ip3,ip4]=ip.strip().split('.')
                lignes_r += '%s.%s.%s\tIN\tPTR\t%s.%s.\n' % (ip4,ip3,ip2,nom,self.zone_multicast)
                lignes_d +='%s\tIN\tA\t%s' % (nom,ip)
            except:
                pass
251
        # Écriture de la zone directe
252
253
254
255
256
257
258
259
260
        file = self.DNS_DIR  + 'db.' + self.zone_multicast
        fd = self._open_conf(file,';')
        fd.write(self.zone_entete % \
        { 'zone' : self.zone_multicast, 'serveur_autoritaire' : self.DELEG[self.zone_multicast][0][0:-1] , 'serial' : serial } )
        fd.write('\n')
        fd.write(DNS)
        fd.write(lignes_d)
        fd.close()
        
261
        # Écriture du reverse
262
263
264
265
266
267
268
269
270
271
        file = self.DNS_DIR  + 'db.' + zone_reverse
        fd = self._open_conf(file,';')
        fd.write(self.zone_entete % \
        { 'zone' : zone_reverse, 'serveur_autoritaire' : self.DELEG[self.zone_multicast][0][0:-1] , 'serial' : serial } )
        fd.write('\n')
        fd.write(DNS)
        fd.write(lignes_r)
        fd.close()
        
    
272
    def gen_slave(self) :
273
        """ Génération du fichier de config de zone pour les esclaves """
bernat's avatar
bernat committed
274
        zones = self.zones_direct
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
275
        zones.extend(self.zones_v4_to_v6.values())
276

bernat's avatar
bernat committed
277
        # Ajout des zones reverse
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
278
279
        for net in self.zones_reverse:
            # IPv4 reverse
280
            zones.extend(netv4_to_arpa(net))
281

Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
282
283
        for net in set(self.zones_v6_to_net.values()):
            # IPv6 reverse
284
            zones.append(netv6_to_arpa(net))
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
285

bernat's avatar
bernat committed
286
        # Ecriture
287
        fd = self._open_conf(self.DNS_CONF_BCFG2,'//')
bernat's avatar
bernat committed
288
        for zone in zones :
289
290
291
292
            if zone in self.zones_dnssec:
                path=self.DNSSEC_DIR  + 'db.' + zone
            else:
                path=self.DNS_DIR  + 'db.' + zone
bernat's avatar
bernat committed
293
            fd.write(self.zone_template_slave % { 'NOM_zone' : zone,
294
                       'FICHIER_zone' : path,
295
                       'ip_master_DNS': self.ip_master_DNS})
296

297
        fd.close()
298

bernat's avatar
bernat committed
299
    def _gen(self) :
300
301
302
        ### Génération du numéro de série
        # Le + 1000.... s'explique pas l'idée précédente et peu pratique d'avoir
        # le numéro de série du type AAAAMMJJNN (année, mois, jour, incrément par jour)
bernat's avatar
bernat committed
303
        serial = time.time() + 1000000000
304

bernat's avatar
bernat committed
305
        ### DNS
306
        DNS='; DNS de la zone par ordre de priorité\n'
bernat's avatar
bernat committed
307
308
309
        for d in self.DNSs :
            DNS += '@\tIN\tNS %s.\n' % d
        DNS += '\n'
310

bernat's avatar
bernat committed
311
312
313
        ### Serveurs de mail
        MX='; Serveurs de mails\n'
        for m in self.MXs :
314
            MX += '%(zone)s.\t' # Sera remplacé par le nom de zone plus tard
bernat's avatar
bernat committed
315
316
            MX += 'IN\tMX\t%s.\n' % m
        MX += '\n'
317

bernat's avatar
bernat committed
318
319
320
        direct = {} # format : { zone : [ lignes correspondantes] }
        reverse = {}
        warnings = ''
bernat's avatar
bernat committed
321

322
        direct['crans.org'] = ""
323

bernat's avatar
bernat committed
324
        # P'tit lien vers irc.rezosup.org
325
326
        #direct["crans.org"]  = "\n; irc.crans.org -> irc.rezosup.org\n"
        #direct["crans.org"] += "irc\tIN\tCNAME\tirc.rezosup.org.\n\n"
327
328
329

        ### Ajout des parametres SPF
        direct['crans.org'] +='; Parametres SPF\n'
330
        direct['crans.org'] +='crans.org.\tIN\tTXT\t"v=spf1 a mx ?all"\n'
331
        for m in self.MXs:
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
332
            direct['crans.org'] +='%s.\tIN\tTXT\t"v=spf1 a ?all"\n' % m.split()[-1]
333
334
        direct['crans.org'] += '\n'

cohen's avatar
cohen committed
335
        direct['crans.ens-cachan.fr'] ='; Parametres SPF\n'
336
        direct['crans.ens-cachan.fr'] +='crans.ens-cachan.fr.\tIN\tTXT\t"v=spf1 a:crans.org mx ?all"\n\n'
337

338
        ### Ajout d'eventuels champs SRV
Mathieu.Segaud's avatar
Mathieu.Segaud committed
339
340
341
        direct['crans.org'] +='; Champs SRV\n'
        for s in self.SRVs:
            direct['crans.org'] += s + '\n'
mathieu.segaud's avatar
mathieu.segaud committed
342
343
        direct['crans.org'] += '\n'

bernat's avatar
bernat committed
344
        ### Tri des machines
345
        self.anim.iter=len(self.machines)
bernat's avatar
bernat committed
346
347
        for machine in self.machines :
            self.anim.cycle()
348
            # Calculs préliminaires
349
            try :
bernat's avatar
bernat committed
350
                nom , zone = machine.nom().split('.',1)
351
                zone = zone.encode('utf-8')
bernat's avatar
bernat committed
352
            except :
353
                warnings += u'Machine ignorée (mid=%s) : format nom incorrect (%s)\n' % ( machine.id().encode('utf-8'), machine.nom().encode('utf-8') )
bernat's avatar
bernat committed
354
                continue
355

bernat's avatar
bernat committed
356
357
358
            # Le direct
            if zone in self.zones_direct :
                ligne = "%s\tIN\tA\t%s\n" % ( nom, machine.ip() )
359
                # Si la machine est une borne wifi, on ajoute la position
360
                if isinstance(machine,ldap_crans.BorneWifi) and machine.position():
361
                    ligne +="%s\tIN\tTXT\t\"LOC %s,%s \"\n" %  (nom,machine.position()[0],machine.position()[1])
362
                # Si la machine à des clefs ssh, on ajoute les champs SSFP correspondant
363
                for sshkey in machine.sshFingerprint():
364
                    try:
365
366
367
368
369
370
371
372
                        [algo_txt,key]=sshkey.split()[:2]
                        algo=None
                        for value in config.sshfp_algo.values():
                            if algo_txt == value[1]:
                                algo=value[0]
                                break
                        if not algo: 
                            raise ValueError("Invalid Algorithms %s" % algo_txt)
373
374
375
                        key=hashlib.sha1(base64.b64decode(key)).hexdigest()
                        ligne +="%s\tIN\tSSHFP\t%s\t1\t%s\n" % (nom,algo,key)
                    except(ValueError,TypeError): pass
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
376
                direct[zone] = direct.get(zone, "") + ligne
377
378
                if isinstance(machine,ldap_crans.BorneWifi):
                    direct['ap.crans.org'] = direct.get('ap.crans.org', "") + ligne
379
380
381
                if zone in self.zone_alias:
                    for alias in self.zone_alias[zone]:
                        direct[alias] = direct.get(alias, "") + ligne
382
            elif self.verbose and machine.nom() != "ftp.federez.net":
383
                warnings += u'Résolution directe ignorée (mid=%s) : zone non autoritaire (%s)\n' % ( machine.id().encode('utf-8'), zone.encode('utf-8') )
384

Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
385
386
387
388
389
390
391
392
393
394
            # IPv6
            if zone in self.zones_v4_to_v6:
                # Direct
                zone_v6 = self.zones_v4_to_v6[zone]
                ipv6 = machine.ipv6()
                net_v6 = machine.netv6()
                ligne = "%s\tIN\tAAAA\t%s\n" % (nom, ipv6)
                direct[zone_v6] = direct.get(zone_v6, "") + ligne
                if machine.dnsIpv6():
                    direct[zone] = direct.get(zone, "") + ligne
395
396
397
398
399
400
401
                if zone in self.zone_alias:
                    for alias in self.zone_alias[zone]:
                        if alias in self.zones_v4_to_v6:
                            alias_v6=self.zones_v4_to_v6[alias]
                            direct[alias_v6] = direct.get(alias_v6, "") + ligne
                            if machine.dnsIpv6():
                                direct[alias] = direct.get(alias, "") + ligne
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
402
403
404
405
406
407
408
                # Reverse
                zone_rev, length = self.reverse(net_v6, ipv6)
                rev = '.'.join(ipv6.reverse_dns.split('.')[:length])
                ligne = "%s\tIN\tPTR\t%s.\n" % (rev, machine.nom6())
                reverse[zone_rev] = reverse.get(zone_rev, "") + ligne


bernat's avatar
bernat committed
409
410
            # Le direct avec alias
            for alias in machine.alias() :
411
                alias = alias.encode('utf-8')
bernat's avatar
bernat committed
412
413
414
                # Cas particulier : nom de l'alias = nom de la zone
                if alias in self.zones_direct :
                    ligne = "@\tIN\tA\t%s\n" % machine.ip()
415
                    ligne = ligne.encode('utf-8')
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
416
                    direct[alias] = direct.get(alias, "") + ligne
417
418
                    if alias in self.zone_alias: 
                        for alias2 in self.zone_alias[alias]: direct[alias2] = direct.get(alias2, "") + ligne
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
419
420
                    if machine.dnsIpv6():
                        ligne = "@\tIN\tAAAA\t%s\n" % machine.ipv6()
421
                        ligne = ligne.encode('utf-8')
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
422
                        direct[alias]= direct.get(alias, "") + ligne
423
424
                        if alias in self.zone_alias: 
                            for alias2 in self.zone_alias[alias]: direct[alias2] = direct.get(alias2, "") + ligne
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
425
426
                    if alias in self.zones_v4_to_v6:
                        ligne = "@\tIN\tAAAA\t%s\n" % machine.ipv6()
427
                        ligne = ligne.encode('utf-8')
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
428
429
                        zone6 = self.zones_v4_to_v6[alias]
                        direct[zone6] = direct.get(zone6, '') + ligne
430
431
432
433
434
                        if alias in self.zone_alias: 
                            for alias2 in self.zone_alias[alias]: 
                                if alias2 in self.zones_v4_to_v6: 
                                    alias26=self.zones_v4_to_v6[alias2]
                                    direct[alias26] = direct.get(alias26, "") + ligne
bernat's avatar
bernat committed
435
                    continue
436

bernat's avatar
bernat committed
437
438
439
440
441
442
443
444
445
446
447
448
                # Bon format ?
                alias_l = alias.split('.')
                ok = 0
                for i in range(len(alias_l)) :
                    zone_essai = '.'.join(alias_l[i:])
                    if zone_essai in self.zones_direct :
                        # On est autoritaire sur cette zone
                        # On place donc l'alias dans le fichier de cette zone
                        zone = zone_essai
                        nom = '.'.join(alias_l[:i])
                        ok = 1
                        break
449
                if not ok:
450
                    warnings += u'Alias ignoré (mid=%s) : %s\n' % ( machine.id().encode('utf-8'), alias.encode('utf-8') )
bernat's avatar
bernat committed
451
                    continue
452
                zone = zone.encode('utf-8')
bernat's avatar
bernat committed
453
                ligne = "%s\tIN\tCNAME\t%s.\n" % ( nom, machine.nom() )
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
454
455
456
457
458
                direct[zone] = direct.get(zone, '') + ligne
                if zone in self.zones_v4_to_v6:
                    zone6 = self.zones_v4_to_v6[zone]
                    ligne = "%s\tIN\tCNAME\t%s.\n" % ( nom, machine.nom6() )
                    direct[zone6] = direct.get(zone6, '') + ligne
459
460
461
462
463
464
                if zone in self.zone_alias:
                    for alias in self.zone_alias[zone]:
                        direct[alias] = direct.get(alias, '') + ligne
                        if alias in self.zones_v4_to_v6:
                            alias6 = self.zones_v4_to_v6[alias]
                            direct[alias6] = direct.get(alias6, '') + ligne
465

bernat's avatar
bernat committed
466
            # Le reverse
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
467
468
469
470
            ip = machine.ip()
            net = AddrInNets(ip, self.zones_reverse)
            if net:
                base_ip = ip.split('.')
bernat's avatar
bernat committed
471
                base_ip.reverse()
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
472
                zone, length = self.reverse(net, ip)
473
                zone = zone.encode('utf-8')
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
474
                ligne = '%s\tIN\tPTR\t%s.\n' % ('.'.join(base_ip[:length]), machine.nom())
bernat's avatar
bernat committed
475
476
                try : reverse[zone] += ligne
                except : reverse[zone] = ligne
477
            elif self.verbose >= 2 or machine.nom() not in ('freebox.crans.org', 'ovh.crans.org', 'kokarde.crans.org'):
478
                warnings += u'Résolution inverse ignorée (mid=%s) : ip sur zone non autoritaire (%s)\n' % ( machine.id().encode('utf-8'), machine.ip().encode('utf-8') )
479

480
        ### Ajouts pour les fichiers de résolution directs
pauget's avatar
pauget committed
481
        for zone in direct.keys() :
bernat's avatar
bernat committed
482
483
            # MXs
            direct[zone] = MX % { 'zone' : zone } + direct[zone]
484

485
        ### XXX: création de la zone inverse pour le /48 IPv6 complet du Cr@ns
Nicolas Dandrimont's avatar
Nicolas Dandrimont committed
486
487
488
        full_net_v6 = self.zones_v6_to_net["##HACK##"]
        zone_rev, length = self.reverse(full_net_v6, netaddr.IPNetwork(full_net_v6).first)
        reverse[zone_rev] = reverse.get(zone_rev, "")
489

490
        ### Ajout des délégations de zones
491
492
493
        for deleg in self.DELEG.keys():
            nom, zone = deleg.split('.',1)
            if not zone in direct.keys():
494
                warnings += u'Délégation ignorée %s : on ne génère pas la zone parent\n' % deleg
495
496
497
                continue
            for serv in self.DELEG[deleg]:
                direct[zone] = direct[zone] + "%s\tIN\tNS\t%s\n" % ( nom, serv )
498

499
500
501
502
503
504
505
506
        for zone in self.zones_direct:
            child, parent = zone.split('.',1)
            if not zone in self.DELEG.keys() and parent in self.zones_direct:
                for d in self.DNSs:
                    direct[parent] = direct.get(parent, "") + '%s\tIN\tNS %s\n' % (child, d)
                if zone in self.zones_v4_to_v6.keys():
                    for d in self.DNSs:
                        direct[parent] = direct.get(parent, "") + '%s.\tIN\tNS %s\n' % (self.zones_v4_to_v6[zone], d)
507

508
        ### Ajout d'eventuel champs DS pour les délégation dnssec
509
510
511
512
        for zone,ds in self.DS.items():
            for s in ds:
              direct[zone] += s + '\n'
            direct[zone] += '\n'
513

514
        ### Ecriture des fichiers de zone et préparation du fichier de définition
bernat's avatar
bernat committed
515
516
        f = ''
        for zone, lignes in direct.items() + reverse.items() :
517
518
519
520
521
            if zone in self.zones_dnssec:
                path = self.DNSSEC_DIR  + 'db.' + zone
            else:
                path = self.DNS_DIR  + 'db.' + zone
            file = self.DNS_DIR  + 'db.' + zone
bernat's avatar
bernat committed
522
            fd = self._open_conf(file,';')
pauget's avatar
pauget committed
523
524
            fd.write(self.zone_entete % \
            { 'zone' : zone, 'serveur_autoritaire' : self.DNSs[0] , 'serial' : serial } )
bernat's avatar
bernat committed
525
526
527
528
            fd.write('\n')
            fd.write(DNS)
            fd.write(lignes)
            fd.close()
529
            os.chmod(file,0664)
530
            if short_name(gethostname()) in map(short_name, dns.DNSs[1:]):
531
                f += self.zone_template_slave % {'NOM_zone': zone, 'FICHIER_zone': path,
532
533
                        'ip_master_DNS': self.ip_master_DNS}
            else:
534
                f += self.zone_template % { 'NOM_zone' : zone, 'FICHIER_zone' : path }
535

536
        ### Ecriture fichier de définition
bernat's avatar
bernat committed
537
538
539
        fd = self._open_conf(self.DNS_CONF,'//')
        fd.write(f)
        fd.close()
540

bernat's avatar
bernat committed
541
        return warnings
542

543

544
if __name__ == '__main__' :
545
    from config import bcfg2_main
546
    hostname = short_name(gethostname())
547
    if hostname == short_name(bcfg2_main):
548
        print "Reconfiguration du fichier de BCfg2 pour configurer le bind d'un serveur en esclave (pensez à lancer bcfg2 sur les esclaves)."
bernat's avatar
bernat committed
549
550
        c = dns()
        c.gen_slave()
551
        if hostname ==  short_name(dns.DNSs[0]):
552
            print "Ce serveur est également serveur maitre, mais la reconfiguration du DNS maitre se fait par generate."
553
    elif hostname == short_name(dns.DELEG['tv.crans.org'][0][0:-1]):
554
        print "Serveur maᅵtre pour tv.crans.org, génération de la zone"
555
556
557
        c = dns()
        c.gen_tv()
        import subprocess
558
        args="/usr/sbin/ods-signer sign tv.crans.org".split()
559
        p=subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
560
561
562
563
        ret=p.communicate()
        print ret[0].strip()
        print ret[1].strip()
        if hostname ==  short_name(dns.DNSs[0]):
564
            print "Ce serveur est également serveur maitre, mais la reconfiguration du DNS maitre se fait par generate."
565
    elif hostname ==  short_name(dns.DNSs[0]):
566
        print "Ce serveur est maître ! Utilisez generate."
567
    elif hostname in map(lambda fullhostname : short_name(fullhostname),dns.DNSs[1:]+dns.DNSs_private):
568
        print "Ce serveur est esclave! Lancez ce script sur %s, puis lancez bcfg2 ici" % bcfg2_main
569
    else:
570
        print "Ce serveur ne correspond à rien pour la configuration DNS."