All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anthony Liguori <aliguori@us.ibm.com>
To: xen-devel <xen-devel@lists.xensource.com>
Cc: Ewan Mellor <ewan@xensource.com>
Subject: [RFC][PATCH] Secure XML-RPC for Xend
Date: Thu, 08 Jun 2006 21:13:17 -0500	[thread overview]
Message-ID: <4488D93D.7070303@us.ibm.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 804 bytes --]

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@rhesis.austin.ibm.com/RPC2' xm list

Runs xm list on a local machine but does all of the RPCs over a secure 
connection (prompting for passwords).

Thoughts?

Regards,

Anthony Liguori



[-- Attachment #2: xend-secure-xmlrpc.diff --]
[-- Type: text/plain, Size: 9293 bytes --]

# HG changeset patch
# User anthony@rhesis.austin.ibm.com
# 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,

[-- Attachment #3: Type: text/plain, Size: 138 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel

             reply	other threads:[~2006-06-09  2:13 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-06-09  2:13 Anthony Liguori [this message]
2006-06-09  2:45 ` [RFC][PATCH] Secure XML-RPC for Xend Matthew Palmer
2006-06-09  8:34 ` Anil Madhavapeddy
2006-06-09  8:41   ` Daniel Veillard
2006-06-09  8:54     ` Anil Madhavapeddy
2006-06-09 14:57       ` Anthony Liguori
2006-06-09 15:45         ` Daniel Veillard
2006-06-09 15:57           ` Anthony Liguori
2006-06-09 12:00   ` Anthony Liguori
2006-06-14  8:36 ` Ewan Mellor
2006-06-14 17:26   ` Anthony Liguori
2006-06-14 17:36     ` Ewan Mellor
  -- strict thread matches above, loose matches on Subject: below --
2006-06-09  8:10 Ian Pratt
2006-06-09 12:10 ` Anthony Liguori
2006-06-14  8:43   ` Ewan Mellor
2006-06-14 17:34     ` Anthony Liguori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4488D93D.7070303@us.ibm.com \
    --to=aliguori@us.ibm.com \
    --cc=ewan@xensource.com \
    --cc=xen-devel@lists.xensource.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.