Реклама: |
The job of your firewall is to prevent unwanted traffic getting to point B from point
A. We have general rules which say "as long as this packet is to port 23, it's
okay." We have general rules which say "as long as this packet has its FIN flag
set, it's okay." Our firewalls don't know the beginning, middle, or end of any
TCP/UDP/ICMP session. They merely have vague rules that are applied to all packets. We're
left to hope that the packet with its FIN flag set isn't really a FIN scan, mapping our
services. We hope that the packet to port 23 isn't an attempted hijack of our telnet
session. What if there was a way to identify and authorize individual TCP/UDP/ICMP
sessions and distinguish them from port scanners and DoS attacks? There is a way, it's
called keeping state.
We want convenience and security in one. Lots of people do, that's why Ciscos have an
"established" clause that lets established tcp sessions go through. Ipfw has
established. Ipfwadm has setup/established. They all have this feature, but the name is
very misleading. When we first saw it, we thought it meant our packet filter was keeping
track of what was going on, that it knew if a connection was really established or not.
The fact is, they're all taking the packet's word for it from a part of the packet anybody
can lie about. They read the TCP packet's flags section and there's the reason UDP/ICMP
don't work with it, they have no such thing. Anybody who can create a packet with bogus
flags can get by a firewall with this setup.
Where does IPF come in to play here, you ask? Well, unlike the other firewalls, IPF
really can keep track of whether or not a connection is established. And it'll do it with
TCP, UDP and ICMP, not just TCP. Ipf calls it keeping state. The keyword for the ruleset
is keep state.
Up until now, we've told you that packets come in, then the ruleset gets checked;
packets go out, then the ruleset gets checked. Actually, what happens is packets come in,
the state table gets checked, then *maybe* the inbound ruleset gets checked; packets go
out, the state table gets checked, then *maybe* the outbound ruleset gets checked. The
state table is a list of TCP/UDP/ICMP sessions that are unquestionadely passed through the
firewall, circumventing the entire ruleset. Sound like a serious security hole? Hang on,
it's the best thing that ever happened to your firewall.
All TCP/IP sessions have a start, a middle, and an end (even though they're sometimes
all in the same packet). You can't have an end without a middle and you can't have a
middle without a start. This means that all you really need to filter on is the beginning
of a TCP/UDP/ICMP session. If the beginning of the session is allowed by your firewall
rules, you really want the middle and end to be allowed too (lest your IP stack should
overflow and your machines become useless). Keeping state allows you to ignore the middle
and end and simply focus on blocking/passing new sessions. If the new session is passed,
all its subsequent packets will be allowed through. If it's blocked, none of its
subsequent packets will be allowed through. Here's an example for running an ssh server
(and nothing but an ssh server):
block out quick on tun0 all
pass in quick on tun0 proto tcp from any to 20.20.20.1/32 port = 22 keep state
The first thing you might notice is that there's no "pass out" provision.
In fact, there's only an all-inclusive "block out" rule. Despite this, the
ruleset is complete. This is because by keeping state, the entire ruleset is circumvented.
Once the first SYN packet hits the ssh server, state is created and the remainder of the
ssh session is allowed to take place without interference from the firewall. Here's
another example:
block in quick on tun0 all
pass out quick on tun0 proto tcp from 20.20.20.1/42 to any keep state
In this case, the server is running no services. Infact, it's not a server, it's a
client. And this client doesn't want unauthorized packets entering its IP stack at all.
However, the client wants full access to the internet and the reply packets that such
privledge entails. This simple ruleset creates state entries for every new outgoing TCP
session. Again, since a state entry is created, these new TCP sessions are free to talk
back and forth as they please without the hinderance or inspection of the firewall
ruleset. We mentioned that this also works for UDP and ICMP:
block in quick on tun0 all
pass out quick on tun0 proto tcp from 20.20.20.1/42 to any keep state
pass out quick on tun0 proto udp from 20.20.20.1/42 to any keep state
pass out quick on tun0 proto icmp from 20.20.20.1/42 to any keep state
Yes Virginia, we can ping. Now we're keeping state on TCP, UDP, ICMP. Now we can make
outgoing connections as though there's no firewall at all, yet would-be attackers can't
get back in. This is very handy because there's no need to track down what ports we're
listening to, only the ports we want people to be able to get to.
State is pretty handy, but it's also a bit tricky. You can shoot yourself in the foot
in strange and mysterious ways. Consider the following ruleset:
pass in quick on tun0 proto tcp from any to 20.20.20.1/32 port = 23
pass out quick on tun0 proto tcp from any to any keep state
block in quick all
block out quick all
At first glance, this seems to be a good setup. We allow incoming sessions to port
23, and outgoing sessions anywhere. Naturally packets going to port 23 will have reply
packets, but the ruleset is setup in such a way that the pass out rule will generate a
state entry and everything will work perfectly. At least, you'd think so.
The unfortunate truth is that after 60 seconds of idle time the state entry will be
closed (as opposed to the normal 5 days). This is because the state tracker never saw the
original SYN packet destined to port 23, it only saw the SYN ACK. IPF is very good about
following TCP sessions from start to finish, but it's not very good about coming into the
middle of a connection, so rewrite the rule to look like this:
pass in quick on tun0 proto tcp from any to 20.20.20.1/32 port = 23 keep state
pass out quick on tun0 proto tcp from any to any keep state
block in quick all
block out quick all
The additional of this rule will enter the very first packet into the state table and
everything will work as expected. Once the 3-way handshake has been witness by the state
engine, it is marked in 4/4 mode, which means it's setup for long-term data exchange until
such time as the connection is torn down (wherein the mode changes again. You can see the
current modes of your state table with ipfstat -s.