[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [RFC][PATCH] Secure XML-RPC for Xend
Hi,The following patch implements a secure XML-RPC protocol for Xend. Instead of using HTTPS with basic authentication and dealing with all that nasty OpenSSL/PAM integration, it just uses SSH. This gives you all the properties you want (great security and PAM integration) with very little code. There are some minor issues so I'd rather it not be applied immediately. I'd like to get some feedback from people as to whether this approach is reasonable. A user-facing change is that now you can use the XM_SERVER environmental variable to specific an XML-RPC URI. For instance: XM_SERVER='ssh://root@xxxxxxxxxxxxxxxxxxxxx/RPC2' xm listRuns xm list on a local machine but does all of the RPCs over a secure connection (prompting for passwords). Thoughts? Regards, Anthony Liguori # HG changeset patch # User anthony@xxxxxxxxxxxxxxxxxxxxx # Node ID 4de241a7e91a1e59b6db965f5d2ef10014f764e5 # Parent 4f1e39ec05d6ec711f9ba4a66a3653ed3e168311 Add support secure XML-RPC. This is done by multiplexing multiple SSH sessions over a single session (to avoid multiple password entries). Here are the changes: 1) Add support to xmlrpclib2.ServerProxy for ssh:// protocol 2) Add an xm serve command which proxies XML-RPC over stdio 3) Make xm look at the XM_SERVER variable to determine which XML-RPC protocol to use There are some issues that need to be addressed before inclusion. Namely: 1) Python moans about tempnam(). I don't think there's a better solution though. 2) A command *must* be executed to cleanup the ssh session on exit. I currently use __del__() which doesn't seem to make Python happy in certain cases. 3) I have done basic testing but not regression testing with xm-test diff -r 4f1e39ec05d6 -r 4de241a7e91a tools/python/xen/util/xmlrpclib2.py --- a/tools/python/xen/util/xmlrpclib2.py Thu Jun 8 15:51:39 2006 +++ b/tools/python/xen/util/xmlrpclib2.py Fri Jun 9 01:59:02 2006 @@ -24,14 +24,117 @@ import types from httplib import HTTPConnection, HTTP -from xmlrpclib import Transport +from xmlrpclib import Transport, getparser + from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler import xmlrpclib, socket, os, stat import SocketServer -import xen.xend.XendClient from xen.xend.XendLogging import log +import os, commands, getpass, termios, fcntl + +class SSH: + def __init__(self, host, user=None, askpass=None): + """Constructor for SSH object. + + This constructor allows you to specify a hostname, a username, + and an askpass program. username will default to the current + user and if askpass is not specified, the password will be + read (if necessary) from the controlling terminal.""" + + self.sock = os.tempnam() + self.host = host + sock = self.sock + if user == None: user = getpass.getuser() + self.user = user + + pid = os.fork() + if pid == 0: + if askpass: + f = open('/dev/tty', 'w') + os.environ['SSH_ASKPASS'] = askpass + fcntl.ioctl(f.fileno(), termios.TIOCNOTTY) + f.close() + + os.execvp('/usr/bin/ssh', ['ssh', '-f', '-2', '-N', '-M', '-S', + sock, '-a', '-x', '-l', user, host]) + + p,s = os.waitpid(pid, 0) + if s != 0: + raise OSError(s, 'Failed to start ssh server') + + def close(self): + """Closes an SSH object destroying the SSH server. + + This command must be called when the object is no longer + needed to ensure that the SSH server is destroyed properly.""" + + if self.sock: + commands.getstatusoutput('ssh -q -t -S %s -l %s -O exit %s' % + (self.sock, self.user, self.host)) + + # is this the best way to deal with this?? + def __del__(self): + self.close() + + def getcmd(self, cmd): + """Returns a command expanded to include SSH options. + + This function will add the appropriate ssh command and + options to the passed in command string. This is useful + if commands.getstatusoutput is not an appropriate exec + interface or if you want to handle error conditions in + your own way.""" + + return 'ssh -q -t -S %s -l %s %s %s' % (self.sock, self.user, + self.host, cmd) + + def runcmd(self, cmd, data=None): + """Runs a command using an existing SSH connection. + + This function will run the passed in command on a remote + machine and either return the output or raise an OSError + if the command exits with a non-zero status (or some + other failure occurs).""" + + cmdline = self.getcmd(cmd) + if data: + f = open("/tmp/stuff.txt", "w") + f.write(data) + f.close() + cmdline = "cat /tmp/stuff.txt | %s" % cmdline + else: + cmdline = cmdline + + o,s = commands.getstatusoutput(cmdline) + if o != 0: + raise OSError(o,s) + return s + +class SSHTransport(object): + def __init__(self, ssh): + self.ssh = ssh + + def request(self, host, handler, request_body, verbose=0): + p, u = getparser() + s = self.ssh.runcmd("xm serve", + """POST /%s HTTP/1.0 +User-Agent: Xen +Host: %s +Content-Type: text/xml +Content-Length: %d + +%s""" % (handler, host, len(request_body), request_body)) + lines = s.split('\n') + for i in range(len(lines)): + if lines[i] == '' or lines[i] == '\r': + s = '\n'.join(lines[(i + 1):]) + break + + p.feed(s) + p.close() + return u.close() # A new ServerProxy that also supports httpu urls. An http URL comes in the # form: @@ -68,13 +171,32 @@ class ServerProxy(xmlrpclib.ServerProxy): + def process_ssh_uri(self, uri, askpass): + uri = uri[6:] + parts = uri.split('@') + if len(parts) > 1: + user = parts[0] + uri = '@'.join(parts[1:]) + else: + user = None + parts = uri.split('/') + if len(parts) < 2: + raise ValueError("Invalid ssh:// URL '%s'" % uri) + host = parts[0] + path = '/'.join(parts[1:]) + ssh = SSH(host, user, askpass=askpass) + transport = SSHTransport(ssh) + return transport, 'http://%s/%s' % (host, path) + def __init__(self, uri, transport=None, encoding=None, verbose=0, - allow_none=1): + allow_none=1, askpass=None): if transport == None: (protocol, rest) = uri.split(':', 1) if protocol == 'httpu': uri = 'http:' + rest transport = UnixTransport() + elif protocol == 'ssh': + transport, uri = self.process_ssh_uri(uri, askpass) xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding, verbose, allow_none) @@ -121,6 +243,7 @@ except xmlrpclib.Fault, fault: response = xmlrpclib.dumps(fault) except Exception, exn: + import xen.xend.XendClient log.exception(exn) response = xmlrpclib.dumps( xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn))) diff -r 4f1e39ec05d6 -r 4de241a7e91a tools/python/xen/xend/XendClient.py --- a/tools/python/xen/xend/XendClient.py Thu Jun 8 15:51:39 2006 +++ b/tools/python/xen/xend/XendClient.py Fri Jun 9 01:59:02 2006 @@ -18,6 +18,7 @@ #============================================================================ from xen.util.xmlrpclib2 import ServerProxy +import os XML_RPC_SOCKET = "/var/run/xend/xmlrpc.sock" @@ -25,4 +26,7 @@ ERROR_GENERIC = 2 ERROR_INVALID_DOMAIN = 3 -server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock') +if os.environ.has_key('XM_SERVER'): + server = ServerProxy(os.environ['XM_SERVER']) +else: + server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock') diff -r 4f1e39ec05d6 -r 4de241a7e91a tools/python/xen/xm/main.py --- a/tools/python/xen/xm/main.py Thu Jun 8 15:51:39 2006 +++ b/tools/python/xen/xm/main.py Fri Jun 9 01:59:02 2006 @@ -124,6 +124,7 @@ loadpolicy_help = "loadpolicy <policy> Load binary policy into hypervisor" makepolicy_help = "makepolicy <policy> Build policy and create .bin/.map files" labels_help = "labels [policy] [type=DOM|..] List <type> labels for (active) policy." +serve_help = "serve Proxy Xend XML-RPC over stdio" short_command_list = [ "console", @@ -171,7 +172,8 @@ host_commands = [ "dmesg", "info", - "log" + "log", + "serve", ] scheduler_commands = [ @@ -833,6 +835,36 @@ arg_check(args, "log", 0) print server.xend.node.log() + +def xm_serve(args): + arg_check(args, "serve", 0) + + try: + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(xen.xend.XendClient.XML_RPC_SOCKET) + f = sys.stdin + + content_length = 0 + line = f.readline() + headers = '' + while len(line) != 0: + if line.lower().startswith('content-length: '): + content_length = int(line[16:]) + headers += line + if line in ['\r\n', '\n']: + break + line = f.readline() + + buf = f.read(content_length) + s.sendall(headers + buf) + + buf = s.recv(4096) + while len(buf) != 0: + sys.stdout.write(buf) + buf = s.recv(4096) + s.close() + except Exception, ex: + print ex def parse_dev_info(info): def get_info(n, t, d): @@ -1072,6 +1104,7 @@ "dmesg": xm_dmesg, "info": xm_info, "log": xm_log, + "serve": xm_serve, # scheduler "sched-bvt": xm_sched_bvt, "sched-bvt-ctxallow": xm_sched_bvt_ctxallow, _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |