ia64/xen-unstable

changeset 12604:6206685650f5

Implement an "allowed hosts" mechanism for the XML-RPC server layer, using
code from the relocation protocol handling and some plumbing. Add a new
configuration entry for the Xen-API server, including use of this mechanism.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Tue Nov 28 10:23:34 2006 +0000 (2006-11-28)
parents b4baf35cff11
children 018af1c94a5e
files tools/examples/xend-config.sxp tools/python/xen/util/xmlrpclib2.py tools/python/xen/web/connection.py tools/python/xen/web/tcp.py tools/python/xen/xend/XendRoot.py tools/python/xen/xend/server/SrvServer.py tools/python/xen/xend/server/XMLRPCServer.py
line diff
     1.1 --- a/tools/examples/xend-config.sxp	Tue Nov 28 09:51:05 2006 +0000
     1.2 +++ b/tools/examples/xend-config.sxp	Tue Nov 28 10:23:34 2006 +0000
     1.3 @@ -14,6 +14,32 @@
     1.4  #(logfile /var/log/xen/xend.log)
     1.5  #(loglevel DEBUG)
     1.6  
     1.7 +# The Xen-API server configuration.  (Please note that this server is available
     1.8 +# as an UNSUPPORTED PREVIEW in Xen 3.0.4, and should not be relied upon).
     1.9 +#
    1.10 +# This value configures the ports, interfaces, and access controls for the
    1.11 +# Xen-API server.  Each entry in the list starts with either unix, a port
    1.12 +# number, or an address:port pair.  If this is "unix", then a UDP socket is
    1.13 +# opened, and this entry applies to that.  If it is a port, then Xend will
    1.14 +# listen on all interfaces on that TCP port, and if it is an address:port pair,
    1.15 +# then Xend will listen on the specified port, using the interface with the
    1.16 +# specified address.
    1.17 +#
    1.18 +# The subsequent string gives the access control for the listener in question. 
    1.19 +# If this is missing or empty, then all connections are accepted.
    1.20 +# Otherwise, this should be a space-separated sequence of regular expressions;
    1.21 +# any host with a fully-qualified domain name or an IP address that matches one
    1.22 +# of these regular expressions will be accepted.
    1.23 +#
    1.24 +# Example:
    1.25 +#
    1.26 +#  Listen on TCP port 9363 on all interfaces, accepting connections only from
    1.27 +#  machines in example.com or localhost.
    1.28 +#  (xen-api-server ((9363 '^localhost$ example\\.com$')))
    1.29 +#
    1.30 +# Default:
    1.31 +#   (xen-api-server ((unix)))
    1.32 +
    1.33  #(xend-http-server no)
    1.34  #(xend-unix-server no)
    1.35  #(xend-tcp-xmlrpc-server no)
     2.1 --- a/tools/python/xen/util/xmlrpclib2.py	Tue Nov 28 09:51:05 2006 +0000
     2.2 +++ b/tools/python/xen/util/xmlrpclib2.py	Tue Nov 28 10:23:34 2006 +0000
     2.3 @@ -30,6 +30,7 @@ from SimpleXMLRPCServer import SimpleXML
     2.4  import SocketServer
     2.5  import xmlrpclib, socket, os, stat
     2.6  
     2.7 +from xen.web import connection
     2.8  from xen.xend.XendLogging import log
     2.9  
    2.10  try:
    2.11 @@ -70,6 +71,11 @@ def stringify(value):
    2.12  class XMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
    2.13      protocol_version = "HTTP/1.1"
    2.14  
    2.15 +    def __init__(self, hosts_allowed, request, client_address, server):
    2.16 +        self.hosts_allowed = hosts_allowed
    2.17 +        SimpleXMLRPCRequestHandler.__init__(self, request, client_address,
    2.18 +                                            server)
    2.19 +
    2.20      # this is inspired by SimpleXMLRPCRequestHandler's do_POST but differs
    2.21      # in a few non-trivial ways
    2.22      # 1) we never generate internal server errors.  We let the exception
    2.23 @@ -77,6 +83,11 @@ class XMLRPCRequestHandler(SimpleXMLRPCR
    2.24      # 2) we don't bother checking for a _dispatch function since we don't
    2.25      #    use one
    2.26      def do_POST(self):
    2.27 +        addrport = self.client_address
    2.28 +        if not connection.hostAllowed(addrport, self.hosts_allowed):
    2.29 +            self.connection.shutdown(1)
    2.30 +            return
    2.31 +
    2.32          data = self.rfile.read(int(self.headers["content-length"]))
    2.33          rsp = self.server._marshaled_dispatch(data)
    2.34  
    2.35 @@ -150,9 +161,14 @@ class ServerProxy(xmlrpclib.ServerProxy)
    2.36  class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
    2.37      allow_reuse_address = True
    2.38  
    2.39 -    def __init__(self, addr, requestHandler=XMLRPCRequestHandler,
    2.40 +    def __init__(self, addr, allowed, requestHandler=None,
    2.41                   logRequests = 1):
    2.42 -        SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
    2.43 +        if requestHandler is None:
    2.44 +            requestHandler = XMLRPCRequestHandler
    2.45 +        SimpleXMLRPCServer.__init__(self, addr,
    2.46 +                                    (lambda x, y, z:
    2.47 +                                     requestHandler(allowed, x, y, z)),
    2.48 +                                    logRequests)
    2.49  
    2.50          flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
    2.51          flags |= fcntl.FD_CLOEXEC
    2.52 @@ -217,7 +233,7 @@ class UnixXMLRPCRequestHandler(XMLRPCReq
    2.53  class UnixXMLRPCServer(TCPXMLRPCServer):
    2.54      address_family = socket.AF_UNIX
    2.55  
    2.56 -    def __init__(self, addr, logRequests = 1):
    2.57 +    def __init__(self, addr, allowed, logRequests = 1):
    2.58          parent = os.path.dirname(addr)
    2.59          if os.path.exists(parent):
    2.60              os.chown(parent, os.geteuid(), os.getegid())
    2.61 @@ -226,5 +242,6 @@ class UnixXMLRPCServer(TCPXMLRPCServer):
    2.62                  os.unlink(addr)
    2.63          else:
    2.64              os.makedirs(parent, stat.S_IRWXU)
    2.65 -        TCPXMLRPCServer.__init__(self, addr, UnixXMLRPCRequestHandler,
    2.66 -                                 logRequests)
    2.67 +
    2.68 +        TCPXMLRPCServer.__init__(self, addr, allowed,
    2.69 +                                 UnixXMLRPCRequestHandler, logRequests)
     3.1 --- a/tools/python/xen/web/connection.py	Tue Nov 28 09:51:05 2006 +0000
     3.2 +++ b/tools/python/xen/web/connection.py	Tue Nov 28 10:23:34 2006 +0000
     3.3 @@ -24,6 +24,8 @@ import fcntl
     3.4  
     3.5  from errno import EAGAIN, EINTR, EWOULDBLOCK
     3.6  
     3.7 +from xen.xend.XendLogging import log
     3.8 +
     3.9  """General classes to support server and client sockets, without
    3.10  specifying what kind of socket they are. There are subclasses
    3.11  for TCP and unix-domain sockets (see tcp.py and unix.py).
    3.12 @@ -76,7 +78,7 @@ class SocketListener:
    3.13      Accepts connections and runs a thread for each one.
    3.14      """
    3.15  
    3.16 -    def __init__(self, protocol_class, hosts_allow = ''):
    3.17 +    def __init__(self, protocol_class):
    3.18          self.protocol_class = protocol_class
    3.19          self.sock = self.createSocket()
    3.20          threading.Thread(target=self.main).start()
    3.21 @@ -111,3 +113,15 @@ class SocketListener:
    3.22                          break
    3.23          finally:
    3.24              self.close()
    3.25 +
    3.26 +
    3.27 +def hostAllowed(addrport, hosts_allowed):
    3.28 +    if hosts_allowed is None:
    3.29 +        return True
    3.30 +    else:
    3.31 +        fqdn = socket.getfqdn(addrport[0])
    3.32 +        for h in hosts_allowed:
    3.33 +            if h.match(fqdn) or h.match(addrport[0]):
    3.34 +                return True
    3.35 +        log.warn("Rejected connection from %s (%s).", addrport[0], fqdn)
    3.36 +        return False
     4.1 --- a/tools/python/xen/web/tcp.py	Tue Nov 28 09:51:05 2006 +0000
     4.2 +++ b/tools/python/xen/web/tcp.py	Tue Nov 28 10:23:34 2006 +0000
     4.3 @@ -57,20 +57,10 @@ class TCPListener(connection.SocketListe
     4.4  
     4.5      def acceptConnection(self, sock, addrport):
     4.6          addr = addrport[0]
     4.7 -        if self.hosts_allow is None:
     4.8 -                connection.SocketServerConnection(sock, self.protocol_class)
     4.9 +        if connection.hostAllowed(addrport, self.hosts_allow):
    4.10 +            connection.SocketServerConnection(sock, self.protocol_class)
    4.11          else:
    4.12 -            fqdn = socket.getfqdn(addr)
    4.13 -            for h in self.hosts_allow:
    4.14 -                if h.match(fqdn) or h.match(addr):
    4.15 -                    log.debug("Match %s %s", fqdn, h.pattern)
    4.16 -                    connection.SocketServerConnection(sock,
    4.17 -                                                      self.protocol_class)
    4.18 -                    return
    4.19 -
    4.20              try:
    4.21 -                log.warn("Rejected connection from %s:%d (%s) for port %d.",
    4.22 -                         addr, addrport[1], fqdn, self.port)
    4.23                  sock.close()
    4.24              except:
    4.25                  pass
     5.1 --- a/tools/python/xen/xend/XendRoot.py	Tue Nov 28 09:51:05 2006 +0000
     5.2 +++ b/tools/python/xen/xend/XendRoot.py	Tue Nov 28 10:23:34 2006 +0000
     5.3 @@ -54,6 +54,9 @@ class XendRoot:
     5.4      """Default level of information to be logged."""
     5.5      loglevel_default = 'DEBUG'
     5.6  
     5.7 +    """Default Xen-API server configuration. """
     5.8 +    xen_api_server_default = [['unix']]
     5.9 +
    5.10      """Default for the flag indicating whether xend should run an http server
    5.11      (deprecated)."""
    5.12      xend_http_server_default = 'no'
    5.13 @@ -189,6 +192,12 @@ class XendRoot:
    5.14          except Exception:
    5.15              raise XendError("invalid xend config %s: expected int: %s" % (name, v))
    5.16  
    5.17 +    def get_xen_api_server(self):
    5.18 +        """Get the Xen-API server configuration.
    5.19 +        """
    5.20 +        return self.get_config_value('xen-api-server',
    5.21 +                                     self.xen_api_server_default)
    5.22 +
    5.23      def get_xend_http_server(self):
    5.24          """Get the flag indicating whether xend should run an http server.
    5.25          """
     6.1 --- a/tools/python/xen/xend/server/SrvServer.py	Tue Nov 28 09:51:05 2006 +0000
     6.2 +++ b/tools/python/xen/xend/server/SrvServer.py	Tue Nov 28 10:23:34 2006 +0000
     6.3 @@ -41,6 +41,7 @@
     6.4  # todo Support command-line args.
     6.5  
     6.6  import fcntl
     6.7 +import re
     6.8  import time
     6.9  import signal
    6.10  from threading import Thread
    6.11 @@ -148,6 +149,29 @@ def create():
    6.12          log.info('unix path=' + path)
    6.13          servers.add(UnixHttpServer(root, path))
    6.14  
    6.15 +    api_cfg = xroot.get_xen_api_server()
    6.16 +    if api_cfg:
    6.17 +        try:
    6.18 +            addrs = [(str(x[0]).split(':'),
    6.19 +                      len(x) > 1 and x[1] and map(re.compile, x[1].split(" "))
    6.20 +                      or None)
    6.21 +                     for x in api_cfg]
    6.22 +            for addrport, allowed in addrs:
    6.23 +                if len(addrport) == 1:
    6.24 +                    if addrport[0] == 'unix':
    6.25 +                        servers.add(XMLRPCServer(allowed = allowed))
    6.26 +                    else:
    6.27 +                        servers.add(XMLRPCServer(True, '', int(addrport[0]),
    6.28 +                                                 allowed = allowed))
    6.29 +                else:
    6.30 +                    addr, port = addrport
    6.31 +                    servers.add(XMLRPCServer(True, addr, int(port),
    6.32 +                                             allowed = allowed))
    6.33 +        except ValueError:
    6.34 +            log.error('Xen-API server configuration %s is invalid' % api_cfg)
    6.35 +        except TypeError:
    6.36 +            log.error('Xen-API server configuration %s is invalid' % api_cfg)
    6.37 +
    6.38      if xroot.get_xend_tcp_xmlrpc_server():
    6.39          servers.add(XMLRPCServer(True))
    6.40  
     7.1 --- a/tools/python/xen/xend/server/XMLRPCServer.py	Tue Nov 28 09:51:05 2006 +0000
     7.2 +++ b/tools/python/xen/xend/server/XMLRPCServer.py	Tue Nov 28 10:23:34 2006 +0000
     7.3 @@ -85,11 +85,12 @@ exclude = ['domain_create', 'domain_rest
     7.4  
     7.5  class XMLRPCServer:
     7.6      def __init__(self, use_tcp=False, host = "localhost", port = 8006,
     7.7 -                 path = XML_RPC_SOCKET):
     7.8 +                 path = XML_RPC_SOCKET, hosts_allowed = None):
     7.9          self.use_tcp = use_tcp
    7.10          self.port = port
    7.11          self.host = host
    7.12          self.path = path
    7.13 +        self.hosts_allowed = hosts_allowed
    7.14          
    7.15          self.ready = False        
    7.16          self.running = True
    7.17 @@ -97,10 +98,18 @@ class XMLRPCServer:
    7.18          
    7.19      def run(self):
    7.20          if self.use_tcp:
    7.21 +            log.info("Opening TCP XML-RPC server on %s%d.",
    7.22 +                     self.host and '%s:' % self.host or
    7.23 +                     'all interfaces, port ',
    7.24 +                     self.port)
    7.25              self.server = TCPXMLRPCServer((self.host, self.port),
    7.26 +                                          self.hosts_allowed,
    7.27                                            logRequests = False)
    7.28          else:
    7.29 -            self.server = UnixXMLRPCServer(self.path, logRequests = False)
    7.30 +            log.info("Opening Unix domain socket XML-RPC server on %s:%d.",
    7.31 +                     self.path)
    7.32 +            self.server = UnixXMLRPCServer(self.path, self.hosts_allowed,
    7.33 +                                           logRequests = False)
    7.34  
    7.35          # Register Xen API Functions
    7.36          # -------------------------------------------------------------------