ia64/xen-unstable

changeset 14609:966c65f0ddba

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@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Mar 27 23:23:01 2007 +0100 (2007-03-27)
parents 94b873fb033a
children a138ae831515
files tools/examples/xend-config.sxp tools/python/xen/util/xmlrpcclient.py tools/python/xen/xend/XendOptions.py tools/python/xen/xend/server/SSLXMLRPCServer.py tools/python/xen/xend/server/SrvServer.py tools/python/xen/xend/server/XMLRPCServer.py
line diff
     1.1 --- a/tools/examples/xend-config.sxp	Tue Mar 27 23:03:32 2007 +0100
     1.2 +++ b/tools/examples/xend-config.sxp	Tue Mar 27 23:23:01 2007 +0100
     1.3 @@ -46,6 +46,11 @@
     1.4  #   (xen-api-server ((9363 pam '^localhost$ example\\.com$')
     1.5  #                    (unix none)))
     1.6  #
     1.7 +# Optionally, the TCP Xen-API server can use SSL by specifying the private
     1.8 +# key and certificate location:
     1.9 +#
    1.10 +#                    (9367 pam '' /etc/xen/xen-api.key /etc/xen/xen-api.crt)
    1.11 +#
    1.12  # Default:
    1.13  #   (xen-api-server ((unix)))
    1.14  
    1.15 @@ -59,11 +64,18 @@
    1.16  
    1.17  #(xend-unix-path /var/lib/xend/xend-socket)
    1.18  
    1.19 -# Address and port xend should use for the TCP XMLRPC interface, 
    1.20 +
    1.21 +# Address and port xend should use for the legacy TCP XMLRPC interface, 
    1.22  # if xen-tcp-xmlrpc-server is set.
    1.23  #(xen-tcp-xmlrpc-server-address 'localhost')
    1.24  #(xen-tcp-xmlrpc-server-port 8006)
    1.25  
    1.26 +# SSL key and certificate to use for the legacy TCP XMLRPC interface.
    1.27 +# Setting these will mean that this port serves only SSL connections as
    1.28 +# opposed to plaintext ones.
    1.29 +#(xend-tcp-xmlrpc-server-ssl-key-file  /etc/xen/xmlrpc.key)
    1.30 +#(xend-tcp-xmlrpc-server-ssl-cert-file /etc/xen/xmlrpc.crt)
    1.31 +
    1.32  
    1.33  # Port xend should use for the HTTP interface, if xend-http-server is set.
    1.34  #(xend-port            8000)
     2.1 --- a/tools/python/xen/util/xmlrpcclient.py	Tue Mar 27 23:03:32 2007 +0100
     2.2 +++ b/tools/python/xen/util/xmlrpcclient.py	Tue Mar 27 23:23:01 2007 +0100
     2.3 @@ -32,7 +32,6 @@ except ImportError:
     2.4      ssh_enabled = False
     2.5  
     2.6  
     2.7 -
     2.8  # A new ServerProxy that also supports httpu urls.  An http URL comes in the
     2.9  # form:
    2.10  #
    2.11 @@ -57,6 +56,33 @@ class UnixTransport(xmlrpclib.Transport)
    2.12          return HTTPUnix(self.__handler)
    2.13  
    2.14  
    2.15 +# We need our own transport for HTTPS, because xmlrpclib.SafeTransport is
    2.16 +# broken -- it does not handle ERROR_ZERO_RETURN properly.
    2.17 +class HTTPSTransport(xmlrpclib.SafeTransport):
    2.18 +    def _parse_response(self, file, sock):
    2.19 +        p, u = self.getparser()
    2.20 +        while 1:
    2.21 +            try:
    2.22 +                if sock:
    2.23 +                    response = sock.recv(1024)
    2.24 +                else:
    2.25 +                    response = file.read(1024)
    2.26 +            except socket.sslerror, exn:
    2.27 +                if exn[0] == socket.SSL_ERROR_ZERO_RETURN:
    2.28 +                    break
    2.29 +                raise
    2.30 +                
    2.31 +            if not response:
    2.32 +                break
    2.33 +            if self.verbose:
    2.34 +                print 'body:', repr(response)
    2.35 +            p.feed(response)
    2.36 +            
    2.37 +        file.close()
    2.38 +        p.close()
    2.39 +        return u.close()
    2.40 +
    2.41 +
    2.42  # See xmlrpclib2.TCPXMLRPCServer._marshalled_dispatch.
    2.43  def conv_string(x):
    2.44      if isinstance(x, StringTypes):
    2.45 @@ -75,6 +101,8 @@ class ServerProxy(xmlrpclib.ServerProxy)
    2.46              if protocol == 'httpu':
    2.47                  uri = 'http:' + rest
    2.48                  transport = UnixTransport()
    2.49 +            elif protocol == 'https':
    2.50 +                transport = HTTPSTransport()
    2.51              elif protocol == 'ssh':
    2.52                  global ssh_enabled
    2.53                  if ssh_enabled:
     3.1 --- a/tools/python/xen/xend/XendOptions.py	Tue Mar 27 23:03:32 2007 +0100
     3.2 +++ b/tools/python/xen/xend/XendOptions.py	Tue Mar 27 23:23:01 2007 +0100
     3.3 @@ -165,7 +165,13 @@ class XendOptions:
     3.4  
     3.5      def get_xend_tcp_xmlrpc_server_address(self):
     3.6          return self.get_config_string("xend-tcp-xmlrpc-server-address",
     3.7 -                                    self.xend_tcp_xmlrpc_server_address_default)    
     3.8 +                                      self.xend_tcp_xmlrpc_server_address_default)
     3.9 +
    3.10 +    def get_xend_tcp_xmlrpc_server_ssl_key_file(self):
    3.11 +        return self.get_config_string("xend-tcp-xmlrpc-server-ssl-key-file")
    3.12 +
    3.13 +    def get_xend_tcp_xmlrpc_server_ssl_cert_file(self):
    3.14 +        return self.get_config_string("xend-tcp-xmlrpc-server-ssl-cert-file")
    3.15  
    3.16      def get_xend_unix_xmlrpc_server(self):
    3.17          return self.get_config_bool("xend-unix-xmlrpc-server",
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/tools/python/xen/xend/server/SSLXMLRPCServer.py	Tue Mar 27 23:23:01 2007 +0100
     4.3 @@ -0,0 +1,103 @@
     4.4 +#============================================================================
     4.5 +# This library is free software; you can redistribute it and/or
     4.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     4.7 +# License as published by the Free Software Foundation.
     4.8 +#
     4.9 +# This library is distributed in the hope that it will be useful,
    4.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    4.12 +# Lesser General Public License for more details.
    4.13 +#
    4.14 +# You should have received a copy of the GNU Lesser General Public
    4.15 +# License along with this library; if not, write to the Free Software
    4.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    4.17 +#============================================================================
    4.18 +# Copyright (C) 2007 XenSource Inc.
    4.19 +#============================================================================
    4.20 +
    4.21 +
    4.22 +"""
    4.23 +HTTPS wrapper for an XML-RPC server interface.  Requires PyOpenSSL (Debian
    4.24 +package python-pyopenssl).
    4.25 +"""
    4.26 +
    4.27 +import socket
    4.28 +
    4.29 +from OpenSSL import SSL
    4.30 +
    4.31 +from xen.util.xmlrpclib2 import XMLRPCRequestHandler, TCPXMLRPCServer
    4.32 +
    4.33 +
    4.34 +class SSLXMLRPCRequestHandler(XMLRPCRequestHandler):
    4.35 +    def setup(self):
    4.36 +        self.connection = self.request
    4.37 +        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
    4.38 +        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
    4.39 +
    4.40 +#
    4.41 +# Taken from pyOpenSSL-0.6 examples (public-domain)
    4.42 +#
    4.43 +
    4.44 +class SSLWrapper:
    4.45 +    """
    4.46 +    """
    4.47 +    def __init__(self, conn):
    4.48 +        """
    4.49 +        Connection is not yet a new-style class,
    4.50 +        so I'm making a proxy instead of subclassing.
    4.51 +        """
    4.52 +        self.__dict__["conn"] = conn
    4.53 +    def __getattr__(self, name):
    4.54 +        return getattr(self.__dict__["conn"], name)
    4.55 +    def __setattr__(self, name, value):
    4.56 +        setattr(self.__dict__["conn"], name, value)
    4.57 +
    4.58 +    def close(self):
    4.59 +        self.shutdown()
    4.60 +        return self.__dict__["conn"].close()
    4.61 +
    4.62 +    def shutdown(self, how=1):
    4.63 +        """
    4.64 +        SimpleXMLRpcServer.doPOST calls shutdown(1),
    4.65 +        and Connection.shutdown() doesn't take
    4.66 +        an argument. So we just discard the argument.
    4.67 +        """
    4.68 +        # Block until the shutdown is complete
    4.69 +        self.__dict__["conn"].shutdown()
    4.70 +        self.__dict__["conn"].shutdown()
    4.71 +
    4.72 +    def accept(self):
    4.73 +        """
    4.74 +        This is the other part of the shutdown() workaround.
    4.75 +        Since servers create new sockets, we have to infect
    4.76 +        them with our magic. :)
    4.77 +        """
    4.78 +        c, a = self.__dict__["conn"].accept()
    4.79 +        return (SSLWrapper(c), a)
    4.80 +
    4.81 +#
    4.82 +# End of pyOpenSSL-0.6 example code.
    4.83 +#
    4.84 +
    4.85 +class SSLXMLRPCServer(TCPXMLRPCServer):
    4.86 +    def __init__(self, addr, allowed, xenapi, logRequests = 1,
    4.87 +                 ssl_key_file = None, ssl_cert_file = None):
    4.88 +
    4.89 +        TCPXMLRPCServer.__init__(self, addr, allowed, xenapi,
    4.90 +                                 SSLXMLRPCRequestHandler, logRequests)
    4.91 +
    4.92 +        if not ssl_key_file or not ssl_cert_file:
    4.93 +            raise ValueError("SSLXMLRPCServer requires ssl_key_file "
    4.94 +                             "and ssl_cert_file to be set.")
    4.95 +
    4.96 +        # make a SSL socket
    4.97 +        ctx = SSL.Context(SSL.SSLv23_METHOD)
    4.98 +        ctx.set_options(SSL.OP_NO_SSLv2)
    4.99 +        ctx.use_privatekey_file (ssl_key_file)
   4.100 +        ctx.use_certificate_file(ssl_cert_file)
   4.101 +        self.socket = SSLWrapper(SSL.Connection(ctx,
   4.102 +                                 socket.socket(self.address_family,
   4.103 +                                               self.socket_type)))
   4.104 +        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
   4.105 +        self.server_bind()
   4.106 +        self.server_activate()
     5.1 --- a/tools/python/xen/xend/server/SrvServer.py	Tue Mar 27 23:03:32 2007 +0100
     5.2 +++ b/tools/python/xen/xend/server/SrvServer.py	Tue Mar 27 23:23:01 2007 +0100
     5.3 @@ -185,33 +185,49 @@ def _loadConfig(servers, root, reload):
     5.4      api_cfg = xoptions.get_xen_api_server()
     5.5      if api_cfg:
     5.6          try:
     5.7 -            addrs = [(str(x[0]).split(':'),
     5.8 -                      len(x) > 1 and x[1] or XendAPI.AUTH_PAM,
     5.9 -                      len(x) > 2 and x[2] and map(re.compile, x[2].split(" "))
    5.10 -                      or None)
    5.11 -                     for x in api_cfg]
    5.12 -            for addrport, auth, allowed in addrs:
    5.13 -                if auth not in [XendAPI.AUTH_PAM, XendAPI.AUTH_NONE]:
    5.14 -                    log.error('Xen-API server configuration %s is invalid, ' +
    5.15 -                              'as %s is not a valid authentication type.',
    5.16 -                              api_cfg, auth)
    5.17 -                    break
    5.18 +            for server_cfg in api_cfg:
    5.19 +                # Parse the xen-api-server config
    5.20 +                
    5.21 +                host = 'localhost'
    5.22 +                port = 0
    5.23 +                use_tcp = False
    5.24 +                ssl_key_file = None
    5.25 +                ssl_cert_file = None
    5.26 +                auth_method = XendAPI.AUTH_NONE
    5.27 +                hosts_allowed = None
    5.28 +                
    5.29 +                host_addr = server_cfg[0].split(':', 1)
    5.30 +                if len(host_addr) == 1 and host_addr[0].lower() == 'unix':
    5.31 +                    use_tcp = False
    5.32 +                elif len(host_addr) == 1:
    5.33 +                    use_tcp = True
    5.34 +                    port = int(host_addr[0])
    5.35 +                elif len(host_addr) == 2:
    5.36 +                    use_tcp = True
    5.37 +                    host = str(host_addr[0])
    5.38 +                    port = int(host_addr[1])
    5.39  
    5.40 -                if len(addrport) == 1:
    5.41 -                    if addrport[0] == 'unix':
    5.42 -                        servers.add(XMLRPCServer(auth, True,
    5.43 -                                                 path = XEN_API_SOCKET,
    5.44 -                                                 hosts_allowed = allowed))
    5.45 -                    else:
    5.46 -                        servers.add(
    5.47 -                            XMLRPCServer(auth, True, True, '',
    5.48 -                                         int(addrport[0]),
    5.49 -                                         hosts_allowed = allowed))
    5.50 -                else:
    5.51 -                    addr, port = addrport
    5.52 -                    servers.add(XMLRPCServer(auth, True, True, addr,
    5.53 -                                             int(port),
    5.54 -                                             hosts_allowed = allowed))
    5.55 +                if len(server_cfg) > 1:
    5.56 +                    if server_cfg[1] in [XendAPI.AUTH_PAM, XendAPI.AUTH_NONE]:
    5.57 +                        auth_method = server_cfg[1]
    5.58 +
    5.59 +                if len(server_cfg) > 2:
    5.60 +                    hosts_allowed = server_cfg[2] or None
    5.61 +                
    5.62 +
    5.63 +                if len(server_cfg) > 4:
    5.64 +                    # SSL key and cert file
    5.65 +                    ssl_key_file = server_cfg[3]
    5.66 +                    ssl_cert_file = server_cfg[4]
    5.67 +
    5.68 +
    5.69 +                servers.add(XMLRPCServer(auth_method, True, use_tcp = use_tcp,
    5.70 +                                         ssl_key_file = ssl_key_file,
    5.71 +                                         ssl_cert_file = ssl_cert_file,
    5.72 +                                         host = host, port = port,
    5.73 +                                         path = XEN_API_SOCKET,
    5.74 +                                         hosts_allowed = hosts_allowed))
    5.75 +
    5.76          except (ValueError, TypeError), exn:
    5.77              log.exception('Xen API Server init failed')
    5.78              log.error('Xen-API server configuration %s is invalid.', api_cfg)
    5.79 @@ -219,8 +235,17 @@ def _loadConfig(servers, root, reload):
    5.80      if xoptions.get_xend_tcp_xmlrpc_server():
    5.81          addr = xoptions.get_xend_tcp_xmlrpc_server_address()
    5.82          port = xoptions.get_xend_tcp_xmlrpc_server_port()
    5.83 -        servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True,
    5.84 -                                 host = addr, port = port))
    5.85 +        ssl_key_file = xoptions.get_xend_tcp_xmlrpc_server_ssl_key_file()
    5.86 +        ssl_cert_file = xoptions.get_xend_tcp_xmlrpc_server_ssl_cert_file()
    5.87 +
    5.88 +        if ssl_key_file and ssl_cert_file:
    5.89 +            servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True,
    5.90 +                                     ssl_key_file = ssl_key_file,
    5.91 +                                     ssl_cert_file = ssl_cert_file,
    5.92 +                                     host = addr, port = port))
    5.93 +        else:
    5.94 +            servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True,
    5.95 +                                     host = addr, port = port))
    5.96  
    5.97      if xoptions.get_xend_unix_xmlrpc_server():
    5.98          servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False))
     6.1 --- a/tools/python/xen/xend/server/XMLRPCServer.py	Tue Mar 27 23:03:32 2007 +0100
     6.2 +++ b/tools/python/xen/xend/server/XMLRPCServer.py	Tue Mar 27 23:23:01 2007 +0100
     6.3 @@ -21,6 +21,11 @@ import socket
     6.4  import types
     6.5  import xmlrpclib
     6.6  from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer
     6.7 +try:
     6.8 +    from SSLXMLRPCServer import SSLXMLRPCServer
     6.9 +    ssl_enabled = True
    6.10 +except ImportError:
    6.11 +    ssl_enabled = False
    6.12  
    6.13  from xen.xend import XendAPI, XendDomain, XendDomainInfo, XendNode
    6.14  from xen.xend import XendLogging, XendDmesg
    6.15 @@ -87,14 +92,20 @@ methods = ['device_create', 'device_conf
    6.16  exclude = ['domain_create', 'domain_restore']
    6.17  
    6.18  class XMLRPCServer:
    6.19 -    def __init__(self, auth, use_xenapi, use_tcp=False, host = "localhost",
    6.20 -                 port = 8006, path = XML_RPC_SOCKET, hosts_allowed = None):
    6.21 +    def __init__(self, auth, use_xenapi, use_tcp = False,
    6.22 +                 ssl_key_file = None, ssl_cert_file = None,
    6.23 +                 host = "localhost", port = 8006, path = XML_RPC_SOCKET,
    6.24 +                 hosts_allowed = None):
    6.25 +        
    6.26          self.use_tcp = use_tcp
    6.27          self.port = port
    6.28          self.host = host
    6.29          self.path = path
    6.30          self.hosts_allowed = hosts_allowed
    6.31          
    6.32 +        self.ssl_key_file = ssl_key_file
    6.33 +        self.ssl_cert_file = ssl_cert_file
    6.34 +        
    6.35          self.ready = False        
    6.36          self.running = True
    6.37          self.auth = auth
    6.38 @@ -107,14 +118,33 @@ class XMLRPCServer:
    6.39  
    6.40          try:
    6.41              if self.use_tcp:
    6.42 -                log.info("Opening TCP XML-RPC server on %s%d%s",
    6.43 +                using_ssl = self.ssl_key_file and self.ssl_cert_file
    6.44 +
    6.45 +                log.info("Opening %s XML-RPC server on %s%d%s",
    6.46 +                         using_ssl and 'HTTPS' or 'TCP',
    6.47                           self.host and '%s:' % self.host or
    6.48                           'all interfaces, port ',
    6.49                           self.port, authmsg)
    6.50 -                self.server = TCPXMLRPCServer((self.host, self.port),
    6.51 -                                              self.hosts_allowed,
    6.52 -                                              self.xenapi is not None,
    6.53 -                                              logRequests = False)
    6.54 +
    6.55 +                if not ssl_enabled:
    6.56 +                    raise ValueError("pyOpenSSL not installed. "
    6.57 +                                     "Unable to start HTTPS XML-RPC server")
    6.58 +
    6.59 +                if using_ssl:
    6.60 +                    self.server = SSLXMLRPCServer(
    6.61 +                        (self.host, self.port),
    6.62 +                        self.hosts_allowed,
    6.63 +                        self.xenapi is not None,
    6.64 +                        logRequests = False,
    6.65 +                        ssl_key_file = self.ssl_key_file,
    6.66 +                        ssl_cert_file = self.ssl_cert_file)
    6.67 +                else:
    6.68 +                    self.server = TCPXMLRPCServer(
    6.69 +                        (self.host, self.port),
    6.70 +                        self.hosts_allowed,
    6.71 +                        self.xenapi is not None,
    6.72 +                        logRequests = False)
    6.73 +
    6.74              else:
    6.75                  log.info("Opening Unix domain socket XML-RPC server on %s%s",
    6.76                           self.path, authmsg)
    6.77 @@ -126,7 +156,12 @@ class XMLRPCServer:
    6.78              ready = True
    6.79              running = False
    6.80              return
    6.81 -
    6.82 +        except Exception, e:
    6.83 +            log.exception('Cannot start server: %s!', e)
    6.84 +            ready = True
    6.85 +            running = False
    6.86 +            return
    6.87 +        
    6.88          # Register Xen API Functions
    6.89          # -------------------------------------------------------------------
    6.90          # exportable functions are ones that do not begin with '_'