Duo 2FA External Directory Sync with RedHat FreeIPA
Posted by broseSep 30
Recently I was working on a PaloAlto VPN and had to set up the Duo Authentication Proxy service. This allowed the VPN to auth as a RADIUS client, first stage being LDAP auth to a RedHat 9 FreeIPA server, and the second stage being Duo itself. This was working great, but it meant I had to manage users in 2 places everytime someone came or went which is less than ideal. Duo has the possibility to do an External Directory Sync with the same tool, but they only seem to support OpenLDAP and not actual FreeIPA. However, I was able to get this directory sync working with a little modification, and here’s how:
A note before we begin, in all of the below I’m assuming you’re setting things up from scratch.
1) Log in to the Duo admin portal, go to Users => External Directories and set up a new one. It will ask you what connection to use, you’ll create a new one. It gives you an Integration key, Secret Key, and API Hostname you need to put into your Duo Authentication Proxy config file. You also need to have a user who can bind to LDAP and configure that in the same place. So this section in the INI-style config file ends up looking like (note that the service_account_username assignment breaks into 2 lines here, it is a single line without the backslash and newline in my actual config):
[cloud]
ikey=<integration_key>
skey=<secret_key>
api_host=<api_hostname>
service_account_username=uid=mybindaccount,cn=users,\
cn=accounts,dc=mydomain,dc=com
service_account_password=<mybindaccount_password>
Now, restart the Duo Authentication Proxy service. Back on the Duo connection config page, give it the hostname of your FreeIPA server, and I used port 389. Specify the base DN, it would be “cn=accounts,dc=mydomain,dc=com” in this example. Authentication type is plain, and I used a STARTTLS Transport Type. On a FreeIPA-enabled machine, see /etc/ipa/ca.crt for the CA certificate – paste that into the “SSL CA Certs” box. Save the config, and your connection should be all set.
2) Now for the sync settings. The username attribute is “uid”, the Display Name attribute is “displayname”, and Email Address is “mail”. You can optionally add more attributes, but this is up to you, just the above was fine for me. Now’s when you’ll pick a group, an easy win is to just pick the group “ipausers” which is everyone. Click save on this config, and note that while it reports success in saving, the whole thing doesn’t work – the “ipausers” group wasn’t saved no matter how many times you try, so it “successfully” syncs 0 groups and 0 users, hardly useful. The reason is that Duo is looking for each entry to have a value named entryUUID, but FreeIPA uses an attribute named IPAUniqueID.
3) Patch the Duo Auth Proxy service to replace “entryUUID” strings with “IPAUniqueID”. The magic happens in the pkgs/duoauthproxy/duoauthproxy/modules/drpc_plugins/ldap_base.py file. At line 294, you need to patch any search filters:
if filter_text != None:
# print(“brosepatch filter before: ” + filter_text)
filter_text = filter_text.replace(“entryuuid”, “ipauniqueid”)
# print(“brosepatch filter after: ” + filter_text)
At line 307, you need to delete the “entryuuid” attribute from the list and add the “ipauniqueid” attribute:
if “entryuuid” in attributes:
# log.msg(“brosepatch: replacing entryuuid with ipauniqueid for query!”)
attributes.remove(“entryuuid”)
attributes.add(“ipauniqueid”)
And finally, at line 377, we need to swap the name of the value back before returning it to the Duo cloud:
# log.msg(“brosepatch result before: “)
# print(result)
# log.msg(“brosepatch iterate over result and fix…”)
for broseitem in result:
if ‘ipauniqueid’ in broseitem:
broseitem[‘entryuuid’] = broseitem.pop(‘ipauniqueid’)
# log.msg(“brosepatch result after: “)
# print(result)
4) Finish up. Recompile the Duo Auth Proxy with those patch changes, install it, and restart the service. You can see the changes happening in the logs if you uncommented the debug log/print statements. You can now go back to the Duo Admin web portal, maybe log out and back in to clear any cache, and now you will be able to select a group. I found that for some reason, I still could not select the “ipausers” group, but I could select any group that I created manually with the CLI “ipa” utility on the FreeIPA server. So, I simply did this:
[root@freeipaserver ~]# ipa group-add myorg
——————-
Added group “myorg”
——————-
…
[root@freeipaserver ~]# ipa group-add-member myorg
[member user]:
[member group]: ipausers
[member service]:
[member User ID override]:
Group name: myorg
GID: …
Member groups: ipausers
Indirect Member users: every, user, in, the, directory
————————-
Number of members added 1
————————-
I also had to modify the FreeIPA default permissions so that the binding account could read the things that were needed:
[root@freeipaserver ~]# ipa permission-mod \
‘System: Read Groups’ –includedattrs=entrydn
[root@freeipaserver ~]# ipa permission-mod \
‘System: Read User Standard Attributes’ –includedattrs=mail \
–includedattrs=entrydn
That’s it! You can now set the Duo Admin Web Console to use the group “myorg” and any new users will automatically sync into Duo, and any deleted users will end up in Trash. I also enabled high-frequency syncing because I am impatient. Perfect!
Happy 2-factoring!