[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] Re: [Xen-changelog] [xen-unstable] Added HTTPS support to Xend. There are new configuration options for the



The patch below was almost wholly based on work by Alastair Tse.
Apologies to him for not crediting him in the changeset comment.

Ewan.

On Wed, Mar 28, 2007 at 10:10:11AM -0700, Xen patchbot-unstable wrote:

> # HG changeset patch
> # User Ewan Mellor <ewan@xxxxxxxxxxxxx>
> # Date 1175034181 -3600
> # Node ID 966c65f0ddba39d45c34480a9395d828028cda26
> # Parent  94b873fb033a3ba7b7f991d389bce8caa49d43e9
> Added HTTPS support to Xend.  There are new configuration options for the
> Xen-API and legacy XML-RPC servers to set key and certificate files, and
> xm simply needs to be configured use an https rather than an http URL.
> 
> Signed-off-by: Ewan Mellor <ewan@xxxxxxxxxxxxx>
> ---
>  tools/examples/xend-config.sxp                  |   14 +++
>  tools/python/xen/util/xmlrpcclient.py           |   30 ++++++
>  tools/python/xen/xend/XendOptions.py            |    8 +
>  tools/python/xen/xend/server/SSLXMLRPCServer.py |  103 
> ++++++++++++++++++++++++
>  tools/python/xen/xend/server/SrvServer.py       |   83 ++++++++++++-------
>  tools/python/xen/xend/server/XMLRPCServer.py    |   51 ++++++++++-
>  6 files changed, 249 insertions(+), 40 deletions(-)
> 
> diff -r 94b873fb033a -r 966c65f0ddba tools/examples/xend-config.sxp
> --- a/tools/examples/xend-config.sxp  Tue Mar 27 23:03:32 2007 +0100
> +++ b/tools/examples/xend-config.sxp  Tue Mar 27 23:23:01 2007 +0100
> @@ -46,6 +46,11 @@
>  #   (xen-api-server ((9363 pam '^localhost$ example\\.com$')
>  #                    (unix none)))
>  #
> +# Optionally, the TCP Xen-API server can use SSL by specifying the private
> +# key and certificate location:
> +#
> +#                    (9367 pam '' /etc/xen/xen-api.key /etc/xen/xen-api.crt)
> +#
>  # Default:
>  #   (xen-api-server ((unix)))
>  
> @@ -59,10 +64,17 @@
>  
>  #(xend-unix-path /var/lib/xend/xend-socket)
>  
> -# Address and port xend should use for the TCP XMLRPC interface, 
> +
> +# Address and port xend should use for the legacy TCP XMLRPC interface, 
>  # if xen-tcp-xmlrpc-server is set.
>  #(xen-tcp-xmlrpc-server-address 'localhost')
>  #(xen-tcp-xmlrpc-server-port 8006)
> +
> +# SSL key and certificate to use for the legacy TCP XMLRPC interface.
> +# Setting these will mean that this port serves only SSL connections as
> +# opposed to plaintext ones.
> +#(xend-tcp-xmlrpc-server-ssl-key-file  /etc/xen/xmlrpc.key)
> +#(xend-tcp-xmlrpc-server-ssl-cert-file /etc/xen/xmlrpc.crt)
>  
>  
>  # Port xend should use for the HTTP interface, if xend-http-server is set.
> diff -r 94b873fb033a -r 966c65f0ddba tools/python/xen/util/xmlrpcclient.py
> --- a/tools/python/xen/util/xmlrpcclient.py   Tue Mar 27 23:03:32 2007 +0100
> +++ b/tools/python/xen/util/xmlrpcclient.py   Tue Mar 27 23:23:01 2007 +0100
> @@ -30,7 +30,6 @@ except ImportError:
>      # SSHTransport is disabled on Python <2.4, because it uses the subprocess
>      # package.
>      ssh_enabled = False
> -
>  
>  
>  # A new ServerProxy that also supports httpu urls.  An http URL comes in the
> @@ -57,6 +56,33 @@ class UnixTransport(xmlrpclib.Transport)
>          return HTTPUnix(self.__handler)
>  
>  
> +# We need our own transport for HTTPS, because xmlrpclib.SafeTransport is
> +# broken -- it does not handle ERROR_ZERO_RETURN properly.
> +class HTTPSTransport(xmlrpclib.SafeTransport):
> +    def _parse_response(self, file, sock):
> +        p, u = self.getparser()
> +        while 1:
> +            try:
> +                if sock:
> +                    response = sock.recv(1024)
> +                else:
> +                    response = file.read(1024)
> +            except socket.sslerror, exn:
> +                if exn[0] == socket.SSL_ERROR_ZERO_RETURN:
> +                    break
> +                raise
> +                
> +            if not response:
> +                break
> +            if self.verbose:
> +                print 'body:', repr(response)
> +            p.feed(response)
> +            
> +        file.close()
> +        p.close()
> +        return u.close()
> +
> +
>  # See xmlrpclib2.TCPXMLRPCServer._marshalled_dispatch.
>  def conv_string(x):
>      if isinstance(x, StringTypes):
> @@ -75,6 +101,8 @@ class ServerProxy(xmlrpclib.ServerProxy)
>              if protocol == 'httpu':
>                  uri = 'http:' + rest
>                  transport = UnixTransport()
> +            elif protocol == 'https':
> +                transport = HTTPSTransport()
>              elif protocol == 'ssh':
>                  global ssh_enabled
>                  if ssh_enabled:
> diff -r 94b873fb033a -r 966c65f0ddba tools/python/xen/xend/XendOptions.py
> --- a/tools/python/xen/xend/XendOptions.py    Tue Mar 27 23:03:32 2007 +0100
> +++ b/tools/python/xen/xend/XendOptions.py    Tue Mar 27 23:23:01 2007 +0100
> @@ -165,7 +165,13 @@ class XendOptions:
>  
>      def get_xend_tcp_xmlrpc_server_address(self):
>          return self.get_config_string("xend-tcp-xmlrpc-server-address",
> -                                    
> self.xend_tcp_xmlrpc_server_address_default)    
> +                                      
> self.xend_tcp_xmlrpc_server_address_default)
> +
> +    def get_xend_tcp_xmlrpc_server_ssl_key_file(self):
> +        return self.get_config_string("xend-tcp-xmlrpc-server-ssl-key-file")
> +
> +    def get_xend_tcp_xmlrpc_server_ssl_cert_file(self):
> +        return self.get_config_string("xend-tcp-xmlrpc-server-ssl-cert-file")
>  
>      def get_xend_unix_xmlrpc_server(self):
>          return self.get_config_bool("xend-unix-xmlrpc-server",
> diff -r 94b873fb033a -r 966c65f0ddba 
> tools/python/xen/xend/server/SSLXMLRPCServer.py
> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
> +++ b/tools/python/xen/xend/server/SSLXMLRPCServer.py Tue Mar 27 23:23:01 
> 2007 +0100
> @@ -0,0 +1,103 @@
> +#============================================================================
> +# This library is free software; you can redistribute it and/or
> +# modify it under the terms of version 2.1 of the GNU Lesser General Public
> +# License as published by the Free Software Foundation.
> +#
> +# This library is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +# Lesser General Public License for more details.
> +#
> +# You should have received a copy of the GNU Lesser General Public
> +# License along with this library; if not, write to the Free Software
> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> +#============================================================================
> +# Copyright (C) 2007 XenSource Inc.
> +#============================================================================
> +
> +
> +"""
> +HTTPS wrapper for an XML-RPC server interface.  Requires PyOpenSSL (Debian
> +package python-pyopenssl).
> +"""
> +
> +import socket
> +
> +from OpenSSL import SSL
> +
> +from xen.util.xmlrpclib2 import XMLRPCRequestHandler, TCPXMLRPCServer
> +
> +
> +class SSLXMLRPCRequestHandler(XMLRPCRequestHandler):
> +    def setup(self):
> +        self.connection = self.request
> +        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
> +        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
> +
> +#
> +# Taken from pyOpenSSL-0.6 examples (public-domain)
> +#
> +
> +class SSLWrapper:
> +    """
> +    """
> +    def __init__(self, conn):
> +        """
> +        Connection is not yet a new-style class,
> +        so I'm making a proxy instead of subclassing.
> +        """
> +        self.__dict__["conn"] = conn
> +    def __getattr__(self, name):
> +        return getattr(self.__dict__["conn"], name)
> +    def __setattr__(self, name, value):
> +        setattr(self.__dict__["conn"], name, value)
> +
> +    def close(self):
> +        self.shutdown()
> +        return self.__dict__["conn"].close()
> +
> +    def shutdown(self, how=1):
> +        """
> +        SimpleXMLRpcServer.doPOST calls shutdown(1),
> +        and Connection.shutdown() doesn't take
> +        an argument. So we just discard the argument.
> +        """
> +        # Block until the shutdown is complete
> +        self.__dict__["conn"].shutdown()
> +        self.__dict__["conn"].shutdown()
> +
> +    def accept(self):
> +        """
> +        This is the other part of the shutdown() workaround.
> +        Since servers create new sockets, we have to infect
> +        them with our magic. :)
> +        """
> +        c, a = self.__dict__["conn"].accept()
> +        return (SSLWrapper(c), a)
> +
> +#
> +# End of pyOpenSSL-0.6 example code.
> +#
> +
> +class SSLXMLRPCServer(TCPXMLRPCServer):
> +    def __init__(self, addr, allowed, xenapi, logRequests = 1,
> +                 ssl_key_file = None, ssl_cert_file = None):
> +
> +        TCPXMLRPCServer.__init__(self, addr, allowed, xenapi,
> +                                 SSLXMLRPCRequestHandler, logRequests)
> +
> +        if not ssl_key_file or not ssl_cert_file:
> +            raise ValueError("SSLXMLRPCServer requires ssl_key_file "
> +                             "and ssl_cert_file to be set.")
> +
> +        # make a SSL socket
> +        ctx = SSL.Context(SSL.SSLv23_METHOD)
> +        ctx.set_options(SSL.OP_NO_SSLv2)
> +        ctx.use_privatekey_file (ssl_key_file)
> +        ctx.use_certificate_file(ssl_cert_file)
> +        self.socket = SSLWrapper(SSL.Connection(ctx,
> +                                 socket.socket(self.address_family,
> +                                               self.socket_type)))
> +        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
> +        self.server_bind()
> +        self.server_activate()
> diff -r 94b873fb033a -r 966c65f0ddba tools/python/xen/xend/server/SrvServer.py
> --- a/tools/python/xen/xend/server/SrvServer.py       Tue Mar 27 23:03:32 
> 2007 +0100
> +++ b/tools/python/xen/xend/server/SrvServer.py       Tue Mar 27 23:23:01 
> 2007 +0100
> @@ -185,33 +185,49 @@ def _loadConfig(servers, root, reload):
>      api_cfg = xoptions.get_xen_api_server()
>      if api_cfg:
>          try:
> -            addrs = [(str(x[0]).split(':'),
> -                      len(x) > 1 and x[1] or XendAPI.AUTH_PAM,
> -                      len(x) > 2 and x[2] and map(re.compile, x[2].split(" 
> "))
> -                      or None)
> -                     for x in api_cfg]
> -            for addrport, auth, allowed in addrs:
> -                if auth not in [XendAPI.AUTH_PAM, XendAPI.AUTH_NONE]:
> -                    log.error('Xen-API server configuration %s is invalid, ' 
> +
> -                              'as %s is not a valid authentication type.',
> -                              api_cfg, auth)
> -                    break
> -
> -                if len(addrport) == 1:
> -                    if addrport[0] == 'unix':
> -                        servers.add(XMLRPCServer(auth, True,
> -                                                 path = XEN_API_SOCKET,
> -                                                 hosts_allowed = allowed))
> -                    else:
> -                        servers.add(
> -                            XMLRPCServer(auth, True, True, '',
> -                                         int(addrport[0]),
> -                                         hosts_allowed = allowed))
> -                else:
> -                    addr, port = addrport
> -                    servers.add(XMLRPCServer(auth, True, True, addr,
> -                                             int(port),
> -                                             hosts_allowed = allowed))
> +            for server_cfg in api_cfg:
> +                # Parse the xen-api-server config
> +                
> +                host = 'localhost'
> +                port = 0
> +                use_tcp = False
> +                ssl_key_file = None
> +                ssl_cert_file = None
> +                auth_method = XendAPI.AUTH_NONE
> +                hosts_allowed = None
> +                
> +                host_addr = server_cfg[0].split(':', 1)
> +                if len(host_addr) == 1 and host_addr[0].lower() == 'unix':
> +                    use_tcp = False
> +                elif len(host_addr) == 1:
> +                    use_tcp = True
> +                    port = int(host_addr[0])
> +                elif len(host_addr) == 2:
> +                    use_tcp = True
> +                    host = str(host_addr[0])
> +                    port = int(host_addr[1])
> +
> +                if len(server_cfg) > 1:
> +                    if server_cfg[1] in [XendAPI.AUTH_PAM, 
> XendAPI.AUTH_NONE]:
> +                        auth_method = server_cfg[1]
> +
> +                if len(server_cfg) > 2:
> +                    hosts_allowed = server_cfg[2] or None
> +                
> +
> +                if len(server_cfg) > 4:
> +                    # SSL key and cert file
> +                    ssl_key_file = server_cfg[3]
> +                    ssl_cert_file = server_cfg[4]
> +
> +
> +                servers.add(XMLRPCServer(auth_method, True, use_tcp = 
> use_tcp,
> +                                         ssl_key_file = ssl_key_file,
> +                                         ssl_cert_file = ssl_cert_file,
> +                                         host = host, port = port,
> +                                         path = XEN_API_SOCKET,
> +                                         hosts_allowed = hosts_allowed))
> +
>          except (ValueError, TypeError), exn:
>              log.exception('Xen API Server init failed')
>              log.error('Xen-API server configuration %s is invalid.', api_cfg)
> @@ -219,8 +235,17 @@ def _loadConfig(servers, root, reload):
>      if xoptions.get_xend_tcp_xmlrpc_server():
>          addr = xoptions.get_xend_tcp_xmlrpc_server_address()
>          port = xoptions.get_xend_tcp_xmlrpc_server_port()
> -        servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True,
> -                                 host = addr, port = port))
> +        ssl_key_file = xoptions.get_xend_tcp_xmlrpc_server_ssl_key_file()
> +        ssl_cert_file = xoptions.get_xend_tcp_xmlrpc_server_ssl_cert_file()
> +
> +        if ssl_key_file and ssl_cert_file:
> +            servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True,
> +                                     ssl_key_file = ssl_key_file,
> +                                     ssl_cert_file = ssl_cert_file,
> +                                     host = addr, port = port))
> +        else:
> +            servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True,
> +                                     host = addr, port = port))
>  
>      if xoptions.get_xend_unix_xmlrpc_server():
>          servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False))
> diff -r 94b873fb033a -r 966c65f0ddba 
> tools/python/xen/xend/server/XMLRPCServer.py
> --- a/tools/python/xen/xend/server/XMLRPCServer.py    Tue Mar 27 23:03:32 
> 2007 +0100
> +++ b/tools/python/xen/xend/server/XMLRPCServer.py    Tue Mar 27 23:23:01 
> 2007 +0100
> @@ -21,6 +21,11 @@ import types
>  import types
>  import xmlrpclib
>  from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer
> +try:
> +    from SSLXMLRPCServer import SSLXMLRPCServer
> +    ssl_enabled = True
> +except ImportError:
> +    ssl_enabled = False
>  
>  from xen.xend import XendAPI, XendDomain, XendDomainInfo, XendNode
>  from xen.xend import XendLogging, XendDmesg
> @@ -87,14 +92,20 @@ exclude = ['domain_create', 'domain_rest
>  exclude = ['domain_create', 'domain_restore']
>  
>  class XMLRPCServer:
> -    def __init__(self, auth, use_xenapi, use_tcp=False, host = "localhost",
> -                 port = 8006, path = XML_RPC_SOCKET, hosts_allowed = None):
> +    def __init__(self, auth, use_xenapi, use_tcp = False,
> +                 ssl_key_file = None, ssl_cert_file = None,
> +                 host = "localhost", port = 8006, path = XML_RPC_SOCKET,
> +                 hosts_allowed = None):
> +        
>          self.use_tcp = use_tcp
>          self.port = port
>          self.host = host
>          self.path = path
>          self.hosts_allowed = hosts_allowed
>          
> +        self.ssl_key_file = ssl_key_file
> +        self.ssl_cert_file = ssl_cert_file
> +        
>          self.ready = False        
>          self.running = True
>          self.auth = auth
> @@ -107,14 +118,33 @@ class XMLRPCServer:
>  
>          try:
>              if self.use_tcp:
> -                log.info("Opening TCP XML-RPC server on %s%d%s",
> +                using_ssl = self.ssl_key_file and self.ssl_cert_file
> +
> +                log.info("Opening %s XML-RPC server on %s%d%s",
> +                         using_ssl and 'HTTPS' or 'TCP',
>                           self.host and '%s:' % self.host or
>                           'all interfaces, port ',
>                           self.port, authmsg)
> -                self.server = TCPXMLRPCServer((self.host, self.port),
> -                                              self.hosts_allowed,
> -                                              self.xenapi is not None,
> -                                              logRequests = False)
> +
> +                if not ssl_enabled:
> +                    raise ValueError("pyOpenSSL not installed. "
> +                                     "Unable to start HTTPS XML-RPC server")
> +
> +                if using_ssl:
> +                    self.server = SSLXMLRPCServer(
> +                        (self.host, self.port),
> +                        self.hosts_allowed,
> +                        self.xenapi is not None,
> +                        logRequests = False,
> +                        ssl_key_file = self.ssl_key_file,
> +                        ssl_cert_file = self.ssl_cert_file)
> +                else:
> +                    self.server = TCPXMLRPCServer(
> +                        (self.host, self.port),
> +                        self.hosts_allowed,
> +                        self.xenapi is not None,
> +                        logRequests = False)
> +
>              else:
>                  log.info("Opening Unix domain socket XML-RPC server on %s%s",
>                           self.path, authmsg)
> @@ -126,7 +156,12 @@ class XMLRPCServer:
>              ready = True
>              running = False
>              return
> -
> +        except Exception, e:
> +            log.exception('Cannot start server: %s!', e)
> +            ready = True
> +            running = False
> +            return
> +        
>          # Register Xen API Functions
>          # -------------------------------------------------------------------
>          # exportable functions are ones that do not begin with '_'
> 
> _______________________________________________
> Xen-changelog mailing list
> Xen-changelog@xxxxxxxxxxxxxxxxxxx
> http://lists.xensource.com/xen-changelog

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.