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
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.
Thanks for the briliant tutorial!
ReplyDeleteUnfortunately 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!
Same here: OpenID Connect Provider error: Error in handling response type.
ReplyDeleteThis was a great help, thanks! Especially translating the Azure parameters into config entries.
ReplyDeletefor 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
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"
ReplyDeletewget
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
is this setup available for apache running on windows?
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteYes! Finally something about %keyword1%.
ReplyDeleteHi There, can someone point me to the similar implementation on Windows10 machine please :)
ReplyDeleteHi, how do I install this for Ubuntu 20.04?
ReplyDeletelibhiredis0.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
Finally managed to get it to work on Ubuntu 20.04:
Deleteapt-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