Pass client IP to keystoneauth1 session

This passes the client IP to the keystoneauth1 Session's
original_ip parameter.

This sets the Forwarder HTTP header so that when the request
lands in Keystone the request can actually be interpreted who
made the request and not only that it was proxied by Horizon.

  Forwarded: for=100.64.10.1;by=openstack_auth keystoneauth1/4.4.0 python-requests/2.25.1 CPython/3.6.8

In the above example header the 100.64.10.1 is the client IP
that is sent from a load balancer in the X-Forwarded-For header
while the actual REMOTE_ADDR in the HTTP request is the load balancers
IP address.

Change-Id: I52da9dcd7fb6b1ac46852718f285795628121e26
This commit is contained in:
Tobias Urdin 2022-04-25 08:31:33 +00:00
parent 2bb253800a
commit 6a3fa87cde
3 changed files with 31 additions and 14 deletions

View File

@ -111,22 +111,27 @@ class KeystoneBackend(object):
plugin, unscoped_auth = self._get_auth_backend(auth_url, **kwargs)
client_ip = utils.get_client_ip(request)
session = utils.get_session(original_ip=client_ip)
# the recent project id a user might have set in a cookie
recent_project = None
if request:
# Grab recent_project found in the cookie, try to scope
# to the last project used.
recent_project = request.COOKIES.get('recent_project')
unscoped_auth_ref = plugin.get_access_info(unscoped_auth)
unscoped_auth_ref = plugin.get_access_info(unscoped_auth,
session=session)
# Check expiry for our unscoped auth ref.
self._check_auth_expiry(unscoped_auth_ref)
domain_name = kwargs.get('user_domain_name', None)
domain_auth, domain_auth_ref = plugin.get_domain_scoped_auth(
unscoped_auth, unscoped_auth_ref, domain_name)
unscoped_auth, unscoped_auth_ref, domain_name, session=session)
scoped_auth, scoped_auth_ref = plugin.get_project_scoped_auth(
unscoped_auth, unscoped_auth_ref, recent_project=recent_project)
unscoped_auth, unscoped_auth_ref, recent_project=recent_project,
session=session)
# Abort if there are no projects for this user and a valid domain
# token has not been obtained
@ -207,7 +212,6 @@ class KeystoneBackend(object):
request.session.set_expiry(session_time)
keystone_client_class = utils.get_keystone_client().Client
session = utils.get_session()
scoped_client = keystone_client_class(session=session,
auth=scoped_auth)

View File

@ -99,17 +99,19 @@ class BasePlugin(object, metaclass=abc.ABCMeta):
msg = _('Unable to retrieve authorized domains.')
raise exceptions.KeystoneRetrieveDomainsException(msg)
def get_access_info(self, keystone_auth):
def get_access_info(self, keystone_auth, session=None):
"""Get the access info from an unscoped auth
This function provides the base functionality that the
plugins will use to authenticate and get the access info object.
:param keystone_auth: keystoneauth1 identity plugin
:param session: keystoneauth1 session to use otherwise gets one
:raises: exceptions.KeystoneAuthException on auth failure
:returns: keystoneclient.access.AccessInfo
"""
session = utils.get_session()
if session is None:
session = utils.get_session()
try:
unscoped_auth_ref = keystone_auth.get_access(session)
@ -140,7 +142,7 @@ class BasePlugin(object, metaclass=abc.ABCMeta):
return unscoped_auth_ref
def get_project_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
recent_project=None):
recent_project=None, session=None):
"""Get the project scoped keystone auth and access info
This function returns a project scoped keystone token plugin
@ -149,10 +151,13 @@ class BasePlugin(object, metaclass=abc.ABCMeta):
:param unscoped_auth: keystone auth plugin
:param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
:param recent_project: project that we should try to scope to
:param session: keystoneauth1 session to use otherwise gets one
:return: keystone token auth plugin, AccessInfo object
"""
if session is None:
session = utils.get_session()
auth_url = unscoped_auth.auth_url
session = utils.get_session()
projects = self.list_projects(
session, unscoped_auth, unscoped_auth_ref)
@ -187,7 +192,7 @@ class BasePlugin(object, metaclass=abc.ABCMeta):
return scoped_auth, scoped_auth_ref
def get_domain_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
domain_name=None):
domain_name=None, session=None):
"""Get the domain scoped keystone auth and access info
This function returns a domain scoped keystone token plugin
@ -196,9 +201,12 @@ class BasePlugin(object, metaclass=abc.ABCMeta):
:param unscoped_auth: keystone auth plugin
:param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
:param domain_name: domain that we should try to scope to
:param session: keystoneauth1 session to use otherwise gets one
:return: keystone token auth plugin, AccessInfo object
"""
session = utils.get_session()
if session is None:
session = utils.get_session()
auth_url = unscoped_auth.auth_url
if domain_name:
@ -235,7 +243,7 @@ class BasePlugin(object, metaclass=abc.ABCMeta):
return domain_auth, domain_auth_ref
def get_system_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
system_scope):
system_scope, session=None):
"""Get the system scoped keystone auth and access info
This function returns a system scoped keystone token plugin
@ -244,9 +252,12 @@ class BasePlugin(object, metaclass=abc.ABCMeta):
:param unscoped_auth: keystone auth plugin
:param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
:param system_scope: system that we should try to scope to
:param session: keystoneauth1 session to use otherwise gets one
:return: keystone token auth plugin, AccessInfo object
"""
session = utils.get_session()
if session is None:
session = utils.get_session()
auth_url = unscoped_auth.auth_url
system_auth = None

View File

@ -267,7 +267,8 @@ def switch(request, tenant_id, redirect_field_name=auth.REDIRECT_FIELD_NAME):
tenant_id, request.user.username)
endpoint, __ = utils.fix_auth_url_version_prefix(request.user.endpoint)
session = utils.get_session()
client_ip = utils.get_client_ip(request)
session = utils.get_session(original_ip=client_ip)
# Keystone can be configured to prevent exchanging a scoped token for
# another token. Always use the unscoped token for requesting a
# scoped token.
@ -421,7 +422,8 @@ def switch_system_scope(request, redirect_field_name=auth.REDIRECT_FIELD_NAME):
LOG.debug('Switching to system scope for user "%s".', request.user.username)
endpoint, __ = utils.fix_auth_url_version_prefix(request.user.endpoint)
session = utils.get_session()
client_ip = utils.get_client_ip(request)
session = utils.get_session(original_ip=client_ip)
# Keystone can be configured to prevent exchanging a scoped token for
# another token. Always use the unscoped token for requesting a
# scoped token.