Firewalls are no different, you shoot for an optimal balance between operability and security. You don’t want to fiddle with the firewall everytime there’s a new update to install or everytime a new application is deployed. Instead you want to have a firewall that protects you from:
- The malicious entities outside
- The vulnerable applications running inside
The default configuration of UFW can help us understand how to achieve this balance.
Default Configuration
If you enable the UFW on a newly installed server, out-of-the-box, the default settings would:
- Allow any outgoing connections
- Deny any incoming connections
It’s worthwhile to understand the reason behind this. People install all sorts of software on their system. The package managers continuously need to sync with official repositories and fetch updates, this is usually automated. Moreover, new security patches are as important to the security of the server as is the firewall itself, so blocking outgoing connections seems like an unnecessary obstruction. Incoming connections can, like port 22 for SSH, on the other hand can cause serious trouble. If you are not using a service like SSH, there’s no point in having that port open.
This configuration is not bulletproof by any means. Outgoing requests can also result in applications leaking crucial information about the server but most of the applications are restricted to their own little slice of file system and don’t have permission to read any other file in the system.
ufw allow and ufw deny
The allow and deny subcommands for ufw are used to implement firewall policies. If we want to allow incoming SSH connections we can simply say:
If we want we can explicitly state whether the allow rule is for incoming (ingress) or outgoing (egress).
If no direction is supplied then it is implicitly accepted as a rule for incoming request (part of the simple syntax). Outgoing requests are allowed by default anyways. When we mention things like ingress or egress, it constitutes a full syntax. As you can tell from the name it is more verbose than the simple counterpart.
Protocol
You can specify protocol by adding a /protocol next to the port number. For example:
TCP and UDP are the protocols that you need to concern yourselves with, for the most part. Notice the use of deny instead of allow. This is to let the reader know that you can use the deny to prohibit certain traffic flows and allow to permit others.
To and From
You can also whitelist (allow) or blacklist (deny) specific IP addresses or range of addresses using UFW.
$ ufw deny in from 172.19.0.0/16
The latter command will block incoming packets from IP address from the range of 172.19.0.0 to 172.19.255.255.
Specifying Interfaces and Forwarding Packets
Sometimes the packets are not for the consumption of the host itself but for some other system and in those cases we use another keyword route followed by allow or deny. This fits nicely with the specification of interface names in ufw rules as well.
Although you can use interface names like ufw allow 22 on eth0 independently, the picture fits together quite well when we use route along with it.
The above rule, for example, forwards incoming requests from eth0 (ethernet interface) to a virtual interface docker0 for your docker containers. Now your host system has an extra layer of isolation from the outer world and only your containers deal with the dangers of listening on incoming requests.
Of course, the main use for packet forwarding is not to forward packets internally to the containers but to other hosts inside a subnet.
UFW Deny VS UFW Reject
Sometimes the sender needs to know that the packet was rejected at the firewall and ufw reject does exactly that. In addition to denying the packet to go forward to its destination, the ufw reject also returns an error packet back to the sender saying that packet was denied.
This is useful for diagnosis purposes as it can tell the sender directly the reason behind the dropped packets. When implementing rules for large networks it is easy to block off the wrong port and using reject can tell you when that has happened.
Implementing your rules
The above discussion revolved around the syntax of the Firewall but the implementation would depend on your particular use case. Desktops at home or office are already behind a firewall and implementing firewalls onto your local machine is redundant.
Cloud environments on the other hand are much more insidious, and services running on your VM can inadvertently leak information without proper firewalls in place. You have to think of various edge cases and carefully weed out all the possibilities if you want to secure your server.