Ubicloud Firewalls: How Linux Nftables Enables Flexible Rules

March 18, 2024 · 4 min read
Furkan Sahin
Senior Software Engineer

Ubicloud is an open and portable cloud. Networking is an important part of what we do; and includes features such as virtual networking, encryption in transit, and firewalls.

To implement these features, Ubicloud’s dataplane reprograms the Linux bare metal host’s networking stack, including changes to routing tables and packet filtering. When you create a virtual machine (VM) on the host, the VM’s networking features then come from these changes.

One of Ubicloud’s networking features is firewalls. In this blog post, we’re going to describe how we implemented firewalls using the Linux kernel’s Netfilter / Nftables subsystems. We’ll also share our implementation (122 lines of code) and ask for any input into our design.

Nftables: Packet Filtering in the Linux Kernel

Nftables provides a granular way to process each packet coming to the Linux kernel. Our blog post on Improving Network Performance with Linux Flowtables gives more information about the Nftables design and hooks available in the kernel for packet processing.

For firewalls, we’ll start with a simple example, where we want to block all traffic from a specific IP address (123.123.123.123). For this, we only need to add the following Nftables rule.


ip saddr 123.123.123.123 drop
   

ip saddr 123.123.123.123 drop

Of course, in the real world, we don’t want to add rules individually for each IP address or CIDR. To simplify our implementation, Ubicloud uses Nftables sets. These give you the ability to group different IP addresses together and then refer to this grouping with a single line rule. For example, let’s define a set named “allowed_ipv4_cidrs”.


set allowed_ipv4_cidrs {
	type ipv4_addr;
	flags interval;
	elements = {123.123.123.0/24, 10.0.0.0/8 }
}
   

set allowed_ipv4_cidrs {
type ipv4_addr;
flags interval;
elements = {123.123.123.0/24, 10.0.0.0/8 }
}

Now, to use this set, we can add a single line that allows packets from these CIDRs listed in the elements part of the set.


ip saddr @allowed_ipv4_cidrs accept
   

ip saddr @allowed_ipv4_cidrs accept

As an open source cloud, we also need a way to selectively allow ports in combination with IP addresses. For example, our PostgreSQL offering needs to block all the ports, except 5432. A second cloud service may need to allow port ranges from 1 to 100 for a CIDR. In that scenario, we can modify the set to include port ranges with a simple addition.


set allowed_ipv4_port_tuple {
	type ipv4_addr . inet_service;
	flags interval;
	elements = {123.123.123.0/24 . 5432-5432, 10.0.0.0/8 . 1-100}
}
   

set allowed_ipv4_port_tuple {
type ipv4_addr . inet_service;
flags interval;
elements = {123.123.123.0/24 . 5432-5432, 10.0.0.0/8 . 1-100}
}

We could then add this accept set with the following simple rule.


ip saddr . tcp dport @allowed_ipv4_port_tuple accept
   

set allowed_ipv4_port_tuple {
type ipv4_addr . inet_service;
flags interval;
elements = {123.123.123.0/24 . 5432-5432, 10.0.0.0/8 . 1-100}
}

Dynamic Firewall Rules with Nftables and Conntrack

Ubicloud customers need more than just allowing or blocking traffic. Yes, it's crucial to block unauthorized internet packets from reaching a virtual machine (VM). However, we also need that VM to freely communicate with the internet for tasks like downloading software packages. Addressing this challenge requires a nuanced approach beyond simple allow or drop rules.

This is where the combination of Nftables and the “Conntrack” utility comes into play. The rules we've discussed previously work on a stateless basis—they don't keep track of the connection's history. Stateless rules alone would force us into a corner, either blocking or allowing traffic based on static conditions. This isn't practical for dynamic cloud environments.

To navigate this complexity without overcomplicating our firewall configurations, we leverage Conntrack. Conntrack is part of the Linux network stack, specifically part of the firewall subsystem. It allows us to create stateful firewall rules. These rules understand the network traffic’s context, and enables us to manage packets based on the connections’ states.

For example, let’s say we wanted to distinguish between new incoming connections and responses to requests initiated by a VM with the IP address 11.11.11.11. To implement stateful filtering, we would just need to add Conntrack’s “ct” parameters to our Nftables filtering rules.


ip saddr 11.11.11.11 ct state established,related,new accept
ip daddr 11.11.11.11 ct state established,related accept
   

set allowed_ipv4_port_tuple {
type ipv4_addr . inet_service;
flags interval;
elements = {123.123.123.0/24 . 5432-5432, 10.0.0.0/8 . 1-100}
}

These rules mean that a packet that is related to any new, already established or a related connection coming out of our VM is accepted. In return, packets destined to our VM are accepted when they are part of an already established or a related connection, not a new one. Therefore, the new connections coming from the internet to the VM are not accepted but the bidirectional communication that is initiated by the VM is all allowed.

How Ubicloud Implements Firewall Rules

Nftables and Conntrack features give us the primitives we need to implement firewall rules. At Ubicloud, we define an individual network namespace for each VM running on a host. So, we can manage individual Nftables in an isolated environment for each VM. Ubicloud’s firewall implementation starts by creating a fresh table in Nftables in the VM’s dedicated namespace, and ensures that any previous configurations are cleared to avoid conflicts. We then populate this table with our predefined sets and rules and update them according to customers configuration, later on.

For example, our firewall rules include conditions for accepting or rejecting traffic based on the source address or port ranges. We can also easily improve this model to support egress firewalls or additional protocols. These features give us a lot of flexibility in protecting against unauthorized access and potential attacks. For now, to keep things simple, we started with only ingress traffic and applying the same rules to both TCP and UDP protocols on our IPv4 and IPv6 stacks. Thanks to Nftables and Conntrack, we could implement all of this in 122 lines of code:

https://github.com/ubicloud/ubicloud/blob/main/prog/vnet/update_firewall_rules.rb

Ubicloud Firewalls - Key Properties

We had three design goals in mind for Ubicloud Firewalls: simple, portable, and flexible. By using open source projects, in particular Nftables and Conntrack modules in the Linux kernel, we were able to build a solution that runs anywhere where Linux is available. Given how well these projects were designed, we could also do this in 122 lines of code.

We also think that Linux’s network stack evolved to the point that it can now support cloud use-cases. Here’s a breakdown of Ubicloud Firewalls’ key properties:

  • IP Address and Port Sets: We define sets for allowed IPv4 and IPv6 CIDRs, as well as port tuples for managing access to services running on VMs. These sets enable us to specify which traffic is permitted, based on source addresses and destination ports.
  • Private Network Ranges: To ensure internal traffic flows securely, we also define sets for private IPv4 and IPv6 CIDRs. This allows for the segregation of internal and external traffic, a fundamental practice for maintaining network security.
  • Flowtable Integration: Flowtables is an optimization that allows us to process packets faster. For more on them, you can read our previous blog post.

Conclusion

At Ubicloud, we use Nftables and Conntrack as a key part of our network security strategy. Over the past decade, the Linux kernel has evolved and received many improvements. What wasn’t possible when public clouds took off, what Cloudflare called dark edges four years ago, is now mainstream. Although it may not be groundbreaking, the practical application of Nftables allows us to maintain a secure and efficient networking environment for Ubicloud customers.

As always, we’re open to feedback and questions from our users and the tech community. If you have any thoughts or inquiries about our network security practices, feel free to reach out at info@ubicloud.com. Or, you can simply try our hosted version to see how the firewall rules are integrated into our products like VMs or fully managed PostgreSQL instances in console.ubicloud.com.