How to Configure a Firewall for IPv6

Table of Contents


Why IPv6 Requires Different Firewall Thinking

IPv6 fundamentally changes how we approach network security. Unlike IPv4, where Network Address Translation (NAT) accidentally provided a layer of obscurity, IPv6's massive address space (340 undecillion addresses) eliminates the need for address sharing. Every device can have a globally routable address, which means your firewall configuration becomes your primary defense perimeter.

This is not a security weakness—it's an opportunity to implement proper security controls. In IPv4 networks, many administrators mistakenly believed NAT provided security, when in reality it was the stateful firewall function within the NAT device doing the heavy lifting. With IPv6, you explicitly configure these protections, making your security posture clearer and more maintainable.

The key principle remains unchanged: allow only the traffic you need, deny everything else. However, IPv6 requires more careful configuration, particularly around ICMPv6 messages, which are far more critical to network operation than ICMP was in IPv4.


Understanding the NAT Security Myth

One of the most persistent myths about IPv6 is that removing NAT makes networks less secure. This misconception stems from conflating two separate functions:

  1. Address translation (the primary purpose of NAT) - Conserving IPv4 addresses
  2. Stateful packet inspection (the security function) - Tracking connection states

NAT was never designed as a security feature. It emerged as a workaround for IPv4 address exhaustion. The security benefits were incidental side effects of the stateful connection tracking required to make NAT work.

With IPv6, you implement stateful packet filtering directly, without the complexity and limitations of address translation. This actually provides better security because:

The slight increase in attack surface (devices are directly addressable) is more than offset by proper firewall rules, which you should have been implementing anyway.


Stateful vs Stateless Filtering

Stateful firewalls track the state of network connections and automatically allow return traffic for established connections. This is the modern standard and what you should implement for IPv6.

Advantages:

Connection states:

Stateless Filtering

Stateless firewalls examine each packet independently without connection context. While simpler, they require bidirectional rules and offer less protection. Only use stateless filtering if you have specific technical constraints or are working with embedded systems with limited resources.


Essential ICMPv6 Messages

Unlike IPv4, where ICMP was often completely blocked with minimal consequences, IPv6 will not function properly without ICMPv6. RFC 4890 provides authoritative guidance on filtering ICMPv6 messages.

Messages You Must NOT Drop

Error Messages (Critical for Communication)

Connectivity Checking

Neighbor Discovery Protocol (NDP)

These are the IPv6 equivalent of ARP. Block them and your network breaks:

Security note: NDP messages should only be accepted on local links (link-local addresses fe80::/10) with hop limit 255, ensuring they haven't traversed a router.

  1. Rate limiting - Apply rate limits to prevent ICMPv6 floods:

    ip6tables -A INPUT -p ipv6-icmp -m limit --limit 10/sec -j ACCEPT
    
  2. Scope restrictions - Only accept NDP messages from link-local addresses:

    ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -s fe80::/10 -j ACCEPT
    
  3. Drop unnecessary types - Block ICMPv6 types not essential for your network while allowing the critical ones listed above.


Default Deny Policy

A secure IPv6 firewall follows the principle of least privilege: deny everything by default, then explicitly allow only necessary traffic.

Setting Default Policies

Linux (ip6tables):

ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT  # Allow outbound by default (or DROP for maximum security)

Windows Firewall (PowerShell):

Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultInboundAction Block
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultOutboundAction Allow

BSD (pf):

# /etc/pf.conf
block all
pass out all keep state

This approach means any service or protocol you need must be explicitly permitted. While this requires more initial configuration, it significantly reduces attack surface and makes security audits straightforward.


Platform-Specific Configuration

Linux: ip6tables

IPv6 firewalling on Linux uses ip6tables, a separate command from iptables. Important: Many systems have iptables configured but ip6tables wide open by default. For detailed IPv6 configuration on Linux, see How to Configure IPv6 on Linux.

Basic stateful firewall configuration:

#!/bin/bash
# Flush existing rules
ip6tables -F
ip6tables -X

# Set default policies
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT

# Allow loopback
ip6tables -A INPUT -i lo -j ACCEPT

# Allow established and related connections
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow essential ICMPv6
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 1 -j ACCEPT   # Destination unreachable
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 2 -j ACCEPT   # Packet too big
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 3 -j ACCEPT   # Time exceeded
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 4 -j ACCEPT   # Parameter problem
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 128 -j ACCEPT # Echo request
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 129 -j ACCEPT # Echo reply

# Allow NDP (Neighbor Discovery Protocol) - link-local only
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 133 -s fe80::/10 -j ACCEPT # Router solicitation
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 134 -s fe80::/10 -j ACCEPT # Router advertisement
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -s fe80::/10 -j ACCEPT # Neighbor solicitation
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 136 -s fe80::/10 -j ACCEPT # Neighbor advertisement

# Allow specific services (example: SSH, HTTP, HTTPS)
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT

# Log dropped packets (optional, useful for debugging)
ip6tables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IP6TABLES-DROP: "

# Save rules (Debian/Ubuntu)
ip6tables-save > /etc/iptables/rules.v6

Making rules persistent:

Windows Firewall

Windows Firewall handles both IPv4 and IPv6 with the same rules by default. However, you can create IPv6-specific rules:

PowerShell examples:

# Allow ICMPv6 echo requests (ping)
New-NetFirewallRule -DisplayName "ICMPv6 Echo Request" `
  -Protocol ICMPv6 -IcmpType 128 `
  -Enabled True -Action Allow

# Allow web server (IPv6 only)
New-NetFirewallRule -DisplayName "HTTP IPv6" `
  -Direction Inbound -Protocol TCP -LocalPort 80 `
  -RemoteAddress Any -Action Allow

# Block specific IPv6 address
New-NetFirewallRule -DisplayName "Block specific IPv6" `
  -Direction Inbound -RemoteAddress 2001:db8::bad:actor `
  -Action Block

GUI configuration:

  1. Open Windows Defender Firewall with Advanced Security
  2. Create rules as normal - they apply to both protocols
  3. In rule properties, specify IPv6 in the "Protocols and Ports" tab if needed

BSD: pf (Packet Filter)

OpenBSD, FreeBSD, and macOS use pf, which handles IPv6 naturally:

# /etc/pf.conf
# Basic IPv6 stateful firewall

# Interfaces
ext_if = "em0"
int_if = "em1"

# Default deny
block all

# Allow loopback
pass on lo0 all

# Stateful outbound
pass out on $ext_if inet6 keep state

# Allow essential ICMPv6
pass in inet6 proto icmp6 icmp6-type { echoreq, echorep, unreach, toobig, timex, paramprob }
pass in inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv, routersol, routeradv }

# Allow specific services
pass in on $ext_if inet6 proto tcp to port { 22, 80, 443 } keep state

macOS: Application Firewall and pf

macOS has two firewalls:

  1. Application Firewall (GUI) - Application-based, limited IPv6 control
  2. pf (command-line) - Full IPv6 support, requires manual configuration

For advanced IPv6 control, use pf as shown in the BSD section above.


Common Rule Sets

Home Network/End User

# Allow all outbound, established inbound, essential ICMPv6
ip6tables -P INPUT DROP
ip6tables -P OUTPUT ACCEPT
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT  # Simplified: allow all ICMPv6
ip6tables -A INPUT -i lo -j ACCEPT

Web Server

# Add to basic ruleset above
ip6tables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT

SSH Server with Rate Limiting

# Prevent SSH brute force
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update \
  --seconds 60 --hitcount 4 -j DROP
ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT

DNS Server

# Allow DNS queries
ip6tables -A INPUT -p udp --dport 53 -m conntrack --ctstate NEW -j ACCEPT
ip6tables -A INPUT -p tcp --dport 53 -m conntrack --ctstate NEW -j ACCEPT

Testing and Validation

After configuring your IPv6 firewall, thorough testing is essential:

1. Test Connectivity

Use test-ipv6.run to validate your IPv6 configuration:

This tool runs entirely in your browser and provides a comprehensive score of your IPv6 readiness.

2. Test ICMPv6

# From external host, test ping
ping6 your-ipv6-address

# Test Path MTU Discovery (requires Packet Too Big messages)
ping6 -s 1500 your-ipv6-address

3. Verify Firewall Rules

# List all IPv6 rules
ip6tables -L -v -n

# Monitor dropped packets
watch -n 1 'ip6tables -L -v -n | grep DROP'

# Check connection tracking
cat /proc/net/nf_conntrack | grep ipv6

4. Test Specific Services

# Test SSH from external host
ssh -6 username@your-ipv6-address

# Test web server
curl -6 http://[your-ipv6-address]/

# Test DNS
dig @your-ipv6-address AAAA example.com

5. Security Scanning

# Port scan your own IPv6 address (from external host)
nmap -6 your-ipv6-address

# Test firewall rules
nmap -6 -sS -p- your-ipv6-address  # SYN scan all ports

6. Log Analysis

Monitor your firewall logs for:

# Watch firewall logs
tail -f /var/log/syslog | grep IP6TABLES

# Or for systemd-based systems
journalctl -f | grep IP6TABLES

7. IPv6 Readiness Checklist


Best Practices Summary

  1. Enable IPv6 firewalling explicitly - Many systems have IPv4 firewalls but leave IPv6 wide open
  2. Never completely block ICMPv6 - Your network won't function correctly
  3. Use stateful filtering - Simpler rules, better security
  4. Default deny policy - Only allow what you need
  5. Don't use NAT66 - It breaks IPv6 design principles and offers no security benefit
  6. Rate limit ICMPv6 - Prevent floods while allowing essential messages
  7. Scope NDP correctly - Only allow from link-local addresses
  8. Test regularly - Use automated tools like test-ipv6.run to validate configuration
  9. Log dropped packets - At least during initial configuration to identify issues
  10. Document your rules - Future you will appreciate understanding why each rule exists

For comprehensive security guidance beyond firewalls, see How Do I Secure My IPv6 Network?


Additional Resources


Need to test your IPv6 configuration? Visit test-ipv6.run for a comprehensive, browser-based connectivity test that checks IPv4-only, IPv6-only, and dual-stack scenarios with detailed scoring and latency measurements.