Modern apache authentication with Azure AD



One of the key topic areas for us at the moment is modernizing our applications - in most cases this is trying to move to more cloud native architectures like PaaS. However another aspect of this is dealing with the authentication process for applications - over time we (as most other companies will have) have built up a huge array of different authentication mechanisms (and authorization - but I'll ignore that for now). Users have multiple accounts to remember and manage and it's not a great situation for anyone.

Modernizing this authentication gives a much nicer experience to end users - you can get true single sign on and a much slicker experience over all.

The big enabler for us has been the rollout of office 365 and with it the Azure AD identity provider that backs that up. This means we can now use mail/intranet and some applications by just logging on to Azure AD once - when we do that we get a 'token' that all the applications can work with and we are allowed to login to everything with that token. How this is technically really working at the lower levels i don't understand well enough to be able to explain but the purposes of this blog i don't think that's too relevant.

What i wanted to demo here is how a basic apache website can also be authenticated using this sso/token based approach relatively easily. In the example i create a url that is protected and can only be accessed when i have the token - this enforces that the person accessing the site has authenticated and i can see who that person is. This could easily replace basic authentication or some other authentication built on window identities (or indeed some other custom authentication).

OK enough of the chat lets just demo what i want to show.

In this case I'm using the open id connect method to commnuicate between apache and Azure AD.

So here are the steps (I'm using ubuntu version 18.04 from an azure marketplace image for this - but the general process is the same and would be applicable to apache running anywhere).

I'll miss out all the output text you get from the command and just get to the point so its simpler to read/follow.

1) Install apache

apt install apache2

2) generate proper certificates for your site to make it https (self signed may work I didnt test that)

cd /etc/apache2
mkdir certs
cd certs
openssl genrsa -out  apacheaad.key 2048
openssl req -new -sha256 -key apacheaad.key -out apacheaad.csr

3) submit the request to your certificate provider - when you get the reply certificate (mine was in pem format including the parent cert chain in it) save it to

/etc/apache2/certs/apacheaad.crt

4) reconfigure apache to have a https site by editing /etc/apache2/sites-enabled/000-default.conf to have this content

<VirtualHost *:443>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
        SSLEngine on
        SSLCertificateFile /etc/apache2/certs/apacheaad.crt
        SSLCertificateKeyFile /etc/apache2/certs/apacheaad.key
</VirtualHost>

5) enable the ssl module of apache - this is easy in ubuntu

a2enmod ssl
systemctl restart apache2


6) at this point you should have a working https site with no authentication on it - note no warning on the page below



7) now we need to enable the authentication - first up we need some extra software from the standard repos

apt-get install libjansson4 libhiredis0.13

8) now we need the open id connect specific packages which i think are in the epel repo but are just very old there - so we get directly from github

wget https://github.com/zmartzone/mod_auth_openidc/releases/download/v2.3.7/libapache2-mod-auth-openidc_2.3.7-1.bionic.1_amd64.deb

wget https://github.com/zmartzone/mod_auth_openidc/releases/download/v2.3.0/libcjose0_0.5.1-1.bionic.1_amd64.deb

9) Then we install these github downloads

dpkg -i libcjose0_0.5.1-1.bionic.1_amd64.deb
dpkg -i libapache2-mod-auth-openidc_2.3.7-1.bionic.1_amd64.deb


10) then we enable the open id connect module for apache

a2enmod auth_openidc
systemctl restart apache2

So now the system is able to do open id connect authentication we just need to configure it to talk to Azure AD

So now we switch to the azure portal to set up the things we need there

11) We need to create an application registration from within the Azure AD blade - see + icon below


12) now we fill in some basic details - give it a name and set the sign on url to be the https site you created


13) now generate a key - call it anything you like - i called mine client secret so its obvious what it is - take heed to the warning on the 2nd screenshot below - you only get to see this value once!


14) The other value we need is the application id (referred to as the client id in a lot of the OIDC stuff) - this can be gotten from here:


15) now we have everything we need from the portal lets switch back to linux and modify the /etc/apache2/mods-enabled/auth_openidc.conf file - i added the following lines at the top

OIDCProviderMetadataURL https://login.microsoftonline.com/your-tenant-id-here/.well-known/openid-configuration
OIDCClientID xxxxxxxxxxxxxx
OIDCClientSecret xxxxxxx
OIDCRedirectURI https://apacheaad.yourdomain.com/secure/redirect_uri
OIDCCryptoPassphrase anyrandomstringoftextandnumbershere

<Location /secure>
   AuthType openid-connect
   Require valid-user
</Location>

Some quick explanations

OIDCProviderMetadataURL - this is the same for all apps - just needs your tenant id inserting whcih can be found in the portal in lots of places - for example click on switch directory at the top right and you'll see a list - the value you want looks like a long hex string

OIDCClientID - application id from your registered app here


OIDCClientSecret key you got generated from the one time thing earlier here

OIDCRedirectURI - url that refers to a path under the secure location but must not actually exist as a file

OIDCCryptoPassphrase - SomeRandomString you can just choose (just type in about 20 random characters)

The <Location> item - this needs to be the virtual path that is protected by open id connect - so in my case navigating to https://apacheaad/secure will trigger it to happen

16) now essentially everything is prepared but i want to be able to show the token attributes somehow and show this really is working as expected - to do this i create a simple cgi page to return the env variables for my session - the module automatically sets token attributes to be env variables. To enable the cgi i do the following

a2enmod cgi

update the /etc/apache2/sites-enabled/000-default.conf file to add an extra few lines for the cgi script

<VirtualHost *:443>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
        SSLEngine on
        SSLCertificateFile /etc/apache2/certs/apacheaad.crt
        SSLCertificateKeyFile /etc/apache2/certs/apacheaad.key
ScriptAlias /secure /var/www/html/secure/
<Directory "/var/www/html/secure">
                        AllowOverride None
                        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
</Directory>
</VirtualHost>

Now create a cgi file in /var/www/html/secure called test.cgi with the following content

#!/usr/bin/python

import os

d = os.environ
k = d.keys()
k.sort()

print "Content-type: text/html\n\n"

print "<HTML><Head><TITLE>Print Env Variables</TITLE></Head><BODY>"
print "<h1>Environment Variables</H1>"
for item in k:
    print "<p><B>%s</B>: %s </p>" % (item, d[item])
    print "</BODY></HTML>"

Then set the permission

chown www-data:www-data /var/www/html/secure/test.cgi
chmod ug+x /var/www/html/secure/test.cgi

Then restart apache

systemctl restart apache2

17) now we're good to go with a test - if i now navigate to https://apacheaad.xxxx/secure/test.cgi i initially get this




Here it is asking me to give permission fot he app to be able to read my profile in azure ad - i click accept to that - but then it fails with the following error - this is due to a mismatch between my apache config and my azure AD config



To fix this i just need to make the two match - so i update Azure AD reply url to be the same as set in the config file - which was https://apacheaad.yourdomain.com/secure/redirect_uri (i.e. i had to add on the /secure/redirect_uri to the path in azure ad)


Now when i try again the error disappears and i am allowed on to the page - which now looks like this:

You can see towards the end the OIDC_CLAIM_ parameters start to appear which shows that the token has been successful (to be honest if you got to the site its been successful - but nice to see the actual claim values here).

There you go - how to set it in in a few relatively easy steps - you'll find that if you have other apps working the same way you won;t get prompted for any kind of auth you'll just be allowed straight in (as long as the token timeout didn't expire).

Who said modernization was hard....?

I would guess the same kind of stuff is possible for nginx and the like i just didn't look any further into this.

Comments

  1. Thanks for the briliant tutorial!

    Unfortunately I failed on the last step. Instead of test.cgi output I got "OpenID Connect Provider error: Error in handling response type." error.
    Also in error.log I see "id_token signature could not be validated, aborting" message.

    Could you share some hint how to resolve it?
    Thanks again!

    ReplyDelete
  2. Same here: OpenID Connect Provider error: Error in handling response type.

    ReplyDelete
  3. This was a great help, thanks! Especially translating the Azure parameters into config entries.
    for anyone who doesn't need the latest version, this simplifies the install
    check the version: apt-cache show libapache2-mod-auth-openidc
    install: sudo apt install libapache2-mod-auth-openidc

    ReplyDelete
  4. Using this guide as of 4/17/2020 works, but you will want the newer releases of these two, otherwise you get the "Error in handling response"

    wget
    https://github.com/zmartzone/mod_auth_openidc/releases/download/v2.4.2.1/libapache2-mod-auth-openidc_2.4.2.1-1.bionic+1_amd64.deb

    wget
    https://github.com/zmartzone/mod_auth_openidc/releases/download/v2.4.0/libcjose0_0.6.1.5-1.bionic+1_amd64.deb

    ReplyDelete
  5. is this setup available for apache running on windows?

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. Yes! Finally something about %keyword1%.

    ReplyDelete
  8. Hi There, can someone point me to the similar implementation on Windows10 machine please :)

    ReplyDelete
  9. Hi, how do I install this for Ubuntu 20.04?

    libhiredis0.14 is the default / installed package.

    Running the below:
    sudo dpkg -i libapache2-mod-auth-openidc_2.4.8.4-1.bionic+1_amd64.deb

    Creates the error:
    Package libhiredis0.13 is not installed

    ReplyDelete
    Replies
    1. Finally managed to get it to work on Ubuntu 20.04:

      apt-cache search --names-only 'libhiredis'
      sudo apt-get install libhiredis0.14

      apt-cache search --names-only 'libjansson'
      sudo apt-get install libjansson4

      apt-cache search --names-only 'libcjose'
      sudo apt-get install libcjose0

      # if your distribution does not provide libcjose in its package repository, recent packages for a number of platforms are available from the "Assets" section in release 2.4.0
      # https://github.com/zmartzone/mod_auth_openidc/releases/tag/v2.4.0


      sudo apt-get install python3-python-openidc-client

      https://github.com/zmartzone/mod_auth_openidc/releases/latest
      https://github.com/zmartzone/mod_auth_openidc/releases (all releases)

      Ubuntu 20.04 Focal = libapache2-mod-auth-openidc_2.4.9-1.focal+1_amd64.deb
      Ubuntu 18.04 Bionic = libapache2-mod-auth-openidc_2.4.9-1.bionic+1_amd64.deb

      wget https://github.com/zmartzone/mod_auth_openidc/releases/download/v2.4.9/libapache2-mod-auth-openidc_2.4.9-1.focal+1_amd64.deb
      sudo dpkg -i libapache2-mod-auth-openidc_2.4.9-1.focal+1_amd64.deb
      sudo a2enmod auth_openidc

      Delete

Post a Comment