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 nftablesmeter and limit features to track connection attempts per IP address.

  1. 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_meter
    

    Replace <ip_count> with the maximum allowed connection attempts (e.g., 10). Replace <time_window> with the time window in seconds (e.g., 60 for 1 minute).

  2. 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\; }
    
  3. 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 reject
    

    This rule rejects any incoming TCP traffic on port 22 from IPs found in the blocklist set.

  4. 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_add
    

    This rule, placed after the meter rule, checks if an IP has exceeded the meter limit and, if so, adds it to the blocklist set.

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:

  1. 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
    
  2. 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
    
  3. 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:

  1. A general input chain with a low priority (e.g., -10) for meters and rate limiting.
  2. A filter chain 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.

Want structured learning?

Take the full Nftables course →