Mail servers are surprisingly bad at blocking spam without help, and nftables is a surprisingly flexible tool to give them that help.
Let’s watch nftables in action, protecting an smtpd process. Imagine we have a server running Postfix, and we want to expose SMTP (port 25) to the world, IMAP (port 143, 993) for clients, and have our own mail server be able to perform DKIM signing and verification. We’ll use nftables to manage this.
Here’s a basic nftables configuration to get us started:
table ip filter {
chain input {
type filter hook input priority 0; policy accept;
# Allow loopback traffic
iifname "lo" accept
# Allow established and related connections
ct state established,related accept
# Allow SSH for management
tcp dport 22 accept
# Allow SMTP inbound
tcp dport 25 accept
# Allow IMAP and IMAPS
tcp dport { 143, 993 } accept
# Drop everything else
drop
}
}
This is a simplified view. A real-world scenario would involve more nuance. The table ip filter defines a set of rules for IP packets. The chain input hooks into the incoming packet path. policy accept means by default, packets are allowed unless explicitly dropped. We then add rules to accept specific traffic: loopback, established connections, SSH, and our mail ports. Finally, drop explicitly discards anything that hasn’t matched a preceding accept rule. This is crucial for security.
The problem nftables solves here is providing granular control over network access to services. Without it, your mail server might be exposed to an unnecessarily large attack surface. For instance, allowing all incoming ports would make it vulnerable to services you don’t intend to run. nftables acts as a gatekeeper, ensuring only authorized traffic reaches your mail server processes.
Internally, nftables processes packets sequentially against the rules in a chain. When a packet matches a rule’s criteria (like protocol, port, or connection state), the action specified in that rule (e.g., accept, drop, reject) is taken. If no rule matches, the chain’s default policy is applied. This deterministic, rule-based approach allows for precise control.
Here are the levers you control:
- Tables: Collections of chains. You might have one for IP (
ip), one for IPv6 (ip6), or one for MAC addresses (ether). - Chains: Ordered lists of rules. Chains have types (
filter,nat,route) and hook points (input,output,prerouting,postrouting,inputfor the network stack). - Rules: The core of the configuration. Each rule has a set of matches (e.g.,
tcp dport 25,ip saddr 192.168.1.0/24) and a statement (e.g.,accept,drop,reject,log). - Sets: Named collections of elements (IP addresses, ports, etc.) that can be referenced in rules, making configurations cleaner and more efficient.
- Maps: Similar to sets but allow associating values with elements, useful for complex NAT or port translation.
Now, let’s consider DKIM. For a mail server to sign outgoing mail with DKIM, it needs to access its private key. This key is typically stored in a file. The mail transfer agent (MTA) process, often running as a specific user (e.g., postfix or smmsp), needs read access to this file. nftables doesn’t directly manage file permissions, but it can ensure the MTA process itself is protected.
A common mistake is to assume that just opening port 25 is enough. However, a compromised mail server can be used for spam relay. nftables can help mitigate this by being more restrictive. For example, if your mail server only needs to send mail to specific IP ranges or domains, you can add rules to nftables to enforce this.
Consider this more advanced rule for outgoing SMTP:
table ip filter {
chain output {
type filter hook output priority 0; policy accept;
# Allow loopback
oifname "lo" accept
# Allow established/related
ct state established,related accept
# Allow outgoing SSH
tcp dport 22 accept
# Allow outgoing SMTP to specific trusted relays
tcp dport 25 ip daddr { 1.2.3.4/24, 5.6.7.8 } accept
# Allow DNS queries
udp dport 53 accept
tcp dport 53 accept
# Drop other outgoing SMTP if not explicitly allowed
tcp dport 25 drop
}
}
This output chain rule restricts outgoing SMTP to only specific IP addresses. This is a powerful way to prevent your server from becoming an open relay for spam if its security is breached. The ip daddr { 1.2.3.4/24, 5.6.7.8 } part specifies the allowed destination IP ranges.
The one thing that trips people up with nftables is its extensibility and the sheer number of options. You can match on TCP flags, packet sizes, and even use Lua scripts for highly custom packet inspection. For DKIM, while nftables itself doesn’t perform the signing, it can ensure that only the MTA process, running with the correct user, has network access to send mail, and that this access is restricted to legitimate destinations. If your MTA process tries to connect to a port or IP that nftables blocks in the output chain, the connection will fail.
The next thing you’ll likely want to tackle is rate limiting, to prevent brute-force attacks on your mail server’s authentication ports or to mitigate DoS attempts.