FreeRADIUS with Two Factor Authentication (Google Authenticator)
Goal: Setup FreeRADIUS server that uses Google two factor authentication + LDAP (CentOS 7 based)
My specific use case was to setup a Cisco AnyConnect VPN and authenticate against a RADIUS server. I needed to have strong two factor authentication and easy group administration of users belonging to specific VPN group profiles. It is easy enough to point a Cisco ASA to a RADIUS server, and tying in Google Authenticator via PAM is straightforward, but things quickly become more complicated if you need to manage more than one VPN profile that is backed by different LDAP groups.
Consider the following situation:
1. You need two VPN profiles, one for Sales and one for Engineering
2. You need to verify that users logging in belong to the correct LDAP group
Let's examine how FreeRADIUS integrates with PAM, and how PAM in turn interacts with LDAP:
FreeRADIUS lets you define sites, similar to Apache on Debian based systems, and these sites have one or more servers which tell FreeRADIUS how to handle authentication, authorization, etc. You can also define which addresses and ports FreeRADIUS will bind to, which turns out to be critically important to achieving what we are trying to accomplish.
Since there isn't a way to disambiguate which PAM file to use for LDAP group validation from two different authorization requests coming from the same source (Cisco ASA), we'll need to configure the ASA with multiple AAA servers. I will leave the ASA configuration items, as well as the AnyConnect setup, as an exercise for the reader. We only have one FreeRADIUS server, so we'll have to configure each AAA server entry to use a different port number for authentication requests.
First, add an entry to /etc/raddb/clients.conf to direct requests from the ASA to the default site, /etc/raddb/sites-enabled/default:
We'll have two 'listen' stanzas, each one listening on a specific port and forwarding all request types to its own server configuration. A site can have multiple server stanzas, and these stanzas are where you can define what port the authentication request should be sent to, and optionally which PAM file to use when PAM is enabled as the authentication method. Here's an example of a site configured with multiple listen stanzas, each pointing to a different server stanza:
Take note of the 'update control' section in each server stanza. The Pam Auth directive defines which file under /etc/pam.d will be used for the incoming authorization request. Here's an example of a pam file which uses google auth and ties into LDAP using Winbind (there are several other ways to use LDAP with PAM which aren't covered in this post):
To summarize the process:
My specific use case was to setup a Cisco AnyConnect VPN and authenticate against a RADIUS server. I needed to have strong two factor authentication and easy group administration of users belonging to specific VPN group profiles. It is easy enough to point a Cisco ASA to a RADIUS server, and tying in Google Authenticator via PAM is straightforward, but things quickly become more complicated if you need to manage more than one VPN profile that is backed by different LDAP groups.
Consider the following situation:
1. You need two VPN profiles, one for Sales and one for Engineering
2. You need to verify that users logging in belong to the correct LDAP group
Let's examine how FreeRADIUS integrates with PAM, and how PAM in turn interacts with LDAP:
- Install the google authenticator PAM module
- Install FreeRADIUS
cd ~ git clone https://code.google.com/p/google-authenticator/ cd google-authenticator/libpam/ make make install |
yum install freeradius
yum install freeradius-utils
|
- Configure FreeRADIUS to use PAM
- Open /etc/raddb/users and add:
DEFAULT Auth-Type := PAM |
- Configure the specific PAM config file to use when PAM is being used for authentication within FreeRADIUS.
- This is done by setting the pam_auth directive in /etc/raddb/mods-enabled/pam, i.e. pam_auth = radiusd (this assumes /etc/pam.d/radiusd exists)
FreeRADIUS lets you define sites, similar to Apache on Debian based systems, and these sites have one or more servers which tell FreeRADIUS how to handle authentication, authorization, etc. You can also define which addresses and ports FreeRADIUS will bind to, which turns out to be critically important to achieving what we are trying to accomplish.
Since there isn't a way to disambiguate which PAM file to use for LDAP group validation from two different authorization requests coming from the same source (Cisco ASA), we'll need to configure the ASA with multiple AAA servers. I will leave the ASA configuration items, as well as the AnyConnect setup, as an exercise for the reader. We only have one FreeRADIUS server, so we'll have to configure each AAA server entry to use a different port number for authentication requests.
First, add an entry to /etc/raddb/clients.conf to direct requests from the ASA to the default site, /etc/raddb/sites-enabled/default:
1 2 3 4 5 6 7 8 9 10 11 12 | client vpn { ipaddr = ASA IP ADDRESS proto = * secret = CLIENT SECRET require_message_authenticator = no nas_type = other limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } |
We'll have two 'listen' stanzas, each one listening on a specific port and forwarding all request types to its own server configuration. A site can have multiple server stanzas, and these stanzas are where you can define what port the authentication request should be sent to, and optionally which PAM file to use when PAM is enabled as the authentication method. Here's an example of a site configured with multiple listen stanzas, each pointing to a different server stanza:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | listen { type = auth ipaddr = 192.168.1.100 port = 2012 virtual_server = vpn-group-sales limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } listen { ipaddr = 192.168.1.100 port = 2013 type = acct } listen { type = auth ipaddr = 192.168.1.100 port = 2112 virtual_server = vpn-group-support limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } listen { ipaddr = 192.168.1.100 port = 2113 type = acct } server vpn-group-sales { <SNIP>... authorize { ... pap update control { Pam-Auth = "radiusd-vpn-group-sales" # or whatever... } } authenticate { Auth-Type PAP { pap } ... pam } ...</SNIP> } server vpn-group-support { <SNIP>... authorize { ... pap update control { Pam-Auth = "radiusd-vpn-group-support" # or whatever... } } authenticate { Auth-Type PAP { pap } ... pam } ...</SNIP> } |
Take note of the 'update control' section in each server stanza. The Pam Auth directive defines which file under /etc/pam.d will be used for the incoming authorization request. Here's an example of a pam file which uses google auth and ties into LDAP using Winbind (there are several other ways to use LDAP with PAM which aren't covered in this post):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | auth required pam_env.so auth requisite pam_succeed_if.so uid >= 1000 quiet_success auth requisite pam_google_authenticator.so forward_pass auth requisite pam_succeed_if.so user ingroup LDAP_GROUP_NAME debug auth sufficient pam_krb5.so use_first_pass auth sufficient pam_winbind.so use_first_pass auth required pam_deny.so account required pam_access.so account required pam_unix.so broken_shadow account sufficient pam_localuser.so account sufficient pam_succeed_if.so uid < 1000 quiet account [default=bad success=ok user_unknown=ignore] pam_krb5.so account [default=bad success=ok user_unknown=ignore] pam_winbind.so account required pam_permit.so password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type= password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok password sufficient pam_krb5.so use_authtok password sufficient pam_winbind.so use_authtok password required pam_deny.so session optional pam_keyinit.so revoke session required pam_limits.so -session optional pam_systemd.so session optional pam_oddjob_mkhomedir.so umask=0077 session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid session required pam_unix.so session optional pam_krb5.so session optional pam_winbind.so |
To summarize the process:
- Install Google Authenticator
- Install FreeRADIUS
- ASA - Setup AAA servers
- Point each AAA server at the FreeRADIUS server
- Each server needs its own authentication port
- Add default rule to use PAM libraries for authentication
- Add client entry to clients.conf
- Add listen directives for each authentication port
- configure each listen directive to point to a unique server stanza
- Configure each server stanza with the PAM file you want to use for authentication
Comments
Post a Comment