FreeRADIUS  Documentation for the world's most popular RADIUS Server.

not logged in | [Login]

How to Implement Huntgroups in SQL with FreeRADIUS 2.x/3.x

What are Huntgroups?

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.

Using unlang to emulate huntgroup behaviour in SQL

Traditionally in FreeRADIUS huntgroups were implemented in the preprocess module (rlm_preprocess), which on start up, read the configuration file /etc/raddb/huntgroups to associate each NAS with a huntgroup. This worked well for configurations using flat files, but if your configuration relied heavily on SQL, it was a bit awkward.

With the introduction of ulang in FreeRADIUS 2.0 it is easy to implement huntgroups using SQL XLAT. This example uses MySQL as the backend database, but it's easy to adjust for another SQL server.

Building the table

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)
) ;

Inserting entries

Populate the radhuntgroup table with your NAS information. For our example we'll insert an entry for a NAS whose ip-address is 192.168.0.10 and put it in the huntgroup "private".

INSERT INTO `radhuntgroup` (groupname, nasipaddress) VALUES ("example", "192.168.0.10");

The contents of the table show now look like this SELECT * FROM radhuntgroup; +----+-----------+--------------+-----------+ | id | groupname | nasipaddress | nasportid | +----+-----------+--------------+-----------+ | 1 | foo | 2.2.2.2 | NULL | +----+-----------+--------------+-----------+

Configuring FreeRADIUS

  • Set up your database configuration in sql.conf (if you haven't done so already)
  • Locate the authorize { } section in your radiusd.conf or sites-enabled/defaut configuration.
  • After the preprocess module insert the following update request { Huntgroup-Name := "%{sql:SELECT groupname FROM radhuntgroup WHERE nasipaddress='%{NAS-IP-Address}'}" }

This performs 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. You can check for this using the unlang statement if(Huntgroup-Name == ''){.

More examples

Combining with SQL authorisation

Suppose you want to only allow the group site_a_admins to be used when logging into a NAS' at site_a .

Assuming example_user was already a member of site_a_admins, you would follow the steps below. SELECT * FROM radusergroup; +--------------+---------------+----------+ | username | groupname | priority | +--------------+---------------+----------+ | example_user | site_a_admins | 0 | +---------------+---------------+----------+

  • First add a list of the IP addresses of NAS' at site_a into the radhuntgroup table. INSERT INTO radhuntgroup (groupname, nasipaddress) VALUES ("site_a", "192.168.0.10"); INSERT INTO radhuntgroup (groupname, nasipaddress) VALUES ("site_a", "192.168.0.11"); INSERT INTO radhuntgroup (groupname, nasipaddress) VALUES ("site_a", "192.168.0.12");
  • Now add an entry in the radgroupcheck table which says for the site_a_admins group only matches, when a Huntgroup-Name attribute with a value of site_a exists in the request. @todo insert query example SELECT * FROM radgroupcheck +----+----------------+----------------+----+----------+ | id | groupname | attribute | op | value | +----+----------------+----------------+----+----------+ | 1 | site_a_admins | Huntgroup-Name | == | site_a | +----+----------------+----------------+----+----------+
  • @todo reply table example

  1. A new request arrives, in the request are the following attribute/value pairs (along with other attribute/value pairs) User-Name = "example_user" NAS-IP-Address = 192.168.0.10
  2. The authorize section executes and the "update request" we added performs a SQL query on the radhuntgroup table. The variable %{NAS-IP-Address} is replaced with the value of NAS-IP-Address in the request.
  3. SQL XLAT query runs and matches the first row in our radhuntgroup table and returns site_a as the huntgroup name. The request is then updated with the attribute/value pair Huntgroup-Name = "site_a".
  4. SQL modules runs. If group checking is enabled the first thing it does is lookup the user name in the radusergroup table. In our example example_user is looked up and is found to belong to the group site_a_admins.
  5. SQL group check runs. The radgroupcheck table is consulted; for every group the user is a member of a list of tuples are returned. These tuples are then compared to the attribute/value pairs in the request using the operator specified.
  6. If all the check items match, the radgroupreply table is consulted, and all attributes listed there for the group are added to the reply.