IPv6 Firewalling
This article is for information regarding firewalls on an IPv6 network.
Please see the SixXS FAQ for some information on what one should filter.
Contents
Linux support
Linux Kernel (ip6tables)
There is now an IPv6 stateful inspection kernel module since version 2.6.20 and ip6tables version 1.3.5 [1] [2]; you will need to enable the kernel module named CONFIG_NF_CONNTRACK_IPV6.
Configurable scripts
You don't have to build a specialized script for every firewall; there are configurable ones. If you are familiar with Shorewall (and even if you aren't) you may like 6wall, originally from the LEAF Project, rescued from oblivion and packaged for Debian/Ubuntu by Flavio Visentin. The configuration files are very similar to Shorewall's.
Beginning with Shorewall version 4.2.4, there are two additional packages that provide IPv6 support that can be installed to ipv4 Shorewall in parallel.
shorewall6, is documented at shorewall6
6wall, much like the example script below, is ready for old kernels (up to 2.6.20) that do not support properly IPv6 connection tracking. It relies on the SYN flag to allow new TCP connections, and makes no distinction between new and established connections for UDP and other protocols.
If you are running Linux 2.6.21+, you can apply a patch to add proper connection tracking support.
Using upstart on Ubuntu without additional packages
If you don't feel like using some additional software to manage couple of firewall lines you can use upstart to trigger firewall rules. Rules can be saved in whichever place using ip[6]tables-save, below upstart task is using /etc/default/ip[6]tables: File: /etc/init/iptables.conf
description "Starts and stops firewall by restoring ip[6]tables policies. Save rules with iptables-save > /etc/default/iptables and ip6tables-save > /etc/default/ip6tables" start on runlevel [2345] or net-device-up IFACE!=lo stop on runlevel [!2345] emits firewall console output pre-start script test -f /etc/default/iptables && /sbin/iptables-restore < /etc/default/iptables test -f /etc/default/ip6tables && /sbin/ip6tables-restore < /etc/default/ip6tables end script post-stop script /sbin/iptables -F /sbin/ip6tables -F end script
Save and restore IPv6 firewall rules
To configure firewall rules you can execute commands from below examples and then save them with:
# ip6tables-save > /etc/default/ip6tables.rules
To restore stored firewall rules, run:
# ip6tables-restore < /etc/default/ip6tables.rules
Example script for IPv6 stateless firewall
Note that this example, is exactly that, an example. It doesn't show best practices or what one should do.
#!/bin/sh # Flush & default ip6tables -F INPUT ip6tables -F OUTPUT ip6tables -F FORWARD ip6tables -F # Set the default policy to accept #ip6tables -P INPUT ACCEPT #ip6tables -P OUTPUT ACCEPT #ip6tables -P FORWARD ACCEPT # Enable the following lines only if a router! # Enabling IPv6 forwarding disables route-advertisement reception. # A static gateway will need to be assigned. # #echo "1" >/proc/sys/net/ipv6/conf/all/forwarding # #End router forwarding rules # Disable processing of any RH0 packet # Which could allow a ping-pong of packets ip6tables -A INPUT -m rt --rt-type 0 -j DROP ip6tables -A OUTPUT -m rt --rt-type 0 -j DROP ip6tables -A FORWARD -m rt --rt-type 0 -j DROP # Allow anything on the local link ip6tables -A INPUT -i lo -j ACCEPT ip6tables -A OUTPUT -o lo -j ACCEPT # Allow Link-Local addresses ip6tables -A INPUT -s fe80::/10 -j ACCEPT ip6tables -A OUTPUT -s fe80::/10 -j ACCEPT # Allow multicast ip6tables -A INPUT -d ff00::/8 -j ACCEPT ip6tables -A OUTPUT -d ff00::/8 -j ACCEPT # Allow ICMP ip6tables -A INPUT -p icmpv6 -j ACCEPT ip6tables -A OUTPUT -p icmpv6 -j ACCEPT #ip6tables -A FORWARD -p icmpv6 -j ACCEPT # Disable privileged ports for the outside, except ports 22, 515, and 631 # Specifying an interface (-i ethX) is probably a good idea to specify what is the outside ip6tables -A INPUT -p tcp --dport 1:21 -j REJECT ip6tables -A INPUT -p udp --dport 1:21 -j REJECT ip6tables -A INPUT -p tcp --dport 23:514 -j REJECT ip6tables -A INPUT -p udp --dport 23:514 -j REJECT ip6tables -A INPUT -p tcp --dport 516:630 -j REJECT ip6tables -A INPUT -p udp --dport 516:630 -j REJECT ip6tables -A INPUT -p tcp --dport 632:1024 -j REJECT ip6tables -A INPUT -p udp --dport 632:1024 -j REJECT
NOTE: REJECT target support for netfilter/IPv6 was first introduced with Linux-kernel v2.6.14.
Example script for IPv6 stateful firewall
Almost as above, but with state. You need a new kernel with the correct modules (see above). This firewall denies everything by default, but allows link-local addresses, multicast and icmpv6. Everything internal is also allowed. The host itself and every client can access the Internet and it also forward (inwards) ssh and bittorrent traffic to a specified host on the subnet.
sixxs is the external interface, br-lan is internal.
# First, delete all: ip6tables -F ip6tables -X # Allow anything on the local link ip6tables -A INPUT -i lo -j ACCEPT ip6tables -A OUTPUT -o lo -j ACCEPT # Allow anything out on the internet ip6tables -A OUTPUT -o sixxs -j ACCEPT # Allow established, related packets back in ip6tables -A INPUT -i sixxs -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow the localnet access us: ip6tables -A INPUT -i br-lan -j ACCEPT ip6tables -A OUTPUT -o br-lan -j ACCEPT # Filter all packets that have RH0 headers: ip6tables -A INPUT -m rt --rt-type 0 -j DROP ip6tables -A FORWARD -m rt --rt-type 0 -j DROP ip6tables -A OUTPUT -m rt --rt-type 0 -j DROP # Allow Link-Local addresses ip6tables -A INPUT -s fe80::/10 -j ACCEPT ip6tables -A OUTPUT -s fe80::/10 -j ACCEPT # Allow multicast ip6tables -A INPUT -d ff00::/8 -j ACCEPT ip6tables -A OUTPUT -d ff00::/8 -j ACCEPT # Allow ICMPv6 everywhere ip6tables -I INPUT -p icmpv6 -j ACCEPT ip6tables -I OUTPUT -p icmpv6 -j ACCEPT ip6tables -I FORWARD -p icmpv6 -j ACCEPT # Allow forwarding ip6tables -A FORWARD -m state --state NEW -i br-lan -o sixxs -s <subnet-prefix>::/48 -j ACCEPT ip6tables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT # SSH in ip6tables -A FORWARD -i sixxs -p tcp -d <subnet-prefix>::5 --dport 22 -j ACCEPT # Bittorrent ip6tables -A FORWARD -i sixxs -p tcp -d <subnet-prefix>::5 --dport 33600:33604 -j ACCEPT # Set the default policy ip6tables -P INPUT DROP ip6tables -P FORWARD DROP ip6tables -P OUTPUT DROP
A more sophisticated script for IPv6 stateful firewall
Almost as above, but with state. You need a new kernel with the correct modules, using asuswrt (Merlin Build) load
insmod /lib/modules/2.6.22.19/kernel/net/ipv6/netfilter/nf_conntrack_ipv6.ko
before FW launch.
This firewall denies everything by default, but allows link-local addresses, multicast and icmpv6. Everything internal is also allowed. The host itself and every client can access the Internet and only the sixxs POP is allowed to ping us, nobody else!
#!/bin/sh # # Test your ipv6 firewall rule set using: # http://ipv6.chappell-family.com/ipv6tcptest/index.php # Thank you Tim for providing this test tool. # # Ver. 2.0 (RHO and Logging, speciall ICMP Blocking) # 29.12.2012 # # Definitions IP6TABLES='/usr/sbin/ip6tables' # change LAN and IPv6 WAN interface name according your requirements WAN_IF='sixxs' LAN_IF='br0' SUBNETPREFIX='<subnet-prefix::>/48' MYTUNNEL='Your IP' SIXXSTUNNEL='Pop IP' # First Flush and delete all: $IP6TABLES -F INPUT $IP6TABLES -F OUTPUT $IP6TABLES -F FORWARD $IP6TABLES -F $IP6TABLES -X # DROP all incomming traffic $IP6TABLES -P INPUT DROP $IP6TABLES -P OUTPUT DROP $IP6TABLES -P FORWARD DROP # Filter all packets that have RH0 headers: $IP6TABLES -A INPUT -m rt --rt-type 0 -j DROP $IP6TABLES -A FORWARD -m rt --rt-type 0 -j DROP $IP6TABLES -A OUTPUT -m rt --rt-type 0 -j DROP # Allow anything on the local link $IP6TABLES -A INPUT -i lo -j ACCEPT $IP6TABLES -A OUTPUT -o lo -j ACCEPT # Allow anything out on the internet $IP6TABLES -A OUTPUT -o $WAN_IF -j ACCEPT # Allow established, related packets back in $IP6TABLES -A INPUT -i $WAN_IF -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow the localnet access us: $IP6TABLES -A INPUT -i $LAN_IF -j ACCEPT $IP6TABLES -A OUTPUT -o $LAN_IF -j ACCEPT # Allow Link-Local addresses $IP6TABLES -A INPUT -s fe80::/10 -j ACCEPT $IP6TABLES -A OUTPUT -s fe80::/10 -j ACCEPT # Allow multicast $IP6TABLES -A INPUT -d ff00::/8 -j ACCEPT $IP6TABLES -A OUTPUT -d ff00::/8 -j ACCEPT # Paranoia setting on ipv6 interface $IP6TABLES -I INPUT -i $WAN_IF -p tcp --syn -j DROP $IP6TABLES -I FORWARD -i $WAN_IF -p tcp --syn -j DROP $IP6TABLES -I INPUT -i $WAN_IF -p udp -j DROP $IP6TABLES -I FORWARD -i $WAN_IF -p udp -j DROP # Allow forwarding on ipv6 interface $IP6TABLES -A FORWARD -m state --state NEW -i $LAN_IF -o $WAN_IF -s $SUBNETPREFIX -j ACCEPT $IP6TABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow dedicated ICMPv6 packettypes, do this in an extra chain because we need it everywhere $IP6TABLES -N AllowICMPs # Destination unreachable $IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 1 -j ACCEPT # Packet too big $IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 2 -j ACCEPT # Time exceeded $IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 3 -j ACCEPT # Parameter problem $IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 4 -j ACCEPT # Echo Request (protect against flood) $IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 128 -m limit --limit 5/sec --limit-burst 10 -j ACCEPT # Echo Reply $IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 129 -j ACCEPT # # Only the sixxs POP is allowed to ping us (read FAQ this is a requirement) # $IP6TABLES -A INPUT -p icmpv6 -s $SIXXSTUNNEL -d $MYTUNNEL -j AllowICMPs # Log $IP6TABLES -A INPUT -j LOG --log-prefix "INPUT-v6:" $IP6TABLES -A FORWARD -j LOG --log-prefix "FORWARD-v6:" $IP6TABLES -A OUTPUT -j LOG --log-prefix "OUTPUT-v6:"
FreeBSD support
FreeBSD supports 3 different firewalls (although two of them are very close):
- ipfw6 is the IPv6 version of ipfw and has been part of FreeBSD for a long time.
- ipf aka IPFirewall by Darren Reed
- pf was started as a fork of ipf. pf was integrated in FreeBSD starting at version 5.3
The FreeBSD Handbook entry on firewalls is a good introduction to the concepts and tools.
pf
To enable pf on FreeBSD, you need to insert the following lines in /etc/rc.conf:
pf_enable="YES" pflog_enable="YES" pf_rules="/etc/filters/pf.conf" # rules definition file for pf pflog_logfile="/var/log/pflog" # where pflogd should store the logfile
You can change the path specified in pf_rules of course. The pf.conf(5) manpage give some extensive examples (incl. the complete grammar for rules and configuration).
The most simple firewall rule set is
Skeleton of an pf.conf firewall configuration
... your firewall rule / interfaces etc .... tun_if = "gif0" # <--- add the tunnel interface # don't filter on the loopback interface set skip on lo0 # scrub incomming packets scrub in all ..... other rules # # --- ipv6 pf.conf rules # # block in/out on $tun_if block in log on $tun_if inet6 block out log on $tun_if inet6 # allow heartbeat ping pass in log quick on $tun_if inet6 proto { ipv6-icmp } from <your individual net>::1 to <your individual net>::2 keep state # pass tcp, udp, and icmp6 out on the ipv6 tunnel interface. pass out log quick on $tun_if inet6 proto { tcp udp ipv6-icmp} keep state
Don't forget to check your configuraition with an external scanner to ensure your firewall settings are correct.
You can also find more information on the web (tutorials, book and so on). You can start with these: Wikipedia page on pf
Simple pf.conf firewall configuration
# # --- Simple pf.conf for ipv4 and ipv6 (aiccu) # # Macros ext_if = "vr0" # macro for external interface (tun0 or pppoe0 for PPPoE) int_if = "vr1" # macro for internal interface tun_if = "gif0" # macro for aiccu tunnel interface localnet = $int_if:network mytunnelstart = 2001:dead:beef:fdb8::1 mytunnelendpoint = 2001:dead:beef:fdb8::2 # don't filter on the loopback interface set skip on lo0 # scrub incomming packets scrub in all # # --- ipv4 rule set # nat on $ext_if from $localnet to any -> ($ext_if) block log all pass from {lo0, $localnet } to any keep state # # --- ipv6 rule set # # keep alive rules pass out log proto 41 from ($ext_if) to [PoP IPv4 Endpoint] keep state pass in log proto 41 from [PoP IPv4 Endpoint] to ($ext_if) keep state # block in/out on $tun_if block in log on $tun_if inet6 block out log on $tun_if inet6 # allow heartbeat ping pass in log quick on $tun_if inet6 proto { ipv6-icmp } from $mytunnelstart to $mytunnelendpoint keep state # pass tcp, udp, and icmp6 out on the ipv6 tunnel interface. pass out log quick on $tun_if inet6 proto { tcp udp ipv6-icmp} keep state
Please modify interfaces according your hardware requirements.
Check configuration
router# pfctl -sr No ALTQ support in kernel ALTQ related functions disabled scrub in all fragment reassemble block drop in log on gif0 inet6 all block drop out log on gif0 inet6 all pass in log quick on gif0 inet6 proto ipv6-icmp from 2001:dead:beef:dfb0::1 to 2001:dead:beef:dfb0::2 keep state pass out log quick on gif0 inet6 proto tcp all flags S/SA keep state pass out log quick on gif0 inet6 proto udp all keep state pass out log quick on gif0 inet6 proto ipv6-icmp all keep state
The sample shown above is from an ipv6 injector firewall according. Implemented on an dedicated FreeBSD instance after an NAT Firewall.
ipfw6
Your main source of information will be the following page in the handbook ipfw page
ipfw6 is the IPv6 version of ipfw. It can be enabled in /etc/rc.conf with these lines:
firewall_flags="" # Flags passed to ipfw when type is a file firewall_myservices="" # List of TCP ports on which this host # offers services firewall_allowservices="" # List of IPs which has access to # $firewall_myservices firewall_trusted="" # List of IPs which has full access to this host firewall_logdeny="NO" # Set to YES to log default denied incoming # packets. firewall_nologports="135-139,445 1026,1027 1433,1434" # List of TCP/UDP ports # for which denied incoming packets are not # logged.
More information within the default rc file in /etc/default/rc.conf and in the /etc/rc.firewall6 script.
ipf
Main page in the handbook on ipf is here
To enable ipf on FreeBSD, you need to insert the following lines in /etc/rc.conf:
ipfilter_enable="YES" ipnat_enable="YES" ipmon_enable="YES" ipfs_enable="NO" ipfilter_rules="/etc/filters/adsl.rules" ipnat_rules="/etc/filters/nat.rules" ipmon_flags="-Ds" ipfilter_flags="6"
IPnat is not required for IPv6 support of course.
OpenBSD support
For the sake of completeness a brief view on OpenBSD.
pf
pf on OpenBSD is enabled per default.
You can also find more information on the web (tutorials, book and so on). You can start with these: Wikipedia page on pf
We use the same pf.conf as already shown in the FreeBSD section.
Simple pf.conf firewall configuration
Check the minor syntax changes in pf. This work with OpenBSD 5.7.
# # --- Simple pf.conf for ipv4 and ipv6 (aiccu) # # Macros ext_if = "vr0" # macro for external interface (tun0 or pppoe0 for PPPoE) int_if = "vr1" # macro for internal interface tun_if = "gif0" # macro for aiccu tunnel interface localnet = $int_if:network mytunnelstart = 2001:dead:beef:fdb8::1 mytunnelendpoint = 2001:dead:beef:fdb8::2 # don't filter on the loopback interface set skip on lo0 # scrub incomming packets match on $tun_if scrub (no-df) # # --- ipv4 rule set # nat on $ext_if from $localnet to any -> ($ext_if) block log all pass from {lo0, $localnet } to any keep state # # --- ipv6 rule set # # keep alive rules pass out log proto 41 from ($ext_if) to [PoP IPv4 Endpoint] keep state pass in log proto 41 from [PoP IPv4 Endpoint] to ($ext_if) keep state # block in/out on $tun_if block in log on $tun_if inet6 block out log on $tun_if inet6 # allow heartbeat ping pass in log quick on $tun_if inet6 proto { ipv6-icmp } from $mytunnelstart to $mytunnelendpoint keep state # pass tcp, udp, and icmp6 out on the ipv6 tunnel interface. pass out log quick on $tun_if inet6 proto { tcp udp ipv6-icmp} keep state
Please modify interfaces according your hardware requirements.
Simple pf.conf router firewall configuration
Configuration
Cable Internet Provider
AICCU heartbeat tunnel
PC Engines ALIX / OpenBSD 5.7
Consult OpenBSD and pf documentation prior to any changes on your firewall configuration.
# $OpenBSD: pf.conf,v 1.1 2015/11/01 21:12:47 tom Exp $ ## Macros # The internal interface (connected to the local network) int_if="{ vether0 vr1 vr2 }" # The external interfaces (connected to the ipv4 and ipv6 network) ext_if="{ vr0 }" sixxs_if="{ gif0 }" ## don't forget to modify your parameters Your_IPv6="2001:dead:beaf:ab4::2" PoP_IPv6="2001:dead:beaf:ab4::1" sixxsgw="xx.yy.zz.dd" # replace by your PoP IPv4 ## Options set loginterface egress set optimization aggressive # scrub incomming packets match on egress scrub (no-df random-id max-mss 1440) # Set the default policy to return RSTs or ICMPs for blocked traffic set block-policy drop # Ignore the loopback interface entirely set skip on lo0 anchor "ftp-proxy/*" pass in quick on $int_if inet proto tcp to any port ftp \ divert-to 127.0.0.1 port 8021 ## Translation rules # NAT traffic on the interface in the default egress interface group (to # which the interface out of which the default route goes is assigned) from the # local network match out on egress inet from !(egress:network) to any nat-to (egress:0) ##### ## Queueing queue limits on $ext_if bandwidth 100M max 100M queue sixxsping parent limits bandwidth 5K queue priorityack parent limits bandwidth 100K queue std parent limits bandwidth 17M default ## Filtering ruleS # Default deny rule. which all blocked packets logged. block log all block in log on $ext_if all antispoof log for egress # Pass all traffic to and from the local network, using quick so that later # rules are not evaluated if a packet match this. Some rulesets would restrict # local traffic much further pass quick on $int_if all antispoof quick for "{ lo $int_if }" # Permit all traffic going out, keep state so that replies are # automatically passed many rulesets would have many rules here, # restricting traffic in an out on the external (egress) interface. # (keep state is not needed on the newest version of pf) pass out quick pass out on $ext_if proto tcp from $ext_if to any flags S/SA keep state queue priorityack set prio (1,7) #pass in on $ext_if proto tcp from any to $ext_if flags S/SA keep state queue priorityack set prio (1,7) ## ipv6 # Allow sixxs regular pings to our inbound interface # used for tunnel keep alive pass out log proto 41 from $ext_if to $sixxsgw keep state # set queue sixxsping pass in log proto 41 from $sixxsgw to $ext_if keep state # set queue sixxsping # # # pass in log on $sixxs_if inet6 proto { ipv6-icmp } from $PoP_IPv6 to $Your_IPv6 # Allow outgoing ipv6 traffic # pass tcp, udp, and icmp6 out on the external (Internet) interface. pass out on gif0 inet6 proto { tcp udp ipv6-icmp}
Check firewall
Check if the AICCU tunnel heartbeat is accepted.
# tcpdump -n -e -ttt -i pflog0 tcpdump: WARNING: snaplen raised from 116 to 160 tcpdump: listening on pflog0, link-type PFLOG Feb 21 17:20:42.904336 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request Feb 21 17:21:41.925066 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request Feb 21 17:22:40.944487 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request .... Feb 21 17:23:39.963511 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request Feb 21 17:24:38.983139 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request
As shown above, inbound icmp6 is allowed from your PoP IPv6. If you see frequent inbound requests the is up and running.
Make an additional test using Tim Chappel's free online IPV6 scanner.