# 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
|