Mac Auth against LDAP`
Hi, I am using FreeRadius v2.2.0 on CentOS 6.4 x86_64. I am trying to adapt Plain Mac-Auth as described at: http://wiki.freeradius.org/guide/Mac-Auth to work work from LDAP. (Note: The server is also used for eduroam and is going to be used for 802.1x too.) My setup follows below. The questions: -------------- 1. Should I also define the value: # access_attr ...for correct results? 2. Can I test this using radtest (since I am not using user/password for this query)? 3. Any other suggestions? -------------- The setup: ldap ldap_macauth { server = "localhost" identity = "uid=binduser,ou=System,dc=example,dc=com" password = "bindpasswd" basedn = "ou=Nodes,dc=example,dc=com" filter = "(macAddress=%{Calling-Station-Id})" start_tls = no dictionary_mapping = ${raddbdir}/ldap.attrmap ldap_connections_number = 2 timeout = 4 timelimit = 3 net_timeout = 1 } and I have a test entry: dn: cn=hostABC,ou=Nodes,dc=example,dc=com cn: hostABC macAddress: 00:24:8b:3c:d1:db objectClass: device objectClass: ieee802Device objectClass: top ou: tech owner: uid=johndoe,ou=people,dc=example,dc=com l: Main Site I have preferred to set: rewrite_calling_station_id { if (Calling-Station-Id =~ /([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})/i){ update request { Calling-Station-Id := "%{tolower:%{1}:%{2}:%{3}:%{4}:%{5}:%{6}}" } } else { noop } } and in sites-enabled/default: authorize { preprocess chap mschap digest suffix eap { ok = return } files ldap_macauth if (!ok) { reject } else { # accept update control { Auth-Type := Accept } } expiration logintime pap } Thanks and regards, Nick
On 14 Aug 2013, at 11:02, Nikolaos Milas <nmilas@noa.gr> wrote:
Hi,
I am using FreeRadius v2.2.0 on CentOS 6.4 x86_64.
I am trying to adapt Plain Mac-Auth as described at: http://wiki.freeradius.org/guide/Mac-Auth to work work from LDAP.
(Note: The server is also used for eduroam and is going to be used for 802.1x too.)
My setup follows below.
The questions: -------------- 1. Should I also define the value: # access_attr ...for correct results?
Sure if you want to have an attribute which enables/disables access?
2. Can I test this using radtest (since I am not using user/password for this query)?
Yes, use just the mac-address as the username and password
3. Any other suggestions?
Nope.
--------------
The setup:
ldap ldap_macauth { server = "localhost" identity = "uid=binduser,ou=System,dc=example,dc=com" password = "bindpasswd" basedn = "ou=Nodes,dc=example,dc=com" filter = "(macAddress=%{Calling-Station-Id})" start_tls = no dictionary_mapping = ${raddbdir}/ldap.attrmap ldap_connections_number = 2 timeout = 4 timelimit = 3 net_timeout = 1 }
and I have a test entry:
dn: cn=hostABC,ou=Nodes,dc=example,dc=com cn: hostABC macAddress: 00:24:8b:3c:d1:db objectClass: device objectClass: ieee802Device objectClass: top ou: tech owner: uid=johndoe,ou=people,dc=example,dc=com l: Main Site
I have preferred to set:
rewrite_calling_station_id { if (Calling-Station-Id =~ /([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})[-:]?([0-9a-f]{2})/i){ update request { Calling-Station-Id := "%{tolower:%{1}:%{2}:%{3}:%{4}:%{5}:%{6}}" } } else { noop } }
That's fine.
and in sites-enabled/default:
authorize { preprocess chap mschap digest suffix
Do you need all these? Are you ever going to be doing chap/mschap/digest in the outer server?
eap { ok = return } files ldap_macauth if (!ok) { reject } else { # accept update control { Auth-Type := Accept } }
Yay for magic indentation.
expiration logintime pap }
-Arran Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team
On 14/8/2013 2:39 μμ, Arran Cudbard-Bell wrote:
and in sites-enabled/default:
authorize { preprocess chap mschap digest suffix Do you need all these? Are you ever going to be doing chap/mschap/digest in the outer server?
First, thanks for the reply. Second, sorry for the late answer but I only now managed to fully test the setup. As for the above methods, they were remnants from the default config, so I just let them there (I am a newbie with FreeRadius). The config now is: server macauth { authorize { preprocess rewrite_calling_station_id ldap_macauth if (!ok) { reject } else { # accept update control { Auth-Type := Accept } } } authenticate { Auth-Type LDAP_MACAUTH { ldap_macauth } } preacct { preprocess acct_unique } accounting { detail exec attr_filter.accounting_response } session { } post-auth { } pre-proxy { } post-proxy { } } Tests went fine and I am able to run MAC-Auth successfully on a Cisco 2960 over FreeRadius with LDAP backend! Thanks FreeRadius people! I have 3 main virtual servers now: Default, eduroam (with an eduroam-inner-tunnel) and macauth, working fine in parallel. I would like to ask some customization-oriented questions (for MAC-Auth): 1. Can we somehow limit a host to connect to only a particular port/NAS device based on data stored in LDAP attributes (or, respectively, in flat files) and reject it otherwise? 2. Can we assign the client to a particular VLAN based on data stored in LDAP attributes (or, respectively, in flat files)? 3. Can we configure in FreeRadius an auto email to an administrator when there is a MAC-auth failure with the associated info (time, MAC Address, NAS device, port)? If the answer to any of the above is yes, any pointers to related docs showing how to configure things (FreeRadius, Cisco Switches) would be appreciated. Please advise. Thanks, Nick
1. Can we somehow limit a host to connect to only a particular port/NAS device based on data stored in LDAP attributes (or, respectively, in flat files) and reject it otherwise?
Yes. See ldap_xlat http://wiki.freeradius.org/modules/Rlm_ldap Use a query that searches for the value of NAS-IP-Address in the user object in a custom attribute. If the query expands to something other than a zero length string, the attribute exists. authorize { if ("%{ldap:<query>}" == '') { reject } }
2. Can we assign the client to a particular VLAN based on data stored in LDAP attributes (or, respectively, in flat files)?
Yes, using ldap.attrmap map an LDAP attribute to Tunnel-Private-Group-ID. You also need to return Tunnel-Type and Tunnel-Medium-Type but they can be static one you know you're assigning a VLAN. http://wiki.freeradius.org/vendor/HP#RFC-3580-(single-untagged-VLAN)-Assignm...
3. Can we configure in FreeRadius an auto email to an administrator when there is a MAC-auth failure with the associated info (time, MAC Address, NAS device, port)?
Yes, use rlm_exec in async mode and call sendmail or something similar. Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team
On 23/8/2013 7:25 μμ, Arran Cudbard-Bell wrote:
See ldap_xlathttp://wiki.freeradius.org/modules/Rlm_ldap
Use a query that searches for the value of NAS-IP-Address in the user object in a custom attribute.
If the query expands to something other than a zero length string, the attribute exists.
authorize { if ("%{ldap:<query>}" == '') { reject } }
Thanks Aran, I'll focus on the 1st part for now. I understand that the value of NAS-IP-Address (CheckItem) can be checked against '%{Packet-Src-IP-Address}'. Right? authorize { if ("%{ldap:<query>}" == '%{Packet-Src-IP-Address}') { # accept update control { Auth-Type := Accept } } else { reject } } Is there a way to also check the port of the NAS being used by the host to connect as well (I guess the NAS should provide this info somehow during auth)? Thanks again, Nick
On 23 Aug 2013, at 18:30, Nikolaos Milas <nmilas@noa.gr> wrote:
On 23/8/2013 7:25 μμ, Arran Cudbard-Bell wrote:
See ldap_xlathttp://wiki.freeradius.org/modules/Rlm_ldap
Use a query that searches for the value of NAS-IP-Address in the user object in a custom attribute.
If the query expands to something other than a zero length string, the attribute exists.
authorize { if ("%{ldap:<query>}" == '') { reject } }
Thanks Aran,
I'll focus on the 1st part for now.
I understand that the value of NAS-IP-Address (CheckItem) can be checked against '%{Packet-Src-IP-Address}'. Right?
You could check they're the same... yes. If you want to retrieve the single authorized NAS a device is allowed to connect to, and then check it against Packet-Src-IP-Address then you could do it with the query below.
authorize { if ("%{ldap:<query>}" == '%{Packet-Src-IP-Address}') { # accept update control { Auth-Type := Accept } } else { reject } }
Is there a way to also check the port of the NAS being used by the host to connect as well (I guess the NAS should provide this info somehow during auth)?
Run freeradius with -X, see what attributes are being sent. It'll either be in NAS-Port or NAS-Port-ID if the NAS is providing that information. -Arran
On 23/8/2013 9:19 μμ, Arran Cudbard-Bell wrote:
It'll either be in NAS-Port or NAS-Port-ID if the NAS is providing that information.
Thanks Arran, It was NAS-Port indeed. Strangely enough, this is not included either in ldap.attrmap or the freeradius schema. Shouldn't it (and other attributes missing from ldap.attrmap and freeradius schema but defined in RFC 2865, like NAS-Port-Type) be included at least in future FreeRadius releases? Or there is a particular reason for which they were not included? In any case, could I include the (desired) NAS-Port value in another (seemingly unused) attribute of the FreeRadius Schema, like radiusHint (which -if I understand right- has a suitable syntax: IA5 String), for which I guess I should also add an entry in ldap.attrmap (because there is no radiusHint attribute mapping therein), like: checkItem NAS-Port radiusHint ...and then I could simply use my *exact current configuration* by simply changing the ldap filter to: filter = "(&(macAddress=%{Calling-Station-Id})(radiusNASIpAddress=%{NAS-IP-Address})(radiusHint=%{NAS-Port}))" ...provided that I am storing the NAS (Cisco switch) IP address in radiusNASIpAddress and radiusHint attributes respectively? Would you agree in using radiusHint attribute for that purpose? If not, any other? (I would like to avoid changing the freeradius schema by adding attributes.) Thanks and regards, Nick
On 24/8/2013 12:00 μμ, Nikolaos Milas wrote:
...and then I could simply use my *exact current configuration* by simply changing the ldap filter to:
filter = "(&(macAddress=%{Calling-Station-Id})(radiusNASIpAddress=%{NAS-IP-Address})(radiusHint=%{NAS-Port}))"
...provided that I am storing the NAS (Cisco switch) IP address in radiusNASIpAddress and radiusHint attributes respectively?
Correction: ...provided that I am storing the NAS (Cisco switch) IP address in radiusNASIpAddress and ++ the NAS Port (to which the host is connecting) in ...radiusHint attributes respectively? Sorry for the confusion, Nick
On 24/8/2013 12:00 μμ, Nikolaos Milas wrote:
...and then I could simply use my *exact current configuration* by simply changing the ldap filter to:
filter = "(&(macAddress=%{Calling-Station-Id})(radiusNASIpAddress=%{NAS-IP-Address})(radiusHint=%{NAS-Port}))"
I tested this and it works. (Yet, please respond to the questions on my earlier mails.) Now, I am thinking of an extended (MAC-Auth) "policy" (rule set) like: Rule 1: If in the LDAP host entry we have included a radiusNASIpAddress value AND a radiusHint value, then authorize based on all, otherwise reject. (No mobility allowed) Rule 2: If in the LDAP host entry we have included only a radiusNASIpAddress value, then authorize based only on it, otherwise reject. (NAS-mobility) Rule 3: If in the LDAP host entry we have included NEITHER a radiusNASIpAddress value NOR a radiusHint value, then authorize based only on the MAC Address. (Full Mobility) QUESTION 1: Is it legitimate to set up the above policy as follows? server macauth { authorize { preprocess rewrite_calling_station_id if (%{ldap_macauth:ldap:///ou=Nodes,dc=example,dc=com?radiusHint?sub?radiusHint=*} and %{ldap_macauth:ldap:///ou=Nodes,dc=example,dc=com?radiusNASIpAddress?sub?radiusNASIpAddress=*}) { ldap_macauth if (ok) { update control { Auth-Type := Accept } } } else if (%{ldap_macauth_NAS_only:ldap:///ou=Nodes,dc=example,dc=com?radiusNASIpAddress?sub?radiusNASIpAddress=*}) { ldap_macauth_NAS_only if (ok) { update control { Auth-Type := Accept } } } else { ldap_macauth_mobility if (ok) { update control { Auth-Type := Accept } } else { reject } } } authenticate { Auth-Type LDAP_MACAUTH { ldap_macauth } } preacct { preprocess acct_unique } accounting { detail exec attr_filter.accounting_response } } ...where the three ldap instances above are identical except the filter which is: ldap_macauth: filter = "(&(macAddress=%{Calling-Station-Id})(radiusNASIpAddress=%{NAS-IP-Address})(radiusHint=%{NAS-Port}))" ldap_macauth_NAS_only: filter = "(&(macAddress=%{Calling-Station-Id})(radiusNASIpAddress=%{NAS-IP-Address}))" ldap_macauth_mobility: filter = "(macAddress=%{Calling-Station-Id})" If the above configuration is legitimate, what should we place in the authenticate {} section ? All ldap_machauth* instances used in authorize {} section should be included? I am confused. Thanks and regards, Nick
...where the three ldap instances above are identical except the filter which is:
ldap_macauth: filter = "(&(macAddress=%{Calling-Station-Id})(radiusNASIpAddress=%{NAS-IP-Address})(radiusHint=%{NAS-Port}))" ldap_macauth_NAS_only: filter = "(&(macAddress=%{Calling-Station-Id})(radiusNASIpAddress=%{NAS-IP-Address}))" ldap_macauth_mobility: filter = "(macAddress=%{Calling-Station-Id})"
No. It's a really inefficient way of doing this. Use generic attribute maps or an update ldap schema to pull the necessary values into control attributes, and then do the comparison in policy language. Otherwise you end up doing multiple LDAP queries which are comparatively extremely slow to anything else you're doing in the policy. Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team
On 26/8/2013 12:15 μμ, Arran Cudbard-Bell wrote:
No. It's a really inefficient way of doing this.
Thanks Arran, Yet, would it be logically/technically correct?
Use generic attribute maps or an update ldap schema to pull the necessary values into control attributes, and then do the comparison in policy language.
Hmm, if I understand right, I could query once a custom attribute with e.g. the value of 0/1/2 depending on the type of mac-auth we would like to do. Thus, two LDAP queries would suffice to check correctly the client in all cases, where in my initial script we would need 3 at best or 4 at worst. The example would be to use a policy like: set_mac_auth_type { update request { auth_type := %{ldap_macauth:ldap:///ou=Nodes,dc=example,dc=com?macauthtype?sub?macAddress=%{Calling-Station-Id}}) } } And finally: server macauth { authorize { preprocess rewrite_calling_station_id set_mac_auth_type if (%{auth_type} = 0) { ldap_macauth if (ok) { update control { Auth-Type := Accept } } else { reject } } else if (%{auth_type} = 1) { ldap_macauth_NAS_only if (ok) { update control { Auth-Type := Accept } } else { reject } } else if (%{auth_type} = 2) { ldap_macauth_mobility if (ok) { update control { Auth-Type := Accept } } else { reject } } else { reject } } ... } Is the above syntax/logic correct? That is what you are suggesting?
Otherwise you end up doing multiple LDAP queries which are comparatively extremely slow to anything else you're doing in the policy.
OK, although we are mirroring the LDAP DIT locally (OpenLDAP Consumer) so LDAP queries are VERY fast, and our FreeRadius load is expected to be low (hosts on campus are less than 1000). Thanks, Nick
On 26 Aug 2013, at 11:39, Nikolaos Milas <nmilas@noa.gr> wrote:
On 26/8/2013 12:15 μμ, Arran Cudbard-Bell wrote:
No. It's a really inefficient way of doing this.
Thanks Arran,
Yet, would it be logically/technically correct?
Sure.
Use generic attribute maps or an update ldap schema to pull the necessary values into control attributes, and then do the comparison in policy language.
Hmm, if I understand right, I could query once a custom attribute with e.g. the value of 0/1/2 depending on the type of mac-auth we would like to do.
Thus, two LDAP queries would suffice to check correctly the client in all cases, where in my initial script we would need 3 at best or 4 at worst.
Unless you are querying different DNs for the different Mac-Auth types then doing this is the wrong way to approach this. the presence of the attributes in the LDAP object to dictate what type of authorisation you're doing. Map the check items: radiusNASIPAddress radiusNASPort (add it to the default schema or use radiusHint instead) to the check items NAS-IP-Address and NAS-Port authorize { preprocess rewrite_calling_station_id ldap if (!ok && !updated) { reject } if (control:NAS-IP-Address) { if (control:NAS-IP-Address != "%{%{NAS-IP-Address}:-%{Packet-Src-IP-Address}}") { reject } if (control:NAS-Port && (control:NAS-Port != "%{%{NAS-Port}:-%{NAS-Port-ID}}")) { reject } } update control { Auth-Type := Accept } } Reject has the same effect as a return statement in the above.
The example would be to use a policy like:
set_mac_auth_type { update request { auth_type := %{ldap_macauth:ldap:///ou=Nodes,dc=example,dc=com?macauthtype?sub?macAddress=%{Calling-Station-Id}}) } }
You may as well use Autz-Type. i.e. map an attribute macautztype to the checkItem autz-type authorize { preprocess rewrite_calling_station_id ldap if (!ok && !updated) { reject } Autz-Type nas { # do stuff } Autz-Type nas_and_port { # do stuff } } You can then check the states of the various other control attributes inside one of those Autz-Type sections. Policy execution will effectively ignore Autz-Type sections the first pass through authorize, and then loop back round and enter one of them. Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team
On 26/8/2013 2:15 μμ, Arran Cudbard-Bell wrote:
Unless you are querying different DNs for the different Mac-Auth types then doing this is the wrong way to approach this.
the presence of the attributes in the LDAP object to dictate what type of authorisation you're doing.
Thanks Arran, I tried and tested all scenarios with your (former) suggestion and it worked flawlessly as: ldap_macauth if (!ok && !updated) { reject } if (control:NAS-IP-Address) { if (control:NAS-IP-Address != "%{NAS-IP-Address}") { reject } if (control:NAS-Port && (control:NAS-Port != "%{NAS-Port}")) { reject } } update control { Auth-Type := Accept } Thanks so much. Correctly using the policy language is not so obvious and it would take me long to figure out. Finally, one finishing touch: Can we use the new DHCP functionality to assign an IP address (stored in the host's LDAP entry) to a correctly authenticated host? -OR- Can we check the IP address being used by the authenticated host, compare it against a stored IP Address in the host's LDAP entry, and deny access if there is a mismatch? Best regards, Nick
On 24 Aug 2013, at 10:00, Nikolaos Milas <nmilas@noa.gr> wrote:
On 23/8/2013 9:19 μμ, Arran Cudbard-Bell wrote:
It'll either be in NAS-Port or NAS-Port-ID if the NAS is providing that information.
Thanks Arran,
It was NAS-Port indeed. Strangely enough, this is not included either in ldap.attrmap or the freeradius schema. Shouldn't it (and other attributes missing from ldap.attrmap and freeradius schema but defined in RFC 2865, like NAS-Port-Type) be included at least in future FreeRadius releases? Or there is a particular reason for which they were not included?
No, they should not be included in future releases. It is inefficient to check for the presence of hundreds of attributes in the retrieved object. The generic attribute format supported in both 2.0.0 and 3.0.0 allows you to map any attribute present in the FreeRADIUS dictionary, and even specify the operator used to add them to the various lists. You can of course, also use generic attributes as part of filters.
In any case, could I include the (desired) NAS-Port value in another (seemingly unused) attribute of the FreeRadius Schema, like radiusHint (which -if I understand right- has a suitable syntax: IA5 String), for which I guess I should also add an entry in ldap.attrmap (because there is no radiusHint attribute mapping therein), like:
checkItem NAS-Port radiusHint
Either update the schema for your installation, or use the generic attribute mapping and do the comparisons server side. Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team
participants (2)
-
Arran Cudbard-Bell -
Nikolaos Milas