not logged in | [Login]

How to Implement Huntgroups in SQL with FreeRADIUS 2.x

Huntgroups provide a mechanism to group NAS's into groups. Each NAS can be a member of a particular hunt group. When a user authentication request arrives you can then tag the request with the hunt group name the NAS is a member of. This is done by adding the attribute value pair to the list of request pairs. During request processing the Huntgroup-Name attribute can be checked to make decisions about how to handle the request. For example you may want to restrict the authentication mechansim based on the type of NAS.

Traditionally in FreeRADIUS huntgroups were implemented in the preprocess module (rlm_preprocess) which at start up read the configuration file /etc/raddb/huntgroups to associate each NAS with a huntgroup. But what if you want to configure FreeRADIUS to use SQL to store your data for users, groups, NAS's, etc? It would be awkward to have to rely on a flat file for huntgroups when everything else is in SQL. With the introduction of ulang in FreeRADIUS 2.0 it is easy to implement huntgroups using SQL.

These are the following steps necessary. The example uses MySQL as the backend database, but it's easy to adjust for another SQL server.

  1. Create a huntgroup table where we store each NAS and the huntgroup it is a member of, we'll call this table radhuntgroup.

    CREATE TABLE radhuntgroup (
    id int(11) unsigned NOT NULL auto_increment,
    groupname varchar(64) NOT NULL default '',
    nasipaddress varchar(15) NOT NULL default '',
    nasportid varchar(15) default NULL,
    PRIMARY KEY  (id),
    KEY nasipaddress (nasipaddress)
    ) ;
    
  2. Populate the radhuntgroup table with your NAS information. For our example we'll add one NAS whose ip-address is 2.2.2.2 and put it in the huntgroup "foo".

    insert into radhuntgroup (groupname, nasipaddress) values ("foo", "2.2.2.2");
    select * from radhuntgroup;
    +----+-----------+--------------+-----------+
    | id | groupname | nasipaddress | nasportid |
    +----+-----------+--------------+-----------+
    |  1 | foo       | 2.2.2.2      | NULL      | 
    +----+-----------+--------------+-----------+
    
  3. Locate the authorize section in your radiusd.conf or sites-enabled/defaut configuration file and edit it. At the top of the authorize section after the preprocess module insert these lines:

    update request {
    Huntgroup-Name := "%{sql:select groupname from radhuntgroup where nasipaddress=\"%{NAS-IP-Address}\"}"
    }
    

What this does is perform a lookup in the radhuntgroup table using the ip-address as a key to return the huntgroup name. It then adds an attribute/value pair to the request where the name of the attribute is Huntgroup-Name and it's value is whatever was returned from the SQL query. If the query did not find anything then the value is the empty string.

Thats all you need to do. Now let's finsh the example by seeing how you might use the huntgroup information. Suppose you want to assure any user who is in the group FOO-AUTH-ONLY is coming in on a NAS for which FOO-AUTH is a requirement. You could use the group checking feature in the SQL module. Let's start by putting the user Bob in the FOO-AUTH-ONLY group. We do this by adding him in the radusergroup table:

select * from radusergroup;
+----------+----------------+----------+
| username | groupname      | priority |
+----------+----------------+----------+
| Bob      | FOO-AUTH-ONLY  |        0 | 
+----------+----------------+----------+

Then let's define a rule in the radgroupcheck table which says if the user is in the FOO-AUTH-ONLY group then they must have a Huntgroup-Name whose attribute is "foo".

select * from radgroupcheck
+----+----------------+----------------+----+----------+
| id | groupname      | attribute      | op | value    |
+----+----------------+----------------+----+----------+
|  1 | FOO-AUTH-ONLY  | Huntgroup-Name | == | foo      | 
+----+----------------+----------------+----+----------+

Now lets trace through the sequence of events.

  1. A new request arrives, in the request are the following attribute/value pairs (along with other attribute/value pairs)

    User-Name,"Bob"
    NAS-IP-Address,2.2.2.2
    
  2. The authorize section executes and the "update request" we added in step 3 performs a SQL query on the radhuntgroup table. The value 2.2.2.2 is substituted for the variable %{NAS-IP-Address} in the query string. This matches row 1 in our radhuntgroup table and the query returns "foo" as the huntgroup name. Then the request is updated with the attribute/value pair .

  3. Later the SQL modules runs. If group checking is enabled the first thing it does is lookup the user name in the radusergroup. In our example Bob is looked up and the group name FOO-AUTH-ONLY is returned. We now know Bob is in the FOO-AUTH-ONLY group.

  4. Next the sql group check runs. The radgroupcheck table is consulted, for every group the user is a member of an tuple are returned and those are then compared to the attribute/value pairs in the request to see if there is a match by applying the operator to the value(s) found in the request to the value in the radgroupcheck row. In our example the radgroupcheck table has a row whose groupname is FOO-AUTH-ONLY which Bob is a member of, that row has an attribute called Huntgroup-Name. The request also has an attribute named Huntgroup-Name which was added earlier by our huntgroup table query. Because both the request and the radgroupcheck rule have an attribute named Huntgroup-Name their values are then evaluated with the operator in the rule (equality in this instance, e.g. ==) and since the operator test succeeds the radgroupcheck test succeeds.