Allow Castellan to Support different types of Keystone Auth

Blueprint: https://blueprints.launchpad.net/castellan/+spec/remove-keystone-dependency

Problem Description

Currently in Castellan in order to obtain access to Barbican via the Barbican Key manager a context containing a valid Keystone auth-token must be provided.

The Swift Keymaster under development[1] will access Castellan in order to obtain Secrets from Barbican, but would like to have a Keystone service user with access to all keys and in the future be able to have independent user access. For a service user, a context containing a Keystone username and password will be used. Castellan must support this.

Proposed Change

The proposed change is to allow Castellan to be able to check the context for the Barbican Key Manager and be able to determine what type of Keystone authentication function to use.

There will be a new type of hierarchal context object which will be passed from the user/service to Castellan. It will be used instead of oslo.context.

The hierarchal context will consist of a Credential object as the parent class and the children will be:

1.) TokenCredential, for authenticating with a token.

2.) PasswordCredential, for authenticating with a username and password.

3.) CertificateCredential, for authenticating with a certificate.

The context is first checked to see what type of object it is, after that we determine which Keystone auth-type to use.

Note

oslo.context still needs to be supported until it is deprecated in the future. tenant and project_id should be backwards compatible. Patch https://review.openstack.org/#/c/235671/ adds check for project_id, It allows avoidance of confusion between tenant and project_id and is necessary if passing a keystone auth-middleware object as context.

Example Usage within the swift keymaster:

from castellan.common.objects import symmetric_key
from castellan import credential
from castellan import key_manager

CONF = <swift conf>

# If keystone auth-middleware exists then it provides the authentication
# for the logged in user[2].
ks_context = env.get('keystone.token_auth').user

# Depending on how the configuration is setup, then a different 'credential` object
# will be created and processed by Castellan. More information on this can be
# found in examples below.
context = credential.credential_factory(context=ks_context, conf=CONF)

# create a key
key = symmetric_key.SymmetricKey('aes', 128, '==8hykeh')
manager = key_manager.API(configuration=CONF)
stored_key_id = manager.store(context, key)

The swift configuration must be altered to contain certain variables that Castellan will use for authentication.

For password-based authentication the user must provide the following in the Swift Configuration:

[castellan]
auth_type = 'password'
username = 'swift'
password = 'xswift'
project_id = '1a4a0618b306462c9830f876b0bd6af2'
domain_id = '5abc43'

Note

The above is an example on how it can be used in the Swift Keymaster. It is just provided for demonstration purposes.

The auth_type variable will be used in order to determine what type of credential Castellan should create. The possible auth_types will be token, password and certificate. Castellan will contain a context factory in order to process the different auth_types. Below is an example of the context factory.

def credential_factory(context=None, conf=None):
    if CONF.castellan.auth_type == 'token':
        if context:
            auth_token = context.auth_token
        else:
            auth_token = CONF.castellan.auth_token

        context = catstellan.KeystoneTokenContext(auth_token,
                                                  CONF.castellan.auth_url,
                                                  CONF.castellan.project_id)
    elif CONF.castellan.auth_type == 'password':
        context = castellan.PasswordContext(CONF.castellan.username,
                                            CONF.castellan.password,
                                            CONF.castellan.auth_url,
                                            CONF.castellan.project_id,
                                            CONF.castellan.domain_id)
    elif CONF.castellan.auth_type == 'certificate':
        context = castellan.CertificateContext(CONF.castellan.public_key,
                                               CONF.castellan.private_key)

    return context

Alternatives

A Keystone Auth-Token can be derived using Username and Password. An oslo.context object containing the Auth-Token can be created and passed to a Castellan call.

Data model impact

None

REST API impact

None

Security impact

If v3.Password is being used, then the username, password, project_id, and domain information(id or name) must be stored.

Notifications & Audit Impact

None

Python and Command Line Client Impact

None

Other end user impact

None

Performance Impact

None

Other deployer impact

None

Developer impact

Developer has more options of context that can be passed to Castellan.

Implementation

Assignee(s)

Primary assignee:

diazjf

Other contributors:

rellerreller

Work Items

1. Create support for multiple Keystone auth types in Castellan for the Barbican Key manager. 2. Add documentation on how to generate context for the Barbican Key Manager.

Dependencies

None

Testing

New unit tests and functional tests need to be added.

Documentation Impact

Castellan documentation must be updated to include these changes.

References

[1] https://github.com/openstack/swift-specs/blob/master/specs/in_progress/at_rest_encryption.rst [2] https://github.com/openstack/keystonemiddleware/blob/1047cececf68c3c22add66b6a8a1c499a667c036/keystonemiddleware/auth_token/__init__.py#L171-L174