scripts/gestion/gen_confs/bind.py
mathieu.segaud 998f5f28be Champs SRV (rfc2782)
Permet de dfinir de dcouvrir des services autres que Mail Xchanger,
c'est une gnralisation du champ MX. Il est notamment utilis dans
les protocoles XMPP (jabber) et SIP, pour retrouver les serveurs
associs. La syntaxe est de la forme:
_xmpp-client._tcp.crans.org 86400    IN  SRV  5 0  5222  xmpp.crans.org.
_sip._udp.crans.org         86400    IN  SRV  5 0  5060  voip.crans.org.

les 2 valeurs precedant le port sont respectivement la priorite et le
poids (2 niveaux de priorite pour du load-balancing par exemple)
Necessaire pour le futur schema d'adresse unifie
mail/xmpp/sip login@crans.org

Pour l'instant aucun champ SRV n'est utilise.

darcs-hash:20080906215214-e59bf-40746138791ce4dbad036c28b75a13ad81bb2c1c.gz
2008-09-06 23:52:14 +02:00

293 lines
12 KiB
Python
Executable file

#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
""" Génération de la configuration pour bind9
Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
import time, sys
sys.path.append('/usr/scripts/gestion')
from gen_confs import gen_config
from iptools import AddrInNet, AddrInNets
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
# 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 cfengine : python /usr/scripts/gestion/gen_confs/bind.py puis lancer cfrun)
# Résolution directe
zones_direct = [ 'crans.org' , 'crans.ens-cachan.fr', 'wifi.crans.org' , 'ferme.crans.org' , 'clubs.ens-cachan.fr', 'adm.crans.org' ]
# Résolution inverse
zones_reverse = [ '138.231.136.0/21', '138.231.144.0/21', '10.231.136.0/24' ]
### Liste DNS
# Le premier doit être le maitre
DNSs = [ 'rouge.crans.org' , 'sila.crans.org' , 'freebox.crans.org' , 'ovh.crans.org' , 'sable.crans.org' ]
DNSs_private = ['vert.adm.crans.org']
ip_master_DNS = "10.231.136.3"
### 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 = { 'tv.crans.org' : ['rouge.crans.org.' , 'sila.crans.org.' , 'freebox.crans.org.', 'sable.crans.org' , 'mouton.ferme.crans.org.'] }
### Serveurs de mail
# format : [ priorité serveur , .... ]
MXs = ['10 rouge.crans.org', '20 ovh.crans.org', '20 freebox.crans.org']
SRVs = []
### Entète des fichiers de zone
zone_entete="""
$ORIGIN %(zone)s.
$TTL 86400
@\tIN\tSOA rouge.crans.org. root.crans.org. (
%(serial)i ; numero de serie
21600 ; refresh (s)
3600 ; retry (s)
1209600 ; expire (s)
86400 ; 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_CFENFINE 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
restart_cmd = '/etc/init.d/bind9 reload'
######################################FIN PARTIE DE CONFIGURATION
def __str__(self) :
return "DNS"
def gen_slave(self) :
""" Génération du fichier de config de zone pour les esclaves """
zones = self.zones_direct
# Ajout des zones reverse
for net in self.zones_reverse :
n = map(int,net.split('/')[0].split('.')[:3])
while 1 :
if not AddrInNet("%d.%d.%d.1" % tuple(n),net):
break
else :
n.reverse()
zones.append("%d.%d.%d.in-addr.arpa" % tuple(n))
n.reverse()
n[2] += 1
# Ecriture
fd = self._open_conf(self.DNS_CONF_BCFG2,'//')
for zone in zones :
fd.write(self.zone_template_slave % { 'NOM_zone' : zone,
'FICHIER_zone' : self.DNS_DIR + 'db.' + zone,
'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 += '%(zone)s.\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"
### Ajout des parametres SPF
direct['crans.org'] +='; Parametres SPF\n'
direct['crans.org'] +='crans.org.\tIN\tTXT\t"v=spf1 a mx ?all"\n'
for m in self.MXs:
direct['crans.org'] +='%s.\tIN\tTXT\t"v=spf1 a -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\tTXT\t"v=spf1 a:crans.org mx ?all"\n\n'
### Ajout d'eventuels champs SRV
direct['crans.org'] +='; Champs SRV\n'
for s in self.SRVs:
direct['crans.org'] += s + '\n'
direct['crans.org'] += '\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('iso-8859-1')
except :
warnings += u'Machine ignorée (mid=%s) : format nom incorrect (%s)\n' % ( machine.id().encode('iso-8859-1'), machine.nom().encode('iso-8859-1') )
continue
# Le direct
if zone in self.zones_direct :
ligne = "%s\tIN\tA\t%s\n" % ( nom, machine.ip() )
try : direct[zone] += ligne
except : direct[zone] = 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('iso-8859-1'), zone.encode('iso-8859-1') )
# Le direct avec alias
for alias in machine.alias() :
# Cas particulier : nom de l'alias = nom de la zone
if alias in self.zones_direct :
ligne = "@\tIN\tA\t%s\n" % machine.ip()
ligne = ligne.encode('iso-8859-1')
alias = alias.encode('iso-8859-1')
try : direct[alias] += ligne
except : direct[alias] = 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('iso-8859-1'), alias.encode('iso-8859-1') )
continue
zone = zone.encode('iso-8859-1')
ligne = "%s\tIN\tCNAME\t%s.\n" % ( nom, machine.nom() )
try : direct[zone] += ligne
except : direct[zone] = ligne
# Le reverse
if AddrInNets(machine.ip(), self.zones_reverse) :
base_ip = machine.ip().split('.')
base_ip.reverse()
zone = "%s.%s.%s.in-addr.arpa" % tuple(base_ip[1:])
zone = zone.encode('iso-8859-1')
ligne = '%s\tIN\tPTR\t%s.\n' % (base_ip[0],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'):
warnings += u'Résolution inverse ignorée (mid=%s) : ip sur zone non autoritaire (%s)\n' % ( machine.id().encode('iso-8859-1'), machine.ip().encode('iso-8859-1') )
### Ajouts pour les fichiers de résolution directs
for zone in direct.keys() :
# MXs
direct[zone] = MX % { 'zone' : zone } + direct[zone]
### 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 )
### Ecriture des fichiers de zone et préparation du fichier de définition
f = ''
for zone, lignes in direct.items() + reverse.items() :
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()
f += self.zone_template % { 'NOM_zone' : zone, 'FICHIER_zone' : file }
### Ecriture fichier de définition
fd = self._open_conf(self.DNS_CONF,'//')
fd.write(f)
fd.close()
return warnings
if __name__ == '__main__' :
from socket import gethostname
from config import cfengine_main
def short_name(fullhostname):
return fullhostname.split(".")[0]
hostname = short_name(gethostname())
if hostname == short_name(cfengine_main):
print "Reconfiguration du fichier de cfengine pour configurer le bind d'un serveur en esclave (pensez à lancer cfrun)."
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.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 le sur %s, puis lancez cfrun" % cfengine_main
else:
print "Ce serveur ne correspond à rien pour la configuration DNS."