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 identities/passwords. 

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 using:

freeradius-utils-2.1.8-2
freeradius-2.1.8-2
freeradius-ldap-2.1.8-2

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. Both domains must be queried for authorization & authentication. The following rules apply for access:

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 wifiusers.

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 improvements.

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:

Annoyances: 

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.

radiusd.conf
---------------------------------------------------------------------------------------------
# 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 / EAP-PEAP / EAP-MSCHAPv2)
# 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 SUB.MYCOMPANY.COM and MYCOMPANY.COM
                identity = "CN=myqueryacct,OU=System Accounts,DC=mycompany,DC=com"
                password = "mypassword"

                basedn = "DC=mycompany,DC=com"
                filter = "(&(objectCategory=person)(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}}))"
                groupname_attribute = "cn"
                groupmembership_filter = "(&(objectClass=group)(member=%{control:Ldap-UserDn}))"
                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 = ${cert_dir}/combined-cert-and-key-file.pem
                        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 --username=%{%{mschap:User-Name}:-None} --domain=%{%{mschap:NT-Domain}:-EDLENDING} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00} --require-membership-of=wifiusers"
        }

        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 {
        preprocess
        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..
        ntdomain
        files
        ldap
}

authenticate {
        Auth-Type LDAP {
                ldap
        }
        Auth-Type MS-CHAP {
                mschap
        }
        eap
}

preacct {
        preprocess
        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.
        }
        ntdomain
        acct_unique
}

accounting {
        detail
        radutmp
}

session {
        radutmp
}

# Realms (domains)
realm MYCOMPANY {
        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

---------------------------------------------------------------------------------------------
huntgroups

# 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 seperate
# accounting file, or various other uses.
wireless        NAS-IP-Address == ipaddr1, NAS-Port-Type == "Wireless-802.11"
wireless        NAS-IP-Address == ipaddr2, NAS-Port-Type == "Wireless-802.11"
wireless        NAS-IP-Address == ipaddr3, NAS-Port-Type == "Wireless-802.11"

# Cisco 3000 Concentrators
# These also send "VirtuaL" as type for users connecting via IPsec. Unfortunately,
# 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"

---------------------------------------------------------------------------------------------
users

# 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 := "localadminaccountpw"
                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 == "networkteam"
                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 logged.",

---------------------------------------------------------------------------------------------
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 myradiuskey
!
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