The WallFire HOWTO
1 - About this document
This document has been started on May 1st 2004 by Grégoire HUBERT <greg AT coolkeums DOT org>.
Revisions:
- Initial release 0.1 on May 1st 2004
- version 0.2 on January 11th 2005 by Hervé Eychenne
2 - What is WallFire
WallFire is a coherent toolset to help managing firewalls.
Considering the fact that most firewalls have powerful but
complicated and sophisticated languages, the aim of this project is to
offer comprehensive tools to set the network security policy whatever
the underlying firewall may be. WallFire makes it possible to configure
your firewall in few operations. It comes with his own high-level rules
langage that can be translated into different firwalls rulesets. For the
moment, support for netfilter (almost working) and ipfilter is planned.
WallFire currently provides the following tools:
- wfconvert: the WallFire rules converter.
- wflogs: the firewalling log analyzer.
What can I do with WallFire now?
you can only set a simple firewall script to test
the language. WallFire is still under heavy development and we need
your feedback and/or bug reports to make it the perfect firewall
toolkit. ;-)
3 - The WallFire language
3.1 - Introduction
As described above, the WallFire language is a high level ruleset
description language. It can be translated into a netfilter ruleset
using the wfconvert tool.
firewall $fw {
interface ppp0 {
zone $internet;
}
}
accept {
from $internet to $fw dport ssh;
from $fw to $internet;
};
|
This simple WallFire script will set up a firewall accepting SSH
only. You will find in this document the complete WallFire language
guide.
3.2 - Structure
The language is divided in two main parts: a head (declarations) and a
body (rules). The head part is also separated in 5 parts.
- HEAD (objects definitions)
- zones
- firewalls
- variables (metahosts and metaports)
- groups
- BODY (filter and NAT rules)
3.3 - Syntax
3.3.1 - Comments
WallFire has several comment types:
- Both C (/* ... */) and C++ (prefixed by //) comment styles exist
in WallFire language.
/* These comments will */
// be stripped by wfconvert.
|
- The "inline" comment lines (prefixed by #) are located just before
(without a blank line) an object definition (zone, firewall, variable, group)
or a rule. These comment are associated to the object it preceeds, and will
not be discarded by any WallFire processing.
For example, when using the netfilter output module, those will be
turned into shell comments contained in the produced script.
# This comment is related to the rule below and will NOT
# be stripped by wfconvert
accept from $firewall to $internet;
|
3.3.2 - The WallFire variables
All WallFire variables are prefixed by $ symbol.
For now, supported objects are:
3.3.3 - Zones
The zones section enables to declare networks by associating a name to
their addresses.
There is a special builtin zone which is called internet, which is
already declared by default (by definition, it has no network address).
This is generally the zone where the firewall's defaut gateway points to.
The zones section can be empty when there is no zone (no other zone than
internet).
You can define several zones.
# DMZ zone
zone $dmz = 192.168.1.0/28;
# LAN zone
zone $lan = 10.0.0.0/255.255.0.0;
|
3.3.4 - Firewalls
The firewalls section enables to declare one or more firewalls, with
their interfaces.
You have to define at least one firewall, but you can describe several ones
if needed.
The syntax is:
firewall $name {
interface name {
[macaddr;]
[addr;]
[broadcast;]
zone $zone;
}
}
|
The lines into brackets are optional parameters.
- maccaddr is : macaddress MAC
Example:
- macaddress 00:D0:90:8F:23:F5;
- addr is : [(|static|dynamic)] [address [IP]] [guess (none|compiletime|runtime)]
Examples:
- dynamic address guess runtime; (this is the default)
- static address 192.168.0.1;
- broadcast is : broadcast [IP]
Example:
- broadcast 192.168.255.255;
Each interface must have an associated declared zone. If we considere
the previous zones $lan and $dmz we can declare the following
firewall:
firewall $fw {
interface ppp0 {
zone internet;
}
interface eth0 {
static address 192.168.1.1;
zone $dmz;
}
interface eth1 {
static address 10.0.0.1;
broadcast 10.0.255.255;
zone $lan;
}
}
|
3.3.5 - The metahost and metaport keywords
You can identify several hosts, networks in a wallfire variable using
the metahost keyword.
metahost $web_server = 192.168.11.80;
metahost $dns = (192.168.11.53, 10.0.0.53);
metahost $servers = ($web_server, $dns, $dmz);
|
You can also define UDP or TCP ports with the metaport keyword:
metaport $forbidden = (111, telnet, 340:353);
|
3.3.6 - The group statement
Inline groups
Inline groups are similar to inline functions in C or C++. An inline group
is a kind of macro: the bits of rules it contains are simply substituted
into its caller.
The syntax of its definition is:
inline groupdef mygroup((ARGS) {
RULES
}
|
ARGS is optional. If no arguments are given, () can be omitted.
ARGS is a list of variables (such as metahost or metaport) separated
by commas. Each can be given a default value with sign '=' (like in C++).
RULES do not have to contain a target.
inline groupdef dns {
proto udp dport domain;
proto tcp dport domain;
}
accept from toto to dnsservers group dns;
|
is strictly equivalent to:
accept from toto to dnsservers {
proto udp dport domain;
proto tcp dport domain;
};
|
Now, let's declare an inline-group named toproxy:
inline groupdef toproxy(metahost $proxy = 192.168.0.2) {
to $proxy proto tcp dport 8080;
}
|
The $proxy argument will be set to default value 192.168.0.2 if $proxy is
not defined when calling toproxy group.
accept from toto to dnsservers group toproxy($proxy: 10.0.0.3);
accept from toto to dnsservers group toproxy;
|
is strictly equivalent to:
accept from toto to dnsservers to 10.0.0.3 proto tcp dport 8080;
accept from toto to dnsservers to 192.168.0.2 proto tcp dport 8080;
|
Groups (non-inline)
Groups (non inline ones) are groups of rules (each rule must contain a target).
They behave like netfilter/iptables's chains, or ipfilter groups.
When translated into the target firewalling language, the wallfire groups will
not be substituted at the caller's place, but will take advantage of
the underlying facilities (chains, groups).
groupdef logdrop {
log;
drop;
}
|
When translated into netfilter/iptables, the example above will create
a logdrop chain with the corresponding rules:
iptables -N logdrop
iptables -A logdrop -j LOG
iptables -A logdrop -j DROP
|
3.3.7 - The rules
A rule is a combinaison of instructions. Here are the supported keywords:
- accept | drop | reject | log | group group_name | nat (target)
- in interface_name (input interface, for example: in eth0)
- out interface_name (output interface, for example: out eth1)
- proto protocol_name (for example: proto tcp)
- from network_object (for example: from 192.168.102.33)
- to network_object (for example: to 10.10.107.99)
- sport port_object (source port, if supported by the protocol, for example: sport 22)
- dport port_object (destination port, if supported by the protocol, for example: dport 80)
- group group_name[(args)] (refers to a declared inline group)
- bloc (containing one or several rules enclosed in {})
where
- reject is:
reject [with reject_type]
where reject_type is:
- icmp-net-unreachable
- icmp-host-unreachable
- icmp-port-unreachable
- icmp-proto-unreachable
- icmp-echo-reply
- icmp-net-prohibited
- icmp-host-prohibited
- tcp-reset (with protocol TCP only)
By default (if not type is specified), when translating into target
firewalling language, wallfire chooses what's best.
- log is:
log [text "log_text"]
where "log_text" is a string.
- nat is one of the following forms:
- nat (src|dst) into none (no NAT)
- nat src (masquerading)
- nat (src|dst) into ipaddr1[-ipaddr2] [port1:port2]
- nat (src|dst) into network/mask (map a network to another: NAT 1:1)
- network_object is:
- an IP address (for example 192.168.102.33)
- a network adress (for example 192.168.0.0/255.255.0.0, or 192.168.0.0/16, or 192.168.*.*)
- a variable (for example $proxy or $zonename)
- a list of what preceeds, enclosed in parenthesis
(for example (192.168.102.33, 10.0.0.0/8, $proxy)).
- port_object is:
- a port number (for example 22)
- a service name (for example ssh)
- a variable (for example $samba)
- a list of what preceeds, enclosed in parenthesis
(for example (22, www, $samba)).
Simple example:
from $internet to $fw accept proto tcp dport ssh;
|
Example using a bloc:
from $internet to $fw {
accept proto tcp dport ssh;
reject proto tcp dport auth;
# drop packets without logging them
drop group samba;
# log and drop everything else according to our logdrop group definition (see 3.3.6).
logdrop;
};
from $network to $metahost {
reject with tcp-reset proto tcp dport ident;
reject with icmp-host-unreachable proto udp sport domain;
log "Virus:" proto (udp, tcp) dport 12345;
};
|
4.0 - Example: A Dual-DMZ network
zone $public_dmz = 212.155.209.128/29;
zone $private_dmz = 192.168.101.0/24;
zone $lan = 10.0.0.0/255.255.0.0;
firewall $obiwan {
interface eth0 {
static address 212.155.209.128;
zone $internet;
}
interface eth1 {
static address 212.155.209.128;
zone $public_dmz;
}
interface eth2 {
static address 192.168.101.1;
zone $private_dmz;
}
interface tr0 {
static address 10.0.0.1;
zone $lan;
}
}
metahost $dns_servers = (192.168.101.69, 192.168.101.45);
metahost $proxy_cache = 212.155.209.134;
metahost $web_cluster = 192.168.101.80;
metahost $web_servers = 192.168.101.81-192.168.101.84;
metahost $database = 192.168.101.132;
metahost $mail_relay = 212.155.209.130;
metahost $mail_server = 192.168.101.25;
metahost $backup_server = 192.168.101.250;
metahost $admins = (10.0.1.100, 10.0.1.101, 10.0.1.103);
inline groupdef dns {
proto udp dport domain;
proto tcp dport domain;
}
groupdef logdrop {
log;
drop;
}
to $obiwan {
from $admins accept proto tcp dport ssh;
accept from ($lan, $private_dmz, $public_dmz) proto icmp;
group logdrop;
};
to $public_dmz {
from $internet {
accept proto tcp {
dport www to $proxy_cache;
dport smtp to $mail_relay;
};
};
accept proto (tcp, udp) from $backup_server;
reject from $private_dmz;
accept from $admins proto tcp dport ssh;
from $lan {
accept proto tcp dport www to $proxy_cache;
reject;
};
};
accept from $obiwan;
to $private_dmz {
from $public_dmz {
accept group dns to $dns_servers;
accept proto tcp {
dport www from $proxy_cache to $web_cluster;
dport smtp from $mail_relay to $mail_server;
};
};
from $lan {
accept group dns to $dns_servers;
accept proto tcp {
dport ssh from $admins;
dport www to $web_cluster;
dport smtp to $mail_server;
dport 5432 to $database;
};
reject;
};
from $obiwan accept;
};
reject to $lan from ($private_dmz, $obiwan);
|