Example for ufw

Paolo Predonzani edited this page Dec 27, 2017 · 2 revisions

Ufw - the UncomplicatedFirewall - is a popular, simple firewall for Linux. This page shows how to use Tstconfig to test its configuration automatically.

Testing ufw's status header

Ufw's runtime configuration can be queried as follows:

$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22                         LIMIT IN    Anywhere
22 (v6)                    LIMIT IN    Anywhere (v6)

The first four lines (the header) present the general configuration of ufw. The rest of the lines are the rules, which we'll look at in a section below.

To test the header, create a definition file called ufw.tstconfig with the following content:

# The command to query ufw's runtime configuration
command sudo /usr/sbin/ufw status verbose

# read the first four lines, ignore everything else
read_lines 4

# The syntax is a simple key-value map
parse_mode keyvalue

# The key and values are separated by ':'
key_separator :

Now we're ready to add some assertions:

# Check that status is active
property Status
assert_eq active

# Check the default filtering rule
property Default
assert_eq deny (incoming), allow (outgoing), disabled (routed)

As you can see, property selects the property to be tested and assert_eq tests for a specific value.

To run the tests, save the file and execute:

$ tstconfig ufw.tstconfig

If you get a report like the following:

Tstconfig 0.2

Reading definition file: ufw.tstconfig
ASSERTION FAILED
 Command:   sudo /usr/sbin/ufw status verbose
 Property:  Default
 Value:     allow (incoming), allow (outgoing), disabled (routed)
 Assertion: assert_eq deny (incoming), allow (outgoing), disabled (routed)


SUMMARY REPORT: FAIL
Assertions tested: 2
Assertions passed: 1
Assertions failed: 1
Errors: 0

...then it means that some assertions failed (for property Default in the example). Reconfigure ufw accordingly and rerun the test. You should finally get a success report like the following:

Tstconfig 0.2

Reading definition file: ufw.tstconfig

SUMMARY REPORT: PASS
Assertions tested: 2
Assertions passed: 2
Assertions failed: 0
Errors: 0

Testing the firewall's rules

Let's go back to the output of sudo ufw status verbose:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22                         LIMIT IN    Anywhere
22 (v6)                    LIMIT IN    Anywhere (v6)

After the header, we find a table presenting the currently configured rules. The table is made of three columns (To, Action and From). The table is formatted so that the values start at fixed positions (0, 27 and 39). The interesting stuff starts al line eight, so the first seven lines can be ignored.

With this in mind, we're ready to add a few lines to the ufw.tstconfig file:

### Test the firewall rules
# Re-read the runtime configuration
command sudo /usr/sbin/ufw status verbose

# This time ignore the first seven lines then read the rest
skip_header_lines 7

# A fixed-position table with values starting at position 0, 27 and 39 
parse_mode fixed
positions 0 27 39

When the format is a fixed-position table, the first column is interpreted as the property name, while the rest of the columns as property values. We're now ready for the assertions:

property 22 
assert_eq "LIMIT IN" Anywhere

property "22 (v6)"
assert_eq "LIMIT IN" "Anywhere (v6)"

If we want to check that these are the only two rules, we can add the following assertion:

section_size
assert_eq 2

Run the test as usual with tstconfig ufw.tstconfig

A more complex situation

In the previous example, ufw was configured to allow traffic directed to ssh. If we had other rules for other protocols (http, smtp, etc) the situation would be conceptually similar and we could add more assertions to test these other rules.

Instead, a more interesting scenario is when we have more than one rule for the same port. Let's assume we had the following firewall rules:

To                         Action      From
--                         ------      ----
22                         LIMIT IN    Anywhere
22                         DENY IN     192.168.56.102
22 (v6)                    LIMIT IN    Anywhere (v6)

There are two rules for port 22. One is the generic rule that applies to all From addresses (Anywhere). The other applies only to address 192.168.56.102. We need a way to test the values not simply for a given port, but for a combination of port and From address.

The solution is to tell Tstconfig to rearrange the columns in an order that is more useful for our purposes:

columns 0 2 1

This command re-maps the columns so that they can now be read in this order: To, From, Action. We can now write the assertions in a different way:

# Check the combination of port 22 / from Anywhere
property 22 Anywhere
assert_eq "LIMIT IN" 

# Check the combination of port 22 / from 192.168.56.102
property 22 192.168.56.102
assert_eq "DENY IN" 

# Check the combination of port 22 (v6)/ from Anywhere (v6)
property "22 (v6)" "Anywhere (v6)"
assert_eq "LIMIT IN"

Notice the peculiar use of property and assert_eq. In a table with three columns (To, From, Action), the first two (To, From) are used as a property name while the last one (Action) as the property value.

Putting it all together

The complete ufw.tstconfig should now look like shown below. Feel free to customise it to your needs:

### Test the status header
# The command to query ufw's runtime configuration
command sudo /usr/sbin/ufw status verbose

# read the first four lines, ignore everything else
read_lines 4

# The syntax is a simple key-value map
parse_mode keyvalue

# The key and values are separated by ':'
key_separator :

# Check that status is active
property Status
assert_eq active

# Check the default filtering rule
property Default
assert_eq deny (incoming), allow (outgoing), disabled (routed)

### Test the firewall rules
# Re-read the runtime configuration
command sudo /usr/sbin/ufw status verbose

# This time ignore the first seven lines then read the rest
skip_header_lines 7

# A fixed-position table with values starting at position 0, 27 and 39 
parse_mode fixed
positions 0 27 39

# Rearrange the columns so they are: To From Action 
columns 0 2 1

# Check the combination of port 22 / from Anywhere
property 22 Anywhere
assert_eq "LIMIT IN" 

# Check the combination of port 22 / from 192.168.56.102
property 22 192.168.56.102
assert_eq "DENY IN" 

# Check the combination of port 22 (v6)/ from Anywhere (v6)
property "22 (v6)" "Anywhere (v6)"
assert_eq "LIMIT IN"

# Check that there are only three rules
section_size
assert_eq 3
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.