Freeradius, Unifi and Vlans
Freeradius and Unifi
Introduction
When I joined my employer in 2012 i discovered they did something interesting called MAB which allowed the same switch port to put different vlans based on mac address. At the time I thought this was pretty interesting at it would allow me to have a guest vlan on my wireless access points. Having an old Cisco switch I setup something called VMPS using freeradius and a built in module called mac2vlan and I managed to set this up with pretty much no knowledge of radius. I stopped doing this as it really didn't work very well when a clients roamed between access points. Roaming between access points was still a problem so in time bit the bullet and bought expensive pro-consumer unifi access points. These could broadcast multiple sids and use different vlans. Roll on to the near past and IOT has arrived along with horrific security of devices. Spinning up lots of SIDs for each type of device is not possible (a limit of 4 per AP) and is just ugly. I had hoped that WPA2-Enterprise would be the way forward as that supported dynamic vlan assignment however the IOT devices I had were based on ESP32 devices which doesn't support than.
Unifi and "Radius MAC Authentication"
So there I was looking at my unifi controller for the first time in years (I had just migrated it to a new server), and I see an option called "Radius MAC Authentication", previously I had been using the "Mac Address Filter" add an additional layer of security to my main network. Which was ok but I did have to login to the GUI to use it rather than it being a nice flat file. When I started researching this I discovered that not only could this be used as a filter but also it could be used to assign specific vlans to macs.
After some search i discovered Neils Browns post on freeradius and unifi which is a great setting up guide to installation of freeradius. This however only goes as far as setting up each mac in the mod-config/files/authorize file.
The entry below in the authorize file means that the mac address 001122334455 will be assigned vlan 399
001122334455 Cleartext-Password := "001122334455"
Tunnel-Type = 13,
Tunnel-Medium-Type = 6,
Tunnel-Private-Group-Id = 399
Now this is interesting, putting this last in the authorize file results in any mac address not matched, taking the vlan as setup in unifi controller. If it is left out access will be deigned to clients without a match mac address even if they have the correct password. Interesting... this could be useful.
DEFAULT Auth-Type := Accept
Ok so I have a number of problems with this setup, one its ugly as I need to put that tunnel stuff in every entry. Ok I could write a script to do this do this. However the biggest problem is to matter what sid you connect on that mac will always be mabbed to the vlan if you use the same radius server and radius mac authenication on more than one sid. This also does not advance my knowledge of how freeradius configs and put together. How hard can it be, after that mac2vlan thing worked out the box last time.
Knowing what to write
Now this is a BIG problem with writtng for freeradius, there is very little documentation provided by vendors for what they sent and what they are expecting to receive from radius. Try to search and you get documents like this from the unifi web site. Which simply contains how to set things up using their preconfigured freeradius and from the gui. Nothing about what values are in the request set from the access points or the response required to them. Fortunately Neil already told me what I needed to reply, and freeradius can be run in an excellent debug mode, which steps though every line of code (except magic... sorry modules).
freeradius -X
(0) Received Access-Request Id 22 from 192.168.129.11:59341 to 192.168.128.203:1812 length 169
(0) User-Name = "001122334455"
(0) User-Password = "001122334455"
(0) NAS-IP-Address = 192.168.129.11
(0) NAS-Identifier = "554433221100"
(0) Called-Station-Id = "55-44-33-22-11-00:foobar-guest"
(0) NAS-Port-Type = Wireless-802.11
(0) Calling-Station-Id = "00-11-22-33-44-55"
(0) Connect-Info = "CONNECT 11Mbps 802.11b"
(0) Message-Authenticator = 0x0bce844dce1b40df9d5c717423ff1f66
So this is what a request looks like for a unifi access point other access point vendors requests will look slightly different, providing different variable, variable names, or different mac address formats. Unifi allows you to set the mac address format, however that only affects the format of the User-Name and User-Password. Here with start to see what freeradius config is actually all about its converting inputs into a common format processed by modules with is then converted into a particular output.
Variables, modules where is the magic?
I want now to use that mac2vlan thing again, so I thought I would put it into my radius site config and magic should happen, complete fail. Beneath we have the module definition for mac2vlan.
root@radius:/etc/freeradius/3.0/mods-available# cat mac2vlan
# -*- text -*-
#
# $Id$
# A simple file to map a MAC address to a VLAN.
#
# The file should be in the format MAC,VLAN
# the VLAN name cannot have spaces in it, for example:
#
# 00:01:02:03:04:05,VLAN1
# 03:04:05:06:07:08,VLAN2
# ...
#
passwd mac2vlan {
filename = ${modconfdir}/${.:name}/${.:instance}
format = "*VMPS-Mac:=VMPS-VLAN-Name"
delimiter = ","
}
Lets pick apart what we have. Ok I cannot see any code to actually do a search of a file here, so where is the magic done? The simple answer to the magic is it done in C code a module is more like a prototype interface file to some hidden code.
So whats going on with the VMPS-Mac VMPS-VLAN-Name? So no variable passing into modules it uses hard coded variable fields!
Namespaces
My first attempt was to do something like which didn't work at all.
VMPS-MAC:=%{User-Name}
From looking at other code examples I found that the user name should be referenced as %{request:User-Name} and you need to say namespace to put the value in (Future me: I didn't understand there were other namespaces and was just using request from examples I found). After all nothing in the freeradius log references "request".
update request {
VMPS-MAC := "%{request:User-Name}
}
Great now mac2vlan actually does something, next I wanted to assign "VMPS-VLAN-Name" to "Tunnel-Private-Group-Id".
So I guess something like?
WRONG! VMPS-VLAN-Name magically uses the "reply" namespace, great documentation girls. So perhaps this one will work? Nope....
It should have been update reply as this is stuff being sent back to the access point. Ah this explains how the magic worked back with my cisco switch it populated the VMPS-MAC field and expected the VMPS-VLAN-Name back.
update reply {
Tunnel-Private-Group-ID := "%{reply:VMPS-VLAN-Name}
}
However my code still does not work. Ah! Back in the files example
DEFAULT Auth-Type := Accept
So lets set
Noooooo! Apparently there is another namespace called "state" the correct answer children is
update control {
Auth-Type := Accept
}
To be Continued
In part 2 I will be putting together all these lesson learnt in a simple config.
Comments
Post a Comment