Splitting strings
Goal: Explore uses of regular expressions and subcapture groups Time: 10-20 minutes
File:
- policy.d/*
man page: unlang
documentation page(s):
For this tutorial you should start with an empty authorization section:
recv Access-Request {
# We'll include our policy in here
}
in the virtual server you’re using to process requests.
Common control attributes
Attributes in the control list can control the behaviour of the server. Commonly used control attributes are:
-
control.Auth-Typespecifies theauthenticate <name> {}section to run
Regular expressions are an extremely powerful tool in the 'unlang' policy language. They provide both validation capabilities, allowing users to check the format of incoming attributes, and substring extraction (via capture groups).
If you’ve completed the Proxy exercise you’ll have used the suffix module to
split an incoming User-Name value into its components and setup the request for
proxying.
Task:
Create an unlang version of "suffix" that splits an incoming User-Name into two
components on the "@" separator.
The first component should be written to the request.Stripped-User-Name attribute
and the second component should be written to the control.Stripped-User-Domain
attribute.
Use bob@realm1.com and bob@realm2.com to test your new policy to ensure it works
as expected.
If you’ve completed the Proxy tutorial and have test realms setup, modify the
policy code you have just written to proxy the request to the realm specified
in the User-Name attribute.
Step 1 – Basic splitting policy
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}"
}
}
}
Add the above created policy into sites-available/default:
server default {
recv Access-Request {
realm_split
files
}
}
Also make sure our testing user "bob" exists in mods-config/files/authorize:
bob Password.Cleartext := "hello"
Then execute the following commands to test this configuration:
echo 'User-Name = "bob@realm1.com", User-Password = "hello"' | radclient -x 127.0.0.1 auth testing123
Expected debug (relevant lines):
(0) recv Access-Request {
(0) policy realm-split {
(0) if (request.User-Name =~ /^([^@]+)@(.+)$/) {
(0) | =~
(0) | request.User-Name
(0) | %{request.User-Name}
(0) | --> bob@realm1.com
(0) | ({bob@realm1.com} =~ (null))
(0) | --> true
(0) | %regex.match(1)
(0) | %regex.match(1)
(0) | regex.match
(0) | %regex.match({1})
(0) | --> bob
(0) request.Stripped-User-Name := "bob"
(0) | %regex.match(2)
(0) | %regex.match(2)
(0) | regex.match
(0) | %regex.match({2})
(0) | --> realm1.com
(0) control.Stripped-User-Domain := "realm1.com"
(0) | %{request.Stripped-User-Name}
(0) | --> bob
(0) | %{control.Stripped-User-Domain}
(0) | --> realm1.com
(0) reply += {
(0) Reply-Message = "User: bob"
(0) Reply-Message = "Domain: realm1.com"
(0) }
(0) } # if (request.User-Name =~ /^([^@]+)@(.+)$/) (...)
(0) } # policy realm-split (...)
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: realm1.com"
If you want to test above scenario with different username "alice" you need to add that user into mods-config/files/authorize, after that you can test that
user using the command below:
echo 'User-Name = "alice@abc.com", User-Password = "hello"' | radclient -x 127.0.0.1 auth testing123
Expected output:
Reply-Message = "User: alice"
Reply-Message = "Domain: abc.com"
If we want to test the above scenario with a different domain, such as bob@realm2.com, use the following command:
echo 'User-Name = "bob@realm2.com", User-Password = "hello"' | radclient -x 127.0.0.1 auth testing123
Expected output:
Reply-Message = "User: bob"
Reply-Message = "Domain: realm2.com"
Questions
-
Regular expressions can contain attribute expansions. Given that all supported regular expression libraries support pre-compilation of expressions, why would regular expressions containing expansions be avoided when the server being deployed will be under heavy load?
-
Why might you want to re-implement functionality offered by modules in unlang?
-
What is an advantage of using expression based string splitting over the suffix module?