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?

update request {
        Tunnel-Private-Group-ID := "%{request:VMPS-VLAN-Name}
}

WRONG!  VMPS-VLAN-Name magically uses the "reply" namespace, great documentation girls. So perhaps this one will work? Nope....


update request {
        Tunnel-Private-Group-ID := "%{reply:VMPS-VLAN-Name}
}

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 

update reply {
        Auth-Type := Accept
}

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

Popular posts from this blog

First Post