diff --git a/config.yaml b/config.yaml index 6c19c508..9e9465d2 100644 --- a/config.yaml +++ b/config.yaml @@ -82,12 +82,6 @@ options: Admin password. To be used *for testing only*. Randomly generated by default. To retreive generated password, juju run --unit keystone/0 leader-get admin_passwd - admin-token: - type: string - default: None - description: | - Admin token. If set, this token will be used for all services instead of - being generated per service. admin-role: type: string default: 'Admin' diff --git a/hooks/keystone_context.py b/hooks/keystone_context.py index 3a65d94c..5ec53043 100644 --- a/hooks/keystone_context.py +++ b/hooks/keystone_context.py @@ -175,12 +175,11 @@ class KeystoneContext(context.OSContextGenerator): def __call__(self): from keystone_utils import ( - api_port, set_admin_token, endpoint_url, resolve_address, + api_port, endpoint_url, resolve_address, PUBLIC, ADMIN, ADMIN_DOMAIN, snap_install_requested, get_api_version, ) ctxt = {} - ctxt['token'] = set_admin_token(config('admin-token')) ctxt['api_version'] = get_api_version() ctxt['admin_role'] = config('admin-role') if ctxt['api_version'] > 2: @@ -191,6 +190,9 @@ class KeystoneContext(context.OSContextGenerator): leader_get(attribute='admin_domain_id') ctxt['default_domain_id'] = \ leader_get(attribute='default_domain_id') + # This is required prior to system-scope being implemented (Queens) + ctxt['transitional_charm_user_id'] = leader_get( + attribute='transitional_charm_user_id') ctxt['admin_port'] = determine_api_port(api_port('keystone-admin'), singlenode_mode=True) ctxt['public_port'] = determine_api_port(api_port('keystone-public'), diff --git a/hooks/keystone_hooks.py b/hooks/keystone_hooks.py index 186bebc7..e4fa8018 100755 --- a/hooks/keystone_hooks.py +++ b/hooks/keystone_hooks.py @@ -85,6 +85,7 @@ from keystone_context import fernet_enabled from keystone_utils import ( add_service_to_keystone, + bootstrap_keystone, ensure_all_service_accounts_protected_for_pci_dss_options, add_credentials_to_keystone, determine_packages, @@ -370,6 +371,7 @@ def update_all_fid_backends(): update_keystone_fid_service_provider(relation_id=rid) +@restart_on_change(restart_map(), restart_functions=restart_function_map()) def leader_init_db_if_ready(use_current_context=False): """ Initialise the keystone db if it is ready and mark it as initialised. @@ -393,6 +395,7 @@ def leader_init_db_if_ready(use_current_context=False): return migrate_database() + bootstrap_keystone(configs=CONFIGS) ensure_initial_admin(config) if CompareOpenStackReleases( os_release('keystone')) >= 'liberty': @@ -705,6 +708,12 @@ def upgrade_charm(): status_set('maintenance', 'Regenerating configuration files') CONFIGS.write_all() + # We no longer use the admin_token and need to ensure the charm has + # credentials. This call is idempotent and safe to run on existing + # deployments. + if is_leader(): + bootstrap_keystone(configs=CONFIGS) + # See LP bug 1519035 leader_init_db_if_ready() diff --git a/hooks/keystone_types.py b/hooks/keystone_types.py new file mode 100644 index 00000000..ea77b781 --- /dev/null +++ b/hooks/keystone_types.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python2 + +# Copyright 2020 Canonical Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# NOTE(fnordahl): This file needs to remain Python2 as it is used both by the +# charm code which is Python3 and the special ``manager.py`` script that is in +# place to support both Python2 and Python3 systems with the same codebase. +# +# The need for jumping through these hoops come from classic charms being +# deployed without any form of Python environment with direct dependencies. +# Subsequently we live at the grace of whatever dependencies our payload has +# in the running system. This may change underneath us as we upgrade between +# UCA pockets and across series. + +import collections + + +CharmCredentials = collections.namedtuple( + 'CharmCredentials', + ( + 'username', + 'password', + 'system_scope', + 'project_name', # For V2 and pre system scope compatibility + 'project_domain_name', # For Mitaka -> Pike (pre system scope) + 'user_domain_name', # For Mitaka -> Pike (pre system scope) + ), +) diff --git a/hooks/keystone_utils.py b/hooks/keystone_utils.py index 7b0d1488..df03e805 100644 --- a/hooks/keystone_utils.py +++ b/hooks/keystone_utils.py @@ -129,6 +129,8 @@ import keystone_context import uds_comms as uds +import keystone_types + TEMPLATES = 'templates/' # removed from original: charm-helper-sh @@ -178,7 +180,6 @@ if snap_install_requested(): KEYSTONE_LOGGER_CONF = "{}/logging.conf".format(SNAP_COMMON_KEYSTONE_DIR) SNAP_LIB_DIR = '{}/lib'.format(SNAP_COMMON_DIR) STORED_PASSWD = "{}/keystone.passwd".format(SNAP_LIB_DIR) - STORED_TOKEN = "{}/keystone.token".format(SNAP_LIB_DIR) STORED_ADMIN_DOMAIN_ID = ("{}/keystone.admin_domain_id" "".format(SNAP_LIB_DIR)) STORED_DEFAULT_DOMAIN_ID = ("{}/keystone.default_domain_id" @@ -196,7 +197,6 @@ else: KEYSTONE_LOGGER_CONF = "/etc/keystone/logging.conf" KEYSTONE_CONF_DIR = os.path.dirname(KEYSTONE_CONF) STORED_PASSWD = "/var/lib/keystone/keystone.passwd" - STORED_TOKEN = "/var/lib/keystone/keystone.token" STORED_ADMIN_DOMAIN_ID = "/var/lib/keystone/keystone.admin_domain_id" STORED_DEFAULT_DOMAIN_ID = "/var/lib/keystone/keystone.default_domain_id" SERVICE_PASSWD_PATH = '/var/lib/keystone/services.passwd' @@ -212,6 +212,7 @@ APACHE_CONF = '/etc/apache2/sites-available/openstack_https_frontend' APACHE_24_CONF = '/etc/apache2/sites-available/openstack_https_frontend.conf' MEMCACHED_CONF = '/etc/memcached.conf' +CHARM_USER = '_charm-keystone-admin' CLUSTER_RES = 'grp_ks_vips' ADMIN_DOMAIN = 'admin_domain' ADMIN_PROJECT = 'admin' @@ -772,6 +773,10 @@ def do_openstack_upgrade(configs): if is_elected_leader(CLUSTER_RES): if is_db_ready(): migrate_database() + # After an OpenStack upgrade we re-run bootstrap to make sure role + # assignments are up to date. One example is the system role + # assignment support that first appeared at Queens. + bootstrap_keystone(configs=configs) else: log("Database not ready - deferring to shared-db relation", level=INFO) @@ -821,6 +826,94 @@ def migrate_database(): leader_set({'db-initialised': True}) stop_manager_instance() + +def is_bootstrapped(): + """Determines whether Keystone has been bootstrapped. + + :returns: True when Keystone bootstrap has been run, False otherwise. + :rtype: bool + """ + return ( + leader_get('keystone-bootstrapped') is True and + leader_get('{}_passwd'.format(CHARM_USER)) is not None + ) + + +def bootstrap_keystone(configs=None): + """Runs ``keystone-manage bootstrap`` to bootstrap keystone. + + The bootstrap command is designed to be idempotent when it needs to be, + i.e. if nothing has changed it will do nothing. It is also safe to run + the bootstrap command on a already deployed Keystone. + + The bootstrap command creates resources in the ``default`` domain. It + assigns a system-scoped role to the created user and as such the charm can + use it to manage any domains resources. + + For successful operation of the ``keystoneclient`` used by the charm, we + must create initial endpoints at bootstrap time. For HA deployments these + will be replaced as soon as HA configuration is complete. + + :param configs: Registered configs + :type configs: Optional[Dict] + """ + log('Bootstrapping keystone.', level=INFO) + status_set('maintenance', 'Bootstrapping keystone') + # NOTE: The bootstrap process is necessary for the charm to be able to + # talk to Keystone. We will still rely on ``ensure_initial_admin`` to + # maintain Keystone's endpoints and the rest of the CRUD. + api_suffix = get_api_suffix() + charm_password = leader_get('{}_passwd'.format(CHARM_USER)) or pwgen(64) + subprocess.check_call(( + 'keystone-manage', 'bootstrap', + '--bootstrap-username', CHARM_USER, + '--bootstrap-password', charm_password, + '--bootstrap-project-name', ADMIN_PROJECT, + '--bootstrap-role-name', config('admin-role'), + '--bootstrap-service-name', 'keystone', + '--bootstrap-admin-url', endpoint_url( + resolve_address(ADMIN), + config('admin-port'), + api_suffix), + '--bootstrap-public-url', endpoint_url( + resolve_address(PUBLIC), + config('service-port'), + api_suffix), + '--bootstrap-internal-url', endpoint_url( + resolve_address(INTERNAL), + config('service-port'), + api_suffix), + '--bootstrap-region-id', config('region').split()[0]), + ) + # TODO: we should consider to add --immutable-roles for supported releases + # and/or make it configurable. Saving for a future change as this one is + # big enough as-is. + leader_set({ + 'keystone-bootstrapped': True, + '{}_passwd'.format(CHARM_USER): charm_password, + }) + + cmp_release = CompareOpenStackReleases(os_release('keystone')) + if configs and cmp_release < 'queens': + # For Mitaka through Pike we need to work around the lack of support + # for system scope by having a special bootstrap version of the + # policy.json that ensures the charm has access to retrieve the user ID + # created for the charm in the bootstrap process. + # + # As soon as the user ID is retrieved it will be stored in leader + # storage which will be picked up by a context and subsequently written + # to the runtime policy.json. + # + # NOTE: Remove this and the associated policy change as soon as + # support for Mitaka -> Pike is removed. + manager = get_manager() + transitional_charm_user_id = manager.resolve_user_id( + CHARM_USER, user_domain='default') + leader_set({ + 'transitional_charm_user_id': transitional_charm_user_id, + }) + configs.write_all() + # OLD @@ -850,41 +943,30 @@ def get_local_endpoint(api_suffix=None): return local_endpoint -def set_admin_token(admin_token='None'): - """Set admin token according to deployment config or use a randomly - generated token if none is specified (default). - """ - if admin_token != 'None': - log('Configuring Keystone to use a pre-configured admin token.') - token = admin_token - else: - log('Configuring Keystone to use a random admin token.') - if os.path.isfile(STORED_TOKEN): - msg = 'Loading a previously generated' \ - ' admin token from %s' % STORED_TOKEN - log(msg) - with open(STORED_TOKEN, 'r') as f: - token = f.read().strip() - else: - token = pwgen(length=64) - with open(STORED_TOKEN, 'w') as out: - out.write('%s\n' % token) - return(token) +def get_charm_credentials(): + """Retrieve credentials for use by charm when managing identity CRUD. + The bootstrap process creates a user for the charm in the default domain + and assigns a system level role. Subsequently the charm authenticates with + a system-scoped token so it can manage all domain's resources. -def get_admin_token(): - """Temporary utility to grab the admin token as configured in - keystone.conf + :returns: CharmCredentials with username, password and defaults for scoping + :rtype: collections.namedtuple[str,str,str,str,str,str] + :raises: RuntimeError """ - with open(KEYSTONE_CONF, 'r') as f: - for l in f.readlines(): - if l.split(' ')[0] == 'admin_token': - try: - return l.split('=')[1].strip() - except Exception: - error_out('Could not parse admin_token line from %s' % - KEYSTONE_CONF) - error_out('Could not find admin_token line in %s' % KEYSTONE_CONF) + charm_password = leader_get('{}_passwd'.format(CHARM_USER)) + if charm_password is None: + raise RuntimeError('Leader unit has not provided credentials required ' + 'for speaking with Keystone yet.') + + return keystone_types.CharmCredentials( + CHARM_USER, + charm_password, + 'all', + ADMIN_PROJECT, # For V2 and pre system scope compatibility + 'default', # For Mitaka -> Pike (pre system scope) + 'default', # For Mitaka -> Pike (pre system scope) + ) def is_service_present(service_name, service_type): @@ -950,7 +1032,14 @@ def create_endpoint_template_v2(manager, region, service, publicurl, adminurl, else: # delete endpoint and recreate if endpoint urls need updating. log("Updating endpoint template with new endpoint urls.") - manager.delete_endpoint_by_id(ep['id']) + # NOTE: When using the 2.0 API and not using the admin_token + # the call to delete_endpoint_by_id returns 404. + # Deleting service works and will cascade delete endpoint. + svc = manager.get_service_by_id(service_id) + manager.delete_service_by_id(service_id) + # NOTE: We do not get the service description in API v2.0 + create_service_entry(svc['name'], svc['type'], '') + service_id = manager.resolve_service_id(service) manager.create_endpoints(region=region, service_id=service_id, @@ -1133,7 +1222,7 @@ def _proxy_manager_call(path, api_version, args, kwargs): package = dict(path=path, api_version=api_version, api_local_endpoint=get_local_endpoint(), - admin_token=get_admin_token(), + charm_credentials=get_charm_credentials(), args=args, kwargs=kwargs) serialized = json.dumps(package, **JSON_ENCODE_OPTIONS) @@ -1696,7 +1785,11 @@ def ensure_all_service_accounts_protected_for_pci_dss_options(): if get_api_version() < 3: return log("Ensuring all service users are protected from PCI-DSS options") - users = list_users_for_domain(domain=SERVICE_DOMAIN) + # We want to make sure our own charm credentials are protected too, they + # only exist in DEFAULT_DOMAIN, but the called function gracefully deals + # with that. + users = [{'name': CHARM_USER}] + users += list_users_for_domain(domain=SERVICE_DOMAIN) for user in users: protect_user_account_from_pci_dss_force_change_password(user['name']) @@ -1799,7 +1892,6 @@ def add_service_to_keystone(relation_id=None, remote_unit=None): if not service_username: return - token = get_admin_token() roles = get_requested_roles(settings) service_password = create_service_credentials(service_username, new_roles=roles) @@ -1829,7 +1921,6 @@ def add_service_to_keystone(relation_id=None, remote_unit=None): relation_data = { "auth_host": resolve_address(ADMIN), "service_host": resolve_address(PUBLIC), - "admin_token": token, "service_port": config("service-port"), "auth_port": config("admin-port"), "service_username": service_username, diff --git a/hooks/manager.py b/hooks/manager.py index 6d1dc14f..db01d9dc 100755 --- a/hooks/manager.py +++ b/hooks/manager.py @@ -23,13 +23,17 @@ import stat import sys import time -from keystoneclient.v2_0 import client +from keystoneauth1 import session as ks_session +from keystoneauth1.identity import v2 as ks_identity_v2 +from keystoneauth1.identity import v3 as ks_identity_v3 +from keystoneclient.v2_0 import client as keystoneclient_v2 from keystoneclient.v3 import client as keystoneclient_v3 -from keystoneclient.auth import token_endpoint -from keystoneclient import session, exceptions +from keystoneclient import exceptions import uds_comms as uds +import keystone_types + _usage = """This file is called from the keystone_utils.py file to implement various keystone calls and functions. It is called with one parameter which is @@ -42,7 +46,7 @@ following keys: 'path': The api path on the keystone manager object. 'api_version': the keystone API version to use. 'api_local_endpoint': the local endpoint to connect to. - 'admin_token': the admin token to use with keystone. + 'charm_credentials': the credentials to use when speaking with Keystone. 'args': the non-keyword argument to supply to the keystone manager call. 'kwargs': any keyword args to supply to the keystone manager call. } @@ -75,17 +79,17 @@ else: econnrefused = exceptions.ConnectionError -def _get_keystone_manager_class(endpoint, token, api_version): +def _get_keystone_manager_class(endpoint, charm_credentials, api_version): """Return KeystoneManager class for the given API version @param endpoint: the keystone endpoint to point client at - @param token: the keystone admin_token + @param charm_credentials: the keystone credentials @param api_version: version of the keystone api the client should use @returns keystonemanager class used for interrogating keystone """ if api_version == 2: - return KeystoneManager2(endpoint, token) + return KeystoneManager2(endpoint, charm_credentials) if api_version == 3: - return KeystoneManager3(endpoint, token) + return KeystoneManager3(endpoint, charm_credentials) raise ValueError('No manager found for api version {}'.format(api_version)) @@ -118,7 +122,7 @@ def retry_on_exception(num_retries, base_delay=0, exc_type=Exception): @retry_on_exception(5, base_delay=3, exc_type=econnrefused) -def get_keystone_manager(endpoint, token, api_version=None): +def get_keystone_manager(endpoint, charm_credentials, api_version=None): """Return a keystonemanager for the correct API version If api_version has not been set then create a manager based on the endpoint @@ -131,17 +135,20 @@ def get_keystone_manager(endpoint, token, api_version=None): simplified @param endpoint: the keystone endpoint to point client at - @param token: the keystone admin_token + @param charm_credentials: the keystone credentials @param api_version: version of the keystone api the client should use @returns keystonemanager class used for interrogating keystone """ if api_version: - return _get_keystone_manager_class(endpoint, token, api_version) + return _get_keystone_manager_class( + endpoint, charm_credentials, api_version) else: if 'v2.0' in endpoint.split('/'): - manager = _get_keystone_manager_class(endpoint, token, 2) + manager = _get_keystone_manager_class( + endpoint, charm_credentials, 2) else: - manager = _get_keystone_manager_class(endpoint, token, 3) + manager = _get_keystone_manager_class( + endpoint, charm_credentials, 3) if endpoint.endswith('/'): base_ep = endpoint.rsplit('/', 2)[0] else: @@ -158,10 +165,12 @@ def get_keystone_manager(endpoint, token, api_version=None): break if version and version == 'v2.0': new_ep = base_ep + "/" + 'v2.0' - return _get_keystone_manager_class(new_ep, token, 2) + return _get_keystone_manager_class( + new_ep, charm_credentials, 2) elif version and version == 'v3': new_ep = base_ep + "/" + 'v3' - return _get_keystone_manager_class(new_ep, token, 3) + return _get_keystone_manager_class( + new_ep, charm_credentials, 3) else: return manager @@ -207,6 +216,11 @@ class KeystoneManager(object): if type == s['type']: return s['id'] + def get_service_by_id(self, service_id): + """Get a service by the service id""" + service = self.api.services.get(service_id) + return service.to_dict() + def delete_service_by_id(self, service_id): """Delete a service by the service id""" self.api.services.delete(service_id) @@ -232,9 +246,21 @@ class KeystoneManager(object): class KeystoneManager2(KeystoneManager): - def __init__(self, endpoint, token): + def __init__(self, endpoint, charm_credentials): self.api_version = 2 - self.api = client.Client(endpoint=endpoint, token=token) + auth = ks_identity_v2.Password( + auth_url=endpoint, + username=charm_credentials.username, + password=charm_credentials.password, + tenant_name=charm_credentials.project_name) + session = ks_session.Session(auth=auth) + + # NOTE: We need to also provide the local endpoint URL as an + # endpoint_override, otherwise the client will attempt to discover the + # endpoint to use in the catalog. Since we are managing said catalog + # we need to avoid situations where there is no endpoint to be found. + self.api = keystoneclient_v2.Client( + session=session, endpoint_override=endpoint) def resolve_user_id(self, name, user_domain=None): """Find the user_id of a given user""" @@ -300,11 +326,36 @@ class KeystoneManager2(KeystoneManager): class KeystoneManager3(KeystoneManager): - def __init__(self, endpoint, token): + def __init__(self, endpoint, charm_credentials): self.api_version = 3 - keystone_auth_v3 = token_endpoint.Token(endpoint=endpoint, token=token) - keystone_session_v3 = session.Session(auth=keystone_auth_v3) - self.api = keystoneclient_v3.Client(session=keystone_session_v3) + # The bootstrap process creates a user for the charm in the ``default`` + # domain and assigns a system level role. We need to specify domain + # name even when we request a system scoped token. + try: + auth = ks_identity_v3.Password( + auth_url=endpoint, + username=charm_credentials.username, + password=charm_credentials.password, + system_scope=charm_credentials.system_scope, + project_domain_name=charm_credentials.project_domain_name, + user_domain_name=charm_credentials.user_domain_name) + except TypeError: + # Support for OpenStack versions prior to Queens + auth = ks_identity_v3.Password( + auth_url=endpoint, + username=charm_credentials.username, + password=charm_credentials.password, + project_name=charm_credentials.project_name, + project_domain_name=charm_credentials.project_domain_name, + user_domain_name=charm_credentials.user_domain_name) + keystone_session_v3 = ks_session.Session(auth=auth) + + # NOTE: We need to also provide the local endpoint URL as an + # endpoint_override, otherwise the client will attempt to discover the + # endpoint to use in the catalog. Since we are managing said catalog + # we need to avoid situations where there is no endpoint to be found. + self.api = keystoneclient_v3.Client( + session=keystone_session_v3, endpoint_override=endpoint) def resolve_tenant_id(self, name, domain=None): """Find the tenant_id of a given tenant""" @@ -532,19 +583,22 @@ class KeystoneManager3(KeystoneManager): _keystone_manager = dict( api_version=None, api_local_endpoint=None, - admin_token=None, + charm_credentials=None, manager=None) -def get_manager(api_version=None, api_local_endpoint=None, admin_token=None): +def get_manager(api_version=None, api_local_endpoint=None, + charm_credentials=None): """Return a keystonemanager for the correct API version This function actually returns a singleton of the right kind of KeystoneManager (v2 or v3). If the api_version, api_local_endpoint and - admin_token haven't changed then the current _keystone_manager object is - returned, otherwise a new one is created (and thus the old one goes out of - scope and is closed). This is to that repeated calls to get_manager(...) - only results in a single authorisation request if the details don't change. + charm_credentials haven't changed then the current _keystone_manager object + is returned, otherwise a new one is created (and thus the old one goes out + of scope and is closed). This is to that repeated calls to + get_manager(...) only results in a single authorisation request if the + details don't change. + This is to speed up calls from the keystone charm into keystone and make the charm more performant. It's hoped that the complexity/performance trade-off is a good choice. @@ -552,25 +606,26 @@ def get_manager(api_version=None, api_local_endpoint=None, admin_token=None): :param api_verion: The version of the api to use or None. if None then the version is determined from the api_local_enpoint variable. :param api_local_endpoint: where to find the keystone API - :param admin_token: the token used for authentication. - :raises: RuntimeError if api_local_endpoint or admin_token is not set. + :param charm_credentials: the credentials used for authentication. + :raises: RuntimeError if api_local_endpoint or charm_credentials is not + set. :returns: a KeystoneManager derived class (possibly the singleton). """ if api_local_endpoint is None: raise RuntimeError("get_manager(): api_local_endpoint is not set") - if admin_token is None: - raise RuntimeError("get_manager(): admin_token is not set") + if charm_credentials is None: + raise RuntimeError("get_manager(): charm_credentials is not set") global _keystone_manager if (api_version == _keystone_manager['api_version'] and api_local_endpoint == _keystone_manager['api_local_endpoint'] and - admin_token == _keystone_manager['admin_token']): + charm_credentials == _keystone_manager['charm_credentials']): return _keystone_manager['manager'] # only retain the params IF getting the manager actually works _keystone_manager['manager'] = get_keystone_manager( - api_local_endpoint, admin_token, api_version) + api_local_endpoint, charm_credentials, api_version) _keystone_manager['api_version'] = api_version _keystone_manager['api_local_endpoint'] = api_local_endpoint - _keystone_manager['admin_token'] = admin_token + _keystone_manager['charm_credentials'] = charm_credentials return _keystone_manager['manager'] @@ -638,7 +693,8 @@ if __name__ == '__main__': manager = get_manager( api_version=spec['api_version'], api_local_endpoint=spec['api_local_endpoint'], - admin_token=spec['admin_token']) + charm_credentials=keystone_types.CharmCredentials._make( + spec['charm_credentials'])) _callable = manager for attr in spec['path']: _callable = getattr(_callable, attr) diff --git a/templates/icehouse/keystone.conf b/templates/icehouse/keystone.conf deleted file mode 100644 index 9f071c4f..00000000 --- a/templates/icehouse/keystone.conf +++ /dev/null @@ -1,106 +0,0 @@ -# icehouse -############################################################################### -# [ WARNING ] -# Configuration file maintained by Juju. Local changes may be overwritten. -############################################################################### -[DEFAULT] -admin_token = {{ token }} -admin_port = {{ admin_port }} -public_port = {{ public_port }} -use_syslog = {{ use_syslog }} -log_config = /etc/keystone/logging.conf -debug = {{ debug }} -verbose = {{ verbose }} -public_endpoint = {{ public_endpoint }} -admin_endpoint = {{ admin_endpoint }} -bind_host = {{ bind_host }} -public_workers = {{ workers }} -admin_workers = {{ workers }} - -[database] -{% if database_host -%} -connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} -{% else -%} -connection = sqlite:////var/lib/keystone/keystone.db -{% endif -%} -connection_recycle_time = 200 - -[identity] -driver = keystone.identity.backends.{{ identity_backend }}.Identity - -[credential] -driver = keystone.credential.backends.sql.Credential - -[trust] -driver = keystone.trust.backends.sql.Trust - -[os_inherit] - -[catalog] -driver = keystone.catalog.backends.sql.Catalog - -[endpoint_filter] - -[token] -driver = keystone.token.backends.sql.Token -provider = keystone.token.providers.uuid.Provider -expiration = {{ token_expiration }} - -{% include "parts/section-signing" %} - -[cache] - -[policy] -driver = keystone.policy.backends.sql.Policy - -[ec2] -driver = keystone.contrib.ec2.backends.sql.Ec2 - -[assignment] -driver = keystone.assignment.backends.{{ assignment_backend }}.Assignment - -[oauth1] - -[auth] -methods = external,password,token,oauth1 -password = keystone.auth.plugins.password.Password -token = keystone.auth.plugins.token.Token -oauth1 = keystone.auth.plugins.oauth1.OAuth - -[paste_deploy] -config_file = keystone-paste.ini - -[extra_headers] -Distribution = Ubuntu - -[ldap] -{% if identity_backend == 'ldap' -%} -url = {{ ldap_server }} -user = {{ ldap_user }} -password = {{ ldap_password }} -suffix = {{ ldap_suffix }} - -{% if ldap_config_flags -%} -{% for key, value in ldap_config_flags.items() -%} -{{ key }} = {{ value }} -{% endfor -%} -{% endif -%} - -{% if ldap_readonly -%} -user_allow_create = False -user_allow_update = False -user_allow_delete = False - -tenant_allow_create = False -tenant_allow_update = False -tenant_allow_delete = False - -role_allow_create = False -role_allow_update = False -role_allow_delete = False - -group_allow_create = False -group_allow_update = False -group_allow_delete = False -{% endif -%} -{% endif -%} diff --git a/templates/icehouse/logging.conf b/templates/icehouse/logging.conf deleted file mode 100644 index f84a4d73..00000000 --- a/templates/icehouse/logging.conf +++ /dev/null @@ -1,49 +0,0 @@ -# icehouse - -[loggers] -keys=root - -[formatters] -keys=normal,normal_with_name,debug - -[handlers] -keys=production,file,devel - -[logger_root] -{% if root_level -%} -level={{ root_level }} -{% else -%} -level=WARNING -{% endif -%} -handlers=file,production - -[handler_production] -class=handlers.SysLogHandler -{% if log_level -%} -level={{ log_level }} -{% else -%} -level=ERROR -{% endif -%} -formatter=normal_with_name -args=(('/dev/log'), handlers.SysLogHandler.LOG_USER) - -[handler_file] -class=FileHandler -level=DEBUG -formatter=normal_with_name -args=('/var/log/keystone/keystone.log', 'a') - -[handler_devel] -class=StreamHandler -level=NOTSET -formatter=debug -args=(sys.stdout,) - -[formatter_normal] -format=%(asctime)s %(levelname)s %(message)s - -[formatter_normal_with_name] -format=(%(name)s): %(asctime)s %(levelname)s %(message)s - -[formatter_debug] -format=(%(name)s): %(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s diff --git a/templates/kilo/keystone.conf b/templates/kilo/keystone.conf deleted file mode 100644 index fec774b8..00000000 --- a/templates/kilo/keystone.conf +++ /dev/null @@ -1,121 +0,0 @@ -# kilo -############################################################################### -# [ WARNING ] -# Configuration file maintained by Juju. Local changes may be overwritten. -############################################################################### -[DEFAULT] -admin_token = {{ token }} -use_syslog = {{ use_syslog }} -log_config = /etc/keystone/logging.conf -debug = {{ debug }} -verbose = {{ verbose }} -public_endpoint = {{ public_endpoint }} -admin_endpoint = {{ admin_endpoint }} - -[eventlet_server] -admin_bind_host = {{ bind_host }} -public_bind_host = {{ bind_host }} -public_workers = {{ workers }} -admin_workers = {{ workers }} -admin_port = {{ admin_port }} -public_port = {{ public_port }} - -[database] -{% if database_host -%} -connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} -{% else -%} -connection = sqlite:////var/lib/keystone/keystone.db -{% endif -%} -connection_recycle_time = 200 - -[identity] -driver = keystone.identity.backends.{{ identity_backend }}.Identity -{% if default_domain_id -%} -default_domain_id = {{ default_domain_id }} -{% endif -%} - -{% if api_version == 3 -%} -domain_specific_drivers_enabled = True -domain_config_dir = /etc/keystone/domains -{% endif -%} - -[credential] -driver = keystone.credential.backends.sql.Credential - -[trust] -driver = keystone.trust.backends.sql.Trust - -[os_inherit] - -[catalog] -driver = keystone.catalog.backends.sql.Catalog - -[endpoint_filter] - -[token] -driver = keystone.token.persistence.backends.sql.Token -provider = keystone.token.providers.uuid.Provider -expiration = {{ token_expiration }} - -{% include "parts/section-signing" %} - -[cache] - -[policy] -driver = keystone.policy.backends.sql.Policy - -[ec2] -driver = keystone.contrib.ec2.backends.sql.Ec2 - -[assignment] -driver = keystone.assignment.backends.{{ assignment_backend }}.Assignment - -[oauth1] - -[auth] -methods = external,password,token,oauth1 -password = keystone.auth.plugins.password.Password -token = keystone.auth.plugins.token.Token -oauth1 = keystone.auth.plugins.oauth1.OAuth - -[paste_deploy] -config_file = /etc/keystone/keystone-paste.ini - -[extra_headers] -Distribution = Ubuntu - -[ldap] -{% if identity_backend == 'ldap' -%} -url = {{ ldap_server }} -user = {{ ldap_user }} -password = {{ ldap_password }} -suffix = {{ ldap_suffix }} - -{% if ldap_config_flags -%} -{% for key, value in ldap_config_flags.items() -%} -{{ key }} = {{ value }} -{% endfor -%} -{% endif -%} - -{% if ldap_readonly -%} -user_allow_create = False -user_allow_update = False -user_allow_delete = False - -tenant_allow_create = False -tenant_allow_update = False -tenant_allow_delete = False - -role_allow_create = False -role_allow_update = False -role_allow_delete = False - -group_allow_create = False -group_allow_update = False -group_allow_delete = False -{% endif -%} -{% endif -%} - -[oslo_middleware] -# Bug #1819134 -max_request_body_size = 114688 \ No newline at end of file diff --git a/templates/liberty/policy.json b/templates/liberty/policy.json deleted file mode 100644 index 8821ba06..00000000 --- a/templates/liberty/policy.json +++ /dev/null @@ -1,382 +0,0 @@ -{% if api_version == 3 -%} -{ - "admin_required": "role:{{ admin_role }}", - "cloud_admin": "rule:admin_required and domain_id:{{ admin_domain_id }}", - "service_role": "role:service", - "service_or_admin": "rule:admin_required or rule:service_role", - "owner" : "user_id:%(user_id)s or user_id:%(target.token.user_id)s", - "admin_or_owner": "(rule:admin_required and domain_id:%(target.token.user.domain.id)s) or rule:owner", - "admin_or_cloud_admin": "rule:admin_required or rule:cloud_admin", - "admin_and_matching_domain_id": "rule:admin_required and domain_id:%(domain_id)s", - "service_admin_or_owner": "rule:service_or_admin or rule:owner", - - "default": "rule:admin_required", - - "identity:get_region": "", - "identity:list_regions": "", - "identity:create_region": "rule:cloud_admin", - "identity:update_region": "rule:cloud_admin", - "identity:delete_region": "rule:cloud_admin", - - "identity:get_service": "rule:admin_or_cloud_admin", - "identity:list_services": "rule:admin_or_cloud_admin", - "identity:create_service": "rule:cloud_admin", - "identity:update_service": "rule:cloud_admin", - "identity:delete_service": "rule:cloud_admin", - - "identity:get_endpoint": "rule:admin_or_cloud_admin", - "identity:list_endpoints": "rule:admin_or_cloud_admin", - "identity:create_endpoint": "rule:cloud_admin", - "identity:update_endpoint": "rule:cloud_admin", - "identity:delete_endpoint": "rule:cloud_admin", - - "identity:get_domain": "rule:cloud_admin or rule:admin_and_matching_domain_id", - "identity:list_domains": "rule:cloud_admin", - "identity:create_domain": "rule:cloud_admin", - "identity:update_domain": "rule:cloud_admin", - "identity:delete_domain": "rule:cloud_admin", - - "admin_and_matching_target_project_domain_id": "rule:admin_required and domain_id:%(target.project.domain_id)s", - "admin_and_matching_project_domain_id": "rule:admin_required and domain_id:%(project.domain_id)s", - "identity:get_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id", - "identity:list_projects": "rule:cloud_admin or rule:admin_and_matching_domain_id", - "identity:list_user_projects": "rule:owner or rule:admin_and_matching_domain_id", - "identity:create_project": "rule:cloud_admin or rule:admin_and_matching_project_domain_id", - "identity:update_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id", - "identity:delete_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id", - - "admin_and_matching_target_user_domain_id": "rule:admin_required and domain_id:%(target.user.domain_id)s", - "admin_and_matching_user_domain_id": "rule:admin_required and domain_id:%(user.domain_id)s", - "identity:get_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id", - "identity:list_users": "rule:cloud_admin or rule:admin_and_matching_domain_id", - "identity:create_user": "rule:cloud_admin or rule:admin_and_matching_user_domain_id", - "identity:update_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id", - "identity:delete_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id", - - "admin_and_matching_target_group_domain_id": "rule:admin_required and domain_id:%(target.group.domain_id)s", - "admin_and_matching_group_domain_id": "rule:admin_required and domain_id:%(group.domain_id)s", - "identity:get_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", - "identity:list_groups": "rule:cloud_admin or rule:admin_and_matching_domain_id", - "identity:list_groups_for_user": "rule:owner or rule:admin_and_matching_domain_id", - "identity:create_group": "rule:cloud_admin or rule:admin_and_matching_group_domain_id", - "identity:update_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", - "identity:delete_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", - "identity:list_users_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", - "identity:remove_user_from_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", - "identity:check_user_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", - "identity:add_user_to_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", - - "identity:get_credential": "rule:admin_required", - "identity:list_credentials": "rule:admin_required or user_id:%(user_id)s", - "identity:create_credential": "rule:admin_required", - "identity:update_credential": "rule:admin_required", - "identity:delete_credential": "rule:admin_required", - - "identity:ec2_get_credential": "rule:admin_or_cloud_admin or (rule:owner and user_id:%(target.credential.user_id)s)", - "identity:ec2_list_credentials": "rule:admin_or_cloud_admin or rule:owner", - "identity:ec2_create_credential": "rule:admin_or_cloud_admin or rule:owner", - "identity:ec2_delete_credential": "rule:admin_or_cloud_admin or (rule:owner and user_id:%(target.credential.user_id)s)", - - "identity:get_role": "rule:admin_or_cloud_admin", - "identity:list_roles": "rule:admin_or_cloud_admin", - "identity:create_role": "rule:cloud_admin", - "identity:update_role": "rule:cloud_admin", - "identity:delete_role": "rule:cloud_admin", - - "domain_admin_for_grants": "rule:admin_required and (domain_id:%(domain_id)s or domain_id:%(target.project.domain_id)s)", - "project_admin_for_grants": "rule:admin_required and project_id:%(project_id)s", - "identity:check_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", - "identity:list_grants": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", - "identity:create_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", - "identity:revoke_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", - - "admin_on_domain_filter" : "rule:admin_required and domain_id:%(scope.domain.id)s", - "admin_on_project_filter" : "rule:admin_required and project_id:%(scope.project.id)s", - "identity:list_role_assignments": "rule:cloud_admin or rule:admin_on_domain_filter or rule:admin_on_project_filter", - - "identity:get_policy": "rule:cloud_admin", - "identity:list_policies": "rule:cloud_admin", - "identity:create_policy": "rule:cloud_admin", - "identity:update_policy": "rule:cloud_admin", - "identity:delete_policy": "rule:cloud_admin", - - "identity:change_password": "rule:owner", - "identity:check_token": "rule:admin_or_owner", - "identity:validate_token": "rule:service_admin_or_owner", - "identity:validate_token_head": "rule:service_or_admin", - "identity:revocation_list": "rule:service_or_admin", - "identity:revoke_token": "rule:admin_or_owner", - - "identity:create_trust": "user_id:%(trust.trustor_user_id)s", - "identity:list_trusts": "", - "identity:list_roles_for_trust": "", - "identity:get_role_for_trust": "", - "identity:delete_trust": "", - - "identity:create_consumer": "rule:admin_required", - "identity:get_consumer": "rule:admin_required", - "identity:list_consumers": "rule:admin_required", - "identity:delete_consumer": "rule:admin_required", - "identity:update_consumer": "rule:admin_required", - - "identity:authorize_request_token": "rule:admin_required", - "identity:list_access_token_roles": "rule:admin_required", - "identity:get_access_token_role": "rule:admin_required", - "identity:list_access_tokens": "rule:admin_required", - "identity:get_access_token": "rule:admin_required", - "identity:delete_access_token": "rule:admin_required", - - "identity:list_projects_for_endpoint": "rule:admin_required", - "identity:add_endpoint_to_project": "rule:admin_required", - "identity:check_endpoint_in_project": "rule:admin_required", - "identity:list_endpoints_for_project": "rule:admin_required", - "identity:remove_endpoint_from_project": "rule:admin_required", - - "identity:create_endpoint_group": "rule:admin_required", - "identity:list_endpoint_groups": "rule:admin_required", - "identity:get_endpoint_group": "rule:admin_required", - "identity:update_endpoint_group": "rule:admin_required", - "identity:delete_endpoint_group": "rule:admin_required", - "identity:list_projects_associated_with_endpoint_group": "rule:admin_required", - "identity:list_endpoints_associated_with_endpoint_group": "rule:admin_required", - "identity:get_endpoint_group_in_project": "rule:admin_required", - "identity:list_endpoint_groups_for_project": "rule:admin_required", - "identity:add_endpoint_group_to_project": "rule:admin_required", - "identity:remove_endpoint_group_from_project": "rule:admin_required", - - "identity:create_identity_provider": "rule:cloud_admin", - "identity:list_identity_providers": "rule:cloud_admin", - "identity:get_identity_providers": "rule:cloud_admin", - "identity:update_identity_provider": "rule:cloud_admin", - "identity:delete_identity_provider": "rule:cloud_admin", - - "identity:create_protocol": "rule:cloud_admin", - "identity:update_protocol": "rule:cloud_admin", - "identity:get_protocol": "rule:cloud_admin", - "identity:list_protocols": "rule:cloud_admin", - "identity:delete_protocol": "rule:cloud_admin", - - "identity:create_mapping": "rule:cloud_admin", - "identity:get_mapping": "rule:cloud_admin", - "identity:list_mappings": "rule:cloud_admin", - "identity:delete_mapping": "rule:cloud_admin", - "identity:update_mapping": "rule:cloud_admin", - - "identity:create_service_provider": "rule:cloud_admin", - "identity:list_service_providers": "rule:cloud_admin", - "identity:get_service_provider": "rule:cloud_admin", - "identity:update_service_provider": "rule:cloud_admin", - "identity:delete_service_provider": "rule:cloud_admin", - - "identity:get_auth_catalog": "", - "identity:get_auth_projects": "", - "identity:get_auth_domains": "", - - "identity:list_projects_for_groups": "", - "identity:list_domains_for_groups": "", - - "identity:list_revoke_events": "", - - "identity:create_policy_association_for_endpoint": "rule:cloud_admin", - "identity:check_policy_association_for_endpoint": "rule:cloud_admin", - "identity:delete_policy_association_for_endpoint": "rule:cloud_admin", - "identity:create_policy_association_for_service": "rule:cloud_admin", - "identity:check_policy_association_for_service": "rule:cloud_admin", - "identity:delete_policy_association_for_service": "rule:cloud_admin", - "identity:create_policy_association_for_region_and_service": "rule:cloud_admin", - "identity:check_policy_association_for_region_and_service": "rule:cloud_admin", - "identity:delete_policy_association_for_region_and_service": "rule:cloud_admin", - "identity:get_policy_for_endpoint": "rule:cloud_admin", - "identity:list_endpoints_for_policy": "rule:cloud_admin", - - "identity:create_domain_config": "rule:cloud_admin", - "identity:get_domain_config": "rule:cloud_admin", - "identity:update_domain_config": "rule:cloud_admin", - "identity:delete_domain_config": "rule:cloud_admin" -} -{% else -%} -{ - "admin_required": "role:admin or is_admin:1", - "service_role": "role:service", - "service_or_admin": "rule:admin_required or rule:service_role", - "owner" : "user_id:%(user_id)s", - "admin_or_owner": "rule:admin_required or rule:owner", - "token_subject": "user_id:%(target.token.user_id)s", - "admin_or_token_subject": "rule:admin_required or rule:token_subject", - "service_admin_or_token_subject": "rule:service_or_admin or rule:token_subject", - - "default": "rule:admin_required", - - "identity:get_region": "", - "identity:list_regions": "", - "identity:create_region": "rule:admin_required", - "identity:update_region": "rule:admin_required", - "identity:delete_region": "rule:admin_required", - - "identity:get_service": "rule:admin_required", - "identity:list_services": "rule:admin_required", - "identity:create_service": "rule:admin_required", - "identity:update_service": "rule:admin_required", - "identity:delete_service": "rule:admin_required", - - "identity:get_endpoint": "rule:admin_required", - "identity:list_endpoints": "rule:admin_required", - "identity:create_endpoint": "rule:admin_required", - "identity:update_endpoint": "rule:admin_required", - "identity:delete_endpoint": "rule:admin_required", - - "identity:get_domain": "rule:admin_required", - "identity:list_domains": "rule:admin_required", - "identity:create_domain": "rule:admin_required", - "identity:update_domain": "rule:admin_required", - "identity:delete_domain": "rule:admin_required", - - "identity:get_project": "rule:admin_required", - "identity:list_projects": "rule:admin_required", - "identity:list_user_projects": "rule:admin_or_owner", - "identity:create_project": "rule:admin_required", - "identity:update_project": "rule:admin_required", - "identity:delete_project": "rule:admin_required", - - "identity:get_user": "rule:admin_required", - "identity:list_users": "rule:admin_required", - "identity:create_user": "rule:admin_required", - "identity:update_user": "rule:admin_required", - "identity:delete_user": "rule:admin_required", - "identity:change_password": "rule:admin_or_owner", - - "identity:get_group": "rule:admin_required", - "identity:list_groups": "rule:admin_required", - "identity:list_groups_for_user": "rule:admin_or_owner", - "identity:create_group": "rule:admin_required", - "identity:update_group": "rule:admin_required", - "identity:delete_group": "rule:admin_required", - "identity:list_users_in_group": "rule:admin_required", - "identity:remove_user_from_group": "rule:admin_required", - "identity:check_user_in_group": "rule:admin_required", - "identity:add_user_to_group": "rule:admin_required", - - "identity:get_credential": "rule:admin_required", - "identity:list_credentials": "rule:admin_required", - "identity:create_credential": "rule:admin_required", - "identity:update_credential": "rule:admin_required", - "identity:delete_credential": "rule:admin_required", - - "identity:ec2_get_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)", - "identity:ec2_list_credentials": "rule:admin_or_owner", - "identity:ec2_create_credential": "rule:admin_or_owner", - "identity:ec2_delete_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)", - - "identity:get_role": "rule:admin_required", - "identity:list_roles": "rule:admin_required", - "identity:create_role": "rule:admin_required", - "identity:update_role": "rule:admin_required", - "identity:delete_role": "rule:admin_required", - - "identity:check_grant": "rule:admin_required", - "identity:list_grants": "rule:admin_required", - "identity:create_grant": "rule:admin_required", - "identity:revoke_grant": "rule:admin_required", - - "identity:list_role_assignments": "rule:admin_required", - - "identity:get_policy": "rule:admin_required", - "identity:list_policies": "rule:admin_required", - "identity:create_policy": "rule:admin_required", - "identity:update_policy": "rule:admin_required", - "identity:delete_policy": "rule:admin_required", - - "identity:check_token": "rule:admin_or_token_subject", - "identity:validate_token": "rule:service_admin_or_token_subject", - "identity:validate_token_head": "rule:service_or_admin", - "identity:revocation_list": "rule:service_or_admin", - "identity:revoke_token": "rule:admin_or_token_subject", - - "identity:create_trust": "user_id:%(trust.trustor_user_id)s", - "identity:list_trusts": "", - "identity:list_roles_for_trust": "", - "identity:get_role_for_trust": "", - "identity:delete_trust": "", - - "identity:create_consumer": "rule:admin_required", - "identity:get_consumer": "rule:admin_required", - "identity:list_consumers": "rule:admin_required", - "identity:delete_consumer": "rule:admin_required", - "identity:update_consumer": "rule:admin_required", - - "identity:authorize_request_token": "rule:admin_required", - "identity:list_access_token_roles": "rule:admin_required", - "identity:get_access_token_role": "rule:admin_required", - "identity:list_access_tokens": "rule:admin_required", - "identity:get_access_token": "rule:admin_required", - "identity:delete_access_token": "rule:admin_required", - - "identity:list_projects_for_endpoint": "rule:admin_required", - "identity:add_endpoint_to_project": "rule:admin_required", - "identity:check_endpoint_in_project": "rule:admin_required", - "identity:list_endpoints_for_project": "rule:admin_required", - "identity:remove_endpoint_from_project": "rule:admin_required", - - "identity:create_endpoint_group": "rule:admin_required", - "identity:list_endpoint_groups": "rule:admin_required", - "identity:get_endpoint_group": "rule:admin_required", - "identity:update_endpoint_group": "rule:admin_required", - "identity:delete_endpoint_group": "rule:admin_required", - "identity:list_projects_associated_with_endpoint_group": "rule:admin_required", - "identity:list_endpoints_associated_with_endpoint_group": "rule:admin_required", - "identity:get_endpoint_group_in_project": "rule:admin_required", - "identity:list_endpoint_groups_for_project": "rule:admin_required", - "identity:add_endpoint_group_to_project": "rule:admin_required", - "identity:remove_endpoint_group_from_project": "rule:admin_required", - - "identity:create_identity_provider": "rule:admin_required", - "identity:list_identity_providers": "rule:admin_required", - "identity:get_identity_providers": "rule:admin_required", - "identity:update_identity_provider": "rule:admin_required", - "identity:delete_identity_provider": "rule:admin_required", - - "identity:create_protocol": "rule:admin_required", - "identity:update_protocol": "rule:admin_required", - "identity:get_protocol": "rule:admin_required", - "identity:list_protocols": "rule:admin_required", - "identity:delete_protocol": "rule:admin_required", - - "identity:create_mapping": "rule:admin_required", - "identity:get_mapping": "rule:admin_required", - "identity:list_mappings": "rule:admin_required", - "identity:delete_mapping": "rule:admin_required", - "identity:update_mapping": "rule:admin_required", - - "identity:create_service_provider": "rule:admin_required", - "identity:list_service_providers": "rule:admin_required", - "identity:get_service_provider": "rule:admin_required", - "identity:update_service_provider": "rule:admin_required", - "identity:delete_service_provider": "rule:admin_required", - - "identity:get_auth_catalog": "", - "identity:get_auth_projects": "", - "identity:get_auth_domains": "", - - "identity:list_projects_for_groups": "", - "identity:list_domains_for_groups": "", - - "identity:list_revoke_events": "", - - "identity:create_policy_association_for_endpoint": "rule:admin_required", - "identity:check_policy_association_for_endpoint": "rule:admin_required", - "identity:delete_policy_association_for_endpoint": "rule:admin_required", - "identity:create_policy_association_for_service": "rule:admin_required", - "identity:check_policy_association_for_service": "rule:admin_required", - "identity:delete_policy_association_for_service": "rule:admin_required", - "identity:create_policy_association_for_region_and_service": "rule:admin_required", - "identity:check_policy_association_for_region_and_service": "rule:admin_required", - "identity:delete_policy_association_for_region_and_service": "rule:admin_required", - "identity:get_policy_for_endpoint": "rule:admin_required", - "identity:list_endpoints_for_policy": "rule:admin_required", - - "identity:create_domain_config": "rule:admin_required", - "identity:get_domain_config": "rule:admin_required", - "identity:update_domain_config": "rule:admin_required", - "identity:delete_domain_config": "rule:admin_required" -} -{% endif -%} diff --git a/templates/mitaka/keystone.conf b/templates/mitaka/keystone.conf index 6f728f0e..f6b46ed5 100644 --- a/templates/mitaka/keystone.conf +++ b/templates/mitaka/keystone.conf @@ -4,7 +4,6 @@ # Configuration file maintained by Juju. Local changes may be overwritten. ############################################################################### [DEFAULT] -admin_token = {{ token }} use_syslog = {{ use_syslog }} log_config_append = /etc/keystone/logging.conf debug = {{ debug }} @@ -111,4 +110,4 @@ admin_project_name = admin {% include "section-oslo-middleware" %} # This goes in the section above, selectively # Bug #1819134 -max_request_body_size = 114688 \ No newline at end of file +max_request_body_size = 114688 diff --git a/templates/kilo/logging.conf b/templates/mitaka/logging.conf similarity index 100% rename from templates/kilo/logging.conf rename to templates/mitaka/logging.conf diff --git a/templates/mitaka/policy.json b/templates/mitaka/policy.json index 086bb9fd..34d3cd96 100644 --- a/templates/mitaka/policy.json +++ b/templates/mitaka/policy.json @@ -1,7 +1,11 @@ {% if api_version == 3 -%} { "admin_required": "role:{{ admin_role }}", - "cloud_admin": "rule:admin_required and (token.is_admin_project:True or domain_id:{{ admin_domain_id }} or project_id:{{ service_tenant_id }})", +{% if transitional_charm_user_id %} + "cloud_admin": "rule:admin_required and (user_id:{{ transitional_charm_user_id }} or token.is_admin_project:True or domain_id:{{ admin_domain_id }} or project_id:{{ service_tenant_id }})", +{% else %} + "cloud_admin": "rule:admin_required", +{% endif %} "service_role": "role:service", "service_or_admin": "rule:admin_required or rule:service_role", "owner" : "user_id:%(user_id)s or user_id:%(target.token.user_id)s", diff --git a/templates/liberty/policy.json.v2 b/templates/mitaka/policy.json.v2 similarity index 100% rename from templates/liberty/policy.json.v2 rename to templates/mitaka/policy.json.v2 diff --git a/templates/newton/policy.json b/templates/newton/policy.json index d288cb02..c92c5f01 100644 --- a/templates/newton/policy.json +++ b/templates/newton/policy.json @@ -1,7 +1,11 @@ {% if api_version == 3 -%} { "admin_required": "role:{{ admin_role }}", - "cloud_admin": "rule:admin_required and (token.is_admin_project:True or domain_id:{{ admin_domain_id }} or project_id:{{ service_tenant_id }})", +{% if transitional_charm_user_id %} + "cloud_admin": "rule:admin_required and (user_id:{{ transitional_charm_user_id }} or token.is_admin_project:True or domain_id:{{ admin_domain_id }} or project_id:{{ service_tenant_id }})", +{% else %} + "cloud_admin": "rule:admin_required", +{% endif %} "service_role": "role:service", "service_or_admin": "rule:admin_required or rule:service_role", "owner" : "user_id:%(user_id)s or user_id:%(target.token.user_id)s", diff --git a/templates/ocata/keystone.conf b/templates/ocata/keystone.conf index 5640df96..bef9e835 100644 --- a/templates/ocata/keystone.conf +++ b/templates/ocata/keystone.conf @@ -4,7 +4,6 @@ # Configuration file maintained by Juju. Local changes may be overwritten. ############################################################################### [DEFAULT] -admin_token = {{ token }} use_syslog = {{ use_syslog }} log_config_append = {{ log_config }} debug = {{ debug }} diff --git a/templates/ocata/policy.json b/templates/ocata/policy.json index 1053fd6a..526ea08a 100644 --- a/templates/ocata/policy.json +++ b/templates/ocata/policy.json @@ -1,7 +1,11 @@ {% if api_version == 3 -%} { "admin_required": "role:{{ admin_role }}", - "cloud_admin": "rule:admin_required and (is_admin_project:True or domain_id:{{ admin_domain_id }} or project_id:{{ service_tenant_id }})", +{% if transitional_charm_user_id %} + "cloud_admin": "rule:admin_required and (user_id:{{ transitional_charm_user_id }} or token.is_admin_project:True or domain_id:{{ admin_domain_id }} or project_id:{{ service_tenant_id }})", +{% else %} + "cloud_admin": "rule:admin_required", +{% endif %} "service_role": "role:service", "service_or_admin": "rule:admin_required or rule:service_role", "owner" : "user_id:%(user_id)s or user_id:%(target.token.user_id)s", diff --git a/templates/queens/keystone.conf b/templates/queens/keystone.conf index c699921e..748178c3 100644 --- a/templates/queens/keystone.conf +++ b/templates/queens/keystone.conf @@ -4,7 +4,6 @@ # Configuration file maintained by Juju. Local changes may be overwritten. ############################################################################### [DEFAULT] -admin_token = {{ token }} use_syslog = {{ use_syslog }} log_config_append = {{ log_config }} debug = {{ debug }} diff --git a/templates/rocky/keystone.conf b/templates/rocky/keystone.conf index e2f870eb..54651e20 100644 --- a/templates/rocky/keystone.conf +++ b/templates/rocky/keystone.conf @@ -4,7 +4,6 @@ # Configuration file maintained by Juju. Local changes may be overwritten. ############################################################################### [DEFAULT] -admin_token = {{ token }} use_syslog = {{ use_syslog }} log_config_append = {{ log_config }} debug = {{ debug }} diff --git a/unit_tests/test_keystone_hooks.py b/unit_tests/test_keystone_hooks.py index 11212cc3..4e829a5d 100644 --- a/unit_tests/test_keystone_hooks.py +++ b/unit_tests/test_keystone_hooks.py @@ -582,6 +582,7 @@ class KeystoneRelationTests(CharmTestCase): cmd = ['a2dissite', 'openstack_https_frontend'] self.check_call.assert_called_with(cmd) + @patch.object(hooks, 'bootstrap_keystone') @patch.object(hooks, 'ensure_all_service_accounts_protected_for_pci_dss_options') @patch.object(hooks, 'maybe_do_policyd_overrides') @@ -601,7 +602,8 @@ class KeystoneRelationTests(CharmTestCase): os_release, update, mock_maybe_do_policyd_overrides, - mock_protect_service_accounts): + mock_protect_service_accounts, + mock_bootstrap_keystone): os_release.return_value = 'havana' mock_is_db_initialised.return_value = True mock_is_db_ready.return_value = True @@ -615,11 +617,13 @@ class KeystoneRelationTests(CharmTestCase): self.assertTrue(update.called) self.remove_old_packages.assert_called_once_with() self.service_restart.assert_called_with('apache2') + mock_bootstrap_keystone.assert_called_once_with(configs=ANY) mock_stop_manager_instance.assert_called_once_with() mock_maybe_do_policyd_overrides.assert_called_once_with( ANY, "keystone") mock_protect_service_accounts.assert_called_once_with() + @patch.object(hooks, 'bootstrap_keystone') @patch.object(hooks, 'ensure_all_service_accounts_protected_for_pci_dss_options') @patch.object(hooks, 'maybe_do_policyd_overrides') @@ -639,7 +643,8 @@ class KeystoneRelationTests(CharmTestCase): os_release, update, mock_maybe_do_policyd_overrides, - mock_protect_service_accounts): + mock_protect_service_accounts, + mock_bootstrap_keystone): os_release.return_value = 'havana' mock_is_db_initialised.return_value = True mock_is_db_ready.return_value = True @@ -653,15 +658,17 @@ class KeystoneRelationTests(CharmTestCase): self.assertTrue(update.called) self.remove_old_packages.assert_called_once_with() self.service_restart.assert_called_with('apache2') + mock_bootstrap_keystone.assert_called_once_with(configs=ANY) mock_stop_manager_instance.assert_called_once_with() mock_maybe_do_policyd_overrides.assert_called_once_with( ANY, "keystone") mock_protect_service_accounts.assert_called_once_with() + @patch.object(hooks, 'bootstrap_keystone') @patch.object(hooks, 'update_all_identity_relation_units') @patch.object(hooks, 'is_db_initialised') def test_leader_init_db_if_ready(self, is_db_initialized, - update): + update, mock_bootstrap_keystone): """ Verify leader initilaizes db """ self.is_elected_leader.return_value = True is_db_initialized.return_value = False @@ -670,6 +677,7 @@ class KeystoneRelationTests(CharmTestCase): hooks.leader_init_db_if_ready() self.is_db_ready.assert_called_with(use_current_context=False) self.migrate_database.assert_called_with() + mock_bootstrap_keystone.assert_called_once_with(configs=ANY) update.assert_called_with(check_db_ready=False) @patch.object(hooks, 'update_all_identity_relation_units') @@ -803,6 +811,7 @@ class KeystoneRelationTests(CharmTestCase): # Still updates relations self.assertTrue(self.relation_ids.called) + @patch.object(hooks, 'bootstrap_keystone') @patch.object(hooks, 'maybe_do_policyd_overrides') @patch.object(hooks, 'update_all_identity_relation_units') @patch.object(utils, 'os_release') @@ -814,7 +823,8 @@ class KeystoneRelationTests(CharmTestCase): mock_relation_ids, mock_log, os_release, update, - mock_maybe_do_policyd_overrides): + mock_maybe_do_policyd_overrides, + mock_bootstrap_keystone): os_release.return_value = 'havana' self.filter_installed_packages.return_value = ['something'] @@ -823,10 +833,12 @@ class KeystoneRelationTests(CharmTestCase): self.assertTrue(self.apt_install.called) self.assertTrue(self.log.called) self.assertFalse(update.called) + mock_bootstrap_keystone.assert_called_once_with(configs=ANY) mock_stop_manager_instance.assert_called_once() mock_maybe_do_policyd_overrides.assert_called_once_with( ANY, "keystone") + @patch.object(hooks, 'bootstrap_keystone') @patch.object(hooks, 'maybe_do_policyd_overrides') @patch.object(hooks, 'update_all_identity_relation_units') @patch.object(utils, 'os_release') @@ -840,7 +852,8 @@ class KeystoneRelationTests(CharmTestCase): mock_log, os_release, update, - mock_maybe_do_policyd_overrides + mock_maybe_do_policyd_overrides, + mock_bootstrap_keystone, ): os_release.return_value = 'havana' @@ -850,6 +863,7 @@ class KeystoneRelationTests(CharmTestCase): self.assertFalse(self.apt_install.called) self.assertTrue(self.log.called) self.assertFalse(update.called) + mock_bootstrap_keystone.assert_called_once_with(configs=ANY) mock_stop_manager_instance.assert_called_once() mock_maybe_do_policyd_overrides.assert_called_once_with( ANY, "keystone") diff --git a/unit_tests/test_keystone_utils.py b/unit_tests/test_keystone_utils.py index fd08fe6c..a976498f 100644 --- a/unit_tests/test_keystone_utils.py +++ b/unit_tests/test_keystone_utils.py @@ -15,7 +15,7 @@ import builtins import collections import copy -from mock import patch, call, MagicMock, mock_open, Mock +from mock import ANY, patch, call, MagicMock, mock_open, Mock import json import os import subprocess @@ -23,6 +23,8 @@ import time from test_utils import CharmTestCase +import keystone_types as ks_types + os.environ['JUJU_UNIT_NAME'] = 'keystone' with patch('charmhelpers.core.hookenv.config') as config, \ patch('charmhelpers.contrib.openstack.' @@ -42,7 +44,6 @@ TO_PATCH = [ 'create_role', 'create_service_entry', 'create_endpoint_template', - 'get_admin_token', 'get_local_endpoint', 'get_requested_roles', 'get_service_password', @@ -242,6 +243,7 @@ class TestKeystoneUtils(CharmTestCase): if p.startswith('python-')] + ['python-keystone', 'python-memcache']) + @patch.object(utils, 'bootstrap_keystone') @patch.object(utils, 'is_elected_leader') @patch.object(utils, 'disable_unused_apache_sites') @patch('os.path.exists') @@ -251,7 +253,7 @@ class TestKeystoneUtils(CharmTestCase): def test_openstack_upgrade_leader( self, migrate_database, determine_packages, run_in_apache, os_path_exists, disable_unused_apache_sites, - mock_is_elected_leader): + mock_is_elected_leader, mock_bootstrap_keystone): configs = MagicMock() self.test_config.set('openstack-origin', 'cloud:xenial-newton') self.os_release.return_value = 'ocata' @@ -285,6 +287,7 @@ class TestKeystoneUtils(CharmTestCase): self.assertTrue(configs.set_release.called) self.assertTrue(configs.write_all.called) self.assertTrue(migrate_database.called) + mock_bootstrap_keystone.assert_called_once_with(configs=ANY) disable_unused_apache_sites.assert_called_with() self.reset_os_release.assert_called() @@ -374,7 +377,6 @@ class TestKeystoneUtils(CharmTestCase): leader_get.return_value = None relation_id = 'identity-service:0' remote_unit = 'unit/0' - self.get_admin_token.return_value = 'token' self.get_service_password.return_value = 'password' self.test_config.set('service-tenant', 'tenant') self.test_config.set('admin-role', 'Admin') @@ -418,7 +420,6 @@ class TestKeystoneUtils(CharmTestCase): publicurl='10.0.0.1', adminurl='10.0.0.2', internalurl='192.168.1.2') - self.assertTrue(self.get_admin_token.called) self.get_service_password.assert_called_with('keystone') create_user.assert_called_with('keystone', 'password', domain=service_domain, @@ -435,7 +436,7 @@ class TestKeystoneUtils(CharmTestCase): 'admin_user_id': admin_user_id, 'admin_project_id': admin_project_id, 'auth_host': '10.0.0.3', - 'service_host': '10.0.0.3', 'admin_token': 'token', + 'service_host': '10.0.0.3', 'service_port': 81, 'auth_port': 80, 'service_username': 'keystone', 'service_password': 'password', @@ -1830,3 +1831,78 @@ class TestKeystoneUtils(CharmTestCase): self.assertEqual(csum, ('d938ff5656d3d9b50345e8061b4c73c8' '116a9c7fbc087765ce2e3a4a5df7cb17')) + + @patch.object(utils, 'leader_get') + def test_get_charm_credentials(self, mock_leader_get): + expect = ks_types.CharmCredentials( + '_charm-keystone-admin', 'fakepassword', + 'all', 'admin', 'default', 'default') + mock_leader_get.return_value = 'fakepassword' + self.assertEquals(utils.get_charm_credentials(), expect) + mock_leader_get.assert_called_once_with(expect.username + '_passwd') + mock_leader_get.retrun_value = None + mock_leader_get.side_effect = [None] + with self.assertRaises(RuntimeError): + utils.get_charm_credentials() + + @patch.object(utils, 'leader_get') + def test_is_bootstrapped(self, mock_leader_get): + mock_leader_get.side_effect = [None, None] + self.assertFalse(utils.is_bootstrapped()) + mock_leader_get.assert_called_once_with('keystone-bootstrapped') + mock_leader_get.side_effect = [True, None] + self.assertFalse(utils.is_bootstrapped()) + mock_leader_get.side_effect = [True, 'fakepassword'] + self.assertTrue(utils.is_bootstrapped()) + mock_leader_get.assert_called_with('_charm-keystone-admin_passwd') + + @patch.object(utils, 'get_manager') + @patch.object(utils, 'leader_set') + @patch.object(utils, 'resolve_address') + @patch.object(utils, 'endpoint_url') + @patch.object(utils, 'pwgen') + @patch.object(utils, 'leader_get') + @patch.object(utils, 'get_api_suffix') + def test_bootstrap_keystone( + self, + mock_get_api_suffix, + mock_leader_get, + mock_pwgen, + mock_endpoint_url, + mock_resolve_address, + mock_leader_set, + mock_get_manager): + configs = MagicMock() + mock_get_api_suffix.return_value = 'suffix' + mock_resolve_address.side_effect = lambda x: x + mock_endpoint_url.side_effect = ( + lambda x, y, z: 'http://{}:{}/{}'.format(x, y, z)) + mock_leader_get.return_value = 'fakepassword' + mock_get_manager().resolve_user_id.return_value = 'fakeid' + self.os_release.return_value = 'queens' + utils.bootstrap_keystone(configs=configs) + self.subprocess.check_call.assert_called_once_with( + ('keystone-manage', 'bootstrap', + '--bootstrap-username', '_charm-keystone-admin', + '--bootstrap-password', 'fakepassword', + '--bootstrap-project-name', 'admin', + '--bootstrap-role-name', 'Admin', + '--bootstrap-service-name', 'keystone', + '--bootstrap-admin-url', 'http://admin:35357/suffix', + '--bootstrap-public-url', 'http://public:5000/suffix', + '--bootstrap-internal-url', 'http://int:5000/suffix', + '--bootstrap-region-id', 'RegionOne'), + ) + mock_leader_set.assert_called_once_with({ + 'keystone-bootstrapped': True, + '_charm-keystone-admin_passwd': 'fakepassword'}) + self.assertFalse(configs.write_all.called) + mock_leader_set.reset_mock() + self.os_release.return_value = 'pike' + utils.bootstrap_keystone(configs=configs) + mock_leader_set.assert_has_calls([ + call({'keystone-bootstrapped': True, + '_charm-keystone-admin_passwd': 'fakepassword'}), + call({'transitional_charm_user_id': 'fakeid'}), + ]) + configs.write_all.assert_called_once_with()