keystone/doc/source/external-auth.rst
“Richard 82c7b8bedc Doc update on enabled external auth and federation
By default the external auth is enabled and can cause user_id conflict
when REMOTE_USER is set due to the fact that federation uses
REMOTE_USER as well. Therefore, the docs were updated to advise users
against using both external auth and federation on the same sequence.

Closes-Bug: #1563454

Change-Id: I193f78ae0ad0232471b725d5700870c349703310
2016-07-08 13:50:47 +00:00

176 lines
7.5 KiB
ReStructuredText

===========================================
Using external authentication with Keystone
===========================================
.. _external-auth:
When Keystone is executed in a web server like :doc:`Apache HTTPD
<apache-httpd>` it is possible to use external authentication methods different
from the authentication provided by the identity store backend or the different
authentication plugins. For example, this makes possible to use an SQL identity
backend together with, X.509 authentication or Kerberos, for example, instead
of using the username and password combination.
When a web server is in charge of authentication, it is normally possible to
set the ``REMOTE_USER`` environment variable so that it can be used in the
underlying application. Keystone can be configured to use that environment
variable if set, so that the authentication is handled by the web server.
Configuration
=============
In Identity API v2, there is no way to disable external authentication. In
order to activate the external authentication mechanism for Identity API v3,
the ``external`` method must be in the list of enabled authentication methods.
By default it is enabled, so if you don't want to use external authentication,
remove it from the ``methods`` option in the ``auth`` section.
To configure the plugin that should be used set the ``external`` option again
in the ``auth`` section. There are two external authentication method plugins
provided by Keystone:
* ``DefaultDomain``: This plugin won't take into account the domain information
that the external authentication method may pass down to Keystone and will
always use the configured default domain. The ``REMOTE_USER`` variable is the
username. This is the default if no plugin is given.
* ``Domain``: This plugin expects that the ``REMOTE_DOMAIN`` variable contains
the domain for the user. If this variable is not present, the configured
default domain will be used. The ``REMOTE_USER`` variable is the username.
.. CAUTION::
You should disable the external auth method if you are currently using
federation. External auth and federation both use the ``REMOTE_USER``
variable. Since both the mapped and external plugin are being invoked to
validate attributes in the request environment, it can cause conflicts.
For example, imagine there are two distinct users with the same username
`foo`, one in the `Default` domain while the other is in the `BAR` domain.
The external Federation modules (i.e. mod_shib) sets the ``REMOTE_USER``
attribute to `foo`. The external auth module also tries to set the
``REMOTE_USER`` attribute to `foo` for the `Default` domain. The
federated mapping engine maps the incoming identity to `foo` in the `BAR`
domain. This results in user_id conflict since both are using different
user_ids to set `foo` in the `Default` domain and the `BAR` domain.
To disable this, simply remove `external` from the `methods` option in
`keystone.conf`::
methods = external,password,token,oauth1
Using HTTPD authentication
==========================
Web servers like Apache HTTP support many methods of authentication. Keystone
can profit from this feature and let the authentication be done in the web
server, that will pass down the authenticated user to Keystone using the
``REMOTE_USER`` environment variable. This user must exist in advance in the
identity backend to get a token from the controller.
To use this method, Keystone should be running on :doc:`HTTPD <apache-httpd>`.
X.509 example
-------------
The following snippet for the Apache conf will authenticate the user based on
a valid X.509 certificate from a known CA::
<VirtualHost _default_:5000>
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl.cert
SSLCertificateKeyFile /etc/ssl/private/ssl.key
SSLCACertificatePath /etc/ssl/allowed_cas
SSLCARevocationPath /etc/ssl/allowed_cas
SSLUserName SSL_CLIENT_S_DN_CN
SSLVerifyClient require
SSLVerifyDepth 10
(...)
</VirtualHost>
Developing a WSGI middleware for authentication
===============================================
In addition to the method described above, it is possible to implement other
custom authentication mechanisms using the ``REMOTE_USER`` WSGI environment
variable.
.. ATTENTION::
Please note that even if it is possible to develop a custom authentication
module, it is preferable to use the modules in the HTTPD server. Such
authentication modules in webservers like Apache have normally undergone
years of development and use in production systems and are actively
maintained upstream. Developing a custom authentication module that
implements the same authentication as an existing Apache module likely
introduces a higher security risk.
If you find you must implement a custom authentication mechanism, you will need
to develop a custom WSGI middleware pipeline component. This middleware should
set the environment variable ``REMOTE_USER`` to the authenticated username.
Keystone then will assume that the user has been already authenticated upstream
and will not try to authenticate it. However, as with HTTPD authentication, the
user must exist in advance in the identity backend so that a proper token can
be issued.
Your code should set the ``REMOTE_USER`` if the user is properly authenticated,
following the semantics below:
.. code-block:: python
from keystone.common import wsgi
from keystone import exception
class MyMiddlewareAuth(wsgi.Middleware):
def __init__(self, *args, **kwargs):
super(MyMiddlewareAuth, self).__init__(*args, **kwargs)
def process_request(self, request):
if request.environ.get('REMOTE_USER', None) is not None:
# Assume that it is authenticated upstream
return self.application
if not self.is_auth_applicable(request):
# Not applicable
return self.application
username = self.do_auth(request)
if username is not None:
# User is authenticated
request.environ['REMOTE_USER'] = username
else:
# User is not authenticated, render exception
raise exception.Unauthorized("Invalid user")
Pipeline configuration
----------------------
Once you have your WSGI middleware component developed you have to add it to
your pipeline. The first step is to add the middleware to your configuration
file. Assuming that your middleware module is
``keystone.middleware.MyMiddlewareAuth``, you can configure it in your
``keystone-paste.ini`` as::
[filter:my_auth]
paste.filter_factory = keystone.middleware.MyMiddlewareAuth.factory
The second step is to add your middleware to the pipeline. The exact place
where you should place it will depend on your code (i.e. if you need for
example that the request body is converted from JSON before perform the
authentication you should place it after the ``json_body`` filter) but it
should be set before the ``public_service`` (for the ``public_api`` pipeline)
or ``admin_service`` (for the ``admin_api`` pipeline), since they consume
authentication.
For example, if the original pipeline looks like this::
[pipeline:public_api]
pipeline = url_normalize token_auth admin_token_auth json_body debug ec2_extension user_crud_extension public_service
Your modified pipeline might then look like this::
[pipeline:public_api]
pipeline = url_normalize token_auth admin_token_auth json_body my_auth debug ec2_extension user_crud_extension public_service