Solaris tip of the week: Global zone as firewall
As I mentioned in an earlier post, I think every developer should have a private development environment to match his/her production architecture. In general that means that I need a firewall that exposes a limited set of services provided by set of web, application, and database server tiers. Using Solaris zones to model a production architecture, the Global zone can be configured to act as firewall, and the Local zones represent your service hosts.
A firewall is commonly configured with two ip addresses - one external facing (the public ip address), and one internal facing (on a private subnet). To configure a Solaris global zone as a firewall, first select a private subnet for the environment.
Example:
Global Zone public IP: 192.168.1.200
Global Zone private IP: 172.0.1.1
Create local zones web01, dbs01 on IPs 172.0.1.2 and 172.0.1.3
My /etc/hosts file:
192.9.168.200 global
# 172.0.1.1 is a logical ip address configured in the global zone
172.0.1.1 frw01
172.0.1.2 web01
172.0.1.3 dbs01
Our next step is to configure ipnat (network address translation) to expose the services of web and dbs hosts to the outside world through the global zone.
Our ipnat configuration has to handle two types of
requests:
Inbound - an external host connects to the global zone to
contact an exposed service.
- and -
Outbound - an internal host wants to
access a service on the public network (for example, contacting an
external dns server).
We can get by with two ipnat rules - rdr and map. The rdr rule is used to redirect a packet to a new destination, and the map rule is used to rewrite the source address of the packet.
Example 1: Forward inbound port 80 request to web01:80
In
this case we use a rdr rule to forward the request to the web01 host,
and a map rule to rewrite the source address so that
when the packet reaches the local zone, it looks like the packet came from the
global zone private address rather than some public ip
address that the
local zone cannot resolve.
rdr bge0 192.168.1.200/32 port 80 -> 172.0.1.2 port 80 tcp
map bge0 from any to 172.0.1.2 port = 80 -> 172.0.1.1/32
Note: In these examples, my default physical interface is bge0 - substitute your default interface in place of bge0 if it is different. You can use the output of 'ifconfig -a' to identify your default interface.
Example 2: Add outbound rule so all local zones can access an external dns server
The
only rule required here is the map rule, this time we want to rewrite
the outbound private address of any local zone to the public address of
the global zone, so that when the packet reaches the dns server, the
dns server can return the response data.
map bge0 from 172.0.1.0/24 to 4.2.2.1/32 port = 53 -> 192.168.1.200/32 portmap tcp/udp 1025:65000
Here
any client on the private subnet (172.0.1.0) will have it's source
address re-written to the public address of the global zone
(192.168.1.200) when the packet is sent out. In addition, the portmap
parameter instructs ipnat to rewrite the source port. You will always
use the portmap parameter when rewriting a whole subnet (172.0.1.0) to
resolve potential source port conflicts. Consider the case where web01
and dbs01 both send a dns request with source port 10000. If the ipnat
is not instructed to manage source ports, there would be no way to tell
which host should get the response from the dns server.
One extra step is required to enable outbound access to your private subnet zones. Since the local zones are on an isolated private subnet, they need a default route. Two possibilities are:
1. run routed in the global zone:
# routeadm -e ipv4-routing -e ipv4-forwarding -u
2. Instruct the local zones to use the global zone's default router. (This is my preference).
From the global zone:
# route add net $globalsubnet/${netmask} ${addr} -interface
globalsubnet = 192.168.1.200
netmask = 24
addr = ip address of local zone host
To enable an external default router on web01:
# route add 192.168.1.0/24 172.0.1.2 -interface
Finally, publish the mac address of the global zone's default router so the local zones can find it:
# ip=`netstat -rn | grep default | head -1 | awk '{print $2}'`
# ping ${ip} > /dev/null
# ether=`arp -an | grep $ip | awk '{print \$NF}'`
#arp -s $ip $ether
Hope this is useful - Enjoy!
Oops, left off one bit of information - how to start the ipnat service ...
1. Add your rules to the file /etc/ipf/ipnat.conf
2. enable ipfilter service with: "svcadm enable ipfilter"
Posted by jay on July 18, 2008 at 11:47 AM EDT #