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:
- Flush existing rules
- Include the variables file first (ensuring variables are available to all other files)
- 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 definitionsbase-fw.nft
: Contains basic firewall rules and chain definitionsssh.nft
: Contains SSH-specific access rulesincus-nat.nft
: Contains NAT rules for container traffic- Additional
.nft
files can be created for specific services or requirements
Best Practices for Modular Configuration
- Naming Convention
- Use descriptive names for your rule files
- End service-specific files with
.nft
- Keep the variables file as
vars.conf
- File Organization
- Group related rules in the same file
- Keep files focused on specific functions
- Use comments to document complex rules
- 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
- 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 = 22
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
- Default Deny Policy: The configuration uses a default deny policy, meaning only explicitly allowed traffic is permitted.
- Protection Against Common Attacks:
- SYN flood protection through rate limiting and synproxy
- Fragment attack protection
- TCP flag-based attack prevention
- Logging: Important events are logged for monitoring and troubleshooting.
- Stateful Inspection: Connection tracking ensures proper handling of established connections.
Maintenance Tips
- Regularly review logs for suspicious activity:
journalctl -t nft
- Keep your system and nftables updated:
apt update && apt upgrade
- Periodically review and update firewall rules based on your changing needs.
- 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.