Skip to content

Configuration

Kim B. Heino edited this page Oct 8, 2024 · 67 revisions

Configuration

Configuration files

Foomuuri reads configuration files from /etc/foomuuri/*.conf in alphabetical order, including all sub directories. Foomuuri also reads static configuration from /usr/share/foomuuri/*.conf which can be overwritten in /etc/foomuuri.

Raw nftables rules can be written to /etc/foomuuri/*.nft and they will be included to generated ruleset.

See best practices for recommendations how to split different sections to multiple configuration files.

foomuuri

This section can be usually omitted as default values should be fine.

This section defines common options for Foomuuri. If really needed, it is usually better to override single value, not full section. Example:

foomuuri {
  # Change rpfilter's value, keep everything else as default
  rpfilter no
}

Full list of default values are:

foomuuri {
  log_rate "1/second burst 3"
  log_input yes
  log_output yes
  log_forward yes
  log_rpfilter yes
  log_invalid no
  log_smurfs no
  log_level "level info flags skuid"
  localhost_zone localhost
  dbus_zone public
  rpfilter yes
  counter no
  set_size 65535
  recursion_limit 65535
  priority_offset 5
  dbus_firewalld no
  nft_bin nft
}

log_rate is the default logging rate per source IP. Default value is to log first three entries per source IP and then one additional entry per second. Rate specification is the same as in rate limit rule.

log_input ... log_smurfs defines default value for specific logging. Value can be:

  • yes to log it with log_rate
  • no to not log
  • "3/second burst 10" to log it with specific rate

log_level is the syslog level of logging. For possible values see rule specific version.

localhost_zone is the name of zone used for the computer running Foomuuri, similar to "localhost" in hostnames. See zone and zone-zone sections for more information.

dbus_zone is the name of your outgoing internet zone. This is used in D-Bus support.

rpfilter is to enable or disable reverse path filtering. Value can be:

  • yes to enable it to all interfaces
  • no to disable it
  • eth0 eth1 eth2 to enable it to specified interfaces eth0 eth1 eth2
  • -eth1 -eth2 to enable it to all other interfaces than eth1 eth2

counter is to add anonymous byte and packet counter to all rules. Value can be:

  • yes to add it to all rules
  • no to not add it
  • localhost-public public-localhost to add it to all rules in localhost-public and public-localhost sections

set_size is the size for rate limit sets. Value 65535 is fine for normal hosts. For company firewall larger value is required, like 262143 (=2^18 - 1). This is the maximum amount of rate limit entries in kernel. If set is full, new entry can't be added and the traffic is accepted without rate limit. See foomuuri list for content of your active sets.

recursion_limit is the internal limit to avoid template expansion loop. Increase this value if you get false "Possible template loop" error.

priority_offset is the chain priority offset for rules generated by Foomuuri. Tune this value if you have multiple software adding chains and want to process them in some particular order. For example, iptables uses offset 0 and FirewallD uses 10. Lowest priority is processed first. So to process Foomuuri rules first, use value -5. To process them last, use 20.

dbus_firewalld is to enable or disable FirewallD D-Bus emulation inside Foomuuri. NetworkManager can tell FirewallD to attach and detach interfaces to zones via D-Bus call. This option enables Foomuuri to listen to FirewallD D-Bus and do the same thing.

nft_bin is the name for nft binary. Full path can be specified if required.

zone

This section is required on all configurations. It lists all known zones.

zone {
  localhost
  public
}

Above example defines two zones, localhost and public. All configurations must have zone localhost, which is the computer running Foomuuri, similar to "localhost" in hostnames. See best practices for recommended zone names.

Above example assumes that you are using FirewallD D-Bus (dbus_firewalld config option) emulation where interfaces are attached and detached to zones by NetworkManager. It is the recommended way for laptops and personal servers. This config option will be turned on by default when installing foomuuri-firewalld package.

You can also define default interface to zone mapping by specifying interface name(s) after zone name. This is useful for corporate servers with static network configuration. This method can be used with or without FirewallD D-Bus emulation. This mapping is only default, not static. Interfaces can still be moved to other zones with D-Bus calls.

zone {
  localhost            # Localhost must be left empty
  public    eth0       # eth0 is attached to public
  dmz       eth1 eth2  # eth1 and eth2 are in dmz
}

It is also possible to use wildcard interface names. If you define wg* then make sure that NetworkManager doesn't try to assing wg0 to any zone. It would create "interval overlap" error as wg* and wg0 conflicts.

zone {
  localhost
  public     eth0
  wireguard  wg*  # Matches wg0, wg1, wgfoo, and so on
}

macro

Instead of writing rule tcp 443 it is easier and more readable to use rule https. These alias names are called macros. Macro can be used in any part of rule you want to, defining rule fully or partially.

macro {
  # Define service as macro
  smtp        tcp 25
  https       tcp 443; udp 443
  googlemeet  udp 3478 19302-19309; https

  # Define rate limit as macro
  ssh_rate    saddr_rate "5/minute burst 5"

  # Macro can be split to two lines with + (append) or \ (continue in next line)
  good_hosts  10.0.0.1 fd00:f00::1
  good_hosts  + 10.0.0.2 fd00:f00::2
}

You can use above macros in zone-zone section:

localhost-public {
  https daddr good_hosts   # Allow https to specific IP addresses
  tcp 23 daddr good_hosts  # Allow TCP 23 to specific IP addresses
  https reject  # Reject all other https traffic
  googlemeet    # Allow Google Meet to everywhere
}

public-localhost {
  ssh ssh_rate  # Allow incoming ssh with rate limit
}

All known macros can be listed with foomuuri list macro command.

zone-zone

FromZone-ToZone section defines rules for traffic coming from FromZone and going to ToZone. Normally you first accept some traffic and then reject or drop everything else as final rule. Rules inside zone-zone section are (mostly, see below) processed in listed order. Example:

public-localhost {
  # Allow some incoming traffic
  dhcp-client
  ping
  ssh

  # Drop everything else
  drop log
}

localhost-public {
  # Allow some outgoing traffic
  dhcp-server
  domain
  https
  ping
  ssh

  # Reject everything else
  reject log
}

Foomuuri will automatically add final drop log (or reject log for localhost-something) rule to zone-zone section if not specified. It is always better to add explicit final rule to configuration.

Zone-zone section localhost-localhost [new in version 0.22] (aka loopback traffic, aka 127.0.0.1 and ::1) is special case. It's final rule is accept. Usually there is no need to add localhost-localhostsection.

Normal use case for localhost-localhost is to deny some traffic and then accept everything else.

localhost-localhost {
  # Don't allow user "untrusted" to connect local services
  uid untrusted drop log

  # Don't allow local http traffic
  http reject log

  # Accept everything else
  accept
}

Please note that loopback traffic from your public IP to your public IP belongs to localhost-localhost, not public-public.

If you have a lot of zones there will be a lot of zone-zone pairs. See best practices for recommendations how to split them to multiple files.

"Mostly": Rules inside zone-zone section are automatically sorted and processed in following block order:

  1. ICMP rules in listed order
  2. Previously accepted established and related traffic is accepted again by conntrack
  3. Incoming multicast and broadcast rules in listed order
  4. Everything else in listed order

resolve

Instead of using static IP addresses Foomuuri can perform periodical DNS lookups for hostnames and store their IPv4 and IPv6 addresses to set. Set name must begin with @ character. Static IPv4 or IPv6 address with or without mask can also be used [new in version 0.25].

resolve {
  # Resolve foobar.fi and store its IP addresses to @foobar_fi set
  @foobar_fi  foobar.fi

  # Resolve multiple hostnames to set @fooweb
  @fooweb     foo-web.foobar.fi bar-web.foobar.fi
  @fooweb     + baz-web.foobar.fi

  # Mixing hostnames and IP addresses
  @one_ip     10.0.0.1
  @mixed      foobar.fi 192.0.2.0/24
}

localhost-public {
  https daddr @fooweb  # Allow https to above hostnames
  https reject         # Reject all other https
}

Lookups are refreshed every 15 minutes. This interval can be changed using systemctl edit foomuuri-resolve.timer. Single lookup failure does not matter, Foomuuri will cache results for 24 hours. These results are also saved for reloads and reboots.

iplist

This is similar to resolve but set content can be specified as:

  • Filename containing one IP address per line, with or without mask
  • URL for file, as above
  • IPv4 or IPv6 address, with or without mask [new in version 0.25]

iplist can be used to blacklist or whitelist IP addresses, or to download IP address country databases (aka geolocation). Set name must begin with @ character.

Set name without filename or URL configures an empty set. Its entries can be manipulated with foomuuri iplist add and foomuuri iplist del commands.

iplist {
  # Download Finnish IPv4 and IPv6 addresses from https://github.com/ipverse/rir-ip
  @fi   https://raw.githubusercontent.com/ipverse/rir-ip/master/country/fi/ipv4-aggregated.txt
  @fi   + https://raw.githubusercontent.com/ipverse/rir-ip/master/country/fi/ipv6-aggregated.txt

  # Download Finnish Elisa operator IP addresses from https://github.com/ipverse/asn-ip
  @elisa  https://raw.githubusercontent.com/ipverse/asn-ip/master/as/719/ipv4-aggregated.txt
  @elisa  + https://raw.githubusercontent.com/ipverse/asn-ip/master/as/719/ipv6-aggregated.txt

  # Read blacklist from text files
  @blacklist  /etc/foomuuri/blacklist*.txt

  # Read content from file and add some extra IPs to it
  @whitelist  /etc/foomuuri/whitelist*.txt 10.0.0.0/8 192.0.2.32

  # Manipulate this list with "foomuuri iplist add mylist 10.0.0.1" command.
  @mylist
}

public-localhost {
  # Don't allow blacklisted addresses to IMAP
  imap saddr @blacklist drop log "public-localhost DROP-blacklist"

  # Allow mylist entries to IMAP without rate
  imap saddr @mylist

  # Allow Finnish users to IMAP with fast 1 per second rate
  imap saddr @fi saddr_rate "1/second burst 10"

  # Allow everybody to IMAP with slow 1 per minute rate. This includes
  # over rate limit Finnish users.
  imap saddr_rate "1/minute burst 1"

  # ...rest of the rules...
}

Another example how to create a macro to allow access to Valve Steam:

iplist {
  # Create iplist from Valve's IP addresses
  @valve_as	 https://raw.githubusercontent.com/ipverse/asn-ip/master/as/32590/ipv4-aggregated.txt
  @valve_as	 + https://raw.githubusercontent.com/ipverse/asn-ip/master/as/32590/ipv6-aggregated.txt
}

macro {
  # Create macro to allow outgoing traffic to Valve iplist
  valve-steam	udp 3478 4379-4380 27000-27100 daddr @valve_as;
  valve-steam	+ tcp 27015-27050 daddr @valve_as
}

localhost-public {
  # Allow outgoing traffic to Valve Steam
  valve-steam
}

Lookups are refreshed every 24 hours. Like in resolve, lookups are cached for 10 days for lookup failures, reloads and reboots.

Refresh interval can be configured [new in version 0.22] globally or per-iplist with refresh option:

iplist {
  # Set global refresh interval
  refresh 4h15m

  # Use global refresh interval
  @blacklist  /etc/foomuuri/blacklist*.txt

  # Use per-iplist refresh interval
  @whitelist  /etc/foomuuri/whitelist*.txt refresh=1h15m
}

Interval is rounded to 15 minutes. This can be configured with systemctl edit foomuuri-iplist.timer command.

zone-any, any-zone, any-any

These sections are similar to zone-zone section, except that they match any destination (zone-any), any source (any-zone) or all (any-any) traffic. These rules are processed first and then normal zone-zone rules. Example:

localhost-any {
  # Allow ping and SSH for all traffic from localhost, no matter where
  # it is going. Normally reject/drop statement is added to specific
  # localhost-zone section, not to localhost-any.
  ping
  ssh
}

localhost-public {
  # Accept HTTPS to public, in addition to localhost-any rules.
  https
  reject log
}

localhost-internal {
  # Accept DNS queries to internal, in addition to localhost-any rules.
  domain
  reject log
}

Matcher szone -public can be used in rule to skip adding it to public-localhost [new in version 0.24]. Example:

any-localhost {
  ssh                  # allow ssh from anywhere
  https szone -public  # allow https from anywhere except from public
}

localhost-any {
  ssh                  # allow ssh to anywhere
  vnc dzone -public    # allow vnc to anywhere except to public
}

template

Template is similar to macro but it is used to define list of rules, while macro defines single rule. Example:

template outgoing_services {
  # Define template called "outgoing_services"
  dhcp-server
  domain
  https
  ntp
  ping
  ssh
}

localhost-public {
  # Include template's content here
  template outgoing_services

  # Continue with other rules
  http
  reject log
}

dmz-public {
  # Use same template for traffic coming from dmz zone
  template outgoing_services

  # Continue with other rules
  reject log
}

zonemap

Normally Foomuuri will map incoming and outgoing traffic to zones by source and destination network interface. These interfaces are assigned to zones dynamically by NetworkManager, or configured in zone section.

Zonemap section can be used to map traffic to different zone by using standard rules. Example:

zonemap {
  # Map outgoing IPsec traffic that is going to zone "public" to use zone
  # "vpn" instead.
  dipsec dzone public new_dzone vpn

  # Same for incoming.
  sipsec szone public new_szone vpn
}

localhost-public {
  # Rules for non-IPsec traffic
  ipsec   # You must allow IPsec traffic here and in public-localhost
  ...accept some traffic...
  reject log
}

localhost-vpn {
  # Rules for IPsec traffic
  ...accept some traffic...
  reject log
}

Above example, splitting traffic to IPsec and non-IPsec zones is the most common use case. You can use any matcher, for example daddr or saddr to map some IP addresses to own zones, or uid or gid to map outgoing traffic from some local user to own zone:

zonemap {
  # Map IP address 10.2.3.0/24 from internal to dmz
  saddr 10.2.3.0/24 szone internal new_szone dmz
  daddr 10.2.3.0/24 dzone internal new_dzone dmz

  # Map outgoing traffic from user myservice to myzone
  uid myservice szone localhost new_szone myzone

  # Map all outgoing IPsec traffic to xxx-vpn, no matter what the original
  # dzone was
  dipsec new_dzone vpn
}

snat

Source NAT is used to mangle traffic by using standard rules. Example:

snat {
  # Masquerade all traffic from 10.0.0.0/8 going to eth0 interface.
  # New outgoing IP is eth0's IP address.
  saddr 10.0.0.0/8 oifname eth0 masquerade

  # Use outgoing IP 192.0.2.32 to all non-IPsec traffic coming from
  # 10.0.0.0/8 and going to eth1 interface.
  saddr 10.0.0.0/8 oifname eth1 -dipsec snat to 192.0.2.32
}

Remember to accept SNAT'ed traffic in zone-zone section. See dnat below for more examples.

dnat

Destination NAT is used to mangle traffic by using standard rules. Example:

dnat {
  # http traffic to 10.0.0.1 is DNAT'ed to 10.0.0.2 port 8080
  daddr 10.0.0.1 http dnat to 10.0.0.2:8080

  # http traffic to fd00:f00::1 is DNAT'ed to fd00:f00::2 port 8080
  # [new in version 0.22]
  daddr fd00:f00::1 http dnat to [fd00:f00::2]:8080

  # http traffic to 10.0.0.6 is DNAT'ed to 10.0.0.7, port doesn't change
  daddr 10.0.0.6 http dnat to 10.0.0.7

  # All smtp traffic from eth2 is DNAT'ed to 10.0.0.8 or fd00:f00::8,
  # port doesn't change
  iifname eth2 smtp dnat to 10.0.0.8 fd00:f00::8

  # All traffic from eth1 inteface is DNAT'ed to 10.0.0.3
  iifname eth1 dnat to 10.0.0.3

  # http traffic to 10.0.0.4 coming from interface eth2 is DNAT'ed to 10.0.0.5
  iifname eth2 daddr 10.0.0.4 http dnat to 10.0.0.5
}

Remember to accept DNAT'ed traffic in zone-zone section. This can be done with specific rule or with ct_status matcher. Example:

public-internal {
  # Specific rule to accept https
  https daddr 10.0.0.9

  # Generic rule to accept all DNAT'ed traffic
  ct_status dnat
}

prerouting, postrouting, output, forward

These sections are used to specify packet mangle rules. prerouting is for all incoming packets, postrouting is for all outgoing packets, output is for all locally generated packets and forward is for all forwarded packets. Mangle rules are processed before normal zone-zone filtering rules.

Normally these sections are used to set packet mark value. Example:

prerouting {
  # Do nothing if mark is already set
  mark_match -0x0000/0xff00 accept

  # Set default mark 0x100 to packet
  mark_set 0x100/0xff00

  # Change mark to 0x200 if it is coming from eth2
  iifname eth2 mark_set 0x200/0xff00

  # Use mark 0x300 for SSH traffic from eth2
  iifname eth2 ssh mark_set 0x300/0xff00
}

invalid, rpfilter, smurfs

Packets entering invalid, rpfilter or smurfs chains will be dropped. These sections can be used to specify more rules to them [new in version 0.23]. For example load balanced IPVS traffic might enter to invalid chain and must be accepted:

invalid {
  # Accept HTTPS IPVS traffic
  https
}

hook

Foomuuri can run external command before/after starting and stopping. This section configures those commands. Example:

hook {
  pre_start  command-to-run with arguments before loading ruleset
  post_start echo firewall started
  pre_stop   /etc/foomuuri/pre_stop.sh
  post_stop  /etc/foomuuri/post_stop.sh with arguments
}

target

Foomuuri includes simple network connectivity monitor. It can monitor your internet connection by pinging some external server. Command can be run if network link goes up or down. Example script to send an email to root is included in doc directory. Another example is multi-ISP configuration and script.

Minimal configuration is:

target google {
  command fping 8.8.4.4
}

This creates monitor called google and runs fping command pinging IP 8.8.4.4 every second. Foomuuri parses its output and logs up and down events. Multiple targets can be defined.

Better real life example is:

target my-isp-router {
  command      fping --interval 2000 172.25.31.149
  command_up   /etc/foomuuri/monitor.event
  command_down /etc/foomuuri/monitor.event
}

This pings IP 172.25.31.149 every two seconds and runs monitor.event script when link goes up or down. That script sends an email to root. See man fping or their website for description of fping parameters.

"Up" and "down" are defined with parameters:

target my-isp-router {
  history_size     100       # how many results are saved
  history_up       80        # count of UPs => n     => UP
  history_down     30        # count of DOWNs >= n   => DOWN
  consecutive_up   20        # last n were UP        => UP
  consecutive_down 10        # last n were DOWN      => DOWN
  ...
}

Target is considered up if 80 of the last 100 pings were successful (allowing failures in between) and last 20 pings were successful (no failures allowed).

Target is considered down if 30 of the last 100 pings were failures or last 10 pings were failures.

curl and other programs can also be used instead of fping. See example shell script how to use them.

group

Multiple network connectivity monitor targets can be grouped to single monitor. Example:

group my-isp-group {
  target       my-isp-router google
  command_up   /etc/foomuuri/monitor.event
  command_down /etc/foomuuri/monitor.event
}

This creates monitor called my-isp-group which includes two targets. Group is considered up if any of the targets is up. It is considered down if all of the targets are down.

Miscellaneous

Comments can be written as # comment.

Long line can be split to multiple lines by adding \ to end of line.

Multiple words can be combined to single word by writing them in quotes. For example ssh accept log "accept ssh for testing" will accept SSH traffic with log message accept ssh for testing.

Macro expansion can be skipped by writing word in quotes. For example "ssh" is kept as ssh and not expanded to tcp 22.

Output from external command can be used to generate rules. It can return single line, multiple lines or part of line. Syntax is $(shell command to run). Command is run with shell so pipes and ; will work. Be careful to run only trusted commands.

Example:

# This is a comment

macro {
  # Define local_port_range macro by reading correct value from /proc file
  local_port_range $(shell sed s/\\t/-/ < /proc/sys/net/ipv4/ip_local_port_range)
}

localhost-public {
  ssh  # This is a comment
  smtp \
    daddr 192.0.2.32  # Allow SMTP to single IP
}

public-localhost {
  tcp local_port_range  # Allow TCP to ports 32768-60999 (default range)
}