FreeRADIUS InkBridge

Policies

Goal: Create and use policies to abstract and reuse business logic in FreeRADIUS.

Time: 10-20 minutes

Files / Directories:

  • policy.d/* (policy.d where we create and define policies)

  • sites-available/default (virtual server where we call the policy)

Preparation

Look through the existing files in policy.d/ and review other documentation sections (especially the unlang reference, conditions, and regular expressions) to become familiar with:

  • Policy syntax

  • Attribute references (request., control., reply., etc.)

  • Regular expression matching in if conditions

  • Common update operators (:=, +=, etc.)

Policies are named blocks of unlang code that can be called almost anywhere a module can be called.

Basic structure of a policy:

policy_name {
    # unlang statements here
}

Example:

You should have already created a policy named realm-split in the Splitting Strings tutorial.

File: policy.d/realm-split

realm-split {
    # Check if User-Name exists and contains an "@" symbol
    if (request.User-Name =~ /^([^@]+)@(.+)$/) {

        # First capture group - username part (before @)
        request.Stripped-User-Name := %regex.match(1)

        # Second capture group - domain/realm part (after @)
        control.Stripped-User-Domain := %regex.match(2)

        # Add reply attributes to validate the split worked correctly
        # Using Reply-Message to show both values (visible in reply)
        reply += {
            Reply-Message = "User: %{request.Stripped-User-Name}"
            Reply-Message = "Domain: %{control.Stripped-User-Domain}"
        }
    }
}

Task: Call this policy at the very beginning of the recv Access-Request section in the default virtual server.

Open the file:

$ vi sites-available/default

Add the policy call near the top of the authorize section:

recv Access-Request {
    realm-split
    # ... rest of the section (chap, mschap, digest, files, etc.) ...
}

Placing it early ensures the User-Name is stripped before any local authorization modules (like files, sql, ldap, etc.) access it.

Testing

Make sure the testing user "bob" exists in mods-config/files/authorize:

Run the server in debug mode:

$ radiusd -X

Send an Access-Request with User-Name: bob@example.com

echo 'User-Name = "bob@example.com", User-Password = "hello"' | radclient -x 127.0.0.1 auth testing123

Expected output:

Received Access-Accept Id 246 from 127.0.0.1:1812 to 0.0.0.0:56242 via lo length 74
        Message-Authenticator = 0x1a82a50a6973affe690efe4146e6581e
        Reply-Message = "User: bob"
        Reply-Message = "Domain: example.com"
        User-Name = "bob"

Questions

  1. What are the advantages of using the policy language over interpreted language modules?

  2. What are the main differences between a policy and a function in other languages?