Using NAT

One of the critical functions of a firewall is NAT. Use NAT to provide IPv4 network access to multiple machines but show only one public IPv4 address. Some companies provide Internet access to thousands and thousands of machines via NAT.

NAT is like making soup out of a bone—it stretches what you have so that it covers more. Some protocols won’t work well with NAT. It really confuses anyone who is trying to restrict access by IP address. And it can cause nightmares for network forensics and troubleshooters. But NAT is the chosen solution for the IPv4 address shortage.

NAT is not intended as a security mechanism. There are minor security benefits, but they are inadequate against today’s network threats. Relying on NAT for security is chasing 10 boilermakers with a cup of black coffee before staggering out of the pub to drive home. You might get away with it, but only by luck.

IPv6 was designed without NAT, but it was shoehorned in several years later by popular demand. (IPv4 was originally designed without NAT as well, so IPv6 is just following tradition.) Note that an IPv6 address—even a globally unique IPv6 address—does not mean or even imply “reachable from the world.” You can have solid network separation without NAT. Avoiding NAT means using your packet filter to protect your machines, with additional application proxies as needed.

Private NAT Addresses

In theory, you can use any addresses behind your NAT device. If you use some random IP addresses, though, you cannot exchange packets with whoever uses those IP addresses out in the real world. It’s highly advisable to use some of the IP addresses reserved for private use, generally referred to as “RFC 1918 addresses.” These include the following IP addresses:

  • 10.0.0.0/8 (10.0.0.0-10.255.255.255)

  • 172.16.0.0/12 (172.16.0.0-172.31.255.255)

  • 192.168.0.0/16 (192.168.0.0-192.168.255.255)

You can subnet and rearrange those addresses any way you like, as long as you don’t try to route them on the public Internet.

You can use other IP addresses behind your NAT if you have a really good reason for doing so. For example, RFC 5737 defines IPv4 addresses for use in documentation. Like RFC 1918 addresses, RFC 5737 addresses should never appear on the public Internet. I write documentation, so I use those addresses on my home and test networks. It saves me from doing search and replace as I write books.[48] There’s still no chance of those addresses appearing on other networks.

Configuring NAT

Perhaps the most common form of NAT is for use in hiding a small network behind a single IP address. You’ll find this in many homes and small businesses. Very few home offices have internal routing and multiple subnets. For this example, I have two interface groups: the Internet-facing egress group and the lan group attached to my office.

pass out on egress from 1lan:network to any 2nat-to egress

The first part of this rule looks just like any other firewall rule permitting the addresses on the lan interface access to everywhere, but the last two words additionally configure NAT. The nat-to keyword tells PF to translate addresses 2. The egress that follows tells PF to hide the internal addresses behind the addresses of the egress interfaces 1. You could use an interface name or a specific IP address here, but if you do, you must change your filter rules when you change your server.

In order to have PF recognize IP address changes from DHCP, put the interface group name in parentheses.

pass out on egress from lan:network to any nat-to (egress)

Now load your firewall rules, enable IP forwarding, and suddenly, hosts on your LAN will have access to the Internet through the firewall’s public address.

How NAT Works

The easiest way to understand how address translation works is to look at the state table (discussed in the previous chapter) after PF passes translated packets back and forth. On the office network from machine 192.0.2.2, I ran this command:

$ ping www.michaelwlucas.com

Several pings later, I checked the state table and found entries like this:

# pfctl -ss | grep 192.0.2.2
all udp 1203.0.113.5:55797 2(192.0.2.2:10853) -> 3203.0.113.15:53    MULTIPLE:SINGLE
all icmp 203.0.113.5:8813 (192.0.2.2:41584) -> 198.22.63.8:8       0:0

The first state represents a UDP connection from the firewall’s public address 1 to the local DNS server 3. This state entry includes the client’s private IP address 2, as well as the actual ports used by the client, the firewall, and the DNS server.

The client initiated this state by sending a request from port 10853 on its IP address to port 53 on the DNS server. When the packet passed through PF, OpenBSD rewrote the packet so that it appeared to come from the address 203.0.113.5 on port 55797 and sent it on to the DNS server. The DNS server sent its response to the firewall’s public IP on port 55797. When the reply arrived, the firewall checked the state table, and found that UDP packets on port 55797 were part of the state for the client. PF rewrote the packet’s destination address and forwarded it to the client.

The second state represents an ICMP connection. The state table encodes the various ICMP codes used for a ping request as port numbers, and forwards responses back to the client based on that information. Otherwise, it’s very similar to the DNS example above it.

In other words, NAT works by lying. PF lies to the client, telling it that it has direct access to the public Internet. It lies to the external servers, giving a false source address and port for client connections. PF uses the state table to track its lies and keep everything consistent. These lies are convenient for IPv4 address conservation, but they’re exactly why address translation complicates troubleshooting and intrusion forensics.

Now that you understand the basics of NAT, let’s tell the network even more complicated and interesting lies.

Multiple or Specific Public Addresses

You can use several public IP addresses for address translation. If you use an interface group for the external address in your NAT rule, any addresses in that interface group can become the public address of any connection. If you want to be specific, list particular addresses.

pass out on egress from lan:network to any 1nat-to 203.0.113.5

I use this configuration when my firewall’s external interface has multiple IP addresses and I want to conceal my desktop clients behind a single address (although I probably would define and use a macro for the external address 1).

But how many public addresses do you need? The answer depends on your clients.

Port numbers range from 0 to 65535. The bottom 1024 ports are generally used for services on the localhost. Not all of those ports will be used on the localhost, but a packet filter generally won’t use those ports for translated connections. I’m lazy, so I’ll round off to 64,000 free ports.

Even the most heavily loaded desktop client rarely can use as many as 100 outbound connections simultaneously. Most will use far fewer, but again, I’m lazy, and I want a worst-case scenario, so I’ll call it 100.

One IP address can support 64,000 / 100 = 640 machines being pathological simultaneously. Realistically, each client might have 10 simultaneous outbound connections, so a public address could support 6,400 simultaneous clients. How many of your users browse the Internet at the same time? The answer probably is not many. And if you have thousands of users, you would probably benefit from implementing a caching proxy, which would greatly reduce the number of connections.

If you’re concerned about overflowing the number of client machines for one address, watch your state table. Until you have multiple tens of thousands of states for one public IP address, don’t worry.

Specifying individual addresses in a NAT rule is most useful for bidirectional NAT.

Bidirectional NAT

Some applications work better if you dedicate a public IP address as the NAT address for a specific private IP address. For example, if you have a server that offers several different services on different ports, and you want to put it behind your firewall, you might want to dedicate a single address to it. This is called bidirectional, one-to-one, or static NAT. OpenBSD docs use “bidirectional,” but the terms all mean the same thing.

Configure bidirectional NAT with the binat-to keyword.

pass on lan from 192.0.2.65 to any binat-to 203.0.113.6

PF dedicates the public IP address 203.0.113.6 for NAT services for the private IP address 192.0.2.65.

If you use bidirectional NAT, be sure to specify a specific IP address for your general NAT and consider using the following NAT rules:

pass out log on egress from lan:network to any nat-to egress
pass on lan from 192.0.2.2 to any binat-to 203.0.113.6

The IP addresses on this LAN are hidden behind the IP addresses on the egress interface. If 203.0.113.6 is an address on an egress interface, outbound packets from the LAN might use it as a source address.

When I need bidirectional NAT, I usually write my NAT rules like this:

mainnat="203.0.113.5"
servernat="203.0.113.6"
pass out log on egress from lan:network to any nat-to $mainnat
pass on lan from 192.0.2.2 to any binat-to $servernat

In this way, packets leaving my network are unambiguously translated. Only the one specific server uses the IP address 203.0.113.6; all other hosts on my local network use 203.0.113.5. If I change IP addresses, I must reconfigure pf.conf, but that’s a minor annoyance compared to troubleshooting network ambiguity.

Bidirectional NAT and Security

The use of bidirectional NAT, and allowing the redirection of connections, lets you give people outside your network access to servers behind your firewall, and every one of these gaps is a potential security hole. If you allow the world access to your web servers, and an intruder compromises one of your servers, you have a compromised machine inside your firewall. The firewall doesn’t really secure the web servers; it just controls who can try to break into them and limits the available attack vectors.

Packet Filtering, Bidirectional NAT, and Rule Order

When writing packet-filtering rules for bidirectional NAT, the order in which you list rules is important. Consider the following rules:

pass on lan from 192.0.2.2 to any binat-to 203.0.113.6
pass in on egress proto tcp from any to 192.0.2.2 port 80

The first rule establishes static NAT for the host 192.0.2.2 on the LAN, hiding it behind the public IP address 203.0.113.6. All is well and good. The second line permits connections to port 80 on the same host, or does it? Packets meant for this server that arrive on the firewall’s egress interface won’t be addressed to 192.0.2.2; they’ll be addressed to the public NAT address, or 203.0.113.6. They won’t match this rule, so they are discarded.

In order to permit connections from the world to the web server behind this firewall, permit packets sent to the proper port on the public address.

pass on lan from 192.0.2.2 to any binat-to 203.0.113.6
pass in on egress proto tcp from any to 203.0.113.6 port 80

This translates 192.0.2.2 to the public address 203.0.113.6, and then allows packets with a destination of port 80 on 203.0.113.6 to pass. You’ll see this in the state table, like this:

all tcp 203.0.113.6:80 <- 198.22.63.8:64791       ESTABLISHED:ESTABLISHED

The host 198.22.63.8 has connected to the server’s public IP address on port 80.

Why doesn’t this state entry have the hidden IP address in it? Because this is a bidirectional NAT. PF can send port numbers through unaltered, so it can track a little less information in the state table.

The tricky thing here is that the rule order impacts how you filter, and you must read your filtering rules carefully to see how address translation interacts with packet filtering. I always write my rules so that I do address translation before I filter. I consistently use the public IP address in the filter rules, but sometimes that’s not practical. PF lets you write arbitrarily complex rules mainly because the real world is arbitrarily complex. If you have trouble passing traffic through NAT, read your rules very carefully.

To see a bidirectional NAT, look at the loaded rules.

# pfctl -sr
…
pass out on lan inet from 192.0.2.2 to any flags S/SA nat-to 203.0.113.6 static-port
pass in on lan inet from any to 203.0.113.6 flags S/SA rdr-to 192.0.2.2
pass on egress inet proto tcp from any to 203.0.113.6 port = 80 flags S/SA

The first rule gives the private IP address access to the public Internet, translated to the specific IP address. The third rule passes traffic to the translated address.

But what about the second rule, with that rdr-to stuff? That’s a redirection, which is how PF implements static NAT.

Redirection

Bidirectional NAT is actually a combination of address translation and redirection; in other words, it twists a connection intended for one IP or port to another. In bidirectional NAT, all connections to the designated public IP address are redirected to a different IP address. Sometimes you don’t want to twist all traffic for an IP address—only a few ports. Sometimes you want to redirect one port one way, but a different port elsewhere. Do this with redirection rules.

Suppose you have one public IP address: 203.0.113.5. You want port 80 on that IP address routed to your web server at 192.0.2.2, ports 25 and 110 to your mail server at 192.0.2.3, and port 443 to your e-commerce server at 192.0.2.4. PF lets you choose where to send each port via redirection by using a standard packet-filtering rule and adding the rdr-to redirection keyword.

pass in on egress proto tcp from any to egress port 80 rdr-to 192.0.2.2
pass in on egress proto tcp from any to egress port {25, 110} rdr-to 192.0.2.3
pass in on egress proto tcp from any to egress port 443 rdr-to 192.0.2.4

These rules declare that any connection coming to the egress interface group (the interface facing the public Internet, with a default route going over it) can be redirected in three different ways. The first rule directs port 80 requests to one internal server. The second rule directs requests for ports 25 and 110 to the second server. The last rule redirects requests for port 443 to the third server. One public IP address is now providing services to the world from three different servers.

All port redirection rules must include a protocol, because specifying a TCP/IP port works only if you’re forwarding a protocol that includes port numbers, such as TCP or UDP. If you want to forward both TCP and UDP ports, you must specify both protocols. For example, DNS uses port 53 on both TCP and UDP. Here’s a rule that forwards both of these protocols’ port 53 to the internal server 192.0.2.5:

pass in on egress proto {tcp, udp} from any to egress port 53 rdr-to 192.0.2.5

Pick a port, say where you want it to go, and PF will redirect it as you please.

Note

You’ve learned how bidirectional NAT combines redirection and address translation. The in-kernel PF engine doesn’t actually know anything about this beastie called “bidirectional NAT.” pfctl(8) translates the binat rule into two separate rules: one for translation and one for redirection.

Multiple Addresses and Interface Groups

All of the preceding discussion makes sense when you have only one public IP address. But what happens when you have multiple addresses?

Remember that using an interface group in pf.conf tells pfctl to create a matching rule for every IP address in the interface group. Suppose you have three IP addresses on your egress interface: 203.0.113.5, 203.0.113.6, and 203.0.113.7. You write this pf.conf rule:

pass in on egress proto tcp from any to egress port 80 rdr-to 192.0.2.2

Load this rule into the kernel with pfctl, and what do you get?

# pfctl -sr
…
pass in on egress inet proto tcp from any to 203.0.113.5 port = 80 flags S/SA rdr-to 192.0.2.2
pass in on egress inet proto tcp from any to 203.0.113.6 port = 80 flags S/SA rdr-to 192.0.2.2
pass in on egress inet proto tcp from any to 203.0.113.7 port = 80 flags S/SA rdr-to 192.0.2.2

Any connection to port 80 on any of these IP addresses is directed to port 80 on the same server. This might be useful in some environments, but that’s not what most of us want. If you have multiple IP addresses, and you want to redirect a port on only one IP address, you must specify the interface name and the public IP address.

pass in on em0 proto tcp from any to 203.0.113.5 port 80 rdr-to 192.0.2.2

This doesn’t expand; it doesn’t have any interface groups, lists of addresses, variables, or macros. When pfctl parses this, it loads only one PF rule into the kernel.

Port Manipulation and Ranges

As you redirect ports from one machine to another, you can change the port. The following example takes requests to TCP port 2222 on the firewall and redirects them to port 22 on a machine inside the firewall.

pass in on egress proto tcp from any to egress port 2222 rdr-to 192.0.2.2 port 22

This is a reasonable way to offer SSH services to several machines inside the firewall on only one IP address, and to give each machine its own port.

If you have specific source addresses that you want to abuse, you can give them special port redirections by source IP address.

pass in on egress proto tcp from 198.51.100.0/24 to egress port 80 rdr-to 192.0.2.2
pass in on egress proto tcp from ! 198.51.100.0/24 to egress port 80 rdr-to 192.0.2.3

Every HTTP connection from the IP addresses in 198.51.100.0/24 will be redirected to one server, while every other connection will be directed elsewhere. (To redirect connections for many source addresses, use a table for the source address.)

PF can also redirect entire ranges of ports using the same logical operators used for filtering ports. One obvious thing to do is to redirect a range of ports to a single machine. NFS is a prime example, as it requires TCP port 111, as well as all TCP and UDP ports from 1024 to 65535.

pass in on egress proto {tcp, udp} from any to egress port {111, 1024:65535} rdr-to 192.0.2.15

Recall from Chapter 21 that a colon between port numbers indicates a range of ports. This rule passes ports 1024 through 65535, inclusive. Admittedly, certain NFS implementations can be restricted to use either TCP or UDP, and that’s a great big gaping hole in your packet filter. But NFS uses random high-numbered ports that come and go very quickly, and cannot be effectively filtered or restricted at the packet level.

You can also funnel an entire range of ports to one port on one machine.

pass in on egress proto tcp from any to egress port {1024:65535} rdr-to 192.0.2.15 port 80

I’ve used this to point random traffic at a web page that says “Go away. You cannot use this service.”

Transparent Interception

Traffic interception is similar to redirection in that PF intercepts traffic bound for one port and steers it to a port on the local machine. Traffic interception is one way to implement a transparent proxy. Use the divert-to keyword to tell PF to steer any matching packets to a local server.

pass in inet proto tcp from lan:network to any port 80 divert-to 127.0.0.1 port 3129

Any traffic from the local LAN to port 80 will be diverted to port 3129 on the firewall. Port 3129 is usually used by the Squid caching proxy (/usr/ports/www/squid). If you choose to implement a caching proxy like Squid, you’ll probably want to redirect several ports to the cache. (We’ll take a closer look at diverting connections in FTP and PF.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.78.237