diff -Nru keystone-19.0.2.dev11.202308251656.focal/ChangeLog keystone-19.0.2.dev14.202310061826.focal/ChangeLog --- keystone-19.0.2.dev11.202308251656.focal/ChangeLog 2023-08-25 16:58:38.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/ChangeLog 2023-10-06 18:30:17.000000000 +0000 @@ -1,6 +1,8 @@ CHANGES ======= +* Force algo specific maximum length & Properly trimm bcrypt hashed passwords +* Add an option to randomize LDAP urls list * [stable-only] Cap keystone-tempest-plugin version for stable/wallaby * [PooledLDAPHandler] Ensure result3() invokes message.clean() * Limit token expiration to application credential expiration diff -Nru keystone-19.0.2.dev11.202308251656.focal/debian/changelog keystone-19.0.2.dev14.202310061826.focal/debian/changelog --- keystone-19.0.2.dev11.202308251656.focal/debian/changelog 2023-08-25 16:59:04.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/debian/changelog 2023-10-06 18:30:54.000000000 +0000 @@ -1,14 +1,11 @@ -keystone (2:19.0.2.dev11.202308251656.focal-0ubuntu1) focal; urgency=medium +keystone (2:19.0.2.dev14.202310061826.focal-0ubuntu1) focal; urgency=medium * Automated Ubuntu testing build: - * [e7a7061] [stable-only] Cap keystone-tempest-plugin version for - stable/wallaby - * [ec0ad69] [PooledLDAPHandler] Ensure result3() invokes - message.clean() - * [bcef9db] Limit token expiration to application credential - expiration + * [11e1258] Force algo specific maximum length & Properly trimm bcrypt + hashed passwords + * [5a1f367] Add an option to randomize LDAP urls list - -- Openstack Ubuntu Testing Bot Fri, 25 Aug 2023 16:59:04 +0000 + -- Openstack Ubuntu Testing Bot Fri, 06 Oct 2023 18:30:54 +0000 keystone (2:19.0.1-0ubuntu1~cloud0) focal-wallaby; urgency=medium diff -Nru keystone-19.0.2.dev11.202308251656.focal/doc/source/admin/integrate-with-ldap.inc keystone-19.0.2.dev14.202310061826.focal/doc/source/admin/integrate-with-ldap.inc --- keystone-19.0.2.dev11.202308251656.focal/doc/source/admin/integrate-with-ldap.inc 2023-08-25 16:56:15.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/doc/source/admin/integrate-with-ldap.inc 2023-10-06 18:26:22.000000000 +0000 @@ -68,14 +68,32 @@ suffix = dc=example,dc=org -Multiple LDAP servers can be supplied to ``url`` to provide high-availability -support for a single LDAP backend. To specify multiple LDAP servers, simply -change the ``url`` option in the ``[ldap]`` section to be a list, separated by -commas: +Although it's not recommended (see note below), multiple LDAP servers can be +supplied to ``url`` to provide high-availability support for a single LDAP +backend. By default, these will be tried in order of apperance, but an +additional option, ``randomize_urls`` can be set to true, to randomize the +list in each process (when it starts). To specify multiple LDAP servers, +simply change the ``url`` option in the ``[ldap]`` section to be a list, +separated by commas: .. code-block:: ini url = "ldap://localhost,ldap://backup.localhost" + randomize_urls = true + +.. NOTE:: + + Failover mechanisms in the LDAP backend can cause delays when switching + over to the next working LDAP server. Randomizing the order in which the + servers are tried only makes the failure behavior not dependent on which + of the ordered servers fail. Individual processes can still be delayed or + time out, so this doesn't fix the issue at hand, but only makes the + failure mode more gradual. This behavior cannot be easily fixed inside the + service, because keystone would have to monitor the status of each LDAP + server, which is in fact a task for a load balancer. Because of this, it + is recommended to use a load balancer in front of the LDAP servers, + which can monitor the state of the cluster and instantly redirect + connections to the working LDAP server. **Additional LDAP integration settings** diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone/common/password_hashing.py keystone-19.0.2.dev14.202310061826.focal/keystone/common/password_hashing.py --- keystone-19.0.2.dev11.202308251656.focal/keystone/common/password_hashing.py 2023-08-25 16:56:15.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone/common/password_hashing.py 2023-10-06 18:26:22.000000000 +0000 @@ -57,19 +57,38 @@ def verify_length_and_trunc_password(password): - """Verify and truncate the provided password to the max_password_length.""" - max_length = CONF.identity.max_password_length + """Verify and truncate the provided password to the max_password_length. + + We also need to check that the configured password hashing algorithm does + not silently truncate the password. For example, passlib.hash.bcrypt does + this: + https://passlib.readthedocs.io/en/stable/lib/passlib.hash.bcrypt.html#security-issues + + """ + # When using bcrypt, we limit the password length to 54 to ensure all + # bytes are fully mixed. See: + # https://passlib.readthedocs.io/en/stable/lib/passlib.hash.bcrypt.html#security-issues + BCRYPT_MAX_LENGTH = 72 + if (CONF.identity.password_hash_algorithm == 'bcrypt' and # nosec: B105 + CONF.identity.max_password_length > BCRYPT_MAX_LENGTH): + msg = "Truncating password to algorithm specific maximum length %d characters." + LOG.warning(msg, BCRYPT_MAX_LENGTH) + max_length = BCRYPT_MAX_LENGTH + else: + max_length = CONF.identity.max_password_length + try: - if len(password) > max_length: + password_utf8 = password.encode('utf-8') + if len(password_utf8) > max_length: if CONF.strict_password_check: raise exception.PasswordVerificationError(size=max_length) else: msg = "Truncating user password to %d characters." LOG.warning(msg, max_length) - return password[:max_length] + return password_utf8[:max_length] else: - return password - except TypeError: + return password_utf8 + except AttributeError: raise exception.ValidationError(attribute='string', target='password') @@ -82,7 +101,7 @@ """ if password is None or hashed is None: return False - password_utf8 = verify_length_and_trunc_password(password).encode('utf-8') + password_utf8 = verify_length_and_trunc_password(password) hasher = _get_hasher_from_ident(hashed) return hasher.verify(password_utf8, hashed) @@ -99,7 +118,7 @@ def hash_password(password): """Hash a password. Harder.""" params = {} - password_utf8 = verify_length_and_trunc_password(password).encode('utf-8') + password_utf8 = verify_length_and_trunc_password(password) conf_hasher = CONF.identity.password_hash_algorithm hasher = _HASHER_NAME_MAP.get(conf_hasher) diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone/conf/identity.py keystone-19.0.2.dev14.202310061826.focal/keystone/conf/identity.py --- keystone-19.0.2.dev11.202308251656.focal/keystone/conf/identity.py 2023-08-25 16:56:15.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone/conf/identity.py 2023-10-06 18:26:22.000000000 +0000 @@ -99,7 +99,11 @@ max=passlib.utils.MAX_PASSWORD_SIZE, help=utils.fmt(""" Maximum allowed length for user passwords. Decrease this value to improve -performance. Changing this value does not effect existing passwords. +performance. Changing this value does not effect existing passwords. This value +can also be overridden by certain hashing algorithms maximum allowed length +which takes precedence over the configured value. + +The bcrypt max_password_length is 72 bytes. """)) list_limit = cfg.IntOpt( diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone/conf/ldap.py keystone-19.0.2.dev14.202310061826.focal/keystone/conf/ldap.py --- keystone-19.0.2.dev11.202308251656.focal/keystone/conf/ldap.py 2023-08-25 16:56:15.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone/conf/ldap.py 2023-10-06 18:26:22.000000000 +0000 @@ -24,6 +24,18 @@ connection. """)) +randomize_urls = cfg.BoolOpt( + 'randomize_urls', + default=False, + help=utils.fmt(""" +Randomize the order of URLs in each keystone process. This makes the failure +behavior more gradual, since if the first server is down, a process/thread +will wait for the specified timeout before attempting a connection to a +server further down the list. This defaults to False, for backward +compatibility. +""")) + + user = cfg.StrOpt( 'user', help=utils.fmt(""" @@ -479,6 +491,7 @@ GROUP_NAME = __name__.split('.')[-1] ALL_OPTS = [ url, + randomize_urls, user, password, suffix, diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone/identity/backends/ldap/common.py keystone-19.0.2.dev14.202310061826.focal/keystone/identity/backends/ldap/common.py --- keystone-19.0.2.dev11.202308251656.focal/keystone/identity/backends/ldap/common.py 2023-08-25 16:56:15.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone/identity/backends/ldap/common.py 2023-10-06 18:26:22.000000000 +0000 @@ -15,6 +15,7 @@ import abc import codecs import os.path +import random import re import sys import uuid @@ -1166,7 +1167,12 @@ tree_dn = None def __init__(self, conf): - self.LDAP_URL = conf.ldap.url + if conf.ldap.randomize_urls: + urls = re.split(r'[\s,]+', conf.ldap.url) + random.shuffle(urls) + self.LDAP_URL = ','.join(urls) + else: + self.LDAP_URL = conf.ldap.url self.LDAP_USER = conf.ldap.user self.LDAP_PASSWORD = conf.ldap.password self.LDAP_SCOPE = ldap_scope(conf.ldap.query_scope) diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone/tests/unit/common/test_utils.py keystone-19.0.2.dev14.202310061826.focal/keystone/tests/unit/common/test_utils.py --- keystone-19.0.2.dev11.202308251656.focal/keystone/tests/unit/common/test_utils.py 2023-08-25 16:56:16.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone/tests/unit/common/test_utils.py 2023-10-06 18:26:23.000000000 +0000 @@ -77,7 +77,7 @@ self.config_fixture.config(strict_password_check=False) password = uuid.uuid4().hex verified = common_utils.verify_length_and_trunc_password(password) - self.assertEqual(password, verified) + self.assertEqual(password.encode('utf-8'), verified) def test_that_a_hash_can_not_be_validated_against_a_hash(self): # NOTE(dstanek): Bug 1279849 reported a problem where passwords @@ -97,7 +97,7 @@ max_length = CONF.identity.max_password_length invalid_password = 'passw0rd' trunc = common_utils.verify_length_and_trunc_password(invalid_password) - self.assertEqual(invalid_password[:max_length], trunc) + self.assertEqual(invalid_password.encode('utf-8')[:max_length], trunc) def test_verify_long_password_strict_raises_exception(self): self.config_fixture.config(strict_password_check=True) @@ -133,6 +133,17 @@ self.assertRaises(exception.PasswordVerificationError, common_utils.hash_password, invalid_length_password) + + def test_max_algo_length_truncates_password(self): + self.config_fixture.config(strict_password_check=True) + self.config_fixture.config(group='identity', + password_hash_algorithm='bcrypt') + self.config_fixture.config(group='identity', + max_password_length='96') + invalid_length_password = '0' * 96 + self.assertRaises(exception.PasswordVerificationError, + common_utils.hash_password, + invalid_length_password) def _create_test_user(self, password=OPTIONAL): user = {"name": "hthtest"} diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone/tests/unit/identity/backends/test_ldap_common.py keystone-19.0.2.dev14.202310061826.focal/keystone/tests/unit/identity/backends/test_ldap_common.py --- keystone-19.0.2.dev11.202308251656.focal/keystone/tests/unit/identity/backends/test_ldap_common.py 2023-08-25 16:56:16.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone/tests/unit/identity/backends/test_ldap_common.py 2023-10-06 18:26:23.000000000 +0000 @@ -245,6 +245,33 @@ ldap_connection = base_ldap.get_connection() self.assertEqual(urls, ldap_connection.conn.conn_pool.uri) + @mock.patch.object(common_ldap.KeystoneLDAPHandler, 'simple_bind_s') + def test_multiple_urls_with_comma_randomized(self, mock_ldap_bind): + urls = ('ldap://localhost1,ldap://localhost2,' + 'ldap://localhost3,ldap://localhost4,' + 'ldap://localhost5,ldap://localhost6,' + 'ldap://localhost7,ldap://localhost8,' + 'ldap://localhost9,ldap://localhost0') + self.config_fixture.config(group='ldap', url=urls, + randomize_urls=True) + base_ldap = common_ldap.BaseLdap(CONF) + ldap_connection = base_ldap.get_connection() + + # Sanity check + self.assertEqual(len(urls.split(',')), 10) + + # Check that the list is split into the same number of URIs + self.assertEqual(len(urls.split(',')), + len(ldap_connection.conn.conn_pool.uri.split(','))) + + # Check that the list is randomized + self.assertNotEqual(urls.split(','), + ldap_connection.conn.conn_pool.uri.split(',')) + + # Check that the list contains the same URIs + self.assertEqual(set(urls.split(',')), + set(ldap_connection.conn.conn_pool.uri.split(','))) + class LDAPConnectionTimeoutTest(unit.TestCase): """Test for Network Connection timeout on LDAP URL connection.""" diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone.egg-info/pbr.json keystone-19.0.2.dev14.202310061826.focal/keystone.egg-info/pbr.json --- keystone-19.0.2.dev11.202308251656.focal/keystone.egg-info/pbr.json 2023-08-25 16:58:40.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone.egg-info/pbr.json 2023-10-06 18:30:19.000000000 +0000 @@ -1 +1 @@ -{"git_version": "08bcd6c3b", "is_release": false} \ No newline at end of file +{"git_version": "54dd95db4", "is_release": false} \ No newline at end of file diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone.egg-info/PKG-INFO keystone-19.0.2.dev14.202310061826.focal/keystone.egg-info/PKG-INFO --- keystone-19.0.2.dev11.202308251656.focal/keystone.egg-info/PKG-INFO 2023-08-25 16:58:39.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone.egg-info/PKG-INFO 2023-10-06 18:30:19.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: keystone -Version: 19.0.2.dev11 +Version: 19.0.2.dev14 Summary: OpenStack Identity Home-page: https://docs.openstack.org/keystone/latest Author: OpenStack diff -Nru keystone-19.0.2.dev11.202308251656.focal/keystone.egg-info/SOURCES.txt keystone-19.0.2.dev14.202310061826.focal/keystone.egg-info/SOURCES.txt --- keystone-19.0.2.dev11.202308251656.focal/keystone.egg-info/SOURCES.txt 2023-08-25 16:58:40.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/keystone.egg-info/SOURCES.txt 2023-10-06 18:30:20.000000000 +0000 @@ -1345,6 +1345,7 @@ releasenotes/notes/add_password_expires_at_to_user_response-22f14ab629c48bc2.yaml releasenotes/notes/admin_token-a5678d712783c145.yaml releasenotes/notes/admin_token-c634ec12fc714255.yaml +releasenotes/notes/bcrypt_truncation_fix-674dc5d7f1e776f2.yaml releasenotes/notes/bootstrap-update-endpoint-7a63a2329822b6e7.yaml releasenotes/notes/bp-allow-expired-f5d845b9601bc1ef.yaml releasenotes/notes/bp-application-credentials-c699f1f17c7d4e2f.yaml @@ -1624,6 +1625,7 @@ releasenotes/notes/list_limit-ldap-support-5d31d51466fc49a6.yaml releasenotes/notes/list_role_assignment_names-33aedc1e521230b6.yaml releasenotes/notes/mapping_populate-521d92445505b8a3.yaml +releasenotes/notes/max-password-length-truncation-and-warning-bd69090315ec18a7.yaml releasenotes/notes/migration_squash-f655329ddad7fc2a.yaml releasenotes/notes/no-default-domain-2161ada44bf7a3f7.yaml releasenotes/notes/notify-on-user-group-membership-8c0136ee0484e255.yaml @@ -1635,6 +1637,7 @@ releasenotes/notes/project-tags-1e72a6779d9d02c5.yaml releasenotes/notes/projects_as_domains-3ea8a58b4c2965e1.yaml releasenotes/notes/python3-support-e4189e0a1a6e2e4f.yaml +releasenotes/notes/randomize_urls-c0c19f48b2bfa299.yaml releasenotes/notes/remove-token-auth-middleware-5ea3b3734ce1d9e6.yaml releasenotes/notes/remove-trust-auth-support-from-v2-de316c9ba46d556d.yaml releasenotes/notes/removed-as-of-mitaka-9ff14f87d0b98e7e.yaml diff -Nru keystone-19.0.2.dev11.202308251656.focal/PKG-INFO keystone-19.0.2.dev14.202310061826.focal/PKG-INFO --- keystone-19.0.2.dev11.202308251656.focal/PKG-INFO 2023-08-25 16:58:40.776009300 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/PKG-INFO 2023-10-06 18:30:21.492228700 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: keystone -Version: 19.0.2.dev11 +Version: 19.0.2.dev14 Summary: OpenStack Identity Home-page: https://docs.openstack.org/keystone/latest Author: OpenStack diff -Nru keystone-19.0.2.dev11.202308251656.focal/releasenotes/notes/bcrypt_truncation_fix-674dc5d7f1e776f2.yaml keystone-19.0.2.dev14.202310061826.focal/releasenotes/notes/bcrypt_truncation_fix-674dc5d7f1e776f2.yaml --- keystone-19.0.2.dev11.202308251656.focal/releasenotes/notes/bcrypt_truncation_fix-674dc5d7f1e776f2.yaml 1970-01-01 00:00:00.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/releasenotes/notes/bcrypt_truncation_fix-674dc5d7f1e776f2.yaml 2023-10-06 18:26:23.000000000 +0000 @@ -0,0 +1,7 @@ +--- +fixes: + - | + Passwords that are hashed using bcrypt are now truncated properly to the + maximum allowed length by the algorythm. This solves regression, when + passwords longer then 54 symbols are getting invalidated after the + Keystone upgrade. diff -Nru keystone-19.0.2.dev11.202308251656.focal/releasenotes/notes/max-password-length-truncation-and-warning-bd69090315ec18a7.yaml keystone-19.0.2.dev14.202310061826.focal/releasenotes/notes/max-password-length-truncation-and-warning-bd69090315ec18a7.yaml --- keystone-19.0.2.dev11.202308251656.focal/releasenotes/notes/max-password-length-truncation-and-warning-bd69090315ec18a7.yaml 1970-01-01 00:00:00.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/releasenotes/notes/max-password-length-truncation-and-warning-bd69090315ec18a7.yaml 2023-10-06 18:26:23.000000000 +0000 @@ -0,0 +1,9 @@ +--- +security: + - | + Passwords will now be automatically truncated if the max_password_length is + greater than the allowed length for the selected password hashing + algorithm. Currently only bcrypt has fixed allowed lengths defined which is + 54 characters. A warning will be generated in the log if a password is + truncated. This will not affect existing passwords, however only the first + 54 characters of existing bcrypt passwords will be validated. diff -Nru keystone-19.0.2.dev11.202308251656.focal/releasenotes/notes/randomize_urls-c0c19f48b2bfa299.yaml keystone-19.0.2.dev14.202310061826.focal/releasenotes/notes/randomize_urls-c0c19f48b2bfa299.yaml --- keystone-19.0.2.dev11.202308251656.focal/releasenotes/notes/randomize_urls-c0c19f48b2bfa299.yaml 1970-01-01 00:00:00.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/releasenotes/notes/randomize_urls-c0c19f48b2bfa299.yaml 2023-10-06 18:26:23.000000000 +0000 @@ -0,0 +1,6 @@ +--- +features: + - | + A new option 'randomize_urls' can be used to randomize the order in which + keystone connects to the LDAP servers in [ldap] 'url' list. + It is false by default. diff -Nru keystone-19.0.2.dev11.202308251656.focal/tox.ini keystone-19.0.2.dev14.202310061826.focal/tox.ini --- keystone-19.0.2.dev11.202308251656.focal/tox.ini 2023-08-25 16:56:16.000000000 +0000 +++ keystone-19.0.2.dev14.202310061826.focal/tox.ini 2023-10-06 18:26:23.000000000 +0000 @@ -123,6 +123,9 @@ exclude=.venv,.git,.tox,build,dist,*lib/python*,*egg,tools,vendor,.update-venv,*.ini,*.po,*.pot max-complexity=24 +per-file-ignores = +# URL lines too long + keystone/common/password_hashing.py: E501 [testenv:docs] deps =