aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorEwan Mellor <ewan@xensource.com>2007-03-27 23:23:01 +0100
committerEwan Mellor <ewan@xensource.com>2007-03-27 23:23:01 +0100
commitb843dd7af822820d29e587f192414d44acdf3dfa (patch)
tree08a84badab97726f684d58ced51703a213c8602a /tools
parent2a5cee571cb8eb1896f35431b54fa4d00fe2d682 (diff)
downloadxen-b843dd7af822820d29e587f192414d44acdf3dfa.tar.gz
xen-b843dd7af822820d29e587f192414d44acdf3dfa.tar.bz2
xen-b843dd7af822820d29e587f192414d44acdf3dfa.zip
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>
Diffstat (limited to 'tools')
-rw-r--r--tools/examples/xend-config.sxp14
-rw-r--r--tools/python/xen/util/xmlrpcclient.py30
-rw-r--r--tools/python/xen/xend/XendOptions.py8
-rw-r--r--tools/python/xen/xend/server/SSLXMLRPCServer.py103
-rw-r--r--tools/python/xen/xend/server/SrvServer.py83
-rw-r--r--tools/python/xen/xend/server/XMLRPCServer.py51
6 files changed, 249 insertions, 40 deletions
diff --git a/tools/examples/xend-config.sxp b/tools/examples/xend-config.sxp
index a229ccd43f..047ed71c1e 100644
--- a/tools/examples/xend-config.sxp
+++ b/tools/examples/xend-config.sxp
@@ -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,11 +64,18 @@
#(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.
#(xend-port 8000)
diff --git a/tools/python/xen/util/xmlrpcclient.py b/tools/python/xen/util/xmlrpcclient.py
index ec12c62f27..3a0295667f 100644
--- a/tools/python/xen/util/xmlrpcclient.py
+++ b/tools/python/xen/util/xmlrpcclient.py
@@ -32,7 +32,6 @@ except ImportError:
ssh_enabled = False
-
# A new ServerProxy that also supports httpu urls. An http URL comes in the
# form:
#
@@ -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 --git a/tools/python/xen/xend/XendOptions.py b/tools/python/xen/xend/XendOptions.py
index c220af0c6e..73de5a39c3 100644
--- a/tools/python/xen/xend/XendOptions.py
+++ b/tools/python/xen/xend/XendOptions.py
@@ -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 --git a/tools/python/xen/xend/server/SSLXMLRPCServer.py b/tools/python/xen/xend/server/SSLXMLRPCServer.py
new file mode 100644
index 0000000000..1bbd636a4f
--- /dev/null
+++ b/tools/python/xen/xend/server/SSLXMLRPCServer.py
@@ -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 --git a/tools/python/xen/xend/server/SrvServer.py b/tools/python/xen/xend/server/SrvServer.py
index 40e62fdb7b..7646466bd8 100644
--- a/tools/python/xen/xend/server/SrvServer.py
+++ b/tools/python/xen/xend/server/SrvServer.py
@@ -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 --git a/tools/python/xen/xend/server/XMLRPCServer.py b/tools/python/xen/xend/server/XMLRPCServer.py
index ed07811ff6..661581fd3a 100644
--- a/tools/python/xen/xend/server/XMLRPCServer.py
+++ b/tools/python/xen/xend/server/XMLRPCServer.py
@@ -21,6 +21,11 @@ import socket
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 @@ methods = ['device_create', 'device_configure',
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 '_'