[gen_confs/bind] Archivage de l'ancient script et remplacement par le nouveau
This commit is contained in:
parent
a6742c500b
commit
74f6d571db
4 changed files with 1060 additions and 1055 deletions
592
archive/gestion/gen_confs/bind.py
Executable file
592
archive/gestion/gen_confs/bind.py
Executable file
|
@ -0,0 +1,592 @@
|
||||||
|
#! /usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
""" Génération de la configuration pour bind9
|
||||||
|
|
||||||
|
Copyright (C) Frédéric Pauget
|
||||||
|
Licence : GPLv2
|
||||||
|
"""
|
||||||
|
import time, sys, re, hashlib, base64, os
|
||||||
|
sys.path.append('/usr/scripts/gestion')
|
||||||
|
from socket import gethostname
|
||||||
|
from gen_confs import gen_config
|
||||||
|
|
||||||
|
import config
|
||||||
|
import config.dns
|
||||||
|
from iptools import AddrInNet, AddrInNets
|
||||||
|
import ip6tools
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
import ldap_crans
|
||||||
|
|
||||||
|
def short_name(fullhostname):
|
||||||
|
return fullhostname.split(".")[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def netv4_to_arpa(net):
|
||||||
|
addr, prefixlen = net.split('/')
|
||||||
|
if prefixlen == '8':
|
||||||
|
return ["%s.in-addr.arpa" % addr.split('.')[0]]
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class dns(gen_config) :
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
de bind
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
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
|
||||||
|
la base LDAP
|
||||||
|
"""
|
||||||
|
######################################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"
|
||||||
|
|
||||||
|
### Sur quelles zones on a autorité ?
|
||||||
|
## En cas de modification de ces zones penser à regéner le fichier de
|
||||||
|
## zone des esclaves (sur le serveur principal de bcfg2 : python /usr/scripts/gestion/gen_confs/bind.py puis lancer bcfg2 sur les miroirs)
|
||||||
|
# Résolution directe
|
||||||
|
zones_direct = config.dns.zones_direct
|
||||||
|
# Zones signée par opendnssec sur le serveur maitre
|
||||||
|
zones_dnssec = config.dns.zones_dnssec
|
||||||
|
# Zones alias pour les enregistrement A AAAA CNAME TXT et SSHFP
|
||||||
|
zone_alias = config.dns.zone_alias
|
||||||
|
zones_v4_to_v6 = {
|
||||||
|
'wifi.crans.eu': 'wifi.v6.crans.eu',
|
||||||
|
'crans.eu': 'v6.crans.eu',
|
||||||
|
'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',
|
||||||
|
}
|
||||||
|
zone_alias.update({
|
||||||
|
'wifi.v6.crans.eu': ['v6.wifi.crans.eu'],
|
||||||
|
'wifi.v6.crans.org': ['v6.wifi.crans.org'],
|
||||||
|
'adm.v6.crans.org': ['v6.adm.crans.org'],
|
||||||
|
'ferme.v6.crans.org': ['v6.ferme.crans.org'],
|
||||||
|
})
|
||||||
|
|
||||||
|
# Résolution inverse
|
||||||
|
zones_reverse = config.dns.zones_reverse
|
||||||
|
zones_v6_to_net = {
|
||||||
|
'crans.eu': config.prefix["fil"][0],
|
||||||
|
'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],
|
||||||
|
# Hack pour générer un fichier de zone vide
|
||||||
|
'##HACK##': config.prefix["subnet"][0],
|
||||||
|
}
|
||||||
|
### Liste DNS
|
||||||
|
# Le premier doit être le maitre
|
||||||
|
DNSs = config.dns.DNSs
|
||||||
|
DNSs_private = []
|
||||||
|
ip_master_DNS = config.dns.master
|
||||||
|
|
||||||
|
zone_multicast = config.dns.zone_tv
|
||||||
|
|
||||||
|
### 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 l'IP sans point ou le nom avec un point
|
||||||
|
DELEG = {
|
||||||
|
zone_multicast : [ 'sable.crans.org.' , 'mdr.crans.org.', 'freebox.crans.org.', 'ovh.crans.org.'] ,
|
||||||
|
}
|
||||||
|
|
||||||
|
### Serveurs de mail
|
||||||
|
# format : [ priorité serveur , .... ]
|
||||||
|
MXs = ['10 redisdead.crans.org', '20 ovh.crans.org', '25 freebox.crans.org']
|
||||||
|
SRVs = [
|
||||||
|
'_jabber._tcp.crans.org. 86400 IN SRV 5 0 5269 xmpp.crans.org.',
|
||||||
|
'_xmpp-server._tcp.crans.org. 86400 IN SRV 5 0 5269 xmpp.crans.org.',
|
||||||
|
'_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.',
|
||||||
|
]
|
||||||
|
|
||||||
|
NATPRs = [
|
||||||
|
'@ 86400 IN NAPTR 5 100 "S" "SIPS+D2T" "" _sips._tcp.crans.org.',
|
||||||
|
'@ 86400 IN NAPTR 10 100 "S" "SIP+D2U" "" _sip._udp.crans.org.',
|
||||||
|
'@ 86400 IN NAPTR 15 100 "S" "SIP+D2T" "" _sip._tcp.crans.org.',
|
||||||
|
]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
DS = {
|
||||||
|
'crans.eu': [
|
||||||
|
'wifi.crans.eu. 3600 IN DS 37582 8 2 51809b508e450d8fec44572a3fa31754c27507465775c7d1c86570abd7a21024',
|
||||||
|
'v6.crans.eu. 3600 IN DS 12562 8 2 0a8e92398c5213cf2907b79f0fa8bd7db6729e0d2f6ca7443ac5a4b8441e38bd',
|
||||||
|
],
|
||||||
|
'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',
|
||||||
|
'tv.crans.org. 3600 IN DS 30910 8 2 3317f684081867ab94402804fbb3cd187e29655cc7f34cb92c938183fe0b71f5',
|
||||||
|
],
|
||||||
|
'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',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### Entète des fichiers de zone
|
||||||
|
zone_entete="""
|
||||||
|
$ORIGIN %(zone)s.
|
||||||
|
$TTL 3600
|
||||||
|
@\tIN\tSOA %(serveur_autoritaire)s. root.crans.org. (
|
||||||
|
%(serial)i ; numero de serie
|
||||||
|
21600 ; refresh (s)
|
||||||
|
3600 ; retry (s)
|
||||||
|
1209600 ; expire (s)
|
||||||
|
3600 ; TTL (s)
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Syntaxe utilisée dans le fichier DNS_CONF pour définir une zone sur le maître
|
||||||
|
zone_template="""
|
||||||
|
zone "%(NOM_zone)s" {
|
||||||
|
type master;
|
||||||
|
file "%(FICHIER_zone)s";
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
# Syntaxe utilisée dans le fichier DNS_CONF_BCFG2 pour définir une zone sur un esclave
|
||||||
|
zone_template_slave="""
|
||||||
|
zone "%(NOM_zone)s" {
|
||||||
|
type slave;
|
||||||
|
file "%(FICHIER_zone)s";
|
||||||
|
masters { %(ip_master_DNS)s; };
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
### Verbosité
|
||||||
|
# Si =2, ralera (chaine warnings) si machines hors zone trouvée
|
||||||
|
# Si =1, comme ci-dessus, mais ne ralera pas pour freebox
|
||||||
|
# Si =0, ralera seulement contre les machines ne pouvant être classées
|
||||||
|
verbose = 1
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
######################################FIN PARTIE DE CONFIGURATION
|
||||||
|
|
||||||
|
def __str__(self) :
|
||||||
|
return "DNS"
|
||||||
|
|
||||||
|
def reverse(self, 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 == 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)
|
||||||
|
|
||||||
|
|
||||||
|
def is_ascii(self, s):
|
||||||
|
return all(ord(c) < 128 for c in s)
|
||||||
|
|
||||||
|
def gen_tv(self):
|
||||||
|
sys.path.append('/usr/scripts')
|
||||||
|
import tv.dns
|
||||||
|
serial = time.time() + 1000000000
|
||||||
|
zone_reverse=netv4_to_arpa(config.NETs['multicast'][0])[0]
|
||||||
|
sap=open('/usr/scripts/var/tv/sap.txt').readlines()
|
||||||
|
|
||||||
|
DNS='; DNS de la zone par ordre de priorité\n'
|
||||||
|
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.88'
|
||||||
|
for line in sap:
|
||||||
|
[nom,ip]=line.split(':')
|
||||||
|
nom=unicode(nom, 'utf-8')
|
||||||
|
nom_ascii=tv.dns.ascii(nom)
|
||||||
|
nom_punycode=tv.dns.punycode(nom)
|
||||||
|
try:
|
||||||
|
[ip1,ip2,ip3,ip4]=ip.strip().split('.')
|
||||||
|
lignes_r += '%s.%s.%s\tIN\tPTR\t%s.%s.\n' % (ip4,ip3,ip2,nom_ascii,self.zone_multicast)
|
||||||
|
lignes_d +='%s\tIN\tA\t%s' % (nom_ascii,ip)
|
||||||
|
if nom_punycode:
|
||||||
|
lignes_d +='%s\tIN\tA\t%s' % (nom_punycode, ip)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
# Écriture de la zone directe
|
||||||
|
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()
|
||||||
|
|
||||||
|
# Écriture du reverse
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
def gen_slave(self) :
|
||||||
|
""" Génération du fichier de config de zone pour les esclaves """
|
||||||
|
zones = self.zones_direct
|
||||||
|
zones.extend(self.zones_v4_to_v6.values())
|
||||||
|
zones.extend([z for l in self.zone_alias.values() for z in l])
|
||||||
|
zones = list(set(zones))
|
||||||
|
zones.sort()
|
||||||
|
|
||||||
|
# Ajout des zones reverse
|
||||||
|
for net in self.zones_reverse:
|
||||||
|
# IPv4 reverse
|
||||||
|
zones.extend(netv4_to_arpa(net))
|
||||||
|
|
||||||
|
for net in set(self.zones_v6_to_net.values()):
|
||||||
|
# IPv6 reverse
|
||||||
|
zones.append(netv6_to_arpa(net))
|
||||||
|
|
||||||
|
# Ecriture
|
||||||
|
fd = self._open_conf(self.DNS_CONF_BCFG2,'//')
|
||||||
|
for zone in zones :
|
||||||
|
if zone in self.zones_dnssec:
|
||||||
|
path=self.DNSSEC_DIR + 'db.' + zone
|
||||||
|
else:
|
||||||
|
path=self.DNS_DIR + 'db.' + zone
|
||||||
|
fd.write(self.zone_template_slave % { 'NOM_zone' : zone,
|
||||||
|
'FICHIER_zone' : path,
|
||||||
|
'ip_master_DNS': self.ip_master_DNS})
|
||||||
|
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
def _gen(self) :
|
||||||
|
### 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)
|
||||||
|
serial = time.time() + 1000000000
|
||||||
|
|
||||||
|
### DNS
|
||||||
|
DNS='; DNS de la zone par ordre de priorité\n'
|
||||||
|
for d in self.DNSs :
|
||||||
|
DNS += '@\tIN\tNS %s.\n' % d
|
||||||
|
DNS += '\n'
|
||||||
|
|
||||||
|
### Serveurs de mail
|
||||||
|
MX='; Serveurs de mails\n'
|
||||||
|
for m in self.MXs :
|
||||||
|
MX += '@\t' # Sera remplacé par le nom de zone plus tard
|
||||||
|
MX += 'IN\tMX\t%s.\n' % m
|
||||||
|
MX += '\n'
|
||||||
|
|
||||||
|
direct = {} # format : { zone : [ lignes correspondantes] }
|
||||||
|
reverse = {}
|
||||||
|
warnings = ''
|
||||||
|
|
||||||
|
direct['crans.org'] = ""
|
||||||
|
|
||||||
|
# P'tit lien vers irc.rezosup.org
|
||||||
|
#direct["crans.org"] = "\n; irc.crans.org -> irc.rezosup.org\n"
|
||||||
|
#direct["crans.org"] += "irc\tIN\tCNAME\tirc.rezosup.org.\n\n"
|
||||||
|
|
||||||
|
### Tri des machines
|
||||||
|
self.anim.iter=len(self.machines)
|
||||||
|
for machine in self.machines :
|
||||||
|
self.anim.cycle()
|
||||||
|
# Calculs préliminaires
|
||||||
|
try :
|
||||||
|
nom , zone = machine.nom().split('.',1)
|
||||||
|
zone = zone.encode('utf-8')
|
||||||
|
except :
|
||||||
|
warnings += u'Machine ignorée (mid=%s) : format nom incorrect (%s)\n' % ( machine.id().encode('utf-8'), machine.nom().encode('utf-8') )
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Le direct
|
||||||
|
if zone in self.zones_direct :
|
||||||
|
ligne=''
|
||||||
|
if machine.ip() != '<automatique>':
|
||||||
|
ligne += "%s\tIN\tA\t%s\n" % ( nom, machine.ip() )
|
||||||
|
# Si la machine est une borne wifi, on ajoute la position
|
||||||
|
if isinstance(machine,ldap_crans.BorneWifi) and machine.position():
|
||||||
|
ligne +="%s\tIN\tTXT\t\"LOC %s,%s \"\n" % (nom,machine.position()[0],machine.position()[1])
|
||||||
|
# Si la machine à des clefs ssh, on ajoute les champs SSFP correspondant
|
||||||
|
for sshkey in machine.sshFingerprint():
|
||||||
|
try:
|
||||||
|
[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)
|
||||||
|
key1=hashlib.sha1(base64.b64decode(key)).hexdigest()
|
||||||
|
key2=hashlib.sha256(base64.b64decode(key)).hexdigest()
|
||||||
|
ligne +="%s\tIN\tSSHFP\t%s\t1\t%s\n" % (nom,algo,key1)
|
||||||
|
ligne +="%s\tIN\tSSHFP\t%s\t2\t%s\n" % (nom,algo,key2)
|
||||||
|
except(ValueError,TypeError): pass
|
||||||
|
direct[zone] = direct.get(zone, "") + ligne
|
||||||
|
if isinstance(machine,ldap_crans.BorneWifi):
|
||||||
|
direct['ap.crans.org'] = direct.get('ap.crans.org', "") + ligne
|
||||||
|
elif self.verbose and machine.nom() != "ftp.federez.net":
|
||||||
|
warnings += u'Résolution directe ignorée (mid=%s) : zone non autoritaire (%s)\n' % ( machine.id().encode('utf-8'), zone.encode('utf-8') )
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
# Le direct avec alias
|
||||||
|
for alias in machine.alias() :
|
||||||
|
alias = alias.encode('utf-8')
|
||||||
|
# Cas particulier : nom de l'alias = nom de la zone
|
||||||
|
if alias in self.zones_direct :
|
||||||
|
if machine.ip() != '<automatique>':
|
||||||
|
ligne = "@\tIN\tA\t%s\n" % machine.ip()
|
||||||
|
ligne = ligne.encode('utf-8')
|
||||||
|
direct[alias] = direct.get(alias, "") + ligne
|
||||||
|
if machine.dnsIpv6():
|
||||||
|
ligne = "@\tIN\tAAAA\t%s\n" % machine.ipv6()
|
||||||
|
ligne = ligne.encode('utf-8')
|
||||||
|
direct[alias]= direct.get(alias, "") + ligne
|
||||||
|
if alias in self.zones_v4_to_v6:
|
||||||
|
ligne = "@\tIN\tAAAA\t%s\n" % machine.ipv6()
|
||||||
|
ligne = ligne.encode('utf-8')
|
||||||
|
zone6 = self.zones_v4_to_v6[alias]
|
||||||
|
direct[zone6] = direct.get(zone6, '') + ligne
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 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
|
||||||
|
if not ok:
|
||||||
|
warnings += u'Alias ignoré (mid=%s) : %s\n' % ( machine.id().encode('utf-8'), alias.encode('utf-8') )
|
||||||
|
continue
|
||||||
|
zone = zone.encode('utf-8')
|
||||||
|
ligne = "%s\tIN\tCNAME\t%s.\n" % ( nom, machine.nom() )
|
||||||
|
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
|
||||||
|
|
||||||
|
# Le reverse
|
||||||
|
ip = machine.ip()
|
||||||
|
if ip == '<automatique>':
|
||||||
|
net=False
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
net = AddrInNets(ip, self.zones_reverse)
|
||||||
|
if net:
|
||||||
|
base_ip = ip.split('.')
|
||||||
|
base_ip.reverse()
|
||||||
|
zone, length = self.reverse(net, ip)
|
||||||
|
zone = zone.encode('utf-8')
|
||||||
|
ligne = '%s\tIN\tPTR\t%s.\n' % ('.'.join(base_ip[:length]), machine.nom())
|
||||||
|
try : reverse[zone] += ligne
|
||||||
|
except : reverse[zone] = ligne
|
||||||
|
elif self.verbose >= 2 or machine.nom() not in ('freebox.crans.org', 'ovh.crans.org', 'kokarde.crans.org'):
|
||||||
|
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') )
|
||||||
|
|
||||||
|
### Ajouts pour les fichiers de résolution directs
|
||||||
|
for zone in direct.keys() :
|
||||||
|
# MXs
|
||||||
|
direct[zone] = MX + direct[zone]
|
||||||
|
|
||||||
|
### XXX: création de la zone inverse pour le /48 IPv6 complet du Cr@ns
|
||||||
|
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, "")
|
||||||
|
|
||||||
|
### Alias de zone
|
||||||
|
zone_todo = [zone for zone in self.zone_alias]
|
||||||
|
while zone_todo:
|
||||||
|
for zone in zone_todo:
|
||||||
|
for alias in self.zone_alias[zone]:
|
||||||
|
try:
|
||||||
|
direct[alias] = direct[zone]
|
||||||
|
zone_todo.remove(zone)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
if alias in self.zones_v4_to_v6:
|
||||||
|
alias_v6=self.zones_v4_to_v6[alias]
|
||||||
|
zone_v6 = self.zones_v4_to_v6[zone]
|
||||||
|
direct[alias_v6] = direct[zone_v6]
|
||||||
|
|
||||||
|
### Ajout des parametres SPF
|
||||||
|
direct['crans.org'] +='; Parametres SPF\n'
|
||||||
|
direct['crans.org'] +='crans.org.\tIN\tSPF\t"v=spf1 a mx ip6:2a01:240:fe3d:4::/64 ?all"\n'
|
||||||
|
for m in self.MXs:
|
||||||
|
direct['crans.org'] +='%s.\tIN\tSPF\t"v=spf1 a ip6:2a01:240:fe3d:4::/64 ?all"\n' % m.split()[-1]
|
||||||
|
direct['crans.org'] += '\n'
|
||||||
|
|
||||||
|
direct['crans.ens-cachan.fr'] += '; Parametres SPF\n'
|
||||||
|
direct['crans.ens-cachan.fr'] += 'crans.ens-cachan.fr.\tIN\tSPF\t"v=spf1 a:crans.org mx ip6:2a01:240:fe3d:4::/64 ?all"\n\n'
|
||||||
|
|
||||||
|
### Ajout d'eventuels champs SRV
|
||||||
|
direct['crans.org'] +='; Champs SRV\n'
|
||||||
|
for s in self.SRVs:
|
||||||
|
direct['crans.org'] += s + '\n'
|
||||||
|
for s in self.NATPRs:
|
||||||
|
direct['crans.org'] += s + '\n'
|
||||||
|
direct['crans.org'] += '\n'
|
||||||
|
|
||||||
|
### Ajout des délégations de zones
|
||||||
|
for deleg in self.DELEG.keys():
|
||||||
|
nom, zone = deleg.split('.',1)
|
||||||
|
if not zone in direct.keys():
|
||||||
|
warnings += u'Délégation ignorée %s : on ne génère pas la zone parent\n' % deleg
|
||||||
|
continue
|
||||||
|
for serv in self.DELEG[deleg]:
|
||||||
|
direct[zone] = direct[zone] + "%s\tIN\tNS\t%s\n" % ( nom, serv )
|
||||||
|
|
||||||
|
for zone in direct.keys():
|
||||||
|
child, parent = zone.split('.',1)
|
||||||
|
if not zone in self.DELEG.keys() and parent in self.zones_direct + [z for l in self.zone_alias.values() for z in l] + self.zones_v4_to_v6.values():
|
||||||
|
for d in self.DNSs:
|
||||||
|
direct[parent] = direct.get(parent, "") + '%s\tIN\tNS %s.\n' % (child, d)
|
||||||
|
|
||||||
|
|
||||||
|
### Ajout d'eventuel champs DS pour les délégation dnssec
|
||||||
|
for zone,ds in self.DS.items():
|
||||||
|
for s in ds:
|
||||||
|
direct[zone] += s + '\n'
|
||||||
|
direct[zone] += '\n'
|
||||||
|
|
||||||
|
### Ecriture des fichiers de zone et préparation du fichier de définition
|
||||||
|
f = ''
|
||||||
|
for zone, lignes in direct.items() + reverse.items() :
|
||||||
|
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
|
||||||
|
fd = self._open_conf(file,';')
|
||||||
|
fd.write(self.zone_entete % \
|
||||||
|
{ 'zone' : zone, 'serveur_autoritaire' : self.DNSs[0] , 'serial' : serial } )
|
||||||
|
fd.write('\n')
|
||||||
|
fd.write(DNS)
|
||||||
|
fd.write(lignes)
|
||||||
|
fd.close()
|
||||||
|
os.chmod(file,0664)
|
||||||
|
if short_name(gethostname()) in map(short_name, dns.DNSs[1:]):
|
||||||
|
f += self.zone_template_slave % {'NOM_zone': zone, 'FICHIER_zone': path,
|
||||||
|
'ip_master_DNS': self.ip_master_DNS}
|
||||||
|
else:
|
||||||
|
f += self.zone_template % { 'NOM_zone' : zone, 'FICHIER_zone' : path }
|
||||||
|
|
||||||
|
### Ecriture fichier de définition
|
||||||
|
fd = self._open_conf(self.DNS_CONF,'//')
|
||||||
|
fd.write(f)
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
return warnings
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__' :
|
||||||
|
from config import bcfg2_main
|
||||||
|
hostname = short_name(gethostname())
|
||||||
|
if hostname == short_name(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()
|
||||||
|
if hostname == short_name(dns.DNSs[0]):
|
||||||
|
print "Ce serveur est également serveur maitre, mais la reconfiguration du DNS maitre se fait par generate."
|
||||||
|
elif hostname == short_name(dns.DELEG[config.dns.zone_tv][0][0:-1]):
|
||||||
|
print "Serveur maître pour tv.crans.org, génération de la zone"
|
||||||
|
c = dns()
|
||||||
|
c.gen_tv()
|
||||||
|
import subprocess
|
||||||
|
args="/usr/sbin/ods-signer sign tv.crans.org".split()
|
||||||
|
p=subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
||||||
|
ret=p.communicate()
|
||||||
|
print ret[0].strip()
|
||||||
|
print ret[1].strip()
|
||||||
|
if hostname == short_name(dns.DNSs[0]):
|
||||||
|
print "Ce serveur est également serveur maitre, mais la reconfiguration du DNS maitre se fait par generate."
|
||||||
|
elif hostname == short_name(dns.DNSs[0]):
|
||||||
|
print "Ce serveur est maître ! Utilisez generate."
|
||||||
|
elif hostname in map(lambda fullhostname : short_name(fullhostname),dns.DNSs[1:]+dns.DNSs_private):
|
||||||
|
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."
|
File diff suppressed because it is too large
Load diff
|
@ -1,532 +0,0 @@
|
||||||
#! /usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import base64
|
|
||||||
import hashlib
|
|
||||||
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]
|
|
||||||
|
|
||||||
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 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 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):
|
|
||||||
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:
|
|
||||||
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 = 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.eu': [
|
|
||||||
DS('wifi', '37582 8 2 51809b508e450d8fec44572a3fa31754c27507465775c7d1c86570abd7a21024'),
|
|
||||||
DS('v6','12562 8 2 0a8e92398c5213cf2907b79f0fa8bd7db6729e0d2f6ca7443ac5a4b8441e38bd'),
|
|
||||||
],
|
|
||||||
'v6.crans.eu' : [
|
|
||||||
DS('wifi','1799 8 2 52a40a7dfb3e9c88aee032c21c59be756c8d3de29149c408ed8b699d83e30032'),
|
|
||||||
],
|
|
||||||
'crans.org': [
|
|
||||||
DS('v6','23641 8 2 3fff97a2581f0f2f49257b4914d5badf8ccb0a49c5a6f4cbf2f520b97de332d0'),
|
|
||||||
DS('adm','565 8 2 498f6cd5bcf291aae4129700a7569fa6e9a86821185bd655f0b9efc6a3bf547e'),
|
|
||||||
DS('ferme','35156 8 2 b63a1443b3d7434429e879e046bc8ba89056cdcb4b9c3566853e64fd521895b8'),
|
|
||||||
DS('wifi','41320 8 2 024799c1d53f1e827f03d17bc96709b85ee1c05d77eb0ebeadcfbe207ee776a4'),
|
|
||||||
DS('tv','30910 8 2 3317f684081867ab94402804fbb3cd187e29655cc7f34cb92c938183fe0b71f5'),
|
|
||||||
],
|
|
||||||
'v6.crans.org' : [
|
|
||||||
DS('adm','1711 8 2 f154eeb8eb346d2ca5cffb3f9cc464a17c0c4d69ee425b4fe44eaed7f5dd253b'),
|
|
||||||
DS('ferme','44434 8 2 fb87cb4216599cb6574add543078a9e48d0e50438483386585a9960557434ab0'),
|
|
||||||
DS('wifi','59539 8 2 dbe86f2f2e92d6a27bd1436f03ec1588f2948a2aa02124de0383be801cced85e'),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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]:
|
|
||||||
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):
|
|
||||||
# reverse ipv4
|
|
||||||
for net in zones_reverse_v4:
|
|
||||||
net = netaddr.IPNetwork(net)
|
|
||||||
if net.prefixlen > 24:
|
|
||||||
subnets = net.subnet(32)
|
|
||||||
elif net.prefixlen > 16:
|
|
||||||
subnets = net.subnet(24)
|
|
||||||
elif net.prefixlen > 8:
|
|
||||||
subnets = net.subnet(16)
|
|
||||||
else:
|
|
||||||
subnets = net.subnet(8)
|
|
||||||
for subnet in subnets:
|
|
||||||
zones[str(subnet)]=ZoneReverse(str(subnet), ttl, self.gen_soa(ns_list, serial, ttl), ns_list)
|
|
||||||
# reverse ipv6
|
|
||||||
for net in zones_reverse_v6:
|
|
||||||
zones[net]=ZoneReverse(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()
|
|
||||||
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."
|
|
||||||
|
|
|
@ -289,7 +289,7 @@ class isc(base_reconfigure):
|
||||||
class sable(base_reconfigure):
|
class sable(base_reconfigure):
|
||||||
|
|
||||||
def dns(self):
|
def dns(self):
|
||||||
from gen_confs.bind2 import dns
|
from gen_confs.bind import dns
|
||||||
self._do(dns(), [])
|
self._do(dns(), [])
|
||||||
|
|
||||||
class routeur(base_reconfigure):
|
class routeur(base_reconfigure):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue