mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
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>
This commit is contained in:
1 parent
2a5cee571c
commit
b843dd7af8
6 files changed
+248
-39
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
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))
|
||||
|
||||
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))
|
||||
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))
|
||||
|
||||
@@ -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 '_'
|
||||
|
||||