Merge branch 'master' of ssh://git.crans.org/git/usr-scripts

This commit is contained in:
Kévin "NeK" Moisy-Mabille 2014-03-03 12:07:49 +01:00
commit 30aab68b86
46 changed files with 1075 additions and 200 deletions

4
.gitignore vendored
View file

@ -45,6 +45,10 @@ var/
surveillance/mac_prises/output/ surveillance/mac_prises/output/
doc/build/ doc/build/
# modules tier
pyasn1_modules/
pythondialog/
# etat_* de la connexion de secours # etat_* de la connexion de secours
secours/etat_* secours/etat_*

View file

@ -19,12 +19,15 @@
# Voir des exemples plus complets ici: # Voir des exemples plus complets ici:
# https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/ # https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/
from lc_ldap.shortcuts import with_ldap_conn, lc_ldap_anonymous import lc_ldap.shortcuts
from lc_ldap.crans_utils import escape as escape_ldap from lc_ldap.crans_utils import escape as escape_ldap
import lc_ldap.crans_utils
from gestion.config.config import vlans from gestion.config.config import vlans
import lc_ldap.objets import lc_ldap.objets
import radiusd import radiusd
import netaddr import netaddr
import traceback
from gestion.gen_confs.trigger import trigger_generate_cochon as trigger_generate
# Voilà, pour faire marcher le V6Only, il faut se retirer l'ipv4 de sa machine # Voilà, pour faire marcher le V6Only, il faut se retirer l'ipv4 de sa machine
# ou en enregistrer une nouvelle (sans ipv4) avec une autre mac. Moi j'ai la # ou en enregistrer une nouvelle (sans ipv4) avec une autre mac. Moi j'ai la
@ -35,12 +38,28 @@ u'dc:9f:db:5c:c3:ea', # polynice-wlan0
u'00:26:c7:a6:9e:16', # cerveaulent u'00:26:c7:a6:9e:16', # cerveaulent
] ]
# TODO (à metre dans bcfg2)
#setfacl -m u:freerad:rx /etc/crans/
#setfacl -m u:freerad:rx /etc/crans/secrets
#setfacl -m u:freerad:r /etc/crans/secrets/dhcp.py
#setfacl -m u:freerad:r /etc/crans/secrets/secrets.py
#setfacl -m u:freerad:r /etc/crans/secrets/trigger-generate.pub
#setfacl -m m::r /etc/crans/secrets/trigger-generate
#setfacl -m u:freerad:r /etc/crans/secrets/trigger-generate
bl_reject = [u'bloq'] bl_reject = [u'bloq']
bl_isolement = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra'] bl_isolement = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra']
# TODO carte_etudiant: dépend si sursis ou non (regarder lc_ldap) # TODO carte_etudiant: dépend si sursis ou non (regarder lc_ldap)
# TODO LOGSSSSS # TODO LOGSSSSS
bl_accueil = [u'carte_etudiant', u'chambre_invalide', u'paiement'] bl_accueil = [u'carte_etudiant', u'chambre_invalide', u'paiement']
# Decorateur utilisé plus tard (same connection)
use_ldap_admin = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5,
constructor=lc_ldap.shortcuts.lc_ldap_admin)
use_ldap = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5,
constructor=lc_ldap.shortcuts.lc_ldap_anonymous)
@use_ldap
def get_machines(auth_data, conn): def get_machines(auth_data, conn):
mac = None mac = None
username = None username = None
@ -49,25 +68,100 @@ def get_machines(auth_data, conn):
# Calling-Station-Id: "une_adresse_mac" # Calling-Station-Id: "une_adresse_mac"
for (key, value) in auth_data: for (key, value) in auth_data:
if key == 'Calling-Station-Id': if key == 'Calling-Station-Id':
mac = escape_ldap(value.decode('ascii', 'ignore').replace('"','')) try:
value = value.decode('ascii', 'ignore').replace('"','')
mac = lc_ldap.crans_utils.format_mac(value)
except:
radiusd.radlog(radiusd.L_ERR, 'Cannot format MAC !')
if key == 'User-Name': if key == 'User-Name':
username = escape_ldap(value.decode('ascii', 'ignore').replace('"','')) username = escape_ldap(value.decode('ascii', 'ignore').replace('"',''))
# TODO # TODO
# format mac (done by preprocess & hints)
# format username (strip .wifi.crans.org) # format username (strip .wifi.crans.org)
base = u'(objectclass=machine)' base = u'(objectclass=machine)'
# Search by reported mac, then (if failure) search by username (mac or host)
return conn.search(u'(&%s(macAddress=%s))' % (base, mac)) or \
conn.search(u'(&%s(|(macAddress=%s)(host=%s.wifi.crans.org)))' %
(base, username, username))
# Decorateur utilisé plus tard (same connection) if mac is None:
use_ldap = with_ldap_conn(retries=2, delay=5, constructor=lc_ldap_anonymous) radiusd.radlog(radiusd.L_ERR, 'Cannot read client MAC from AP !')
return []
mac = escape_ldap(mac)
username = escape_ldap(username)
search_strats = [
# Case 1: Search by mac (reported by AP)
u'(&%s(macAddress=%s))' % (base, mac),
# Case 2: unregistered mac
u'(&%s(macAddress=<automatique>)(host=%s.wifi.crans.org))' %
(base, username),
]
for filter_s in search_strats:
res = conn.search(filter_s)
if res:
break
return res
def get_prise(auth_data):
"""Extrait la prise"""
## Regarder dans
## Filaire: NAS-Identifier => contient le nom du switch (batm-3.adm.crans.org)
## Nas-Port => port du switch (ex 42)
## WiFi: NAS-Identifier => vide
## Nas-Port => numéro sur l'interface
## Nas-IP-Address => adresse IP de la borne
is_wifi = True
bat_name = None
bat_num = None
port = None
for (key, value) in auth_data:
if key == 'NAS-Identifier':
if value.startswith('bat'):
nas = value.split('.', 1)[0]
try:
bat_name = nas[3]
bat_num = nas.split('-', 1)[1]
except IndexError:
pass
if key == 'Nas-Port':
port = int(value)
if bat_num and bat_name and port:
return bat_name + "%01d%02d" % (bat_num, port)
@use_ldap_admin
def register_mac(auth_data, machine, conn):
for (key, value) in auth_data:
if key == 'Calling-Station-Id':
try:
value = value.decode('ascii', 'ignore').replace('"','')
mac = lc_ldap.crans_utils.format_mac(value)
except:
radiusd.radlog(radiusd.L_ERR, 'Cannot format MAC !')
if mac is None:
radiusd.radlog(radiusd.L_ERR, 'Cannot find MAC')
return
mac = unicode(mac.lower())
with conn.search(unicode(machine.dn.split(',',1)[0]), mode='rw')[0] as machine:
radiusd.radlog(radiusd.L_INFO, 'Registering mac %s' % mac)
machine['macAddress'] = mac
machine.history_add(u'auth.py', u'macAddress (<automatique> -> %s)' % mac)
machine.validate_changes()
machine.save()
radiusd.radlog(radiusd.L_INFO, 'Mac set')
radiusd.radlog(radiusd.L_INFO, 'Triggering komaz')
trigger_generate('komaz')
radiusd.radlog(radiusd.L_INFO, 'done ! (triggered komaz)')
@use_ldap_admin
@use_ldap @use_ldap
def instantiate(p, conn): def instantiate(p, *conns):
"""Utile pour initialiser la connexion ldap une première fois (otherwise, """Utile pour initialiser les connexions ldap une première fois (otherwise,
do nothing)""" do nothing)"""
pass pass
@ -79,16 +173,21 @@ def wifi_authorize(auth_data, conn):
champs login et mot de passe qui serviront ensuite à l'authentification champs login et mot de passe qui serviront ensuite à l'authentification
(MschapV2/PEAP ou MschapV2/TTLS)""" (MschapV2/PEAP ou MschapV2/TTLS)"""
items = get_machines(auth_data, conn) items = get_machines(auth_data)
if not items: if not items:
radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Nobody found') radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Nobody found')
return radiusd.RLM_MODULE_NOTFOUND return radiusd.RLM_MODULE_NOTFOUND
if len(items) > 1: if len(items) > 1:
radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Too many results') radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Too many results (took first)')
machine = items[0] machine = items[0]
if '<automatique>' in machine['macAddress']:
register_mac(auth_data, machine)
proprio = machine.proprio() proprio = machine.proprio()
if isinstance(proprio, lc_ldap.objets.AssociationCrans): if isinstance(proprio, lc_ldap.objets.AssociationCrans):
radiusd.radlog(radiusd.L_ERR, 'Crans machine trying to authenticate !') radiusd.radlog(radiusd.L_ERR, 'Crans machine trying to authenticate !')
@ -102,8 +201,10 @@ def wifi_authorize(auth_data, conn):
radiusd.radlog(radiusd.L_ERR, 'WiFi authentication but machine has no' + radiusd.radlog(radiusd.L_ERR, 'WiFi authentication but machine has no' +
'password') 'password')
return radiusd.RLM_MODULE_REJECT return radiusd.RLM_MODULE_REJECT
password = machine['ipsec'][0].value.encode('ascii', 'ignore') password = machine['ipsec'][0].value.encode('ascii', 'ignore')
# TODO: feed cert here
return (radiusd.RLM_MODULE_UPDATED, return (radiusd.RLM_MODULE_UPDATED,
(), (),
( (
@ -121,7 +222,7 @@ def post_auth(auth_data, conn):
reason = '' reason = ''
identity = "" #TODO identity = "" #TODO
prise = "" #TODO prise = "" #TODO
items = get_machines(auth_data, conn) items = get_machines(auth_data)
decision = 'adherent','' decision = 'adherent',''
if not items: if not items:
@ -133,7 +234,6 @@ def post_auth(auth_data, conn):
if isinstance(machine, lc_ldap.objets.machineWifi): if isinstance(machine, lc_ldap.objets.machineWifi):
decision = 'wifi', '' decision = 'wifi', ''
print machine['macAddress'][0].value
if not machine['ipHostNumber'] or unicode(machine['macAddress'][0]) in test_v6: if not machine['ipHostNumber'] or unicode(machine['macAddress'][0]) in test_v6:
decision = 'v6only', 'No IPv4' decision = 'v6only', 'No IPv4'
elif machine['ipHostNumber'][0].value in netaddr.IPNetwork('10.2.9.0/24'): elif machine['ipHostNumber'][0].value in netaddr.IPNetwork('10.2.9.0/24'):
@ -142,14 +242,14 @@ def post_auth(auth_data, conn):
for bl in machine.blacklist_actif(): for bl in machine.blacklist_actif():
if bl.value['type'] in bl_isolement: if bl.value['type'] in bl_isolement:
decision = 'isolement', unicode(bl).encode('utf-8') decision = 'isolement', unicode(bl)
if bl.value['type'] in bl_accueil: if bl.value['type'] in bl_accueil:
decision = 'accueil', unicode(bl).encode('utf-8') decision = 'accueil', unicode(bl)
vlan_name, reason = decision vlan_name, reason = decision
vlan = vlans[vlan_name] vlan = vlans[vlan_name]
radiusd.radlog(radiusd.L_INFO, 'auth.py: %s -> %s [%s%s]' % radiusd.radlog(radiusd.L_INFO, 'auth.py: %s -> %s [%s%s]' %
(prise, identity, vlan_name, (reason and ': ' + reason)) (prise, identity, vlan_name, (reason and u': ' + reason).encode(u'utf-8'))
) )
#<!> #<!>
@ -169,7 +269,10 @@ def post_auth(auth_data, conn):
# return (0, "Hébergeur non à jour", "accueil") # return (0, "Hébergeur non à jour", "accueil")
# #
#<!> #<!>
# Pour l'instant, on ne met pas d'infos de vlans dans la réponse
return radiusd.RLM_MODULE_OK
# This is dead code (for now)
return (radiusd.RLM_MODULE_UPDATED, return (radiusd.RLM_MODULE_UPDATED,
( (
("Tunnel-Type", "VLAN"), ("Tunnel-Type", "VLAN"),
@ -182,14 +285,6 @@ def post_auth(auth_data, conn):
def dummy_fun(p): def dummy_fun(p):
return radiusd.RLM_MODULE_OK return radiusd.RLM_MODULE_OK
recv_coa = dummy_fun
send_coa = dummy_fun
preacct = dummy_fun
accounting = dummy_fun
pre_proxy = dummy_fun
post_proxy = dummy_fun
def detach(p=None): def detach(p=None):
"""Appelé lors du déchargement du module (enfin, normalement)""" """Appelé lors du déchargement du module (enfin, normalement)"""
print "*** goodbye from example.py ***" print "*** goodbye from example.py ***"

View file

@ -11,25 +11,27 @@ python crans_wifi {
func_authorize = wifi_authorize func_authorize = wifi_authorize
# Renseigne le vlan # Renseigne le vlan
# remplacer par dummy_fun pour ignorer le tagging de vlan
mod_post_auth = freeradius.auth mod_post_auth = freeradius.auth
func_post_auth = post_auth func_post_auth = post_auth
# Le reste est dumb et inutile # Que faire avant de quitter
mod_accounting = freeradius.auth
func_accounting = accounting
mod_pre_proxy = freeradius.auth
func_pre_proxy = pre_proxy
mod_post_proxy = freeradius.auth
func_post_proxy = post_proxy
mod_recv_coa = freeradius.auth
func_recv_coa = recv_coa
mod_send_coa = freeradius.auth
func_send_coa = send_coa
mod_detach = freeradius.auth mod_detach = freeradius.auth
func_detach = detach func_detach = detach
# Le reste est dumb et inutile
mod_accounting = freeradius.auth
func_accounting = dummy_fun
mod_pre_proxy = freeradius.auth
func_pre_proxy = dummy_fun
mod_post_proxy = freeradius.auth
func_post_proxy = dummy_fun
mod_recv_coa = freeradius.auth
func_recv_coa = dummy_fun
mod_send_coa = freeradius.auth
func_send_coa = dummy_fun
} }

View file

@ -5,12 +5,21 @@ import auth
import sys import sys
import time import time
if len(sys.argv) < 2: delattr(sys, 'argv')
print "Give me a mac !"
sys.exit(1) #if len(sys.argv) < 2 and False:
# print "Give me a mac !"
# sys.exit(1)
# Machine à s'authentifier (cerveaulent) # Machine à s'authentifier (cerveaulent)
p=(('Calling-Station-Id', sys.argv[1]),) #p=(('Calling-Station-Id', sys.argv[1]),)
auth.instantiate(())
p=(
('Calling-Station-Id', 'ba:27:eb:3c:54:d5'),
('User-Name', 'test18'),
)
print repr(auth.wifi_authorize(p)) print repr(auth.wifi_authorize(p))
print "wait for 3s, tu peux aller crasher le serveur pg ou ldap" print "wait for 3s, tu peux aller crasher le serveur pg ou ldap"

View file

@ -74,7 +74,7 @@ def dialog(backtitle,arg,dialogrc='') :
elif not result : result=[''] elif not result : result=['']
return [ 0, result ] return [ 0, result ]
def coul(txt, col=None): def coul(txt, col=None, dialog=False):
""" """
Retourne la chaine donnée encadrée des séquences qui Retourne la chaine donnée encadrée des séquences qui
vont bien pour obtenir la couleur souhaitée vont bien pour obtenir la couleur souhaitée
@ -92,6 +92,21 @@ def coul(txt, col=None):
'cyan': 36, 'cyan': 36,
'gris': 30, 'gris': 30,
'gras': 50 } 'gras': 50 }
codecol_dialog = {
'rouge': 1,
'vert': 2,
'jaune': 3,
'bleu': 4,
'violet': 5,
'cyan': 6,
'gris': 0,
'gras': 'b' }
if dialog:
try:
txt = "\Z%s%s\Zn" % (codecol_dialog[col], txt)
finally:
return txt
try: try:
if col[:2] == 'f_': if col[:2] == 'f_':
add = 10 add = 10
@ -128,7 +143,7 @@ def cprint(txt, col='blanc', newline=True):
else: else:
print t, print t,
def tableau(data, titre=None, largeur=None, alignement=None, format=None): def tableau(data, titre=None, largeur=None, alignement=None, format=None, dialog=False):
""" """
Retourne une chaine formatée repésentant un tableau. Retourne une chaine formatée repésentant un tableau.
@ -183,9 +198,11 @@ def tableau(data, titre=None, largeur=None, alignement=None, format=None):
# Largeurs # Largeurs
########## ##########
if not largeur : if not largeur :
largeur = [ max([len(re.sub('\x1b\[1;([0-9]|[0-9][0-9])m','',ligne[i])) for ligne in data]) for i in range(nbcols) ] largeur = [ max([len(re.sub('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m','',ligne[i])) for ligne in data]) for i in range(nbcols) ]
elif '*' in largeur: elif '*' in largeur:
rows, cols = get_screen_size() rows, cols = get_screen_size()
if dialog:
cols = cols - 6
for i in range(nbcols) : for i in range(nbcols) :
if largeur[i] in ['*',-1] : if largeur[i] in ['*',-1] :
largeur[i] = max(cols - sum([l for l in largeur if l != '*']) - nbcols - 1, 3) largeur[i] = max(cols - sum([l for l in largeur if l != '*']) - nbcols - 1, 3)
@ -198,12 +215,12 @@ def tableau(data, titre=None, largeur=None, alignement=None, format=None):
def aligne (data, alignement, largeur) : def aligne (data, alignement, largeur) :
# Longeur sans les chaines de formatage # Longeur sans les chaines de formatage
l = len(re.sub('\x1b\[1;([0-9]|[0-9][0-9])m','',data)) l = len(re.sub('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m','',data))
# Alignement # Alignement
if l > largeur : if l > largeur :
# découpage d'une chaine trop longue # découpage d'une chaine trop longue
regexp = re.compile('\x1b\[1;([0-9]|[0-9][0-9])m') regexp = re.compile('\\\Z.' if dialog else '\x1b\[1;([0-9]|[0-9][0-9])m')
new_data = u'' new_data = u''
new_len = 0 new_len = 0

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/bin/bash /usr/scripts/python.sh
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
""" Pour détecter les gens en chambre invalide, les prévenir, et supprimer leurs machines """ Pour détecter les gens en chambre invalide, les prévenir, et supprimer leurs machines
@ -12,8 +12,8 @@
import datetime import datetime
import time import time
import re import re
import ldap_crans import lc_ldap.shortcuts
conn = ldap_crans.CransLdap() conn = lc_ldap.shortcuts.lc_ldap_admin()
import mail as mail_module import mail as mail_module
import sys import sys
@ -39,19 +39,20 @@ delai = config.demenagement_delai
# On récupère ceux qui n'ont pas payé cette année # On récupère ceux qui n'ont pas payé cette année
if config.periode_transitoire: if config.periode_transitoire:
bad_boys_e_s = conn.search('chbre=????&paiement=%d&paiement!=%d' % (year-1,year))['adherent'] bad_boys_e_s = conn.search(u'(&(aid)*)(chbre=????)(paiement=%d)(!(paiement=%d)))' % (year-1,year))
else: else:
bad_boys_e_s = conn.search('chbre=????&paiement=%d' % year)['adherent'] bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(paiement=%d))' % year)
now = time.time() now = time.time()
to_print = [] to_print = []
to_error = []
for clandestin in bad_boys_e_s: for clandestin in bad_boys_e_s:
# On cherche la dernière fois qu'il s'est retrouvé en chambre ???? # On cherche la dernière fois qu'il s'est retrouvé en chambre ????
for l in clandestin.historique(): for l in clandestin['historique'][::-1]:
# On récupère la date du dernier changement de chambre # On récupère la date du dernier changement de chambre
# (l'historique est enregistré par ordre chronologique) # (l'historique est enregistré par ordre chronologique)
x = re.match("(.*),.* : chbre \((.*) -> \?\?\?\?\)",l) x = re.match("(.*),.* : chbre \((.*) -> \?\?\?\?\)", str(l))
if x <> None: if x <> None:
kickout_date = x.group(1) kickout_date = x.group(1)
exchambre = x.group(2) exchambre = x.group(2)
@ -64,9 +65,7 @@ for clandestin in bad_boys_e_s:
if ttl > 0: if ttl > 0:
if (sendmails and machine_liste != [] or DEBUG) and (ttl >= (delai - 1)*86400 or 0 < ttl <= 86400): if (sendmails and machine_liste != [] or DEBUG) and (ttl >= (delai - 1)*86400 or 0 < ttl <= 86400):
# On lui envoie un mail pour le prévenir # On lui envoie un mail pour le prévenir
to = clandestin.mail() to = clandestin['mail'][0]
if not "@" in to:
to += "@crans.org"
mail = mail_module.generate('demenagement', {"from" : "respbats@crans.org", mail = mail_module.generate('demenagement', {"from" : "respbats@crans.org",
"chambre" : exchambre, "chambre" : exchambre,
"jours" : int(ttl/86400) + 1, "jours" : int(ttl/86400) + 1,
@ -81,16 +80,19 @@ for clandestin in bad_boys_e_s:
else: else:
for m in machine_liste: for m in machine_liste:
to_print.append( (clandestin.id(), m.ip(), m.id(), m.nom()) ) try:
m2 = conn.search('mid=%s' % m.id(),mode='w')['machine'][0] m2 = conn.search(u'mid=%s' % m['mid'][0],mode='w')[0]
m2.delete('Adherent sans chambre valide depuis %d jours' % delai) m2.delete('Adherent sans chambre valide depuis %d jours' % delai)
to_print.append( (clandestin['aid'][0], m['ipHostNumber'][0], m['mid'][0], m['host'][0]) )
except Exception as e:
to_error.append((clandestin['aid'][0], m['ipHostNumber'][0], m['mid'][0], m['host'][0], e))
message = u""
if to_print != []: if to_print != []:
# Il s'est passé quelque chose, donc on envoie un mail # Il s'est passé quelque chose, donc on envoie un mail
# On regarde le plus grand hostname # On regarde le plus grand hostname
hostnamemaxsize = max([len(i[3]) for i in to_print]) hostnamemaxsize = max([len(str(i[3])) for i in to_print])
template = u"| %%4s | %%-15s | %%4s | %%-%ss |\n" % (hostnamemaxsize) template = u"| %%4s | %%-15s | %%4s | %%-%ss |\n" % (hostnamemaxsize)
message = u""
message += u"\nListe des machines supprimées pour chambre invalide depuis plus de %s jours :\n" % delai message += u"\nListe des machines supprimées pour chambre invalide depuis plus de %s jours :\n" % delai
tiret_line = u"+------+-----------------+------+-%s-+\n" % ("-" * hostnamemaxsize) tiret_line = u"+------+-----------------+------+-%s-+\n" % ("-" * hostnamemaxsize)
message += tiret_line message += tiret_line
@ -98,8 +100,24 @@ if to_print != []:
message += tiret_line message += tiret_line
for aid, ip, mid, hostname in to_print: for aid, ip, mid, hostname in to_print:
message += template % (aid, ip, mid, hostname) message += template % (aid, ip, mid, hostname)
message += tiret_line message += tiret_line
message += u"\nScore de cette nuit : %s" % (len(to_print)) message += u"\nScore de cette nuit : %s" % (len(to_print))
if to_error != []:
hostnamemaxsize = max([len(str(i[3])) for i in to_error])
errormaxsize = max([len(str(i[4])) for i in to_error])
template = u"| %%4s | %%-15s | %%4s | %%-%ss | %%-%ss |\n" % (hostnamemaxsize, errormaxsize)
message += u"\n"
tiret_line = u"+------+-----------------+------+-%s-+-%s-+\n" % ("-" * hostnamemaxsize, "-" * errormaxsize)
message += u"\nListe des machines dont la supression à échoué :\n"
message += tiret_line
message += template % ("aid", " ip", "mid", (" " * (max((hostnamemaxsize-8)/2,0)) + "hostname"), (" " * (max((errormaxsize-6)/2,0)) + "erreur"))
for aid, ip, mid, hostname, error in to_error:
message += template % (aid, ip, mid, hostname, error)
message += tiret_line
if to_print != [] or to_error != []:
headers = u"From: respbats@crans.org\nSubject: %s\n" % Header("Machines supprimées pour chambre invalide", "utf8").encode() headers = u"From: respbats@crans.org\nSubject: %s\n" % Header("Machines supprimées pour chambre invalide", "utf8").encode()
headers += u"Content-Type: text/plain; charset=UTF-8\n" headers += u"Content-Type: text/plain; charset=UTF-8\n"
headers += u"X-Mailer: /usr/scripts/gestion/chambres_vides.py\n" headers += u"X-Mailer: /usr/scripts/gestion/chambres_vides.py\n"

View file

@ -5,4 +5,4 @@
adm_only = [] adm_only = []
role = {'zamok': ['adherents-server'], 'nat64': ['routeur-nat64'], 'komaz': ['wifi-router', 'appt-proxy', 'main-router'], 'dyson': ['sniffer'], 'isc': ['appt-proxy'], 'dhcp': ['appt-proxy'], 'ovh': ['externe'], 'routeur': ['appt-proxy']} role = {'zamok': ['adherents-server'], 'nat64': ['routeur-nat64'], 'komaz': ['wifi-router', 'appt-proxy', 'main-router'], 'dyson': ['sniffer'], 'isc': ['appt-proxy'], 'dhcp': ['appt-proxy'], 'ovh': ['externe'], 'soyouz': ['externe'], 'routeur': ['appt-proxy']}

View file

@ -30,8 +30,8 @@ zone_tv = 'tv.crans.org'
#: DNS en connexion de secours #: DNS en connexion de secours
secours_relay='10.231.136.14'; secours_relay='10.231.136.14';
#: Serveurs authoritaires pour les zones crans, le master doit être le premier #: Serveurs autoritaires pour les zones crans, le master doit être le premier
DNSs = ['sable.crans.org', 'freebox.crans.org', 'ovh.crans.org'] DNSs = ['sable.crans.org', 'freebox.crans.org', 'soyouz.crans.org']
#: Résolution DNS directe, liste de toutes les zones crans hors reverse #: Résolution DNS directe, liste de toutes les zones crans hors reverse
zones_direct = [ 'crans.org', 'crans.ens-cachan.fr', 'wifi.crans.org', 'ferme.crans.org' , 'clubs.ens-cachan.fr', 'adm.crans.org','crans.eu','wifi.crans.eu', 'tv.crans.org', 'ap.crans.org' ] zones_direct = [ 'crans.org', 'crans.ens-cachan.fr', 'wifi.crans.org', 'ferme.crans.org' , 'clubs.ens-cachan.fr', 'adm.crans.org','crans.eu','wifi.crans.eu', 'tv.crans.org', 'ap.crans.org' ]

1
gestion/config/services.py Symbolic link
View file

@ -0,0 +1 @@
/etc/crans/services.py

View file

@ -51,17 +51,18 @@ class ResourceRecord(object):
return str(self) return str(self)
class TLSA(ResourceRecord): class TLSA(ResourceRecord):
def __init__(self, name, port, proto, cert, certtype, reftype, compat=True, ttl=None): def __init__(self, name, port, proto, cert, certtype, reftype, selector=0, compat=True, format='pem', ttl=None):
""" """
name: nom du domaine du certificat name: nom du domaine du certificat
port: port écoute le service utilisant le certificat port: port écoute le service utilisant le certificat
proto: udp ou tcp proto: udp ou tcp
cert: le certificat au format pem (selector est donc toujours à 0) cert: le certificat au format ``format`` (pem ou der) (selector est donc toujours à 0)
certtype: type d'enregistrement 0 = CA pinning, 1 = cert pinning, 2 = self trusted CA, 3 = self trusted cert 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 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 compat: on génère un enregistement compris même par les serveurs dns n'implémentant pas TLSA
""" """
selector = 0 if not format in ['pem', 'der']:
raise ValueError("format should be pem or der")
if cert is None and proto == 'tcp' and name[-1] == '.': if cert is None and proto == 'tcp' and name[-1] == '.':
try: try:
cert = ssl.get_server_certificate((name[:-1], port), ca_certs='/etc/ssl/certs/ca-certificates.crt') cert = ssl.get_server_certificate((name[:-1], port), ca_certs='/etc/ssl/certs/ca-certificates.crt')
@ -69,10 +70,13 @@ class TLSA(ResourceRecord):
raise ValueError("Unable de retrieve cert dynamically: %s" % e) raise ValueError("Unable de retrieve cert dynamically: %s" % e)
elif cert is None: elif cert is None:
raise ValueError("cert can only be retrive if proto is tcp and name fqdn") raise ValueError("cert can only be retrive if proto is tcp and name fqdn")
dercert = ssl.PEM_cert_to_DER_cert(cert) if format is not 'der':
dercert = ssl.PEM_cert_to_DER_cert(cert)
else:
dercert = cert
if not dercert: if not dercert:
raise ValueError("Impossible de convertir le certificat au format DER %s %s %s\n%s" % (name, port, proto, cert)) raise ValueError("Impossible de convertir le certificat au format DER %s %s %s\n%s" % (name, port, proto, cert))
certhex = TLSA.hashCert(reftype, dercert) certhex = TLSA.hashCert(reftype, str(dercert))
if compat: if compat:
super(TLSA, self).__init__( super(TLSA, self).__init__(
'TYPE52', 'TYPE52',
@ -254,17 +258,32 @@ class Zone(ZoneBase):
def add_sshfp_record(self, nom, machine): def add_sshfp_record(self, nom, machine):
for sshkey in machine.get('sshFingerprint', []): for sshkey in machine.get('sshFingerprint', []):
algo_txt, key = str(sshkey).split()[:2] try:
algo=config.sshfs_ralgo[algo_txt][1] algo_txt, key = str(sshkey).split()[:2]
for hash in config.sshfp_hash.keys(): algo=config.sshfs_ralgo[algo_txt][1]
self.add(SSHFP(nom, hash, algo, key)) for hash in config.sshfp_hash.keys():
if self.ipv4 and self.ipv6: self.add(SSHFP(nom, hash, algo, key))
if nom == '@': if self.ipv4 and self.ipv6:
self.add(SSHFP("v4", hash, algo, key)) if nom == '@':
self.add(SSHFP("v6", hash, algo, key)) self.add(SSHFP("v4", hash, algo, key))
else: self.add(SSHFP("v6", hash, algo, key))
self.add(SSHFP("%s.v4" % nom, hash, algo, key)) else:
self.add(SSHFP("%s.v6" % nom, hash, algo, key)) self.add(SSHFP("%s.v4" % nom, hash, algo, key))
self.add(SSHFP("%s.v6" % nom, hash, algo, key))
# KeyError is l'algo dans ldap n'est pas connu
# TypeError si la clef n'est pas bien en base64
except (KeyError, TypeError):
pass
def add_tlsa_record(self, cert):
if 'TLSACert' in cert['objectClass']:
for host in cert['hostCert']:
nom=self.get_name(host)
if nom is None: continue
for port in cert['portTCPin']:
self.add(TLSA(nom, port, 'tcp', cert['certificat'][0], cert['certificatUsage'][0], cert['matchingType'][0], cert['selector'][0], format='der'))
for port in cert['portUDPin']:
self.add(TLSA(nom, port, 'udp', cert['certificat'][0], cert['certificatUsage'][0], cert['matchingType'][0], cert['selector'][0], format='der'))
def add_machine(self, machine): def add_machine(self, machine):
for host in machine['host']: for host in machine['host']:
@ -274,6 +293,8 @@ class Zone(ZoneBase):
self.add_a_record(nom, machine) self.add_a_record(nom, machine)
self.add_aaaa_record(nom, machine) self.add_aaaa_record(nom, machine)
self.add_sshfp_record(nom, machine) self.add_sshfp_record(nom, machine)
for cert in machine.certificats():
self.add_tlsa_record(cert)
if machine['host']: if machine['host']:
@ -411,7 +432,7 @@ class dns(gen_config) :
# format : [ priorité serveur , .... ] # format : [ priorité serveur , .... ]
MXs = [ MXs = [
MX('@',10, 'redisdead.crans.org.'), MX('@',10, 'redisdead.crans.org.'),
MX('@',20, 'ovh.crans.org.'), MX('@',15, 'soyouz.crans.org.'),
MX('@',25, 'freebox.crans.org.'), MX('@',25, 'freebox.crans.org.'),
] ]
SRVs = { SRVs = {
@ -437,10 +458,10 @@ class dns(gen_config) :
# /!\ Il faut faire attention au rollback des keys, il faudrait faire quelque chose d'automatique avec opendnssec # /!\ Il faut faire attention au rollback des keys, il faudrait faire quelque chose d'automatique avec opendnssec
DSs = { DSs = {
'crans.org': [ 'crans.org': [
DS('adm','565 8 2 498f6cd5bcf291aae4129700a7569fa6e9a86821185bd655f0b9efc6a3bf547e'), DS('adm', '64649 8 2 9c45f0fef063672d96c983d5a3813a08a649c72d357f41ddece73ae8872d60cf'),
DS('ferme','35156 8 2 b63a1443b3d7434429e879e046bc8ba89056cdcb4b9c3566853e64fd521895b8'), DS('ferme', '57424 8 2 2c21ec2a80a9ceb93fe085409ebdbab8d39145c18dc8ea8b23e9a38b5c414eb4'),
DS('wifi','41320 8 2 024799c1d53f1e827f03d17bc96709b85ee1c05d77eb0ebeadcfbe207ee776a4'), DS('wifi', '5531 8 2 daf30a647566234edc1617546fd74abbbaf965b17389248f72fc66a33d6f5063'),
DS('tv','30910 8 2 3317f684081867ab94402804fbb3cd187e29655cc7f34cb92c938183fe0b71f5'), DS('tv', '18199 8 2 d3cc2f5f81b830cbb8894ffd32c236e968edd3b0c0305112b6eb970aa763418e'),
], ],
} }
@ -473,6 +494,7 @@ class dns(gen_config) :
TLSA('nagios.crans.org.', 443, 'tcp', None, 3, 2), TLSA('nagios.crans.org.', 443, 'tcp', None, 3, 2),
TLSA('pad.crans.org.', 443, 'tcp', None, 3, 2), TLSA('pad.crans.org.', 443, 'tcp', None, 3, 2),
TLSA('news.crans.org.', 443, 'tcp', None, 3, 2), TLSA('news.crans.org.', 443, 'tcp', None, 3, 2),
TLSA('lists.crans.org.', 443, 'tcp', None, 3, 2),
TLSA('asterisk.crans.org.', 5061, 'tcp', None, 3, 2), TLSA('asterisk.crans.org.', 5061, 'tcp', None, 3, 2),
TLSA('smtp.crans.org.', 465, 'tcp', None, 3, 2), TLSA('smtp.crans.org.', 465, 'tcp', None, 3, 2),
TLSA('imap.crans.org.', 993, 'tcp', None, 3, 2), TLSA('imap.crans.org.', 993, 'tcp', None, 3, 2),

View file

@ -36,6 +36,8 @@ class dydhcp:
@raises OmapiError: @raises OmapiError:
@raises socket.error: @raises socket.error:
""" """
if '<automatique>' in [ip, mac]:
return
msg = OmapiMessage.open(b"host") msg = OmapiMessage.open(b"host")
msg.message.append((b"create", struct.pack("!I", 1))) msg.message.append((b"create", struct.pack("!I", 1)))
msg.message.append((b"exclusive", struct.pack("!I", 1))) msg.message.append((b"exclusive", struct.pack("!I", 1)))
@ -56,6 +58,8 @@ class dydhcp:
@raises OmapiError: @raises OmapiError:
@raises socket.error: @raises socket.error:
""" """
if '<automatique>' in [ip, mac]:
return
msg = OmapiMessage.open(b"host") msg = OmapiMessage.open(b"host")
msg.obj.append((b"hardware-address", pack_mac(mac))) msg.obj.append((b"hardware-address", pack_mac(mac)))
msg.obj.append((b"hardware-type", struct.pack("!I", 1))) msg.obj.append((b"hardware-type", struct.pack("!I", 1)))
@ -147,7 +151,8 @@ class dhcp(gen_config) :
for machine in self.machines : for machine in self.machines :
self.anim.cycle() self.anim.cycle()
for net in self.reseaux.keys() : for net in self.reseaux.keys() :
if machine.ip() != '<automatique>' and AddrInNet(machine.ip(), net) : if '<automatique>' not in [machine.ip(), machine.mac()] and \
AddrInNet(machine.ip(), net):
host_template = self.host_template host_template = self.host_template
# variable pour remplir le template # variable pour remplir le template
#d = { 'nom' : machine.nom().split('.')[0] , 'mac' : machine.mac() , 'ip' : machine.ip() } #d = { 'nom' : machine.nom().split('.')[0] , 'mac' : machine.mac() , 'ip' : machine.ip() }

View file

@ -3,7 +3,6 @@
import os import os
import sys import sys
import socket import socket
import netaddr
import utils import utils
from utils import pretty_print, anim, OK, cprint from utils import pretty_print, anim, OK, cprint
@ -96,7 +95,7 @@ class firewall(utils.firewall_tools) :
# for ip in ip_list: # for ip in ip_list:
# machine = self.conn.search(u"ipHostNumber=%s" % ip) # machine = self.conn.search(u"ipHostNumber=%s" % ip)
# # Est-ce qu'il y a des blacklists hard parmis les blacklists de la machine # # Est-ce qu'il y a des blacklists hard parmis les blacklists de la machine
# if machine and set([bl.value['type'] for bl in machine[0].blacklist_actif() ]).intersection(config.blacklist_sanctions): # if machine and set([bl['type'] for bl in machine[0].blacklist_actif() ]).intersection(config.blacklist_sanctions):
# try: self.ipset['blacklist']['hard'].add(ip) # try: self.ipset['blacklist']['hard'].add(ip)
# except IpsetError: pass # except IpsetError: pass
# else: # else:
@ -110,17 +109,9 @@ class firewall(utils.firewall_tools) :
chain = 'BLACKLIST_HARD' chain = 'BLACKLIST_HARD'
if fill_ipset: if fill_ipset:
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard'])
# On récupère la liste de toutes les ips blacklistés hard # On récupère la liste de toutes les ips blacklistés hard
bl_hard_ips = set( bl_hard_ips = self.blacklisted_ips(config.blacklist_sanctions, config.NETs['all'])
str(ip) for ips in anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard'])
[
machine['ipHostNumber'] for machine in self.blacklisted_machines() if machine['ipHostNumber'] and reduce(lambda x,y: x or y, ( ip.value in netaddr.IPNetwork(n) for n in config.NETs['all'] for ip in machine['ipHostNumber']))
if set([bl.value['type'] for bl in machine.blacklist_actif() ]).intersection(config.blacklist_sanctions)
]
for ip in ips
)
self.ipset['blacklist']['hard'].restore(bl_hard_ips) self.ipset['blacklist']['hard'].restore(bl_hard_ips)
print OK print OK
@ -137,6 +128,8 @@ class firewall(utils.firewall_tools) :
def test_mac_ip_dispatch(self, func, machine): def test_mac_ip_dispatch(self, func, machine):
"""Détermine à quel set de mac-ip appliquer la fonction ``func`` (add, delete, append, ...)""" """Détermine à quel set de mac-ip appliquer la fonction ``func`` (add, delete, append, ...)"""
ips = machine['ipHostNumber'] ips = machine['ipHostNumber']
if '<automatique>' in machine['macAddress'] :
return
for ip in ips: for ip in ips:
# Si la machines est sur le réseau des adhérents # Si la machines est sur le réseau des adhérents
if utils.AddrInNet(str(ip), config.NETs['wifi']): if utils.AddrInNet(str(ip), config.NETs['wifi']):
@ -182,6 +175,11 @@ class firewall(utils.firewall_tools) :
self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, config.mac_komaz)) self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, config.mac_komaz))
self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, config.mac_titanic)) self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, config.mac_titanic))
# Proxy ARP de Komaz et Titanic pour OVH
ip_soyouz = self.conn.search(u"host=soyouz.adm.crans.org")[0]['ipHostNumber'][0]
self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_soyouz, config.mac_komaz))
self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_soyouz, config.mac_titanic))
self.add(table, chain, '-j REJECT') self.add(table, chain, '-j REJECT')
print OK print OK
@ -210,6 +208,8 @@ class firewall_routeur(firewall):
def test_mac_ip_dispatch(self, func, machine): def test_mac_ip_dispatch(self, func, machine):
"""Détermine à quel set de mac-ip appliquer la fonction func (add, delete, append, ...)""" """Détermine à quel set de mac-ip appliquer la fonction func (add, delete, append, ...)"""
ips = machine['ipHostNumber'] ips = machine['ipHostNumber']
if '<automatique>' in machine['macAddress'] :
return
for ip in ips: for ip in ips:
# Si la machines est sur le réseau des adhérents # Si la machines est sur le réseau des adhérents
if utils.AddrInNet(str(ip), config.NETs['wifi']): if utils.AddrInNet(str(ip), config.NETs['wifi']):
@ -228,6 +228,8 @@ class firewall_wifionly(firewall):
def test_mac_ip_dispatch(self, func, machine): def test_mac_ip_dispatch(self, func, machine):
"""Détermine à quel set de mac-ip appliquer la fonction func (add, delete, append, ...)""" """Détermine à quel set de mac-ip appliquer la fonction func (add, delete, append, ...)"""
ips = machine['ipHostNumber'] ips = machine['ipHostNumber']
if '<automatique>' in machine['macAddress'] :
return
for ip in ips: for ip in ips:
# Si la machines est sur le réseau des adhérents # Si la machines est sur le réseau des adhérents
if utils.AddrInNet(str(ip), config.NETs['wifi']): if utils.AddrInNet(str(ip), config.NETs['wifi']):

View file

@ -298,7 +298,7 @@ class firewall(base.firewall_routeur):
# for ip in ip_list: # for ip in ip_list:
# machine = self.conn.search(u"ipHostNumber=%s" % ip) # machine = self.conn.search(u"ipHostNumber=%s" % ip)
# # Est-ce qu'il y a des blacklists soft parmis les blacklists de la machine # # Est-ce qu'il y a des blacklists soft parmis les blacklists de la machine
# if machine and set([bl.value['type'] for bl in machine[0].blacklist_actif() ]).intersection(base.config.blacklist_sanctions_soft): # if machine and set([bl['type'] for bl in machine[0].blacklist_actif() ]).intersection(base.config.blacklist_sanctions_soft):
# try: self.ipset['blacklist']['soft'].add(ip) # try: self.ipset['blacklist']['soft'].add(ip)
# except IpsetError: pass # except IpsetError: pass
# else: # else:
@ -310,17 +310,9 @@ class firewall(base.firewall_routeur):
chain = 'BLACKLIST_SOFT' chain = 'BLACKLIST_SOFT'
if fill_ipset: if fill_ipset:
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['soft'])
# On récupère la liste de toutes les ips blacklistés soft # On récupère la liste de toutes les ips blacklistés soft
bl_soft_ips = set( bl_soft_ips = self.blacklisted_ips(base.config.blacklist_sanctions_soft, base.config.NETs['all'])
str(ip) for ips in anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['soft'])
[
machine['ipHostNumber'] for machine in self.blacklisted_machines() if machine['ipHostNumber'] and reduce(lambda x,y: x or y, ( ip.value in base.netaddr.IPNetwork(n) for n in base.config.NETs['all'] for ip in machine['ipHostNumber']))
if set([bl.value['type'] for bl in machine.blacklist_actif() ]).intersection(base.config.blacklist_sanctions_soft)
]
for ip in ips
)
self.ipset['blacklist']['soft'].restore(bl_soft_ips) self.ipset['blacklist']['soft'].restore(bl_soft_ips)
print OK print OK
@ -348,7 +340,7 @@ class firewall(base.firewall_routeur):
# for ip in ip_list: # for ip in ip_list:
# machine = self.conn.search(u"ipHostNumber=%s" % ip) # machine = self.conn.search(u"ipHostNumber=%s" % ip)
# # Est-ce qu'il y a des blacklists pour upload parmis les blacklists de la machine # # Est-ce qu'il y a des blacklists pour upload parmis les blacklists de la machine
# if machine and set([bl.value['type'] for bl in machine[0].blacklist_actif() ]).intersection(blacklist_bridage_upload): # if machine and set([bl['type'] for bl in machine[0].blacklist_actif() ]).intersection(blacklist_bridage_upload):
# try: self.ipset['blacklist']['upload'].add(ip) # try: self.ipset['blacklist']['upload'].add(ip)
# except IpsetError: pass # except IpsetError: pass
# else: # else:
@ -360,17 +352,9 @@ class firewall(base.firewall_routeur):
chain = 'BLACKLIST_UPLOAD' chain = 'BLACKLIST_UPLOAD'
if fill_ipset: if fill_ipset:
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['upload'])
# On récupère la liste de toutes les ips blacklistés pour upload # On récupère la liste de toutes les ips blacklistés pour upload
bl_upload_ips = set( bl_upload_ips = self.blacklisted_ips(base.config.blacklist_bridage_upload, base.config.NETs['all'])
str(ip) for ips in anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['upload'])
[
machine['ipHostNumber'] for machine in self.blacklisted_machines()
if set([bl.value['type'] for bl in machine.blacklist_actif() ]).intersection(base.config.blacklist_bridage_upload)
]
for ip in ips
)
self.ipset['blacklist']['upload'].restore(bl_upload_ips) self.ipset['blacklist']['upload'].restore(bl_upload_ips)
print OK print OK
@ -446,13 +430,13 @@ class firewall(base.firewall_routeur):
for machine in self.machines(): for machine in self.machines():
for ip in machine['ipHostNumber']: for ip in machine['ipHostNumber']:
if 'portTCPout' in machine.attrs.keys(): if 'portTCPout' in machine:
add_ports(ip, machine, 'tcp', 'out') add_ports(ip, machine, 'tcp', 'out')
if 'portUDPout' in machine.attrs.keys(): if 'portUDPout' in machine:
add_ports(ip, machine, 'udp', 'out') add_ports(ip, machine, 'udp', 'out')
if 'portTCPin' in machine.attrs.keys(): if 'portTCPin' in machine:
add_ports(ip, machine, 'tcp', 'in') add_ports(ip, machine, 'tcp', 'in')
if 'portUDPin' in machine.attrs.keys(): if 'portUDPin' in machine:
add_ports(ip, machine, 'udp', 'in') add_ports(ip, machine, 'udp', 'in')
self.add(table, chain, '-j REJECT') self.add(table, chain, '-j REJECT')

View file

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import sys import sys
import netaddr
if '/usr/scripts/' not in sys.path: if '/usr/scripts/' not in sys.path:
sys.path.append('/usr/scripts/') sys.path.append('/usr/scripts/')
@ -58,11 +59,21 @@ class firewall_tools(object) :
"""Renvois la liste de toutes les machines""" """Renvois la liste de toutes les machines"""
if self._machines: if self._machines:
return self._machines return self._machines
# On utilise allMachinesAdherents car on a besoin que
# les machine.proprio() soit déjà peuplés. En effet, on regarde
# les blacklistes d'un proprio lorsque l'on regarde les blacklistes
# d'une machine
anim('\tChargement des machines')
self._machines, self._adherents = self.conn.allMachinesAdherents() self._machines, self._adherents = self.conn.allMachinesAdherents()
self._adherents = [ adh for adh in self._adherents if adh.paiement_ok() ]
print OK
return self._machines return self._machines
def adherents(self): def adherents(self):
"""Renvois la liste de tous les adhérents""" """
Renvois la liste de tous les adhérents à jour de paiement
(car on suppose que la blackliste paiement est hard)
"""
if self._adherents: if self._adherents:
return self._adherents return self._adherents
self._machines, self._adherents = self.conn.allMachinesAdherents() self._machines, self._adherents = self.conn.allMachinesAdherents()
@ -76,8 +87,22 @@ class firewall_tools(object) :
self._blacklisted_machines = [ machine for machine in self.machines() if machine.blacklist_actif() ] self._blacklisted_machines = [ machine for machine in self.machines() if machine.blacklist_actif() ]
return self._blacklisted_machines return self._blacklisted_machines
def blacklisted_ips(self, blacklist_sanctions=None, nets=None):
"""Renvois l'ensemble des ips des machines ayant une blacklist dans blacklist_sanctions et étant dans nets si spécifié"""
bl_ips = set()
for machine in self.blacklisted_machines():
if blacklist_sanctions is None or set(bl['type'] for bl in machine.blacklist_actif()).intersection(blacklist_sanctions):
for ip in machine['ipHostNumber']:
if nets is None:
bl_ips.add(str(ip))
else:
for net in nets:
if ip in netaddr.IPNetwork(net):
bl_ips.add(str(ip))
return bl_ips
def blacklisted_adherents(self, excepts=[]): def blacklisted_adherents(self, excepts=[]):
"""Renvois la liste de tous les adhérents ayant une blackliste active""" """Renvois la liste de tous les adhérents ayant une blackliste active en ignorant les blacklist de excepts"""
if self._blacklisted_adherents and self._blacklisted_adherents_type == set(excepts): if self._blacklisted_adherents and self._blacklisted_adherents_type == set(excepts):
return self._blacklisted_adherents return self._blacklisted_adherents
self._blacklisted_adherents = filter(lambda adh: adh.blacklist_actif(excepts), self.adherents()) self._blacklisted_adherents = filter(lambda adh: adh.blacklist_actif(excepts), self.adherents())
@ -218,10 +243,7 @@ class firewall_tools(object) :
def start(self): def start(self):
"""Démarre le pare-feu : génère les règles, puis les restore""" """Démarre le pare-feu : génère les règles, puis les restore"""
anim('\tChargement des machines')
self.machines() self.machines()
self.blacklisted_machines()
print OK
if squeeze: if squeeze:
anim('\tVidage du pare-feu') anim('\tVidage du pare-feu')

View file

@ -102,8 +102,8 @@ class firewall(base.firewall):
self.add(table, chain, '-d 127.0.0.1/8 -j RETURN') self.add(table, chain, '-d 127.0.0.1/8 -j RETURN')
for net in base.config.NETs['all']: for net in base.config.NETs['all']:
self.add(table, chain, '-d %s -j RETURN' % net) self.add(table, chain, '-d %s -j RETURN' % net)
for adh in self.blacklisted_adherents(['paiement']): for adh in self.blacklisted_adherents():
if 'uidNumber' in adh.attrs.keys(): if 'uidNumber' in adh:
self.add(table, chain, '-m owner --uid-owner %s -j REJECT' % adh['uidNumber'][0]) self.add(table, chain, '-m owner --uid-owner %s -j REJECT' % adh['uidNumber'][0])
print OK print OK

View file

@ -15,7 +15,7 @@ arguments peuvent être founis pour une même option, les séparer par &
""" """
import sys, signal, os, getopt import sys, signal, os, getopt
from trigger import trigger_generate as trigger
sys.path.append('/usr/scripts/gestion') sys.path.append('/usr/scripts/gestion')
from ldap_crans import crans_ldap, hostname from ldap_crans import crans_ldap, hostname
@ -28,8 +28,6 @@ from syslog import *
import platform import platform
openlog("generate") openlog("generate")
signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
db = crans_ldap() db = crans_ldap()
make_lock('auto_generate', 'Big lock', nowait=1) make_lock('auto_generate', 'Big lock', nowait=1)
@ -205,15 +203,6 @@ class owl(base_reconfigure):
from adherents import del_user from adherents import del_user
self._do(del_user(args)) self._do(del_user(args))
class pgsql(base_reconfigure):
def surveillance_exemptions(self):
from gen_confs.surveillance import exemptions
self._do(exemptions())
def surveillance_machines(self):
from gen_confs.surveillance import machines
self._do(machines(), self._machines())
class thot(base_reconfigure): class thot(base_reconfigure):
def surveillance_exemptions(self): def surveillance_exemptions(self):
from gen_confs.surveillance import exemptions from gen_confs.surveillance import exemptions
@ -304,10 +293,10 @@ class gordon(base_reconfigure) :
class titanic(base_reconfigure): class titanic(base_reconfigure):
pass pass
signal.signal(signal.SIGINT, signal.SIG_DFL) # Comportement normal de Ctrl-C
remove_lock('auto_generate') remove_lock('auto_generate')
if __name__ == '__main__': if __name__ == '__main__':
signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
openlog('generate', LOG_PID) openlog('generate', LOG_PID)
for x in db.services_to_restart(): for x in db.services_to_restart():
try: try:
@ -362,15 +351,17 @@ if __name__ == '__main__':
print 'Recherche des personnes en fin de sanction...' print 'Recherche des personnes en fin de sanction...'
c = db.search('blacklist=*') c = db.search('blacklist=*')
services = [] services = []
nom = time()
hier = time() - 24*3600 hier = time() - 24*3600
demain = time() + 24*3600
for a_reco in c['adherent'] + c['machine'] + c['club']: for a_reco in c['adherent'] + c['machine'] + c['club']:
for bl in a_reco.blacklist(): for bl in a_reco.blacklist():
fin, sanction = bl.split('$')[1:3] fin, sanction = bl.split('$')[1:3]
if fin > hier and sanction not in services: if fin != '-' and fin <= demain and fin >= now and (sanction, fin) not in services:
services.append(sanction) services.append((sanction, fin))
for s in services: for s,f in services:
print "Ajout de blacklist_%s pour reconfiguration" % s print "Ajout de blacklist_%s pour reconfiguration" % s
db.services_to_restart('blacklist_%s' % s) db.services_to_restart('blacklist_%s' % s, start=f)
sys.exit(0) sys.exit(0)
elif opt == '--add': elif opt == '--add':
@ -397,3 +388,4 @@ if __name__ == '__main__':
# On fait ce qu'il y a à faire # On fait ce qu'il y a à faire
classe(to_do) classe(to_do)
signal.signal(signal.SIGINT, signal.SIG_DFL) # Comportement normal de Ctrl-C

View file

@ -19,11 +19,8 @@ import sys
sys.path.append('/usr/scripts/gestion') sys.path.append('/usr/scripts/gestion')
import commands import commands
import lock
import os import os
class IpsetError(Exception): class IpsetError(Exception):
# Gestion des erreurs d'ipset # Gestion des erreurs d'ipset
def __init__(self,cmd,err_code,output): def __init__(self,cmd,err_code,output):

View file

@ -57,10 +57,11 @@ def ssh_keygen(algo,size):
print("Nouvelle clef %s générée" % key_path) print("Nouvelle clef %s générée" % key_path)
def get_machines(): def get_machines():
machines=[] filter=""
for ip in set(ip4_addresses()): for ip in set(ip4_addresses()):
machines.extend(conn.search(u'ipHostNumber=%s' % ip, mode='rw')) filter+=u'(ipHostNumber=%s)' % ip
return machines filter = u"(|%s)" % filter
return conn.search(filter, mode='rw')
def check_keys_age(key_path,algo): def check_keys_age(key_path,algo):
age=time.time()-os.path.getmtime(key_path) age=time.time()-os.path.getmtime(key_path)
@ -74,22 +75,25 @@ def get_local_keys():
key_path='/etc/ssh/ssh_host_%s_key.pub' % algo key_path='/etc/ssh/ssh_host_%s_key.pub' % algo
if os.path.isfile(key_path): if os.path.isfile(key_path):
check_keys_age(key_path,algo) check_keys_age(key_path,algo)
keys[algo]=open(key_path).read() keys[algo]=unicode(open(key_path).read().strip())
return keys return keys
def check_keys(keys): def check_keys(keys):
return dict([ (algo,key.split()[1] == ssh_keyscan('localhost',algo)) for algo,key in keys.items() ]) return dict([ (algo,key.split()[1] == ssh_keyscan('localhost',algo)) for algo,key in keys.items() ])
def publish_keys(): def valid_keys():
keys=get_local_keys() keys=get_local_keys()
validation=check_keys(keys) validation=check_keys(keys)
machines=get_machines() return [key for algo,key in keys.items() if validation[algo]]
for machine in machines:
sshkeys_old=[key.value for key in machine.get('sshFingerprint',[])] def publish_keys():
sshkeys_new=[key.decode('UTF-8') for algo,key in keys.items() if validation[algo]] keys=valid_keys()
if not set(sshkeys_old)==set(sshkeys_new): for machine in get_machines():
machine['sshFingerprint']=sshkeys_new with machine:
machine.save() if machine['sshFingerprint'] != keys:
print "Key list changed"
machine['sshFingerprint'] = keys
machine.save()
if __name__ == '__main__' : if __name__ == '__main__' :

View file

@ -49,6 +49,9 @@ class exemptions(gen_config) :
source = machine.ip() source = machine.ip()
else: else:
source = machine.ipv6() source = machine.ipv6()
# Si ip vide, passons au suivant
if not source:
continue
requete="INSERT INTO exemptes (ip_crans,ip_dest) VALUES ('%s','%s')" % (source, destination) requete="INSERT INTO exemptes (ip_crans,ip_dest) VALUES ('%s','%s')" % (source, destination)
curseur.execute(requete) curseur.execute(requete)
@ -83,6 +86,10 @@ class machines(gen_config) :
ipv6_vu={} ipv6_vu={}
def ipv6_already_set(ipv6): def ipv6_already_set(ipv6):
# S'il ne s'agit pas d'une IP valide (vide ?) faisons comme si
# on l'avait déjà vue :p
if not ipv6:
return True
ret = ipv6_vu.get(ipv6, False) ret = ipv6_vu.get(ipv6, False)
ipv6_vu[ipv6] = True ipv6_vu[ipv6] = True
return ret return ret

37
gestion/gen_confs/trigger.py Executable file
View file

@ -0,0 +1,37 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import subprocess
_options = ['PasswordAuthentication=no', 'ConnectTimeout=1', 'VerifyHostKeyDNS=yes',
'BatchMode=yes', 'ServerAliveInterval=5', 'ServerAliveCountMax=1']
_args = ["ssh", "-4", "-i", "/etc/crans/secrets/trigger-generate" ]
def build_args(host):
if not 'adm.crans.org' in host:
host=host + '.adm.crans.org'
args = list(_args)
for opt in _options:
args.append('-o')
args.append(opt)
args.extend(["rpcssh@%s" % host, "generate"])
return args
def trigger_generate(host, background=False):
args = build_args(host)
if background:
subprocess.Popen(args)
else:
p=subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
if err:
raise Exception(err)
return out
def trigger_generate_cochon(host):
"""Ceci est une fonction crade qui permet de se débarraser du process enfant
que l'on aurait laissé en arrière plan"""
args = build_args(host)
p = subprocess.Popen(['/bin/bash'],
stdin=subprocess.PIPE, )
p.communicate(' '.join(args) + ' &> /dev/null &')

View file

@ -933,7 +933,7 @@ def __prompt_input_menu(method, titre, prompt):
else: else:
method([num-1, val]) method([num-1, val])
except ValueError, c: except (EnvironmentError, ValueError) as c:
arg = u'--title "%s" ' % titre arg = u'--title "%s" ' % titre
arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0]) arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
dialog(arg) dialog(arg)
@ -1135,7 +1135,7 @@ def confirm(clas):
return 1 return 1
try: try:
res = clas.save() res = clas.save()
except RuntimeError, c: except (EnvironmentError, RuntimeError) as c:
arg = u'--title "Enregistrement" ' arg = u'--title "Enregistrement" '
arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0]) arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
dialog(arg) dialog(arg)

305
gestion/gest_crans_lc.py Executable file
View file

@ -0,0 +1,305 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
u"""
Interface utilisateur du système de gestion des machines
et adhérents du crans
Copyright (C) Valentin Samir
Licence : GPLv3
"""
import os
import sys
import signal
from pythondialog import Dialog
from gestion.affich_tools import get_screen_size, coul
import lc_ldap.shortcuts
import lc_ldap.printing as printing
def handle_exit_code(d, code):
if code in (d.DIALOG_CANCEL, d.DIALOG_ESC):
if code == d.DIALOG_CANCEL:
msg = "Vous avez choisi Annuler dans la dernière fenêtre de dialogue.\n\n" \
"Voulez vous quitter le programme ?"
os.system('clear')
sys.exit(0)
else:
msg = "Vous avez appuyer sur ESC dans la dernière fenêtre de dialogue.\n\n" \
"Voulez vous quitter le programme ?"
if d.yesno(msg, width=60) == d.DIALOG_OK:
os.system('clear')
sys.exit(0)
return False
else:
return True # code est d.DIALOG_OK
def search(dialog, objectClassS, title, values={}):
"""
Rechercher des adhérents ou des machines dans la base ldap
retourne le tuple (code de retour dialog, valeurs entrée par l'utilisateur, liste d'objets trouvés)
La fonction est découpé en trois partie :
* affichage dialog et récupération du résultat
* construction de filtres de recherche ldap et recherches ldap
* filtre sur les résultats des recherches ldap
"""
select_dict = {
#label attribut ldap search for substring param dialog: line col valeur input-line icol len max-chars
'Nom' : {'ldap':'nom', 'sub':True, 'params' : [ 1, 1, values.get('nom', ""), 1, 13, 20, 20]},
'Prenom' : {'ldap':'prenom', 'sub':True, 'params' : [ 2, 1, values.get('prenom', ""), 2, 13, 20, 20]},
'Téléphone' : {'ldap':'tel', 'sub':True, 'params' : [ 3, 1, values.get('tel', ""), 3, 13, 10, 00]},
'Chambre' : {'ldap':'chbre','sub':True, 'params' : [ 4, 1, values.get('chbre',""), 4, 13, 05, 00]},
'aid' : {'ldap' : 'aid', 'sub':False, 'params' : [ 5, 1, values.get('aid',""), 5, 13, 05, 05]},
'mail' : {'ldap' : 'mail', 'sub':True, 'params' : [ 6, 1, values.get('mail',""), 6, 13, 20, 00]},
# seconde colone
'Machine' : {'ldap' : '*', 'sub':True, 'params' : [1, 35, "", 1, 43, 0, 0]},
'Host' : {'ldap' : 'host', 'sub':True, 'params' : [2, 37, values.get('host',""), 2, 43, 17, 17]},
'Mac' : {'ldap' : 'macAddress', 'sub':False, 'params' : [3, 37, values.get('macAddress',""), 3, 43, 17, 17]},
'IP' : {'ldap' : 'ipHostNumber', 'sub':False,'params' : [4, 37, values.get('ipHostNumber',""), 4, 43, 15, 15]},
'mid' : {'ldap' : 'mid', 'sub':False, 'params' : [5, 37, values.get('mid',""), 5, 43, 5, 5]},
}
# On a besoin de l'ordre pour récupérer les valeurs ensuite
select_adherent = ['Nom', 'Prenom', 'Téléphone', 'Chambre', 'aid', 'mail']
select_machine = ['Host', 'Mac', 'IP', 'mid']
def box():
# On met les argument à dialog à la main ici, sinon, c'est difficile de choisir comment mettre une seconde colone
cmd = ["--form", "Entrez vos paramètres de recherche", '0', '0', '0']
for key in select_adherent:
cmd.extend(['%s :' % key] + [str(e) for e in select_dict[key]['params']])
cmd.extend(['Machine :'] + [str(e) for e in select_dict['Machine']['params']])
for key in select_machine:
cmd.extend(['%s :' % key] + [str(e) for e in select_dict[key]['params']])
cmd.extend(["Les champs vides sont ignorés.", '7', '1', "", '0', '0', '0', '0'])
# On utilise quand même la fonction de la bibliothèques pour passer les arguments
(code, output) = dialog._perform(*(cmd,), title=title, backtitle="Entrez vos paramètres de recherche")
if output:
return (code, output.split('\n')[:-1])
else: # empty selection
return (code, [])
(code, dialog_values) = box()
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
return code, values, []
else:
# Transformation de la liste des valeures entrée en dictionnnaire
dialog_values = dict(zip(select_adherent + select_machine, dialog_values))
ldap_values = dict([(select_dict[key]['ldap'], value) for key, value in dialog_values.items()])
# Construction des filtres ldap pour les adhérents et les machines
filter_adherent = []
filter_machine = []
for (key, value) in dialog_values.items():
if value:
if key in select_adherent:
filter_adherent.append((u"(%s=*%s*)" if select_dict[key]['sub'] else u"(%s=%s)") % (select_dict[key]['ldap'], unicode(value, 'utf-8')))
elif key in select_machine:
filter_machine.append((u"(%s=*%s*)" if select_dict[key]['sub'] else u"(%s=%s)") % (select_dict[key]['ldap'], unicode(value, 'utf-8')))
if filter_adherent:
filter_adherent=u"(&%s)" % "".join(filter_adherent)
if filter_machine:
filter_machine=u"(&%s)" % "".join(filter_machine)
# Récupération des adhérents et des machines
adherents=conn.search(filter_adherent) if filter_adherent else []
machines=conn.search(filter_machine) if filter_machine else []
# Filtrage des machines en fonction des adhérents
if filter_adherent:
if filter_machine:
# Si on filtre sur des adhérent et des machines, on calcule l'intersection
adherents_dn = set([a.dn for a in adherents])
machines_f = [m for m in machines if m.parent_dn in adherents_dn]
else:
# Sinon on filtre seulement sur les adhérents, récupère les machines des adhérents trouvés
machines_f = [m for a in adherents for m in a.machines()]
else:
# Sinon si on filtre seulement sur des machines
machines_f = machines
# Filtrage des adhérents en fonction des machines
if filter_machine:
if filter_adherent:
# Si on filtre sur des adhérents et des machines, on calcule l'intersection
machines_dn = set([m.parent_dn for m in machines])
adherents_f = [a for a in adherents if a.dn in machines_dn]
else:
# Sinon on récupères les proprios des machines trouvées
adherents_f = [m.proprio() for m in machines]
else:
# Sinon si on filtre seulement sur des adhérents
adherents_f = adherents
# On filtre sur les objectClassS
return code, ldap_values, [ o for objectClass in objectClassS for o in machines_f+adherents_f if objectClass in o['objectClass'] ]
def select_one(dialog, items, default_item=None):
"""Fait selectionner un item parmis une liste d'items à l'utisateur"""
### TODO afficher correctement les objets dans items
choices=[]
count = 0
default_tag = items.index(default_item) if default_item in items else default_item
for i in items:
choices.append((str(count), str(i), 1 if default_tag == count else 0))
count+=1
tag=''
while not tag:
(line, col) = get_screen_size()
(code, tag) = dialog.radiolist(
"Choisir",
choices=choices,
default_item=str(default_tag),
width=col-4,
height=line-3,
list_height=line-3,
scrollbar=True)
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
return code, None
if not tag:
dialog.msgbox("Merci de choisir l'un des item de la liste ou d'annuler", title="Sélection", width=0, height=0)
return code, items[int(tag)]
def select_confirm(dialog, item, title):
"""Affiche un item et demande si c'est bien celui là que l'on veux (supprimer, éditer, créer,...)"""
return dialog.yesno(printing.sprint(item), no_collapse=True, colors=True, title=title, width=0, height=0, backtitle="Appuyez sur MAJ pour selectionner du texte") == dialog.DIALOG_OK
def select(dialog, objectClassS, title, values={}):
"""Permet de choisir un objet adhérent ou machine dans la base ldap"""
while True:
try:
# On fait effectuer une recherche à l'utilisateur
code, values, items = search(dialog, objectClassS, title, values)
# Si il a appuyé sur annuler ou sur escape, on annule
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
return code, None
# S'il n'y a pas de résultas, on recommence
elif not items:
dialog.msgbox("Aucun Résultat", title="Recherche", width=0, height=0)
# S'il y a plusieurs résultats
elif len(items)>1:
item = None
while True:
# On en fait choisir un
code, item = select_one(dialog, items, item)
# Si il à appuyer sur annuler ou sur escape, on recommence la recherche
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
break
# Sinon, on fait confirmer son choix à l'utilisateur
elif select_confirm(dialog, item, title):
return code, item
# S'il y a exactement 1 résultat à la recherche, on fait confirmer son choix à l'utilisateur
elif len(items) == 1 and select_confirm(dialog, items[0], title):
return code, items[0]
except Exception as e:
dialog.msgbox("%r" % e, title="Erreur rencontrée", width=0, height=0)
def modif_adherent(dialog):
code, adherent = select(dialog, ["adherent"], "Recherche d'un adhérent")
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
return code
dialog.msgbox("todo", width=0, height=0)
def create_adherent(dialog):
dialog.msgbox("todo", width=0, height=0)
def delete_adherent(dialog):
code, adherent = select(dialog, ["adherent"], "Recherche d'un adhérent")
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
return code
dialog.msgbox("todo", width=0, height=0)
def modif_machine(dialog):
code, machine = select(dialog, ["machineFixe", "machineWifi", "machineCrans", "borneWifi"], "Recherche d'une machine")
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
return code
dialog.msgbox("todo", width=0, height=0)
def create_machine_adherent(dialog):
dialog.msgbox("todo", width=0, height=0)
def delete_machine(dialog):
code, machine = select(dialog, ["machineFixe", "machineWifi", "machineCrans", "borneWifi"], "Recherche d'une machine")
if code in (dialog.DIALOG_CANCEL, dialog.DIALOG_ESC):
return code
dialog.msgbox("todo", width=0, height=0)
def create_club(dialog):
dialog.msgbox("todo", width=0, height=0)
def delete_club(dialog):
dialog.msgbox("todo", width=0, height=0)
def modif_club(dialog):
dialog.msgbox("todo", width=0, height=0)
def create_machine_club(dialog):
dialog.msgbox("todo", width=0, height=0)
def create_machine_crans(dialog):
dialog.msgbox("todo", width=0, height=0)
def create_borne(dialog):
dialog.msgbox("todo", width=0, height=0)
def menu_principal(dialog):
menu = {
'aA' : {'text':"Inscrire un nouvel adhérent", 'callback': create_adherent},
'mA' : {'text':"Modifier l'inscription d'un adhérent", 'callback': modif_adherent, 'help':"Changer la chambre, la remarque, la section, la carte d'étudiant ou précâbler."},
'aMA': {'text':"Ajouter une machine à un adhérent", 'callback': create_machine_adherent},
'dA' : {'text':"Détruire un adhérent", 'callback': delete_adherent, 'help':"Suppression de l'adhérent ainsi que de ses machines"},
'mM' : {'text':"Modifier une machine existante", 'callback': modif_machine, 'help':"Changer le nom ou la MAC d'une machine."},
'dM' : {'text':"Détruire une machine", 'callback': delete_machine},
'aC' : {'text':"Inscrire un nouveau club", 'callback': create_club},
'mC' : {'text':"Modifier un club", 'callback': modif_club},
'aMC': {'text':"Ajouter une machine à un club", 'callback': create_machine_club},
'dC' : {'text':"Détruire un club", 'callback': delete_club},
'aKM': {'text':"Ajouter une machine à l'association", 'callback': create_machine_crans},
'aKB': {'text':"Ajouter une borne wifi", 'callback': create_borne},
'' : {'text':"---------------------------------------",'callback': lambda x: x},
}
### Les clef qui n'existe pas sont toute renvoyé sur la clef ''
medu_order = ["aA", "mA", "aMA", "dA", "", "mM", "dM", " ", "aC", "mC", "aMC", "dC", " ", "aKM", "aKB"]
def box(default_item=None):
return dialog.menu(
"Que souhaitez vous faire ?",
width=55,
height=0,
menu_height=15,
item_help=1,
default_item=str(default_item),
title="Menu principal",
scrollbar=True,
cancel_label="Quitter",
backtitle=u"Vous êtes connecté en tant que %s" % conn.current_login,
choices=[(key, menu.get(key, menu[''])['text'], menu.get(key, menu['']).get('help', "")) for key in medu_order])
tag=None
while True:
(code, tag) = box(tag)
if handle_exit_code(dialog, code):
menu.get(tag, menu[''])['callback'](dialog)
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal.SIG_IGN)
# On initialise le moteur de rendu en spécifiant qu'on va faire du dialog
printing.template(dialog=True)
# On ouvre une connexion lc_ldap
conn = lc_ldap.shortcuts.lc_ldap_admin()
# On vérifie que l'utilisateur système existe dans ldap (pour la gestion des droits)
luser=conn.search(u"uid=%s" % conn.current_login)
if not luser:
sys.stderr.write("L'utilisateur %s n'existe pas dans la base de donnée" % conn.current_login)
sys.exit(1)
conn.droits = [str(d) for d in luser[0]['droits']]
conn.dn = luser[0].dn
dialog = Dialog()
menu_principal(dialog)
os.system('clear')

View file

@ -24,7 +24,11 @@ import netaddr
def mac_to_ipv6(ipv6_prefix, mac_address): def mac_to_ipv6(ipv6_prefix, mac_address):
"""Convert a MAC address (EUI48) to an IPv6 (prefix::EUI64).""" """Convert a MAC address (EUI48) to an IPv6 (prefix::EUI64)."""
if mac_address == '<automatique>':
return ''
if type(mac_address) in [str, unicode]:
mac_address = netaddr.EUI(mac_address)
addr = int(mac_address.bits(netaddr.mac_bare), 2) addr = int(mac_address.bits(netaddr.mac_bare), 2)
ip6addr = (((addr >> 24) ^ (1 << 17)) << 40) | (0xFFFE << 24) | (addr & 0xFFFFFF) ip6addr = (((addr >> 24) ^ (1 << 17)) << 40) | (0xFFFE << 24) | (addr & 0xFFFFFF)
n = netaddr.IPNetwork(ipv6_prefix) n = netaddr.IPNetwork(ipv6_prefix)

View file

@ -30,6 +30,7 @@ from iptools import AddrInNet
from ridtools import Rid, find_rid_plage from ridtools import Rid, find_rid_plage
import subprocess import subprocess
import netaddr import netaddr
import ip6tools
blacklist_sanctions_ipv6 = list(blacklist_sanctions) blacklist_sanctions_ipv6 = list(blacklist_sanctions)
blacklist_sanctions_ipv6.extend(blacklist_sanctions_soft) blacklist_sanctions_ipv6.extend(blacklist_sanctions_soft)
@ -126,6 +127,8 @@ class Ip6tables(object):
def macip(self, mac, type_m): def macip(self, mac, type_m):
'''Fait la correspondance MAC-IP''' '''Fait la correspondance MAC-IP'''
if '<automatique>' == mac:
return
tab = {'serveurs' : 'fil' } tab = {'serveurs' : 'fil' }
if type_m in tab.keys(): type_m = tab[type_m] if type_m in tab.keys(): type_m = tab[type_m]
type_mm = re.sub('-', '', type_m) type_mm = re.sub('-', '', type_m)
@ -141,6 +144,8 @@ class Ip6tables(object):
'wifi-adh-v6' : 'extwifiv6', 'wifi-adh-v6' : 'extwifiv6',
'serveurs':'extfil' } 'serveurs':'extfil' }
ip = ipv6_addr(mac, type_machine) ip = ipv6_addr(mac, type_machine)
if not ip:
return
for proto in ['tcp', 'udp']: for proto in ['tcp', 'udp']:
for port in ports[proto]: for port in ports[proto]:
if port != ':': if port != ':':
@ -157,6 +162,8 @@ ACCEPT' % (dev, proto, ip, port))
'wifi-adh-v6' : 'cranswifiv6', 'wifi-adh-v6' : 'cranswifiv6',
'serveurs':'cransfil' } 'serveurs':'cransfil' }
ip = ipv6_addr(mac, type_machine) ip = ipv6_addr(mac, type_machine)
if not ip:
return
for proto in ['tcp', 'udp']: for proto in ['tcp', 'udp']:
for port in ports[proto]: for port in ports[proto]:
if port != ':': if port != ':':
@ -470,9 +477,7 @@ def check_ip_proto(ip_proto):
def ipv6_addr(mac, net): def ipv6_addr(mac, net):
''' Renvoie l'adresse ipv6 d'auto-configuration de la mac sur le réseau ''' ''' Renvoie l'adresse ipv6 d'auto-configuration de la mac sur le réseau '''
mac_s = mac.split(':') return ip6tools.mac_to_ipv6(prefix[dprefix[net]][0], mac)
eui = hex(int(mac_s[0],16) ^ 0x02)[2:] + ':'.join(mac_s[1:3]) + 'ff:fe' + ':'.join(mac_s[3:5]) + mac_s[5]
return re.sub(':/64', eui , prefix[dprefix[net]][0])
def mac_addr(ipv6): def mac_addr(ipv6):
''' Renvoie l'adresse mac de l'ipv6 d'auto-configuration ''' ''' Renvoie l'adresse mac de l'ipv6 d'auto-configuration '''

View file

@ -179,4 +179,4 @@ class Nols(object):
def rename_volume(self, origin_name, new_name): def rename_volume(self, origin_name, new_name):
'''Renomme un volume.''' '''Renomme un volume.'''
self.cmd("set volume name %s %s" % (new_name, orig_name)) self.cmd("set volume name %s %s" % (new_name, origin_name))

View file

@ -223,6 +223,9 @@ def format_mac(mac):
Le séparateur original peut être :, - ou rien Le séparateur original peut être :, - ou rien
Retourne la mac formatée. Retourne la mac formatée.
""" """
mac = mac.strip()
if mac == '<automatique>':
return mac
l, mac = preattr(mac) l, mac = preattr(mac)
mac = mac.strip().replace(' ','').replace("-", ":") mac = mac.strip().replace(' ','').replace("-", ":")
if mac.count(":") == 5: if mac.count(":") == 5:
@ -2863,6 +2866,10 @@ class Machine(BaseClasseCrans):
mac = format_mac(mac) mac = format_mac(mac)
if mac == '<automatique>':
multi_ok = True
lock = False
# La MAC serait-elle une MAC à la con ? # La MAC serait-elle une MAC à la con ?
if mac == "00:04:4b:80:80:03": if mac == "00:04:4b:80:80:03":
raise ValueError(u"Il s'agit de l'unique adresse MAC achetée par nVidia pour ses cartes réseau. Il faut changer cette adresse.", 2) raise ValueError(u"Il s'agit de l'unique adresse MAC achetée par nVidia pour ses cartes réseau. Il faut changer cette adresse.", 2)
@ -2898,7 +2905,7 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
try: try:
self._set('macAddress', [mac]) self._set('macAddress', [mac])
if net.size > 1: if net.size > 1:
self.ipv6(ip6tools.mac_to_ipv6(net, netaddr.EUI(mac)), lock) self.ipv6(ip6tools.mac_to_ipv6(net, mac), lock)
else: else:
self.ipv6(config.ipv6_machines_speciales[int(self.rid())], lock) self.ipv6(config.ipv6_machines_speciales[int(self.rid())], lock)
except Exception as e: except Exception as e:
@ -3001,6 +3008,9 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
if type(new) == list: if type(new) == list:
# Modif # Modif
res = self.conn.search_s(self.dn, 1, "xid=*")
if res:
raise EnvironmentError("La machine possède des certificats, utilisez lc_ldap ou l'intranet2")
index = new[0] index = new[0]
new = new[1] new = new[1]
if new == '': if new == '':
@ -3214,6 +3224,12 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
ret = '' ret = ''
# test si la machine a des certificats
if 'host' in self.modifs:
res = self.conn.search_s(self.dn, 1, "xid=*")
if res:
raise EnvironmentError("La machine possède des certificats, utilisez lc_ldap ou l'intranet2")
# Besoin de redémarrer les firewalls ? # Besoin de redémarrer les firewalls ?
if 'ipHostNumber' in self.modifs or 'macAddress' in self.modifs: if 'ipHostNumber' in self.modifs or 'macAddress' in self.modifs:
reconf_ip = self._init_data.get('ipHostNumber', []) reconf_ip = self._init_data.get('ipHostNumber', [])
@ -3294,6 +3310,9 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
if self.proprietaire().__class__ == AssociationCrans and not isadm(): if self.proprietaire().__class__ == AssociationCrans and not isadm():
raise EnvironmentError(u'Il faut être administrateur pour effectuer cette opération.') raise EnvironmentError(u'Il faut être administrateur pour effectuer cette opération.')
res = self.conn.search_s(self.dn, 1, "xid=*")
if res:
raise EnvironmentError("La machine possède des certificats, utilisez lc_ldap ou l'intranet2")
self.proprio = self.__proprietaire.Nom() # On met dans un coin le nom du proprio self.proprio = self.__proprietaire.Nom() # On met dans un coin le nom du proprio
self.__proprietaire = None # On oublie le propriétaire self.__proprietaire = None # On oublie le propriétaire
@ -3390,7 +3409,14 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
"""Retourne l'adresse IPv6 correspondant à la machine""" """Retourne l'adresse IPv6 correspondant à la machine"""
if ipv6 == None: if ipv6 == None:
return netaddr.IPAddress(self._data.get('ip6HostNumber', [''])[0]) if self._data.get('ip6HostNumber', []) == []:
return None
else:
return netaddr.IPAddress(self._data.get('ip6HostNumber')[0])
if ipv6 == '':
self._set('ip6HostNumber', [])
return
ipv6 = str(ipv6) ipv6 = str(ipv6)
net = self.netv6() net = self.netv6()

52
gestion/mail/convocation_ago.py Executable file
View file

@ -0,0 +1,52 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
import sys
import smtplib
from gestion import config
from gestion.affich_tools import cprint
from gestion import mail
import lc_ldap.shortcuts
from utils.sendmail import actually_sendmail
# Attention, si à True envoie effectivement les mails
SEND=('--do-it' in sys.argv)
# Prévisualisation
PREV=('--prev' in sys.argv)
ldap_filter=u"(&(paiement=%(annee)s)(aid=*))" % {'annee': config.ann_scol}
#ldap_filter=u"(|(uid=dstan)(uid=lasseri))"
conn=lc_ldap.shortcuts.lc_ldap_readonly()
mailaddrs=set()
for adh in conn.search(ldap_filter, sizelimit=2000):
if 'canonicalAlias' in adh.attrs.keys():
mailaddrs.add(str(adh['canonicalAlias'][0]))
elif 'mail' in adh.attrs.keys():
mailaddrs.add(str(adh['mail'][0]))
elif 'uid' in adh.attrs.keys():
mailaddrs.add(str(adh['uid'][0]) + '@crans.org')
else:
raise ValueError("%r has nor mail nor canonicalAlias, only %s" % (adh, adh.attrs.keys()))
echecs=[]
From = 'ca@crans.org'
for To in mailaddrs:
cprint(u"Envoi du mail à %s" % To)
mailtxt=mail.generate('ago', {'To':To, 'From': From})
if PREV:
print mailtxt.as_string()
try:
if SEND:
actually_sendmail('ca@crans.org', (To,), mailtxt)
cprint(" Envoyé !")
else:
cprint(" (simulé)")
except:
cprint(u"Erreur lors de l'envoi à %s " % To, "rouge")
echecs.append(To)
if echecs:
print "\nIl y a eu des erreurs :"
print echecs

View file

@ -32,7 +32,7 @@ markup = {
### For an example: ### For an example:
### print generate('bienvenue', {'From':'respbats@crans.org', 'To':'admin@genua.fr', 'lang_info':'English version below'}).as_string() ### print generate('bienvenue', {'From':'respbats@crans.org', 'To':'admin@genua.fr', 'lang_info':'English version below'}).as_string()
### or from a shell : python -c "import mail; print mail.generate('bienvenue', {'From':'respbats@crans.org', 'To':'admin@genua.fr', 'lang_info':'English version below'})"
def submessage(playload, type, charset='utf-8'): def submessage(playload, type, charset='utf-8'):
"""Renvois un sous message à mettre dans un message multipart""" """Renvois un sous message à mettre dans un message multipart"""

View file

@ -0,0 +1 @@
Le CA du Crans <{{From}}>

View file

@ -0,0 +1 @@
[Crans] Convocation à l'Assemblée Générale Ordinaire de l'association

View file

@ -0,0 +1 @@
{{To}}

View file

@ -0,0 +1,37 @@
Dear members,
The Ordinary General Meeting of the Cr@ns will take place Thursday, March 6th, 2014 at 7:15pm in Pavillon des Jardins.
The present email serve as formal notice to attend.
The main goal of this meeting is the election of a new Board of Directors.
= Agenda =
== New statutes and Internal regulation ==
Both statutes and internal regulation have been modified. First drafts will soon be available on the wiki.
Final versions will be available Thursday, February 27th after the last meeting of the current members from the Board of Directors. New modifications will be submitted to the vote of all members present at the General Meeting.
== Activity and financial report ==
President Ariane Soret will present the activity report.
Treasurer Vincent Guiraud will present the financial report.
Both reports will be submitted to the vote of all present members.
== Technical report ==
Technical Manager Valentin Samir will present the technical report.
== Elections ==
As anounced by the President, the application phase [1] lasts until Thursday, February 27th. The elections shall be held during the Ordinary General Meeting. Execution modalities will be provided later.
== Counting of votes ==
== Announcement of new Board of Directors ==
During the Ordinary General Meeting, internet connection may be altered.
After the Ordinary General Meeting, the new Board of Directors will eventually have a meeting.
Good evening,
--
Raphaël-David Lasseri
Secrétaire du Crans
Secretary of Crans Association

View file

@ -0,0 +1,45 @@
Chers adhérents,
Le Jeudi 6 Mars 2014, à partir de 19h15 au Pavillon Des Jardins aura lieu
l'Assemblée Générale Ordinaire du Crans. Le présent mail tient lieu de
convocation. L'objet principal de cette assemblée sera l'élection du nouveau
Conseil d'Administration.
= Ordre du jour =
== Nouveaux Status et Règlement Intérieur ==
Les Statuts et le Règlement Intérieur ont été modifiés.
Leurs brouillons sont déja disponible sur la mailing list CA
et seront mis en ligne très prochainement sur le wiki.
Leurs versions finales seront disponible le jeudi 27 février à l'issue
du dernier Conseil d'Administration du bureau actuel.
Ces modifications seront soumises au vote des adhérents présents.
== Bilans moral et financier ==
Le président Ariane Soret présentera le bilan moral et le trésorier sortant
Vincent Guiraud présentera le bilan financier. Ces bilans seront soumis au vote
des adhérents présents.
== Bilan technique ==
Le responsable technique en chef Valentin Samir présentera le bilan technique.
== Elections ==
Comme précédemment annoncé par notre président. La phase de candidature [1] est
ouverte jusquau jeudi 27 février. Les élections se dérouleront le jour de
l'AGO; les modalités électorales seront communiqués ultérieurement.
=== Dépouillement des votes ===
=== Annonce du nouveau bureau ===
Durant l'Assemblée Générale Ordinaire la connexion à Internet pourra être altérée.
À l'issue de l'Assemblée Générale Ordinaire le nouveau Conseil d'Administration
pourra éventuellement tenir une réunion.
Bonne soirée à tous !
[1]: https://wiki.crans.org/CransAdministratif/R%C3%A8glementInt%C3%A9rieur

View file

@ -11,6 +11,10 @@ Attention, ce fichier est osbolète
""" """
import cPickle, sys import cPickle, sys
if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts')
from cranslib.deprecated import module as dep_module
dep_module('ressucite or ressucite_lc')
import config import config
from whos import aff from whos import aff

View file

@ -31,7 +31,7 @@ import getpass
def get(secret): def get(secret):
""" Recupere un secret. """ """ Recupere un secret. """
openlog('secrets_new') openlog('secrets_new')
prog = os.path.basename(sys.argv[0]) prog = os.path.basename(getattr(sys, 'argv', ['undefined'])[0])
syslog('%s (in %s) asked for %s' % (getpass.getuser(), prog, secret)) syslog('%s (in %s) asked for %s' % (getpass.getuser(), prog, secret))
try: try:
f = open("/etc/crans/secrets/" + secret) f = open("/etc/crans/secrets/" + secret)

View file

@ -11,6 +11,9 @@ import services
if not 'crans-nfs' in services.services or not socket.gethostname() in services.services['crans-nfs']: if not 'crans-nfs' in services.services or not socket.gethostname() in services.services['crans-nfs']:
sys.stderr.write("Devrait être exécuté sur une machine du groupe 'crans-nfs'\n") sys.stderr.write("Devrait être exécuté sur une machine du groupe 'crans-nfs'\n")
exit(1) exit(1)
if len(sys.argv) < 1:
sys.stderr.write("Usage : %s [user1] [user2] [user3] ...\n" % sys.argv[0])
exit(1)
import lc_ldap.shortcuts import lc_ldap.shortcuts
import gestion.gen_confs.adherents import gestion.gen_confs.adherents
@ -23,9 +26,6 @@ for user in sys.argv[1:]:
if a and a[0]['homeDirectory'] and a[0]['uidNumber'] and a[0]['uid']: if a and a[0]['homeDirectory'] and a[0]['uidNumber'] and a[0]['uid']:
l.append("%s,%s,%s" % (a[0]['homeDirectory'][0], a[0]['uidNumber'][0], a[0]['uid'][0])) l.append("%s,%s,%s" % (a[0]['homeDirectory'][0], a[0]['uidNumber'][0], a[0]['uid'][0]))
else:
sys.stderr.write("Usage : %s [user1] [user2] [user3] ...\n" % sys.argv[0])
if l: if l:
h=gestion.gen_confs.adherents.home(l) h=gestion.gen_confs.adherents.home(l)
h.reconfigure() h.reconfigure()

View file

@ -0,0 +1,8 @@
#!/bin/bash
# Sur certains virtualiseurs xen, les interfaces xen n'étaient pas dans udev.
# Voici un petit script qui écrit les règles correspondantes
ip -o l show | sed 's/^[0-9]*: \(eth[^:]*\).*ether \([^ ]*\) .*$/SUBSYSTEM=="net", DRIVERS=="?*", ATTR{address}=="\2", NAME="\1"/; t; d'

46
gestion/virtualisation/vmid Executable file
View file

@ -0,0 +1,46 @@
#!/bin/bash
VM_PATH=/etc/pve/qemu-server
PVE_PATH=/etc/pve
LOCAL_VM_PATH=/etc/pve/local/qemu-server
SERIAL_PATH=/var/run/qemu-server
if [[ ! -d /etc/pve ]]; then
echo "Not a proxmox server !"
exit 1
fi
if [[ -z "$1" ]]; then
echo "Please give vmid or pve name"
exit 5
fi
if [[ "`whoami`" != "root" ]]; then
echo "You must probably be root"
exit 42
fi
if [[ $1 != *[!0-9]* ]]; then
vmid=$1
else
echo "Looking for vmid of $1 ..."
for host in `ls $PVE_PATH/nodes`; do
p=$PVE_PATH/nodes/$host/qemu-server
for f in `ls $p`; do
grep "name: *$1" $p/$f -q && {
vmid=`echo $f | grep -o "[0-9]*"`
node=$host
echo "Found vmid $vmid"
break
}
done
done
if [[ -z "$node" ]]; then
echo "vmid not found"
exit 2
fi
if [[ "$node" != "`hostname`" ]]; then
echo "Wrong node (go to $node)"
exit 3
fi
fi

View file

@ -670,7 +670,8 @@ def machine_details(machine) :
f+= coul(u'IP : ','gras') + "%s\t\t" %machine.ip() f+= coul(u'IP : ','gras') + "%s\t\t" %machine.ip()
f+= coul(u'MAC : ','gras') + "%s\n" %machine.mac() f+= coul(u'MAC : ','gras') + "%s\n" %machine.mac()
f+= coul(u'IPv6 : ','gras') + "%s\n" %machine.ipv6() if machine.ipv6() != None:
f+= coul(u'IPv6 : ','gras') + "%s\n" %machine.ipv6()
if len(machine.sshFingerprint()) > 0 and aff_ssh: if len(machine.sshFingerprint()) > 0 and aff_ssh:
f += u"\n".join([coul(u'Fingerprint SSH : ', 'gras') + u"%s" % (i) for i in machine.sshFingerprint()])+"\n" f += u"\n".join([coul(u'Fingerprint SSH : ', 'gras') + u"%s" % (i) for i in machine.sshFingerprint()])+"\n"

View file

@ -9,7 +9,7 @@ import lc_ldap.filter2 as filter
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) >1: if len(sys.argv) >1:
conn=lc_ldap.shortcuts.lc_ldap_admin() conn=lc_ldap.shortcuts.lc_ldap_admin()
result=conn.search(filter.human_to_ldap(sys.argv[1]), sizelimit=4000) result=conn.search(filter.human_to_ldap(sys.argv[1].decode('utf-8')), sizelimit=4000)
if not result: if not result:
print "rien trouvé" print "rien trouvé"
else: else:

View file

@ -33,9 +33,10 @@ import commands
import string import string
import random import random
import requests import requests
sys.path.append("/usr/scripts/") if not '/usr/scripts' in sys.path:
sys.path.append("/usr/scripts")
import cranslib.utils.files import cranslib.utils.files
import secrets_new import gestion.secrets_new as secrets_new
digicode_pass = secrets_new.get("digicode_pass") digicode_pass = secrets_new.get("digicode_pass")
# ############################################################# # #############################################################

View file

@ -22,6 +22,9 @@ import tempfile
from hosts_plugins import hosts_plugins, general_plugins from hosts_plugins import hosts_plugins, general_plugins
from gestion.config.services import services
# Plugins munin classiques à ignorer # Plugins munin classiques à ignorer
IGNORE_PLUGINS = ( IGNORE_PLUGINS = (
'apt_all', 'apt_all',
@ -39,6 +42,13 @@ IGNORE_PLUGINS = (
'vlan_', 'vlan_',
) )
if socket.gethostname() in services.services.get('iscsi', []):
IGNORE_PLUGINS += (
'diskstat',
'diskstats',
'diskstat_',
)
# Chemin d'accès aux plugins munin # Chemin d'accès aux plugins munin
MUNIN_PATH = "/usr/share/munin/plugins" MUNIN_PATH = "/usr/share/munin/plugins"

View file

@ -7,7 +7,7 @@ def dir(path):
return l return l
multicast={ multicast={
'Radio': { 'Radio': {
'Armitunes': ('armitunes','239.231.140.162','1234',['http://149.255.33.76:8004/','http://95.31.11.136:9010/','http://95.31.3.225:9010/','http://hanyo.dyndns-server.com:9078/']), 'Armitunes': ('armitunes','239.231.140.162','1234',['http://198.27.80.17:8000/','http://95.31.11.136:9010/','http://95.31.3.225:9010/']),
'Radio Classique': ('classique','239.231.140.163','1234',['http://broadcast.infomaniak.net:80/radioclassique-high.mp3']), 'Radio Classique': ('classique','239.231.140.163','1234',['http://broadcast.infomaniak.net:80/radioclassique-high.mp3']),
'France Inter': ('inter','239.231.140.164','1234',['http://mp3.live.tv-radio.com/franceinter/all/franceinterhautdebit.mp3']), 'France Inter': ('inter','239.231.140.164','1234',['http://mp3.live.tv-radio.com/franceinter/all/franceinterhautdebit.mp3']),
'France Info': ('info','239.231.140.165','1234',['http://mp3.live.tv-radio.com/franceinfo/all/franceinfo-32k.mp3']), 'France Info': ('info','239.231.140.165','1234',['http://mp3.live.tv-radio.com/franceinfo/all/franceinfo-32k.mp3']),
@ -16,7 +16,7 @@ multicast={
# 'Webradio Rock': ('rock','239.231.140.168','1234',['http://webradio.crans.org:8000/rock.mp3']), # 'Webradio Rock': ('rock','239.231.140.168','1234',['http://webradio.crans.org:8000/rock.mp3']),
'I.ACTIVE DANCE': ('iactive','239.231.140.170', '1234', ['http://serveur.wanastream.com:48700/']), 'I.ACTIVE DANCE': ('iactive','239.231.140.170', '1234', ['http://serveur.wanastream.com:48700/']),
'Skyrock': ('skyrock', '239.231.140.171', '1234', ['http://95.81.146.6/3665/sky_122353.mp3']), 'Skyrock': ('skyrock', '239.231.140.171', '1234', ['http://95.81.146.6/3665/sky_122353.mp3']),
'Rire et Chanson': ('rireetchanson', '239.231.140.172', '1234', ['http://95.81.146.2/rire_et_chansons/all/rir_124629.mp3']), 'Rire et Chanson': ('rireetchanson', '239.231.140.172', '1234', ['http://95.81.146.10/5011/nrj_122230.mp3']),
'Europe 1': ('europe1', '239.231.140.173', '1234', ['http://vipicecast.yacast.net/europe1.mp3']), 'Europe 1': ('europe1', '239.231.140.173', '1234', ['http://vipicecast.yacast.net/europe1.mp3']),
'Chérie FM': ('cherie_fm', '239.231.140.174', '1234', ['http://95.81.146.2/cherie_fm/all/che_124310.mp3']), 'Chérie FM': ('cherie_fm', '239.231.140.174', '1234', ['http://95.81.146.2/cherie_fm/all/che_124310.mp3']),
'France Culture': ('culture', '239.231.140.175', '1234', ['http://95.81.147.3/franceculture/all/franceculturehautdebit.mp3']), 'France Culture': ('culture', '239.231.140.175', '1234', ['http://95.81.147.3/franceculture/all/franceculturehautdebit.mp3']),

View file

@ -51,6 +51,12 @@ def do_auth(mac, prise):
conn = crans_ldap(readonly=True) conn = crans_ldap(readonly=True)
m = conn.search('mac=%s' % mac)['machine'] m = conn.search('mac=%s' % mac)['machine']
if len(m) == 0: if len(m) == 0:
# Est-ce un ancien client de l'offre Crous ?
# on le met sur le vlan install-party où on aura activé un forwarding
# (uniquement en attendant qu'il soit inscrit proprement)
for chbre in annuaires_pg.reverse(prise[0], prise[1:]):
if not annuaires_pg.is_crans(prise[0], chbre):
return (0, "TMP: ancien client CROUS", 'event')
return (0, "Mac inconnue", "accueil") return (0, "Mac inconnue", "accueil")
elif len(m) > 1: elif len(m) > 1:
return (-1, "Pb recherche mac (nb résultat %d!=1)" % len(m), "") return (-1, "Pb recherche mac (nb résultat %d!=1)" % len(m), "")

View file

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
"""
MoinMoin - AllPagesWithACL Macro
@copyright: 2007 Alexander "Loki" Agibalov
@license: GNU GPL, see COPYING for details.
changes:
12.2007 - conversion to new syntax by Bolesław Kulbabiński
Modifié par Vincent Le Galli <legallic@crans.org>
(cf http://moinmo.in/MacroMarket/1.6_AdminTools)
* patch pour ne pas afficher les acl "not defined"
* Si on fournit le paramètre IncludeSystemPages, on a aussi les pages MoinMoin,
mais par défaut, non.
"""
import os
import re
from MoinMoin.Page import Page
from MoinMoin import wikiutil
def getAcl(request, pagename):
pg = Page(request, pagename)
pi = pg.get_pi()
ret = pi["acl"].getString()
if ret == '':
ret = "not defined"
return ret
def macro_AllPagesWithACL(macro, args):
html = "<p><b>All pages:</b><br>"
all = {}
pages = macro.request.rootpage.getPageList()
# pages = macro.request.rootpage.getPageList(filter = re.compile("^WikiSandBox").match)
html += "Total: %s pages </p>" % str(len(pages))
for pagename in pages:
if Page(macro.request,pagename).isStandardPage() or (args != None and "IncludeSystemPages" in args):
all[Page(macro.request, pagename).link_to(macro.request)] = getAcl(macro.request, pagename)
html += "<table>"
all1 = sorted(all.items())
for pg, ac in all1:
if ac != "not defined":
html += "<tr><td>%s</td>" % pg
html += "<td>%s</td></tr>" % ac
html += "</table>"
return macro.formatter.rawHTML(html)
def execute(macro, args):
try:
return wikiutil.invoke_extension_function(
macro.request, macro_AllPagesWithACL, args, [macro])
except ValueError, err:
return macro.request.formatter.text(
"<<AllPagesWithACL: %s>>" % err.args[0])

View file

@ -101,6 +101,49 @@ class Theme(ThemeBase):
'{o}': ("{o}", "star_off.png", 16, 16), '{o}': ("{o}", "star_off.png", 16, 16),
} }
def navibar(self, d):
""" Assemble the navibar
@param d: parameter dictionary
@rtype: unicode
@return: navibar html
"""
request = self.request
found = {} # pages we found. prevent duplicates
items = [] # navibar items
item = u'<li class="%s">%s</li>'
current = d['page_name']
# Process config navi_bar
if request.cfg.navi_bar:
for text in request.cfg.navi_bar:
pagename, link = self.splitNavilink(text)
if pagename == current:
cls = 'wikilink current'
else:
cls = 'wikilink'
items.append(item % (cls, link))
found[pagename] = 1
# Add current page at end of local pages
if not current in found:
title = d['page'].split_title()
title = self.shortenPagename(title)
link = d['page'].link_to(request, title)
cls = 'current'
items.append(item % (cls, link))
# Assemble html
items = u''.join(items)
html = u'''
<ul id="navibar">
%s
</ul>
''' % items
return html
def wikipanel(self, d): def wikipanel(self, d):
""" Create wiki panel """ """ Create wiki panel """
_ = self.request.getText _ = self.request.getText