Pound - reverse-proxy and load-balancer

The Pound program is a reverse proxy, load balancer and
HTTPS front-end for Web server(s). Pound was developped
to enable distributing the load among several Web-servers
and to allow for a convenient SSL wrapper for those Web
servers that do not offer it natively. Pound is distributed
under the GPL - no warranty, it's free to use, copy and
give away.


What Pound is:

(1) a reverse-proxy: it passes requests from client
    browsers to one or more back-end servers.
(2) a load balancer: it will distribute the requests from
    the client browsers among several back-end servers,
    while keeping session information.
(3) an SSL wrapper: Pound will decrypt HTTPS requests
    from client browsers and pass them as plain HTTP
    to the back-end browsers.
(4) an HTTP/HTTPS sanitizer: Pound will verify requests
    for correctness and accept only well-formed ones.
(5) a failover-server: should a back-end server fail,
    Pound will take note of the fact and stop passing
    requests to it until it recovers.
(6) a request redirector: requests may be distributed
    among srevers according to the requested URL.

Pound is a very small program, easily audited for security
problems. It can run as setuid/setgid and/or in a chroot
jail. Pound does not access the hard-disk at all (except
for reading the certificate file on start, if required)
and should thus pose no security threat to any machine.


What Pound is not:

(1) Pound is not a Web server: by itself, Pound serves no
    content - it contacts the back-end server(s) for that
    purpose.
(2) Pound is not a Web accelerator: no caching is done -
    every request is passed "as is" to a back-end server.


Installation

(1) Pound was tested on Linux, Solaris and OpenBSD, but
    it should work unchanged on just about any modern
    Unix-like system. You will require OpenSSL and
    libpthread.
(2) Download the pound.tgz file and unpack it. Check that
    the Makefile is correct and possibly adjust flags as
    needed on your system. Compile:

        make

    If it works, you may want to do some testing before
    installing.
(3) Install the executable somewhere (it's likely that
    /usr/local/sbin would make a good choice), as well
    as the manual page (pound.8 -> /usr/local/man/man8).
    The supplied Makefile will do it for you.
(4) Make sure Pound gets started on boot. Read the man
    page for available options and examples.


Copyright

Pound is copyrighted by Apsis GmbH and is distributed under
the terms of the GNU Public License. Basically, this means
that you can use it free of charge, copy it, distribute it
(provided the copyright is maintained and the full package
is distributed), modify it, or line a bird-cage with it.

We would be happy to hear from you if you use it and
suggestions and improvements are gladly accepted.


Contact

Robert Segall - roseg@apsis.ch

Apsis GmbH
P.O.Box
8707 Uetikon am See
Switzerland
+41-1-920 4904
http://www.apsis.ch


Zope

A special note for Zope users: the original intent on
developing Pound was to allow distributing the load
among several Zope servers running on top of ZEO. This
it does.

A special problem arises when you try using Pound as an
SSL wrapper: Zope assumes that the requests are made via
HTTP and insists on prepending 'http://' to the (correct)
address in the replies, including in the <base> tag and
the absolute URLs it generates (for images for example).
This is clearly an undesirable behaviour.

In order to address this issue, a modified z2.py (as well
as a patch) is included in the distribution. The main
difference is that this z2.py allows starting an additional
HTTP server via the -y flag that sets the environment
HTTPS variable - thus correcting the problem. That means
that in order to use Pound as an SSL wrapper you need to:

- start Zope (modify the 'start' file) as
    python -X -w 8080 -y 8443 ...
- start _two_ copies of Pound on the front-end: one listens
  on port 80 and passes requests to port 8080, the second
  listens on port 443 and passes requests to port 8443.


HTTPS Certificates

If a client browser connects via HTTPS and if it presents a
certificate and if HTTPSHeaders is set, Pound will obtain the
certificate data and add the following HTTP headers to the
request it makes to the server:

- X-SSL-Subject: information about the certificate owner
- X-SSL-Issuer: information about the certificate issuer (CA)
- X-SSL-notBefore: begin validity date for the certificate
- X-SSL-notAfter: end validity date for the certificate

It is the application's responsability to actually use these
headers - Pound just passes this information without checking
it in any way (except for signature and encryption correctness).


Similar Systems

Quite a few people asked "What is wrong with Apache/Squid/
stunnel/your_favourite? Do we really need another proxy
system?". The simple answer is that there is nothing wrong -
they are all excellent systems that do their jobs very well.
The reasoning behind Pound is however slightly diferent:

- In my experience, a load-balancer may easily become a
bottle-neck in itself. If you have a heavily loaded site,
there are few things more depressing than seeing your
"load-balancer" slow down the entire network. This means that
the load-balancer should be kept as light-weight as possible.

- Security: auditing a large system for security issues is a
major undertaking for anybody (ask Bill Gates about it). This
implies that in order to avoid introducing new vulnerabilities
into a system (after all, your installation is only as secure
as its weakest component) the proxy/load-balancer should be
kept as small as possible.

- Protection: I assume Pound will be the only component exposed
to the Internet - your back-end servers will run in a protected
network behind it. This means that Pound should filter requests
and make sure only valid, correctly formed ones are passed to the
back-end servers, thus protecting them from malicious clients.

Taking these criteria into consideration, it is easy to see why
the other systems mentioned above do not fit:

- Apache (with mod_proxy and mod_backhand): great system, but very
large. Imposes a significant load on the system, complex set-up
procedure (and it is so easy to get it wrong: check how many Apache
servers allow proxying from and to external hosts). While Apache
has proven remarkably exploit free, I wouldn't wish to go into a
security audit for the tens of thousands of lines of code involved,
not to mention all the additional modules.

- Squid: great caching proxy, but even should load-balancing
features become available in the future, do you really need
caching on the load-balancer? After all, Pound can easily run on a
diskless system, whereas with Squid you'd better prepare a high
throughput RAID. Squid is still perfectly usable as a caching
proxy between Pound and the actual Web server, should it lack
its own cache (which Zope happily has).

- stunnel: probably comes closest to my understanding of software
design (does one job only and does it very well). However, it
lacks the load balancing and HTTP filtering features that I
considered necessary. Using stunnel in front of Pound (for HTTPS)
would have made sense, except that integrating HTTPS into Pound
proved to be so simple that it was not worth the trouble.

- your favourite system: let me know how it looks in light of the
above criteria - I am always interested in new ideas.


Dedicated Servers

Some people asked about the possibility of dedicating specific
back-end servers to some clients - in other words, if a request
originates from a certain IP address or group of addresses then
it should be sent to a specific group of back-end servers.

Given the ease with which IP addresses can be forged I am personally
doubtful of the utility of such a feature. Even should you think it
desirable, it is probably best implemented via the packet filter,
rather than a proxy server. Assuming that requests from x.com are
to go to s1.local, requests from y.com to s2.local and everything else
to s3.local and s4.local, here is how to do it:

- make sure your firewall blocks requests to port 8080, 8081 and 8082
- start three instances of Pound:
  (1) listens on port 8080, passes requests to s1.local port 80
  (2) listens on port 8081, passes requests to s2.local port 80
  (3) listens on port 8082, passes requests to s3.local port 80 and
      s4.local port 80
- have your packet filter redirect requests to the right local ports
  based on the origin address. In OpenBSD pf syntax this would be
  something like:
    rdr on rl0 from x.com to myhost.com port 80 -> localhost port 8080
    rdr on rl0 from y.com to myhost.com port 80 -> localhost port 8081
    rdr on rl0 from any to myhost.com port 80 -> localhost port 8082
  or in Linux iptables:
    iptables -t nat -A PREROUTING -p tcp -s x.com --dport 80 -i eth0 -j DNAT --to 127.0.0.1:8080
    iptables -t nat -A PREROUTING -p tcp -s y.com --dport 80 -i eth0 -j DNAT --to 127.0.0.1:8081
    iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 127.0.0.1:8082

This would give you the desired effect and probably better performance
than a purely proxy-based solution (though the performance improvement
is debatable, at least on Linux).


Acknowledgements

Ken Lalonde (ken@torus.ca) contributed very useful remarks and
suggestions, as well as correcting a few code errors.

Frank Denis (j@pureftpd.org) contributed a few excelent code
patches and some good ideas.

Gurkan Sengun (gurkan@linuks.mine.nu) tested Pound on Solaris,
contributed the Solaris cc flags and makes a Solaris 7 pre-compiled
version available on his Web-site (www.linuks.mine.nu)

All the others who tested Pound and told me about their results.
