8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-27 07:02:26 +00:00

Freeradius python3 backend

This commit is contained in:
Gabriel Detraz 2020-05-16 03:57:29 +02:00
parent abbcf4a1de
commit 41486d7ba8
7 changed files with 145 additions and 255 deletions

View file

@ -1,14 +1,14 @@
python-django python3-django
python-dateutil python3-dateutil
texlive-latex-base texlive-latex-base
texlive-fonts-recommended texlive-fonts-recommended
python-djangorestframework python3-djangorestframework
python-django-reversion python3-django-reversion
python-pip python3-pip
libsasl2-dev libldap2-dev libsasl2-dev libldap2-dev
libssl-dev libssl-dev
python-crypto python3-crypto
python-git python3-git
javascript-common javascript-common
libjs-jquery libjs-jquery
libjs-jquery-ui libjs-jquery-ui
@ -19,5 +19,6 @@ graphviz
git git
gettext gettext
freeradius-common freeradius-common
freeradius-python2 freeradius-python3
python-mysqldb python3-dev
python3-mysqldb

View file

@ -23,23 +23,22 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """
Backend python pour freeradius. Python backend for freeradius.
Ce fichier contient la définition de plusieurs fonctions d'interface à This file contain definition of some functions called by freeradius backend
freeradius qui peuvent être appelées (suivant les configurations) à certains during auth for wifi, wired device and nas.
moment de l'authentification, en WiFi, filaire, ou par les NAS eux-mêmes.
Inspirés d'autres exemples trouvés ici : Other examples can be found here :
https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/ https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/
Inspiré du travail de Daniel Stan au Crans Inspired by Daniel Stan in Crans
""" """
import os import os
import sys import sys
import logging import logging
import traceback import traceback
import radiusd # Module magique freeradius (radiusd.py is dummy) import radiusd # Magic module freeradius (radiusd.py is dummy)
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
from django.db.models import Q from django.db.models import Q
@ -61,26 +60,23 @@ from users.models import User
from preferences.models import RadiusOption from preferences.models import RadiusOption
#: Serveur radius de test (pas la prod)
TEST_SERVER = bool(os.getenv("DBG_FREERADIUS", False))
# Logging # Logging
class RadiusdHandler(logging.Handler): class RadiusdHandler(logging.Handler):
"""Handler de logs pour freeradius""" """Logs handler for freeradius"""
def emit(self, record): def emit(self, record):
"""Process un message de log, en convertissant les niveaux""" """Log message processing, level are converted"""
if record.levelno >= logging.WARN: if record.levelno >= logging.WARN:
rad_sig = radiusd.L_ERR rad_sig = radiusd.L_ERR
elif record.levelno >= logging.INFO: elif record.levelno >= logging.INFO:
rad_sig = radiusd.L_INFO rad_sig = radiusd.L_INFO
else: else:
rad_sig = radiusd.L_DBG rad_sig = radiusd.L_DBG
radiusd.radlog(rad_sig, record.msg.encode("utf-8")) radiusd.radlog(rad_sig, str(record.msg))
# Initialisation d'un logger (pour logguer unifié) # Init for logging
logger = logging.getLogger("auth.py") logger = logging.getLogger("auth.py")
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s") formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
@ -90,17 +86,15 @@ logger.addHandler(handler)
def radius_event(fun): def radius_event(fun):
"""Décorateur pour les fonctions d'interfaces avec radius. """Decorator for freeradius fonction with radius.
Une telle fonction prend un uniquement argument, qui est une liste de This function take a unique argument which is a list of tuples (key, value)
tuples (clé, valeur) et renvoie un triplet dont les composantes sont : and return a tuple of 3 values which are:
* le code de retour (voir radiusd.RLM_MODULE_* ) * return code (see radiusd.RLM_MODULE_* )
* un tuple de couples (clé, valeur) pour les valeurs de réponse (accès ok * a tuple of 2 elements for response value (access ok , etc)
et autres trucs du genre) * a tuple of 2 elements for internal value to update (password for example)
* un tuple de couples (clé, valeur) pour les valeurs internes à mettre à
jour (mot de passe par exemple)
On se contente avec ce décorateur (pour l'instant) de convertir la liste de Here, we convert the list of tuples into a dictionnary.
tuples en entrée en un dictionnaire.""" """
def new_f(auth_data): def new_f(auth_data):
""" The function transforming the tuples as dict """ """ The function transforming the tuples as dict """
@ -113,8 +107,6 @@ def radius_event(fun):
# Ex: Calling-Station-Id: "une_adresse_mac" # Ex: Calling-Station-Id: "une_adresse_mac"
data[key] = value.replace('"', "") data[key] = value.replace('"', "")
try: try:
# TODO s'assurer ici que les tuples renvoyés sont bien des
# (str,str) : rlm_python ne digère PAS les unicodes
return fun(data) return fun(data)
except Exception as err: except Exception as err:
exc_type, exc_instance, exc_traceback = sys.exc_info() exc_type, exc_instance, exc_traceback = sys.exc_info()
@ -128,36 +120,32 @@ def radius_event(fun):
@radius_event @radius_event
def instantiate(*_): def instantiate(*_):
"""Utile pour initialiser les connexions ldap une première fois (otherwise, """Usefull for instantiate ldap connexions otherwise,
do nothing)""" do nothing"""
logger.info("Instantiation") logger.info("Instantiation")
if TEST_SERVER:
logger.info(u"DBG_FREERADIUS is enabled")
@radius_event @radius_event
def authorize(data): def authorize(data):
"""On test si on connait le calling nas: """Here, we test if the Nas is known.
- si le nas est inconnue, on suppose que c'est une requète 802.1X, on la - If the nas is unknown, we assume that it is a 802.1X request,
traite - If the nas is known, we apply the 802.1X if enabled,
- si le nas est connu, on applique 802.1X si le mode est activé - It the nas is known AND nas auth is enabled with mac address, returns
- si le nas est connu et si il s'agit d'un nas auth par mac, on repond accept here"""
accept en authorize # For proxified request, split
"""
# Pour les requetes proxifiees, on split
nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None)) nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None))
nas_instance = find_nas_from_request(nas) nas_instance = find_nas_from_request(nas)
# Toutes les reuquètes non proxifiées # For none proxified requests
nas_type = None nas_type = None
if nas_instance: if nas_instance:
nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first() nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first()
if not nas_type or nas_type.port_access_mode == "802.1X": if not nas_type or nas_type.port_access_mode == "802.1X":
user = data.get("User-Name", "").decode("utf-8", errors="replace") user = data.get("User-Name", "")
user = user.split("@", 1)[0] user = user.split("@", 1)[0]
mac = data.get("Calling-Station-Id", "") mac = data.get("Calling-Station-Id", "")
result, log, password = check_user_machine_and_register(nas_type, user, mac) result, log, password = check_user_machine_and_register(nas_type, user, mac)
logger.info(log.encode("utf-8")) logger.info(str(log))
logger.info(user.encode("utf-8")) logger.info(str(user))
if not result: if not result:
return radiusd.RLM_MODULE_REJECT return radiusd.RLM_MODULE_REJECT
@ -179,28 +167,26 @@ def post_auth(data):
nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None)) nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None))
nas_instance = find_nas_from_request(nas) nas_instance = find_nas_from_request(nas)
# Toutes les reuquètes non proxifiées # All non proxified requests
if not nas_instance: if not nas_instance:
logger.info(u"Requete proxifiee, nas inconnu".encode("utf-8")) logger.info("Proxified request, nas unknown")
return radiusd.RLM_MODULE_OK return radiusd.RLM_MODULE_OK
nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first() nas_type = Nas.objects.filter(nas_type=nas_instance.machine_type).first()
if not nas_type: if not nas_type:
logger.info(u"Type de nas non enregistre dans la bdd!".encode("utf-8")) logger.info("This kind of nas is not registered in the database!")
return radiusd.RLM_MODULE_OK return radiusd.RLM_MODULE_OK
mac = data.get("Calling-Station-Id", None) mac = data.get("Calling-Station-Id", None)
# Switch et bornes héritent de machine et peuvent avoir plusieurs # Switchs and access point can have several interfaces
# interfaces filles
nas_machine = nas_instance.machine nas_machine = nas_instance.machine
# Si il s'agit d'un switch # If it is a switchs
if hasattr(nas_machine, "switch"): if hasattr(nas_machine, "switch"):
port = data.get("NAS-Port-Id", data.get("NAS-Port", None)) port = data.get("NAS-Port-Id", data.get("NAS-Port", None))
# Pour les infrastructures possédant des switchs Juniper : # If the switch is part of a stack, calling ip is different from calling switch.
# On vérifie si le switch fait partie d'un stack Juniper
instance_stack = nas_machine.switch.stack instance_stack = nas_machine.switch.stack
if instance_stack: if instance_stack:
# Si c'est le cas, on resélectionne le bon switch dans la stack # If it is a stack, we select the correct switch in the stack
id_stack_member = port.split("-")[1].split("/")[0] id_stack_member = port.split("-")[1].split("/")[0]
nas_machine = ( nas_machine = (
Switch.objects.filter(stack=instance_stack) Switch.objects.filter(stack=instance_stack)
@ -208,22 +194,22 @@ def post_auth(data):
.prefetch_related("interface_set__domain__extension") .prefetch_related("interface_set__domain__extension")
.first() .first()
) )
# On récupère le numéro du port sur l'output de freeradius. # Find the port number from freeradius, works both with HP, Cisco
# La ligne suivante fonctionne pour cisco, HP et Juniper # and juniper output
port = port.split(".")[0].split("/")[-1][-2:] port = port.split(".")[0].split("/")[-1][-2:]
out = decide_vlan_switch(nas_machine, nas_type, port, mac) out = decide_vlan_switch(nas_machine, nas_type, port, mac)
sw_name, room, reason, vlan_id, decision, attributes = out sw_name, room, reason, vlan_id, decision, attributes = out
if decision: if decision:
log_message = "(fil) %s -> %s [%s%s]" % ( log_message = "(wired) %s -> %s [%s%s]" % (
sw_name + u":" + port + u"/" + str(room), sw_name + ":" + port + "/" + str(room),
mac, mac,
vlan_id, vlan_id,
(reason and u": " + reason).encode("utf-8"), (reason and ": " + reason),
) )
logger.info(log_message) logger.info(log_message)
# Filaire # Wired connexion
return ( return (
radiusd.RLM_MODULE_UPDATED, radiusd.RLM_MODULE_UPDATED,
( (
@ -235,10 +221,10 @@ def post_auth(data):
(), (),
) )
else: else:
log_message = "(fil) %s -> %s [Reject:%s]" % ( log_message = "(fil) %s -> %s [Reject %s]" % (
sw_name + u":" + port + u"/" + str(room), sw_name + ":" + port + "/" + str(room),
mac, mac,
(reason and u": " + reason).encode("utf-8"), (reason and ": " + reason),
) )
logger.info(log_message) logger.info(log_message)
@ -251,12 +237,12 @@ def post_auth(data):
# TODO : remove this function # TODO : remove this function
@radius_event @radius_event
def dummy_fun(_): def dummy_fun(_):
"""Do nothing, successfully. (C'est pour avoir un truc à mettre)""" """Do nothing, successfully. """
return radiusd.RLM_MODULE_OK return radiusd.RLM_MODULE_OK
def detach(_=None): def detach(_=None):
"""Appelé lors du déchargement du module (enfin, normalement)""" """Detatch the auth"""
print("*** goodbye from auth.py ***") print("*** goodbye from auth.py ***")
return radiusd.RLM_MODULE_OK return radiusd.RLM_MODULE_OK
@ -275,82 +261,74 @@ def find_nas_from_request(nas_id):
def check_user_machine_and_register(nas_type, username, mac_address): def check_user_machine_and_register(nas_type, username, mac_address):
"""Verifie le username et la mac renseignee. L'enregistre si elle est """Check if username and mac are registered. Register it if unknown.
inconnue. Return the user ntlm password if everything is ok.
Renvoie le mot de passe ntlm de l'user si tout est ok Used for 802.1X auth"""
Utilise pour les authentifications en 802.1X"""
interface = Interface.objects.filter(mac_address=mac_address).first() interface = Interface.objects.filter(mac_address=mac_address).first()
user = User.objects.filter(pseudo__iexact=username).first() user = User.objects.filter(pseudo__iexact=username).first()
if not user: if not user:
return (False, u"User inconnu", "") return (False, "User unknown", "")
if not user.has_access(): if not user.has_access():
return (False, u"Adherent non cotisant", "") return (False, "Invalid connexion (non-contributing user)", "")
if interface: if interface:
if interface.machine.user != user: if interface.machine.user != user:
return ( return (
False, False,
u"Machine enregistree sur le compte d'un autre " "user...", "Mac address registered on another user account",
"", "",
) )
elif not interface.is_active: elif not interface.is_active:
return (False, u"Machine desactivee", "") return (False, "Interface/Machine disabled", "")
elif not interface.ipv4: elif not interface.ipv4:
interface.assign_ipv4() interface.assign_ipv4()
return (True, u"Ok, Reassignation de l'ipv4", user.pwd_ntlm) return (True, "Ok, new ipv4 assignement...", user.pwd_ntlm)
else: else:
return (True, u"Access ok", user.pwd_ntlm) return (True, "Access ok", user.pwd_ntlm)
elif nas_type: elif nas_type:
if nas_type.autocapture_mac: if nas_type.autocapture_mac:
result, reason = user.autoregister_machine(mac_address, nas_type) result, reason = user.autoregister_machine(mac_address, nas_type)
if result: if result:
return (True, u"Access Ok, Capture de la mac...", user.pwd_ntlm) return (True, "Access Ok, Registering mac...", user.pwd_ntlm)
else: else:
return (False, u"Erreur dans le register mac %s" % reason, "") return (False, "Error during mac register %s" % reason, "")
else: else:
return (False, u"Machine inconnue", "") return (False, "Unknown interface/machine", "")
else: else:
return (False, u"Machine inconnue", "") return (False, "Unknown interface/machine", "")
def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address): def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
"""Fonction de placement vlan pour un switch en radius filaire auth par """Function for selecting vlan for a switch with wired mac auth radius.
mac. Several modes are available :
Plusieurs modes : - all modes:
- tous les modes: - unknown NAS : VLAN_OK,
- nas inconnu: VLAN_OK - unknown port : Decision set in Re2o RadiusOption
- port inconnu: Politique définie dans RadiusOption - No radius on this port : VLAN_OK
- pas de radius sur le port: VLAN_OK - force : returns vlan provided by the database
- force: placement sur le vlan indiqué dans la bdd
- mode strict: - mode strict:
- pas de chambre associée: Politique définie - no room : Decision set in Re2o RadiusOption,
dans RadiusOption - no user in this room : Reject,
- pas d'utilisateur dans la chambre : Rejet - user of this room is banned or disable : Reject,
(redirection web si disponible) - user of this room non-contributor and not whitelisted:
- utilisateur de la chambre banni ou désactivé : Rejet Decision set in Re2o RadiusOption
(redirection web si disponible)
- utilisateur de la chambre non cotisant et non whiteslist:
Politique définie dans RadiusOption
- sinon passe à common (ci-dessous)
- mode common : - mode common :
- interface connue (macaddress): - mac-address already registered:
- utilisateur proprio non cotisant / machine désactivée: - related user non contributor / interface disabled:
Politique définie dans RadiusOption Decision set in Re2o RadiusOption
- utilisateur proprio banni : - related user is banned:
Politique définie dans RadiusOption Decision set in Re2o RadiusOption
- user à jour : VLAN_OK (réassignation de l'ipv4 au besoin) - user contributing : VLAN_OK (can assign ipv4 if needed)
- interface inconnue : - unknown interface :
- register mac désactivé : Politique définie - register mac disabled : Decision set in Re2o RadiusOption
dans RadiusOption - register mac enabled : redirect to webauth
- register mac activé: redirection vers webauth
Returns: Returns:
tuple avec : tuple with :
- Nom du switch (str) - Switch name (str)
- chambre (str) - Room (str)
- raison de la cision (str) - Reason of the decision (str)
- vlan_id (int) - vlan_id (int)
- decision (bool) - decision (bool)
- Attributs supplémentaires (attribut:str, operateur:str, valeur:str) - Other Attributs (attribut:str, operator:str, value:str)
""" """
attributes_kwargs = { attributes_kwargs = {
"client_mac": str(mac_address), "client_mac": str(mac_address),
@ -358,12 +336,12 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
} }
# Get port from switch and port number # Get port from switch and port number
extra_log = "" extra_log = ""
# Si le NAS est inconnu, on place sur le vlan defaut # If NAS is unknown, go to default vlan
if not nas_machine: if not nas_machine:
return ( return (
"?", "?",
u"Chambre inconnue", "Unknown room",
u"Nas inconnu", "Unknown NAS",
RadiusOption.get_cached_value("vlan_decision_ok").vlan_id, RadiusOption.get_cached_value("vlan_decision_ok").vlan_id,
True, True,
RadiusOption.get_attributes("ok_attributes", attributes_kwargs), RadiusOption.get_attributes("ok_attributes", attributes_kwargs),
@ -375,14 +353,13 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
attributes_kwargs["switch_ip"] = str(switch.ipv4) attributes_kwargs["switch_ip"] = str(switch.ipv4)
port = Port.objects.filter(switch=switch, port=port_number).first() port = Port.objects.filter(switch=switch, port=port_number).first()
# Si le port est inconnu, on place sur le vlan defaut # If the port is unknwon, go to default vlan
# Aucune information particulière ne permet de déterminer quelle # We don't have enought information to make a better decision
# politique à appliquer sur ce port
if not port: if not port:
return ( return (
sw_name, sw_name,
"Port inconnu", "Unknown port",
u"Port inconnu", "PUnknown port",
getattr( getattr(
RadiusOption.get_cached_value("unknown_port_vlan"), "vlan_id", None RadiusOption.get_cached_value("unknown_port_vlan"), "vlan_id", None
), ),
@ -390,61 +367,57 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
RadiusOption.get_attributes("unknown_port_attributes", attributes_kwargs), RadiusOption.get_attributes("unknown_port_attributes", attributes_kwargs),
) )
# On récupère le profil du port # Retrieve port profile
port_profile = port.get_port_profile port_profile = port.get_port_profile
# Si un vlan a été précisé dans la config du port, # If a vlan is precised in port config, we use it
# on l'utilise pour VLAN_OK
if port_profile.vlan_untagged: if port_profile.vlan_untagged:
DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id) DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id)
extra_log = u"Force sur vlan " + str(DECISION_VLAN) extra_log = "Force sur vlan " + str(DECISION_VLAN)
attributes = () attributes = ()
else: else:
DECISION_VLAN = RadiusOption.get_cached_value("vlan_decision_ok").vlan_id DECISION_VLAN = RadiusOption.get_cached_value("vlan_decision_ok").vlan_id
attributes = RadiusOption.get_attributes("ok_attributes", attributes_kwargs) attributes = RadiusOption.get_attributes("ok_attributes", attributes_kwargs)
# Si le port est désactivé, on rejette la connexion # If the port is disabled in re2o, REJECT
if not port.state: if not port.state:
return (sw_name, port.room, u"Port desactive", None, False, ()) return (sw_name, port.room, "Port disabled", None, False, ())
# Si radius est désactivé, on laisse passer # If radius is disabled, decision is OK
if port_profile.radius_type == "NO": if port_profile.radius_type == "NO":
return ( return (
sw_name, sw_name,
"", "",
u"Pas d'authentification sur ce port" + extra_log, "No Radius auth enabled on this port" + extra_log,
DECISION_VLAN, DECISION_VLAN,
True, True,
attributes, attributes,
) )
# Si le 802.1X est activé sur ce port, cela veut dire que la personne a # If 802.1X is enabled, people has been previously accepted.
# été accept précédemment # Go to the decision vlan
# Par conséquent, on laisse passer sur le bon vlan
if (nas_type.port_access_mode, port_profile.radius_type) == ("802.1X", "802.1X"): if (nas_type.port_access_mode, port_profile.radius_type) == ("802.1X", "802.1X"):
room = port.room or "Chambre/local inconnu" room = port.room or "Room unknown"
return ( return (
sw_name, sw_name,
room, room,
u"Acceptation authentification 802.1X", "Accept authentication 802.1X",
DECISION_VLAN, DECISION_VLAN,
True, True,
attributes, attributes,
) )
# Sinon, cela veut dire qu'on fait de l'auth radius par mac # Otherwise, we are in mac radius.
# Si le port est en mode strict, on vérifie que tous les users # If strict mode is enabled, we check every user related with this port. If
# rattachés à ce port sont bien à jour de cotisation. Sinon on rejette # one user or more is not enabled, we reject to prevent from sharing or
# (anti squattage) # spoofing mac.
# Il n'est pas possible de se connecter sur une prise strict sans adhérent
# à jour de cotis dedans
if port_profile.radius_mode == "STRICT": if port_profile.radius_mode == "STRICT":
room = port.room room = port.room
if not room: if not room:
return ( return (
sw_name, sw_name,
"Inconnue", "Unknown",
u"Chambre inconnue", "Unkwown room",
getattr( getattr(
RadiusOption.get_cached_value("unknown_room_vlan"), "vlan_id", None RadiusOption.get_cached_value("unknown_room_vlan"), "vlan_id", None
), ),
@ -461,7 +434,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
return ( return (
sw_name, sw_name,
room, room,
u"Chambre non cotisante", "Non-contributing room",
getattr( getattr(
RadiusOption.get_cached_value("non_member_vlan"), "vlan_id", None RadiusOption.get_cached_value("non_member_vlan"), "vlan_id", None
), ),
@ -473,7 +446,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
return ( return (
sw_name, sw_name,
room, room,
u"Utilisateur banni ou desactive", "User is banned or disabled",
getattr( getattr(
RadiusOption.get_cached_value("banned_vlan"), "vlan_id", None RadiusOption.get_cached_value("banned_vlan"), "vlan_id", None
), ),
@ -484,7 +457,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
return ( return (
sw_name, sw_name,
room, room,
u"Utilisateur suspendu (mail non confirme)", "User is suspended (mail has not been confirmed)",
getattr( getattr(
RadiusOption.get_cached_value("non_member_vlan"), RadiusOption.get_cached_value("non_member_vlan"),
"vlan_id", "vlan_id",
@ -499,7 +472,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
return ( return (
sw_name, sw_name,
room, room,
u"Utilisateur non cotisant", "Non-contributing member",
getattr( getattr(
RadiusOption.get_cached_value("non_member_vlan"), RadiusOption.get_cached_value("non_member_vlan"),
"vlan_id", "vlan_id",
@ -510,27 +483,27 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
"non_member_attributes", attributes_kwargs "non_member_attributes", attributes_kwargs
), ),
) )
# else: user OK, on passe à la verif MAC # else: user OK, so we check MAC now
# Si on fait de l'auth par mac, on cherche l'interface # If we are authenticating with mac, we look for the interfaces and its mac address
# via sa mac dans la bdd
if port_profile.radius_mode == "COMMON" or port_profile.radius_mode == "STRICT": if port_profile.radius_mode == "COMMON" or port_profile.radius_mode == "STRICT":
# Authentification par mac # Mac auth
interface = ( interface = (
Interface.objects.filter(mac_address=mac_address) Interface.objects.filter(mac_address=mac_address)
.select_related("machine__user") .select_related("machine__user")
.select_related("ipv4") .select_related("ipv4")
.first() .first()
) )
# If mac is unknown,
if not interface: if not interface:
room = port.room room = port.room
# On essaye de register la mac, si l'autocapture a été activée, # We try to register mac, if autocapture is enabled
# on rejette pour faire une redirection web si possible. # Final decision depend on RADIUSOption set in re2o
if nas_type.autocapture_mac: if nas_type.autocapture_mac:
return ( return (
sw_name, sw_name,
room, room,
u"Machine Inconnue", "Unknown mac/interface",
getattr( getattr(
RadiusOption.get_cached_value("unknown_machine_vlan"), RadiusOption.get_cached_value("unknown_machine_vlan"),
"vlan_id", "vlan_id",
@ -542,13 +515,12 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
"unknown_machine_attributes", attributes_kwargs "unknown_machine_attributes", attributes_kwargs
), ),
) )
# Sinon on bascule sur la politique définie dans les options # Otherwise, if autocapture mac is not enabled,
# radius.
else: else:
return ( return (
sw_name, sw_name,
"", "",
u"Machine inconnue", "Unknown mac/interface",
getattr( getattr(
RadiusOption.get_cached_value("unknown_machine_vlan"), RadiusOption.get_cached_value("unknown_machine_vlan"),
"vlan_id", "vlan_id",
@ -561,17 +533,15 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
), ),
) )
# L'interface a été trouvée, on vérifie qu'elle est active, # Mac/Interface is found, check if related user is contributing and ok
# sinon on reject # If needed, set ipv4 to it
# Si elle n'a pas d'ipv4, on lui en met une
# Enfin on laisse passer sur le vlan pertinent
else: else:
room = port.room room = port.room
if interface.machine.user.is_ban(): if interface.machine.user.is_ban():
return ( return (
sw_name, sw_name,
room, room,
u"Adherent banni", "Banned user",
getattr( getattr(
RadiusOption.get_cached_value("banned_vlan"), "vlan_id", None RadiusOption.get_cached_value("banned_vlan"), "vlan_id", None
), ),
@ -582,7 +552,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
return ( return (
sw_name, sw_name,
room, room,
u"Machine non active / adherent non cotisant", "Disabled interface / non-contributing member",
getattr( getattr(
RadiusOption.get_cached_value("non_member_vlan"), RadiusOption.get_cached_value("non_member_vlan"),
"vlan_id", "vlan_id",
@ -593,8 +563,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
"non_member_attributes", attributes_kwargs "non_member_attributes", attributes_kwargs
), ),
) )
# Si on choisi de placer les machines sur le vlan # If settings is set to related interface vlan policy based on interface type:
# correspondant à leur type :
if RadiusOption.get_cached_value("radius_general_policy") == "MACHINE": if RadiusOption.get_cached_value("radius_general_policy") == "MACHINE":
DECISION_VLAN = interface.machine_type.ip_type.vlan.vlan_id DECISION_VLAN = interface.machine_type.ip_type.vlan.vlan_id
if not interface.ipv4: if not interface.ipv4:
@ -602,7 +571,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
return ( return (
sw_name, sw_name,
room, room,
u"Ok, Reassignation de l'ipv4" + extra_log, "Ok, assigning new ipv4" + extra_log,
DECISION_VLAN, DECISION_VLAN,
True, True,
attributes, attributes,
@ -611,7 +580,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address):
return ( return (
sw_name, sw_name,
room, room,
u"Machine OK" + extra_log, "Interface OK" + extra_log,
DECISION_VLAN, DECISION_VLAN,
True, True,
attributes, attributes,

View file

@ -1,33 +0,0 @@
python re2o {
mod_instantiate = auth
func_instantiate = instantiate
# Pour le authorize, c'est auth.py qui fait le tri maintenant
mod_authorize = auth
func_authorize = authorize
# Renseigne le vlan si necessaire
# remplacer par dummy_fun pour ignorer le tagging de vlan
mod_post_auth = auth
func_post_auth = post_auth
# Que faire avant de quitter
mod_detach = auth
func_detach = detach
# Le reste sert à rien
mod_accounting = auth
func_accounting = dummy_fun
mod_pre_proxy = auth
func_pre_proxy = dummy_fun
mod_post_proxy = auth
func_post_proxy = dummy_fun
mod_recv_coa = auth
func_recv_coa = dummy_fun
mod_send_coa = auth
func_send_coa = dummy_fun
}

View file

@ -1,43 +0,0 @@
server radius-filaire{
authorize{
re2o
expiration
logintime
pap
}
authenticate{
Auth-Type PAP{
pap
}
Auth-Type CHAP{
chap
}
Auth-Type MS-CHAP{
mschap
}
digest
unix
eap
}
preacct{
preprocess
acct_unique
suffix
files
}
accounting{
}
session{
}
post-auth{
re2o
exec
}
pre-proxy{
}
post-proxy{
eap
}
}

View file

@ -6,10 +6,10 @@
# rlm_python is called for a section which does not have # rlm_python is called for a section which does not have
# a function defined, it will return NOOP. # a function defined, it will return NOOP.
# #
python re2o { python3 re2o {
module = auth module = auth
python_path = /etc/freeradius/3.0:/usr/lib/python2.7:/usr/lib/python2.7/dist-packages:/usr/local/lib/python2.7/site-packages:/usr/lib/python2.7/lib-dynload:/usr/local/lib/python2.7/dist-packages python_path = /etc/freeradius/3.0:/usr/lib/python3.7:/usr/lib/python3.7/dist-packages:/usr/local/lib/python3.7/site-packages:/usr/lib/python3.7/lib-dynload:/usr/local/lib/python3.7/dist-packages
mod_instantiate = ${.module} mod_instantiate = ${.module}
func_instantiate = instantiate func_instantiate = instantiate

View file

@ -6,7 +6,6 @@ SETTINGS_EXAMPLE_FILE='re2o/settings_local.example.py'
APT_REQ_FILE="apt_requirements.txt" APT_REQ_FILE="apt_requirements.txt"
APT_RADIUS_REQ_FILE="apt_requirements_radius.txt" APT_RADIUS_REQ_FILE="apt_requirements_radius.txt"
PIP_REQ_FILE="pip_requirements.txt" PIP_REQ_FILE="pip_requirements.txt"
PIP_RADIUS_REQ_FILE="pip_requirements_radius.txt"
LDIF_DB_FILE="install_utils/db.ldiff" LDIF_DB_FILE="install_utils/db.ldiff"
LDIF_SCHEMA_FILE="install_utils/schema.ldiff" LDIF_SCHEMA_FILE="install_utils/schema.ldiff"
@ -93,7 +92,7 @@ install_radius_requirements() {
echo "Setting up the required packages ..." echo "Setting up the required packages ..."
cat $APT_RADIUS_REQ_FILE | xargs apt-get -y install cat $APT_RADIUS_REQ_FILE | xargs apt-get -y install
python -m pip install -r $PIP_RADIUS_REQ_FILE python -m pip install -r $PIP_REQ_FILE
echo "Setting up the required packages: Done" echo "Setting up the required packages: Done"
} }

View file

@ -1,3 +0,0 @@
django-bootstrap3
django-macaddress
django-ldapdb==1.3.0