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

This commit is contained in:
root 2013-04-24 11:54:46 +02:00
commit a9b7d12314
57 changed files with 19532 additions and 17967 deletions

View file

@ -1,11 +1,11 @@
Configuration des scripts
=========================
config -- Configuration des scripts
===================================
Table des matières :
.. toctree::
:maxdepth: 2
:glob:
*
mails/index

View file

@ -1,5 +1,5 @@
Configuration des mails automatiquement envoyés
===============================================
mails -- Configuration des mails automatiquement envoyés
========================================================
Table des matières :

View file

@ -0,0 +1,7 @@
gen_confs.firewall4 -- Le pare-feu ipv4
=======================================
.. automodule:: gestion.gen_confs.firewall4
:members:
:special-members:

View file

@ -0,0 +1,10 @@
gen_confs -- Cr@ns Configuration Generator
==========================================
Table des matières :
.. toctree::
:maxdepth: 2
:glob:
*

View file

@ -1,5 +1,5 @@
gestion - scripts de gestion du Cr@ns
=====================================
gestion -- scripts de gestion du Cr@ns
======================================
Table des matières :
@ -7,3 +7,4 @@ Table des matières :
:maxdepth: 2
config/index
gen_confs/index

View file

@ -0,0 +1,5 @@
canon_wrapper -- Soumission de jobs à l'imprimante canon
========================================================
.. automodule:: impression.canon_wrapper
:members:

View file

@ -0,0 +1,11 @@
impression -- Scripts relatifs au service d'impression du Cr@ns
===============================================================
Table des matières :
.. toctree::
:maxdepth: 2
:glob:
*

View file

@ -12,6 +12,7 @@ Table des matières :
:maxdepth: 5
gestion/index
impression/index
Indices and tables
==================

View file

@ -2,27 +2,29 @@
# -*- coding: utf-8 -*-
import psycopg2
from functools import wraps
try:
if __name__ == 'annuaires_pg_test':
conn = psycopg2.connect(user='crans', database='switchs', host='localhost')
else:
conn = psycopg2.connect(user='crans', database='switchs', host='pgsql.adm.crans.org')
# Population de la tâble avec les bâtiments
cur = conn.cursor()
cur.execute("SELECT DISTINCT batiment FROM prises")
bat_switchs = [i[0] for i in cur.fetchall()]
cur.close()
del cur
except psycopg2.OperationalError:
bat_switchs = ["a", "b", "c", "g", "h", "i", "j", "m", "o", "p"]
conn = None
def _need_conn(f):
"""Décorateur à appliquer aux fonctions nécessitant une connexion pgsql"""
@wraps(f)
def first_connect(*args, **kwargs):
global conn
if conn == None:
if __name__ == 'annuaires_pg_test':
host='localhost'
else:
host='pgsql.adm.crans.org'
# "connecting …"
conn = psycopg2.connect(user='crans', database='switchs', host=host)
return f(*args, **kwargs)
return first_connect
bat_switchs = ["a", "b", "c", "g", "h", "i", "j", "m", "o", "p"]
bat_manuels = []
@_need_conn
def chbre_prises(batiment, chambre = None):
"""Correspondance chambre -> prise"""
batiment = batiment.lower()
@ -44,6 +46,7 @@ def chbre_prises(batiment, chambre = None):
raise ValueError("Batiment inexistant")
return ret
@_need_conn
def chbre_commentaire(batiment, chambre):
""" Renvoie le commentaire associé à la chambre """
global conn
@ -55,6 +58,7 @@ def chbre_commentaire(batiment, chambre):
except TypeError:
raise ValueError("Chambre inexistante")
@_need_conn
def reverse(batiment, prise = None):
"""Correspondance prise -> chambre"""
batiment = batiment.lower()
@ -79,6 +83,7 @@ def reverse(batiment, prise = None):
raise ValueError("Batiment inexistant")
return ret
@_need_conn
def is_crans(batiment, chambre):
"""Chambre cablee au Cr@ns ?"""
batiment = batiment.lower()
@ -87,6 +92,7 @@ def is_crans(batiment, chambre):
cur.execute("SELECT crans FROM prises WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
return cur.fetchone()[0]
@_need_conn
def is_connected(batiment, chambre):
"""Cablage physique effectue ?"""
batiment = batiment.lower()
@ -95,6 +101,7 @@ def is_connected(batiment, chambre):
cur.execute("SELECT cablage_effectue FROM prises WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
return cur.fetchone()[0]
@_need_conn
def crous_to_crans(batiment, chambre):
"""Passage d'une chambre de CROUS a Cr@ns"""
batiment = batiment.lower()
@ -207,7 +214,7 @@ uplink_prises={ 'a' :
# peuvent être utiles à connaître
401: 'uplink->batp0', 402: 'uplink->batp-1',
403: 'uplink->batp2', 403: 'uplink->batp-3',
405: 'libre-service', 406: 'uplink->backbone',
405: 'libre-service', 406: 'uplink->bato-1',
},
'o' :
{
@ -223,9 +230,9 @@ uplink_prises={ 'a' :
'A2': 'komaz-ens', 'B2': 'multiprise-wifi',
'A3': 'sable', 'B3': 'dyson',
'A4': 'komaz', 'B4': 'fy',
'A5': 'malloc-1', 'B5': 'switch-ilo',
'A5': 'zbee' , 'B5': 'switch-ilo',
'B6': 'vigile 0B',
'B7': 'daath',
'B7': 'kdell',
'B8': 'batb',
'B9': '2b',
'B10': 'fz',

View file

@ -1,7 +1,13 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
""" Pour détecter les gens en chambre invalide, les prévenir, et supprimer leurs machines
en l'absence de réponse. Récupérer des IPs, c'est cool."""
# Codé par b2moo, commenté par 20-100, cr{itiqu|on}é par Nit
# <daniel.stan@crans.org>
# <legallic@crans.org>
# <samir@crans.org>
import datetime
import time
@ -10,13 +16,22 @@ import ldap_crans
conn = ldap_crans.CransLdap()
import sys
#: envoyer un mail à chaque adhérent concerné
sendmails = False
if "--mail-all" in sys.argv:
sendmails = True
import email.Header
import os
#: Envoyer un mail à respbats
sendmail_respbats = True
if "--no-mail" in sys.argv:
sendmail_respbats = False
DEBUG = False
if "--debug" in sys.argv:
DEBUG = True
import os
import config
year = config.ann_scol
delai = config.demenagement_delai
@ -24,8 +39,10 @@ delai = config.demenagement_delai
import config.mails.demenagement
# On récupère ceux qui n'ont pas payé cette année
bad_boys_e_s = conn.search('chbre=????&paiement=%d&paiement!=%d' % (year-1,year))['adherent']
if config.periode_transitoire:
bad_boys_e_s = conn.search('chbre=????&paiement=%d&paiement!=%d' % (year-1,year))['adherent']
else:
bad_boys_e_s = conn.search('chbre=????&paiement=%d' % year)['adherent']
now = time.time()
@ -46,7 +63,7 @@ for clandestin in bad_boys_e_s:
delta = now - kickout_date
ttl = delai*86400 - delta
if ttl > 0:
if sendmails and machine_liste != []:
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
to = clandestin.mail()
if not "@" in to:
@ -55,15 +72,14 @@ for clandestin in bad_boys_e_s:
"chambre" : exchambre,
"jours" : int(ttl/86400) + 1,
"to" : to}
if DEBUG:
print mail
mailer = os.popen("/usr/sbin/sendmail -t", "w")
mailer.write(mail.encode("utf-8") + "\n.")
mailer.close()
else:
for m in machine_liste:
# On n'a pas envie d'essayer de supprimer une machine qui a une blackliste
if len(m.blacklist_actif()) > 0:
continue
to_print.append( (clandestin.id(), m.ip(), m.id(), m.nom()) )
m2 = conn.search('mid=%s' % m.id(),mode='w')['machine'][0]
m2.delete('Adherent sans chambre valide depuis %d jours' % delai)
@ -74,7 +90,7 @@ if to_print != []:
hostnamemaxsize = max([len(i[3]) for i in to_print])
template = u"| %%4s | %%-15s | %%4s | %%-%ss |\n" % (hostnamemaxsize)
message = u""
message += u"\nListe des machines supprimée 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)
message += tiret_line
message += template % ("aid", " ip", "mid", (" " * (max((hostnamemaxsize-8)/2,0)) + "hostname"))
@ -86,7 +102,9 @@ if to_print != []:
headers += u"Content-Type: text/plain; charset=UTF-8\n"
headers += u"To: respbats@crans.org\n"
mail = headers + "\n" + message
mailer = os.popen("/usr/sbin/sendmail -t", "w")
mailer.write(mail.encode("utf-8") + "\n.")
mailer.close()
if sendmails:
mailer = os.popen("/usr/sbin/sendmail -t", "w")
mailer.write(mail.encode("utf-8") + "\n.")
mailer.close()
else:
print mail

View file

@ -168,7 +168,9 @@ NETs_regexp = { 'all' : '^138\.231\.1(3[6789]|4[0123456789]|5[01])\.\d+$' }
# ci-dessus...
rid = {
# Rid pour les machines fixes
'fil' : (256, 2047),
'adherents' : (256, 2047),
# Rid pour les machines filaire ipv4
'fil' : (0, 2047),
# Rid pour les machines wifi
'wifi' : (2048, 4095),
# Rid pour les machines du vlan adm
@ -204,7 +206,8 @@ ipv6_machines_speciales = {
# Les préfixes ipv6
prefix = { 'subnet' : [ '2a01:240:fe3d::/48' ],
'serveurs' : [ '2a01:240:fe3d:4::/64'],
'serveurs' : [ '2a01:240:fe3d:4::/64' ],
'adherents' : [ '2a01:240:fe3d:4::/64' ],
'fil' : [ '2a01:240:fe3d:4::/64' ],
'adm' : [ '2a01:240:fe3d:c804::/64' ],
'wifi' : [ '2a01:240:fe3d:c04::/64' ],
@ -274,9 +277,12 @@ file_pickle = { 4 : '/tmp/ipt_pickle',
6 : '/tmp/ip6t_pickle'
}
blacklist_sanctions = ['upload', 'warez', 'p2p', 'autodisc_p2p','autodisc_virus','virus','autodisc_upload', 'bloq']
blacklist_sanctions = ['upload', 'warez', 'p2p', 'autodisc_p2p','autodisc_virus','virus', 'bloq']
if bl_carte_et_definitif:
blacklist_sanctions.append('carte_etudiant')
blacklist_sanctions_soft = ['autodisc_virus','ipv6_ra','mail_invalide','virus',
'upload', 'warez', 'p2p', 'autodisc_p2p', 'autodisc_upload', 'bloq','carte_etudiant','chambre_invalide']
'upload', 'warez', 'p2p', 'autodisc_p2p', 'bloq','carte_etudiant','chambre_invalide']
blacklist_bridage_upload = ['autodisc_upload']
adm_users = [ 'root', 'identd', 'daemon', 'postfix', 'freerad', 'amavis',
'nut', 'respbats', 'list', 'sqlgrey', 'ntpd', 'lp' ]

View file

@ -4,6 +4,28 @@
""" Variables de configuration pour le firewall """
import datetime
#: Interfaces réseaux des machines ayant un pare-feu particulié
dev = {
'komaz': {
'out' : 'ens',
'wifi' : 'crans.3',
'fil' : 'crans',
'app' : 'crans.21',
'adm' : 'crans.2',
'tun-ovh' : 'tun-ovh'
},
'zamok': {
'fil' : 'crans',
'adm' : 'crans.2'
},
'routeur': {
'fil' : 'eth0',
'adm' : 'eth1',
'accueil' : 'eth2',
'isolement' : 'eth3',
'app' : 'eth4'
},
}
#: Pour marquer les paquets
mark = { 'https-radin': '0x3',
@ -27,4 +49,22 @@ else:
#: Débit maximal autorisé
debit_max = 500 * 1024 / 8 # connexion de nuit et du week-end
#: Est-ce qu'on est en connexion de jour ou de nuit/week-end ?
debit_jour = False
debit_jour = False
#: Liste des réseaux non routables
reseaux_non_routables = [ '10.0.0.0/8', '172.16.0.0/12','198.18.0.0/15',
'169.254.0.0/16', '192.168.0.0/16', '224.0.0.0/4', '100.64.0.0/10',
'0.0.0.0/8','127.0.0.0/8','192.0.2.0/24','198.51.100.0/24','203.0.113.0/24',
]
#: Ports ouverts à défaut pour les adhérents dans le pare-feu
ports_default = {
'tcp' : {
'input' : [ '22' ],
'output' : [ ':24', '26:79', '80:134', '136', '140:444', '446:']
},
'udp' : {
'input' : [],
'output' : [ ':136','140:']
}
}

View file

@ -24,8 +24,8 @@ logiciels envoyant une très grande quantité de petites données
(vidéo-conférence par exemple). Il peut y avoir d'autres raisons.
Si cela continuait, et que tu dépassais la limite acceptable des 4096
Mo sur 24 heures, tu serais automatiquement déconnecté du réseau pour
Si cela continuait, et que tu dépassais la limite acceptable des 3789
Mo sur 24 heures, ton débit serais automatiquement fortement limité pour
une durée de 24 heures. Il t'appartient donc de surveiller cela de
plus près et de faire en sorte que tes machines n'uploadent pas de
manière excessive à l'avenir.
@ -50,15 +50,13 @@ Content-Type: text/plain; charset="utf-8"
Bonjour %(proprio)s,
Tu as temporairement été déconnecté du réseau en raison de l'envoi trop
Ton débit à été temporairement limité en raison de l'envoi trop
important de données vers l'extérieur (%(upload)s Mo en 24h).
Tu as toujours accès au web ainsi qu'à tes mails crans mais tous les
autres services te sont suspendus. Si cela devait se renouveler trop
souvent, tu serais déconnecté complètement pour une durée plus
importante. Il t'appartient donc de surveiller cela de plus près et de
faire en sorte que ta machine n'uploade plus de manière excessive à
l'avenir.
Si cela devait se renouveler trop souvent, tu serais déconnecté
complètement pour une durée plus importante.
Il t'appartient donc de surveiller cela de plus près et de faire en sorte que
ta machine n'uploade plus de manière excessive à l'avenir.
Pour plus d'informations, tu peux consulter la page :
http://wiki.crans.org/VieCrans/DéconnexionPourUpload
@ -85,10 +83,10 @@ Message créé par deconnexion.py"""
#: Envoyé à la ML disconnect@ en cas de dépassement de la limite hard
message_disconnect_hard = u"""From: %(from)s
To: %(to)s
Subject: %(proprio)s a =?iso-8859-1?Q?=E9t=E9=20d=E9connect=E9?=
Subject: %(proprio)s a =?iso-8859-1?Q?=E9t=E9=20brid=E9?=
Content-Type: text/plain; charset="utf-8"
%(proprio)s a été déconnecté pour upload (%(upload)s Mo).
%(proprio)s (aid=%(aid)s) a été limité en débit montant upload (%(upload)s Mo).
Ses machines ont été aperçues pour la dernière fois à ces endroits :
%(mdc)s
@ -101,14 +99,14 @@ Message créé par deconnexion.py"""
#: Envoyé à la ML disconnect@ en cas de dépassement de la limite hard plusieurs fois
message_disconnect_multi = u"""From: %(from)s
To: %(to)s
Subject: %(proprio)s a =?iso-8859-1?Q?=E9t=E9=20d=E9connect=E9?=
Subject: %(proprio)s a =?iso-8859-1?Q?=E9t=E9=20brid=E9?=
%(nbdeco)d fois pour upload en un mois !
Content-Type: text/plain; charset="utf-8"
L'adhérent %(proprio)s a été déconnecté %(nbdeco)d fois pour upload en un mois !
L'adhérent %(proprio)s a été bridé %(nbdeco)d fois pour upload en un mois !
Le PS a été généré et se trouve sur zamok :
%(ps)s
--\u0020
Message créé par deconnexion.py"""
Message créé par deconnexion.py"""

View file

@ -11,7 +11,7 @@ exempt = [ ['138.231.136.0/21', '138.231.0.0/16'],
soft = 300
#: limite hard
hard = 4096
hard = 3789
#: envoyer des mails à disconnect@ en cas de dépassement soft ?
disconnect_mail_soft = False

File diff suppressed because it is too large Load diff

View file

@ -46,7 +46,7 @@ class del_user:
import traceback
traceback.print_exc()
def delete_daath(self):
def delete_zbee(self):
cprint(u'Archivage fichiers utilisateur', 'gras')
for args in self.args:
anim('\t' + args)
@ -54,7 +54,7 @@ class del_user:
login, home = args.split(',')
if not login or not home:
raise ValueError('Argument invalide')
if home.startswith('/home/') and hostname == "daath":
if home.startswith('/home/') and hostname == "zbee":
home = "/home-adh/" + home[6:]
warn = ''
f = '%s/files/%s_%s.tar.bz2' % ('/home-adh/cimetiere',
@ -85,8 +85,8 @@ class del_user:
traceback.print_exc()
def reconfigure(self):
if hostname == "daath":
self.delete_daath()
if hostname == "zbee":
self.delete_zbee()
elif hostname == "owl":
self.delete_directory(u"Suppression des fichiers index de dovecot",
"/var/dovecot-indexes/%s")
@ -110,9 +110,6 @@ class home:
except ValueError:
home, uid, login = args.split(',')
mail_redirect = None
# Kludge pour daath (nfs) normalement inutile
if home.startswith('/home/') and hostname == "daath":
home = "/home-adh/" + home[6:]
### Home
if not os.path.exists(home):
# Le home n'existe pas

1202
gestion/gen_confs/firewall4.py Executable file

File diff suppressed because it is too large Load diff

View file

@ -343,16 +343,16 @@ class firewall_crans :
if ip.startswith("138.231.1"):
if machine.__class__.__name__ == "MachineWifi" and hostname != 'gordon':
# Machine Wifi, c'est la mac de gordon
rules[self.mac_ip_set].append((ip,mac_wifi))
rules[self.mac_ip_set].append("%s,%s" % (ip,mac_wifi))
else:
# Machine fixe
rules[self.mac_ip_set].append((ip,machine.mac()))
rules[self.mac_ip_set].append("%s,%s" % (ip,machine.mac()))
if machine.__class__.__name__ == "MachineWifi" and hostname == 'komaz':
rules[self.mac_ip_set_wifi].append((ip,machine.mac()))
rules[self.mac_ip_set_wifi].append("%s,%s" % (ip,machine.mac()))
elif machine.__class__.__name__ == "MachineWifi" and hostname != 'komaz':
rules[self.mac_ip_set_wifi].append((ip,mac_komaz))
rules[self.mac_ip_set_wifi].append("%s,%s" % (ip,mac_komaz))
elif ip.startswith("10.231.136."):
rules[self.mac_ip_adm_set].append((ip,machine.mac()))
rules[self.mac_ip_adm_set].append("%s,%s" % (ip,machine.mac()))
def mac_ip_gen(self):
self.anim = anim('\tChaîne TEST_MAC-IP', len(self.__machines()))
self.anim.reinit()
@ -485,10 +485,10 @@ class firewall_komaz(firewall_crans) :
eth_adm = "crans.2"
# Ports ouverts
ports_default = { 'tcp_EXT_VERS_CRANS' : [ '22' ],
'tcp_CRANS_VERS_EXT': [ ':24', '26:79', '80:134', '136', '140:444', '446:'],
'udp_EXT_VERS_CRANS' : [ ],
'udp_CRANS_VERS_EXT': [ ':136','140:'] }
ports_default = { 'tcp_EXT_VERS_CRANS' : config.firewall.ports_default['tcp']['input'],
'tcp_CRANS_VERS_EXT': config.firewall.ports_default['tcp']['output'],
'udp_EXT_VERS_CRANS' : config.firewall.ports_default['udp']['input'],
'udp_CRANS_VERS_EXT': config.firewall.ports_default['udp']['output'] }
# on retire 445 et 135 en tcp car plein de mac se font deconnecter
@ -512,10 +512,7 @@ class firewall_komaz(firewall_crans) :
ports_p2p = [ '412', '1214', '4662:4665' , '6346:6347', '6699', '6881:6889' ]
liste_reseaux_non_routables = [ '10.0.0.0/8', '172.16.0.0/12','198.18.0.0/15',
'169.254.0.0/16', '192.168.0.0/16', '224.0.0.0/4', '100.64.0.0/10',
'0.0.0.0/8','127.0.0.0/8','192.0.2.0/24','198.51.100.0/24','203.0.113.0/24',
'255.255.255.255/32']
liste_reseaux_non_routables = config.firewall.reseaux_non_routables
def reseaux_non_routables(self) :
""" Construction de RESEAUX_NON_ROUTABLES_{DST,SRC} """
@ -1313,7 +1310,7 @@ class firewall_zamok(firewall_crans) :
# Pour le nfs (le paquet à laisser passer n'a pas d'owner)
iptables("-A SERV_OUT_ADM -d fx.adm.crans.org -j ACCEPT")
iptables("-A SERV_OUT_ADM -d daath.adm.crans.org -j ACCEPT")
iptables("-A SERV_OUT_ADM -d nfs.adm.crans.org -j ACCEPT")
# Rien d'autre ne passe
iptables("-A SERV_OUT_ADM -j REJECT --reject-with icmp-net-prohibited")

View file

@ -4,14 +4,14 @@
# Copyright (C) Frédéric Pauget
# Licence : GPLv2
"""Ce script permet de lancer la reconfiguration des divers services
Usage: %(prog)s options
Les options possibles sont :
\t%(options)s
Les options avec = doivent être suivies d'un argument. Plusieurs
arguments peuvent être founis pour une même option, les séparer par &
"""
#"""Ce script permet de lancer la reconfiguration des divers services
#
#Usage: %(prog)s options
#Les options possibles sont :
#\t%(options)s
#Les options avec = doivent être suivies d'un argument. Plusieurs
#arguments peuvent être founis pour une même option, les séparer par &
#"""
import sys, signal, os, getopt
@ -27,34 +27,30 @@ from syslog import *
import platform
openlog("generate")
# On vérifie que l'on est root
if os.getuid() != 0:
sys.stderr.write("Il faut être root\n")
sys.exit(1)
signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
db = crans_ldap()
make_lock('auto_generate', 'Big lock', nowait=1)
class base_reconfigure:
__firewalled_servers = [ 'redisdead', 'zamok', 'sable', 'komaz', 'gordon', 'routeur' ]
__blacklist_servers = [ _s + '-blacklist' for _s in __firewalled_servers ]
__service_develop = {
'macip': [ 'redisdead-macip', 'zamok-macip', 'sable-macip', 'komaz-macip', 'gordon-macip',
'routeur-macip' ],
'macip': [ _s + '-macip' for _s in __firewalled_servers ],
# 'droits': [ 'rouge-droits', 'ragnarok-droits' ],
'bl_carte_etudiant':['komaz-blacklist'],
'bl_chbre_invalide':['komaz-blacklist'],
'blacklist_mail_invalide':['komaz-blacklist'],
'blacklist_virus':['komaz-blacklist'],
'blacklist_warez':['komaz-blacklist'],
'blacklist_ipv6_ra':['komaz-blacklist'],
'blacklist_upload': ['komaz-blacklist', 'zamok-blacklist' ],
'blacklist_p2p': ['komaz-blacklist', 'zamok-blacklist' ],
'blacklist_autodisc_virus':['komaz-blacklist'],
'blacklist_autodisc_upload': ['komaz-blacklist', 'zamok-blacklist'],
'blacklist_autodisc_p2p': ['komaz-blacklist', 'zamok-blacklist'],
'blacklist_bloq': [ 'komaz-blacklist', 'zamok-blacklist', 'dns' ],
'del_user': [ 'daath-del_user', 'owl-del_user', 'zamok-del_user' ]
'bl_carte_etudiant': __blacklist_servers,
'bl_chbre_invalide': __blacklist_servers,
'blacklist_mail_invalide': __blacklist_servers,
'blacklist_virus': __blacklist_servers,
'blacklist_warez': __blacklist_servers,
'blacklist_ipv6_ra': __blacklist_servers,
'blacklist_upload': __blacklist_servers,
'blacklist_p2p': __blacklist_servers,
'blacklist_autodisc_virus': __blacklist_servers,
'blacklist_autodisc_upload': __blacklist_servers,
'blacklist_autodisc_p2p': __blacklist_servers,
'blacklist_bloq': __blacklist_servers,
'del_user': [ 'zbee-del_user', 'owl-del_user', 'zamok-del_user' ]
}
#Y R U Aliasing !
__service_develop.update({
@ -72,6 +68,11 @@ class base_reconfigure:
def __init__(self, to_do=None):
# On vérifie que l'on est root
if os.getuid() != 0:
sys.stderr.write("Il faut être root\n")
sys.exit(1)
if not to_do:
if debug:
print 'Lecture des services à redémarrer dans la base LDAP...'
@ -144,14 +145,19 @@ class base_reconfigure:
service.machines = machines
service.reconfigure()
def _fw(self):
if not hasattr(self, '__real_fw'):
from firewall4 import firewall
self.__real_fw = firewall()
return self.__real_fw
def macip(self, ips):
if platform.dist()[1] >= '6':
import firewall_new
firewall = firewall_new
else:
import firewall
cprint(u"Mise a jour correspondance MAC-IP", 'gras')
eval("firewall.firewall_%s()" % hostname).mac_ip_maj(ips)
self._fw().mac_ip_maj(ips)
def blacklist(self, ips):
cprint(u"Mise a jour des blacklists", 'gras')
self._fw().blacklist_maj(ips)
class redisdead(base_reconfigure):
def droits(self):
@ -215,11 +221,7 @@ class zamok(base_reconfigure):
from adherents import del_user
self._do(del_user(args))
def blacklist(self):
from firewall import firewall_zamok
firewall_zamok().blacklist()
class daath(base_reconfigure):
class zbee(base_reconfigure):
def home(self, args):
from adherents import home
self._do(home(args))
@ -235,11 +237,6 @@ class daath(base_reconfigure):
class komaz(base_reconfigure):
def __fw(self):
if not hasattr(self, '__real_fw'):
from firewall_new import firewall_komaz
self.__real_fw = firewall_komaz()
return self.__real_fw
# Mimétisme de ma part -- xhub
def __fw6(self):
@ -251,33 +248,30 @@ class komaz(base_reconfigure):
def macip(self, ips):
cprint(u"Mise a jour correspondance MAC-IP", 'gras')
self.__fw().mac_ip_maj(ips)
self._fw().mac_ip_maj(ips)
self.__fw6().macs([], 6)
def ports(self, ips):
self.__fw().port_maj(ips)
self._fw().filtrage_ports_maj(ips)
#self.__fw6().ports(map(self.midt.from_ipv4, ips), 6)
def blacklist(self):
self.__fw().blacklist()
def blacklist(self, ips):
self._fw().blacklist_maj(ips)
self.__fw6().blacklist(6)
def classify(self, ips):
self.__fw().classes_p2p_maj(ips)
#self.__fw().classes_p2p_maj(ips)
pass
class dyson(base_reconfigure):
def autostatus(self):
from autostatus import autostatus
self._do(autostatus())
def dhcp(self):
from gen_confs.dhcpd_new import dhcp
self._do(dhcp(), self._machines())
class dhcp(base_reconfigure):
def dhcp(self):
from gen_confs.dhcpd_new import dhcp
self._do(dhcp(), self._machines())
self._do(dhcp(), db.search("mid=*")['machine'])
class sable(base_reconfigure):
@ -285,10 +279,6 @@ class sable(base_reconfigure):
from gen_confs.bind import dns
self._do(dns(), self._machines())
def macip(self, ips):
from firewall_new import firewall_sable
firewall_sable().mac_ip_maj(ips)
class ovh(base_reconfigure):
pass

View file

@ -17,13 +17,12 @@
import sys
sys.path.append('/usr/scripts/gestion')
sys.path.append('/usr/scripts/lc_ldap')
import commands
import lock
import os
import lc_ldap
import secrets
class IpsetError(Exception):
# Gestion des erreurs d'ipset
@ -36,10 +35,23 @@ class IpsetError(Exception):
class Ipset(object):
ipset="/usr/sbin/ipset"
def __str__(self):
return self.set
def __init__(self,set,type,typeopt=''):
self.set=set
self.type=type
self.typeopt=typeopt
self.squeeze = os.uname()[2] < '3'
try:
self.create()
except IpsetError as error:
if error.err_code != 256:
raise
elif not "already exists" in error.output:
raise
pass
def call(self,cmd,arg=''):
"""Appel système à ipset"""
@ -71,15 +83,19 @@ class Ipset(object):
def restore(self,rules):
""" restore le set courrant"""
rules_str=self.restore_format(rules)
create_str="-N %s %s %s" % (self.set,self.type,self.typeopt)
str="%s\n%s\nCOMMIT\n" % (create_str,rules_str)
if self.squeeze:
create_str="-N %s %s %s" % (self.set,self.type,self.typeopt)
str="%s\n%s\nCOMMIT\n" % (create_str,rules_str)
else:
str="%s\nCOMMIT\n" % rules_str
path='/tmp/ipset_%s' % self.set
f=open(path, 'w+')
f.write(str)
f.close()
try:
self.flush()
self.destroy()
if self.squeeze:
self.destroy()
except IpsetError: pass
cmd="cat %s | %s -R" % (path,self.ipset)
status,output=commands.getstatusoutput(cmd)
@ -94,5 +110,5 @@ class Ipset(object):
self.call("-X")
def restore_format(self,rules):
return '\n'.join(["-A %s %s,%s" % (self.set,ip,mac) for (ip,mac) in rules])
return '\n'.join(["-A %s %s" % (self.set,data) for data in rules])

View file

@ -1,11 +1,11 @@
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# -*- coding: utf-8 -*-
""" met à jour les propriétés des prises des switchs du bat :
mac autorisée(s), état (activé ou non) et nom de la prise
""" met à jour les propriétés des prises des switchs du bat :
mac autorisée(s), état (activé ou non) et nom de la prise
argument : nom du switch
procédure de configuration initiale :
procédure de configuration initiale :
* mot de passe admin (password manager user-name <username>)
* activation du ssh (crypto key generate ssh)
* copie fichier de conf
@ -23,9 +23,10 @@ from ldap_crans import crans_ldap, BorneWifi
from annuaires_pg import uplink_prises, reverse, bat_manuels, all_switchs, bat_switchs
from random import shuffle
from gen_confs import *
from time import localtime
import datetime
import config
import re
from crans.deprecated import deprecated
capture_model = re.compile(r'\((.*)\)')
headers_by_model = {
@ -38,27 +39,20 @@ headers_by_model = {
gigabit_models = ['J9021A', 'J9145A']
try:
any
except NameError:
def any(iterable):
for item in iterable:
if item:
return True
return False
class switch(gen_config) :
# Répertoire ou écire les fichiers de conf
CONF_REP='/tmp/' # avec un / derrière
"""Classe de configuration d'un switch"""
# Répertoire ou écire les fichiers de conf
CONF_REP='/tmp/' # avec un / derrière
config = """%(switch_config_header)s
hostname "%(switch)s"
; Generated %(date_gen)s by switchs.py
%(module-type)s
;-------------------------------------------------------- Snmp
snmp-server contact "root@crans.org"
snmp-server location "Batiment %(bat)s"
;A faire à la main
;A faire à la main
snmpv3 enable
snmpv3 restricted-access
;snmpv3 user "initial"
@ -121,7 +115,7 @@ vlan %(vlan_appts)s
exit
;-------------------------------------------------------- Logs
%(INTERFACES_CONF)s
;------------------------------------------------------- Accès d'administration
;------------------------------------------------------- Accès d'administration
no telnet-server
no web-management
aaa authentication ssh login public-key none
@ -144,7 +138,7 @@ no cdp run
no stack
"""
# Serveur DHCP des différent vlans
# Serveur DHCP des différent vlans
dhcp_servers = {
'1':'138.231.136.34',
'3':'138.231.148.34',
@ -187,7 +181,7 @@ exit
* un _tulpe_ de noms de switch => reconfig de ces swiths"""
self.db = crans_ldap() # connexion LDAP
if type(truc) == list :
# On enlève les chambres "CRA", "????" et EXT qui n'ont pas besion de config
# On enlève les chambres "CRA", "????" et EXT qui n'ont pas besion de config
self.chbres = [ch for ch in truc if (ch not in [ "CRA", "????", "EXT" ]) ]
self.switch = None
else :
@ -199,10 +193,10 @@ exit
def restart(self) :
if self.chbre :
# Tout est déja fait
# Tout est déja fait
return
####### Vu qu'il n'y a pas de serveur tftp ici
# on excécute pas le truc en dessous
# on excécute pas le truc en dessous
#for switch in self.switch :
# self.aff = anim('\treboot de %s' % switch)
# sw = hptools.switch(switch)
@ -217,61 +211,11 @@ exit
for switch in self.switch :
self.configure_switch(switch)
@deprecated("Tous les switchs possèdent une authentification radius.")
def configure_chbre(self,chbre) :
""" Recontigure la chambre fournie chambre """
try :
bat = chbre[0].lower()
if bat in bat_switchs :
prise = sw_chbre(chbre)
prise.reconfigure() # Vitesse et nom (juste au cas ou ca aurait changé)
elif bat in bat_manuels :
class prise_non_manageable :
def __init__(self,chbre) :
self.chbre = chbre
def __mail(self,sujet) :
To = "clef%s@crans.org" % self.chbre[0].lower()
From = To
conn=smtplib.SMTP('localhost')
txt_mail = "From: Crans scripts <%(From)s>\n"
txt_mail+= "To: %(To)s\n"
txt_mail+= "Subject: (CRANS) %s\n\nMerci." % sujet
conn.sendmail(From, To , txt_mail % { 'From' : From, 'To' : To })
conn.quit()
def disable(self) :
self.__mail("Chambre %s à débrancher." % self.chbre)
def enable(self) :
self.__mail("Chambre %s à brancher." % self.chbre)
prise=prise_non_manageable(chbre)
else :
# Rien a faire
print OK
return True
a = self.db.search('chbre=%s&paiement=ok' % chbre)
a = a['adherent'] + a['club']
if a and 'bloq' not in a[0].blacklist_actif() :
# Il faut activer la prise
anim('\tactivation chbre %s' % chbre)
prise.enable()
else :
# Il faut désactiver la prise
anim('\tdésactivation chbre %s' % chbre)
prise.disable()
print OK
except :
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
return False
return True
""" Recontigure la chambre fournie chambre.
Déprécié. Tous les switchs possèdent une authentification radius."""
return False
def configure_switch(self,switch) :
self.aff = anim('\tconfiguration de %s' % switch)
@ -291,9 +235,9 @@ exit
return 1
def __configure_switch(self,switch) :
""" Génère le fichier de conf du switch donné """
### Récupération données du switch
# Batiment et numéro du switch
""" Génère le fichier de conf du switch donné """
### Récupération données du switch
# Batiment et numéro du switch
bat = switch[3].lower()
sw_num = int(switch[5])
dhcp_servers = self.dhcp_servers
@ -303,12 +247,13 @@ exit
self.aff.cycle()
## On veut par défaut tout confier au serveur radius principal
## On veut par défaut tout confier au serveur radius principal
#shuffle(self.rad_servs)
rad = self.rad_template * len(self.rad_servs)
params = { 'switch' : switch, 'bat' : bat.upper() ,
'date_gen': str(datetime.datetime.now()),
'radius_key' : radius_key ,
'radius-serveurs' : rad[:-1] % tuple(self.rad_servs),
}
@ -328,7 +273,7 @@ exit
res, msg = commands.getstatusoutput("scp bat%s-%i:cfg/startup-config %s" % (bat, sw_num, old_config.name))
if res != 0:
raise RuntimeError(u"Erreur : impossible de récupérer l'ancienne configuration du switch")
raise RuntimeError(u"Erreur : impossible de récupérer l'ancienne configuration du switch")
params['switch_config_header'] = old_config.readline()
old_config.close()
@ -346,7 +291,7 @@ exit
sys.stderr.write(model)
params['switch_config_header']=headers_by_model[model]
except:
sys.stderr.write('Impossible de déterminer le header à utiliser (switch %s)' % switch)
sys.stderr.write('Impossible de déterminer le header à utiliser (switch %s)' % switch)
params['switch_config_header']= '; J4899A Configuration Editor; Created on release #H.10.50'
model = params['switch_config_header'].split(' ', 2)[1]
@ -357,10 +302,10 @@ exit
self.aff.cycle()
# Nombre de prises et modèle
# Nombre de prises et modèle
nb_prises = machine.nombrePrises()
if nb_prises < 0 :
raise RuntimeError("Erreur : impossible de déterminer les caractéristiques du switch.")
raise RuntimeError("Erreur : impossible de déterminer les caractéristiques du switch.")
has_dhcp_snooping = "2810" not in " ".join(machine.info())
@ -370,7 +315,7 @@ exit
# Dictionnaire prise -> chambre
prise_chbres = reverse(bat)
# Prises occupées par des machines du Cr@ns
# Prises occupées par des machines du Cr@ns
crans_prises={}
for m in self.db.search('prise=%s%i*' % (bat.upper(), sw_num))['machine'] :
try: crans_prises[m.prise()].append(m)
@ -378,7 +323,7 @@ exit
self.aff.iter = nb_prises+1
# Paramètres à affecter
# Paramètres à affecter
for key in ( 'uplinks', 'non_uplinks' ) :
params[key] = []
@ -387,21 +332,21 @@ exit
'adm_tagged' : [] , 'adm_untagged' : [] ,
'appts_tagged' : [], 'appts_untagged' : [],
# VLans pour le reste: le vlan des adhérents, des
# VLans pour le reste: le vlan des adhérents, des
# inconnus et de ceux qui ne paie pas
'default' : [] }
personnels_loges = self.db.search('etudes=Personnel ENS')['adherent']
prises_appartements= [ p.chbre() for p in personnels_loges ]
# Génération de la conf de chaque prise
# Génération de la conf de chaque prise
for prise in range(1,nb_prises+1):
self.aff.cycle()
# Conf par défaut : activée, autonégociation
# Conf par défaut : activée, autonégociation
prise_params = { 'prise' : prise , 'speed' : '',
'etat' : '', 'no_flowcontrol': '' }
annu_prise = '%i%02i' % (sw_num, prise) # prise telle que notée dans l'annuaire
annu_prise = '%i%02i' % (sw_num, prise) # prise telle que notée dans l'annuaire
if uplink_prises[bat].has_key(int(annu_prise)) :
### Prise d'uplink
@ -419,7 +364,7 @@ exit
params['non_uplinks'].append(prise)
if crans_prises.has_key("%s%s" % (bat.upper(), annu_prise)) :
### Prise réservée à l'association
### Prise réservée à l'association
wifi=0
adm=0
autres=0
@ -466,7 +411,7 @@ exit
# chambres.
chbres = prise_chbres.get(annu_prise, [])
# Pour les switchs gigabit, on bloque le gigabit par défaut, sauf
# Pour les switchs gigabit, on bloque le gigabit par défaut, sauf
# pour les membres actifs et les clubs (cf plus bas)
if model in gigabit_models:
prise_params['speed'] = 'speed-duplex auto-10-100'
@ -478,7 +423,7 @@ exit
# On selectionne les eventuels adherents y residant
residents = self.db.search("chbre=%s%s" % (bat, chb))
for adherent in residents['adherent']:
if adherent.droits(): #Seuls les membres actifs ont le droit à plus
if adherent.droits(): #Seuls les membres actifs ont le droit à plus
prise_params['speed'] = ''
# On selectionne les machines fixes de l'adherent, et on ajoute le nombre au quota
nombre_de_machines += len(adherent.machines_fixes())
@ -487,9 +432,9 @@ exit
# Authentification RADIUS, pas pour les clubs...
if not any("cl" in chbre.lower() for chbre in chbres):
# "unauth-vid" est le vlan sur lequel sont envoyés les machines
# quand l'authentification RADIUS échoue. On met le VLAN 1 pour
# éviter les problèmes quand LDAP se ch@#! dessus.
# "unauth-vid" est le vlan sur lequel sont envoyés les machines
# quand l'authentification RADIUS échoue. On met le VLAN 1 pour
# éviter les problèmes quand LDAP se ch@#! dessus.
params['INTERFACES_CONF'] += """aaa port-access mac-based %(prise)s
aaa port-access mac-based %(prise)s addr-limit %(nbmac)s
aaa port-access mac-based %(prise)s logoff-period 3600
@ -497,11 +442,11 @@ aaa port-access mac-based %(prise)s unauth-vid 1
""" % { 'nbmac': 2 + nombre_de_machines, 'prise': prise }
# On regle le nombre de machines connectables a la prise au nombre de machines
# sur cette prise dans l'annuaire plus 2
else: # ... et pour les clubs, vlans par défaut
else: # ... et pour les clubs, vlans par défaut
vlans['default'].append(prise)
# On donne à la prise un nom qui dépend des chambres
# connectés dessus
# On donne à la prise un nom qui dépend des chambres
# connectés dessus
if chbres :
prise_params['nom'] = 'Chambre'
if len(chbres) > 1 : prise_params['nom'] += 's'
@ -522,7 +467,7 @@ aaa port-access mac-based %(prise)s unauth-vid 1
# Petite verif
if not params['uplinks'] or not params['non_uplinks'] :
raise RuntimeError('Switch sans uplink ou sans prise adhérent.')
raise RuntimeError('Switch sans uplink ou sans prise adhérent.')
def mk_list(liste_prise) :
"""
@ -544,12 +489,12 @@ aaa port-access mac-based %(prise)s unauth-vid 1
if nouveau == groupe[1] + 1 :
groupe[1] += 1
else :
# Ajout du groupe au résultat
# Ajout du groupe au résultat
if groupe[0] == groupe[1] :
result.append(str(groupe[0]))
else :
result.append('-'.join(map(str,groupe)))
# Réinit de groupe
# Réinit de groupe
groupe = [ nouveau, nouveau ]
return ','.join(result)
@ -563,7 +508,7 @@ aaa port-access mac-based %(prise)s unauth-vid 1
for key, prises in vlans.items() :
vlans[key]=mk_list(prises)
# Config des vlans spéciaux (adm, wifi et appartements)
# Config des vlans spéciaux (adm, wifi et appartements)
for v in ('adm', 'wifi', 'hotspot', 'appts') :
params['prises_%s' % v] = ''
for t in ('tagged' , 'untagged') :
@ -590,7 +535,7 @@ if __name__ == '__main__' :
opts, args = getopt.getopt(sys.argv[1:], 'hga', ['get-conf', 'help', 'all', 'header=' ])
if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) == 1 :
print "%s [-g|--get-conf] <switch>" % sys.argv[0].split('/')[-1].split('.')[0]
print "Génération du fichier de configuration des switchs donnés."
print "Génération du fichier de configuration des switchs donnés."
sys.exit(255)
if args[0] == 'all' or 'a' in opts or '--all' in opts :

View file

@ -25,13 +25,14 @@ import os, re, syslog, cPickle, socket
from ldap_crans import crans_ldap, hostname
from commands import getstatusoutput
from config import NETs, role, prefix, rid, output_file, filter_policy
from config import blacklist_sanctions, blacklist_sanctions_soft, file_pickle, ann_scol, periode_transitoire
from config import blacklist_sanctions, blacklist_sanctions_soft, blacklist_bridage_upload, file_pickle, ann_scol, periode_transitoire
from iptools import AddrInNet
from ridtools import Rid
import subprocess
import netaddr
blacklist_sanctions.extend(blacklist_sanctions_soft)
blacklist_sanctions.extend(blacklist_bridage_upload)
Mangle_policy = """
*mangle

View file

@ -119,6 +119,25 @@ def AddrInNets(ip,nets) :
return net
return ''
def NetInNet(net1, net2) :
"""
net1 est de la forme xxx.xxx.xxx.xxx/yy
net2 est de la forme xxx.xxx.xxx.xxx/yy
Retourne True si net1 est un sous-réseaux de net2
"""
n1 = param(net1, raw=True)
n2 = param(net2, raw=True)
s1 = net1.split('/')[1]
s2 = net1.split('/')[1]
return s1<=s2 and (n1['network'] == n2['network'] or AddrInNet(DecToQuad(n1['network']), net2))
def NetInNets(net1, nets):
""" Vérifie si le premier paramètre est un sous-réseau des réseaux de la liste du second paramètre"""
for net in nets:
if NetInNet(net1, net) :
return net
return ''
def is_crans(ip):
""" Vérifie que l'ip est dans le réseau CRANS
"""

View file

@ -9,7 +9,8 @@
'''Bibliothèque pour accéder à la baie de stockage nols, récupère les données
formatées en XML'''
import telnetlib, re
import telnetlib
import re
from xml.etree.ElementTree import ElementTree, fromstring
# Message envoyé par le serveur pour attendre l'appuie sur touche
@ -24,6 +25,11 @@ password = ""
# Récupère des identifiants
execfile("/etc/crans/secrets/nols.py")
class NolsError(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)
class Nols(object):
'''Objet représentant la baie de stockage'''
@ -83,6 +89,9 @@ class Nols(object):
# Remplace les fins de ligne dos par des fin de lignes unix
resp = crlf_regexp.sub("\n", resp)
if resp.lower().startswith("error"):
raise NolsError(resp.replace("Error: ", ""))
return resp
def show(self, what):
@ -152,7 +161,7 @@ class Nols(object):
while lun in map: lun = lun + 1
# Création du volume
self.cmd("create volume vdisk %s size %d%s lun %d %s" % (vdisk, size, unit, lun, name))
result = self.cmd("create volume vdisk %s size %d%s lun %d %s" % (vdisk, size, unit, lun, name))
print "Le volume %s a été créé, son numéro d'identification est %d" %(name, lun)

View file

@ -8,7 +8,7 @@ Copyright (C) Alexandre Bos, largement pompe sur ldap_crans.py
Licence : GPLv2
"""
import sys
from config import NETs
from iptools import AddrInNet
try:
@ -79,13 +79,20 @@ def update_ip_wifi_adh(occupees):
update_ip('wifi-adh','ip_wifi-adh', occupees)
if __name__ == "__main__":
dlg = Dialog()
dlg.gauge_start(text="Recherche des machines...", backtitle="numeros_disponibles")
if "--cron" in sys.argv:
cron = True
else:
cron = False
if not cron:
dlg = Dialog()
dlg.gauge_start(text="Recherche des machines...", backtitle="numeros_disponibles")
ip_occupees = lister_ip_utilisees()
done = 1
for net in NETs.keys():
dlg.gauge_update(int(done*100/(len(NETs)+1)), text="IP libres dans %s" % net, update_text=True)
if not cron:
dlg.gauge_update(int(done*100/(len(NETs)+1)), text="IP libres dans %s" % net, update_text=True)
update_ip(net, ip_occupees)
done += 1
dlg.gauge_update(100, text="Fini !", update_text=True)
dlg.gauge_stop()
if not cron:
dlg.gauge_update(100, text="Fini !", update_text=True)
dlg.gauge_stop()

View file

@ -445,23 +445,23 @@ def ressuscite_adherent(old):
title=u"Compte existant")
if no:
return
if True:
# On croise les doigts
try:
modlist = ldap.modlist.addModlist(data)
db.conn.add_s(dn, modlist)
dlg.msgbox(text=u"Résurrection effectée ! Veuillez maintenant restaurer le home et les mails",
title=u"Fin")
# Au cas où l'adhérent avait des droits
db.services_to_restart('droits')
# On notifie après une résurrection
db.services_to_restart('mail_modif', ['uid=' + login])
except Exception, e:
dlg.msgbox(text=unicode(e), title=u"Erreur")
if True:
# On croise les doigts
try:
modlist = ldap.modlist.addModlist(data)
db.conn.add_s(dn, modlist)
dlg.msgbox(text=u"Résurrection effectée ! Veuillez maintenant restaurer le home et les mails",
title=u"Fin")
# Au cas où l'adhérent avait des droits
db.services_to_restart('droits')
# On notifie après une résurrection
db.services_to_restart('mail_modif', ['uid=' + login])
except Exception, e:
dlg.msgbox(text=unicode(e), title=u"Erreur")
else:
print data
raise ValueError("debug")
else:
print data
raise ValueError("debug")
def ressuscite_club(old):
"""Ressuscite une instance d'un club"""

0
impression/__init__.py Normal file
View file

View file

@ -1,9 +1,14 @@
#!/usr/bin/python
# -*- encoding: utf-8 -*-
# canon_wrapper.py
# Authors: Daniel STAN <dstan@crans.org>
# Antoine Durand-Gasselin <adg@crans.org>
# License: GPLv3
#canon_wrapper.py
"""
.. codeauthor:: Daniel STAN <dstan@crans.org>
.. codeauthor:: Antoine Durand-Gasselin <adg@crans.org>
Pour envoyer des jobs d'impression à l'imprimante canon via son interface web.
License: GPLv3
"""
import requests #pip install requests
# See:
@ -106,6 +111,7 @@ def build_url(name):
def range_checker(a, b):
"""Type function for a given range"""
def cast(x):
try:
v = int(x)
@ -117,6 +123,7 @@ def range_checker(a, b):
return cast
def build_parser():
"""Make argparse object"""
parser = argparse.ArgumentParser(
description="HTTP printing driver for irc3580, with compatibility mode.",
epilog="logs are sent to syslog (eg /var/log/canon_wrapper.log)",
@ -159,6 +166,7 @@ def build_parser():
return parser
def do_print(args):
"""Effectively compatibilize then print the file"""
opt=build_options(args)
syslog('Options: %s' % repr(opt))
#syslog('Starting ghostscript compat, piped mode')
@ -205,3 +213,7 @@ if __name__ == '__main__':
syslog('Caught exception %s' % repr(e))
raise
else:
parser = build_parser()
__doc__ += '''\nHelp message : ::\n\n'''
__doc__ += '\n'.join(' ' + l for l in parser.format_help().split('\n')) + "\n"

View file

@ -202,7 +202,7 @@ class impression:
# on compte les pages et on regarde le format
pdfinfo = Popen(["pdfinfo",self._fichier],stdout=PIPE,stderr=PIPE).communicate()
if pdfinfo[1] <> '':
raise FichierInvalide(u"pdfinfo n'arrive pas a lire le fichier (il est peut-etre corrompu ou protege par un mot de passe)",path_to_pdf)
raise FichierInvalide(u"pdfinfo n'arrive pas a lire le fichier (il est peut-etre corrompu ou protege par un mot de passe), https://wiki.crans.org/VieCrans/ImpressionReseau#Format_des_fichiers",path_to_pdf)
self._pages = -1
for line in pdfinfo[0].split('\n'):
if line.startswith('Pages'):
@ -378,6 +378,8 @@ class impression:
"""
self._jid = _uniq_jid()
# imprime le document
self._exec_imprime()
# debite l'adhérent si adherent il y a
if (self._adh != None):
adh = self._adh.split('@')
@ -389,9 +391,6 @@ class impression:
adh.solde(-self._prix, "impression(%d): %s par %s" % (self._jid,self._fichier,self._adh))
adh.save()
del adh
# imprime le document
self._exec_imprime()
def _calcule_prix(self):

View file

@ -23,6 +23,7 @@ import smtplib
from ldap_crans import crans_ldap
last_print_filename = "/var/run/print_status/last_print.txt"
error_filename = "/var/run/print_status/error.txt"
# Cette chaîne est utilisée pour construire une regexp, il faut que ce soit une chaîne brute.
files_directory = r"/var/impression/fichiers/"
printer_host = "imprimante.adm.crans.org"
@ -102,11 +103,25 @@ def sendMail(from_addr, to_addrs, mail_content):
http = httplib2.Http()
# On récupère la liste des tâches. Pour faire la requête, on doit récupérer un cookie de session.
headers, _ = http.request("http://" + printer_host + "/twelcome.cgi?CorePGTAG=0&Dummy=" + getMSE())
_, content = http.request("http://" + printer_host + "/pprint.csv?Flag=Csv_Data&LogType=0&Dummy=" + getMSE(), 'GET', headers={'Cookie': headers['set-cookie']})
try:
headers, _ = http.request("http://" + printer_host + "/twelcome.cgi?CorePGTAG=0&Dummy=" + getMSE())
_, content = http.request("http://" + printer_host + "/pprint.csv?Flag=Csv_Data&LogType=0&Dummy=" + getMSE(), 'GET', headers={'Cookie': headers['set-cookie']})
except:
# En cas derreur sur limprimante, plutôt que de spamer sur roots@crans.org, on note léchec quelque part à lattention dun service de monitoring.
error_file = open(error_filename, "w+")
error_file.write("Limprimante semble injoignable.")
error_file.close()
exit(0)
task_list = content.split('\n')
# On enlève les entêtes et les deux lignes vides à la fin de la liste.
task_list.pop(0)
# On vérifie que ce quon a récupéré ressemble à du CSV. Si tout se passe bien, limprimante envoie 13 champs par tâche.
# On fait le test sur les entêtes, ce qui permet de sen débarasser au passage.
if len(task_list.pop(0).split(',')) != 13:
error_file = open(error_filename, "w+")
error_file.write("La liste des tâches renvoyée par limprimante na pas le format attendu.")
error_file.close()
exit(0)
# On enlève aussi les deux lignes vides à la fin de la liste.
task_list.pop()
task_list.pop()
@ -137,7 +152,7 @@ tasks_to_treat.reverse()
for item in tasks_to_treat:
fields = item.split(',', 6)
# On met à jour le numéro de la dernière tâche traitée.
last_file = open(last_print_filename, "w")
last_file = open(last_print_filename, "w+")
last_file.write(fields[0])
last_file.close()
if fields[3].strip('"') in ["root",'DIRECT PRINT']:
@ -146,7 +161,7 @@ for item in tasks_to_treat:
if len(jobinfos) <= 2:
print "Skipping: %s" % fields[2]
continue
taskID, user, _ = jobinfos
taskID, user, filename = jobinfos
user = user.split('@').pop() # On récupère le nom du club si besoin.
date = buildDate(fields[5])
match_taskID = re.compile(r"impression\(%s\)" % taskID)
@ -180,3 +195,6 @@ for item in tasks_to_treat:
else:
mail_content = error_mail % (error_send_to, error_send_to, filename, taskID, full_name, user, date, result, u"\n".join(send_to))
sendMail(error_send_to, error_send_to, mail_content.encode("utf-8"))
error_file = open(error_filename, "w+")
error_file.close()

31
lib/deprecated.py Normal file
View file

@ -0,0 +1,31 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import warnings
import functools
def deprecated(replace=None):
'''This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used.'''
if replace == None:
instead = ""
elif isinstance(replace, str) or isinstance(replace, unicode):
instead = " " + replace
else:
instead = " Use %s instead." % (replace.__name__,)
def real_decorator(func):
"""Nested because a decorator with a parameter has to be coded this way"""
@functools.wraps(func)
def new_func(*args, **kwargs):
warnings.warn_explicit(
"Call to deprecated function %s.%s" % (func.__name__, instead),
category=DeprecationWarning,
filename=func.func_code.co_filename,
lineno=func.func_code.co_firstlineno + 1
)
return func(*args, **kwargs)
return new_func
return real_decorator

44
munin/ipset Executable file
View file

@ -0,0 +1,44 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Compteur des sets ipset
import sys,commands,string
IPSET = "ipset -L "
if sys.argv[0].endswith('macip'):
MACIP = True
else:
MACIP = False
try :
arg = sys.argv[1]
except :
arg = ''
CHAINS = commands.getoutput('%s | grep -- ^Name: | awk \'{print $2}\'' % IPSET).split('\n')
CHAINS = [ chain for chain in CHAINS if (not MACIP and not chain.startswith('MAC-IP')) or ( MACIP and chain.startswith('MAC-IP')) ]
if arg == "config" :
print 'graph_title Ipset' + (MACIP and ' test mac-ip' or '')
print 'graph_args --base 1000 --lower-limit 0'
print 'graph_category network'
print "graph_vlabel nb de regles"
for chain in CHAINS :
nom = string.lower(chain.replace('_', '').replace('-', '').replace('.','').replace('/', ''))
label = chain.replace('_', '-').replace('.','-').replace('/','-')
print "%s.label %s" % (nom, label)
if CHAINS.index(chain) == 0 :
print "%s.draw AREA" % nom
else :
print "%s.draw STACK" % nom
else :
for chain in CHAINS :
nom = string.lower(chain.replace('_', '').replace('-', '').replace('.','').replace('/', ''))
label = chain.replace('_', '-').replace('.','-').replace('/','-')
value = int(commands.getoutput('%s %s | wc -l' % (IPSET, chain))) - 6
print "%s.value %d" % (nom, value)

View file

@ -70,6 +70,9 @@ def do_auth(mac, prise):
if ('virus' in m[0].blacklist_actif() or 'ipv6_ra' in m[0].blacklist_actif()
or 'autodisc_virus' in m[0].blacklist_actif()):
return (0, "Bad boy", "isolement")
if ('carte_etudiant' in m[0].blacklist_actif()):
return (0, "Manque carte etudiant", "accueil")
# Paiement proprio ?
if not paiement_ok(proprio):

View file

@ -126,6 +126,13 @@ def stats(ip_crans=[], ip_ext=[],
# on transforme tout en chaine
results = [ [ str(x) for x in line ] for line in results ]
upload = 0
download = 0
for line in results:
upload+=int(line[4])
download+=int(line[3])
print " upload: %sMo" % (upload/1024/1024)
print " download: %sMo" % (download/1024/1024)
# on modifie les ip en noms de machine et les ports en noms
def nom_de_machine (ip) :

View file

@ -167,14 +167,15 @@ uploadeurs = curseur.fetchall()
# On regarde s'il y a deux ipv6 identiques avec des mac non identiques
collision_mac_ip_request = "SELECT DISTINCT (a.*) FROM mac_ip as a, mac_ip as b where a.ip=b.ip AND a.mac != b.mac AND a.date >= b.date AND a.date - b.date < interval '3 day' ORDER BY a.date;"
collision_mac_ip_request = "SELECT DISTINCT a.date as date1, a.mac as mac1, a.ip as ip1, b.date as date2, b.mac as mac2, b.ip as ip2 FROM mac_ip as a, mac_ip as b where a.ip=b.ip AND a.mac != b.mac AND a.date >= b.date AND a.date - b.date < interval '3 day' ORDER BY a.date;"
curseur.execute(collision_mac_ip_request)
collision_mac_ip = curseur.fetchall()
if collision_mac_ip != []:
print "Collision d'addresses ipv6 : "
for (date, mac, ip) in collision_mac_ip:
print "%s %s %s" % (date, ipt.mac_addr(mac), ip)
for (date1, mac1, ip1, date2, mac2, ip2) in collision_mac_ip:
print "%s %s %s" % (date1, ipt.mac_addr(mac1), ip1)
print "%s %s %s" % (date2, ipt.mac_addr(mac2), ip2)
# Table des avertis
###################
@ -250,7 +251,7 @@ for elupload, eltype, elid in uploadeurs:
# On envoie un mail à disconnect
################################
if upload.disconnect_mail_hard:
corps = config.mails.upload.message_disconnect_hard % {'from': upload.expediteur, 'to': upload.expediteur, 'upload': elupload, 'proprio': proprio.Nom(), 'mdc': mdcf, 'chambre': proprio.chbre()}
corps = config.mails.upload.message_disconnect_hard % {'from': upload.expediteur, 'to': upload.expediteur, 'upload': elupload, 'proprio': proprio.Nom(), 'mdc': mdcf, 'chambre': proprio.chbre(), 'aid':proprio.id()}
corps = corps.encode('utf-8')
mail.sendmail(upload.expediteur, upload.expediteur, corps)
@ -311,7 +312,7 @@ for elupload, eltype, elid in uploadeurs:
mail.sendmail(upload.expediteur, upload.expediteur, corps)
# On supprime les vieux avertisements
curseur.execute("DELETE FROM avertis_upload_hard WHERE date < timestamp 'now' - interval '1 day'")
curseur.execute("DELETE FROM avertis_upload_hard WHERE date < timestamp 'now' - interval '85200 seconds'") # 23h et 40min pour prolonger les blacklists toujours au dessus de la limite
curseur.execute("DELETE FROM avertis_upload_soft WHERE date < timestamp 'now' - interval '1 day'")
################################################################################

View file

@ -137,7 +137,7 @@ Subject: %(subject)s
Content-Type: multipart/mixed; boundary="_424234545aaff-ffca234efff-556adceff5646_"
--_424234545aaff-ffca234efff-556adceff5646_
Content-Type: text/plain, charset="UTF-8"
Content-Type: text/plain; charset="UTF-8"
%(contenu)s

View file

@ -98,7 +98,7 @@ if __name__ == '__main__':
message = """From: %(from)s
To: %(to)s
Subject: %(subject)s
Content-Type: text/plain, charset="UTF-8"
Content-Type: text/plain; charset="UTF-8"
%(contenu)s

View file

@ -4,15 +4,19 @@
-- effacement des vieux enregistrements
DELETE FROM upload where date< timestamp 'now' - interval '2 days';
-- la même pour upload6
DELETE FROM upload6 WHERE date< timestamp 'now' - interval '2 days';
-- On ne blackliste plus pour virus, on droppe seulement
-- DELETE FROM virus where date< timestamp 'now' - interval '2 days';
DELETE FROM flood where date< timestamp 'now' - interval '2 days';
-- Idem pour flood
-- DELETE FROM flood where date< timestamp 'now' - interval '2 days';
-- suppression complète des entrées
VACUUM;
-- réindexation des tables
REINDEX TABLE upload;
REINDEX TABLE virus;
REINDEX TABLE upload6;
-- REINDEX TABLE virus;
-- REINDEX TABLE flood; -- flood n'a pas d'index
REINDEX TABLE p2p;

0
utils/__init__.py Normal file
View file

View file

@ -2,7 +2,8 @@
# -*- coding: utf-8 -*-
#
# check_cert.py -- Petit mail de vérification du certificat d'un serveur
# ce script vérifie principalement la date
# ce script vérifie principalement la date d'expiration et envoie un mail
# d'avertissement si celle-ci est proche (paramétrable)
#
# Copyright (c) 2013 Daniel STAN
# Authors: Daniel STAN <daniel.stan@crans.org>
@ -20,7 +21,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from M2Crypto import SSL
from M2Crypto import X509
import datetime
import sys
@ -31,8 +34,9 @@ import smtplib
#
# Config !
#
host = 'webmail.crans.org'
host = 'localhost'
port = 443
filename = False # if True, port ignored and host is in fact a path
# afficher la sortie plutôt que l'envoyer:
verb = False
# delai d'avertissement
@ -42,46 +46,60 @@ delay = datetime.timedelta(days=15)
mail_src = 'root@crans.org'
mail_dest = "roots@crans.org"
mail_host = 'localhost'
#
# Argument parsing !
#
# TODO argparse + doc
for arg in sys.argv[1:]:
if arg == '-v':
verb = True
continue
if arg == '--filename':
filename = True
try:
port = int(arg)
except ValueError:
host = arg
# TODO: permettre la vérification directement sur un fichier
# get cert:
conn = SSL.Connection(SSL.Context())
try:
conn.connect((host, port))
except SSL.Checker.WrongHost:
if host != 'localhost':
raise
#
# Getting cert !
#
if filename:
cert = X509.load_cert(host)
else:
conn = SSL.Connection(SSL.Context())
try:
conn.connect((host, port))
except SSL.Checker.WrongHost:
if host != 'localhost':
raise
cert = conn.get_peer_cert()
conn.close()
cert = conn.get_peer_cert()
conn.close()
#
# Real computation (woah !)
#
expire_date = cert.get_not_after().get_datetime()
now = datetime.datetime.now(expire_date.tzinfo)
if now + delay > expire_date:
if now + delay > expire_date or verb:
subject = cert.get_subject().as_text()
short_sub = subject
try:
subject += "(alt: %s)" % cert.get_ext('subjectAltName').get_value()
except LookupError:
pass
conn = smtplib.SMTP(mail_host)
msg = MIMEText(u"""Attention, le certificat suivant arrive bientôt à expiration :\n%s\n
Temps avant expiration: %s""" % (subject,(expire_date - now)), _charset="utf-8")
msg['From'] = mail_src
msg['To'] = mail_dest
msg['Subject'] = u"Expiration imminente du certificat %s" % short_sub
if not verb:
conn = smtplib.SMTP(mail_host)
conn.sendmail(mail_src, mail_dest, msg.as_string())
conn.quit()
else:
print msg.as_string()
conn.quit()
print msg.get_payload(decode=True)

View file

@ -8,7 +8,7 @@ TEMPFILE=`tempfile`
# on évite de casser le dépôt darcs
umask 002
wget -o /dev/null -O $TEMPFILE http://standards.ieee.org/regauth/oui/oui.txt \
&& awk -F '[\t ]*(hex)[ \t]*' '(/(hex)/) {gsub("-",":",$1) ; print $1" "$2}' < $TEMPFILE > /usr/scripts/gestion/ethercodes.dat \
&& awk -F '[\t ]*(hex)[ \t]*' '(/(hex)/) {gsub("-",":",$1) ; {gsub("^ *", "", $1)}; {gsub("\\(", "", $1)}; {gsub("\\)", "", $2)}; print $1" "$2}' < $TEMPFILE > /usr/scripts/gestion/ethercodes.dat \
&& cd /usr/scripts/gestion \
&& git commit --author="Cron Daemon <root@crans.org>" -m "[ethercodes.dat] Mise à jour du fichier vendeur" ethercodes.dat > /dev/null \
&& git push > /dev/null

View file

@ -3,17 +3,16 @@
import os
import PagesPerso
def comptes():
"""Retourne la liste des comptes"""
return filter(lambda x: os.path.isdir(u"/home/club/%s" % x) and not os.path.islink(u"/home/club/%s" % x),
os.listdir(u"/home/club"))
class ClubList(PagesPerso.AccountList):
home = "/home/club"
www = ""
url = "http://clubs.ens-cachan.fr/%s/"
def url(self):
"""URL vers la page perso"""
return u"http://clubs.ens-cachan.fr/%s/" % self.login
def comptes(self):
"""Retourne la liste des comptes"""
return filter(lambda x: os.path.isdir(u"/home/club/%s" % x) and not os.path.islink(u"/home/club/%s" % x),
os.listdir(u"/home/club"))
def execute(macro, args):
return macro.formatter.rawHTML(ClubList().to_html())
PagesPerso.comptes = comptes
PagesPerso.account.home = "/home/club"
PagesPerso.account.www = ""
PagesPerso.account.url = url
execute = PagesPerso.execute

View file

@ -2,16 +2,57 @@
import os
class account:
"""Classe représentant la page perso d'une personne"""
class AccountList:
home = "/home"
www = "/www"
url = "http://perso.crans.org/%s/"
def __init__(self, login):
def __init__(self):
return
def comptes(self):
"""Retourne la liste des comptes"""
return filter(lambda x: os.path.isdir(u"/home/%s/www" % x) and not os.path.islink(u"/home/%s/www" % x),
os.listdir(u"/home/mail"))
def makeAnchor(self,letter):
return u"<div class=\"vignetteperso\"><a class=\"letter_anchor\" name=\"index_%s\"><span>%s:</span></a></div>" % ( letter, letter )
def makeIndex(self,letter_list):
index = u''
for aLetter in letter_list:
index = u"%s<a href=\"#index_%s\">%s</a>" % ( index, aLetter, aLetter)
return u"<div class=\"alphabetic_index\">%s</div>" % index
def to_html(self):
dirs = self.comptes()
dirs.sort()
html = u""
premiere_lettre = ''
letter_list = []
for d in dirs:
if premiere_lettre != d[0]:
premiere_lettre = d[0]
letter_list.append(premiere_lettre)
html = u"%s\n%s" % ( html, self.makeAnchor(premiere_lettre) )
html = u"%s\n%s" % (html, Account(self.home, d, self.www, self.url).to_html())
index = self.makeIndex(letter_list)
html = index + html
html += u'<br style="clear: both">'
return html
class Account:
"""Classe représentant la page perso d'une personne"""
def __init__(self, home, login, www, url):
"""Instanciation avec le `login' de la personne"""
self.login = login
self.home = "%s/%s" % (self.home, login)
self.home = "%s/%s" % (home, login)
self.www = www
self.url = url
_info = None
def info(self, champ):
@ -39,10 +80,6 @@ class account:
"""Chemin vers le www"""
return u"%s%s" % (self.home, self.www)
def url(self):
"""URL vers la page perso"""
return u"http://perso.crans.org/%s/" % self.login
def logo(self):
"""URL du logo s'il y en a un"""
if self.info("logo"):
@ -52,13 +89,13 @@ class account:
else:
logo = self.info("logo")
if os.path.isfile("%s/%s" % (self.chemin(), logo)):
return u"%s%s" % (self.url(), logo)
return u"%s%s" % (self.url % self.login, logo)
return u"http://perso.crans.org/pageperso.png"
def __str__(self):
def to_html(self):
"""Renvoie le code HTML correspondant au fichier .info"""
html = [ u'<div class="vignetteperso">',
u'<a href="%s">' % self.url(),
u'<a href="%s">' % (self.url % self.login),
u'<img src="%s" alt="%s">' % (self.logo(), self.login),
u'</a><br>',
self.info("nom") and u'<b>%s</b><br>' % self.info("nom") or u'%s<br>' % self.login,
@ -66,38 +103,6 @@ class account:
u'</div>' ]
return u'\n'.join(html)
def comptes():
"""Retourne la liste des comptes"""
return filter(lambda x: os.path.isdir(u"/home/%s/www" % x) and not os.path.islink(u"/home/%s/www" % x),
os.listdir(u"/home/mail"))
def makeAnchor(letter):
return u"<div class=\"vignetteperso\"><a class=\"letter_anchor\" name=\"index_%s\"><span>%s:</span></a></div>" % ( letter, letter )
def makeIndex(letter_list):
index = u''
for aLetter in letter_list:
index = u"%s<a href=\"#index_%s\">%s</a>" % ( index, aLetter, aLetter)
return u"<div class=\"alphabetic_index\">%s</div>" % index
def execute(macro, args):
dirs = comptes()
dirs.sort()
html = u""
premiere_lettre = ''
letter_list = []
for d in dirs:
if premiere_lettre != d[0]:
premiere_lettre = d[0]
letter_list.append(premiere_lettre)
html = u"%s\n%s" % ( html, makeAnchor(premiere_lettre) )
html = u"%s\n%s" % (html, account(d).__str__())
index = makeIndex(letter_list)
html = index + html
html += u'<br style="clear: both">'
return macro.formatter.rawHTML(html)
return macro.formatter.rawHTML(AccountList().to_html())

View file

@ -33,9 +33,10 @@ class Theme(ThemeBase):
(u'http://www.mozilla-europe.org/fr/firefox/',
u'badges_80x15_firefox.gif', u'Get firefox'),
(u'http://www.debian.org/', u'badges_80x15_debian.png', u'Debian powered'),
(u'http://www.apache.org/', u'badges_80x15_apache.gif', u'Apache powered'),
(u'http://wiki.nginx.org/', u'badges_80x15_nginx.png', u'Nginx powered'),
(u'http://www.python.org/', u'badges_80x15_python.png', u'Python powered'),
(u'http://www.federez.org/', u'badges_80x15_federez.png', u'Membre du r&eacute;seau federez'),
(u'/VieCrans/Donateurs', u'badges_80x15_donor.png', u'Généreux donateurs'),
(u'http://moinmo.in/', u'badges_80x15_moinmoin.png', u'Moinmoin powered'),
(u'http://jigsaw.w3.org/css-validator/check?uri=referer&amp;profile=css3&amp;warning=no',
u'valid_css_80x15.png', u'Valid css3'),