Dejinta shabakada ee FreeRadius iyada oo loo marayo DHCP

Dejinta shabakada ee FreeRadius iyada oo loo marayo DHCP
Hawshu waxay timid si ay u habayso soo saarista ciwaannada IP-ga macaamiisha. Xaaladaha dhibaatada:

  • Ku siin mayno server gooni ah oo oggolaansho ah - waxaad samayn doontaa 😉
  • Macaamiishu waa inay ku helaan dejinta shabakada DHCP
  • Shabakadu waa kala duwan. Tan waxaa ka mid ah qalabka PON iyo furayaasha caadiga ah ee leh Xulashada 82 la habeeyey iyo saldhigyada WiFi oo leh goobo kulul
  • Haddii xogtu aysan hoos iman mid ka mid ah shuruudaha soo saarista IP, waa inaad ka soo saartaa IP-ga shabakadda "martida"

Dhanka wanaagsan: wali waxaa jira server ku yaal FreeBSD kaas oo "shaqeyn kara", laakiin waa "fog" ;), maaha "sax ahaan shabakadan".

Waxa kale oo jira qalab cajiib ah oo loo yaqaan Mikrotik. Shabakadda guud ee jaantusku waa sidatan:

Dejinta shabakada ee FreeRadius iyada oo loo marayo DHCP

Fikir ka dib, waxaa la go'aansaday in la isticmaalo FreeRadius si loo soo saaro goobaha shabakada macaamiisha. Mabda 'ahaan, nidaamku waa caadi: waxaan awood u siineynaa server-ka DHCP ee Microtick, iyo Radius Client oo ku dul yaal. Waxaan dejineynaa serverka DHCP -> Macmiilka Radius -> Isku xirka serverka Radius.

Uma muuqato mid adag. Laakiin! Shaydaanku waa in tafaasiisha. Kuwaas oo kala ah:

  • Marka la ogolanayo PON OLT iyadoo la isticmaalayo nidaamkan, codsi waxaa loo diraa FreeRadius oo wata Magaca-isticmaalka oo u dhigma cinwaanka MAC ee ciwaanka, Agent-Circuit-Id oo la mid ah MAC PON Onu iyo furaha sirta ah ee maran.
  • Marka laga soo fasaxo furayaasha ikhtiyaarka 82, FreeRadius wuxuu helayaa codsi wata magac-isticmaal madhan oo u dhigma MAC-ga qalabka macaamiisha oo ay ka buuxaan sifooyin dheeri ah Wakiilka-Circuit-Id iyo Agent-Remote-Id ka kooban, siday u kala horreeyaan, mar labaad MAC wareejinta wareejinta iyo deked uu macmiilku ku xidhan yahay.
  • Qaar ka mid ah macaamiisha leh dhibcaha WiFI waxaa lagu oggolaaday hab-maamuuska PAP-CHAP
  • Qaar ka mid ah macaamiisha ka yimid dhibcaha WIFI ayaa loo oggolaaday inay wataan Magaca-isticmaalka oo la mid ah ciwaanka MAC ee barta WIFI, bilaa sir ah.

Sooyaalka taariikheed: waa maxay "Doorashada 82" ee DHCP

Kuwani waa xulashooyin dheeraad ah oo loogu talagalay borotokoolka DHCP ee kuu oggolaanaya inaad wareejiso macluumaad dheeraad ah, tusaale ahaan meelaha Agent-Circuit-Id iyo Agent-Remote-ID. Caadi ahaan waxaa loo isticmaalaa in lagu gudbiyo cinwaanka MAC ee wareejinta gudbinta iyo dekedda uu ku xiran yahay macaamiishu. Xaaladda qalabka PON ama saldhigyada WIFI, goobta Agent-Circuit-Id kuma jiraan macluumaad faa'iido leh (ma jirto deked macmiil ah). Nidaamka guud ee hawlgalka DHCP ee kiiskan waa sida soo socota:

Dejinta shabakada ee FreeRadius iyada oo loo marayo DHCP

Talaabo talaabo qorshahani wuxuu u shaqeeyaa sidatan:

  1. Qalabka isticmaaluhu wuxuu sameeyaa codsiga baahinta DHCP si uu u helo goobaha shabakada
  2. Qalabka (tusaale ahaan, beddelka, WiFi ama saldhigga PON) ee qalabka macaamiisha si toos ah ugu xiran yihiin "hortagga" xirmadan oo beddelaya, soo bandhigaya doorashooyin dheeraad ah Xulashada 82 iyo cinwaanka IP-ga wakiil Relay, oo sii gudbiya in ka badan shabakada.
  3. Adeegga DHCP ayaa aqbalaya codsiga, soo saaraya jawaab wuxuuna u diraa qalabka gudbinta
  4. Qalabka gudbinta wuxuu u gudbiyaa xirmada jawaabta qalabka macaamiisha

Dabcan, dhammaantood si sahlan kuma shaqeeyaan; waxaad u baahan tahay inaad u habayso qalabkaaga shabakada si waafaqsan.

Ku rakibida FreeRadius

Dabcan, tan waxaa lagu gaari karaa goobaha qaabeynta FreeRadius, laakiin way adag tahay oo aan caddayn ... gaar ahaan markaad halkaas tagto ka dib bilaha N iyo "wax walbaa way shaqeeyaan." Sidaa darteed, waxaan go'aansanay inaan ku qorno cutubkayaga ogolaanshaha FreeRadius ee Python. Waxaan ka qaadan doonaa xogta oggolaanshaha xogta MySQL. Ma jirto wax macno ah oo lagu sharraxayo qaab-dhismeedkeeda; si kastaba ha ahaatee, qof kastaa wuxuu u samayn doonaa " naftiisa." Gaar ahaan, waxaan qaatay qaab-dhismeedka lagu bixiyo moduleka sql ee FreeRadius, oo wax yar ka beddelay anigoo ku daraya mac iyo garoonka deked ee macmiil kasta, marka lagu daro erayga sirta ah ee login.

Marka, marka hore, ku rakib FreeRadius:

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

Dejinta, dooro inaad ku rakibto:

Dejinta shabakada ee FreeRadius iyada oo loo marayo DHCP

Waxaan u samaynaa summadaynta moduleka Python (ie "shir"):

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

Aan u rakibno module dheeraad ah Python:

pip install mysql-connector

Habaynta moduleka Python ee FreeRadius, waxaad u baahan tahay inaad qeexdo dariiqyada raadinta moduleka ee doorsoomaha Python_path. Tusaale ahaan waxaan leeyahay kan:

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"

Waxaad ku ogaan kartaa waddooyinka adiga oo bilaabaya turjubaanka Python oo geli amarrada:

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']
>

Haddii aadan tallaabadan qaadin, qoraallada ku qoran Python oo ay bilaabeen FreeRadius ma heli doonaan cutubyada ku qoran soo dejinta. Intaa waxaa dheer, waxaad u baahan tahay inaad ka xumaato hawlaha wacitaanka oggolaanshaha iyo xisaabinta ee goobaha cutubka. Tusaale ahaan, modulekani wuxuu u eg yahay sidan:

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}

}

Qoraalka work.py (iyo kuwa kale oo dhan) waa in lagu dhejiyaa /usr/local/etc/raddb/mods-config/python waxaan haystaa saddex qoraal guud ahaan.

shaqada.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

Sida aad ka arki karto koodhka, waxaan isku dayeynaa inaan aqoonsano macaamiisha iyadoo la adeegsanayo dhammaan hababka la heli karo ee ciwaankiisa MAC ee caanka ah ama Xulashada 82, iyo haddii tani aysan shaqeynin, markaa waxaan soo saareynaa cinwaanka IP-ga ugu da'da weyn ee abid laga isticmaalo "martida". "shabakadda. Waxa hadhay oo dhan waa in lagu habeeyo qoraalka caadiga ah ee ku jira galka goobta karti u leh, si hawlaha lagama maarmaanka ah ee qoraalka Python ay u ruxmi doonaan daqiiqadaha loo qoondeeyay. Dhab ahaantii, way ku filan tahay inaad keento faylka foomka:

Default

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
}
}

Aan isku dayno inaan socodsiino oo aragno waxa soo gala diiwaanka cilladaha:

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

Maxaa kale. Markaad dejinayso FreeRadius, way ku habboon tahay in la tijaabiyo hawlgalkeeda iyadoo la adeegsanayo utility radclient. Tusaale ahaan oggolaanshaha:

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

Ama akoon:

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

Waxaan rabaa in aan kaaga digo in ay tahay wax aan macquul aheyn in la isticmaalo nidaamkan iyo qoraallada "isbeddel la'aan" miisaanka "warshad". Ugu yaraan la dareemi karo:

  • waxaa suurtagal ah in la "been abuur" cinwaanka MAC. Way ku filan tahay macaamiishu inuu diiwaangeliyo MAC qof kale waxaana jiri doona dhibaatooyin
  • macquulka ah ee soo saarida shabakadaha martida waa ka baxsan dhaleeceynta. Xitaa jeeg ma jiro "laga yaabee inay horeba u jireen macaamiil cinwaan IP isku mid ah?"

Kani waa uun "xalka-cutter-cutter" loogu talagalay in uu si gaar ah uga shaqeeyo xaaladahayga, wax kale maaha. Si adag ha u xukumin 😉

Source: www.habr.com

Add a comment