近日有需求要实现一个RADIUS服务器。因为需要灵活的控制属性下发。所以不打算使用FREERADIUS,想要自己实现一个。因自身条件所限,我选择的语言为python,平台为WINDWOS
2003 SERVER。
一、首先要安装几个模块
1、twisted:实现UDP服务器框架
2、pywin32 :这个主要是为了在支持IOCP,提高系统吞吐效率
3、pyrad :用python实现的对RADISU的支持
二、准备一下字典文件
我直接使用了freeradius的字典文件,由于我用到华为的设备,我把其它的INCLUDE都删除,只把华为的厂商自定义属性直接加到dictionary文件里面
三、编写代码
pyrad里面有个实现RADIUS服务器的示例,但是在linux平台上的。有个WINDOWS平台的示例,但非常不完整,根本无法工作。但这毕竟给了我参考的东西,根据这两个示例整合一下。
#-* -coding: gbk -* -
'''
Created on 2014-10-16
@author: qh
'''
from twisted.internet import iocpreactor as iocpreactor
try:
iocpreactor.install()
except Exception, e:
print "iocp
install failed:%s" % str(e)
from twisted.internet import reactor
from twisted.internet import protocol
from twisted.python import log
import sys
import traceback
from pyrad import dictionary
from pyrad import host
from pyrad import packet
from pyrad.client import Client
import pyrad
import socket
import random
import time
import binascii
import traceback
class PacketError(Exception):
"""Exception
class for bogus packets
PacketError exceptions are only used inside the Server class
to
abort
processing of a packet.
"""
#RADUYS是一个基类,认证和计帐都是基于这个基类的
class RADIUS(host.Host, protocol.DatagramProtocol):
def
__init__(self, hosts={},key='',
dict=dictionary.Dictionary()):
host.Host.__init__(self, dict=dict)
self.hosts =
hosts
#hosts是一个服务器地址列表,其实就是你RADIUS服务器监听的地址
self.key=key #radius的KEY
self.mysrv=Client(server="1.1.1.1", #这是另一台RADIUS
server,我们要把认证和计费请求发给它
secret="xxxxxxx",
dict=dict)
def
processPacket(self, pkt):
pass
def
createPacket(self, **kwargs):
#这个函数子类一定要实现,进行实际的业务处理
print "error"
raise NotImplementedError('Attempted to use a pure base
class')
def
CreateReplyPacket(self, pkt, **attributes):
reply = pkt.CreateReply(**attributes)
reply.source = pkt.source
return reply
def
datagramReceived(self, datagram, (host, port)):
#这个是用来进行实际报文接收和发送的函数
try:
#print "create packet"
pkt = self.createPacket(packet=datagram)
except packet.PacketError as err:
log.msg('Dropping invalid packet: ' + str(err))
return
#if host not in self.hosts:
#
log.msg('Dropping packet from unknown host ' + host)
# return
pkt.source = (host, port)
try:
reply=self.processPacket(pkt)
except PacketError as err:
log.msg('Dropping packet from %s: %s' % (host, str(err)))
try:
self.transport.write(reply.ReplyPacket(), (host, port))
except Exception, e:
log.msg("transport.write:%s" % str(e))
traceback.print_exc()
#这是用来进行认证的类,我们这个实现比较简单,接到认证请求后,改变或增减一些属性后,转发给另一个radius
SERVER,
class RADIUSAccess(RADIUS):
def
createPacket(self, **kwargs):
#print "create auth packet"
return self.CreateAuthPacket(**kwargs)
def
checkUser(self,myattrb,userName,userPass,myreply): #这个函数用于我们自己对RADIUS属性进行处理
mypos=userName.find('@')
myuserName=userName
if mypos>0:
myuserName=userName[:mypos]
req=self.mysrv.CreateAuthPacket(code=packet.AccessRequest,
User_Name=myuserName
)
req["NAS-IP-Address"]
= "a.b.c.d" #转发时,我们是作为RADIUS客户端的,所以这个地址应该设置为我们RADIUS服务器的地址
req["User-Password"]=req.PwCrypt(userPass)
req["NAS-Identifier"]
= "qh"
for mykey in myattrb:
#print mykey,myattrb[mykey]
req[mykey]=myattrb[mykey]
msg='unknow'
try:
#print "Sending authentication request"
reply=self.mysrv.SendPacket(req)
msg=reply['Reply-Message'][0]
except pyrad.client.Timeout:
print "RADIUS server does not reply"
return False
except socket.error, error:
print "Network error: " + error[1]