[pve-devel] [PATCH v1 master ceph 1/2] provide workaround for PyO3 ImportError
Max R. Carrara
m.carrara at proxmox.com
Wed Jul 16 19:39:55 CEST 2025
By moving the self-signed cert generation into a separate module and
consequently running it via `subprocess.run`--so, as a new,
independent Python process--the PyO3 ImportError is successfully
avoided.
Inspired by an upstream PR [0].
[0]: https://github.com/ceph/ceph/pull/62951
Signed-off-by: Max R. Carrara <m.carrara at proxmox.com>
---
...ul-provide-workaround-for-PyO3-Impor.patch | 152 ++++++++++++++++++
patches/series | 1 +
2 files changed, 153 insertions(+)
create mode 100644 patches/0058-pybind-mgr-restful-provide-workaround-for-PyO3-Impor.patch
diff --git a/patches/0058-pybind-mgr-restful-provide-workaround-for-PyO3-Impor.patch b/patches/0058-pybind-mgr-restful-provide-workaround-for-PyO3-Impor.patch
new file mode 100644
index 0000000000..25fb720410
--- /dev/null
+++ b/patches/0058-pybind-mgr-restful-provide-workaround-for-PyO3-Impor.patch
@@ -0,0 +1,152 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: "Max R. Carrara" <m.carrara at proxmox.com>
+Date: Wed, 16 Jul 2025 13:14:39 +0200
+Subject: [PATCH 58/59] pybind/mgr/restful: provide workaround for PyO3
+ ImportError
+
+Move the self-signed cert generation into a separate module
+inside python-common/ceph and run the module in a separate Python
+process.
+
+This provides a workaround for the ImportError thrown by PyO3 when
+the `restful` module is loaded in the context of multiple Python
+sub-interpreters being present. In particular, the ImportError is
+thrown by the `crypto` module of the `OpenSSL` package.
+
+Inspired by an upstream PR [0].
+
+[0]: https://github.com/ceph/ceph/pull/62951
+
+Signed-off-by: Max R. Carrara <m.carrara at proxmox.com>
+---
+ src/pybind/mgr/restful/module.py | 24 +++------
+ src/python-common/ceph/_crypto_wrap.py | 69 ++++++++++++++++++++++++++
+ 2 files changed, 76 insertions(+), 17 deletions(-)
+ create mode 100644 src/python-common/ceph/_crypto_wrap.py
+
+diff --git a/src/pybind/mgr/restful/module.py b/src/pybind/mgr/restful/module.py
+index 0f8c78e0bd8..7f93c41f1e6 100644
+--- a/src/pybind/mgr/restful/module.py
++++ b/src/pybind/mgr/restful/module.py
+@@ -7,6 +7,7 @@ import json
+ import time
+ import errno
+ import inspect
++import subprocess
+ import tempfile
+ import threading
+ import traceback
+@@ -19,7 +20,6 @@ from . import context
+
+ from uuid import uuid4
+ from pecan import jsonify, make_app
+-from OpenSSL import crypto
+ from pecan.rest import RestController
+ from werkzeug.serving import make_server, make_ssl_devcert
+
+@@ -401,24 +401,14 @@ class Module(MgrModule):
+
+
+ def create_self_signed_cert(self):
+- # create a key pair
+- pkey = crypto.PKey()
+- pkey.generate_key(crypto.TYPE_RSA, 2048)
+-
+- # create a self-signed cert
+- cert = crypto.X509()
+- cert.get_subject().O = "IT"
+- cert.get_subject().CN = "ceph-restful"
+- cert.set_serial_number(int(uuid4()))
+- cert.gmtime_adj_notBefore(0)
+- cert.gmtime_adj_notAfter(10*365*24*60*60)
+- cert.set_issuer(cert.get_subject())
+- cert.set_pubkey(pkey)
+- cert.sign(pkey, 'sha512')
++ cmd = ["python3", "-m", "ceph._crypto_wrap", "create_self_signed_cert"]
++
++ response = subprocess.run(cmd, capture_output=True, check=True)
++ response_obj = json.loads(response.stdout)
+
+ return (
+- crypto.dump_certificate(crypto.FILETYPE_PEM, cert),
+- crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)
++ response_obj["cert"].encode("utf-8"),
++ response_obj["key"].encode("utf-8"),
+ )
+
+
+diff --git a/src/python-common/ceph/_crypto_wrap.py b/src/python-common/ceph/_crypto_wrap.py
+new file mode 100644
+index 00000000000..16a19a5345e
+--- /dev/null
++++ b/src/python-common/ceph/_crypto_wrap.py
+@@ -0,0 +1,69 @@
++"""CLI wrapper for cryptographic functions of the :mod:`restful` module.
++
++To be called via :func:`subprocess.run()` as a workaround for
++:class:`ImportError`s related to PyO3's current lack of sub-interpreter
++support.
++
++Note:
++ Since this module is installed as part of the ``ceph`` package,
++ it should be called like so::
++
++ python3 -m ceph._crypto_wrap create_self_signed_cert
++"""
++
++import argparse
++import sys
++import json
++
++from argparse import Namespace
++from typing import Any
++from uuid import uuid4
++
++from OpenSSL import crypto
++
++
++def _respond(data: dict[str, Any]) -> None:
++ json.dump(data, sys.stdout)
++ sys.stdout.flush()
++
++
++def create_self_signed_cert(args: Namespace) -> None:
++ cert_key_pair = _create_self_signed_cert()
++ _respond(cert_key_pair)
++
++
++def _create_self_signed_cert() -> dict[str, str]:
++ # create a key pair
++ pubkey = crypto.PKey()
++ pubkey.generate_key(crypto.TYPE_RSA, 2048)
++
++ # create a self-signed cert
++ cert = crypto.X509()
++ cert.get_subject().O = "IT"
++ cert.get_subject().CN = "ceph-restful"
++ cert.set_serial_number(int(uuid4()))
++ cert.gmtime_adj_notBefore(0)
++ cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
++ cert.set_issuer(cert.get_subject())
++ cert.set_pubkey(pubkey)
++ cert.sign(pubkey, "sha512")
++
++ return {
++ "cert": crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode(),
++ "key": crypto.dump_privatekey(crypto.FILETYPE_PEM, pubkey).decode(),
++ }
++
++
++def main() -> None:
++ parser = argparse.ArgumentParser(prog="_crypto_wrap.py")
++ subparsers = parser.add_subparsers(required=True)
++
++ parser_cssc = subparsers.add_parser("create_self_signed_cert")
++ parser_cssc.set_defaults(func=create_self_signed_cert)
++
++ args = parser.parse_args()
++ args.func(args)
++
++
++if __name__ == "__main__":
++ main()
diff --git a/patches/series b/patches/series
index 728a9f935e..ff23f8b640 100644
--- a/patches/series
+++ b/patches/series
@@ -51,3 +51,4 @@
0055-python-common-cryptotools-catch-all-failures-to-read.patch
0056-mgr-cephadm-always-use-the-internal-cryptocaller.patch
0057-mgr-dashboard-add-an-option-to-control-the-dashboard.patch
+0058-pybind-mgr-restful-provide-workaround-for-PyO3-Impor.patch
--
2.39.5
More information about the pve-devel
mailing list