杰瑞科技汇

Python radius agent如何实现?

Of course! Let's dive into creating a Python RADIUS agent. This is a fantastic project that involves network protocols, authentication, and security.

Python radius agent如何实现?-图1
(图片来源网络,侵删)

First, let's clarify the terminology. A "RADIUS agent" can mean two main things:

  1. A RADIUS Client: This is the most common use case. It's a device or application that sends authentication requests to a RADIUS server. For example, a VPN server, a Wi-Fi access point, or a network switch acting as a client to authenticate users against a central RADIUS server.
  2. A RADIUS Proxy / Server: This is an intermediary that receives requests from clients and then forwards them to another RADIUS server (or processes them itself). This is more complex and is what a full-fledged RADIUS server like FreeRADIUS does.

In this guide, we will focus on building a RADIUS Client in Python. This is the most practical and common task for developers.

Core Concepts

  • RADIUS (Remote Authentication Dial-In User Service): A networking protocol that provides centralized authentication, authorization, and accounting (AAA) management for users who connect and use a network service.
  • Packet Flow:
    1. Client (Python App) sends a Access-Request packet to the RADIUS Server.
    2. Server validates the user credentials.
    3. Server sends back one of two responses:
      • Access-Accept: Authentication successful.
      • Access-Reject: Authentication failed.
  • Key Attributes:
    • User-Name: The username of the user being authenticated.
    • User-Password: The user's password (which is encrypted using a shared secret).
    • NAS-IP-Address: The IP address of the Network Access Server (our Python client).
    • NAS-Port: The port number on the NAS.
  • Ports:
    • 1812 (Authentication): The standard port for Access-Request and Access-Accept/Reject packets.
    • 1813 (Accounting): The standard port for accounting records (we won't cover this in the basic example).

Step 1: Prerequisites

You need a RADIUS server to test against. The most popular and powerful one is FreeRADIUS.

  1. Install FreeRADIUS:

    Python radius agent如何实现?-图2
    (图片来源网络,侵删)
    • On Ubuntu/Debian: sudo apt-get install freeradius freeradius-utils
    • On CentOS/RHEL: sudo yum install freeradius freeradius-utils
  2. Configure FreeRADIUS for Testing:

    • Edit the default client configuration file: sudo nano /etc/freeradius/3.0/clients.conf
    • Add a client definition for your Python script's machine. Replace 0.0.1 with the actual IP of the machine running your Python script if it's not the same as the server.
    # /etc/freeradius/3.0/clients.conf
    client localhost {
        ipaddr = 127.0.0.1
        secret = testing123 # This is the shared secret
        require_message_authenticator = no
    }
  3. Configure a Test User:

    • Edit the users file: sudo nano /etc/freeradius/3.0/users
    • Add a simple test user at the bottom. The line Auth-Type := Local tells FreeRADIUS to check the local users file for the password.
    # /etc/freeradius/3.0/users
    # Test user
    testuser        Cleartext-Password := "supersecret"

    Note: Cleartext-Password is only for testing. In production, use hashed passwords like MD5-Password.

  4. Restart FreeRADIUS:

    Python radius agent如何实现?-图3
    (图片来源网络,侵删)
    • sudo systemctl restart freeradius

Step 2: The Python RADIUS Client

We will use the excellent pyrad library, which is the de-facto standard for RADIUS in Python.

  1. Install pyrad:

    pip install pyrad
  2. Write the Client Script (radius_client.py):

    This script will:

    • Connect to the FreeRADIUS server.
    • Create an Access-Request packet.
    • Set the User-Name and User-Password.
    • Send the packet and print the server's response.
    import pyrad.client
    import pyrad.packet
    from pyrad.dictionary import Dictionary
    # --- Configuration ---
    # IP address of your RADIUS server
    SERVER = "127.0.0.1"
    # Port for authentication
    PORT = 1812
    # The shared secret defined in clients.conf
    SECRET = b"testing123"
    # The path to the RADIUS dictionary file
    # This file defines all the possible RADIUS attributes
    DICTIONARY = "/etc/freeradius/3.0/dictionary"
    def authenticate(username, password):
        """
        Authenticates a user against a RADIUS server.
        """
        try:
            # 1. Create a client object
            client = pyrad.client.Client(
                server=SERVER,
                authport=PORT,
                secret=SECRET,
                dict=Dictionary(DICTIONARY)
            )
            # 2. Create an Access-Request packet
            request = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
                                             User_Name=username,
                                             User_Password=password)
            # 3. Add the NAS-IP-Address attribute (optional but good practice)
            request["NAS-IP-Address"] = "127.0.0.1"
            # 4. Send the request and receive the response
            print(f"Sending Access-Request for user '{username}'...")
            reply = client.SendPacket(request)
            # 5. Process the response
            if reply.code == pyrad.packet.AccessAccept:
                print("-> Authentication ACCEPTED")
                return True
            elif reply.code == pyrad.packet.AccessReject:
                print("-> Authentication REJECTED")
                return False
            else:
                print(f"-> Unexpected response code: {reply.code}")
                print(f"-> Reply: {reply.ReplyPacket()}")
                return False
        except Exception as e:
            print(f"An error occurred: {e}")
            return False
    if __name__ == "__main__":
        # --- Test Cases ---
        print("--- Test Case 1: Correct Credentials ---")
        authenticate("testuser", "supersecret")
        print("\n--- Test Case 2: Wrong Password ---")
        authenticate("testuser", "wrongpassword")
        print("\n--- Test Case 3: Unknown User ---")
        authenticate("unknownuser", "anypassword")

Step 3: Run and Test

  1. Make sure FreeRADIUS is running.
  2. Save the code above as radius_client.py.
  3. Run the script from your terminal:
    python3 radius_client.py

Expected Output:

You should see output similar to this, confirming that your Python client is successfully communicating with the FreeRADIUS server.

--- Test Case 1: Correct Credentials ---
Sending Access-Request for user 'testuser'...
-> Authentication ACCEPTED
--- Test Case 2: Wrong Password ---
Sending Access-Request for user 'testuser'...
-> Authentication REJECTED
--- Test Case 3: Unknown User ---
Sending Access-Request for user 'unknownuser'...
-> Authentication REJECTED

If you check the FreeRADIUS logs (/var/log/freeradius/radius.log), you will see the incoming requests and how they were processed.


Advanced Topics & Best Practices

  1. Password Encryption (CHAP): The example above uses Cleartext-Password for simplicity. In a real-world scenario, you must not send passwords in cleartext. RADIUS uses a shared secret to encrypt the password.

    • The pyrad library handles this automatically. When you set User_Password, it encrypts the password using the client.secret before sending it.
    • You can also use more advanced methods like CHAP (Challenge-Handshake Authentication Protocol) for even better security. pyrad supports this via the CHAP-Password attribute.
  2. Handling Timeouts and Retries: Network connections can fail. A robust client should handle connection errors and implement a retry mechanism.

    # Inside the authenticate function, around the SendPacket call
    max_retries = 3
    for i in range(max_retries):
        try:
            reply = client.SendPacket(request)
            # ... process reply ...
            break # Success, exit retry loop
        except pyrad.client.TimeoutError:
            print(f"Timeout, retrying ({i+1}/{max_retries})...")
            if i == max_retries - 1:
                print("-> Max retries reached. Authentication FAILED.")
                return False
        except Exception as e:
            print(f"An unexpected error occurred: {e}")
            return False
  3. Error Handling: Always wrap your RADIUS logic in try...except blocks to catch network issues, malformed packets, or other exceptions.

  4. Logging: Instead of using print() for debugging, use Python's built-in logging module. This allows you to control log levels (DEBUG, INFO, ERROR) and output to files.

  5. Using a Library vs. Implementing from Scratch: For any serious application, always use a library like pyrad. Implementing the RADIUS protocol from scratch is extremely complex due to its packet structure, attribute handling, and encryption details. pyrad abstracts all of this away for you.

分享:
扫描分享到社交APP
上一篇
下一篇