Freeradius replacing Cisco ACS in an Active Directory Environment (Wifi PEAP+MSCHAP and other uses)

George Shearer george at
Tue Feb 2 21:00:28 CET 2010

Ugh. Please ignore my previous post to the list, gmail 'plain text' mode ate
most of the message.

All, this is my attempt at giving back to the freeradius community. Maybe
others will find my configuration useful in their efforts.

I'm a network guy, and I do quite a bit of consulting work for
various companies. I have a customer in particular who (prior to this)
wasusing a very out-of-date Cisco ACS Server, and couldn't afford
the renewal/licensing fees to bring it uptodate. They are using Microsoft's
Active Directory on Windows Server 2000 as the primary database for all

The goals of this configuration are:
1. Provide AAA for administrative access to network devices (mostly, SSH
access to Cisco routers/switches/firewalls)
2. Provide AAA for 802.11 WLAN users who are using Windows XP and "Wireless
Zero" clients. (Cisco 1231 series access points)
3. Provide AAA for VPN Users who use cisco Secure VPN client from remote
locations (cisco 3000 series concentrators)

The freeradius server itself will run on a CentOS 5.4 Linux box. The company
doesn't like "make install" application installs (nor do I), so I  simply
grabbed the SRPM from the Fedora project's KOJI build server for the latest
freeradius, and built it. This process is outside the scope of this
particular posting, as I want to focus on Freeradius itself.

The Fedora spec file builds multiple binary packages that you can install
based on what you want to do with freeradius. These are the packages I'm


The company has one Active Directory forest, and one subdomain. Users exist
under both. So, for this configuration, we'll call the primary zone
COMPANY.COM and SUB.COMPANY.COM <http://SUB.COMPANY.COM>. Both domains must
be queried for authorization & authentication. The following rules apply for

1. User must exist within the AD forest. (as defined by my LDAP
search parameters)
2. User must be a member of a particular group (defined by the Users file)
for certain types of access:
  a) For administrative access to network device, the user must be a member
of the group networkteam.
  b) Access to use the VPN, user must be a member of the group vpnusers.
  c) Access to the Wireless network, user must be a member of group

VPN and Network-Admin users achieve both Authorization and Authentication
using nothing more than the rlm_ldap module. Wireless users do not use ldap
at all, but instead, they use the eap and mschap modules. The mschap module
is configured to use ntlm_auth which requires samba's winbind to be
configured on the backend.

It took me some time (and I will not admit how much time) to figure out how
to make this all work. I blame most of this on the fact that freeradius has
been around a long time, and as    result there's lots of obsolete
documentation out there for google to find. I believe this configuration
uses mostly uptodate syntax, however I'm always interested in advice on

Interestingly, this process gave me quite a case of dejavu. I wrote a
combination tacacs/radius server WAY back when the Livingston Portmaster 2
series was king of dialup. Many sers on the livingston mailing list used my
source code. This really made me feel old. :(

Anyway, on to the good stuff:


1. Cisco does not support command accounting via Radius. Only Tacacs. Why?
They want you to buy ACS ! Sorry Cisco, I love ya, but.. cmon!
2. Cisco PIX/ASA do not support radius for command authorization. So, you
have to type 'enable' and your AD account password AGAIN.
3. Cisco 1231-series access points running IOS 12.3(8)JED or earlier do not
include NAS-Port-Type in subsequent password attempts for SSH access. So,
you only get one shot at typing your password right when you SSH for admin
access into a WAP. It'll ask you for your password again, and it'll try to
get approval from your Radius server, however, it will not include the port
type in the request. So freeradius will not classify the request in the
proper huntgroup. Unfortunately, you may think you just keep fat fingering
your password, so you just keep trying. This will eventually get your AD
account locked and you'll have to bug the windows guys to re-enable it. :)
4. Cisco 3000 platform does not support AAA for admin access.. again, only
with tacacs. I know, I know, this platform is OLD, but... you have to admit,
it has to be one of the most reliable platforms cisco has ever sold. It's
hard to say goodbye.

# freeradius config for a typical company using Active Directory for
password management
# George Shearer (doc at lame dot org)
# Primary uses:
# 1. AAA for admin access to network devices (ldap)
# 2. AAA for user access to 802.11 Wireless Networks (WPA2-Enterprise /
# 3. AAA for remote user access to VPN (ldap)

name = radiusd

# Locations
prefix = /usr
exec_prefix = /usr
sysconfdir = /etc
localstatedir = /var
sbindir = /usr/sbin
logdir = ${localstatedir}/log/radius
raddbdir = ${sysconfdir}/raddb
radacctdir = ${logdir}/radacct
confdir = ${raddbdir}
run_dir = ${localstatedir}/run/${name}
db_dir = ${raddbdir}
libdir = /usr/lib/freeradius
pidfile = ${run_dir}/${name}.pid

# Security
#chroot = /path/to/chroot/directory
user = radiusd
group = radiusd
security {
        max_attributes = 200
        reject_delay = 1
        status_server = yes

# Network
hostname_lookups = yes
proxy_requests = no
listen {
        type = auth
        ipaddr = ip-addr-of-eth0
        port = 1812
listen {
        type = acct
        ipaddr = ip-addr-of-eth0
        port = 1813
listen {
        type = control
        socket = ${run_dir}/${name}.sock
        mode = ro

# Performance
max_request_time = 10
cleanup_delay = 5
max_requests = 1024
allow_core_dumps = no
regular_expressions = yes
extended_expressions = yes
thread pool {
        start_servers = 5
        max_servers = 15
        min_spare_servers = 3
        max_spare_servers = 5
        max_requests_per_server = 0

# Logging
log {
        destination = syslog
        syslog_facility = authpriv
        stripped_names = no
        auth = yes
        auth_badpass = yes
        auth_goodpass = no

# freeradius modules
modules {

        # Create unique acct ID's per session -- so tell network devices to
use common id's and let radiusd handle
        acct_unique {
                key = "User-Name, Acct-Session-Id, NAS-IP-Address,
Client-IP-Address, NAS-Port"

        preprocess {
                huntgroups = ${confdir}/huntgroups

        detail {
                detailfile = ${radacctdir}/%{Huntgroup-Name}/detail-%Y%m%d
                detailperm = 0600
                header = "%t"

        files {
                usersfile = ${confdir}/users
                acctusersfile = ${confdir}/acct_users
                compat = no

        ldap {
                server = "ip-addr-of-domain-contoller"
                port = 3268
# Global Catalog port not LDAP. This allows combined searches of both
                identity = "CN=myqueryacct,OU=System
                password = "mypassword"

                basedn = "DC=mycompany,DC=com"
                filter =
                groupname_attribute = "cn"
                groupmembership_filter =
                groupmembership_attribute = memberOf

                ldap_connections_number = 3
                timeout = 4
                timelimit = 3
                net_timeout = 1
                directory_mappings = ${confdir}/ldap.attrmap

        eap {
                default_eap_type = peap
                timer_expire = 60
                max_sessions = 500
                ignore_unknown_eap_types = no
                tls {
                        cert_dir = ${confdir}/certs
                        rsa_key_length = 2048
                        dh_key_length = 2048
                        private_key_password = password-for-my-encrypted-key
                        private_key_file =
                        certificate_file = ${private_key_file}
                        CA_file = ${cert_dir}/my-ca-cert-file.pem
                        dh_file = ${cert_dir}/dh
                        random_file = ${cert_dir}/random
                        fragment_size = 1024
                        include_length = yes
                        cipher_list = "DEFAULT"
                        cache {
                                enable = yes
                                lifetime = 24
                                max_entries = 500
                peap {
                        default_eap_type = mschapv2
                mschapv2 {

        mschap {
                use_mppe = yes
                require_encryption = yes
                require_strong = yes
                with_ntdomain_hack = yes
                ntlm_auth = "/usr/bin/ntlm_auth --request-nt-key

        radutmp {
                filename = ${logdir}/radutmp
                username = %{%{Stripped-User-Name}:-%{User-Name}}
                case_sensitive = yes
                check_with_nas = yes
                perm = 0600

        realm ntdomain {
                format = prefix
                delimiter = "\\"

instantiate {

authorize {
        eap {
                ok = return
                updated = return
# The eap {} stuff above prevents your ldap server from being used for
wireless users. Since we handle this via ntlm, no need to bug ldap.
# also, the ntdomain stuff isnt even needed because the mschap module has
variables to do the stripping built in..

authenticate {
        Auth-Type LDAP {
        Auth-Type MS-CHAP {

preacct {
        update request {
                NAS-Port = 1
# Cisco ASA's annoyingly do not include NAS-Port for administrative logins.
This breaks radwho. The
# above statement forces Nas-Port to be '1' if it doesn't already exist.

accounting {

session {

# Realms (domains)
        authhost = LOCAL
        accthost = LOCAL
        type = radius

realm SUB {
        authhost = LOCAL
        accthost = LOCAL
        type = radius

realm NULL {
        authhost = LOCAL
        accthost = LOCAL
        type = radius
        realm = MYCOMPANY

# Include our clients
$INCLUDE clients.conf


# All devices that network-admins want to log into for administrative access
# Cisco sends "Virtual" for type on most things I've tried for SSH access
network-admin   NAS-IP-Address == ipaddr1, NAS-Port-Type == "Virtual"
network-admin   NAS-IP-Address == ipaddr2, NAS-Port-Type == "Virtual"
network-admin   NAS-IP-Address == ipaddr3, NAS-Port-Type == "Virtual"
network-admin   NAS-IP-Address == ipaddr4, NAS-Port-Type == "Virtual"

# Cisco 1200 series WAPs
# Since I handle AAA for wireless via ntlm_auth (not LDAP), this group isnt
even necessary
# However you could expand upon my config and use this group to create a
# accounting file, or various other uses.
wireless        NAS-IP-Address == ipaddr1, NAS-Port-Type ==
wireless        NAS-IP-Address == ipaddr2, NAS-Port-Type ==
wireless        NAS-IP-Address == ipaddr3, NAS-Port-Type ==

# Cisco 3000 Concentrators
# These also send "VirtuaL" as type for users connecting via IPsec.
# the 3000 platform does not support radius AAA for administrative access
vpn             NAS-IP-Address == ipaddr1, NAS-Port-Type == "Virtual"
vpn             NAS-IP-Address == ipaddr2, NAS-Port-Type == "Virtual"


# this 'locally managed' account matches the same account thats in the local
configs on the devices
# various scripts use this account for things like downloading configs or
whatever. And its not in
# active directory. It also has a limited security level.
admin           Huntgroup-Name == "network-admin", Cleartext-Password :=
                Service-Type := NAS-Prompt-User,
                cisco-avpair := "shell:priv-lvl=2"

# This drops all users who are a member of the 'networkteam' group right
into an "enabled" prompt
# Unfortunately, PIX/ASA's do not support this cisco-avpair on Radius, so
you'll still type 'enable'
# but when you're asked for a password, you'll give your AD account password
again as opposed to
# the real enable password. The only time you should need the real enable
password for any device
# is when the radius server is down
DEFAULT         Huntgroup-Name == "network-admin", Ldap-Group ==
                Service-Type := NAS-Prompt-User,
                cisco-avpair := "shell:priv-lvl=15",
                Auth-Type := LDAP

DEFAULT         Huntgroup-Name == "vpn", Ldap-Group == "vpnusers"
                Auth-Type := LDAP

DEFAULT         Auth-Type := Reject
                Reply-Message := "Access Denied. Your attempt has been

Cisco IOS based device config: (relevant parts, anyway)

aaa new-model
aaa authentication login networkteam group radius local-case
aaa authentication login wifi-access group radius
aaa authorization exec default group radius local
aaa accounting session-duration ntp-adjusted
aaa accounting exec default start-stop group radius
aaa accounting network default start-stop group radius
aaa session-id common
! Obviously you dont need the wireless stuff for non-access points
dot11 ssid OURSSID
  authentication open eap wifi-access
  authentication key-management wpa
  accounting default
! The "accounting-default" statement is what causes IOS to send accounting
packets for that SSID
radius-server host my-radius-server auth-port 1812 acct-port 1813 key
line con 0
login authentication networkteam
line vty 0 15
login authentication networkteam

Cisco ASA/PIX config:

username admin password whatever
aaa-server radius protocol radius
 max-failed-attempts 3
aaa-server radius (INSIDE) host my-radius-server
 timeout 3
 key slxradkey2010
 authentication-port 1812
 accounting-port 1813
aaa authentication ssh console radius LOCAL
aaa authentication enable console radius LOCAL
aaa authentication serial console radius LOCAL
aaa authentication telnet console radius LOCAL
aaa authentication http console radius LOCAL
aaa accounting ssh console radius
aaa accounting telnet console radius
aaa accounting serial console radius
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the Freeradius-Users mailing list