diff --git a/backport-fix-Py03-modules-not-support-error.patch b/backport-fix-Py03-modules-not-support-error.patch new file mode 100644 index 0000000000000000000000000000000000000000..dc53a5ec331e7a4a845bc515a51b19e8865c4be4 --- /dev/null +++ b/backport-fix-Py03-modules-not-support-error.patch @@ -0,0 +1,224 @@ +From fcbd8fdae9d80d9a9bd61838aeaf41b504a5888a Mon Sep 17 00:00:00 2001 +From: Daniel Persson +Date: Wed, 29 Nov 2023 09:39:51 +0000 +Subject: [PATCH 1/3] mgr/dashboard: Simplify authentication protocol By + removing the dependency to PyJWT we also remove the dependency to the + cryptographic library which in the dashboard module will create a crash. In + newer implementations of the library PyO3 is used to run rust code in order + to encrypt with Elliptic Curves. This is never used in the dashboard + communication so a much simpler implementation where we only use the hmac + sha256 algorithm to create the signed JWT message could be used. + +Fixes: https://forum.proxmox.com/threads/ceph-warning-post-upgrade-to-v8.129371 +Signed-off-by: Daniel Persson +(cherry picked from commit c616a9d017b5fcc85bb5c1556bccf4c77cc3899e) +--- + src/pybind/mgr/dashboard/constraints.txt | 1 - + src/pybind/mgr/dashboard/exceptions.py | 12 ++++ + src/pybind/mgr/dashboard/requirements.txt | 1 - + src/pybind/mgr/dashboard/services/auth.py | 70 ++++++++++++++++++++--- + src/pybind/mgr/dashboard/tox.ini | 1 + + 5 files changed, 74 insertions(+), 11 deletions(-) + +diff --git a/src/pybind/mgr/dashboard/constraints.txt b/src/pybind/mgr/dashboard/constraints.txt +index 55f81c92dec06..fd6141048800a 100644 +--- a/src/pybind/mgr/dashboard/constraints.txt ++++ b/src/pybind/mgr/dashboard/constraints.txt +@@ -1,6 +1,5 @@ + CherryPy~=13.1 + more-itertools~=8.14 +-PyJWT~=2.0 + bcrypt~=3.1 + python3-saml~=1.4 + requests~=2.26 +diff --git a/src/pybind/mgr/dashboard/exceptions.py b/src/pybind/mgr/dashboard/exceptions.py +index 96cbc52335613..d396a38d2c3a2 100644 +--- a/src/pybind/mgr/dashboard/exceptions.py ++++ b/src/pybind/mgr/dashboard/exceptions.py +@@ -121,3 +121,15 @@ class GrafanaError(Exception): + + class PasswordPolicyException(Exception): + pass ++ ++ ++class ExpiredSignatureError(Exception): ++ pass ++ ++ ++class InvalidTokenError(Exception): ++ pass ++ ++ ++class InvalidAlgorithmError(Exception): ++ pass +diff --git a/src/pybind/mgr/dashboard/requirements.txt b/src/pybind/mgr/dashboard/requirements.txt +index 8003d62a5523f..292971819c9c6 100644 +--- a/src/pybind/mgr/dashboard/requirements.txt ++++ b/src/pybind/mgr/dashboard/requirements.txt +@@ -1,7 +1,6 @@ + bcrypt + CherryPy + more-itertools +-PyJWT + pyopenssl + requests + Routes +diff --git a/src/pybind/mgr/dashboard/services/auth.py b/src/pybind/mgr/dashboard/services/auth.py +index f13963abffdd4..3c6002312524d 100644 +--- a/src/pybind/mgr/dashboard/services/auth.py ++++ b/src/pybind/mgr/dashboard/services/auth.py +@@ -1,17 +1,19 @@ + # -*- coding: utf-8 -*- + ++import base64 ++import hashlib ++import hmac + import json + import logging + import os + import threading + import time + import uuid +-from base64 import b64encode + + import cherrypy +-import jwt + + from .. import mgr ++from ..exceptions import ExpiredSignatureError, InvalidAlgorithmError, InvalidTokenError + from .access_control import LocalAuthenticator, UserDoesNotExist + + cherrypy.config.update({ +@@ -33,7 +35,7 @@ class JwtManager(object): + @staticmethod + def _gen_secret(): + secret = os.urandom(16) +- return b64encode(secret).decode('utf-8') ++ return base64.b64encode(secret).decode('utf-8') + + @classmethod + def init(cls): +@@ -45,6 +47,54 @@ def init(cls): + mgr.set_store('jwt_secret', secret) + cls._secret = secret + ++ @classmethod ++ def array_to_base64_string(cls, message): ++ jsonstr = json.dumps(message, sort_keys=True).replace(" ", "") ++ string_bytes = base64.urlsafe_b64encode(bytes(jsonstr, 'UTF-8')) ++ return string_bytes.decode('UTF-8').replace("=", "") ++ ++ @classmethod ++ def encode(cls, message, secret): ++ header = {"alg": cls.JWT_ALGORITHM, "typ": "JWT"} ++ base64_header = cls.array_to_base64_string(header) ++ base64_message = cls.array_to_base64_string(message) ++ base64_secret = base64.urlsafe_b64encode(hmac.new( ++ bytes(secret, 'UTF-8'), ++ msg=bytes(base64_header + "." + base64_message, 'UTF-8'), ++ digestmod=hashlib.sha256 ++ ).digest()).decode('UTF-8').replace("=", "") ++ return base64_header + "." + base64_message + "." + base64_secret ++ ++ @classmethod ++ def decode(cls, message, secret): ++ split_message = message.split(".") ++ base64_header = split_message[0] ++ base64_message = split_message[1] ++ base64_secret = split_message[2] ++ ++ decoded_header = json.loads(base64.urlsafe_b64decode(base64_header)) ++ ++ if decoded_header['alg'] != cls.JWT_ALGORITHM: ++ raise InvalidAlgorithmError() ++ ++ incoming_secret = base64.urlsafe_b64encode(hmac.new( ++ bytes(secret, 'UTF-8'), ++ msg=bytes(base64_header + "." + base64_message, 'UTF-8'), ++ digestmod=hashlib.sha256 ++ ).digest()).decode('UTF-8').replace("=", "") ++ ++ if base64_secret != incoming_secret: ++ raise InvalidTokenError() ++ ++ # We add ==== as padding to ignore the requirement to have correct padding in ++ # the urlsafe_b64decode method. ++ decoded_message = json.loads(base64.urlsafe_b64decode(base64_message + "====")) ++ now = int(time.time()) ++ if decoded_message['exp'] < now: ++ raise ExpiredSignatureError() ++ ++ return decoded_message ++ + @classmethod + def gen_token(cls, username): + if not cls._secret: +@@ -59,13 +109,13 @@ def gen_token(cls, username): + 'iat': now, + 'username': username + } +- return jwt.encode(payload, cls._secret, algorithm=cls.JWT_ALGORITHM) # type: ignore ++ return cls.encode(payload, cls._secret) # type: ignore + + @classmethod + def decode_token(cls, token): + if not cls._secret: + cls.init() +- return jwt.decode(token, cls._secret, algorithms=cls.JWT_ALGORITHM) # type: ignore ++ return cls.decode(token, cls._secret) # type: ignore + + @classmethod + def get_token_from_header(cls): +@@ -99,8 +149,8 @@ def get_username(cls): + @classmethod + def get_user(cls, token): + try: +- dtoken = JwtManager.decode_token(token) +- if not JwtManager.is_blocklisted(dtoken['jti']): ++ dtoken = cls.decode_token(token) ++ if not cls.is_blocklisted(dtoken['jti']): + user = AuthManager.get_user(dtoken['username']) + if user.last_update <= dtoken['iat']: + return user +@@ -110,10 +160,12 @@ def get_user(cls, token): + ) + else: + cls.logger.debug('Token is block-listed') # type: ignore +- except jwt.ExpiredSignatureError: ++ except ExpiredSignatureError: + cls.logger.debug("Token has expired") # type: ignore +- except jwt.InvalidTokenError: ++ except InvalidTokenError: + cls.logger.debug("Failed to decode token") # type: ignore ++ except InvalidAlgorithmError: ++ cls.logger.debug("Only the HS256 algorithm is supported.") # type: ignore + except UserDoesNotExist: + cls.logger.debug( # type: ignore + "Invalid token: user %s does not exist", dtoken['username'] +diff --git a/src/pybind/mgr/dashboard/tox.ini b/src/pybind/mgr/dashboard/tox.ini +index 47756e946e125..271df286ec5e8 100644 +--- a/src/pybind/mgr/dashboard/tox.ini ++++ b/src/pybind/mgr/dashboard/tox.ini +@@ -20,6 +20,7 @@ addopts = + deps = + -rrequirements.txt + -cconstraints.txt ++ PyJWT + + [base-test] + deps = + +From d456590743aa26d7d253b20294f5aa33544ab8a7 Mon Sep 17 00:00:00 2001 +From: Daniel Persson +Date: Sun, 3 Dec 2023 08:03:47 +0000 +Subject: [PATCH 2/3] mgr/dashboard: Changes suggested after review by + @epuertat. + +Move the JWT requirement to the test requirements file. Also remove JWT from ceph specification and debian build. + +Signed-off-by: Daniel Persson +(cherry picked from commit c1ea66fe12f86e7a63681cba860fb91b1ea86e12) +--- + ceph.spec.in | 4 ---- + debian/control | 1 - + src/pyb \ No newline at end of file diff --git a/ceph.spec b/ceph.spec index ba2e14808ee69687637f8b20cadac742fccad8d0..6a96afa58e9b4d83448bcc12b0208abb0b6f7e83 100644 --- a/ceph.spec +++ b/ceph.spec @@ -174,7 +174,7 @@ ################################################################################# Name: ceph Version: 18.2.2 -Release: 11 +Release: 12 %if 0%{?fedora} || 0%{?rhel} || 0%{?openEuler} Epoch: 2 %endif @@ -201,6 +201,7 @@ Patch7: 0006-fix-CVE-2024-48916.patch Patch8: 0007-client-set-LIBMOUNT_FORCE_MOUNT2-always.patch Patch9: 0008-client-disallow-unprivileged-users-to-escalate-root.patch Patch10: 0009-add-checking-result-for-rgw-frontend-init.patch +Patch11: backport-fix-Py03-modules-not-support-error.patch Patch9001: 9001-add-supprot-for-loongarch64.patch Patch9002: 9002-fix-riscv64-build.patch @@ -1323,6 +1324,7 @@ This package provides a Ceph MIB for SNMP traps. %patch8 -p1 %patch9 -p1 %patch10 -p1 +%patch11 -p1 %ifarch loongarch64 %patch9001 -p1 @@ -2660,6 +2662,9 @@ exit 0 %{_datadir}/snmp/mibs %changelog +* Tue Oct 14 2025 lizhipeng - 2:18.2.2-12 +- fix dashboard Py03 modules do not yet support subinterpreters issue + * Thu Sep 18 2025 lizhipeng - 2:18.2.2-11 - fix src.rpm not contain loongarch64、riscv64 patch