[zorp] https reverse proxy

Balazs Scheidler zorp@lists.balabit.hu
Fri, 18 Apr 2003 10:14:33 +0200


On Wed, Apr 16, 2003 at 05:24:44PM -0700, Peter Hicks wrote:
> Hi,
> 
> I was wondering if someone could help me with setting up https
> proxying with zorp. I would like zorp to be able to proxy based on
> domain name, as I have a limited number of external IPs. I figured I
> could set up unlimited https servers on internalRFC1918 addresses and
> have zorp reverse proxy them.
> 
> https foo1.com(10.1.1.1) -> 192.168.1.5
> https foo2.com(10.1.1.1) -> 192.168.1.6
> https foo3.com(10.1.1.1) -> 192.168.1.7
> 
> Does this make sense?
> 
> I am still trying to get a handle on zorp's configuration files, so if
> you could include some examples, I would be very grateful.

yes, it perfectly does and it can be done without problems for HTTP. The
encrypted version has a problem though, you can only show a single server
certificate to clients as the certificate is show prior to the client
sending the server name it wants to communicate with.

An example for HTTP would be something like:

class ReverseHostHttpProxy(HttpProxy):
	host_mapping = { 'foo1.com': '192.168.1.5', 'foo2.com': '192.168.1.6' }
	default_ip = '192.168.1.5';

	def config(self):
		HttpProxy.config(self)
		self.request_headers["Host"] = (HTTP_HDR_POLICY, self.checkHostHeader)

	def checkHostHeader(self, name, value):
		# check the host header and set destination address
		# based on the header's value
		if self.host_mapping.has_key(value):
			self.session.server_address.ip_s = self.host_mapping[value]
		else:
			self.session.server_address.ip_s = self.default_ip
		# we don't need further hooks
		del self.request_headers["Host"]
		return HTTP_HDR_ACCEPT

And you could use this class with a service definition like this:

# instance definition
def reverse_http():
	Service("inter_HTTP_farm", ReverseHostHttpProxy,
		router=DirectedRouter(SockAddrInet('192.168.1.5', 80)))
	Listener(SockAddrInet('10.1.1.1', 80), "inter_HTTP_farm")

For SSL traffic you would have to decide whether you want encrypted
communication between the firewall and the server. If you trust the network
between your server and the firewall at least as much as you trust the
firewall you could transmit the traffic flow in cleartext, thus decreasing
the load of both your servers (because they have to do no encryption) and
the firewall (because it does not need to reencrypt traffic). Assuming
cleartext on the trusted site is OK, you would have to do something like
this:

class ReverseUnwrapSSL(PsslProxy):
	def config(self):
		PsslProxy.config(self)
		self.client_cert_file = '/etc/zorp/serverfarm.crt'
		self.client_key_file = '/etc/zorp/serverfarm.key'
		self.client_need_ssl = TRUE
		self.server_need_ssl = FALSE

def reverse_https():
	Service("inter_HTTPS_farm", ReverseUnwrapSSL,
		chainer=SideStackChainer(ReverseHostHttpProxy),
		router=DirectedRouter(SockAddrInet('192.168.1.5', 443)))
	Listener(SockAddrInet('10.1.1.1', 443), "inter_HTTPS_farm")

In this case we used a scenario when our proxies were stacked side-by-side,
e.g. the server side of the SSL proxy was the HTTP proxy, and its server
side was the server itself like this:

                +-----+    +------+
real client ----+ SSL +----+ HTTP +---- real server
                +-----+    +------+

This was needed because the HTTP was selecting its destination based on the
Host header. If the target address would have been based on other things
(like choose it transparently) we could have used the traditional stacking
scenario:

real client ----+       +---- real server
                |       |
                +-------+
                |  SSL  |
                +-+---+-+
                  |   |
                +-------+
                | HTTP  |
                +-------+

Of course neither SSL nor HTTP differentiates between connections to real
servers or other proxies. They are communicating with the abstract Stream
interface. A funnier scenario that represents the power of stacking even
more is when you have a non-transparent HTTP proxy, where clients request
CONNECT to cross the firewall:

real client ----+       +---- real server
                |       |
                +-------+
                | HTTP  |        non-transparent HTTP
                +-+---+-+
                  |   | 
                +-+---+-+
                |  SSL  |        SSL proxy started for the CONNECT flow
                +-+---+-+
                  |   |
                +-------+
                | HTTP  |        HTTP proxy for the decrypted traffic
                +-------+

-- 
Bazsi
PGP info: KeyID 9AF8D0A9 Fingerprint CD27 CFB0 802C 0944 9CFD 804E C82C 8EB1