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

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



# 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


 


Rackspace

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