Zokonda pamaneti kuchokera ku FreeRadius kudzera pa DHCP

Zokonda pamaneti kuchokera ku FreeRadius kudzera pa DHCP
Ntchitoyi idafika yokonzekera kuperekedwa kwa ma adilesi a IP kwa olembetsa. Zovuta zavuto:

  • Sitikupatsirani seva yosiyana kuti muvomereze - mupanga 😉
  • Olembetsa ayenera kulandira zokonda pa netiweki kudzera pa DHCP
  • Ma network ndi osiyanasiyana. Izi zikuphatikiza zida za PON ndi masinthidwe anthawi zonse okhala ndi Option 82 yokhazikika ndi maziko a WiFi okhala ndi malo otentha.
  • Ngati zambiri sizikugwera pansi pamikhalidwe yoperekera IP, muyenera kutulutsa IP kuchokera pa netiweki ya "mlendo".

Kumbali yabwino: pali seva pa FreeBSD yomwe imatha "kugwira ntchito", koma ndi "kutali";), osati "pamenepa pa intaneti".

Palinso chipangizo chodabwitsa chotchedwa Mikrotik. Chithunzi cha general network ndi chonchi:

Zokonda pamaneti kuchokera ku FreeRadius kudzera pa DHCP

Pambuyo poganiza, adaganiza zogwiritsa ntchito FreeRadius kuti apereke zokonda pa intaneti kwa olembetsa. M'malo mwake, dongosololi ndilokhazikika: timathandizira seva ya DHCP pa Microtick, ndi Radius Client pa izo. Timakonza seva ya DHCP -> Radius Client -> Kulumikizana kwa seva ya Radius.

Sizikuwoneka zovuta. Koma! Mdierekezi ali mwatsatanetsatane. Izi:

  • Mukavomereza PON OLT pogwiritsa ntchito chiwembuchi, pempho limatumizidwa kwa FreeRadius ndi User-Name lofanana ndi adilesi ya MAC ya mutu, Agent-Circuit-Id yofanana ndi MAC PON Onu ndi mawu achinsinsi opanda kanthu.
  • Ikavomereza ma switch ndi kusankha 82, FreeRadius ilandila pempho lopanda dzina logwiritsa ntchito dzina lofanana ndi MAC ya chipangizo cha olembetsa ndikudzaza ndi zina zowonjezera Agent-Circuit-Id ndi Agent-Remote-Id yomwe ili, motsatana, ndi MAC ya kusintha kwa relay ndi doko komwe wolembetsa amalumikizidwa.
  • Ena olembetsa omwe ali ndi ma point a WiFI amaloledwa kudzera pa ma protocol a PAP-CHAP
  • Ena olembetsa kuchokera ku mfundo za WIFI amaloledwa ndi User-Name yofanana ndi adilesi ya MAC ya WIFI point, popanda mawu achinsinsi.

Mbiri yakale: "Njira 82" mu DHCP ndi chiyani

Izi ndi zina mwazosankha za protocol ya DHCP zomwe zimakulolani kusamutsa zambiri, mwachitsanzo m'minda ya Agent-Circuit-Id ndi Agent-Remote-Id. Amagwiritsidwa ntchito potumiza adilesi ya MAC ya chosinthira cholumikizira ndi doko lomwe wolembetsa amalumikizidwa. Pankhani ya zida za PON kapena masiteshoni a WIFI, gawo la Agent-Circuit-Id liribe chidziwitso chofunikira (palibe doko lolembetsa). Chiwembu chachikulu cha ntchito ya DHCP pankhaniyi ndi motere:

Zokonda pamaneti kuchokera ku FreeRadius kudzera pa DHCP

Pang'onopang'ono ndondomekoyi imagwira ntchito motere:

  1. Zida zogwiritsira ntchito zimapanga pempho la DHCP kuti lipeze zokonda pa intaneti
  2. Chipangizocho (mwachitsanzo, chosinthira, WiFi kapena PON base station) komwe zida zolembetsa zimalumikizidwa mwachindunji "chimadutsa" paketi iyi ndikuisintha, ndikuyambitsa zina zowonjezera Njira 82 ndi adilesi ya IP ya wothandizira kulowamo, ndikuyitumiza mopitilira. network.
  3. Seva ya DHCP imavomereza pempho, imapanga yankho ndikutumiza ku chipangizo chotumizira
  4. Chipangizo cholumikizira chimatumiza paketi yoyankha ku chipangizo cholembetsa

Zachidziwikire, zonsezi sizigwira ntchito mosavuta; muyenera kukonza zida zanu zapaintaneti moyenera.

Kuyika FreeRadius

Zoonadi, izi zikhoza kutheka ndi makonzedwe a FreeRadius, koma ndizovuta komanso zosadziwika ... makamaka mukapita kumeneko pambuyo pa miyezi N ndi "zonse zimagwira ntchito." Chifukwa chake, tidaganiza zolemba gawo lathu lovomerezeka la FreeRadius ku Python. Tidzatenga zidziwitso kuchokera ku database ya MySQL. Palibe chifukwa chofotokozera kapangidwe kake; Komabe, aliyense adzipangira "kwa iwo eni." Makamaka, ndinatenga dongosolo lomwe limaperekedwa ndi sql module ya FreeRadius, ndikusintha pang'ono powonjezera mac ndi doko kwa olembetsa aliyense, kuwonjezera pa mawu achinsinsi.

Chifukwa chake, choyamba, yikani FreeRadius:

cd /usr/ports/net/freeradius3
make config
make
install clean

Pazokonda, sankhani kukhazikitsa:

Zokonda pamaneti kuchokera ku FreeRadius kudzera pa DHCP

Timapanga symlink ku gawo la python (ie "kuyatsa"):

ln -s /usr/local/etc/raddb/mods-available/python /usr/local/etc/raddb/mods-enabled

Tiyeni tiyike gawo lina la python:

pip install mysql-connector

M'makonzedwe a module a python a FreeRadius, muyenera kufotokoza njira zofufuzira zamodule mu python_path variable. Mwachitsanzo ndili ndi izi:

python_path="/usr/local/etc/raddb/mods-config/python:/usr/local/lib/python2.7:/usr/local/lib/python27.zip:/usr/local/lib/python2.7:/usr/local/lib/python2.7/plat-freebsd12:/usr/local/lib/python2.7/lib-tk:/usr/local/lib/python2.7/lib-old:/usr/local/lib/python2.7/lib-dynload:/usr/local/lib/python2.7/site-packages"

Mutha kudziwa njira poyambitsa womasulira wa python ndikulowetsa malamulo:

root@phaeton:/usr/local/etc/raddb/mods-enabled# python
Python 2.7.15 (default, Dec  8 2018, 01:22:25) 
[GCC 4.2.1 Compatible FreeBSD Clang 6.0.1 (tags/RELEASE_601/final 335540)] on freebsd12
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/local/lib/python27.zip', '/usr/local/lib/python2.7', '/usr/local/lib/python2.7/plat-freebsd12', '/usr/local/lib/python2.7/lib-tk', '/usr/local/lib/python2.7/lib-old', '/usr/local/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/site-packages']
>

Ngati simutenga izi, ndiye kuti zolemba zolembedwa mu python ndikukhazikitsidwa ndi FreeRadius sizipeza ma module omwe adalembedwa kuti alowe. Kuphatikiza apo, muyenera kuletsa ntchito zovomerezeka ndi zowerengera pamagawo a module. Mwachitsanzo, gawo ili likuwoneka motere:

python {
    python_path="/usr/local/etc/raddb/mods-config/python:/usr/local/lib/python2.7:/usr/local/lib/python2.7/site-packages:/usr/local/lib/python27.zip:/usr/local/lib/python2.7:/usr/local/lib/python2.7/plat-freebsd12:/usr/local/lib/python2.7/lib-tk:/usr/local/lib/python2.7/lib-old:/usr/local/lib/python2.7/lib-dynload:/usr/local/lib/python2.7/site-packages"
    module = work
    mod_instantiate = ${.module}
    mod_detach = ${.module}

    mod_authorize = ${.module}
    func_authorize = authorize

    mod_authenticate = ${.module}
    func_authenticate = authenticate

    mod_preacct = ${.module}
    func_preacct = preacct

    mod_accounting = ${.module}
    func_accounting = accounting

    mod_checksimul = ${.module}
    mod_pre_proxy = ${.module}
    mod_post_proxy = ${.module}
    mod_post_auth = ${.module}
    mod_recv_coa = ${.module}
    mod_send_coa = ${.module}

}

Zolemba za work.py (ndi zina zonse) ziyenera kuikidwa /usr/local/etc/raddb/mods-config/python Ndili ndi zolemba zitatu zonse.

work.py:

#!/usr/local/bin/python
# coding=utf-8
import radiusd
import func
import sys
from pprint import pprint
mysql_host="localhost"
mysql_username="укацук"
mysql_password="ыукаыукаыук"
mysql_base="ыукаыкуаыу"
def instantiate(p):
print ("*** instantiate ***")
print (p)
# return 0 for success or -1 for failure
def authenticate(p):
print ("*** Аутенфикация!!***")
print (p)
def authorize(p):
radiusd.radlog(radiusd.L_INFO, '*** radlog call in authorize ***')    
conn=func.GetConnectionMysql(mysql_host, mysql_username, mysql_password, mysql_base);
param=func.ConvertArrayToNames(p);
pprint(param)
print ("*** Авторизация ***")
reply = ()
conf = ()
cnt=0
username="";mac="";
# сначала проверяем "как положено", по связке логин/пароль
if ("User-Name" in param) and ("User-Password" in param) :
print ("Вариант авторизации (1): есть логин-пароль")
pprint(param["User-Name"])
pprint(param["User-Password"])
pprint(conn)
print(sys.version_info)
print (radiusd.config)
sql="select radreply.attribute,radreply.value from radcheck inner join radreply on radreply.username=radcheck.username where radcheck.username=%s and radcheck.value=%s"
print(sql)
cursor = conn.cursor(dictionary=True,buffered=True)
cursor.execute(sql,[param["User-Name"], param["User-Password"]]);
row = cursor.fetchone()	
while row is not None:    
cnt=cnt+1
username=row["username"]
reply = reply+((str(row["attribute"]),str(row["value"])), )
row = cursor.fetchone()	          
# вариант, что User-Name - это МАС адрес БС,пароля и порта нет                
if ("User-Name" in param)  and ("User-Password" in param) and (cnt==0):
if param["User-Password"] =='':
if ":" in param["User-Name"]:
pprint(param["User-Name"])            
print ("Вариант авторизации (2): User-Name - это MAC адрес базовой станции, порта и пароля нет")
sql="select radreply.username,radreply.attribute,radreply.value from radcheck inner join radreply on radreply.username=radcheck.username where REPLACE(radcheck.mac,':','') = REPLACE(REPLACE('"+str(param["User-Name"])+"','0x',''),':','') and radcheck.sw_port=''"
print (sql)
cursor = conn.cursor(dictionary=True,buffered=True)
cursor.execute(sql);
row = cursor.fetchone()	
while row is not None:                  
cnt=cnt+1
username=row["username"]
mac=param["User-Name"]
reply = reply+((str(row["attribute"]),str(row["value"])), )
row = cursor.fetchone()	          
if ("Agent-Remote-Id" in param)  and ("User-Password" in param) and (cnt==0):
if param["User-Password"] =='':
pprint(param["Agent-Remote-Id"])            
print ("Вариант авторизации (2.5): Agent-Remote-Id - это MAC адрес PON оборудования")
sql="select radreply.username,radreply.attribute,radreply.value from radcheck inner join radreply on radreply.username=radcheck.username where REPLACE(radcheck.mac,':','') = REPLACE(REPLACE('"+str(param["Agent-Remote-Id"])+"','0x',''),':','') and radcheck.sw_port=''"
print (sql)
cursor = conn.cursor(dictionary=True,buffered=True)
cursor.execute(sql);
row = cursor.fetchone()	
while row is not None:                  
cnt=cnt+1
username=row["username"]
mac=param["User-Name"]
reply = reply+((str(row["attribute"]),str(row["value"])), )
row = cursor.fetchone()	          
#Вариант, что Agent-Remote-Id - это МАС адрес БС,пароля и порта нет и предыдущие варианты поиска IP результата не дали                
if ("Agent-Remote-Id" in param)  and ("User-Password" not in param) and (cnt==0):
pprint(param["Agent-Remote-Id"])            
print ("Вариант авторизации (3): Agent-Remote-Id - МАС базовой станции/пон. Порта в биллинге нет")
sql="select radreply.username,radreply.attribute,radreply.value from radcheck inner join radreply on radreply.username=radcheck.username where REPLACE(radcheck.mac,':','') = REPLACE(REPLACE('"+str(param["Agent-Remote-Id"])+"','0x',''),':','') and radcheck.sw_port=''"
print(sql)
cursor = conn.cursor(dictionary=True,buffered=True)
cursor.execute(sql);
row = cursor.fetchone()	
while row is not None:    
cnt=cnt+1
mac=param["Agent-Remote-Id"]
username=row["username"]
reply = reply+((str(row["attribute"]),str(row["value"])), )
row = cursor.fetchone()	          
#Вариант, что предыдущие попытки результата не дали, но есть Agent-Remote-Id и Agent-Circuit-Id
if ("Agent-Remote-Id" in param)  and ("Agent-Circuit-Id" in param) and (cnt==0):
pprint(param["Agent-Remote-Id"])            
pprint(param["Agent-Circuit-Id"])            
print ("Вариант авторизации (4): авторизация по Agent-Remote-Id и Agent-Circuit-Id, в биллинге есть порт/мак")
sql="select radreply.username,radreply.attribute,radreply.value from radcheck inner join radreply on radreply.username=radcheck.username where upper(radcheck.sw_mac)=upper(REPLACE('"+str(param["Agent-Remote-Id"])+"','0x','')) and upper(radcheck.sw_port)=upper(RIGHT('"+str(param["Agent-Circuit-Id"])+"',2)) and radcheck.sw_port<>''"
print(sql)
cursor = conn.cursor(dictionary=True,buffered=True)
cursor.execute(sql);
row = cursor.fetchone()	
while row is not None:    
cnt=cnt+1
mac=param["Agent-Remote-Id"]
username=row["username"]
reply = reply+((str(row["attribute"]),str(row["value"])), )
row = cursor.fetchone()	          
# если так до сих пор IP не получен, то выдаю иего из гостевой сети..
if cnt==0:      
print ("Ни один из вариантов авторизации не сработал, получаю IP из гостевой сети..")
ip=func.GetGuestNet(conn)      
if ip!="": 
cnt=cnt+1;
reply = reply+(("Framed-IP-Address",str(ip)), )
# если совсем всё плохо, то Reject
if cnt==0:
conf = ( ("Auth-Type", "Reject"), ) 
else:
#если авторизация успешная (есть такой абонент), то запишем историю авторизации
if username!="":
func.InsertToHistory(conn,username,mac, reply);
conf = ( ("Auth-Type", "Accept"), )             
pprint (reply)
conn=None;
return radiusd.RLM_MODULE_OK, reply, conf
def preacct(p):
print ("*** preacct ***")
print (p)
return radiusd.RLM_MODULE_OK
def accounting(p):
print ("*** Аккаунтинг ***")
radiusd.radlog(radiusd.L_INFO, '*** radlog call in accounting (0) ***')  
print (p)
conn=func.GetConnectionMysql(mysql_host, mysql_username, mysql_password, mysql_base);
param=func.ConvertArrayToNames(p);
pprint(param)  
print("Удалим старые сессии (более 20 минут нет аккаунтинга)");
sql="delete from radacct where TIMESTAMPDIFF(minute,acctupdatetime,now())>20"
cursor = conn.cursor(dictionary=True,buffered=True)
cursor.execute(sql);
conn.commit()
print("Обновим/добавим информацию о сессии")
if (("Acct-Unique-Session-Id" in param) and ("User-Name" in param) and ("Framed-IP-Address" in param)):
sql='insert into radacct (radacctid,acctuniqueid,username,framedipaddress,acctstarttime) values (null,"'+str(param['Acct-Unique-Session-Id'])+'","'+str(param['User-Name'])+'","'+str(param['Framed-IP-Address'])+'",now()) ON DUPLICATE KEY update acctupdatetime=now()'
print(sql)
cursor = conn.cursor(dictionary=True,buffered=True)
cursor.execute(sql)
conn.commit()
conn=None;
return radiusd.RLM_MODULE_OK
def pre_proxy(p):
print ("*** pre_proxy ***")
print (p)
return radiusd.RLM_MODULE_OK
def post_proxy(p):
print ("*** post_proxy ***")
print (p)
return radiusd.RLM_MODULE_OK
def post_auth(p):
print ("*** post_auth ***")
print (p)
return radiusd.RLM_MODULE_OK
def recv_coa(p):
print ("*** recv_coa ***")
print (p)
return radiusd.RLM_MODULE_OK
def send_coa(p):
print ("*** send_coa ***")
print (p)
return radiusd.RLM_MODULE_OK
def detach():
print ("*** На этом всё детишечки ***")
return radiusd.RLM_MODULE_OK

func.py:

#!/usr/bin/python2.7
# coding=utf-8
import mysql.connector
from mysql.connector import Error
# Функция возвращает соединение с MySQL
def GetConnectionMysql(mysql_host, mysql_username, mysql_password, mysql_base):    
try:
conn = mysql.connector.connect(host=mysql_host,database=mysql_base,user=mysql_username,password=mysql_password)
if conn.is_connected(): print('---cоединение с БД '+mysql_base+' установлено')
except Error as e:
print("Ошибка: ",e);
exit(1);       
return conn
def ConvertArrayToNames(p):
mass={};
for z in p:
mass[z[0]]=z[1]
return mass
# Функция записывает историю соединения по известным данным
def InsertToHistory(conn,username,mac, reply):
print("--записываю для истории")
repl=ConvertArrayToNames(reply)
if "Framed-IP-Address" in repl:
sql='insert into radpostauth (username,reply,authdate,ip,mac,session_id,comment) values ("'+username+'","Access-Accept",now(),"'+str(repl["Framed-IP-Address"])+'","'+str(mac)+'","","")'
print(sql)
cursor = conn.cursor(dictionary=True,buffered=True)          
cursor.execute(sql);
conn.commit()
# Функция выдает последний по дате выдачи IP адрес из гостевой сети        
def GetGuestNet(conn):
ip="";id=0
sql="select * from guestnet order by dt limit 1"
print (sql)
cursor = conn.cursor(dictionary=True,buffered=True)          
cursor.execute(sql);
row = cursor.fetchone()	
while row is not None:    
ip=row["ip"]
id=row["id"]
row = cursor.fetchone()	          
if id>0:
sql="update guestnet set dt=now() where id="+str(id)
print (sql)
cursor = conn.cursor(dictionary=True,buffered=True)          
cursor.execute(sql);
conn.commit()
return ip         

radiusd.py:

#!/usr/bin/python2.7
# coding=utf-8
# from modules.h
RLM_MODULE_REJECT = 0
RLM_MODULE_FAIL = 1
RLM_MODULE_OK = 2
RLM_MODULE_HANDLED = 3
RLM_MODULE_INVALID = 4
RLM_MODULE_USERLOCK = 5
RLM_MODULE_NOTFOUND = 6
RLM_MODULE_NOOP = 7
RLM_MODULE_UPDATED = 8
RLM_MODULE_NUMCODES = 9
# from log.h
L_AUTH = 2
L_INFO = 3
L_ERR = 4
L_WARN = 5
L_PROXY = 6
L_ACCT = 7
L_DBG = 16
L_DBG_WARN = 17
L_DBG_ERR = 18
L_DBG_WARN_REQ = 19
L_DBG_ERR_REQ = 20
# log function
def radlog(level, msg):
import sys
sys.stdout.write(msg + 'n')
level = level

Monga mukuwonera kuchokera pamakhodi, tikuyesera kuzindikira wolembetsa pogwiritsa ntchito njira zonse zomwe zilipo ndi maadiresi ake a MAC odziwika kapena Option 82 kuphatikiza, ndipo ngati izi sizikugwira ntchito, ndiye kuti timapereka adilesi yakale kwambiri ya IP yomwe idagwiritsidwapo kale ndi "mlendo". "Network. Zonse zomwe zatsala ndikukonza zolemba zosasinthika mufoda yothandizidwa ndi masamba, kotero kuti ntchito zofunikira kuchokera ku python script zidzagwedezeka panthawi yomwe yasankhidwa. M'malo mwake, ndikwanira kubweretsa fayilo ku fomu:

chosasintha

server default {
listen {
type = auth
ipaddr = *
port = 0
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
ipaddr = *
port = 0
type = acct
limit {
}
}
listen {
type = auth
port = 0
limit {
max_connections = 1600
lifetime = 0
idle_timeout = 30
}
}
listen {
ipv6addr = ::
port = 0
type = acct
limit {
}
}
authorize {
python
filter_username
preprocess
expiration
logintime
}
authenticate {
Auth-Type PAP {
pap
python
}
Auth-Type CHAP {
chap
python
}
Auth-Type MS-CHAP {
mschap
python
}
eap
}
preacct {
preprocess
acct_unique
suffix
files
}
accounting {
python
exec
attr_filter.accounting_response
}
session {
}
post-auth {
update {
&reply: += &session-state:
}
exec
remove_reply_message_if_eap
Post-Auth-Type REJECT {
attr_filter.access_reject
eap
remove_reply_message_if_eap
}
Post-Auth-Type Challenge {
}
}
pre-proxy {
}
post-proxy {
eap
}
}

Tiyeni tiyese kuyendetsa ndikuwona zomwe zimalowa mu chipika chowongolera:

/usr/local/etc/rc.d/radiusd debug

China ndi chiyani. Mukakhazikitsa FreeRadius, ndikosavuta kuyesa ntchito yake pogwiritsa ntchito chida cha radclient. Mwachitsanzo chilolezo:

echo "User-Name=4C:5E:0C:2E:7F:15,Agent-Remote-Id=0x9845623a8c98,Agent-Circuit-Id=0x00010006" | radclient -x  127.0.0.1:1812 auth testing123

Kapena akaunti:

echo "User-Name=4C:5E:0C:2E:7F:15,Agent-Remote-Id=0x00030f26054a,Agent-Circuit-Id=0x00010002" | radclient -x  127.0.0.1:1813 acct testing123

Ndikufuna kukuchenjezani kuti ndizosatheka kugwiritsa ntchito chiwembu chotere ndi zolemba "popanda kusintha" pamlingo wa "mafakitale". Zodziwika bwino:

  • ndizotheka "kunama" adilesi ya MAC. Ndikokwanira kuti wolembetsa alembetse MAC ya munthu wina ndipo padzakhala mavuto
  • malingaliro opereka maukonde a alendo sangatsutse. Palibe ngakhale cheke "mwina pali makasitomala omwe ali ndi adilesi ya IP yomweyi?"

Iyi ndi "cookie-cutter solution" yomwe idapangidwa kuti igwire ntchito m'mikhalidwe yanga, palibenso china. Osaweruza mosamalitsa 😉

Source: www.habr.com

Kuwonjezera ndemanga