Apache SAML SSO – the hard way

This will likely be my last tech post for a while if ever, so with that in mind.

The world is going cloud whether we like it or not. The old concepts of perimeter security are slowly going away and replaced with a zero trust concept based on identity, 2FA and up to date security patching. Good thing too.

Recently I had a requirement to build a cloud based web server. The issue being that I had to lock it away from non authorised users.

There were two options.

  • Use an Apache LDAP plugin against a cloud IdP service with cloud LDAP
  • Use SAML and have authentication via a cloud IdP SSO service.

The first is easier but the second is better as there’s a lot more scope for user requirements past a basic username password combination. If your organisation decides to implement 2FA across all systems, then it’s the matter of a few switches flipped on your IdP than reinventing the wheel. I like that idea. The issue for me was how to implement it with almost zero documentation out there.

Thankfully there is a plugin for Apache called mod_auth_mellon that can do SSO for us with a little configuration. The issue is that it’s extremely poorly documented and there is only one example online.

I’m going to assume you’ve already installed Apache and other services you require. Start by installing that auth plugin and it’s requirements onto an Ubuntu 18.04LTS server with the following command.

sudo apt-get install openssl pkg-config liblasso3 libapache2-mod-auth-mellon

We also need to get a metadata generation script for the mod_auth_mellon apache plugin or we can’t tell the plugin where things are, and secure communications with the proper certificates.

git clone https://github.com/UNINETT/mod_auth_mellon.git

Now that we have the project cloned down, we feed the script the correct information to generate the files we need.

cd mod_auth_mellon
./mellon_create_metadata.sh https://url.of.server/myEntityID https://url.of.server/secret

Obviously replace the url.of.server with the actual FQDN of the server and any custom port you plan to use.

The /secret part is important. It should be different from the URL you plan to protect, because if they match you will end up stuck in an infinite loop between your site and your IdP!

In our case since we’re protecting the entire site, this isn’t an issue. A matter of moments later, the script will generate you three files that look something like this:


These should be copied into a new folder that only apache can access. In this example: /etc/apache2/mellon/

sudo mkdir /etc/apache2/mellon
sudo cp ./.key /etc/apache2/mellon/ sudo cp ./.cert /etc/apache2/mellon/
sudo cp ./*.xml /etc/apache2/mellon/

A brief note on what these files are:

The .cert file is used to encrypt the communications between your server and the IdP service

The .key file is the private key companion to the .cert file above. Keep this safe!

The .xml file is the service provider entity metadata. This is the info from your server (the service provider) supplied to the IdP containing things like the proper URL’s to talk to.

The .cert and .xml files should be copied to your SSO SAML administrator. He/She will give you back an idp-metadata.xml file. This file should be copied to the server and placed in the same folder as /etc/apache2 . If you are the IdP administrator as well, then follow the instructions you have for creating a new SAML integration app.

Finally we need to configure the .conf file for the web server. Assuming you’re using the /etc/apache2/sites-enabled/000-default.conf then you need to insert the following code into that file. (or whichever .conf file you’re editing if you’re being clever like I was at the time.)

# Accessing the / should trigger an IdP request
<Location />
	## Enable the mellon plugin and trigger auth to the IdP
	# MellonEnable is used to enable auth_mellon on a location.
	# It has three possible values: "off", "info" and "auth".
	#   "off":  mod_auth_mellon will not do anything in this location.
	#           This is the default state.
	#   "info": If the user is authorized to access the resource, then
	#           we will populate the environment with information about
	#           the user. If the user isn't authorized, then we won't
	#           populate the environment, but we won't deny the user
	#           access either.
	#   "auth": We will populate the environment with information about
	#           the user if they are authorized. If he isn't authenticated
	#           then we will redirect him to the login page of the IdP.
	MellonEnable "auth"

	## These are the generated service provider metadata files

	# MellonSPPrivateKeyFile is a .pem file which contains the private
	# key of the service provider. The .pem-file cannot be encrypted
	# with a password. If built with lasso-2.2.2 or higher, the
	# private key only needs to be readable by root, otherwise it has
	# to be readable by the Apache pseudo user.
	MellonSPPrivateKeyFile /etc/apache2/mellon/https_url.of.server.key

	# MellonSPCertFile is a .pem file with the certificate for the
	# service provider. This directive is optional.
	MellonSPCertFile /etc/apache2/mellon/https_url.of.server.cert

	# MellonSPMetadataFile is the full path to the file containing
	# the metadata for this service provider.
	# If mod_auth_mellon was compiled against Lasso version 2.2.2
	# or higher, this option is optional. Otherwise, it is mandatory.
	MellonSPMetadataFile /etc/apache2/mellon/https_url.of.server.xml
	# This is the metadata file provided by the IdP administrator
	MellonIdPMetadataFile /etc/apache2/mellon/idp-metadata.xml

Replace the filenames with the actual names you generated earlier or you’ll get weird failures and very little meaningful logs.

Now restart the apache service with:

sudo systemctl restart apache2

Once Apache has restarted you should be able to test your SSO IdP authentication! It’ll automatically redirect to your organisations sign on page and then redirect back if you authenticated properly. If you then implement 2FA through your IdP then this will automatically reflect on your users.