ProFTPD: RADIUS


Some sites use the RADIUS protocol for authenticating users. For these sites, there is the mod_radius ProFTPD module.

Common Configurations
First, let's start with the most basic mod_radius configuration, where we want to use the RADIUS server only for validating the user's password:

  <IfModule mod_radius.c>
    AuthOrder mod_radius.c mod_auth_unix.c mod_auth_file.c

    RadiusEngine on
    RadiusAuthServer localhost:1812 testing123 5
    RadiusLog /etc/proftpd/radius.log
  </IfModule>
Here, we have told mod_radius the IP address of the RADIUS server via the RadiusAuthServer directive, which includes the port, shared secret, and default timeout value (in seconds).

Since this configuration only uses the RADIUS server for validating the password, we still need to get the user's UID, GID, home directory, group membership, etc from somewhere. Thus we need the AuthOrder directive to tell proftpd to use the mod_auth_unix and mod_auth_file modules as well.

Using the above configuration, when a client connects and sends the USER and PASS FTP commands, the mod_radius module will send an Access-Request RADIUS packet to the RADIUS server, which will include the following attributes:

  Service-Type 8 (i.e. Authenticate-Only)
  User-Name username
  User-Password password
  NAS-Identifier "ftp"
  NAS-IP-Address (or NAS-IPv6-Address) server-ip-address
  NAS-Port server-port
  NAS-Port-Type 5 (i.e. Virtual)
  Calling-Station-Id client-ip-address
  Acct-Session-Id session-pid
  Message-Authenticator mac
If the RADIUS server responds with an Access-Accept packet, then the login succeeds, and the FTP session is establish. If, on the other hand, the RADIUS server responds with Access-Reject, the login fails. (The mod_radius module does not currently support the Access-Challenge packet type.)

Now, let's examine a slightly more complex configuration, which enables the use of RADIUS accounting:

  <IfModule mod_radius.c>
    AuthOrder mod_radius.c mod_auth_unix.c mod_auth_file.c

    RadiusEngine on
    RadiusAuthServer localhost:1812 testing123 5
    RadiusAcctServer localhost:1813 testing123 5
    RadiusLog /etc/proftpd/radius.log
  </IfModule>
The additional directive here is the RadiusAcctSerer directive, which is quite similar to the RadiusAuthServer directive. The use of the RadiusAcctServer directive instructs mod_radius to use RADIUS accounting.

With this configuration, mod_radius will do the same as before. In addition, once the login has succeeded, mod_radius will send an Accounting-Request packet to the RADIUS accounting server which includes:

  User-Name username
  Acct-Status-Type 1 (i.e. Start)
  Acct-Session-Id session-pid
  Acct-Authentic 1 (i.e. Local)
  Event-Timestamp timestamp
Then, when the client disconnects, mod_radius sends another Accounting-Request packet, this time with a lot of information about the just-ended session:
  User-Name username
  Acct-Status-Type 2 (i.e. Stop)
  Acct-Session-Id session-pid
  Acct-Authentic 1 (i.e. Local)
  Acct-Session-Time session-duration
  Acct-Input-Octets bytes-in
  Acct-Output-Octets bytes-out
  Acct-Terminate-Cause cause
  Event-Timestamp timestamp
  Class class (if provided in Access-Accept)

The above configurations are the most common, as RADIUS is normally used only as way of checking whether a client should be allowed to connect, based on username/password.

Sophisticated Configurations
It is possible to use RADIUS as the sole means of user authentication, rather than just validating passwords. The mod_radius configuration to do so would look like:

  <IfModule mod_radius.c>
    AuthOrder mod_radius.c

    RadiusEngine on
    RadiusAuthServer localhost:1812 testing123 5
    RadiusAcctServer localhost:1813 testing123 5
    RadiusLog /etc/proftpd/radius.log

    # Use RADIUS Vendor-Specific Attributes (VSAs) for user details 
    RadiusVendor Unix 4
    RadiusUserInfo $(10:1000) $(11:1000) $(12:/tmp) $(13:/bin/bash)
    RadiusGroupInfo $(14:users,ftpd) $(15:500,501)
  </IfModule>
The key difference here is the use of the RadiusUserInfo directive. Its appearance within the configuration is what instructs mod_radius to do more than just password validation. The RadiusUserInfo and RadiusGroupInfo directives together tell mod_radius where to find the necessary information about a user, such as the UID, GID, home directory, group membership, etc in the response packets from the RADIUS server.

To let the RADIUS server know that we are expecting it do more than just validate the password, the Access-Request packet will use a different Service-Type attribute. Now the packet will look like:

  Service-Type 1 (i.e. Login)
  User-Name username
  User-Password password
  NAS-Identifier "ftp"
  NAS-IP-Address (or NAS-IPv6-Address) server-ip-address
  NAS-Port server-port
  NAS-Port-Type 5 (i.e. Virtual)
  Calling-Station-Id client-ip-address
  Acct-Session-Id session-pid
  Message-Authenticator mac

Upon receiving the Access-Accept packet, mod_radius will now look for specific attributes, bearing user details, within the packet. What attributes does it look for? Answer: Vendor-Specific Attributes (commonly called "VSAs").

Every VSA is prefixed with a vendor ID, followed by an attribute ID/value which are defined by that vendor. For example, Cisco has a vendor ID of 9, Microsoft has a vendor ID of 311, and "Unix" has a vendor ID of 4. (For the curious, these vendor IDs, per RFC 2865, Section 5.26, come from the IANA Enterprise Numbers registry.)

With this background, we can explain the RadiusUserInfo and RadiusGroupInfo directives in detail. Notice that we tell mod_radius the vendor ID to look for, using the RadiusVendor directive: RadiusVendor Unix 4 The above is actually not necessary; mod_radius will look for VSAs for vendor ID 4 (Unix) by default. It is useful, though, to make it explicitly visible in the configuration.

Let's now see just what the RadiusUserInfo parameters are doing:

  RadiusUserInfo $(10:1000) $(11:1000) $(12:/tmp) $(13:/bin/bash)
In order for proftpd to log a user in, it needs to know that user's UID, GID, home directory, and shell. And the RadiusUserInfo parameters say where to find those values, in that order.

For UIDs, "$(10:1000)" says to look for a vendor-specific attribute ID of 10. If we find such an attribute, use the attribute value as the UID. Otherwise, use 1000 as the UID for the user logging in.

For GIDs, "$(11:1000)" says to look for a vendor-specific attribute ID of 11. If we find such an attribute, use the attribute value as the GID. Otherwise, use 1000 as the GID for the user logging in.

For home directories, "$(12:/tmp)" says to look for a vendor-specific attribute ID of 12. If we find such an attribute, use the attribute value as the home directory. Otherwise, use /tmp as the home directory for the user logging in.

And for the shell, "$(13:/bin/bash)" says to look for a vendor-specific attribute ID of 13. If we find such an attribute, use the attribute value as the shell. Otherwise, use /bin/bash as the shell for the user logging in.

The RadiusGroupInfo directive is very similar: it tells mod_radius which VSAs will contain the group membership, both in terms of group IDs and group names, for the logging in user:

  RadiusGroupInfo $(14:users,ftpd) $(15:500,501)

For group names, "$(14:users,ftpd)" says to look for a vendor-specific attribute ID of 14. If we find such an attribute, use the attribute value as the comma-separated list of supplemental group names. Otherwise, use users,ftpd as the group names for the user logging in.

For group IDs, "$(15:500,501)" says to look for a vendor-specific attribute ID of 15. If we find such an attribute, use the attribute value as the comma-separated list of supplemental group IDs. Otherwise, use 500,501 as the group IDs for the user logging in.

FreeRADIUS Configuration
To help demonstrate how you would configure and use VSAs, I will show the FreeRADIUS configuration that I used for development and testing.

Here is the FreeRADIUS dictionary.unix file I used (slightly modified from the stock dictionary.unix file distributed with FreeRADIUS); this file defines the attributes supported for the "Unix" vendor:

  VENDOR          Unix    4

  BEGIN-VENDOR Unix

  ATTRIBUTE       Unix-User-UID            10      integer
  ATTRIBUTE       Unix-User-GID            11      integer
  ATTRIBUTE       Unix-User-Home           12      string
  ATTRIBUTE       Unix-User-Shell          13      string
  ATTRIBUTE       Unix-User-Group-Names    14      string
  ATTRIBUTE       Unix-User-Group-Ids      15      string

  END-VENDOR Unix
You can see how:
  VENDOR          Unix    4
here corresponds to the mod_radius configuration line:
  RadiusVendor Unix 4

The following attribute IDs are what we use in our mod_radius directives:

  ATTRIBUTE       Unix-User-UID            10      integer
  ATTRIBUTE       Unix-User-GID            11      integer
  ATTRIBUTE       Unix-User-Home           12      string
  ATTRIBUTE       Unix-User-Shell          13      string
which match up with our RadiusUserInfo parameters:
  RadiusUserInfo $(10:1000) $(11:1000) $(12:/tmp) $(13:/bin/bash)

Similarly for the group membership attributes, dictionary.unix has:

  ATTRIBUTE       Unix-User-Group-Names    14      string
  ATTRIBUTE       Unix-User-Group-Ids      15      string
and our RadiusGroupInfo parameters are:
  RadiusGroupInfo $(14:users,ftpd) $(15:500,501)

Note that only the IDs (numbers) for attributes are used in the RADIUS packets sent between clients/servers. The attribute names are to make the configuration and logging more human-readable.

Now, in order to tell FreeRADIUS that we want it to include those VSAs in its Access-Accept packet back to mod_radius, we have to modify the FreeRADIUS users file, like so:

  DEFAULT Auth-Type := System
          Unix-User-UID := 500,
          Unix-User-GID := 500,
          Unix-User-Home := "/home/tj",
          Unix-User-Shell := "/bin/bash",
          Unix-User-Group-Names := "radius,ftpd",
          Unix-User-Group-Ids := "200,501",
          Fall-Through = 1
See the FreeRADIUS documentation for the users file format in order to learn how to configure different UID/GID/home/group values for each user in that file.

Obtaining Quota Information via RADIUS
If you use the mod_quotatab module for quota support in proftpd, and you use the mod_radius module for authentication, then you might also be interesting in getting your quota information from your RADIUS server, much like you can get user details from the RADIUS server.

The mechanism is identical that used for user details, i.e. vendor-specific attributes (VSAs). Assuming that you are using FreeRADIUS, you would add the following to your FreeRADIUS dictionary.unix file:

  ATTRIBUTE       Unix-FTP-Quota-Per-Session      106      string
  ATTRIBUTE       Unix-FTP-Quota-Limit-Type       107      string
  ATTRIBUTE       Unix-FTP-Quota-Bytes-Upload     108      string
  ATTRIBUTE       Unix-FTP-Quota-Bytes-Download   109      string
  ATTRIBUTE       Unix-FTP-Quota-Bytes-Transfer   110      string
  ATTRIBUTE       Unix-FTP-Quota-Files-Upload     111      string
  ATTRIBUTE       Unix-FTP-Quota-Files-Download   112      string
  ATTRIBUTE       Unix-FTP-Quota-Files-Transfer   113      string
and in the FreeRADIUS users file (assuming the above user-related attributes as well):
  DEFAULT Auth-Type := System
          Unix-User-UID := 500,
          Unix-User-GID := 500,
          Unix-User-Home := "/home/tj",
          Unix-User-Shell := "/bin/bash",
          Unix-User-Group-Names := "radius,ftpd",
          Unix-User-Group-Ids := "200,501",
          Unix-FTP-Quota-Per-Session := "false",
          Unix-FTP-Quota-Limit-Type := "soft",
          Unix-FTP-Quota-Bytes-Upload := "1.1",
          Unix-FTP-Quota-Bytes-Download := "2.2",
          Unix-FTP-Quota-Bytes-Transfer := "3.3",
          Unix-FTP-Quota-Files-Upload := "4",
          Unix-FTP-Quota-Files-Download := "5",
          Unix-FTP-Quota-Files-Transfer := "6",
          Fall-Through = 1
and then, to tell mod_radius that it should look for quota-related VSAs in the Access-Accept RADIUS packet, there is the aptly-named RadiusQuotaInfo directive:
  RadiusQuotaInfo $(106:false) $(107:hard) $(108:40.0) $(109:0.0) $(110:0.0) $(111:0) $(112:0) $(113:0)

Frequently Asked Questions
Question: Do I have to configure my RADIUS server to return VSAs in order to use mod_radius?
Answer: No. As shown above, mod_radius is usually used just for validating user credentials.

It is also possible to use only mod_radius for user authentication, without needing VSAs. For example, using a configuration like this will do what you need:

  <IfModule mod_radius.c>
    AuthOrder mod_radius.c

    RadiusEngine on
    RadiusAuthServer localhost:1812 testing123 5
    RadiusAcctServer localhost:1813 testing123 5
    RadiusLog /etc/proftpd/radius.log

    RadiusUserInfo 1000 1000 /tmp /bin/bash
    RadiusGroupInfo ftpd 1000
  </IfModule>
Notice how the RadiusUserInfo and RadiusGroupInfo directives do not use the "$(N:M)" syntax? That means that we are not telling mod_radius what vendor ID and attribute IDs to look for. Instead, we are telling mod_radius to always use the configured UID, GID, home directory, shell, group membership values.

Note that this means that all of your logged-in users will have the exact same UID, GID, and home directory. For some sites, this is ideal. Other sites need to have different UID/GID/homes for each users, and thus they will use the VSA support.

Question: Can I use mod_radius for SFTP connections?
Answer: Yes. However, there are some caveats. The main issue that clients which want to use SSH publickey authentication cannot use RADIUS, since the RADIUS protocol does not define any means of conveying the public key information from the connecting client to the RADIUS server. So only password-based SSH authentication can be supported using mod_radius.