I have a client that requires two-factor authentication. This is one of the features that the SAML service provides. However, Ansible Tower (AWX) (and, more specifically, the OneLogin SAML authentication library) defaults to setting the "AuthnContext" to "PasswordProtectedTransport" (password authentication). It is required for that variable to not be enforced. Two-factor authentication cannot be used and there is no way to override this in the AWX GUI or CLI.
A temporary workaround is to edit this file [ /var/lib/awx/venv/tower/lib/python2.7/site-packages/onelogin/saml2/settings.py ] and set the "requestedAuthnContext" variable equal to False (changed from True).
self.__security.setdefault('requestedAuthnContext', False)
By disabling this, the SAML service can then correctly enforce two-factor authentication.
The correct way to override this variable is to use a "settings.json" file as shown in this source file:
https://github.com/onelogin/python-saml/blob/v2.2.3/src/onelogin/saml2/settings.py#L227
The override file should contain at least this to ensure SAML two-factor authentication can work (after "flipping a switch" in the GUI or CLI to enable two-factor auth for SAML):
{
"security": {
"requestedAuthnContext": false
}
}
It would be ideal for AWX to create and manage this configuration file.
Setup a SAML server to use for authentication into AWX. Then attempt to force two-factor authentication. It will not work as only passwords are a valid authentication mechanism for SAML currently.
SAML should support two-factor authentication in AWX.
SAML can only be used for "one-factor" password authentication in AWX.
This unrelated issue illustrates a real-world example of what a more complete settings.json file should look like for the OneLogin's SAML Python library:
https://github.com/onelogin/python-saml/issues/101
A completed settings.json should not be required.
I can look at this later today or tomorrow probably.
This workaround works perfectly for the web UI's login page. A SAML button appears on the front page that can be selected and then the client is taken to their external two-factor authentication page.
One problem we did find is that this does not seem to work for directly accessing the API endpoint from a web browser. For example, going to https:://ansible.tower.tld/api/v1/settings/system/ prompts for a username and password. There does not seem to be a way to go through the second authentication factor and I am unsure how this would be accomplished.
Similarly, we've had a request to provide SAML settings such that they can set AuthnRequestsSigned and WantAssertionsSigned to true - I assume this would use the same settings framework.
Yes, those can both be overridden in a settings.json configuration file (that will need to be created).
https://github.com/onelogin/python-saml/blob/v2.2.3/src/onelogin/saml2/settings.py#L271
https://github.com/onelogin/python-saml/blob/v2.2.3/src/onelogin/saml2/settings.py#L278
I assume the configuration file needs to be placed at /var/lib/awx/venv/tower/lib/python2.7/site-packages/onelogin/saml2/settings.json based on this line of code. I could be misinterpreting this, though.
@ekultails I've found a less invasive way to achieve this result.
/etc/tower/conf.d/saml.py root:awx owner
SOCIAL_AUTH_SAML_SECURITY_CONFIG = {
'requestedAuthnContext': False
}
I've verified by introspecting the SAML request sent by the SP (Tower). I'm not sure how to have my SAML service (One Login) provide/require a 2-factor (token) auth. I've made One Login require users to use google auth OTP token. However, One Login doesn't seem to care that the SP sent a PasswordProtectedTransport or an "I don't care" auth requirement.
SP generated SAML request when requestedAuthnContext = True
...
<samlp:RequestedAuthnContext
Comparison="exact">
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
SP generated SAML request when requestedAuthnContext = False does NOT contain the block.
<samlp:RequestedAuthnContext Comparison="exact"></samlp:RequestedAuthnContext> This is good, this is behavior we want.
Most helpful comment
@ekultails I've found a less invasive way to achieve this result.
/etc/tower/conf.d/saml.pyroot:awxownerI've verified by introspecting the SAML request sent by the SP (Tower). I'm not sure how to have my SAML service (One Login) provide/require a 2-factor (token) auth. I've made One Login require users to use google auth OTP token. However, One Login doesn't seem to care that the SP sent a
PasswordProtectedTransportor an "I don't care" auth requirement.SP generated SAML request when
requestedAuthnContext = TrueSP generated SAML request when
requestedAuthnContext = Falsedoes NOT contain the block.<samlp:RequestedAuthnContext Comparison="exact"></samlp:RequestedAuthnContext>This is good, this is behavior we want.