|

Protect Your Incus Server with NFtables

This guide will walk you through the process of implementing a secure firewall configuration for your Incus server using nftables. We’ll use a modular approach by splitting rules into logical units, making the configuration easier to maintain and modify.

Prerequisites

  • Debian 12 (Bookworm) or any modern Linux distribution with systemd
    • These rules have been tested on Debian but should work on most modern Linux distributions including Ubuntu, Fedora, Rocky Linux, and RHEL
    • Package names and installation commands might differ between distributions
  • Root access to the server
  • Incus installed and configured
  • Basic understanding of networking concepts

1. Understanding the Modular Configuration Approach

Before we begin, let’s understand the structure we’ll be using:

/etc/nftables.conf              # Main configuration file
/etc/nftables.d/                # Directory for modular rule files
├── vars.conf                   # Variables definition
├── base-fw.nft                 # Base firewall rules
├── ssh.nft                     # SSH-specific rules
└── incus-nat.nft               # NAT rules for containers

Benefits of this modular approach:

  • Easier maintenance: Each file handles a specific aspect of the firewall
  • Better organization: Rules are grouped logically
  • Simplified troubleshooting: Issues can be isolated to specific files
  • Easy updates: Individual components can be modified without affecting others
  • Version control friendly: Changes can be tracked more effectively

Prepare the System

First, we need to disable the default UFW firewall and remove iptables to prevent any conflicts:

# Disable and stop UFW
systemctl disable --now ufw

# Remove iptables
apt purge iptables -y

# Install nftables
apt install nftables

# Enable nftables service
systemctl enable nftables

2. Configure Incus Network

Check your Incus bridge network configuration:

# List Incus networks
incus network list | grep bridge

You should see output similar to:

| incusbr0 | bridge   | YES     | 10.0.3.1/24 | fd42:d71c:5e1a:3c3b::1/64 |             | 1       | CREATED |

Store your bridge name in a variable and disable the default firewall settings:

# Store bridge name
export INCUS_BR="incusbr0"

# Disable default firewall and NAT settings
incus network set $INCUS_BR ipv4.firewall false; \
incus network set $INCUS_BR ipv4.nat false; \
incus network set $INCUS_BR ipv6.firewall false; \
incus network set $INCUS_BR ipv6.nat false

3. Set Up nftables Configuration Structure

The modular approach uses separate files for different aspects of the firewall configuration. Here’s how to set it up:

# Backup original configuration
cp /etc/nftables.conf /etc/nftables.conf_orig_bu

# Create directory for modular configuration files
mkdir -p /etc/nftables.d

# Create main configuration file that includes modular files
cat > /etc/nftables.conf << EOF
#!/usr/sbin/nft -f

flush ruleset

# Include the variables file only once
include "/etc/nftables.d/vars.conf"

# Include all other configuration files
include "/etc/nftables.d/*.nft"
EOF

The main configuration file (/etc/nftables.conf) is kept minimal and serves primarily to:

  1. Flush existing rules
  2. Include the variables file first (ensuring variables are available to all other files)
  3. Include all other rule files using a wildcard pattern

File Organization Strategy

When creating your rule files, consider this organizational approach:

  • vars.conf: Contains all variable definitions
  • base-fw.nft: Contains basic firewall rules and chain definitions
  • ssh.nft: Contains SSH-specific access rules
  • incus-nat.nft: Contains NAT rules for container traffic
  • Additional .nft files can be created for specific services or requirements

Best Practices for Modular Configuration

  1. Naming Convention
  • Use descriptive names for your rule files
  • End service-specific files with .nft
  • Keep the variables file as vars.conf
  1. File Organization
  • Group related rules in the same file
  • Keep files focused on specific functions
  • Use comments to document complex rules
  1. Maintenance
  • When adding new rules, create new files rather than modifying existing ones
  • Keep backups of modified files
  • Test new rules in a separate file before integrating them
  1. Distribution-Specific Considerations

When using this configuration on different distributions, be aware of:

  • Package Management:
  # Debian/Ubuntu
  apt install nftables

  # Fedora
  dnf install nftables

  # RHEL/Rocky Linux
  yum install nftables
  • Service Management:
  # Most distributions use systemd
  systemctl enable --now nftables

  # Some might use different service names
  # Check your distribution's documentation
  • Default Configuration Locations:
  • Most distributions use /etc/nftables.conf
  • Some might use /etc/sysconfig/nftables.conf
  • Adjust paths in the guide accordingly

4. Configure Variables

Create /etc/nftables.d/vars.conf:

#!/usr/sbin/nft -f

# Define interfaces
define WAN = "eth0"
define INCUS_BRIDGE = "incusbr0"

# Host IP addresses for the WAN interface
define IPV4 = "YOUR_IPV4_ADDRESS"  # Replace with your IPv4 address
define IPV6 = "YOUR_IPV6_ADDRESS"  # Replace with your IPv6 address

# SSH port for secure management
define SSH_PORT = 3344

5. Implement Base Firewall Rules

Create /etc/nftables.d/base-fw.nft:

#!/usr/sbin/nft -f

table inet filter {
    # Define allowed ICMP types
    set icmp_allowed_types {
        type icmp_type
        elements = { destination-unreachable, time-exceeded, parameter-problem }
    }

    set icmpv6_allowed_types {
        type icmpv6_type
        elements = { destination-unreachable, packet-too-big, time-exceeded, parameter-problem,
                     nd-router-advert, nd-neighbor-advert, nd-router-solicit, nd-neighbor-solicit }
    }

    # IPv4 inbound chain
    chain inbound_ipv4 {
        icmp type @icmp_allowed_types accept
        icmp type echo-request limit rate 5/second accept
    }

    # IPv6 inbound chain
    chain inbound_ipv6 {
        icmpv6 type @icmpv6_allowed_types accept
        icmpv6 type echo-request limit rate 5/second accept
    }

    # Main inbound chain
    chain inbound {
        type filter hook input priority 0; policy drop;

        # Connection tracking
        ct state vmap { established : accept, related : accept, invalid : drop }

        # Allow loopback
        iifname lo accept

        # Route IPv4/IPv6 traffic
        meta protocol vmap { ip : jump inbound_ipv4, ip6 : jump inbound_ipv6 }

        # Container management traffic
        iifname $INCUS_BRIDGE udp dport { 53, 67 } accept

        # TCP security measures
        tcp flags & (fin|syn|rst|ack) != syn ct state new drop
        tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg drop
        tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop

        # Logging
        log prefix "DROP IN: " level info

        # Drop fragments
        ip frag-off & 0x1fff != 0 drop
    }

    # Forward chain
    chain forward {
        type filter hook forward priority 0; policy drop;

        ct state established,related accept

        # Container traffic
        iifname $INCUS_BRIDGE oifname $WAN accept
        iifname $WAN oifname $INCUS_BRIDGE ct state new accept

        log prefix "DROP FORWARD: " level info
    }

    # Outgoing chain
    chain outgoing {
        type filter hook output priority 0; policy accept;
    }
}

6. Configure SSH Access

Create /etc/nftables.d/ssh.nft:

#!/usr/sbin/nft -f

table inet filter {
    chain inbound {
        # Allow SSH access
        ip daddr $IPV4 tcp dport $SSH_PORT ct state new limit rate 10/minute accept comment "IPv4 SSH"
        ip6 daddr $IPV6 tcp dport $SSH_PORT ct state new limit rate 10/minute accept comment "IPv6 SSH"

        # Log and drop excessive SSH attempts
        ip daddr $IPV4 tcp dport $SSH_PORT log prefix "SSH IPv4 Dropped: " drop
        ip6 daddr $IPV6 tcp dport $SSH_PORT log prefix "SSH IPv6 Dropped: " drop
    }
}

7. Set Up NAT for Containers

Create /etc/nftables.d/incus-nat.nft:

#!/usr/sbin/nft -f

table ip nat {
    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;
        # Forward HTTP(S) traffic to container
        iifname $WAN tcp dport { 80, 443 } dnat to 10.0.3.10
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        # Masquerade outgoing traffic
        oifname $WAN masquerade
    }
}

table ip6 nat {
    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;
        # Forward HTTP(S) traffic for IPv6
        iifname $WAN ip6 daddr YOUR_IPV6_ADDRESS tcp dport { 80, 443 } dnat to CONTAINER_IPV6_ADDRESS
    }

    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        oifname $WAN masquerade
    }
}

8. Optional: Create a Rules Flush Script

For maintenance purposes, you can create a script to flush rules when needed:

# Create flush script
cat > /usr/local/sbin/flush-nftables.sh << EOF
#!/sbin/nft -f

flush ruleset
EOF

# Make it executable
chmod +x /usr/local/sbin/flush-nftables.sh

To automatically flush rules periodically (use with caution):

# Add to crontab (runs every 15 minutes)
echo "*/15 * * * * root /usr/local/sbin/flush-nftables.sh > /dev/null 2>&1" >> /etc/crontab

To disable automatic flushing:

sed -i -e 's/.*nftables.sh/#&/' /etc/crontab

To re-enable automatic flushing:

sed -i -e '/nftables.sh/ s/^#//' /etc/crontab

Security Considerations

  1. Default Deny Policy: The configuration uses a default deny policy, meaning only explicitly allowed traffic is permitted.
  2. Protection Against Common Attacks:
  • SYN flood protection through rate limiting and synproxy
  • Fragment attack protection
  • TCP flag-based attack prevention
  1. Logging: Important events are logged for monitoring and troubleshooting.
  2. Stateful Inspection: Connection tracking ensures proper handling of established connections.

Maintenance Tips

  1. Regularly review logs for suspicious activity:
journalctl -t nft
  1. Keep your system and nftables updated:
apt update && apt upgrade
  1. Periodically review and update firewall rules based on your changing needs.
  2. Backup your configuration files before making changes:
cp -r /etc/nftables.d/ /etc/nftables.d.backup

Troubleshooting

If you need to temporarily disable the firewall for troubleshooting:

systemctl stop nftables

To check the current ruleset:

nft list ruleset

To verify syntax before applying rules:

nft -c -f /etc/nftables.conf

Conclusion

This modular nftables configuration provides a robust security foundation for your Incus server, regardless of your Linux distribution. The modular approach makes it easy to maintain and modify rules while keeping the configuration organized and manageable.

The flexibility of this setup allows you to easily adapt it to different distributions and specific needs while maintaining a consistent security posture. Remember to:

  • Adjust paths and commands according to your specific distribution
  • Regularly review and update rules
  • Maintain backups of your configuration files
  • Monitor logs for security events

Regular monitoring and maintenance of your firewall rules is essential for maintaining security. Keep your system updated and periodically review logs for any suspicious activity.

Similar Posts