PyRad RADIUS Library
PyRad is a Python library for implementing RADIUS (Remote Authentication Dial-In User Service) clients and servers. It simplifies handling RADIUS packets, attributes, and dictionaries. The current version is 2.5.4, and the project maintains an active release cadence with minor updates and bug fixes.
Warnings
- breaking Python 2.x support has been completely dropped. If you are migrating from an older `pyrad` version (prior to 2.5.0) that supported Python 2, your code will break.
- gotcha Pyrad relies on RADIUS dictionaries (`pyrad.dictionary.Dictionary`) to understand attribute types and names. If you don't explicitly provide a dictionary, it attempts to load from common system paths, which might not always exist or contain the specific attributes you need.
- gotcha Pyrad introduced asynchronous client and server implementations (`client_async`, `server_async`) in version 2.2 for Python 3.5+. These operate differently from their synchronous counterparts (`client`, `server`) and require `asyncio` patterns.
- gotcha When accessing attributes from a `Packet` object, using `packet.get(attribute_name, default_value)` is generally safer than direct dictionary-style access (`packet[attribute_name]`). This is especially true for attributes that might be multi-valued or not present.
- gotcha The Message-Authenticator (Attribute 80) provides integrity protection for RADIUS packets. If used incorrectly or omitted when required by the RADIUS server/client, packets may be rejected.
Install
-
pip install pyrad
Imports
- RadiusClient
import pyrad.client; pyrad.client.RadiusClient()
from pyrad.client import RadiusClient
- RadiusServer
import pyrad.server; pyrad.server.RadiusServer()
from pyrad.server import RadiusServer
- Dictionary
import pyrad.dictionary; pyrad.dictionary.Dictionary()
from pyrad.dictionary import Dictionary
- AccessRequest
from pyrad.packet import AccessRequest
Quickstart
import os
from pyrad.client import RadiusClient
from pyrad.packet import AccessRequest
from pyrad.dictionary import Dictionary, getDictionaryPaths
# Pyrad relies on RADIUS dictionaries for attribute definitions.
# It attempts to find default dictionaries (e.g., in /usr/share/pyrad/dictionaries).
# For robustness, specify a dictionary path or ensure defaults are installed.
# In a real app, you might use Dictionary('/etc/raddb/dictionary') or a custom path.
try:
# Try to load dictionaries from common system paths
radius_dict = Dictionary(getDictionaryPaths())
except FileNotFoundError:
print("Warning: Could not find system RADIUS dictionaries. Using a minimal inline dictionary for example.")
# Fallback to a minimal dictionary for demonstration if no default found.
radius_dict = Dictionary({"RADIUS": [
{"name": "User-Name", "type": "string", "code": 1},
{"name": "User-Password", "type": "string", "code": 2},
{"name": "NAS-IP-Address", "type": "ipv4addr", "code": 4}
]})
# Configure RADIUS server details (use environment variables for security in real apps)
RADIUS_SERVER = os.environ.get('RADIUS_SERVER', '127.0.0.1')
RADIUS_PORT = int(os.environ.get('RADIUS_PORT', '1812'))
RADIUS_SECRET = os.environ.get('RADIUS_SECRET', 'testing123')
try:
# Initialize the RADIUS client
client = RadiusClient(
server=RADIUS_SERVER,
authport=RADIUS_PORT,
secret=RADIUS_SECRET.encode(),
dict=radius_dict
)
# Create an Access-Request packet
request = client.CreateAuthPacket(code=AccessRequest)
request["User-Name"] = "testuser"
request["User-Password"] = "testpassword"
request["NAS-IP-Address"] = "192.168.1.100"
print(f"Sending Access-Request to {RADIUS_SERVER}:{RADIUS_PORT}...")
# Send the packet and wait for a reply
reply = client.SendPacket(request)
if reply:
print(f"Received reply: {reply.code}")
if reply.code == 2: # Access-Accept
print("Authentication successful!")
else:
print("Authentication failed.")
print("Reply attributes:")
for attr_name in reply.keys():
# Using .get() is safer for potentially multi-valued attributes
print(f" {attr_name}: {reply.get(attr_name)}")
else:
print("No reply received from RADIUS server (timeout or network issue).")
except Exception as e:
print(f"An error occurred during RADIUS communication: {e}")