NAME

ferm - a firewall rule parser for linux


SYNOPSYS

ferm options inputfiles


DESCRIPTION

ferm compiles ready to go firewall-rules from a structured rule-setup. These rules will be executed by the preferred kernel interface, such as ipchains(8) and iptables(8).

Besides just executing all rules in one command, the obvious gain is the possibility to provide a structured description of a firewall. No need anymore for tedious typing all firewalls into custom scripts, you can now write logically and coherent rules using a C-style nesting structure, and let ferm create all rules for you.

ferm will also aid in modularizing firewalls, because it creates the possibility to split up the firewall into several different files, which can be reloaded at will, so you can dynamically adjust your rules.

ferm, pronounced ``firm'', stands for ``For Easy Rule Making''.


STRUCTURE OF A FIREWALL FILE

The structure of a proper firewall file looks like simplified C-code. Only a few syntactic characters are used in ferm- configuration files. Besides these special caracters, ferm uses 'keys' and 'values', think of them as options and parameters, or as variables and values, whatever.

With these words, you define the characteristics of your firewall. Every firewall consists of two things: First, look if network traffic matches certain conditions, and second, what to do with that traffic.

You may specify conditions that are valid for the kernel interface program you are using, probably iptables(8). For instance, in iptables, when you are trying to match tcp packets, you would say:

    iptables --protocol tcp

In ferm, this will become:

    protocol tcp;

Just typing this in ferm doesn't do anything, you need to tell ferm (actually, you need to tell iptables(8) and the kernel) what to do with any traffic that matches this condition:

    iptables --protocol tcp -j ACCEPT

Or, translated to ferm:

    protocol tcp accept;

Noticed the ; character? We're getting to that now, because there are some special characters in ferm that make life easy.

Here's a list of the special characters:

;

The effectuation character. This character defines the end of a rule. Anything defined before this character will be put into one or more rules.

This character *makes* the rule. It gathers all the information, all parameters and targets, special things or whatever, that currently is 'valid', and tries to make a decent rule out of it. ferm will do nothing without this character!

Example:

    proto tcp ACCEPT;

THis example shows a single rule, defined by two keys and one value.

{}

The nesting symbol defines a 'block' of rules.

Anything defined before this block will still be available within all rules inside this block. You can nest blocks in blocks as far as you like. For every rule defined in this block the values defined before this block will apply. Usually you would define an often used parameter as the protocol in front of this block, and anything special inside it.

You can put as many rules (using the <;> character) as you like insode this block. but there should always be one or more, although you will get away with none. Not very usefull except for when you frequently edit you config file, and might want to leave a chain empty.

Since the nesting block is left associative, it cannot be bound to keys defined after the block.

Example:

    chain INPUT proto tcp {
        syn DENY;
        ACCEPT;
    }

This block shows two rules inside a block, which will both be merged with anything in front of it, so you will get two rules:

    iptables -A INPUT -p tcp -y -j DENY
    iptables -A INPUT -p tcp -j ACCEPT
$

Further more, ferm now supports variables, so you can define your favorite targets, interfaces etc. before you use them. It works like this:

    set IF eth0
    set $IF ACCEPT
    set TARGET $IF

will result in this:

    $IF = eth0
    $eth0 = ACCEPT
    $TARGET = eth0
()

The array symbol. Using the parentheses, you can define a 'list' of values that should be applied for the key to the left of it.

Example:

    proto ( tcp udp icmp )

this will result in three rules:

    ... -p tcp ...
    ... -p udp ...
    ... -p icmp ...
    
Only values can be 'listed', so you cannot do something like this:

    proto tcp ( ACCEPT LOG );

but you can do this:

    chain (INPUT OUTPUT FORWARD) proto (icmp,udp,tcp) DENY;

(which will result in nine rules!)

Values can be separated either by spaces or commas. The array symbol is both left- and right-associative, in contrast with the nesting block, which is left-associative only.

#

The comment symbol. Anything that follows this symbol up to the end of line is ignored.

These symbols glue all the keywords into a structure, which allows you to specify some keys only a few times, and let them apply to any key/value pairs defined within an entire block, for instance:

    proto tcp {
        dport 22 ACCEPT;
        syn DPORT 0:1023 DENY;
        }
    ACCEPT;

Now here, the 'proto tcp' is valid within the block, but not anymore after is, resulting in:

    ... -p tcp --dport 22 -j accept
    ... -p tcp -y --dport 0:1023 -j deny
    ... -j accept # note '-p tcp' is not in here!

Some important notes:

- Ferm inserts the rules 'chronologically', so the first rule will be inserted before the second one.

- Anything defined within a block is no longer valid when that block ends.

- Everything defined within the current block that is 'effectuated', will be no longer defined immediately after that point.

- Everything defined before a block is undefined when this block closes.

If you do not understand this, don't worry, it alle becomes clear by itself.

Two types of keys exist:

Firewall keys

Firewall keys define a set of firewall packet matching criteria that is supported by the kernel backend. They look like 'name value' pairs or like 'switch'. For instance:

    proto tcp

or:

    syn

A 'name value' pair lets you fill in a value for a certain condition you would like to match packets against, switches are like on/off light switches on the wall, if you specify a switch, you turn paket matching for whatever the switch stands, on. In the latter example, you turn SYN-packet matching on for this rule.

Both types can optionally be preceded by a !. This will be handled that you don't want something to be matching it:

    !syn

or:

    ! syn

Means you want packets which *don't* have the syn-flag set to be matched. Or even:

    proto ! tcp

Means you want to match *anything but* packets from the tcp protocol.

Read iptables(8) or ipchains(8) to see where the ! can be used.

Option keys

Using option keys alter the behaviour of ferm; they can be used to e.g. clear chains before use, or turn off certain sanity checks.

Example:

  option verbose

This option makes ferm show a lot of information about what it is doing.

The syntax is very simple, let's start with a simple example:

    chain input {
        proto tcp ACCEPT;
    }

This will add a rule to the predefined input chain, matching and accepting all tcp packets. Ok, let's make it more complicated:

    chain (input,output) {
        proto (udp,tcp) ACCEPT;
    }

This will insert 4 rules, namely 2 in chain input, and 2 in chain output, matching and accepting both udp and tcp packets. Normally you would type this for ipchains(8):

   ipchains -A input -p tcp ACCEPT
   ipchains -A output -p tcp ACCEPT
   ipchains -A input -p udp ACCEPT
   ipchains -A output -p udp ACCEPT

Note how much less typing we need to do? :-)

Basically, this is all there is to it, although you can make it quite more complex. Something to look at:

   chain input policy ACCEPT {
       destination 10/8 port ! ftp goto mychain sport :1023 tos 4 settos 8 mark 2;
       destination 10/8 port ftp DENY;
   }

My point here is, that *you* need to make nice rules, keep them readable to you and others, and not make it into a mess.

It would aid the reader if the resulting firewall rules were placed here for reference. Also, you could include the nested version with better readability.

Try using comments to show what you are doing:

    # this line enables transparent http-proxying for the internal network:
    proto tcp if eth0 daddr ! 192.168.0.0/255.255.255.0
        dport http REDIRECT 3128;

You will be thankfull for it later!

    chain input policy ACCEPT {
        interface (eth0,ppp0) {
            # deny access to notorius hackers, return here if
            # no match was found to resume normal firewalling
            goto badguys;

            protocol tcp goto fw_tcp;
            protocol udp goto fw_udp;
        }
    }

The more you nest, the better it looks. Make sure the order you specify is correct, you would not want to do this:

    chain forward {
        proto ! udp DENY;
        proto tcp dport ftp ACCEPT;
    }

because the second rule will never match. Best way is to specify first everyting that is allowed, and then deny everything else. Look at the examples for more good snapshots. Most people do something like this:

    proto tcp {
        dport (
            ssh http ftp
        ) ACCEPT;
        dport 1024:65535 ! syn ACCEPT;
        DROP;
    }


keywords

To make life easy, ferm allows you to use shorthands for most keywords. A list of shorthand notations is available at the end of this section.

What kind of value you provide for a keyword depends on the keyword entirely, e.g. 'protocol' expects 'tcp', 'udp' or 'icmp', 'log-prefix' expects a value like '``whoops, someone rang the doorbell''' and 'destination-port' can accept values like 'http', '80' or '0:1023'. Take a look at the kernel backend program manual for possible values and how they look like.

Note you may put a value in single quotes or double quotes, if this may be required because a value contains spaces:

    log-prefix "Dropped tcp package: "

Please don't use commas, exclamation commas, exclamation marks, parentheses, curly brackets or pipe characters between quotes, ferm doesn't like that.

chain [chain-name]

Specifies a chain that this rule will be inserted to. this is a required key for any rule. Chains can be built in, like input, output or forward, or user-defined chains.

interface [interface-name]

Define the interface name, your outside network card, like eth0, or dialup like ppp1, or whatever device you want to match for passing packets. It is equivalent to the -i switch in ipchains(8) and iptables(8).

outerface [interface-name]

Same as interface, only for matching the outgoing interface for a packet, as in iptables(8). ipchains(8) hasn't got this parameter.

protocol [protocol-name|protocol-number]

Currently supported by the kernel are tcp, udp and icmp, or their respective numbers.

port [port-spec]

Specify a port number, name or range

addr [address-spec]

Specify a network address, a hostname or ip-number.

source|destination

Specify that the values provided for port and addr above should be either source or <destination> ports and addresses. This works like a toggle, which can be left on for the entire configuration file. So, if you say source once, all occurences of port will be source port's, as well as for addresses.

saddr|daddr [address-spec]

Specify an address specifically for the source or destination side, read it as a shorthand for source address and destination address, although it does not 'toggle' the source|destination state, which is remembered.

sport|dport [port-spec]

Specify a port number, name or range for the source or destination side, read it as a shorthand for source port and destination port>, although it does not 'toggle' the source|destination state, which is remembered. Ports can be specified for tcp and udp, as well as icmp, only in that case it means 'icmp-type' and only works when you specify the type numerically.

Note that you need to specify a protocol, before you can use ports, that is because not all protocols support the ideas of ports.

Here are some examples of valid addresses:

    192.168/8 (identical to the next one:)
    192.168.0.0/255.255.255.0
    my.domain.com

And some examples of valid ports/ranges:

    80
    http
    ssh:http
    0:1023        which is equivalent to     :1023
    1023:65535    which is equivalent to     1023:65535
icmptype [type]

To specify an icmp message type. Can be numbers, but refer to the manual of the kernel program to retreive a list, for ipchains use ``ipchains -h icmp''. Examples: ping, pong.

tos [value]

Matches a packet on the specified TOS-value. See settos for values.

settos [value]

Set the tcp package Type Of Service bit to this value. This will be used by whatever traffic scheduler is willing to, mostly your own linux-machine, but maybe more. The original tos-bits are blanked and overwritten by this value. Possible values are (look in the shorthands for more, and easier values) :

02 04 08 10

mark [value]

matches packets based on their mark-value

setmark [value]

Sets the mark-value for a packet, use with the MARK target in iptables

syn

Specify that the SYN flag in a tcp package should be matched, which are used to build new tcp connections. You can identify incoming connections with this, and decide wether you want to allow it or not. Packets that do not have this flag are probably from an already established connection, so it's considered reasonably safe to let these through.

fragment

Specify that only fragmented IP packets should be matched. When packets are larger that the maximum packet size your system can handle (called Maximum Transmission Unit or MTU) they will be chopped into bits and sent one by one as single packets. See ifconfig(8) if you want to find the MTU for your system (the default is usually 1500 bytes).

Fragments are frequently used in DOS attacks, because there is no way of finding out the origin of a fragment packet.

policy [policy]

Specifies the default policy for the current chain. Can be either of the standard actions (ACCEPT, DENY, REJECT, MASQ and REDIRECT). A packet that matches no rules will be treated as specified by the policy. You can't specify chain names here. Only the predefined (built-in) chains have policies.

To avoid ambiguity, always specify the policies of all predefined chains explicitly.

log

Log all packets that match this rule in the kernel log. Be carefull with log flooding. Note the difference with LOG in iptables! See LOG as well. In iptables, this makes a copy of the current rule, and inserts it with the LOG target instead of any other specified target.

See also log-[level|prefix|tcp-sequence|tcp-options|ip-options]

goto [chain]

Specify that matching packets should jump to this chain, only user defined chains are valid jump targets.

reverse

Instructs the kernel to use this rule twice, the second time with source and destination swapped. Unfortunately, this doesn't work with iptables.

LOG

Identical to the 'LOG' target in iptables, logs any packet that matches, but doesn't do anything else to it. Only valid for iptables, otherwise use 'log'. See log and also log-[level|prefix|tcp-sequence|tcp-options|ip-options].

ACCEPT

Accepts matching packets.

REJECT

Rejects matching packets.

DENY

Denies matching packets.

MASQ [port|portrange]

Masquerades matching packets. Optionally followed by a port or port-range for iptables. Specify as ``123'', ``123-456'' or ``123:456''. The port range parameter specifies what local ports masqueraded connections should originate from.

RETURN

Returns to the parent chain where the current chain was called if the packet matches.

REDIRECT [port|portrange]

Allows transparent proxying when rule matches, the port that is redirected to must immediately follow this keyword. The target may also be an IP-number in case you are using iptables(8), so something like ``REDIRECT 192.168.0.5:21'' is valid there.

SNAT|DNAT [ip-address|ip-range|ip-port-range]

Allows source/destination address translation, only valid for iptables(8), requires an ip-number, range or ip/port value.

TOS

Changes the packets TOS-field according to the set-tos parameter specified, only valid for iptables.

table [table-name]

Selects this table for the rule. Valid table names are ``filter'', ``nat'' and ``mangle''.

reject-with [value]

Rejects a packet with an ICMP value type message.

limit [value]

Limits these type of packets to a maximim.

burst [value]

Limits bursts of these packets.

mac [value]

Matches packets originating from these mac-addresses.

state [value]

Matches packets with this state. The value may be specified as a normal ferm-list: ``(ESTABLISHED,RELATED)'' but ``NEW:RELATED'', and single values are also allowed.

tcp-flags [!] [flagmask] [flagmatch]

Specify tcp-flags, the ! is optional and has to precede the mask, mask and match are mandatory. The list of mask or match flags may be specified as a normal ferm-list: ``(SYN,ACK,RST)'', but ``SYN:ACK:RST'' and single values are also allowed.

tcp-option [value]

Specify a tcp-option for this rule.

log-[level|prefix|tcp-sequence|tcp-options|ip-options] [value]

Specifies several extra tcp/ip options.

[u|g|p|s]id-owner [value]

Matches packets originating from this User, Group, Pid or Session ID.

set [name] [value]

Set variable ``name'' to value ``value'', you can dereference the variables by ``$name''. You may also put variables within set statements.


SHORTHANDS

Here's a complete list of possible shorthands, just to reduce the amount of typing:

interface:

if

outerface:

of

protocol:

proto

source:

src

destination:

dest

fragment:

frag

ACCEPT:

accept

DENY:

deny, DROP, drop

REJECT:

reject

MASQ:

masq

RETURN:

return

REDIRECT:

redirect, PROXY, proxy

MARK:

mark

QUEUE:

queue

SNAT:

snat

DNAT:

dnat

goto:

to, jump

icmptype

icmp-type

reverse:

bidirectional, swap

tcp-option:

tcpoption

mac:

mac-source, macsource

burst:

limit-burst, limitburst

uid-owner:

uidowner, uid

gid-owner:

gidowner, gid

pid-owner:

pidowner, pid

sid-owner:

sidowner, sid

log-level:

loglev

log-prefix:

logprefix

log-tcp-sequence:

logseq

log-tcp-options:

logtcpopt

log-ip-options:

logipopt

reject-with:

rejectwith

setmark

set-mark

tos/settos-values:

The following Type Of Services values may be given:

    mincost min-cost 2 02 0x02

    reliability reliable 4 04 0x04

    max-throughput maxthroughput 8 08 0x08

    lowdelay interactive min-delay 10 0x10

    clear 0 00 0x00


OPTIONS

Options can be specified with the ``option'' keyword, which can be defined anywhere within the document. Although that may be fine, you almost allways want to define them at the beginning of your document, because the behaviour changes at the moment they are specified.

All options can also be specified on the command line, which has a few more available. The equivalent for the commandline options that are also available in the firewall file is mentioned in the firewall file options section.


Command line options

--noexec

Do not execute the ipchains(8) or iptables(8) commands, but skip instead. This way you can parse your data, use --lines to view the output.

--lines

Show the firewall lines that were generated from the rules. They will be shown just before they are executed, so if you get error messages from ipchains(8) etc., you can see which rule caused the error.

--verbose

Shows some more details of the stages of execution of the program.

--relaxed

Do not fuzz about errors, instead continue and let ipchains(8) etc. give errors back.

--help

Show a brief list of available commandline options.

--version

Shows the version number of the program.

--use [ipchains|iptables|ipfwadm]

Use this kernel program to implement the rules into the kernel. Also available as firewall file option ``option [...]''. This option must be set, either on the commandline or in a ferm config file.

--automod

Automatically insert the correct module parameter when using iptables, making the module parameter unnecessary


Firewall file options

option clearall

Clears the entire firewall, deletes all user chains and flushes the built in chains. Does not alter policies.

option flushall

Flushes all chains but does not delete them.

option flushchains

Flushes any chain which is defined in the setup, even built-in chains are flushed when referred.

option createchains

Creates any chain which is referred to, even when no rule is specified for the chain, but is only referred by with a ``goto'' keyword.

option automod

Automatically insert the correct module parameter when using iptables, making the module parameter unnecessary

option relaxed

Makes the program not whining over serious errors, although that's not very handy because it will fail to install the rules.

option [iptables|ipchains|ipfwadm]

Define which kernel program you have to use to install rules. This one is required, since on some systems, they can both be present, or you want to use a wrapper for an older version. Currently defaults to ipchains.


SEE ALSO

ipchains(8), ipfwadm(8), iptables(8)


NOTES

A good firewall is not the only step in security, even the firewall may be insecure, or someone breaks into your house and steals the hard disk out of your PC. Do not rely on this firewall tool for the use of mission critical or confidential data. It is not fit for such a purpose!

Instead, use this tool to expand your current use of ipchains(8) and routing, create a flexible firewall and look out for anything suspicious. Be carefull with open ports and servers, always get the latest, patched versions. Read more about firewalls before experimenting, you are warned! You might also read the COPYING file provided with the package or visit www.gnu.org to find more about the license.


EXAMPLES

The package comes with a directory full of goodies (examples) that you can try, adjust for your system or just read if you want to understand the syntax and it's possibilities. Look in the ``examples'' directory.


REQUIREMENTS


Operating system

The Operating system currently supported is only linux, although it may be possible to port this program to support FreeBSD or SOLARIS firewall systems, provided they supply a similar firewalling scheme. (Does anybody known about that?)


Software/packages

Required are 2 packages: Perl5, under which this ferm runs, and one of the kernel firewall programs, suited for your system and kernel version.


Kernel

The respective required kernel versions for each of the kernel firewall programs (ipchains(8), ipfwadm(8) or iptables(8)) is also needed. This means you have to have a kernel which can use the firewalling thing, something you might have to compile a kernel for, or set some switches in /proc. Look at the man pages of those kernel programs for more information.


RESTRICTIONS

ferm allows almost anything the used firewall program allows, so go ahead and specify complex port ranges, icmp by number or worse. Just be warned.

Although quite sophisticated, the kernel interface programs ipchains(8) and iptables(8) are very limited in some respects. ferm is only an interface to improve the handling of these programs, and is therefore limited by the possibilities of these programs.

Ipfwadm(8) is extremely limited in rule-building, upgrade or succomb in it. Nothing ferm can do about it.


BUGS

The ipfwadm(8) interface is really limited due to being unable to test it and having no experience with it at all. I'll be concentration on iptables(8), which supports much more options and will be quite more flexible.

Several nasty cleanups are not done well, which may result in surviving data. Tried to remove all of them but suspect more of them to occur.

The --log-prefix construct does not allow certain characters to be put between ``''. Make sure you don't use the bracket {} and [] characters, the ! and , are also not correctly parsed.


TODO

* Improve ipfwadm(8) handling or removing it altogether

* Add more examples, with modularized snipplets (include option)

* Make rpm's for RH and SuSE, or better: get you to do that!

* Review the second half of the manual page

* Make ferm bug you more about errors, i.e. increase validity checking to high levels


COPYRIGHT

Copyright (C) 2001, Auke Kok <koka@geo.vu.nl>


LICENSE

ferm is released under the Gnu Public License, see the COPYING file that came with the package or visit www.gnu.org.

This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


AUTHOR

Auke Kok (koka@geo.vu.nl)