[Openstack] Bug Found: Support domain-specific Identity Backends

Miller, Mark M (EB SW Cloud - R&D - Corvallis) mark.m.miller at hp.com
Fri Aug 23 21:56:50 UTC 2013


I am still working my way through the new multi-ldap split backend code in an attempt to get it working locally. I have isolated one bug and am looking into the next one. The setup is still the same as described in the previous email below.

The first bug:

File "/keystone/common/dependency.py" has a method named "requires" which is a decorator for the "Assignment" class of both files "/keystone/assignment/backends/ldap.py" and "/keystone/assignment/backends/sql.py".

File "/keystone/assignment/backends/ldap.py":

@dependency.requires('identity_api')
class Assignment(assignment.Driver):

File "/keystone/assignment/backends/sql.py":

@dependency.requires('identity_api')
class Assignment(sql.Base, assignment.Driver):


When I specify the following contents for file "/etc/keystone/domains/keystone.Default.conf" I am telling Keystone to use an SQL backend for the default domain:

[assignment]
driver = keystone.assignment.backends.sql.Assignment

[identity]
driver = keystone.identity.backends.sql.Identity

[ldap]

However, there is a problem with the following line in method requires of file "dependency.py":

    def wrapper(self, *args, **kwargs):
        """Inject each dependency from the registry."""
        self.__wrapped_init__(*args, **kwargs)

        for dependency in self._dependencies:
            if dependency not in REGISTRY:
                if dependency in _future_dependencies:
                    _future_dependencies[dependency] += [self]
                else:
                    _future_dependencies[dependency] = [self]

                continue

            setattr(self, dependency, REGISTRY[dependency])

because it is passing arguments to the constructor of the Assignment class in file "/keystone/assignment/backends/sql.py" which extends the sql.Base class (i.e. class Base(object) of file "/keystone/common/sql/core.py") that extends class "object" which does not accept any parameters for its constructor. It took me a full day to pin this one down.

To get around this problem I added the following lines to class Base(object) of file "/keystone/common/sql/core.py" in order to accept any number of parameters:

    def __init__(self, *args, **kwargs):
        super(Base, self).__init__()


Now onto the second bug. I am still chasing this one down and have added quite a few log lines to debug it. Somehow the domain_id is not getting translated into a valid domain_scope.

2013-08-23 14:51:41,803 INFO sqlalchemy.engine.base.Engine ('MyDomain',)
2013-08-23 14:51:41     INFO [sqlalchemy.engine.base.Engine] ('MyDomain',)
2013-08-23 14:51:41     INFO [keystone.identity.core] ************ method _load_driver, assignment_api = <keystone.assignment.core.Manager object at 0x20bbd10>, domain_id = 464bdc5784a446378a85f99a25d216b4
2013-08-23 14:51:41     INFO [keystone.identity.core] ########## method _load_driver, domain_config = {'cfg': <oslo.config.cfg.ConfigOpts object at 0x3143710>}
2013-08-23 14:51:41     INFO [keystone.openstack.common.importutils] ******** import_object: import_str=keystone.identity.backends.ldap.Identity, args=(<oslo.config.cfg.ConfigOpts object at 0x3143710>,), kwargs={}
2013-08-23 14:51:41     INFO [keystone.openstack.common.importutils] ******** import_class: mod_str=keystone.identity.backends.ldap, _sep=., class_str=Identity
2013-08-23 14:51:41     INFO [keystone.openstack.common.importutils] ******** import_class: attr = <class 'keystone.identity.backends.ldap.Identity'>
2013-08-23 14:51:41     INFO [keystone.common.dependency] *********** requires:wrapper: args=(<oslo.config.cfg.ConfigOpts object at 0x3143710>,), kwargs={}
2013-08-23 14:51:41     INFO [keystone.common.dependency] *********** requires:wrapper: class=keystone.identity.backends.ldap.Identity
2013-08-23 14:51:41     INFO [keystone.common.dependency] *********** requires:wrapper: after __Wraped_init__
2013-08-23 14:51:41     INFO [keystone.identity.core] *************** class Manager, method:_get_domain_id_and_driver  domain_scope = None**************
2013-08-23 14:51:41     INFO [keystone.identity.core] *************** class Manager, method:_normailize_scope, domain_scope = None**************
2013-08-23 14:51:41     INFO [keystone.identity.core] *************** class Manager, method:_select_identity_driver, domain_id = default, driver = <keystone.identity.backends.ldap.Identity object at 0x3140d10> **************
2013-08-23 14:51:41     INFO [keystone.identity.core] *************** class Manager, method:get_user, driver = <keystone.identity.backends.ldap.Identity object at 0x3140d10>, domain_scope=None **************
2013-08-23 14:51:41     INFO [keystone.identity.core] *************** class Manager, method:get_user, user_id = mark.m.miller at hp.com, driver.is_domain_aware= False **************
2013-08-23 14:51:42     INFO [keystone.identity.core] *************** in class Manager9b **************
2013-08-23 14:51:42     INFO [keystone.identity.core] *************** in class Manager9c **************
2013-08-23 14:51:42     INFO [keystone.identity.core] *************** class Manager, method:_set_domain_id, domain_id = default **************

From: Miller, Mark M (EB SW Cloud - R&D - Corvallis)
Sent: Wednesday, August 21, 2013 4:33 PM
To: 'Dolph Mathews'; openstack at lists.openstack.org
Subject: Possible Bug: Support domain-specific Identity Backends

Hello,

I am trying to test multiple split LDAP frontends based off of multiple domains (i.e. one LDAP server per domain).  I have the identity and ldap sections of file "keystone.conf" configured as follows:

keystone.conf:


[identity]
# driver = keystone.identity.backends.sql.Identity
domain_specific_drivers_enabled = True
domain_config_dir = /etc/keystone/domains

# This references the domain to use for all Identity API v2 requests (which are
# not aware of domains). A domain with this ID will be created for you by
# keystone-manage db_sync in migration 008.  The domain referenced by this ID
# cannot be deleted on the v3 API, to prevent accidentally breaking the v2 API.
# There is nothing special about this domain, other than the fact that it must
# exist to order to maintain support for your v2 clients.
default_domain_id = default

[ldap]

I have 2 domains (Default and MyDomain) and in the domains directory I have 2 files with identical content named "keystone.Default.conf" and "keystone.MyDomain.conf".

[identity]
driver = keystone.identity.backends.ldap.Identity

[ldap]
# url = "ldap://ldap.hp.com:389"
url = "ldaps://ldap.hp.com:636"
user = "cn=CloudOSKeystoneDev, ou=Applications, o=hp.com"
password = "secretword"
suffix = "o=hp.com"

# suffix = cn=example,cn=com
use_dumb_member = False
allow_subtree_delete = False
# dumb_member = cn=dumb,dc=example,dc=com

# Maximum results per page; a value of zero ('0') disables paging (default)
page_size = 0

# The LDAP dereferencing option for queries. This can be either 'never',
# 'searching', 'always', 'finding' or 'default'. The 'default' option falls
# back to using default dereferencing configured by your ldap.conf.
alias_dereferencing = never

# The LDAP scope for queries, this can be either 'one'
# (onelevel/singleLevel) or 'sub' (subtree/wholeSubtree)
query_scope = sub

user_tree_dn = ou=People,o=hp.com
user_filter = "(hpStatus=Active)"
user_objectclass = hpPerson
user_domain_id_attribute = Groups
user_id_attribute = uid
user_name_attribute = cn
user_mail_attribute = mail
user_pass_attribute = userPassword
user_enabled_mask = 0
# user_enabled_attribute =
# user_enabled_default =
user_attribute_ignore = tenant_id,tenants
user_allow_create = False
user_allow_update = False
user_allow_delete = False
user_enabled_emulation = False
user_enabled_emulation_dn = Noneuse_tls = False

use_tls = False
tls_cacertfile =  "/etc/keystone/ssl/certs/hpca2ssG2_ns.cer"
tls_req_cert = demand

I also have a role assigned to user mark.m.miller at hp.com<mailto:mark.m.miller at hp.com> for project "myapp" in domain "MyDomain" and can get a token with the following JSON script:

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "name": "MyDomain"
                    },
                    "id": "mark.m.miller at hp.com<mailto:mark.m.miller at hp.com>",
                    "password": "secretword"
                }
            }
        },
        "scope": {
            "project": {
                "domain": {
                    "name": "MyDomain"
                },
                "name": "myapp"
            }
        }
    }
}

If I change the domain name  to "Default" in the JSON script above I get an unauthorized response as expected. Now I wanted to make sure that the Keystone code was indeed accessing the correct configuration file in directory "/etc/keystone/domains" so I changed the contents of the file "keystone.Default.conf" to:

[identity]
driver = keystone.identity.backends.sql.Identity

[ldap]

In order to force Keystone to use an SQL Identity backend for the default domain. When I do I get the following error for the token request above (Note: the INFO log lines with "********" in them were added by me):

2013-08-21 16:21:38    DEBUG [routes.middleware] Matched POST /auth/tokens
2013-08-21 16:21:38    DEBUG [routes.middleware] Route path: '{path_info:.*}', defaults: {'controller': <keystone.contrib.s3.core.S3Extension object at 0x3ccb810>}
2013-08-21 16:21:38    DEBUG [routes.middleware] Match dict: {'controller': <keystone.contrib.s3.core.S3Extension object at 0x3ccb810>, 'path_info': '/auth/tokens'}
2013-08-21 16:21:38    DEBUG [routes.middleware] Matched POST /auth/tokens
2013-08-21 16:21:38    DEBUG [routes.middleware] Route path: '{path_info:.*}', defaults: {'controller': <keystone.common.wsgi.ComposingRouter object at 0x3cd5550>}
2013-08-21 16:21:38    DEBUG [routes.middleware] Match dict: {'controller': <keystone.common.wsgi.ComposingRouter object at 0x3cd5550>, 'path_info': '/auth/tokens'}
2013-08-21 16:21:38    DEBUG [routes.middleware] Matched POST /auth/tokens
2013-08-21 16:21:38    DEBUG [routes.middleware] Route path: '/auth/tokens', defaults: {'action': u'authenticate_for_token', 'controller': <keystone.auth.controllers.Auth object at 0x3ccb410>}
2013-08-21 16:21:38    DEBUG [routes.middleware] Match dict: {'action': u'authenticate_for_token', 'controller': <keystone.auth.controllers.Auth object at 0x3ccb410>}
2013-08-21 16:21:38     INFO [keystone.identity.core] ***************  class Manager, method:__init__ **************
2013-08-21 16:21:38,570 INFO sqlalchemy.engine.base.Engine SELECT domain.id AS domain_id, domain.name AS domain_name, domain.enabled AS domain_enabled, domain.extra AS domain_extra
FROM domain
WHERE domain.name = %s
2013-08-21 16:21:38     INFO [sqlalchemy.engine.base.Engine] SELECT domain.id AS domain_id, domain.name AS domain_name, domain.enabled AS domain_enabled, domain.extra AS domain_extra
FROM domain
WHERE domain.name = %s
2013-08-21 16:21:38,571 INFO sqlalchemy.engine.base.Engine ('MyDomain',)
2013-08-21 16:21:38     INFO [sqlalchemy.engine.base.Engine] ('MyDomain',)
2013-08-21 16:21:38,574 INFO sqlalchemy.engine.base.Engine SELECT project.id AS project_id, project.name AS project_name, project.domain_id AS project_domain_id, project.description AS project_description, project.enabled AS project_enabled, project.extra AS project_extra
FROM project
WHERE project.name = %s AND project.domain_id = %s
2013-08-21 16:21:38     INFO [sqlalchemy.engine.base.Engine] SELECT project.id AS project_id, project.name AS project_name, project.domain_id AS project_domain_id, project.description AS project_description, project.enabled AS project_enabled, project.extra AS project_extra
FROM project
WHERE project.name = %s AND project.domain_id = %s
2013-08-21 16:21:38,575 INFO sqlalchemy.engine.base.Engine ('myapp', '464bdc5784a446378a85f99a25d216b4')
2013-08-21 16:21:38     INFO [sqlalchemy.engine.base.Engine] ('myapp', '464bdc5784a446378a85f99a25d216b4')
2013-08-21 16:21:38     INFO [keystone.identity.core] ***************  class Manager, method:__init__ **************
2013-08-21 16:21:38,580 INFO sqlalchemy.engine.base.Engine SELECT domain.id AS domain_id, domain.name AS domain_name, domain.enabled AS domain_enabled, domain.extra AS domain_extra
FROM domain
WHERE domain.name = %s
2013-08-21 16:21:38     INFO [sqlalchemy.engine.base.Engine] SELECT domain.id AS domain_id, domain.name AS domain_name, domain.enabled AS domain_enabled, domain.extra AS domain_extra
FROM domain
WHERE domain.name = %s
2013-08-21 16:21:38,581 INFO sqlalchemy.engine.base.Engine ('Default',)
2013-08-21 16:21:38     INFO [sqlalchemy.engine.base.Engine] ('Default',)
2013-08-21 16:21:38    ERROR [keystone.common.wsgi] object.__init__() takes no parameters
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/keystone/common/wsgi.py", line 237, in __call__
    result = method(context, **params)
  File "/usr/local/lib/python2.7/dist-packages/keystone/auth/controllers.py", line 287, in authenticate_for_token
    self.authenticate(context, auth_info, auth_context)
  File "/usr/local/lib/python2.7/dist-packages/keystone/auth/controllers.py", line 344, in authenticate
    auth_context)
  File "/usr/local/lib/python2.7/dist-packages/keystone/auth/plugins/password.py", line 103, in authenticate
    user_info = UserAuthInfo(auth_payload)
  File "/usr/local/lib/python2.7/dist-packages/keystone/auth/plugins/password.py", line 34, in __init__
    self._validate_and_normalize_auth_data(auth_payload)
  File "/usr/local/lib/python2.7/dist-packages/keystone/auth/plugins/password.py", line 87, in _validate_and_normalize_auth_data
    user_ref = self.identity_api.get_user(user_id)
  File "/usr/local/lib/python2.7/dist-packages/keystone/identity/core.py", line 170, in wrapper
    self.driver, self.assignment_api)
  File "/usr/local/lib/python2.7/dist-packages/keystone/identity/core.py", line 126, in setup_domain_drivers
    names[1])
  File "/usr/local/lib/python2.7/dist-packages/keystone/identity/core.py", line 106, in _load_config
    self._load_driver(assignment_api, domain)
  File "/usr/local/lib/python2.7/dist-packages/keystone/identity/core.py", line 83, in _load_driver
    domain_config['cfg'].identity.driver, domain_config['cfg']))
  File "/usr/local/lib/python2.7/dist-packages/keystone/openstack/common/importutils.py", line 40, in import_object
    return import_class(import_str)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/keystone/common/dependency.py", line 51, in wrapper
    self.__wrapped_init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters
2013-08-21 16:21:38     INFO [access] 15.253.57.88 - - [21/Aug/2013:23:21:38 +0000] "POST https://havanatest:35357/v3/auth/tokens HTTP/1.0" 400 100

Mark

From: Dolph Mathews [mailto:dolph.mathews at gmail.com]
Sent: Monday, August 19, 2013 4:33 PM
To: openstack at lists.openstack.org<mailto:openstack at lists.openstack.org>
Subject: [Openstack] Fwd: [keystone] Support domain-specific Identity Backends


On Mon, Aug 19, 2013 at 6:09 PM, Miller, Mark M (EB SW Cloud - R&D - Corvallis) <mark.m.miller at hp.com<mailto:mark.m.miller at hp.com>> wrote:
Hello Dolph,

We have recently been looking for a way to access multiple LDAP servers from a single Keystone server. It looks like the code you just finished provides this functionality. Am I correct?

I assume you're referring to this blueprint:

  https://blueprints.launchpad.net/keystone/+spec/multiple-ldap-servers

Which was implemented in a commit by henry nash:

  https://review.openstack.org/#/c/39530/

If so, do you have any sample configuration files that demonstrate how it is implemented?

The change in itself describes basic impact on configuration:

  https://review.openstack.org/#/c/39530/21/doc/source/configuration.rst

Subsequent doc work is being tracked against openstack-manuals:

  https://bugs.launchpad.net/openstack-manuals/+bug/1209255


Regards,

Mark Miller




--

-Dolph



--

-Dolph
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openstack.org/pipermail/openstack/attachments/20130823/903ee15e/attachment.html>


More information about the Openstack mailing list