【问题标题】:XMLRPC server over HTTPS in Python 3Python 3 中基于 HTTPS 的 XMLRPC 服务器
【发布时间】:2011-08-07 03:19:06
【问题描述】:

我正在尝试在 Python 3 上实现一个简单的 XML-RPC 服务器,我希望它使用标准 ssl 库(包含在 Python 2.6 和 Python 3.x 中)通过 HTTPS 运行。

我见过一些使用 OpenSSL 或 M2Crypto 模块的代码,但我想避免任何依赖。

我实现了下一个代码,它应该将 SSL 协议包装在套接字上:

"""Monkey patching standard xmlrpc.server.SimpleXMLRPCServer
to run over TLS (SSL)

Changes inspired on http://www.cs.technion.ac.il/~danken/SecureXMLRPCServer.py
"""
import socket
import socketserver
import ssl
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCDispatcher, SimpleXMLRPCRequestHandler
try:
    import fcntl
except ImportError:
    fcntl = None


class SimpleXMLRPCServerTLS(SimpleXMLRPCServer):
    def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
                 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
        """Overriding __init__ method of the SimpleXMLRPCServer

        The method is an exact copy, except the TCPServer __init__
        call, which is rewritten using TLS
        """
        self.logRequests = logRequests

        SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)

        """This is the modified part. Original code was:

            socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)

        which executed:

            def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
                BaseServer.__init__(self, server_address, RequestHandlerClass)
                self.socket = socket.socket(self.address_family,
                                            self.socket_type)
                if bind_and_activate:
                    self.server_bind()
                    self.server_activate()

        """
        socketserver.BaseServer.__init__(self, addr, requestHandler)
        self.socket = ssl.wrap_socket(
            socket.socket(self.address_family, self.socket_type),
            server_side=True,
            cert_reqs=ssl.CERT_NONE,
            ssl_version=ssl.PROTOCOL_TLSv1,
            )
        if bind_and_activate:
            self.server_bind()
            self.server_activate()

        """End of modified part"""

        # [Bug #1222790] If possible, set close-on-exec flag; if a
        # method spawns a subprocess, the subprocess shouldn't have
        # the listening socket open.
        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
            flags |= fcntl.FD_CLOEXEC
            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)

但由于某种原因我无法确定,它引发了这个错误:

[Errno 8] _ssl.c:502: EOF occurred in violation of protocol

在调用远程方法之前。

有谁知道可能会发生什么,或者知道如何收集更多信息以了解可能是什么问题?

非常感谢您!

更新:

已修复。代码有两个错误。首先,需要指定证书文件(也可以包含密钥)。

二是xmlrpc.client.ServerProxy(包含在Python中)使用的是SSLv2,所以TLSv1不起作用。

工作代码是:

"""Monkey patching standard xmlrpc.server.SimpleXMLRPCServer
to run over TLS (SSL)

Changes inspired on http://www.cs.technion.ac.il/~danken/SecureXMLRPCServer.py
"""
import socket
import socketserver
import ssl
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCDispatcher, SimpleXMLRPCRequestHandler
try:
    import fcntl
except ImportError:
    fcntl = None


class SimpleXMLRPCServerTLS(SimpleXMLRPCServer):
    def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
                 logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
        """Overriding __init__ method of the SimpleXMLRPCServer

        The method is an exact copy, except the TCPServer __init__
        call, which is rewritten using TLS
        """
        self.logRequests = logRequests

        SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)

        """This is the modified part. Original code was:

            socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)

        which executed:

            def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
                BaseServer.__init__(self, server_address, RequestHandlerClass)
                self.socket = socket.socket(self.address_family,
                                            self.socket_type)
                if bind_and_activate:
                    self.server_bind()
                    self.server_activate()

        """
        socketserver.BaseServer.__init__(self, addr, requestHandler)
        self.socket = ssl.wrap_socket(
            socket.socket(self.address_family, self.socket_type),
            server_side=True,
            certfile='cert.pem',
            cert_reqs=ssl.CERT_NONE,
            ssl_version=ssl.PROTOCOL_SSLv23,
            )
        if bind_and_activate:
            self.server_bind()
            self.server_activate()

        """End of modified part"""

        # [Bug #1222790] If possible, set close-on-exec flag; if a
        # method spawns a subprocess, the subprocess shouldn't have
        # the listening socket open.
        if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
            flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
            flags |= fcntl.FD_CLOEXEC
            fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)

【问题讨论】:

  • 确保你运行服务器和客户端的两个地方都在运行。
  • 为什么不将 ssl 卸载到真正的 Web 服务器 (apache / lighttpd) 或负载均衡器上?
  • 此代码不是针对单个主机安装,而是针对无需最终用户设置 http 服务器即可分发的程序。

标签: python ssl https python-3.x xml-rpc


【解决方案1】:

你为什么不这样写:

server = SimpleXMLRPCServer(...)
...
server.socket = ssl.wrap_socket(srv.socket, ...)
server.serve_forever()

【讨论】:

    【解决方案2】:

    将您的 Python 服务器移到 Apache、NGinx 等反向代理后面,并让反向代理到 SSL 作业……这比在 Python 级别上工作得更好、更顺畅。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-11
      • 2013-11-11
      • 2013-06-28
      • 2016-12-30
      • 2011-08-05
      • 1970-01-01
      • 1970-01-01
      • 2015-02-08
      相关资源
      最近更新 更多