The core issue is that SSH brute-force attempts are overwhelming your server’s authentication mechanisms, leading to performance degradation or outright denial of service. nftables can be configured to detect and block these attacks at the network level before they even reach the SSH daemon.
Here’s how to set up nftables for SSH brute-force protection, covering the most common causes and solutions:
Cause 1: Repeated Failed SSH Login Attempts
Diagnosis:
You’ll see a flood of connection attempts in your SSH logs (/var/log/auth.log or similar) from various IP addresses, many of which fail authentication.
Command:
sudo grep "Failed password" /var/log/auth.log | wc -l
This will show the total number of failed login attempts.
Fix:
We’ll use nftables’ meter and limit features to track connection attempts per IP address.
-
Create a meter: This meter will count connection attempts.
sudo nft add table inet ssh_brute_force sudo nft add chain inet ssh_brute_force_meter { type filter hook input priority -10 \; } sudo nft add rule inet ssh_brute_force_meter ip saddr @ssh_brute_force_meter meter <ip_count> over <time_window>s counter name ssh_brute_force_meterReplace
<ip_count>with the maximum allowed connection attempts (e.g.,10). Replace<time_window>with the time window in seconds (e.g.,60for 1 minute). -
Create a block list: This set will store IPs that exceed the limit.
sudo nft add set inet ssh_brute_force blocklist { type ipv4_addr\; flags interval\; } -
Add a rule to block exceeding IPs:
sudo nft add chain inet ssh_brute_force_block { type filter hook input priority 0 \; } sudo nft add rule inet ssh_brute_force_block ip saddr @blocklist tcp dport 22 rejectThis rule rejects any incoming TCP traffic on port 22 from IPs found in the
blocklistset. -
Add a rule to add exceeding IPs to the block list:
sudo nft add rule inet ssh_brute_force_block ip saddr @ssh_brute_force_meter meter <ip_count> over <time_window>s name ssh_brute_force_meter add @blocklist { ip saddr limit over <ip_count> over <time_window>s } counter name ssh_brute_force_addThis rule, placed after the meter rule, checks if an IP has exceeded the meter limit and, if so, adds it to the
blocklistset.
Why it works: The meter tracks connection attempts from each source IP. If an IP exceeds the configured ip_count within the time_window, the add @blocklist action automatically adds that IP to the blocklist set, and the preceding reject rule then blocks all subsequent SSH traffic from that IP.
Cause 2: Persistent Malicious IPs
Diagnosis: You’ll notice specific IP addresses repeatedly attempting to brute-force your SSH server, even after being temporarily blocked.
Command:
sudo nft list ruleset | grep blocklist
This will show IPs currently in your blocklist.
Fix:
Manually add known malicious IPs to the blocklist set.
sudo nft add element inet ssh_brute_force blocklist { <malicious_ip_address> }
Replace <malicious_ip_address> with the actual IP address.
Why it works: This directly adds the IP to the blocklist, ensuring that all traffic from that source is rejected by the reject rule, bypassing the meter for that specific IP.
Cause 3: Inefficient SSH Configuration
Diagnosis:
While not directly a nftables issue, an insecure SSH configuration can exacerbate brute-force attacks. For example, allowing root logins or password authentication without strong passwords.
Command: Check your SSH server configuration:
sudo grep -E "PermitRootLogin|PasswordAuthentication" /etc/ssh/sshd_config
Fix:
- Disable root login:
sudo sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config - Disable password authentication (use SSH keys):
sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config - Reload SSH service:
sudo systemctl reload sshd
Why it works: Disabling root login and password authentication significantly reduces the attack surface, making brute-force attacks less effective and less likely to succeed, even if they reach the SSH daemon.
Cause 4: Incorrect nftables Rule Placement
Diagnosis:
Your nftables rules are not being applied in the intended order, or the meter isn’t catching attempts before they hit the SSH daemon.
Command:
sudo nft list ruleset
Examine the output to ensure your ssh_brute_force_meter chain is hooked into the input hook with an appropriate priority (e.g., priority -10 to run early) and that the blocking rules follow. The meter rule should be before the block rule that adds IPs to the set.
Fix: Adjust priorities and chain order. A typical setup would be:
- A general
inputchain with a low priority (e.g.,-10) for meters and rate limiting. - A
filterchain with a higher priority (e.g.,0) for blocking.
Example nftables.conf snippet:
table inet filter {
chain input {
type filter hook input priority -10; policy accept;
# Meter for SSH brute force
ip saddr @ssh_brute_force_meter meter <ip_count> over <time_window>s counter name ssh_brute_force_meter
ip saddr @ssh_brute_force_meter meter <ip_count> over <time_window>s name ssh_brute_force_meter add @blocklist { ip saddr limit over <ip_count> over <time_window>s } counter name ssh_brute_force_add
# Block IPs in the blocklist
ip saddr @blocklist tcp dport 22 reject
}
}
set inet ssh_brute_force blocklist { type ipv4_addr\; flags interval\; }
set inet ssh_brute_force ssh_brute_force_meter { type ipv4_addr\; flags interval\; }
Note: This is a simplified example. Your actual nftables.conf might be more complex.
Why it works: By ensuring the meter runs before other input rules and the block rule runs after the meter has a chance to populate the blocklist, you guarantee that exceeding IPs are identified and blocked efficiently.
Cause 5: Firewall Interfering with nftables
Diagnosis:
You might have another firewall (like iptables or a cloud provider’s security group) that is also filtering SSH traffic, potentially creating conflicts or overriding your nftables rules.
Command:
Check if iptables is active:
sudo iptables -L
Check your cloud provider’s firewall settings.
Fix:
If iptables is active and managing SSH, disable it or ensure its rules are compatible.
sudo systemctl stop iptables
sudo systemctl disable iptables
If using cloud firewalls, ensure they allow SSH traffic from your trusted IPs and that nftables handles the brute-force protection on the instance itself.
Why it works: Consolidating your firewall logic in nftables prevents rule conflicts and ensures that your SSH brute-force protection rules are the definitive source for SSH traffic filtering.
Cause 6: Incorrect Port Configuration
Diagnosis:
You’ve changed the default SSH port (22) but forgot to update your nftables rules accordingly.
Command: Check your SSH port:
sudo grep "Port " /etc/ssh/sshd_config
Check your nftables rules:
sudo nft list ruleset | grep tcp dport
Fix:
Update the dport in your nftables rules to match your SSH port. For example, if SSH is on port 2222:
sudo nft replace rule inet ssh_brute_force_block ip saddr @blocklist tcp dport 2222 reject
sudo nft replace rule inet ssh_brute_force_block ip saddr @ssh_brute_force_meter meter <ip_count> over <time_window>s name ssh_brute_force_meter add @blocklist { ip saddr limit over <ip_count> over <time_window>s } counter name ssh_brute_force_add tcp dport 2222
Make sure to update both the block rule and the rule that adds IPs to the blocklist if they specify the port.
Why it works: The nftables rules need to target the correct port to intercept SSH traffic. If the port is wrong, the rules won’t match the actual SSH connections.
After implementing these nftables rules, you should see a significant reduction in brute-force attempts reaching your SSH daemon. The next potential issue you might encounter is needing to manage the blocklist set for legitimate users who might accidentally trigger the limits, or handling IPv6 brute-force attempts.