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.carr...@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.carr...@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.carr...@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



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to