Paranoid Penguin - Application Proxying with Zorp, Part II
Listing 4. The Filter Table's FORWARD and OUTPUT Chains
iptables -P FORWARD DROP iptables -A FORWARD -j LOG \ --log-prefix "FORWARD DROP: " iptables -A FORWARD -j DROP iptables -P OUTPUT ACCEPT
Finally, we come to actual Zorp configuration files. These are stored in /etc/zorp, and the first one we tackle is instances.conf, which defines and controls Zorp's instances. Usually, the rule of thumb is to define one instance per network zone, so in our example scenario we have, you guessed it, one instance each for our red, purple and blue zones. Listing 5 shows what such an instances.conf file would look like.
Listing 5. instances.conf
blue -v3 -p /etc/zorp/policy.py \ --autobind-ip 18.104.22.168 purple -v3 -p /etc/zorp/policy.py \ --autobind-ip 22.214.171.124 red -v3 -p /etc/zorp/policy.py \ --autobind-ip 126.96.36.199
The first field in each line is the name of the instance. This is user-definable, but we need to refer to it verbatim in the Zorp configuration file proper, policy.py. Speaking of which, you may use separate configuration files for each instance if you wish, or you may configure multiple zones within a single file. Regardless, the -p option in instances.conf tells Zorp which file to use for each instance.
The -v parameter sets log message verbosity: 3 is the medium setting, and 5 is useful for debugging. This parameter controls only Zorp-generated log messages and has no effect whatsoever on Netfilter/iptables logging. Finally, each line ends with an --autobind-ip setting that determines to which dummy IP Zorp should bind TPROXY when proxying connections. This IP address can and should be shared between all instances. This address, obviously, should be the one you set earlier (see Configuring a Dummy Interface, above).
Your iptables script determines how packets get routed to proxies, and /etc/zorp/instances.conf determines how Zorp starts up. But to tell Zorp's proxies how to behave, you need to set up /etc/zorp/policy.py, or whatever you called the configuration file(s) referenced in instances.conf—policy.py is conventional but not mandatory. This policy file contains two parts. The first part is a global section in which zones are defined based on network addresses and allowed services. The second part is a service-instance definition section in which each instance listed in instances.conf is defined based on which services originate in each and in which those services are mapped to application proxies.
Listing 6 shows a complete global section from our example policy.py. It begins with some import sections, in which essential Python functions are included. Next come our zone definitions. If you set up instances.conf to run one Zorp instance per zone, your zone names here can be similar to or even the same as your instance names. In Listing 6 I've chosen different names in order to illustrate that technically, zone names are distinct from instance names.
Listing 6. policy.py, Part I (Global Settings)
from Zorp.Core import * from Zorp.Plug import * from Zorp.Http import * InetZone("bluezone", "10.0.1.0/24", outbound_services=["blue_http", "blue_ssh"], InetZone("purplezone", "192.168.1.0/24", inbound_services=["blue_http", "blue_ssh", "red_http"]) InetZone("redzone", "0.0.0.0/0", outbound_services=["red_http"], inbound_services=["*"]) InetZone("localzone", "127.0.0.0/8", inbound_services=["*"]) # end global section
In each zone definition, you can see a network address that corresponds to those in Figure 1 and specifications of which services are allowed. These service names are user-definable and fleshed out in the subsequent service-instance definitions. The important thing to understand about these statements is that inbound and outbound is relative to the zone/network, not to the firewall.
Figure 2 shows what the internal-to-Internet HTTP data flow looks like as a proxied connection. In this illustration, we see this data flow exists both as an outbound connection out of the Internal (blue) zone and an inbound connection to the Internet (red) zone. This is borne out in the respective bluezone and redzone definitions in Listing 6. It's also important to use the same service name in both zone definitions that a given data flow traverses (blue_http in the case of Figure 2 and Listing 6).
The last point to make about Listing 6 is the * wild card signifies all defined services. This is narrower than it might seem; * includes only those services defined in policy.py's service-instance definitions, not all possible services. Remember, Zorp processes only those packets that Netfilter and TPROXY send to it. If a given zone is to allow no outbound or inbound services, the inbound_services or outbound_services parameter may be either omitted or set to  (empty brackets).
Listing 7 shows our policy.py file's service-instance definitions. The first line of each definition must reference an instance name specified in instances.conf, and the following lines in the definition must be indented because these rules are processed by Python, which is precise about indentation. The definition can't be empty: if no services originate in a given instance, the token pass may be used, as with the purple() instance definition in Listing 7.