httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Michal Kosek <michauht...@nh.pl>
Subject Dispatching MPM
Date Mon, 18 Jul 2005 17:32:21 GMT
Hi,

I'm going to make a new MPM (I called it Dispatching MPM, or D-MPM) that
would do what perchild is supposed to do.  Here is a rough proposal of 
the architecture.

My general idea is shown on the following diagram of example state of
D-MPM:

     +-------+   +-------+    +-------+       +-------+
     |worker1|   |worker2|    |worker3|       |workerN|
     |-------|   |-------|    |-------|       |------ |
     |user1  |   |user2  |    |user1  |  ...  |userX  |
     |group1 |   |group2 |    |group2 |       |groupY |
     |/home/a|   |/home/b|    |/home/c|       |/home/Z|
     +-------+   +-------+    +-------+       +-------+
     /\  /\          /\           /\              /\
     ||  ||          ||           ||     ...      ||
     ||  \/          \/           \/              \/
     || +--------------------------------------------+         +--------+
     || |               dispatcher                   |         | master |
     || |--------------------------------------------|         |--------|
     || |               nobody                       |         |root    |
     || |               nogroup                      |  <===>  |root    |
     || |               /var/empty                   |         |/       |
     || +--------------------------------------------+         +--------+
     ||           /\
     ||           ||
     ||           ||
     \/           \/
  +-------+   +-------+
  |client1|   |client2|
  +-------+   +-------+

Arrows indicate communication, eg. using sockets. First line of each
item contains its name, next lines contain user, group, and root
directory it runs with, respectively.

System consists of 3 parts:

Workers--they are basically the same as in other MPMs, the only
difference is that each has some set of permissions (uid, gid, root
directory, etc.) of particular group of virtual hosts.

Dispatcher, accepting incoming connections, reading requests and
redirecting them to one of appropriate workers (i.e. those having set
of permissions needed to handle that virtual host).

Apache master process, listening to requests from dispatcher, and
creating new workers with specified permissions, according to the
requests.

In presented example, client1 has specified Host: header and has been
redirected to worker1, while client2 hasn't specified it yet, so it is
still connected to the dispatcher.  The diagram is created with
assumption that socket passing will be used, not proxying.

Rationale for the Architecture

  Most existing solutions (that separate different virtual hosts) have
  the architecture where many threads/processes (each running with
  privilege of some virtual host) accept connections, and redirect them
  if client wants another virtual host. This may create potential
  security problem--if there were an exploitable bug in code before
  redirection, an attacker would have a possibility to gain privilege
  of user of any virtual host. In D-MPM, an attacker would gain only
  dispatcher's permissions, which can be very restricted.

  In existing MPMs number of threads for each virtual host is static,
  set in configuration file. D-MPM approach fixes that problem--new
  workers are dynamically created by master process on dispatcher's
  demand.

  Of course there might be a security hole in master's code. But
  communication protocol between dispatcher and creator is going to be
  rather simple: dispatcher may only request that new worker with
  specified permissions should be created, creator returns socket
  descriptor connected to a newly created child or error code.

Extensibility

  Nowadays permissions of process depend not only on process user and
  group ID, but on many other settings that vary from system to system.
  The most popular is process' root directory (changable by chroot(2)),
  but there are many others (eg. POSIX capabilities, jails, other
  security settings in RSBAC and SELinux).

  To allow to utilize any of security extensions particular system has,
  D-MPM will have a loadable module support. Making D-MPM change some
  non-standard permissions would involve only writing a small shared
  library, without need for changing D-MPM's source or even recompiling
  it.

  Someone creating a module needs to provide only two functions: first,
  that reads part of virtual host's configuration, and second, that
  changes process permissions according to that configuration.

Implementation issues

  Dispatcher

    It would be best to make dispatcher single-threaded, I suppose that
    creating separate thread for each connection would take much more
    resources.  OTOH it may cause some problems.  One I am currently
    aware of is mod_ssl, which AFAIK doesn't currently have non-blocking
    API.  Would it be difficult to create such API?

    I want dispatcher to be as simple as possible, and make workers do
    all the work (filters, handlers, etc.).  Of course, as dispatcher
    must read Host header, it must use connection filters.

    This headers is needed, because dispatcher has to decide to which
    worker a connection should be redirected.  I think that it would be
    best if it didn't do full header parsing, but just look for virtual
    host name.

    There are two ways of dispatching connection:
    - passing a socket descriptor (along with data that were already
      read) to the appropriate worker,
    - acting as a proxy, redirecting data to the appropriate worker.

    Passing a descriptor wouldn't involve unnecessary copying.  But in
    case of SSL connection, passing whole connection state may be
    difficult.  I have also heard that sending socket is very resource
    consuming.  So probably proxying whole connection would be a better
    solution.  Another advantage of acting as a proxy would be that less
    things would have to be changed on worker side.

    Many dispatchers may be needed only in specific situations, eg. when
    someone wants each dispatcher to run on a different processor on a SMP
    system.

  Workers

    Workers won't change much, so code from existing MPMs should be
    somehow reused.  Even running different workers for different
    virtual hosts should be possible.  So I'm thinking of possibility of
    some generic code reuse, that would allow me to use workers from
    almost any MPM (prefork, worker, event...).

    Keep-alive requests.  If client, during kept-alive connection,
    specifies request to virtual host that has other set of
    permissions than previous one, worker will have to redirect that
    connection back to dispatcher, to let it find a worker for that
    virtual host and make it handle the connection.  If we decide that
    dispatcher acts as a proxy, it will also have to track Connection
    headers, and redirect connection to other worker, when needed.
    It would involve a lot of work, so I have a proposal of going around
    the problem: if a client specifies another virtual host than before,
    server responds with
    Connection: close
    Location: URL
    sending URL that client has just requested.  It would force client
    to reconnect, and then it would be dispatched to an appropriate
    worker.

    Logging.  Currently every worker has descriptors of every log file.
    To provide security, after creating a worker and changing its
    permissions all the unnecessary log file descriptors will be closed.

  Master

    It is the only part of code that must run with root permissions. 
    It will be a part of Apache master process.  Protocol between
    dispatcher and master will consist of requests (issued by
    dispatcher) to create new workers, and answers, that would allow
    dispatcher communicate with newly created worker (or indicate an
    error).

    It can be done by:

    - passing a socket descriptor,
    - passing name of the socket (it should be a Unix socket),
    - passing port number of the socket (should be a TCP socket bound to
      eg. localhost address).

    I think that passing a descriptor is the best option in this case,
    as it doesn't involve creating sockets that, in some circumstances,
    might be accessible to other processes.

Feedback greatly appreciated...

	MichaƂ

Mime
View raw message