Firewall Rules Creation Using ipchains Utility
Most
aspects of Linux are evolving to meet the increasing demands of its users; IP
firewall is no exception. The traditional IP firewall implementation is fine
for most applications, but can be clumsy and inefficient to configure for
complex environments. To solve this problem, a new method of configuring IP
firewall and related features was developed. This new method was called
"IP Firewall Chains" and was first released for general use in the
2.2.0 Linux kernel.
The
IP Firewall Chains support was developed by Paul Russell and Michael Neuling.
Paul has documented the IP Firewall Chains software in the IPCHAINS-HOWTO.
IP
Firewall Chains allows you to develop classes of firewall rules to which you
may then add and remove hosts or networks. An artifact of firewall rule
chaining is that it may improve firewall performance in configurations in which
there are lots of rules.
IP
Firewall Chains are supported by the 2.2 series kernels and are also available
as a patch against the 2.0.* kernels.
Using ipchains
There
are two ways you can use the ipchains utility. The first way is to make use of the ipfwadm-wrapper shell script, which is
mostly a drop-in replacement for ipfwadm that drives the ipchains program in the background. The second way to use ipchains is to learn its new
syntax and modify any existing configurations you have to use the new syntax
instead of the old. With some careful consideration, you may find you can
optimize your configuration as you convert. The ipchains syntax is easier to learn than the ipfwadm, so this is a good option.
The ipfwadm manipulated three
rulesets for the purpose of configuring firewalling. With IP Firewall Chains
you can create arbitrary numbers of rulesets, each linked to one another, but
there are three rule sets related to firewalling that are always present. The
standard rule sets are direct equivalents of those used with ipfwadm, except they have names: input, forward and output.
Let's
first look at the general syntax of the ipchains command, then we'll look at how we use ipchains instead of ipfwadm without worrying about
any of the advanced chaining features.
ipchains Command Syntax
The ipchains command syntax is
straightforward. We'll now look at the most important of those. The general
syntax of most ipchains commands is:
ipchains command rule-specification options
Commands
There
are a number of ways we can manipulate rules and rulesets with the ipchains command. Those relevant
to IP firewalling are:
-A chain
Append one or more rules to the end of the nominated chain. If a
hostname is supplied as either source or destination and it resolves to more
than one IP address, a rule will be added for each address.
-I chain rulenum
Insert one or more rules to the start of the nominated chain.
Again, if a hostname is supplied in the rule specification, a rule will be
added for each of the addresses it resolves to.
-D chain
Delete one or more rules from the specified chain that matches the
rule specification.
-D chain rulenum
Delete the rule residing at position rulenum in the specified chain. Rule positions start at one for the first
rule in the chain.
-R chain rulenum
Replace the rule residing at position rulenum in the specific chain with the supplied rule
specification.
-C chain
Check the datagram described by the rule specification against the
specific chain. This command will return a message describing how the datagram
was processed by the chain. This is very useful for testing your firewall
configuration, and we look at it in detail a little later.
-L [chain]
List the rules of the specified chain, or for all chains if no
chain is specified.
-F [chain]
Flush the rules of the specified chain, or for all chains if no
chain is specified.
-Z [chain]
Zero the datagram and byte counters for all rules of the specified
chain, or for all chains if no chain is specified.
-N chain
Create a new chain with the specified name. A chain of the same
name must not already exist. This is how user-defined chains are created.
-X [chain]
Delete the specified user-defined chain, or all user-defined
chains if no chain is specified. For this command to be successful, there must
be no references to the specified chain from any other rules chain.
-P chain policy
Set the default policy of the specified chain to the specified
policy. Valid firewalling policies are ACCEPT, DENY, REJECT, REDIR, or RETURN.ACCEPT, DENY, and REJECT have the same meanings
as those for the tradition IP firewall implementation. REDIR specifies that the
datagram should be transparently redirected to a port on the firewall host. The RETURN target causes the IP
firewall code to return to the Firewall Chain that called the one containing
this rule and continues starting at the rule after the calling rule.
Rule specification parameters
A
number of ipchains parameters create a rule specification by determining what types
of packets match. If any of these parameters is omitted from a rule
specification, its default is assumed:
-p [!]protocol
Specifies the protocol of the datagram that will match this rule.
Valid protocol names are tcp, udp, icmp, or all. You may also specify a protocol number here to
match other protocols. For example, you might use 4 to match the ipip encapsulation protocol. If the ! is supplied, the rule is
negated and the datagram will match any protocol other than the protocol
specified. If this parameter isn't supplied, it will default to all.
-s [!]address[/mask] [!]
[port]
Specifies the source address and port of the datagram that will
match this rule. The address may be supplied as a hostname, a network name, or
an IP address. The optional mask is the netmask to use and may be supplied either in the
traditional form (e.g., /255.255.255.0) or the modern form (e.g., /24). The
optional port specifies the TCP or UDP port, or the ICMP datagram type that will
match. You may supply a port specification only if you've supplied the -p parameter with one of the tcp, udp, or icmp protocols. Ports may be specified as a range by
specifying the upper and lower limits of the range with a colon as a delimiter.
For example, 20:25 described all of the ports numbered from 20 up to and including
25. Again, the ! character may be used to negate the values.
-d [!]address[/mask] [!]
[port]
Specifies the destination address and port of the datagram that
will match this rule. The coding of this parameter is the same as that of the -sparameter.
-j target
Specifies the action to take when this rule matches. You can think
of this parameter as meaning "jump to." Valid targets are ACCEPT, DENY,REJECT, REDIR, and RETURN. We described the
meanings of each of these targets earlier. However, you may also specify the
name of a user-defined chain where processing will continue. If this parameter
is omitted, no action is taken on matching rule datagrams at all other than to
update the datagram and byte counters.
-i [!]interface-name
Specifies the interface on which the datagram was received or is
to be transmitted. Again, the ! inverts the result of the match. If the interface name ends with +, then any interface that begins with the supplied string will
match. For example, -i ppp+ would match any PPP network device and -i ! eth+ would match all
interfaces except Ethernet devices.
[!] -f
Specifies that this rule applies to everything but the first
fragment of a fragmented datagram.
Options
The
following ipchains options are more general in nature. Some of them control rather
esoteric features of the IP chains software:
-b
Causes the command to generate two rules. One rule matches the
parameters supplied, and the other rule added matches the corresponding
parameters in the reverse direction.
-v
Causes ipchains to be verbose in its output. It will supply more information.
-n
Causes ipchains to display IP address and ports as numbers without attempting to
resolve them to their corresponding names.
-l
Enables kernel logging of matching datagrams. Any datagram that
matches the rule will be logged by the kernel using its printk() function, which is usually handled by the sysklogd program and written to a
log file. This is useful for making unusual datagrams visible.
-o[maxsize]
Causes the IP chains software to copy any datagrams matching the
rule to the userspace "netlink" device. The maxsize argument limits
the number of bytes from each datagram that are passed to the netlink device.
This option is of most use to software developers, but may be exploited by
software packages in the future.
-m markvalue
Causes matching datagrams to be marked with a value. Mark values are unsigned 32-bit
numbers. In existing implementations this does nothing, but at some point in
the future, it may determine how the datagram is handled by other software such
as the routing code. If a markvalue begins with a + or -, the value is added or
subtracted from the existing markvalue.
-t andmask xormask
Enables you to manipulate the "type of service" bits in
the IP header of any datagram that matches this rule. The type of service bits
are used by intelligent routers to prioritize datagrams before forwarding them.
The Linux routing software is capable of this sort prioritization. The andmaskand xormask represent bit masks that will be logically ANDed
and ORed with the type of service bits of the datagram respectively. This is an
advanced feature that is discussed in more detail in the IPCHAINS-HOWTO.
-x
Causes any numbers in the ipchains output to be expanded to their exact values with
no rounding.
-y
Causes the rule to match any TCP datagram with the SYN bit set and
the ACK and FIN bits clear. This is used to filter TCP connection requests.
Example
Let's
again suppose that we have a network in our organization and that we are using
a Linux-based firewall machine to allow our users access to WWW servers on the
Internet, but to allow no other traffic to be passed.
If
our network has a 24-bit network mask (class C) and has an address of
172.16.1.0, we'd use the following ipchains rules:
# ipchains -F forward
# ipchains -P forward DENY
# ipchains -A forward -s 0/0 80 -d 172.16.1.0/24 -p tcp -y -j DENY
# ipchains -A forward -s 172.16.1.0/24 -d 0/0 80 -p tcp -b -j ACCEPT
# ipchains -P forward DENY
# ipchains -A forward -s 0/0 80 -d 172.16.1.0/24 -p tcp -y -j DENY
# ipchains -A forward -s 172.16.1.0/24 -d 0/0 80 -p tcp -b -j ACCEPT
The
first of the commands flushes all of the rules from the forward rulesets and the second
set of commands sets the default policy of the forwardruleset to DENY. Finally, the third and fourth commands do the specific filtering
we want. The fourth command allows datagrams to and from web servers on the
outside of our network to pass, and the third prevents incoming TCP connections
with a source port of 80.
If
we now wanted to add rules that allowed passive mode only access to FTP servers
in the outside network, we'd add these rules:
# ipchains -A forward -s
0/0 20 -d 172.16.1.0/24 -p tcp -y -j DENY
# ipchains -A forward -s 172.16.1.0/24 -d 0/0 20 -p tcp -b -j ACCEPT
# ipchains -A forward -s 0/0 21 -d 172.16.1.0/24 -p tcp -y -j DENY
# ipchains -A forward -s 172.16.1.0/24 -d 0/0 21 -p tcp -b -j ACCEPT
# ipchains -A forward -s 172.16.1.0/24 -d 0/0 20 -p tcp -b -j ACCEPT
# ipchains -A forward -s 0/0 21 -d 172.16.1.0/24 -p tcp -y -j DENY
# ipchains -A forward -s 172.16.1.0/24 -d 0/0 21 -p tcp -b -j ACCEPT
Listing Our Rules with ipchains
To
list our rules with ipchains, we use its -L argument. Just as with ipfwadm, there are arguments
that control the amount of detail in the output. In its simplest form, ipchains produces output that
looks like:
# ipchains -L -n
Chain input (policy ACCEPT):
Chain input (policy ACCEPT):
Chain forward (policy
DENY):
target prot opt source destination ports
DENY tcp
-y---- 0.0.0.0/0 172.16.1.0/24 80 -> *
ACCEPT tcp
------ 172.16.1.0/24 0.0.0.0/0 * -> 80
ACCEPT tcp
------ 0.0.0.0/0 172.16.1.0/24 80 -> *
ACCEPT tcp
------ 172.16.1.0/24 0.0.0.0/0 * -> 20
ACCEPT tcp
------ 0.0.0.0/0 172.16.1.0/24 20 -> *
ACCEPT tcp
------ 172.16.1.0/24 0.0.0.0/0 * -> 21
ACCEPT tcp
------ 0.0.0.0/0 172.16.1.0/24 21 -> *
Chain output (policy
ACCEPT):
If
you don't supply the name of a chain to list, ipchains will list all rules in all chains. The -n argument in our example
tells ipchains not to attempt to convert any address or ports into names. The
information presented should be self-explanatory.
A
verbose form, invoked by the -u option, provides much
more detail. Its output adds fields for the datagram and byte counters, Type of
Service ANDand XOR flags, the interface name, the mark, and the
outsize.
All
rules created with ipchains have datagram and byte counters associated with them. By
default these counters are presented in a rounded form using the suffixes K and M to represent units of
one thousand and one million, respectively. If the -x argument is supplied,
the counters are expanded to their full unrounded form.
User-defined chains
The
three rulesets of the traditional IP firewall code provided a mechanism for
building firewall configurations that were fairly simple to understand and
manage for small networks with simple firewalling requirements. When the
configuration requirements are not simple, a number of problems become
apparent. Firstly, large networks often require much more than the small number
of firewalling rules we've seen so far; inevitably needs arise that require
firewalling rules added to cover special case scenarios. As the number of rules
grows, the performance of the firewall deterioriates as more and more tests are
conducted on each datagram and managability becomes an issue. Secondly, it is
not possible to enable and disable sets of rules atomically; instead, you are
forced to expose yourself to attack while you are in the middle of rebuilding
your ruleset.
The
design of IP Firewall Chains helps to alleviate these problems by allowing the
network administrator to create arbitrary sets of firwewall rules that we can
link to the three inbuilt rulesets. We can use the -N option of ipchains to create a new chain with any name we please of
eight characters or less. (Restricting the name to lowercase letters only is
probably a good idea.) The -j option configures the
action to take when a datagram matches the rule specification. The -j option specifies that if a datagram matches a
rule, further testing should be performed against a user-defined chain. We'll
illustrate this with a diagram.
Consider
the following ipchains commands:
ipchains -P input DENY
ipchains -N tcpin
ipchains -A tcpin -s !
172.16.0.0/16
ipchains -A tcpin -p tcp
-d 172.16.0.0/16 ssh -j ACCEPT
ipchains -A tcpin -p tcp
-d 172.16.0.0/16 www -j ACCEPT
ipchains -A input -p tcp
-j tcpin
ipchains -A input -p all
We
set the default input chain policy to deny. The second command
creates a user-defined chain called "tcpin." The third command adds a
rule to thetcpin chain that matches any datagram that was sourced from outside our
local network; the rule takes no action. The next two rules match any
datagram that is destined for our local network and either of the ssh orwww ports; datagrams matching these rules are accepted. The next rule
is when the real ipchains magic begins. It causes the firewall software to check any
datagram of protocol TCP against the tcpin user-defined chain. Lastly, we add a
rule to our input chain that matches any datagram; this is another accounting rule.
They will produce the following Firewall Chains shown in diagram:-
Our input and tcpin chains are populated
with our rules. Datagram processing always beings at one of the inbuilt chains.
We'll see how our user-defined chain is called into play by following the
processing path of different types of datagrams.
First,
let's look at what happens when a UDP datagram for one of our hosts is
received.
The
datagram is received by the input chain and falls through the first two rules because they match
ICMP and TCP protocols, respectively. It matches the third rule in the input chain, but it doesn't
specify a target, so its datagram and byte counters are updated, but no other
action takes place. The datagram reaches the end of the input chain, meets with the
default input chain policy, and is denied.
To
see our user-defined chain in operation, let's now consider what happens when
we receive a TCP datagram destined for the ssh port of one of our hosts.
This
time the second rule in the input chain does match and it specifies a target of tcpin, our user-defined chain. Specifying a user-defined chain as a
target causes the datagram to be tested against the rules in that chain, so the
next rule tested is the first rule in the tcpin chain. The first rule matches any datagram that
has a source address outside our local network and specifies no target, so it
too is an accounting rule and testing falls through to the next rule. The
second rule in our tcpin chain does match and specifies a target of ACCEPT. We have arrived at target, so no further firewall processing
occurs. The datagram is accepted.
Finally,
let's look at what happens when we reach the end of a user-defined chain. To
see this, we'll map the flow for a TCP datagram destined for a port other than
than the two we are handling specifically.
The
user-defined chains do not have default policies. When all rules in a
user-defined chain have been tested, and none have matched, the firewall code
acts as though a RETURN rule were present, so if this isn't what you want, you should
ensure you supply a rule at the end of the user-defined chain that takes
whatever action you wish. In our example, our testing returns to the rule in
the input ruleset immediately following the one that moved us to our
user-defined chain. Eventually we reach the end of the input chain, which does have a
default policy and our datagram is denied.
This
example is very simple, but illustrates our point. A more practical use of IP
chains would be much more complex. A slightly more sophisticated example is
provided in the following list of commands:
#
# Set default forwarding
policy to REJECT
ipchains -P forward
REJECT
#
# create our
user-defined chains
ipchains -N sshin
ipchains -N sshout
ipchains -N wwwin
ipchains -N wwwout
#
# Ensure we reject
connections coming the wrong way
ipchains -A wwwin -p tcp
-s 172.16.0.0/16 -y -j REJECT
ipchains -A wwwout -p
tcp -d 172.16.0.0/16 -y -j REJECT
ipchains -A sshin -p tcp
-s 172.16.0.0/16 -y -j REJECT
ipchains -A sshout -p
tcp -d 172.16.0.0/16 -y -j REJECT
#
# Ensure that anything
reaching the end of a user-defined chain is rejected.
ipchains -A sshin -j
REJECT
ipchains -A sshout -j
REJECT
ipchains -A wwwin -j
REJECT
ipchains -A wwwout -j
REJECT
#
# divert www and ssh
services to the relevant user-defined chain
ipchains -A forward -p
tcp -d 172.16.0.0/16 ssh -b -j sshin
ipchains -A forward -p
tcp -s 172.16.0.0/16 -d 0/0 ssh -b -j sshout
ipchains -A forward -p
tcp -d 172.16.0.0/16 www -b -j wwwin
ipchains -A forward -p
tcp -s 172.16.0.0/16 -d 0/0 www -b -j wwwout
#
# Insert our rules to
match hosts at position two in our user-defined chains.
ipchains -I wwwin 2 -d
172.16.1.2 -b -j ACCEPT
ipchains -I wwwout 2 -s
172.16.1.0/24 -b -j ACCEPT
ipchains -I sshin 2 -d
172.16.1.4 -b -j ACCEPT
ipchains -I sshout 2 -s
172.16.1.4 -b -j ACCEPT
ipchains -I sshout 2 -s
172.16.1.6 -b -j ACCEPT
#
In
this example, we've used a selection of user-defined chains both to simplify
management of our firewall configuration and improve the efficiency of our
firewall as compared to a solution involving only the built-in chains.
Our
example creates user-defined chains for each of the ssh and www services in each
connection direction. The chain called www out is where we place rules for hosts that are
allowed to make outgoing World Wide Web connections, and sshin is where we define rules
for hosts to which we want to allow incoming ssh connections. We've assumed
that we have a requirement to allow and deny individual hosts on our network
the ability to make or receive ssh and www connections. The simplication occurs because the user-defined
chains allow us to neatly group the rules for the host incoming and outgoing
permissions rather than muddling them all together. The improvement in
efficiency occurs because for any particular datagram, we have reduced the
average number of tests required before a target is found. The efficiency gain
increases as we add more hosts. If we hadn't used user-defined chains, we'd
potentially have to search the whole list of rules to determine what action to
take with each and every datagram received. Even if we assume that each of the
rules in our list matches an equal proportion of the total number of datagrams
processed, we'd still be searching half the list on average. User-defined
chains allow us to avoid testing large numbers of rules if the datagram being
tested doesn't match the simple rule in the built-in chain that jumps to them.
No comments:
Post a Comment