The iplock
project is an iptables
firewall extension managing the
rules and allowing for instantly blocking an IP address when various
events happens.
The libiplock
offers a way to send a message to the ipwall
service
to add and remove IP addresses to the firewall. The ipwall
service uses
the iplock
command line to update the firewall lists as required.
The project comes with configuration files which control how IP addresses are added and removed from the firewall.
The iplock
project comes with a console and graphical set of tools used
to edit and setup the firewall (?). This tool makes use of configuration files
that are read using advgetopt and generates a script supported by iptables.
The configuration files are saved under /etc/iplock/firewall
. These are
used to setup the base firewall which the iplock
can later use to
dynamically add and remove rules and IP addresses of what looks like
hacking attempts.
The iplock project makes use of the advgetopt to load configuration files
from the /usr/share/iplock/ipload
folder. The rules are ordered using the
before
, after
, and section
names defined within the rules.
This allows us to define the order in which the rules have to be loaded
in the firewall which, obviously, is very important.
The before
/after
parameters work within a section
. It is otherwise
ignored. The section
allows us to have groups of rules such as
"localhost"
, "tools"
, and "footer"
. The sections are sorted using
their own before
and after
parameters.
By default the ipload tool marks all the chains with "DROP". This means we have to have an "ACCEPT" rule for anything that we want to accept. This works by adding intermediate files in the .d folder.
This technique allows other projects to add their own configuration files to open or block the ports they manage.
Addresses written using a domain name are converted to an IP address at the time ipload loads the configuration data. iptables does the same thing when it gets loaded.
This means, if the domain name IP addresses change, you can simply reload your firewall. However, while running, it will not automatically switch from the old address to the new one.
Note: simply reload... as you know, the DNS services cache IP addresses. Reloading your firewall too soon may not pick the new address.
The following shows the list of supported tags (see details below):
Note: This is being moved to the 'ipload' man page
[variables]
<variable-name> = <value>
...
Some entries accept lists, which in most cases means that multiple iptable rules are created to handle one entry.
The log
variable means that we add a -j LOG
rule before the other
rule(s). In iptables parlance, this is an action. We simplify for you
having a single rule implementing both features at once.
Some of the rules parameters support plural and singular names when one or more can be specified.
Sections do not exist in iptables. We use such to automatically sort rules in groups. We can more easily sort rules in the correct order for the final list of rules to upload to the iptables.
The sections support a name. By placing a rule in a section, it is simply added at the end. You can also force your rule(s) to appear before or after another rule if necessary.
Note that sections are not specific to a chain. Rules appearing in different chains can be placed in the same section.
Define a prefix referencing the table from within the chain names.
If the chain name starts with this prefix, then it is added to this table.
The ending '_'
is not expected to be included in the prefix. It is still
safe to have it here.
The prefix should be the same as the table name: filter
, nat
, etc.
The <table-name>
parameter should be a valid name that the iptables system
understands.
Enter a description for this rule.
At the moment nothing is done with this description. It will be displayed in graphical editors once such are made available. It is customary to put the value between double quotes although it is not a requirement.
This parameter defines the list of chains that are to receive this rule.
Chain names include built-in chains (INPUT, OUTPUT, etc.) and user defined
chains. User defined chains do not need to be pre-defined. The tool will
automatically create them as required and apply defaults as defined in
each chain::...
variable.
The name of chains is case sensitive. So it must be INPUT
if you want to
add to the built-in INPUT
chain. It is customary to use lowercase of user
defined chains to avoid any future potential clashes.
Default: none, this is a required parameter, you need to have at least one
chain to which the <name>
rule applies.
The name of the section in which to add this rule.
A single name is allowed.
The named section must exist.
If no section name is defined, the rule is added in the default section of the corresponding chain.
The name of one or more rules that must be added after this one.
If the named rules are not defined, then that name is ignored.
The name of one or more rules that must be added before this one.
This parameter allows you to sort your rules as expected in the final output.
If the named rules are not defined, then that name is ignored.
This parameter is used to conditionally exclude the rule.
At the moment, I do not have a functional as2js script parser and execution environment. So I limit the functionality to one equality like so:
"..." == "..."
"..." != "..."
The "..."
can be literals or variables. For example, to use that rule only
if a private_interface
variable is defined you can write:
condition = '"${private_interface}" != ""'
If this is true
, then the rule is generated. Otherwise it is ignored.
WARNING: You must quote the condition otherwise the start and end quotes get removed and the expression is thus invalid. If some of your variables include quotes, you will probably not be able to test them properly with this version.
No condition (or an empty condition) is equivalent to true
.
This parameter defines the list of interfaces that the rule applies to.
Since this parameter does not specify a source or a destination, it can
be used with rules that have INPUT
and OUTPUT
as chains. The ipload
code will know whether to use the "-i" or "-o" option.
Note that works with the FORWARD
chain. However, it is very unlikely
that is what you want since 99.9% of the time, the input and output
interfaces are different (i.e. eth1
to eth2
). In that case you want
to define both: the source_interface
and the destination_interface
parameters.
The name of set can be specified. This rule generates a test that executes the given action when the source IP address matches one of the set IP addresses.
At the moment, we only supporrt the simplest type of set. It allows you to test up to 65535 addresses at lightning speed (compared to adding so many addresses in your iptables directly).
ipload will automatically create the set if it does not yet exist. The iptables require the set to exist to properly load such a rule.
This parameter defines the list of interfaces that the rule applies to.
Default: none, which means the rule applies to all interfaces.
This parameter defines a list of IP addresses or domain names to use to filter the incoming network traffic.
Default: no source is checked and the rule is accepted whatever the source is.
This parameter defines a list of IP addresses or domain names to not
match when this rule is checked. If the source matches, then that rule
will be skipped. This is particularly useful in the FORWARD
chain to
avoid having your own traffic forwarded.
Default: none.
This parameter defines a list of source ports allowed to connect. The number of ports is not limited. It will be broken up in group of 15 to create corresponding iptables rules.
If you used the except_sources
, then the ports are also exceptions.
Default: none.
This parameter defines the list of interfaces that the rule applies to.
Default: none, which means the rule applies to all interfaces.
This parameter defines a list of IP addresses or domain names to use to filter the outgoing network traffic.
Default: no destination is checked and the rule is accepted whatever the destination is.
This parameter defines a list of IP addresses or domain names to not match when this rule is checked. If the destination matches, then that rule will be skipped.
Default: none.
This parameter defines a list of ports to go with this rules. The number of ports is not limited. It will be split in lists of 15 per iptables rule.
If you used the except_destinations
parameters, then those ports are also
exceptions.
Default: none.
This parameter defines which protocol is necessary to match this rule.
IMPORTANT NOTE: The icmp
protocol is for IPv4 and IPv6. When used,
ipload
automatically adds a rule with ipv6-icmp
. It is very unlikely
that you would need icmp
and not ipv6-icmp
or that having both would
be unsafe. If you directly specify ipv6-icmp
then no rule with icmp
gets added and the rule is specific to IPv6 so it does not get added as
an IPv4 rule.
Default: no protocol is matched meaning that all packets get checked.
The state or type of the packet for its target to be accepted by the rule.
We currently support states for:
- Connection state (
-m state --state ...
) - TCP flags (
--tcpflags
) - ICMP types (
--icmp-type
)
You can use a set of states within one rule by separating each state with a
|
character. You can use the '!' operator to negate a flag or !(...)
to
negate a series of flags. Note that the established
and related
cannot
be negated (the '!' against these two are ignored). You can separate
flag names and operators by any number of spaces.
Separate sets are defined between commas (i.e. commas mean that the states are going to be used in separate rules).
The supported syntax looks like so:
start: mask_compare
| start ',' mask_compare
mask_compare: flag_list
| flag_list '=' flag_list
flag_list: flag_name
| flag_list '|' flag_name
flag_name: 'syn'
| 'ack'
| 'fin'
| 'rst'
| 'urg'
| 'psh'
| 'new'
| 'old'
| 'all'
| 'none'
| 'established'
| 'related'
| 'timestamp-request'
| 'any'
| '(' flag_list ')'
| '!' flag_name
Example:
new, ack|fin, established|!(syn|ack|urg), !syn
Warning: the !
operator is probably not working as expected. If applied to
a single flag, it still applies to the whole set of flags. So the following
are equivalent:
!(syn|ack|urg)
!syn|ack|urg
Only there isn't an easy way I can think of at the moment to prevent the
second syntax. I may later create a node for each entry found in the
expression and reconsiliate the results at the end. Then it would be possible
to detect such inconsistencies (and invalid uses such as !related
).
We currently support:
-
new
The packet must be considered "new", this means it is a TCP connection attempt. It is not available with UDP.
This is equivalent to
--syn
in the iptables parlance. -
old
The opposite of the "new" state. This means the packet must not be a TCP connection attempt. It is not available with UDP.
This is equivalent to
! --syn
in the iptables parlance. -
established
orrelated
Right now, these two are viewed as synomyms. It is used to accept packets that represent an established connection and let them go through early on. This works with TCP and UDP.
In most cases, you want to use
!new
along these flags:state = establised | related | !new
-
syn
The TCP SYN signal.
-
ack
The TCP ACK signal.
-
fin
The TCP FIN signal
-
rst
The TCP RST signal.
-
urg
The TCP URG signal
-
psh
The TCP PSH signal
-
all
This represents all the TCP signals. This is useful for the mask.
-
none
This represents none of the TCP signals.
-
(...)
Group a set of flags. This is useful to negate a set of TCP flags. (i.e.
!(syn|ack)
). -
!<flag>
Negate a TCP flag. Note that this flag has no meaning against flags that are not TCP flags. (i.e.
established
,related
, etc.) -
timestamp-request
An
--icmp-type
to match against. -
any
An
--icmp-type
to match against.
Default: none. Whatever the state, the rule passes.
This parameter defines two numbers.
- Number of allowed connections
The first number can be preceeded with <=
(or just <
) to represents the
minimum number of connections (i.e. --connlimit-upto
). In most cases, this
is used with the ACCEPT
target. It is also often used to enter a user
defined chain.
If no operator or the '>operator is used, then it represents the maximum number of connections (i.e.
--connlimit-above). This rule is most often used with a
REJECT. It can also be used with a
DROP`.
The second number is optional. If defined, it represents a mask applied against the matching host. It creates a group and the counters count the number of connections within that entire group.
The second number can be preceeded by the ->
operator, in which case
the group is generated using the destination IP address (rarely used).
The default is to use the source address. Optionally, you can make this
explicit using the <-
operator.
Example:
limit = >32,->24
Becomes
--connlimit-above 32 --connlimit-mask 24 --connlimit-daddr
Default: no limit enforced.
This parameter defines what to do when the rule is a match.
We support the following actions:
-
ACCEPT
On a match, the packet is accepted.
-
DROP
On a match, the packet is dropped. This means our computer stops working on that packet immediately. No more wasting time or bandwidth.
This should be used to block public traffic (from computers you do not own, i.e. the rest of the Internet). For local traffic, it is nicer to use the
REJECT
option. -
REJECT [<type>]
On a match, the packet is rejected. This means our computer builds a reponse so we can tell the client that we do not want their traffic. This is a nice way to refuse data. However, this makes use of more of your bandwidth and lets the client know that you exist.
In most cases, especially on the Internet, it is wiser to use
DROP
. Limit your use of theREJECT
action to just local traffic.The type is one of the available reject type. See the
--reject-with
docs. We like to useicmp-port-unreachable
which says that the port is not open to the other side, whether it is open or not.This should be used to block connections between your systems on your private network (10.x.x.x, 192.168.x.x, etc.) so that way improper attempts at connection fail quickly.
-
CALL <chain>
This action is used to call a user defined chain. This is particularly useful when you want to check a set of rules from multiple places. For example, we have a set of bad TCP packets which we like to check on
INPUT
andOUTPUT
. -
LOG
The action is rarely used since in most cases you just enter a log message with an existing action (see
rule::<name>::log
). However, there may be cases where you only want to log a message and not otherwise do anything with the rule. In that case, use this action.
A log message assotiated with the rule. It gets sent to the iptables logs if the rule matches.
WARNING: This is to be used with parsimony. Do not add a log to each one
of your rules. In most cases you want to add this to rules that you REJECT
or DROP
. Logging all your rules will likely fill up your disk space very
quickly. It is, however, quite useful while debugging your firewall.
Default: none.
The configuration can reference variables. These are lists of IP addresses, domain names, port numbers, etc.
For example, when specifying an IP address, you may use a variable such as:
rule::<name>::sources = ${admin_ips}
This allows us to write one rule for all the administrators (opposed to one rule per administrator) and with any number of variables, all the users of any given group.
To support multiple lists in variables, separate them with commas:
rule::<name>::destinations = ${admin_ips}, ${lan_users}, ${clients}
Using variables enables you to write rules that do not require to be handled individually.
We want to have the list of IP addresses sorted and check whether we can use a mask in order to optimize the number of rules. So if you use 16 IP addresses for your local network and they all match a given mask then we can use one rule instead of 16.
There is an interesting side effect with lists of ports. These can actually be used as is in a rule (i.e. certain rules support multiple ports). The number of ports in an iptable rule is limited, but our software takes care of handling that limit.
Note that this is a sort of optimization rather than a feature. a.k.a. If possible we optimize the number of rules by using the multi-port feature.
The following lists a set of interfaces that you just want to block completely:
rule::blocked_interfaces::chains = INPUT, OUTPUT
rule::blocked_interfaces::interfaces = eth3, eth4
rule::blocked_interfaces::action = DROP
rule::blocked_interfaces::log = interface unavailable
This rule prevents all communications (in and out) from eth3
and
eth4
.
When the iplock firewall starts:
-
it reads all the configuration files
-
it transforms the configuration files in an iptables script
-
if it detected sets of IPs while handling the configuration files, read the corresponding IPs from the database and add them to the sets
-
it runs the iptables script
Later, the iplock system is sent new IPs to block or unblock via the firewall service:
-
BLOCK
-- add the IP to the database and the firewall; this is done by adding the IP to one of the sets; if the set is full, then a new set is created and added to the firewall -
UNBLOCK
-- remove the IP from the database and the firewall; this is done by removing the IP from one of the sets; if the set is empty and there are more than one for the same set, it gets removed (i.e. if we had to create a second set of an overflow, we want to remove such additional sets)
The iplock
tool is used to make edits to the iptables
rules.
The ipcontroller
service is used to accept / receive BLOCK
and UNBLOCK
messages, manage the database, and run the iplock tool to update the rules.
The ipload
tool reads configuration files and build the iptables
rules.
The following image shows the flow of packets in three of your iptables.
(Source: https://www.frozentux.net/iptables-tutorial/chunkyhtml/c962.html)
Some links to various helpful documents about the iptables firewall system:
https://www.frozentux.net/iptables-tutorial/chunkyhtml/index.html
Note that when you uninstall iplock
from your system, it does not
automatically remove the IP addresses that it was asked to block.
This is for clear security reasons. This is not a bug. It is expected
that if you uninstall our tool, you are probably going to install
another tool which will re-add all or some of those IP addresses
to your firewall.
If you really want to clear you firewall completely, you can use the following command to flush everything:
sudo iptable -F
Submit bug reports and patches on github.
This file is part of the snapcpp project.
vim: ts=4 sw=4 et