diff --git a/.gitreview b/.gitreview index d128bdc7..f4e47bbf 100644 --- a/.gitreview +++ b/.gitreview @@ -2,3 +2,4 @@ host=review.opendev.org port=29418 project=openstack/cloudkitty.git +defaultbranch=stable/2025.2 diff --git a/cloudkitty/common/policy.py b/cloudkitty/common/policy.py index 3dcd5c5e..af26d574 100644 --- a/cloudkitty/common/policy.py +++ b/cloudkitty/common/policy.py @@ -22,6 +22,7 @@ from oslo_log import log as logging from oslo_policy import policy from oslo_utils import excutils +from oslo_utils import strutils from cloudkitty.common import policies @@ -102,8 +103,9 @@ def authorize(context, action, target): init() try: - LOG.debug('Authenticating user with credentials %(credentials)s', - {'credentials': context.to_dict()}) + LOG.debug( + 'Authenticating user with credentials %(credentials)s', + {'credentials': strutils.mask_dict_password(context.to_dict())}) return _ENFORCER.authorize(action, target, context, do_raise=True, exc=PolicyNotAuthorized, @@ -114,9 +116,12 @@ def authorize(context, action, target): LOG.exception('Policy not registered') except Exception: with excutils.save_and_reraise_exception(): - LOG.error('Policy check for %(action)s failed with credentials ' - '%(credentials)s', - {'action': action, 'credentials': context.to_dict()}) + LOG.error( + 'Policy check for %(action)s failed with credentials ' + '%(credentials)s', { + 'action': action, + 'credentials': strutils.mask_dict_password( + context.to_dict())}) def check_is_admin(context): diff --git a/cloudkitty/storage/v2/elasticsearch/client.py b/cloudkitty/storage/v2/elasticsearch/client.py index 98dc8a29..eb87934e 100644 --- a/cloudkitty/storage/v2/elasticsearch/client.py +++ b/cloudkitty/storage/v2/elasticsearch/client.py @@ -78,6 +78,9 @@ def _build_must(start, end, metric_types, filters): must.append({'term': {'type': filters['type']}}) if metric_types: + if type(metric_types) is not list: + metric_types = [metric_types] + must.append({"terms": {"type": metric_types}}) return must diff --git a/cloudkitty/storage/v2/opensearch/client.py b/cloudkitty/storage/v2/opensearch/client.py index 868af2c4..b46122e4 100644 --- a/cloudkitty/storage/v2/opensearch/client.py +++ b/cloudkitty/storage/v2/opensearch/client.py @@ -78,6 +78,9 @@ def _build_must(start, end, metric_types, filters): must.append({'term': {'type': filters['type']}}) if metric_types: + if type(metric_types) is not list: + metric_types = [metric_types] + must.append({"terms": {"type": metric_types}}) return must diff --git a/cloudkitty/tests/storage/v2/elasticsearch/test_client.py b/cloudkitty/tests/storage/v2/elasticsearch/test_client.py index 5d3158b4..c3493574 100644 --- a/cloudkitty/tests/storage/v2/elasticsearch/test_client.py +++ b/cloudkitty/tests/storage/v2/elasticsearch/test_client.py @@ -53,6 +53,13 @@ def test_build_must_with_filters(self): [{'term': {'type': 'awesome'}}], ) + def test_build_must_with_metric_type(self): + types = 'awesome' + self.assertEqual( + self.client._build_must(None, None, types, None), + [{'terms': {'type': ['awesome']}}], + ) + def test_build_must_with_metric_types(self): types = ['awesome', 'amazing'] self.assertEqual( diff --git a/cloudkitty/tests/storage/v2/opensearch/test_client.py b/cloudkitty/tests/storage/v2/opensearch/test_client.py index b33515c6..b3c2ae7a 100644 --- a/cloudkitty/tests/storage/v2/opensearch/test_client.py +++ b/cloudkitty/tests/storage/v2/opensearch/test_client.py @@ -53,6 +53,13 @@ def test_build_must_with_filters(self): [{'term': {'type': 'awesome'}}], ) + def test_build_must_with_metric_type(self): + types = 'awesome' + self.assertEqual( + self.client._build_must(None, None, types, None), + [{'terms': {'type': ['awesome']}}], + ) + def test_build_must_with_metric_types(self): types = ['awesome', 'amazing'] self.assertEqual( diff --git a/devstack/upgrade/upgrade.sh b/devstack/upgrade/upgrade.sh index 273462c2..8b4dfa86 100755 --- a/devstack/upgrade/upgrade.sh +++ b/devstack/upgrade/upgrade.sh @@ -60,6 +60,9 @@ upgrade_project cloudkitty $RUN_DIR $BASE_DEVSTACK_BRANCH $TARGET_DEVSTACK_BRANC # Migrate the database upgrade_cloudkitty_database || die $LINO "ERROR in database migration" +# Update api_paste.ini for healthcheck middleware as app +cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/api_paste.ini $CLOUDKITTY_CONF_DIR + start_cloudkitty # Don't succeed unless the services come up diff --git a/etc/cloudkitty/api_paste.ini b/etc/cloudkitty/api_paste.ini index c19a1f4e..a888c96c 100644 --- a/etc/cloudkitty/api_paste.ini +++ b/etc/cloudkitty/api_paste.ini @@ -1,14 +1,24 @@ -[pipeline:cloudkitty+noauth] -pipeline = cors healthcheck http_proxy_to_wsgi request_id ck_api +[composite:cloudkitty+noauth] +use = egg:Paste#urlmap +/: api+noauth +/healthcheck: healthcheck -[pipeline:cloudkitty+keystone] -pipeline = cors healthcheck http_proxy_to_wsgi request_id authtoken ck_api +[composite:cloudkitty+keystone] +use = egg:Paste#urlmap +/: api+keystone +/healthcheck: healthcheck + +[pipeline:api+noauth] +pipeline = cors http_proxy_to_wsgi request_id ck_api + +[pipeline:api+keystone] +pipeline = cors http_proxy_to_wsgi request_id authtoken ck_api [app:ck_api] paste.app_factory = cloudkitty.api.app:app_factory [filter:authtoken] -acl_public_routes = /, /v1, /v2, /healthcheck +acl_public_routes = /, /v1, /v2 paste.filter_factory = cloudkitty.api.middleware:AuthTokenMiddleware.factory [filter:request_id] @@ -18,8 +28,8 @@ paste.filter_factory = oslo_middleware:RequestId.factory paste.filter_factory = oslo_middleware.cors:filter_factory oslo_config_project = cloudkitty -[filter:healthcheck] -paste.filter_factory = oslo_middleware:Healthcheck.factory +[app:healthcheck] +paste.app_factory = oslo_middleware:Healthcheck.app_factory backends = disable_by_file disable_by_file_path = /etc/cloudkitty/healthcheck_disable diff --git a/releasenotes/notes/fix-elasticsearch-a9a37443f81b3a79.yaml b/releasenotes/notes/fix-elasticsearch-a9a37443f81b3a79.yaml new file mode 100644 index 00000000..b6d18bb8 --- /dev/null +++ b/releasenotes/notes/fix-elasticsearch-a9a37443f81b3a79.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fix request to Elasticsearch/OpenSearch that has potentially not the + correct type. diff --git a/releasenotes/notes/hide-token-from-log-e29066d6c93f3ed4.yaml b/releasenotes/notes/hide-token-from-log-e29066d6c93f3ed4.yaml new file mode 100644 index 00000000..2e78c723 --- /dev/null +++ b/releasenotes/notes/hide-token-from-log-e29066d6c93f3ed4.yaml @@ -0,0 +1,6 @@ +--- +security: + - | + Previously, cloudkitty-api recorded request token in plain text format when + a request does not comply with policy rules or debug log is enabled. This + has been fixed and now token is masked in logs. diff --git a/tox.ini b/tox.ini index 672e7270..ac6967e0 100644 --- a/tox.ini +++ b/tox.ini @@ -12,7 +12,7 @@ setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning usedevelop = True -deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} +deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2025.2} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt @@ -52,7 +52,7 @@ commands = commands = oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/cloudkitty.conf [testenv:docs] -deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} +deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2025.2} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W --keep-going -b html doc/source doc/build/html