Duo Push MFA (2 factor authentication) for Apache (httpd) webserver

Title image of Apache and Duo logos and connectivity

It has been a while since I’ve written about Information Technology so I figure it is time to revisit a few of my favorite technologies. In this post, I will cover the configuration required to put Duo multifactor authentication (using “Duo Push” and the Duo Mobile app) in front of the Apache webserver via the mod_authz_ldap Apache module.

I am a big fan of webserver based authentication - I have horror stories about unsecured “one-off” authentication mechanisms created in code (ask me in the Comments and I will share). As a result, I tend to sleep better knowing authentication is handled securely and centrally by the webserver itself.

Overview

Before I get into this, I want to iterate that this is a proof of concept or illustrative post. I am going to take shortcuts that you should never use in production. I will note such things with #neverinproduction.

To keep things simple, I opted to use Forum System’s test LDAP server as my user directory (very nice of Forum Systems to make this available). This eliminated the need for me to install and configure my own OpenLDAP or Microsoft Active Directory instance. In the real world, this would be pointed to your directory service (Active Directory, OpenLDAP, etc.).

Keeping with the simple theme, I also installed the Duo Auth Proxy (technically a Python-based RADIUS and LDAP proxy) and Apache on the same AWS EC2 nano instance (#neverinproduction). Always keep things separate for improved security.

The Apache module mod_authz_ldap will be configured to use the Duo Authentication Proxy as an LDAP server. The auth proxy is the glue that holds everything together - it authenticates users (first factor, username/password) by proxying them to Forum System’s LDAP server and facilitate Duo Push (second factor) using the Duo MFA cloud service.

Here is the process.

Configure Duo (console)

A few things need to happen in the Duo Administrator Console. These include adding a Duo Integration for LDAP, adding a Duo user that matches one from the test LDAP server, adding a device for that user, and activating the device for Duo Push.

Creating the Integration

The Authentication Proxy needs an Integration Key, API host, and Secret Key to interact with the Duo Cloud service. Search for and create an “LDAP Proxy” integration in the Duo Admin Console.

Adding an LDAP Integration in the Duo Admin Console

I named mine “Apache Duo MFA”. Take note of the Integration Key, API host, and Secret Key - these are needed later during the Authentication Proxy Configuration.

LDAP Integration details in the Duo Admin Console

Adding a user

In real life, your Duo users would be populated with an Active Directory Sync, Bulk Import, or some other batch process. For this demo, I opted to manually add the user “euclid”. The username in Duo just needs to match a username (uid) in Forum System’s test LDAP server.

Adding a Duo user, matching Forum System's LDAP server in the Duo Admin Console

Adding a device and enrolling it for Duo Push

Manually enroll a device (with the Duo Mobile App installed) and associate it with the “euclid” user. This way, when “euclid” authenticates, you can respond to the Push on the device. In real life, this can be automated as well.

Manually enroll a device with Duo Mobile installed, and associate with the "euclid" user

Activate Duo Push on the phone added for the "euclid" user

Install Apache (httpd)

I used Amazon Linux AMI 2018.03.0 for this setup and did not see the mod_authz_ldap module for Apache 2.4 in the repository yet (I was in a hurry and may have missed it). As a result, I opted for Apache 2.2 since the LDAP module can be installed via yum for that version. Your mileage may vary on other distributions.

To install, log in as a user with sudo privileges and run:

sudo yum -y update
sudo yum -y install httpd
sudo yum -y install mod_authz_ldap
sudo service httpd start

Install the Duo Authentication Proxy

At the time of writing, the current version of the Duo Auth Proxy was 3.1.0. If you are replicating the install process line by line, you may need to update references the version. The Auth Proxy install is very easy (albeit time-consuming to compile on a t2.nano instance - around 5 minutes).

Log in as a user with sudo privileges and run:

sudo yum -y install gcc make python-devel libffi-devel perl zlib-devel
wget https://dl.duosecurity.com/duoauthproxy-latest-src.tgz
tar xzf duoauthproxy-latest-src.tgz
cd duoauthproxy-3.1.0-rc.1--src/
make
cd duoauthproxy-build
sudo ./install

Accepting the default options during the install is generally fine.

Configure the Duo Authentication Proxy

The Duo Auth Proxy configuration options are very well documented. Note, the proxy’s default settings are aligned to Microsoft Active Directory. If you are using RedHat Directory Server, OpenLDAP, etc., some minor tweaks are required.

The Forum System’s test LDAP server runs OpenLDAP. I found this guide particularly helpful for configuring the Duo Authentication Proxy to work with OpenLDAP. If you find entries like the following in authproxy.log, it is possible the auth proxy is not configured properly for your directory service:

  • Username lookup failed: ‘invalidDNSyntax: invalid DN’
  • Received extraneous LDAP PDU while resolving a BindRequest

To configure the proxy, log in as a user with sudo privileges and run:

sudo vi /opt/duoauthproxy/conf/authproxy.cfg

Remove the default config and paste the following, saving the file once done (note: replace the ikey, skey and api_host with the values you copied when creating the LDAP Proxy integration in the Duo Console):

[ad_client]
;https://duo.com/docs/authproxy-reference#ad_client
;https://help.duo.com/s/article/2121?language=en_US
;https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
; I am binding on port 389 (no encryption) - #neverinproduction
; Use 636 (LDAPS) instead
host=ldap.forumsys.com
auth_type=plain
service_account_username=read-only-admin
service_account_password=password
bind_dn=cn=read-only-admin,dc=example,dc=com
search_dn=dc=example,dc=com
username_attribute=uid

[ldap_server_auto]
;https://duo.com/docs/authproxy-reference#ldap-auto
ikey=<<from integration in Duo Admin Console>>
skey=<<from integration in Duo Admin Console>>
api_host=<<from integration in Duo Admin Console>>
failmode=secure
client=ad_client
factors=push

The auth proxy config is also available as a Gist if you prefer.

Once done, start the auth proxy:

sudo /opt/duoauthproxy/bin/authproxyctl start

Configure Apache (HTTPD)

Finally, configure Apache to use the LDAP module for authentication. I simply protected the default site (test page) as an example, but this can be applied at the vhost level as well (either in the main configuration files or via .htaccess).

I neglected to set up HTTPS for this test - #neverinproduction. Any authentication on the web should always occur via HTTPS.

To configure, log in as a user with sudo privileges and run:

sudo vi /etc/httpd/conf/httpd.conf

Locate the “<Directory “/var/www/html”>” section and append this to the beginning of the block:

AuthType Basic
AuthBasicProvider ldap
AuthName "LDAP Duo MFA"
# I am binding on port 389 (no encryption) - #neverinproduction - use 636 (LDAPS) instead
AuthLDAPURL ldap://localhost/dc=example,dc=com?uid
AuthLDAPBindDN "cn=read-only-admin,dc=example,dc=com"
AuthLDAPBindPassword "password"
Require valid-user

In context:

#
# This should be changed to whatever you set DocumentRoot to.
#
<Directory "/var/www/html">

    #Duo LDAP configuration
    AuthType Basic
    AuthBasicProvider ldap
    AuthName "LDAP Duo MFA"
    # I am binding on port 389 (no encryption) - #neverinproduction
    # Use 636 (LDAPS) instead
    AuthLDAPURL ldap://localhost/dc=example,dc=com?uid
    AuthLDAPBindDN "cn=read-only-admin,dc=example,dc=com"
    AuthLDAPBindPassword "password"
    Require valid-user

#
# Possible values for the Options directive are "None", "All",
# or any combination of
.
.
.

Once done, start or restart Apache / httpd:

sudo service httpd restart

Test the setup

If all went well, you should have a functional MFA setup for Apache (httpd). If you access your webserver’s URL in a browser, you should be prompted for primary authentication:

Primary (first factor) authentication prompt

And if successful, you should receive a Duo Push on the device you enrolled:

Duo Push MFA prompt for "euclid" user

Once both phases of authentication are completed, you should see the Apache test page. Note that if you plan to test repeatedly, you may need to restart Apache in between tests. mod_ldap performs some caching that prevents the MFA push from being presented each authentication attempt (from the same device).

Closing thoughts

The ldap_server_auto option in the Duo Authentication Proxy is a somewhat hidden gem. There is a lot of documentation surrounding using RADIUS for a similar type of MFA setup for Apache, but I prefer LDAP. Almost all applications and devices (including legacy ones) support LDAP in some form, so this option makes Duo MFA possible on a multitude of devices and applications.

I also wanted to comment on Duo Security as a company. They are fantastic in every way (no, I was not compensated by Duo to say this). Their MFA solution is excellent technically: reliable, easy to implement, maintain and use. What sets them apart (in my opinion) are their business processes: Incident Management, Change Management, Support, and Documentation. It is rare to receive such prompt and professional Incident Management and Change Management communications from an organization, but Duo has excelled at this since I first adopted their solution years ago. Not many companies score high in my book across the board, but Duo shines.

If you are in the market for a robust MFA (2-factor authentication) provider, I highly recommend Duo Security.