Socruithe líonra ó FreeRadius trí DHCP

Socruithe líonra ó FreeRadius trí DHCP
Tháinig an tasc chun eisiúint seoltaí IP a shocrú do shíntiúsóirí. Coinníollacha na faidhbe:

  • Ní thabharfaimid freastalaí ar leith duit le húdarú - déanfaidh tú é sin 😉
  • Ní mór do shíntiúsóirí socruithe líonra a fháil trí DHCP
  • Tá an líonra ilchineálach. Áirítear leis seo trealamh PON agus lasca rialta le bunanna Rogha 82 cumraithe agus bunanna wifi le hotspots
  • Mura dtagann na sonraí faoi aon cheann de na coinníollacha chun IP a eisiúint, ní mór duit IP a eisiúint ón líonra “aoi”

Ar an taobh maith: tá freastalaí fós ar FreeBSD atá in ann “oibriú”, ach tá sé “i bhfad ar shiúl” ;), ní “ar dheis ar an líonra seo”.

Tá gléas iontach ar a dtugtar Mikrotik freisin. Is é seo an léaráid líonra ghinearálta:

Socruithe líonra ó FreeRadius trí DHCP

Tar éis roinnt machnaimh, socraíodh FreeRadius a úsáid chun socruithe líonra a eisiúint do shíntiúsóirí. I bprionsabal, is gnách an scéim: cuirimid ar chumas an fhreastalaí DHCP ar Microtick, agus Radius Cliant air. Déanaimid an freastalaí DHCP a chumrú -> Cliant Radius -> Ceangal freastalaí Ga.

Ní cosúil go bhfuil sé deacair. Ach! Tá an diabhal sna sonraí. Eadhon:

  • Nuair a údaraítear PON OLT ag baint úsáide as an scéim seo, seoltar iarratas chuig FreeRadius ar a bhfuil Ainm Úsáideora comhionann le seoladh MAC an chinn, Gníomhaire-Chiorcad-Id comhionann leis an MAC PON Onu agus pasfhocal folamh.
  • Agus é á údarú ó lasca le rogha 82, faigheann FreeRadius iarratas le hAinm Úsáideora folamh atá comhionann le MAC gléas an tsíntiúsóra agus líonta le tréithe breise Gníomhaire-Circuit-Id agus Gníomhaire-Ianaitheanta ina bhfuil, faoi seach, MAC de an lasc sealaíochta agus an port a bhfuil an síntiúsóir ceangailte leis.
  • Tá roinnt síntiúsóirí a bhfuil pointí WiFI acu údaraithe trí phrótacail PAP-CHAP
  • Tá roinnt síntiúsóirí ó phointí WIFI údaraithe le hAinm Úsáideora atá comhionann le seoladh MAC an phointe WIFI, gan focal faire.

Cúlra stairiúil: cad é “Rogha 82” in DHCP

Is roghanna breise iad seo don phrótacal DHCP a ligeann duit faisnéis bhreise a aistriú, mar shampla sna réimsí Gníomhaire-Circuit-Id agus Gníomhaire-Ianaitheanta. De ghnáth úsáidtear é chun seoladh MAC an lasc sealaíochta agus an port a bhfuil an suibscríobhaí ceangailte leis a tharchur. I gcás trealamh PON nó stáisiúin bonn WIFI, níl faisnéis úsáideach sa réimse Gníomhaire-Circuit-Id (níl aon phort suibscríobhaí). Seo a leanas scéim ghinearálta oibríocht DHCP sa chás seo:

Socruithe líonra ó FreeRadius trí DHCP

Céim ar chéim oibríonn an scéim seo mar seo:

  1. Déanann an trealamh úsáideora iarratas craolta DHCP chun socruithe líonra a fháil
  2. Déanann an gléas (mar shampla, lasc, bunstáisiún wifi nó PON) a bhfuil an trealamh suibscríobhaí nasctha go díreach leis “idircheapadh” ar an bpaicéad seo agus athraíonn sé é, ag tabhairt isteach roghanna breise Rogha 82 agus seoladh IP gníomhaire Relay isteach ann, agus tarchuireann sé arís é. an líonra.
  3. Glacann an freastalaí DHCP leis an iarratas, gineann sé freagra agus seolann chuig an ngléas sealaíochta é
  4. Cuireann an gléas sealaíochta an paicéad freagartha ar aghaidh chuig an ngléas suibscríobhaí

Ar ndóigh, ní oibríonn sé go léir chomh héasca sin; ní mór duit do threalamh líonra a chumrú dá réir.

Suiteáil FreeRadius

Ar ndóigh, is féidir é seo a bhaint amach le socruithe cumraíochta FreeRadius, ach tá sé deacair agus doiléir ... go háirithe nuair a théann tú ann tar éis N mhí agus "oibríonn gach rud." Mar sin, shocraigh muid ár modúl údaraithe féin a scríobh do FreeRadius i Python. Tógfaimid sonraí údaraithe ó bhunachar sonraí MySQL. Ní miste cur síos a dhéanamh ar a struchtúr; mar sin féin, déanfaidh gach duine é “dóibh féin.” Go háirithe, ghlac mé an struchtúr a thairgtear leis an modúl sql do FreeRadius, agus d'athraigh mé beagán é trí réimse mac agus port a chur leis do gach síntiúsóir, chomh maith leis an bhfocal faire logáil isteach.

Mar sin, ar dtús, suiteáil FreeRadius:

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

Sna socruithe, roghnaigh a shuiteáil:

Socruithe líonra ó FreeRadius trí DHCP

Déanaimid nasc nasc leis an modúl python (i.e. “cas ar” é):

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

Déanaimis modúl breise a shuiteáil le haghaidh python:

pip install mysql-connector

Sna socruithe modúil python do FreeRadius, ní mór duit na cosáin cuardaigh modúil a shonrú san athróg python_path. Mar shampla tá seo agam:

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"

Is féidir leat na cosáin a fháil amach tríd an teangaire python a sheoladh agus na horduithe a chur isteach:

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

Mura ndéanann tú an chéim seo, ní bhfaighidh scripteanna scríofa i python agus seolta ag FreeRadius na modúil atá liostaithe in allmhairiú. Ina theannta sin, ní mór duit na feidhmeanna maidir le húdarú agus cuntasaíocht a ghlaoch i socruithe an mhodúil a uncomment. Mar shampla, tá cuma ar an modúl seo:

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}

}

Ní mór an script work.py (agus na cinn eile go léir) a chur in /usr/local/etc/raddb/mods-config/python Tá trí script agam san iomlán.

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

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

Mar a fheiceann tú ón gcód, táimid ag iarraidh an suibscríobhaí a aithint ag baint úsáide as gach modh atá ar fáil ag a sheoltaí MAC sínitheoir aitheanta nó teaglaim Rogha 82, agus mura n-oibríonn sé seo, eiseoimid an seoladh IP is sine riamh ón “aoi ” líonra. Níl fágtha ach an script réamhshocraithe a chumrú san fhillteán atá cumasaithe do shuíomhanna, ionas go bpléascfaidh na feidhmeanna riachtanacha ón script python ag na tráthanna ainmnithe. Go deimhin, is leor an comhad a thabhairt chuig an bhfoirm:

réamhshocraithe

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

Déanaimis iarracht é a rith agus féach cad a thagann isteach sa loga dífhabhtaithe:

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

Cad eile. Agus FreeRadius á bhunú, tá sé áisiúil a oibriú a thástáil ag baint úsáide as an bhfóntas radclient. Mar shampla údarú:

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

Nó cuntas:

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

Ba mhaith liom a chur in iúl duit go bhfuil sé fíor dodhéanta a leithéid de scéim agus scripteanna a úsáid “gan athruithe” ar scála “tionsclaíoch”. Ar a laghad faoi deara:

  • is féidir an seoladh MAC a “bhréige”. Is leor don suibscríobhaí MAC duine eile a chlárú agus beidh fadhbanna ann
  • tá an loighic a bhaineann le líonraí aoi a eisiúint níos faide ná cáineadh. Ní fiú seiceáil “b'fhéidir go bhfuil an seoladh IP céanna ag cliaint cheana féin?”

Níl anseo ach “réiteach gearrthóra fianán” atá deartha chun oibriú go sonrach i mo choinníollacha, rud ar bith eile. Ná déan breithiúnas go docht 😉

Foinse: will.com

Add a comment